Swarm 모드 라우팅 메시 사용하기
Swarm 모드 라우팅 메시 사용하기
Docker 엔진의 Swarm 모드를 사용하면 서비스 포트를 게시해서 스웜 외부의 리소스에서도 서비스에 접근할 수 있게 만들 수 있어요. 모든 노드가 인그레스 라우팅 메시에 참여하는데, 덕분에 특정 노드에 실제 태스크가 없어도 스웜의 어떤 노드든 게시된 포트로 들어오는 연결을 받을 수 있답니다. 라우팅 메시는 사용 가능한 노드의 게시된 포트로 들어온 모든 요청을 활성 컨테이너로 라우팅해 줘요.
출처: 공식문서
본문
스웜에서 인그레스 네트워크를 사용하려면, Swarm 모드를 활성화하기 전에 스웜 노드 간에 다음 포트가 열려 있어야 해요.
- 컨테이너 네트워크 디스커버리를 위한 포트
7946TCP/UDP - 컨테이너 인그레스 네트워크를 위한 포트
4789UDP (설정 가능)
스웜에서 네트워킹을 설정할 때는 특별히 주의해야 해요. 자세한 개요는 튜토리얼을 참고하세요.
또한 스웜 노드와 외부 로드 밸런서처럼 해당 포트에 접근해야 하는 외부 리소스 사이에서도 게시된 포트를 열어 두어야 해요.
특정 서비스에 대해서는 라우팅 메시를 우회할 수도 있어요.
서비스의 포트 게시하기
서비스를 만들 때 --publish 플래그를 사용해서 포트를 게시할 수 있어요. target은 컨테이너 내부 포트를 지정하고, published는 라우팅 메시에 바인딩할 포트를 지정해요. 만약 published 포트를 생략하면 각 서비스 태스크에 높은 번호의 임의 포트가 바인딩돼요. 이때는 태스크를 직접 조회해서 포트를 확인해야 해요.
$ docker service create \
--name <SERVICE-NAME> \
--publish published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<IMAGE>
참고
이 구문의 이전 형태는 콜론으로 구분된 문자열로, 게시 포트가 먼저 오고 대상 포트가 두 번째로 와요(예:
-p 8080:80). 새 구문이 읽기 쉽고 더 유연해서 권장된답니다.
<PUBLISHED-PORT>는 스웜이 서비스를 제공하는 포트예요. 생략하면 높은 번호의 임의 포트가 바인딩돼요. <CONTAINER-PORT>는 컨테이너가 리슨하는 포트로, 필수 파라미터예요.
예를 들어, 다음 명령은 nginx 컨테이너의 80번 포트를 스웜의 모든 노드에서 8080 포트로 게시해요.
$ docker service create \
--name my-web \
--publish published=8080,target=80 \
--replicas 2 \
nginx
어떤 노드에서든 8080 포트에 접근하면 Docker가 요청을 활성 컨테이너로 라우팅해요. 스웜 노드 자체에는 실제로 8080 포트가 바인딩되어 있지 않을 수 있지만, 라우팅 메시가 트래픽을 라우팅하는 방법을 알고 있어서 포트 충돌도 발생하지 않아요.
라우팅 메시는 노드에 할당된 모든 IP 주소에 대해 게시된 포트에서 리슨해요. 외부에서 라우팅 가능한 IP 주소라면 호스트 외부에서도 포트에 접근할 수 있고, 그 외 IP 주소는 호스트 내부에서만 접근할 수 있어요.
기존 서비스에 포트를 게시하려면 다음 명령을 사용하면 돼요.
$ docker service update \
--publish-add published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<SERVICE>
docker service inspect 명령으로 서비스의 게시된 포트를 확인할 수 있어요. 예를 들어:
$ docker service inspect --format="{{json .Endpoint.Spec.Ports}}" my-web
[{"Protocol":"tcp","TargetPort":80,"PublishedPort":8080}]
출력 결과에서 컨테이너의 <CONTAINER-PORT>는 TargetPort로, 노드가 서비스 요청을 리슨하는 <PUBLISHED-PORT>는 PublishedPort로 표시돼요.
TCP 전용 또는 UDP 전용 포트 게시하기
기본적으로 포트를 게시하면 TCP 포트로 게시돼요. TCP 포트 대신 UDP 포트를 게시하거나 TCP 포트에 추가로 UDP 포트를 게시할 수도 있어요. TCP와 UDP 포트를 모두 게시할 때 프로토콜 지정자를 생략하면 TCP 포트로 게시돼요. 긴 구문(권장)을 사용할 때는 protocol 키를 tcp 또는 udp로 설정하면 돼요.
TCP 전용
긴 구문:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
dns-cache
짧은 구문:
$ docker service create --name dns-cache \
-p 53:53 \
dns-cache
TCP 및 UDP
긴 구문:
$ docker service create --name dns-cache \
--publish published=53,target=53 \
--publish published=53,target=53,protocol=udp \
dns-cache
짧은 구문:
$ docker service create --name dns-cache \
-p 53:53 \
-p 53:53/udp \
dns-cache
UDP 전용
긴 구문:
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp \
dns-cache
짧은 구문:
$ docker service create --name dns-cache \
-p 53:53/udp \
dns-cache
라우팅 메시 우회하기
기본적으로 포트를 게시하는 스웜 서비스는 라우팅 메시를 사용해요. 스웜의 어떤 노드에서든 게시된 포트에 연결하면, 그 노드가 해당 서비스를 실행 중인지 여부와 관계없이, 그 서비스를 실행 중인 워커로 투명하게 리다이렉트돼요. 사실상 Docker가 스웜 서비스의 로드 밸런서 역할을 하는 셈이죠.
라우팅 메시를 우회하면 특정 노드의 바인딩된 포트에 접근할 때 항상 그 노드에서 실행 중인 서비스 인스턴스에 접근하게 돼요. 이걸 host 모드라고 해요. 몇 가지 주의할 점이 있어요.
-
서비스 태스크가 실행되지 않는 노드에 접근하면 해당 포트에서 서비스가 리슨하지 않아요. 아무것도 리슨하지 않거나 완전히 다른 애플리케이션이 리슨하고 있을 수도 있어요.
-
각 노드에서 여러 서비스 태스크를 실행하려는 경우(예: 노드 5개에 레플리카 10개를 실행하는 경우) 정적 대상 포트를 지정할 수 없어요.
published를 생략해서 Docker가 높은 번호의 임의 포트를 할당하도록 하거나, replicated 서비스 대신 global 서비스를 사용하거나 배치 제약 조건을 사용해서 특정 노드에서 서비스 인스턴스가 하나만 실행되도록 해야 해요.
라우팅 메시를 우회하려면 긴 --publish 구문을 사용하고 mode를 host로 설정해야 해요. mode 키를 생략하거나 ingress로 설정하면 라우팅 메시가 사용돼요. 다음 명령은 host 모드를 사용해서 라우팅 메시를 우회하는 global 서비스를 만들어요.
$ docker service create --name dns-cache \
--publish published=53,target=53,protocol=udp,mode=host \
--mode global \
dns-cache
외부 로드 밸런서 구성하기
스웜 서비스용 외부 로드 밸런서를 라우팅 메시와 함께 사용하거나 라우팅 메시 없이 구성할 수 있어요.
라우팅 메시 사용하기
외부 로드 밸런서가 스웜 서비스로 요청을 라우팅하도록 구성할 수 있어요. 예를 들어 HAProxy를 사용해서 8080 포트로 게시된 nginx 서비스에 요청을 분산할 수 있어요.
이 경우 로드 밸런서와 스웜 노드 사이에 8080 포트가 열려 있어야 해요. 스웜 노드는 프록시 서버가 접근할 수 있지만 공개적으로는 접근할 수 없는 사설 네트워크에 있을 수 있어요.
노드에 태스크가 예약되어 있지 않더라도 스웜의 모든 노드 간에 요청을 분산하도록 로드 밸런서를 구성할 수 있어요. 예를 들어 /etc/haproxy/haproxy.cfg에 다음과 같은 HAProxy 구성을 사용할 수 있어요.
global
log /dev/log local0
log /dev/log local1 notice
...snip...
# Configure HAProxy to listen on port 80
frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back
# Configure HAProxy to route requests to swarm nodes on port 8080
backend http_back
balance roundrobin
server node1 192.168.99.100:8080 check
server node2 192.168.99.101:8080 check
server node3 192.168.99.102:8080 check
HAProxy 로드 밸런서의 80 포트에 접근하면 요청이 스웜의 노드로 전달돼요. 그러면 스웜 라우팅 메시가 요청을 활성 태스크로 라우팅해요. 스웜 스케줄러가 어떤 이유로 태스크를 다른 노드에 배치하더라도 로드 밸런서를 다시 구성할 필요가 없어요.
어떤 종류의 로드 밸런서든 스웜 노드로 요청을 라우팅하도록 구성할 수 있어요. HAProxy에 대한 자세한 내용은 HAProxy 문서를 참고하세요.
라우팅 메시 없이 사용하기
라우팅 메시 없이 외부 로드 밸런서를 사용하려면 --endpoint-mode를 기본값인 vip 대신 dnsrr로 설정하세요. 이 경우 단일 가상 IP가 없어요. 대신 Docker가 서비스에 대한 DNS 항목을 설정해서, 서비스 이름으로 DNS 쿼리를 하면 IP 주소 목록이 반환되고 클라이언트는 그중 하나에 직접 연결해요.
--endpoint-mode dnsrr는 --publish mode=ingress와 함께 사용할 수 없어요. 서비스 앞에 자체 로드 밸런서를 직접 실행해야 해요. Docker 호스트에서 서비스 이름으로 DNS 쿼리를 하면 서비스를 실행 중인 노드의 IP 주소 목록이 반환돼요. 로드 밸런서가 이 목록을 사용해서 노드 간 트래픽을 분산하도록 구성하세요. 서비스 디스커버리 구성을 참고하세요.