Swarm - routing mesh
- 출처: https://docs.docker.com/engine/swarm/ingress/#publish-a-port-for-a-service
- 개요
Docker Engine swarm mode는 외부 리소스가 사용할 수 있도록 service port를 노출하는 기능을 제공한다. 모든 node는 ingress routing mesh 에 참여한다. routing mesh는 node에서 수행중인 task가 없더라도 다른 node에서 실행중인 task의 service port로 연결해준다. routing mesh는 node에 노출된 port로 들어오는 모든 요청을 활성화된 container로 routing 한다.
본격적으로 들어가기 전에 swarm 에서 ingress network를 사용하기 위해서 swarm node간의 port를 열어줘야 한다.
- TCP/UDP 7946: container netowrk discovery
- UDP 4789: container ingress network
- Service port 노출
--publish flag를 사용하면 service 생성시 port를 공개할 수 있다.
- "published": routing mesh 상에 바인딩될 port를 지정. 지정하지 않으면 random port 할당
- "target": container 내의 port 지정. 해당 parameter는 optional이 아닌 필수 지정이다.
docker service create \
--name <SERVICE-NAME> \
--publish published=<PUBLISHED-PORT>,target=<CONTAINER-PORT> \
<IMAGE>
예를 들어 nginx의 80 port를 swarm의 모든 node에 8080 port로 등록하고자 한다면 아래와 같이 작성한다. 생성 시점 뿐만 아니라 기존에 존재하는 service에도 port를 노출할 수 있다.
[ec2-user@ip-172-31-36-249 ~]$ docker service create --name my-web --publish published=8080,target=80 --replicas 2 nginx
svmv5sj3axo5h0yp1k62v8nqx
overall progress: 2 out of 2 tasks
1/2: running [==================================================>]
2/2: running [==================================================>]
verify: Service converged
[ec2-user@ip-172-31-36-249 ~]$ docker service ps my-web
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
oq2d69rck5nl my-web.1 nginx:latest ip-172-31-36-249.ap-northeast-2.compute.internal Running Running 23 seconds ago
nrxafjljtop5 my-web.2 nginx:latest ip-172-31-46-128.ap-northeast-2.compute.internal Running Running 23 seconds ago
어떤 node의 8080 port로 접근하더라도 Docker는 해당 요청을 활성화된 container로 제공한다. 위의 예제에서는 2개의 replica만 설정했으므로 3개의 node 중에서 2개의 node 에서만 task가 실행되었는데, 실제 task가 없는 node이더라도 routing mesh는 요청을 받으면 해당 요청을 적절한 node의 container로 전달한다.
나는 테스트를 위해 amazon ec2 instance를 이용하였는데 구성한 node 정보는 아래와 같다. task가 없는 node ip로 요청해도 nginx 응답을 받는것을 확인할 수 있다.
- 172.31.36.249: service task가 실행되고 있음
- 172.31.46.128: service task가 실행되고 있음
- 172.31.45.73: task 없음
[ec2-user@ip-172-31-36-249 ~]$ curl -v 172.31.45.73:8080
...
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
...
- 외부 load balancer 설정
실제 운용 환경에서는 아래처럼 docker node로 직접 접근하지 않고 앞단에 load balancer가 있을 가능성이 매우크다. 아래처럼 외부 load balancer가 요청을 swarm service로 보내는 방식이다.
위의 그림처럼 구성하려면 Load balancer -> swarm node 사이의 8080 port가 열려있어야 한다. HAProxy 설정은 요청을 받으면 모든 swarm node에 요청을 분산하도록 설정한다.
Amazon EC2 instance에 HAProxy를 설치하고 설정하는 방법은 아래와 같다. 80 port 요청을 받아서 swarm node ip들의 8080 port로 요청을 넘겨주도록 설정하면된다. // 주석을 따라서 차근차근 해보자.
// haproxy 설치
[ec2-user@ip-172-31-42-66 ~]$ sudo yum install haproxy
Last metadata expiration check: 0:05:27 ago on Sun Oct 8 13:13:06 2023.
...
Installed:
haproxy-2.8.0-1.amzn2023.0.2.x86_64
Complete!
// haproxy 설정
sudo vim /etc/haproxy/haproxy.cfg
...
### docker frontend
frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back
backend http_back
balance roundrobin
server node1 172.31.46.128:8080 check
server node2 172.31.36.249:8080 check
server node3 172.31.45.73:8080 check
// 서비스 시작
systemctl enable haproxy
systemctl start haproxy
// HAproxy node에서 테스트
[ec2-user@ip-172-31-42-66 ~]$ curl 172.31.42.66
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
// local zsh(EC2 외부) 에서 접속 테스트
~ % curl http://3.39.226.208/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
172.31.42.66은 HAProxy를 설치한 EC2 private IP 이며, 3.39.226.208은 외부에서 해당 EC2 instance로 접속할 때 사용하는 ip이다. 이때 보안에서 외부 HTTP protocol이 open 되어있는지도 확인한다.
몇번 접속한 뒤에 요청이 골고루 분산되었는지 통계 페이지에서 확인할 수 있다. HAProxy 설정에서 stats uri 부분을 보면 /haproxy?stats 라고 설정된 부분이 보이는데 http://3.39.226.208/haproxy?stats 로 접속하면 아래와 같은 화면을 볼 수 있다.