다시 시작해도 중단되지 않도록 Docker 컨테이너 간의 연결을 어떻게 설정합니까?


80

다음과 같이 실행되는 몇 가지 Docker 컨테이너가 있습니다.

  • Nginx
  • 웹 앱 1
  • 웹 앱 2
  • PostgreSQL

Nginx는 웹 앱 1과 2 내의 웹 애플리케이션 서버에 연결해야하고 웹 앱은 PostgreSQL과 통신해야하므로 다음과 같은 링크가 있습니다.

  • Nginx --- 링크 ---> 웹 앱 1
  • Nginx --- 링크 ---> 웹 앱 2
  • 웹 앱 1 --- 링크 ---> PostgreSQL
  • 웹 앱 2 --- 링크 ---> PostgreSQL

이것은 처음에는 꽤 잘 작동합니다. 하지만 웹앱 1과 웹앱 2의 새 버전을 개발할 때이를 교체해야합니다. 내가하는 일은 웹 앱 컨테이너를 제거하고 새 컨테이너를 설정 한 다음 시작하는 것입니다.

웹 앱 컨테이너의 경우 처음에 해당 IP 주소는 다음과 같습니다.

  • 172.17.0.2
  • 172.17.0.3

그리고 그것들을 교체하면 새 IP 주소를 갖게됩니다.

  • 172.17.0.5
  • 172.17.0.6

이제 Nginx 컨테이너에 노출 된 환경 변수는 여전히 이전 IP 주소를 가리 킵니다. 여기에 문제가 있습니다. 컨테이너 간의 연결을 끊지 않고 컨테이너를 교체하려면 어떻게해야합니까? PostgreSQL에서도 동일한 문제가 발생합니다. PostgreSQL 이미지 버전을 업그레이드하려면 확실히 제거하고 새 버전을 실행해야하지만 전체 컨테이너 그래프를 다시 빌드해야하므로 실제 서버 운영에는 적합하지 않습니다.

답변:


53

의 효과 --link는 정적이므로 시나리오에서 작동하지 않습니다 (현재는 다시 연결되지 않지만 링크제거 할 수 있음 ).

우리는 dockerize.it에서이 문제를 해결하기 위해 링크 나 대사없이 두 가지 다른 접근 방식을 사용하고 있습니다 (대사도 추가 할 수 있지만).

1) 동적 DNS 사용

일반적인 아이디어는 데이터베이스 (또는 기타 서비스)에 단일 이름을 지정하고 컨테이너를 시작 및 중지 할 때 실제 IP로 수명이 짧은 DNS 서버를 업데이트하는 것입니다.

우리는 SkyDock으로 시작했습니다 . 두 개의 도커 컨테이너, DNS 서버 및 자동 업데이트를 유지하는 모니터와 함께 작동합니다. 나중에 우리는 Consul을 사용하여보다 맞춤화 된 것으로 이동했습니다 (도 커화 된 버전 : docker-consul 사용 ).

이것의 진화 (우리가 시도하지 않은)는 etcd 또는 이와 유사한 것을 설정하고 사용자 정의 API를 사용하여 IP와 포트를 학습하는 것입니다. 소프트웨어는 동적 재구성도 지원해야합니다.

2) 도커 브리지 IP 사용

컨테이너 포트를 노출 할 때 docker0잘 알려진 주소가 있거나 가질 수있는 브리지에 연결하기 만하면 됩니다.

컨테이너를 새 버전으로 교체 할 때 새 컨테이너가 동일한 IP에 동일한 포트를 게시하도록합니다.

이것은 더 간단하지만 더 제한적입니다. 유사한 소프트웨어 (예 : 두 개의 컨테이너가 docker0브리지 의 3306 포트에서 수신 대기 할 수 없음)를 실행하는 경우 포트 충돌이 발생할 수 있으므로 현재 가장 선호하는 옵션은 1입니다.


20

링크 는 컨테이너 이름을 기반으로하지 않고 특정 컨테이너에 대한 것입니다. 따라서 컨테이너를 제거하는 순간 링크 연결이 끊어지고 새 컨테이너 (이름이 같더라도)가 자동으로 자리를 차지하지 않습니다.

새로운 네트워킹 기능을 사용하면 이름으로 컨테이너에 연결할 수 있으므로 새 네트워크를 만들면 해당 네트워크에 연결된 모든 컨테이너가 이름으로 다른 컨테이너에 연결할 수 있습니다. 예:

1) 새 네트워크 생성

$ docker network create <network-name>       

2) 컨테이너를 네트워크에 연결

$ docker run --net=<network-name> ...

또는

$ docker network connect <network-name> <container-name>

3) 이름 별 핑 컨테이너

docker exec -ti <container-name-A> ping <container-name-B> 

64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

문서 의이 섹션을 참조하십시오 .

참고 : 레거시와 달리 links새 네트워킹 환경 변수를 생성하지 않으며 다른 컨테이너와 환경 변수를 공유하지 않습니다.

이 기능은 현재 별칭을 지원하지 않습니다.


이것은 버전 1.9 이상에서만 작동한다는 점을 지적하고 싶습니다. 일부 배포판은 아직 최신 버전으로 출시되지 않았습니다.
John Giotta 16.17.17

또 다른 옵션은 컨테이너 이름 대신 네트워크 범위 별칭을 사용하는 것입니다 (전역 적으로 고유해야하는 것이 항상 좋은 것은 아닙니다). 그럼에도 불구하고 대답은 절대적으로 정확합니다.
Ivan Anishchuk 2016

10

앰배서더 컨테이너를 사용할 수 있습니다 . 그러나 위와 동일한 문제가 발생하므로 앰배서더 컨테이너를 클라이언트에 연결하지 마십시오. 대신 Docker 호스트에서 앰배서더 컨테이너의 노출 된 포트 (일반적으로 172.17.42.1)를 사용합니다. 예:

postgres 볼륨 :

$ docker run --name PGDATA -v /data/pgdata/data:/data -v /data/pgdata/log:/var/log/postgresql phusion/baseimage:0.9.10 true

postgres-container :

$ docker run -d --name postgres --volumes-from PGDATA -e USER=postgres -e PASS='postgres' paintedfox/postgresql

postgres를위한 앰배서더 컨테이너 :

$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador

이제 앰배서더 컨테이너를 연결하지 않고 postgresql 클라이언트 컨테이너를 시작하고 게이트웨이 호스트 (일반적으로 172.17.42.1)에서 postgresql에 액세스 할 수 있습니다.

$ docker run --rm -t -i paintedfox/postgresql /bin/bash
root@b94251eac8be:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
root@b94251eac8be:/# echo $PGHOST
172.17.42.1
root@b94251eac8be:/#
root@b94251eac8be:/# psql -h $PGHOST --user postgres
Password for user postgres: 
psql (9.3.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.

postgres=#
postgres=# select 6*7 as answer;
 answer 
--------
     42
(1 row)

bpostgres=# 

이제 클라이언트를 다시 시작하지 않고도 앰배서더 컨테이너를 다시 시작할 수 있습니다.


"-p 5432 : 5432"는 PostgreSQL을 외부 세계에 노출시키지 않습니까?
Fang-Pen Lin

2
네, 그렇습니다. 원하지 않으면 "-p 172.17.42.1:5432:5432"를 사용할 수 있습니다.
Swen Thümmler 2014-06-21

1
그런데 왜 "PGDATA"컨테이너를 만들어서 postgresql 컨테이너에 연결해야합니까? 이해가 안 돼요, 왜 postgresql 컨테이너를 만들고 그 볼륨을 호스트 디렉토리에 직접 매핑하지 않습니까?
Fang-Pen Lin

PGDATA 컨테이너는 필요하지 않으며 우려 사항을 분리하기 위해 사용합니다. posgres 컨테이너를 시작할 때 PGDATA 컨테이너의 볼륨이 매핑되는 방식을 기억할 필요가 없습니다. 내가 현재하고있는 방식이기 때문에 추가했습니다. 그것은 기본적으로 취향의 문제 - 나 자신은 아직 확실히 그것은 좋은 아이디어 나하지 ...인지하지 않다
스웬 Thümmler

Swen과 같은 데이터 볼륨 컨테이너를 사용하는 것이 실제로 모범 사례입니다.
derFunk

2

여전히 궁금한 사람이 있다면 각 도커 컨테이너의 / etc / hosts 파일에있는 호스트 항목을 사용해야하며 자동으로 업데이트되지 않으므로 ENV 변수에 의존해서는 안됩니다.

LINKEDCONTAINERNAME_PORT_PORTNUMBER_TCP 등의 형식으로 연결된 각 컨테이너에 대한 호스트 파일 항목이 있습니다.

다음은 도커 문서 에서 가져온 것입니다.

Docker 환경 변수에 대한 중요 참고 사항

/ etc / hosts 파일의 호스트 항목과 달리 환경 변수에 저장된 IP 주소는 소스 컨테이너를 다시 시작해도 자동으로 업데이트되지 않습니다. / etc / hosts의 호스트 항목을 사용하여 연결된 컨테이너의 IP 주소를 확인하는 것이 좋습니다.

이러한 환경 변수는 컨테이너의 첫 번째 프로세스에 대해서만 설정됩니다. sshd와 같은 일부 데몬은 연결을 위해 쉘을 생성 할 때이를 스크럽합니다.


2

이것은 3 주 전 Docker의 실험적 빌드에 포함되어 있으며 서비스 도입 : https://github.com/docker/docker/blob/master/experimental/networking.md

--publish-service <name>인수 로 도커 컨테이너를 실행하여 동적 링크를 얻을 수 있어야합니다 . 이 이름은 DNS를 통해 액세스 할 수 있습니다. 이것은 컨테이너를 다시 시작할 때 영구적입니다 (물론 동일한 서비스 이름으로 컨테이너를 다시 시작하는 한)


해당 버전을 어떻게 설치합니까? github.com/docker/docker/releases/tag/v1.8.0-rc1
m59

1
자세한 정보는이 페이지를 참조하십시오 : github.com/docker/docker/tree/master/experimental . 짧은 버전 : 실행 wget -qO- https://experimental.docker.com/ | sh실험 버전을 설치
로렌스 리트 벨트를

1
이 대답은 유효했지만 docker가 실험 publish-service옵션을 제거했기 때문에 이제 구식 입니다. 이제는 대신 네트워크 범위 별칭이 있습니다. 하지만 본질적으로 같은 것입니다.
Ivan Anishchuk

1

이 문제를 해결하기 위해 이름이있는 도커 링크 를 사용할 수 있습니다 .

가장 기본적인 설정은 먼저 명명 된 데이터베이스 컨테이너를 만드는 것입니다 .

$ sudo docker run -d --name db training/postgres

그런 다음 db에 연결하는 웹 컨테이너를 만듭니다.

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

이를 통해 컨테이너를 IP 주소와 수동으로 연결할 필요가 없습니다.


2
흠 ... docker가 어떻게 든 연결된 호스트 이름을 생성하는 것처럼 보이지만 그 방식은 / etc / hosts에 이름을 생성합니다. 연결된 컨테이너를 다시 시작할 때 IP가 변경되지만 / etc / hosts는 동일하게 유지되므로 작동하지 않습니다.
Fang-Pen Lin

2
Docker 버전 1.0은 IP 주소를 더 적극적으로 할당하기 때문입니다. 컨테이너를 다시 시작하면 ( db이 경우) 새 IP 주소를받습니다. 다른 컨테이너 (다시 시작 여부)는 시작 시점부터 ENV 값을 유지하며 쓸모가 없습니다.
GermanDZ

1
fyi가 수정 될 것 같습니다. 연결된 컨테이너가 다시 시작되면 / etc / hosts가 업데이트됩니다. github.com/docker/docker/issues/6350
jamshid

1
이 문제는 해결 된 것으로 보이며 제안 된 방법이 저에게 효과적입니다.
Jens Piegsa 2015 년

1
이것이 가장 정답입니다. 유일한 문제는 링크가 단방향이고 컨테이너간에 종속성을 추가한다는 것입니다. 두 컨테이너를 교차 연결할 수없고 연결된 컨테이너를 중지 한 다음 다시 시작할 수 없습니다 (새 옵션 등을 사용하여). 이러한 경우 네트워크 ( net-alias또는 컨테이너 이름 중 하나 )를 사용하십시오.
Ivan Anishchuk 2016

1

OpenSVC 접근 방식을 사용하면 다음을 통해 해결할 수 있습니다.

  • 고유 한 IP 주소 / DNS 이름 (최종 사용자가 연결할 이름)으로 서비스 사용
  • 이 특정 IP 주소에 포트를 노출하도록 docker에 지시 ( "--ip"docker 옵션)
  • 서비스 IP 주소에 연결하도록 앱 구성

컨테이너를 교체 할 때마다 올바른 IP 주소에 연결될 것입니다.

여기 자습서 => OpenSVC를 사용하는 Docker 다중 컨테이너

tuto 끝에있는 "복잡한 오케스트레이션"부분을 놓치지 마십시오. 컨테이너를 올바른 순서로 시작 / 중지하는 데 도움이 될 수 있습니다 (postgresql 하위 집합 1 개 + webapp 하위 집합 1 개 + nginx 하위 집합 1 개).

가장 큰 단점은 webapp 및 PostgreSQL 포트를 공용 주소에 노출하고 실제로 nginx tcp 포트만 공용으로 노출하면된다는 것입니다.


1

링크를 그대로 유지하기 위해 중간 컨테이너를 사용하는 앰배서더 방법을 시도해 볼 수도 있습니다 ... ( https://docs.docker.com/articles/ambassador_pattern_linking/ 참조 ) 자세한 내용은


Ambassador는 좋은 패턴이지만 동일한 문제가 있습니다. 다시 시작할 때 IP 주소가 반드시 업데이트되는 것은 아닙니다. 그러나 호스트 간 연결에 적합합니다. 글쎄, 아마도 필요하지 않은 새로운 도커 릴리스를 사용할 수 있습니다.
Ivan Anishchuk 2016

@IvanAnishchuk 사실,하지만 시간에 의견이 갈 방법 ...이었다 하였다 (이년 전))
Gekkie

0

이미지의 연결 포트를 호스트의 고정 포트에 바인딩하고 대신 사용하도록 서비스를 구성 할 수 있습니다.

이것에도 단점이 있지만 귀하의 경우에는 작동 할 수 있습니다.


바인딩 로컬 호스트 포트에는 실제로 단점이 있습니다. 새로운 도커 네트워킹은 구식을 만듭니다.
Ivan Anishchuk 2016

0

또 다른 대안은 --net container:$CONTAINER_ID옵션 을 사용하는 것입니다.

1 단계 : "네트워크"컨테이너 생성

docker run --name db_net ubuntu:14.04 sleep infinity
docker run --name app1_net --link db_net:db ubuntu:14.04 sleep infinity
docker run --name app2_net --link db_net:db ubuntu:14.04 sleep infinity
docker run -p 80 -p 443 --name nginx_net --link app1_net:app1 --link app2_net:app2 ubuntu:14.04 sleep infinity

2 단계 : "네트워크"컨테이너에 서비스 삽입

docker run --name db --net container:db_net pgsql
docker run --name app1 --net container:app1_net app1
docker run --name app2 --net container:app1_net app2
docker run --name nginx --net container:app1_net nginx

"네트워크"컨테이너를 건드리지 않는 한 링크의 IP 주소는 변경되지 않아야합니다.


의미있는 이름으로 사용자가 만든 브리지 네트워크가 더 나은 옵션 일 수 있습니다. 네트워크를 사용하기 위해 컨테이너를 만들 필요가 없습니다.
Ivan Anishchuk 2016

0

이 경우 네트워크 범위 별칭 이 필요합니다. 하나의 컨테이너에서만 액세스 할 수있는 링크 별칭과 달리 전체 네트워크에 서비스를 제공하는 컨테이너를 "게시"하는 데 사용할 수있는 다소 새로운 기능입니다.

컨테이너간에 어떤 종류의 종속성도 추가하지 않습니다. 다시 시작, 교체 및 시작 순서에 관계없이 둘 다 실행되는 동안 통신 할 수 있습니다. 내부적으로 / etc / hosts 대신 DNS를 사용합니다.

다음과 같이 사용하십시오 docker run --net=some_user_definied_nw --net-alias postgres .... 동일한 네트워크의 모든 컨테이너에서 해당 별칭을 사용하여 연결할 수 있습니다.

기본 네트워크에서는 작동하지 않습니다. 불행히도 모든 컨테이너에 대해 하나를 docker network create <network>만든 다음 함께 사용해야합니다 --net=<network>( compose도 지원함 ).

컨테이너가 다운되어 별칭으로 연결할 수없는 것 외에도 여러 컨테이너가 별칭을 공유 할 수 있으며이 경우 올바른 것으로 확인되지 않을 수 있습니다. 그러나 어떤 경우에는 원활한 업그레이드에 도움이 될 수 있습니다.

아직 문서화가 잘되어 있지는 않지만 man 페이지를 읽는 것만으로는 파악하기 어렵습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.