Docker Swarm 2026.09.04 6 min read

정적 IP 전환부터 Docker Swarm 5노드까지

Docker Swarm을 구성하기 전에 나는 먼저 네트워크를 고정해야 했다. Cluster node 주소, DB route, NFS export, monitoring target이 모두 IP에 의존하기 때문이다. 이 단계에서 가장 큰 실수는 “Ubuntu니까 NetworkManager로 관리될 것”이라고 가정한 것이었다.

nmtui에 NIC가 보이지 않았던 이유

NetworkManager와 nmtui를 설치했는데도 실제 NIC가 보이지 않았다. 원인은 Netplan renderer가 systemd-networkd였기 때문이다. 운영 중인 renderer를 확인하지 않고 도구부터 설치한 것이 문제였다.

나는 NetworkManager를 억지로 활성화하지 않고 기존 systemd-networkd 체계를 유지했다. 각 서버의 실제 interface 이름과 현재 gateway, DNS, route를 수집한 뒤 Netplan 파일을 수정했다.

장비마다 interface가 ens33, ens36, eno4처럼 달랐기 때문에 예제 이름을 복사하지 않았다. 잘못된 interface 이름으로 원격 서버의 Netplan을 적용하면 SSH를 잃을 수 있기 때문이다.

IP 변경은 한 대씩 진행했다

Swarm node 주소를 한꺼번에 바꾸면 manager quorum과 overlay 통신을 동시에 잃을 수 있다. 나는 manager와 worker를 한 대씩 변경하고 매 단계마다 다음을 확인했다.

ip -br -4 addr
ip route
docker node ls
docker info

Manager 3대 중 과반수가 계속 서로 통신하도록 순서를 잡았고, Worker는 cluster 재참여와 overlay 통신을 확인한 뒤 다음 node로 넘어갔다.

Manager 3대와 Worker 2대를 선택했다

Manager를 3대로 둔 이유는 Raft quorum이다. Manager가 2대면 한 대 장애 시 과반수를 유지하지 못하지만, 3대면 한 대가 내려가도 2대로 제어 영역을 유지할 수 있다.

애플리케이션 workload는 Worker 2대에 배치했다. 최종 상태에서 node 5대가 모두 Ready/Active였고 Manager는 Leader 1대와 Reachable 2대로 구성됐다.

애플리케이션은 replica 2개에 max_replicas_per_node: 1을 적용했다. 그 결과 두 task가 Worker 1과 Worker 2에 하나씩 배치됐다.

App replica 1 → Worker 1
App replica 2 → Worker 2
Endpoint mode → VIP

한 줄의 placement가 해결한 것과 해결하지 못한 것

처음에는 두 replica가 같은 Worker에 함께 배치될 수 있었다. spread: node.hostname은 선호 조건일 뿐 강제가 아니다. 나는 노드당 최대 replica를 1로 제한해 강제로 분산했다.

그러나 Worker가 정확히 두 대인 상태에서 이 정책은 update 방식에도 영향을 줬다. start-first는 기존 두 task가 자리를 차지한 상태에서 새 task를 먼저 띄울 Worker가 없다. 새 task가 Pending에 머물 수 있다.

그래서 현재 운영 정책은 stop-first, parallelism: 1이다. 한 task를 내린 뒤 같은 위치에 새 task를 올리고, 정상 확인 후 다음 task를 갱신한다. 순간적으로 가용 replica가 하나로 줄 수 있지만 현재 자원 조건에서는 예측 가능한 방식이다.

Worker 한 대가 장애 나면 남은 replica가 계속 응답한다. 다만 max_replicas_per_node: 1 때문에 남은 Worker에 두 번째 replica가 뜨지는 않는다. 2/2 용량까지 유지하려면 세 번째 Worker를 추가해야 한다.

Service DNS와 VIP를 연결점으로 사용했다

외부 connector와 NPM이 개별 container IP를 바라보게 하면 task가 재배치될 때마다 설정이 깨진다. 나는 meetback-edgemeetback-backend overlay network를 만들고 서비스 이름과 VIP를 연결점으로 사용했다.

Cloudflared, NPM, app은 필요한 overlay에만 참여한다. DB는 overlay에 직접 넣지 않고 Worker에서 VyOS를 거쳐 전용망의 MySQL로 접근하도록 했다.

Swarm에서 실제로 막혔던 지점

구축 중 다음 문제를 만났다.

  • Worker에서 docker service 관리 명령을 실행해 manager 권한 오류가 발생했다.
  • Private registry image를 Worker가 받지 못해 task가 Rejected 됐다.
  • NFS volume mount 권한 때문에 task가 시작되기 전 실패했다.
  • start-first와 노드당 1 replica 정책이 충돌했다.
  • Compose indentation이 깨져 의도한 deploy 정책이 반영되지 않았다.

문제를 볼 때 나는 docker service ls만 보지 않았다. docker service ps --no-trunc로 이전 task의 Rejected, Shutdown, error reason까지 확인했다. Swarm은 desired state를 맞추기 위해 task를 계속 새로 만들기 때문에 가장 최근 Running 행만 보면 원인을 놓칠 수 있다.

최종 검증

최종 캡처 시점에는 다음 상태를 확인했다.

  • Node 5/5 Ready
  • Manager quorum 정상
  • App 2/2 Running
  • App task가 두 Worker에 하나씩 배치
  • cloudflared global service 5/5
  • overlay network와 service VIP 존재
  • update order stop-first

Swarm을 구축하며 내가 배운 것은 orchestration이 장애를 없애는 기술이 아니라 원하는 상태로 되돌리는 기술이라는 점이다. Scheduler가 원하는 상태를 만들 수 있도록 registry 인증, storage, placement, update capacity를 함께 준비해야 한다.

다음 글에서는 main의 변경분을 일회용 MySQL로 검증하고 image를 build·push한 뒤 실제 stack에 반영한 과정을 정리한다.

실제 검증 화면

  1. 내가 최종 점검 때 실행한 docker node ls 결과다. Manager quorum과 Worker 상태를 한 화면에서 확인할 수 있다.
    01-swarm-nodes.png

  2. App replica가 두 Worker에 하나씩 올라간 상태와 서비스별 replica 수를 함께 캡처했다.
    02-swarm-services-placement.png

  3. meetback-backendmeetback-edge overlay, 그리고 app이 각 network에서 받은 VIP를 확인한 화면이다.
    23-overlay-vip.png