Docker Swarm이란?
Docker Swarm은 Docker에 내장된 컨테이너 오케스트레이션 도구입니다. 여러 대의 Docker 호스트를 하나의 클러스터로 묶어 서비스를 배포하고 관리합니다. Kubernetes보다 설정이 간단하고, Docker CLI만으로 운영할 수 있어 중소 규모 서비스에 적합합니다.
비유하면, 단일 Docker 호스트가 한 명의 요리사라면 Swarm은 여러 요리사가 협력하는 주방입니다. 주방장(매니저 노드)이 주문(서비스)을 배분하고, 요리사(워커 노드)가 음식(컨테이너)을 만듭니다. 한 요리사가 아프면 다른 요리사가 대신합니다.
| 구성 요소 | 역할 |
|---|---|
| Manager 노드 | 클러스터 관리, 스케줄링, 서비스 정의 저장 |
| Worker 노드 | 컨테이너 실행 |
| Service | 배포 단위 (이미지 + 복제 수 + 네트워크 설정) |
| Task | 서비스의 개별 컨테이너 인스턴스 |
| Stack | 여러 서비스를 묶은 애플리케이션 단위 |
클러스터 초기화
Swarm 클러스터를 구성하는 과정입니다.
# === 매니저 노드에서 실행 ===
# Swarm 초기화 (현재 호스트가 매니저)
docker swarm init --advertise-addr 192.168.1.10
# Swarm initialized: current node (abc123) is now a manager.
#
# To add a worker to this swarm, run the following command:
# docker swarm join --token SWMTKN-1-xxx 192.168.1.10:2377
#
# To add a manager to this swarm, run 'docker swarm join-token manager'
# 워커 조인 토큰 확인
docker swarm join-token worker
# SWMTKN-1-xxx...
# 매니저 조인 토큰 확인 (고가용성을 위해 매니저 3대 권장)
docker swarm join-token manager
# === 워커 노드에서 실행 ===
docker swarm join --token SWMTKN-1-xxx 192.168.1.10:2377
# This node joined a swarm as a worker.
# === 매니저 노드에서 클러스터 상태 확인 ===
docker node ls
# ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
# abc123 * manager-1 Ready Active Leader 24.0.7
# def456 worker-1 Ready Active 24.0.7
# ghi789 worker-2 Ready Active 24.0.7
고가용성을 위해 매니저 노드는 홀수 개(3, 5, 7)로 구성합니다. Raft 합의 알고리즘으로 리더를 선출하며, 과반수 이상의 매니저가 살아있어야 클러스터가 정상 운영됩니다.
서비스 배포
Swarm에서 컨테이너는 서비스 단위로 배포합니다. docker service 명령어로 관리합니다.
# 서비스 생성 (Nginx 웹서버 3대)
docker service create \
--name web \
--replicas 3 \
--publish 80:80 \
--update-delay 10s \
--update-parallelism 1 \
--restart-condition on-failure \
nginx:alpine
# 서비스 목록 확인
docker service ls
# ID NAME MODE REPLICAS IMAGE
# xyz123 web replicated 3/3 nginx:alpine
# 서비스 상세 상태 (각 태스크가 어느 노드에서 실행 중인지)
docker service ps web
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# aaa111 web.1 nginx:alpine manager-1 Running Running 2 minutes ago
# bbb222 web.2 nginx:alpine worker-1 Running Running 2 minutes ago
# ccc333 web.3 nginx:alpine worker-2 Running Running 2 minutes ago
# 서비스 로그 확인 (모든 태스크의 로그 통합)
docker service logs -f web
# 서비스 스케일링 (3대 → 5대)
docker service scale web=5
# web scaled to 5
# overall progress: 5 out of 5 tasks
# 서비스 상세 정보
docker service inspect --pretty web
Swarm의 라우팅 메시(Routing Mesh) 덕분에 클러스터의 어떤 노드에 요청해도 서비스에 접근할 수 있습니다. 예를 들어 web 서비스가 worker-1과 worker-2에서만 실행 중이더라도, manager-1의 80번 포트로 요청하면 자동으로 적절한 노드로 라우팅됩니다.
롤링 업데이트
서비스 중단 없이 이미지를 업데이트하는 과정입니다.
# 서비스 이미지 업데이트 (롤링 업데이트)
docker service update \
--image nginx:1.25-alpine \
--update-delay 15s \
--update-parallelism 1 \
--update-failure-action rollback \
--update-order start-first \
web
# 업데이트 진행 상황 확인
docker service ps web
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
# ddd444 web.1 nginx:1.25-alpine manager-1 Running Running 10 seconds ago
# aaa111 \_web.1 nginx:alpine manager-1 Shutdown Shutdown 15 seconds ago
# bbb222 web.2 nginx:alpine worker-1 Running Running 5 minutes ago
# ccc333 web.3 nginx:alpine worker-2 Running Running 5 minutes ago
# 업데이트 실패 시 수동 롤백
docker service rollback web
# web rolled back
# 롤백 상태 확인
docker service inspect --pretty web
# UpdateStatus:
# State: rollback_completed
업데이트 옵션 설명입니다.
| 옵션 | 설명 |
|---|---|
--update-parallelism 1 | 한 번에 1개 태스크씩 업데이트 |
--update-delay 15s | 각 태스크 업데이트 사이 15초 대기 |
--update-failure-action rollback | 실패 시 자동 롤백 |
--update-order start-first | 새 태스크를 먼저 시작한 후 기존 태스크 종료 |
스택 배포
docker stack은 Docker Compose 파일 형식으로 여러 서비스를 한 번에 배포합니다.
# stack.yml — 웹 애플리케이션 스택
version: "3.8"
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
api:
image: my-registry.com/my-app:latest
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/myapp
networks:
- frontend
- backend
deploy:
replicas: 3
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
update_config:
parallelism: 1
delay: 15s
order: start-first
db:
image: postgres:16-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- backend
deploy:
replicas: 1
placement:
constraints:
# DB는 매니저 노드에서만 실행 (볼륨 일관성)
- node.role == manager
redis:
image: redis:7-alpine
networks:
- backend
deploy:
replicas: 1
networks:
frontend:
driver: overlay
backend:
driver: overlay
internal: true
volumes:
pgdata:
# 스택 배포
docker stack deploy -c stack.yml myapp
# 스택 목록
docker stack ls
# NAME SERVICES ORCHESTRATOR
# myapp 4 Swarm
# 스택의 서비스 확인
docker stack services myapp
# ID NAME MODE REPLICAS IMAGE
# aaa myapp_nginx replicated 2/2 nginx:alpine
# bbb myapp_api replicated 3/3 my-registry.com/my-app:latest
# ccc myapp_db replicated 1/1 postgres:16-alpine
# ddd myapp_redis replicated 1/1 redis:7-alpine
# 스택의 모든 태스크 확인
docker stack ps myapp
# 스택 업데이트 (stack.yml 수정 후 같은 명령 실행)
docker stack deploy -c stack.yml myapp
# 스택 삭제
docker stack rm myapp
노드 관리
클러스터 노드의 상태와 역할을 관리하는 방법입니다.
# 노드 상태 확인
docker node ls
# 노드를 유지보수 모드로 전환 (해당 노드의 태스크가 다른 노드로 이동)
docker node update --availability drain worker-1
# worker-1의 모든 태스크가 다른 노드로 재배치됨
# 유지보수 완료 후 활성화
docker node update --availability active worker-1
# 노드에 라벨 추가 (배치 제약 조건에 사용)
docker node update --label-add zone=ap-northeast-2a worker-1
docker node update --label-add ssd=true worker-2
# 라벨 기반 배치 (SSD 노드에서만 DB 실행)
docker service create \
--name db \
--constraint 'node.labels.ssd == true' \
postgres:16-alpine
# 노드를 Swarm에서 제거
# 워커 노드에서 실행:
docker swarm leave
# 매니저에서 제거:
docker node rm worker-1
헬스체크와 서비스 디스커버리
Swarm은 내장 헬스체크와 로드 밸런싱을 제공합니다.
# Dockerfile에 헬스체크 추가
FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm ci --omit=dev
# 30초마다 헬스체크, 5초 타임아웃, 3회 실패 시 unhealthy
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
USER node
EXPOSE 3000
CMD ["node", "server.js"]
# 서비스 생성 시 헬스체크 설정
docker service create \
--name api \
--replicas 3 \
--health-cmd "wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1" \
--health-interval 30s \
--health-timeout 5s \
--health-retries 3 \
my-app:latest
# 서비스 상태에서 헬스체크 결과 확인
docker service ps api
# healthy 태스크만 트래픽을 받음
실전 팁
- 매니저 노드 수: 프로덕션에서는 매니저를 3대 이상(홀수) 구성하세요. 1대면 단일 장애점(SPOF)이 되고, 2대면 1대 장애 시 합의를 달성할 수 없습니다. 3대면 1대 장애를 견딥니다.
- 매니저 노드의 워크로드: 매니저 노드에서도 태스크를 실행할 수 있지만, 대규모 클러스터에서는 매니저를
drain모드로 설정하여 관리 작업에만 집중시키는 것이 안정적입니다. - 시크릿 관리:
docker secret으로 암호화된 시크릿을 Raft 로그에 저장하고, 컨테이너의/run/secrets/경로로 마운트합니다. 환경변수보다 안전합니다. - 롤링 업데이트 전략:
--update-order start-first를 사용하면 새 태스크가 정상 시작된 후 기존 태스크를 종료하므로 다운타임이 발생하지 않습니다.--update-failure-action rollback으로 실패 시 자동 롤백을 설정하세요. - Swarm vs Kubernetes: 10대 미만의 노드, Docker Compose에 익숙한 팀, 빠른 설정이 필요한 경우 Swarm이 적합합니다. 대규모 클러스터, 복잡한 스케줄링, 풍부한 에코시스템이 필요하면 Kubernetes를 선택하세요.