MeetBack 2026.08.23 5 min read

Manager 3대와 Worker 2대로 Docker Swarm을 구성한 이유

Manager 3대와 Worker 2대로 Swarm을 구성했다. quorum, Service DNS/VIP, connector 배치를 확인하고 실제 앱 배포에서 남은 검증을 정리했다.

프로젝트 기간: 2026-08-18 ~ 2026-09-02
글 범위: Manager 3대·Worker 2대 Docker Swarm 구성

MeetBack application을 여러 server에서 실행하기 위해 Docker Swarm을 선택했다. Kubernetes도 검토했지만 프로젝트 기간과 팀이 운영할 범위를 생각하면 Swarm이 맞겠다고 판단했다. image, service, secret, rolling update와 overlay network를 한 환경에서 다룰 수 있다는 점을 봤다.

cluster는 manager 3대와 worker 2대로 구성했다.

Manager 1: Leader
Manager 2: Reachable
Manager 3: Reachable
Worker 1: Application workload
Worker 2: Application workload

Manager를 홀수로 구성한 이유

Swarm manager는 Raft 합의로 cluster state를 관리한다. manager 1대는 단순하지만 장애에 취약하고, 2대는 한 대가 실패하면 과반수가 사라진다. 3대는 한 대 장애까지 quorum을 유지할 수 있다.

manager를 더 늘리면 quorum 통신과 관리할 대상도 함께 늘어난다. 당시 장비 규모에서는 3대로 한 대 장애를 견디는 구성을 선택했다.

manager node를 제어 전용으로 완전히 분리하지는 못했다. application service는 worker를 우선하도록 placement constraint를 계획했고, NPM처럼 local volume에 의존하는 service는 특정 manager에 고정했다. resource reservation과 장애 시 재배치는 실제 application 배포 후 확인할 항목으로 남겼다.

Overlay network와 Service DNS/VIP

Swarm service는 task가 어느 node에 있든 overlay network를 통해 통신할 수 있다. 나는 edge용 overlay network를 만들고 임시 Nginx service를 배포해 service name으로 접근되는지 확인했다.

client service
  → service DNS name
  → Swarm VIP
  → running task

여기서 VIP는 virtual IP를 통해 살아 있는 task로 연결을 분산하는 역할을 한다. application container의 IP를 직접 고정하거나 특정 worker 주소를 origin으로 쓰지 않아도 된다. Cloudflare Tunnel과 NPM도 최종적으로 application service name/VIP를 바라보도록 설계했다.

VIP가 있어도 HTTP session, WebSocket subscription과 Spring SimpleBroker state는 각 JVM에 남는다. replica만 늘리면 서로 다른 JVM에 연결된 사용자가 같은 이벤트를 받지 못할 수 있었다. 그래서 별도의 Redis Pub/Sub 공통 module을 만들었다.

Global service와 Replicated service를 구분했다

cloudflared connector와 cAdvisor는 각 Swarm node에 하나씩 필요하다. 이들은 global mode로 배포했다. node가 cluster에 추가되면 해당 조건을 만족하는 node마다 task가 생기고, 제거되면 함께 사라진다.

NPM은 persistent data와 관리 UI를 가진 단일 instance로 두고 특정 node에 placement했다. local bind mount를 사용하므로 task가 다른 node로 이동해도 같은 data가 따라오지는 않는다. 예비 edge 경로에서 사용하는 단일 component로 범위를 정했다.

실제 application은 replicated service로 여러 task를 배치할 계획이다. 여기에는 다음 정책이 추가로 필요하다.

  • replica 수와 worker placement
  • healthcheck
  • update parallelism과 delay
  • rollback configuration
  • CPU·memory limit/reservation
  • NFS volume과 Docker Secret

application 개발이 끝나지 않았기 때문에 이 부분은 아직 검증 완료로 표시하지 않았다.

Worker에서 관리 명령이 실패한 이유

cloudflared service 상태를 확인하려고 worker에서 docker service ps와 docker service logs를 실행했을 때 다음 오류가 발생했다.

This node is not a swarm manager.
Worker nodes can't be used to view or modify cluster state.

확인해 보니 service 배포 실패가 아니라 worker에서 관리 명령을 실행한 문제였다. worker는 task를 실행하고 cluster 전체 service state 조회와 변경은 manager가 담당한다.

나는 manager node로 이동해 다음을 확인했다.

docker service ls
docker service ps <service>
docker service logs <service>
docker node ls

개별 worker의 container 상태는 docker ps와 local log로 확인했다. cluster 전체 service 상태를 확인할 때는 manager에서 명령을 실행하도록 구분했다.

IP 변경 중 quorum을 지킨 방법

구축 도중 모든 node의 service network 주소를 바꿔야 했다. manager 3대를 동시에 변경하면 Raft quorum을 잃을 수 있기 때문에 한 대씩 진행했다.

각 manager 변경 뒤에는 다음 상태를 확인한 후 다음 node로 넘어갔다.

  • node Status=Ready
  • manager Availability=Active
  • leader 1대와 reachable manager 2대
  • overlay network 통신
  • 기존 service task 상태

worker는 drain 후 변경하고 새 주소로 재가입했다. 이때 Prometheus target과 NFS export 같은 IP 종속 설정도 함께 갱신했다.

Container metric을 위한 cAdvisor 배포

host resource만으로는 container별 CPU와 memory 사용량을 알기 어렵다. cAdvisor를 Docker host 전체에 배포하고 Prometheus가 각 endpoint를 scrape하도록 했다.

monitoring server는 Swarm member가 아니지만 Docker container를 실행하므로 별도로 cAdvisor를 구성했다. 결과적으로 Swarm 5개 node와 monitoring host의 container metric을 한 dashboard에서 볼 수 있게 했다.

/dev/kmsg, Docker root directory와 cgroup v2 compatibility를 사전 확인했고, endpoint를 host의 service address에 bind했다. 최종 접근 정책은 firewall 단계에서 monitoring server만 허용하도록 계획했다.

검증과 남은 과제

이 글을 작성할 때는 manager quorum, worker ready 상태, overlay 양방향 통신, 임시 service DNS/VIP 응답, global connector와 cAdvisor task 배치까지 확인한 상태였다.

실제 MeetBack application에서는 아래 작업이 남아 있었다.

  • Spring Boot image를 replicated service로 배포
  • NFS mount와 application user 권한 확인
  • Secret 주입
  • task 강제 종료 후 다른 worker로 재배치되는지 확인
  • rolling update 중 WebSocket과 session 영향 확인
  • 잘못된 image 배포 뒤 rollback 확인

Swarm에서 service discovery, desired state, secret과 update policy를 함께 관리할 기반을 만들었다. VIP로 공유되지 않는 application state는 Redis 공통 module로 다루도록 설계했다. 실제 업무 기능과 연결한 뒤 재배치와 업데이트 시험을 이어갈 계획이었다.

SEARCH JOURNAL

기록 검색

글 제목, 요약, 태그에서 찾습니다.

검색을 준비하고 있어요.

검색어를 입력해 주세요.

    Ctrl/⌘ K 검색 Esc 닫기 ↑ ↓ 결과 이동