포스트

[이제와서 시작하는 Docker 마스터하기 - 고급편 #7] Docker Swarm 기초

[이제와서 시작하는 Docker 마스터하기 - 고급편 #7] Docker Swarm 기초

“이제와서 시작하는 Docker 마스터하기” Docker Swarm은 Docker의 네이티브 클러스터링 및 오케스트레이션 도구입니다. 여러 Docker 호스트를 하나의 가상 Docker 호스트로 만들어 컨테이너를 관리할 수 있습니다. Kubernetes보다 간단하면서도 강력한 기능을 제공합니다.

학습 목표

  • 🛠️ Docker Swarm의 개념과 아키텍처 이해
  • 🏛️ 클러스터 구성과 노드 관리
  • 🔄 서비스 배포와 업데이트
  • ⚖️ 로드 밸런싱과 고가용성
  • 🔒 보안과 모니터링

Docker Swarm이란?

Docker Swarm은 여러 Docker 호스트를 클러스터로 구성하고 관리하는 도구입니다:

Swarm vs Kubernetes 비교

특징 Docker Swarm Kubernetes
학습 곡선 🟢 낮음 🔴 높음
설치 복잡도 🟢 간단 🟡 복잡
기능 🟡 기본적 🟢 풀기능
생태계 🟡 제한적 🟢 방대함
Docker 통합 🟢 내장 🟡 플러그인

주요 기능

graph TB
    subgraph "Docker Swarm 기능"
        A[🏛️ 클러스터 관리]
        B[🔄 서비스 오케스트레이션]
        C[⚖️ 로드 밸런싱]
        D[🔒 보안 및 Secrets]
        E[🌐 Overlay 네트워크]
        F[📊 모니터링]
    end
    
    A --> G[클러스터링된<br/>컨테이너 환경]
    B --> G
    C --> G
    D --> G
    E --> G
    F --> G

Swarm 모드 시작하기

Swarm 클러스터 구조

graph TB
    subgraph "Manager Nodes"
        M1[Manager 1<br/>Leader]
        M2[Manager 2]
        M3[Manager 3]
    end
    
    subgraph "Worker Nodes"
        W1[Worker 1]
        W2[Worker 2]
        W3[Worker 3]
        W4[Worker 4]
    end
    
    M1 -.->|Raft 동기화| M2
    M2 -.->|Raft 동기화| M3
    M3 -.->|Raft 동기화| M1
    
    M1 -->|Task 할당| W1
    M1 -->|Task 할당| W2
    M1 -->|Task 할당| W3
    M1 -->|Task 할당| W4

1. Swarm 초기화

1
2
3
4
5
6
7
8
# Manager 노드에서 Swarm 초기화
docker swarm init --advertise-addr 192.168.1.100

# 출력 예시:
# Swarm initialized: current node (abcd1234) is now a manager.
# 
# To add a worker to this swarm, run the following command:
#     docker swarm join --token SWMTKN-1-xxxxx 192.168.1.100:2377

2. Worker 노드 추가

1
2
3
4
5
# Worker 노드에서 실행
docker swarm join --token SWMTKN-1-xxxxx 192.168.1.100:2377

# Manager 노드로 추가하려면
docker swarm join-token manager

3. 노드 확인

1
2
3
4
5
6
7
8
9
# 클러스터 노드 목록
docker node ls

# 노드 상세 정보
docker node inspect node-name

# 노드 역할 변경
docker node promote worker-node  # Worker를 Manager로
docker node demote manager-node   # Manager를 Worker로

서비스 생성과 관리

1. 첫 번째 서비스 생성

1
2
3
4
5
6
7
8
9
10
11
12
# 간단한 웹 서비스 생성
docker service create \
  --name web \
  --replicas 3 \
  --publish 80:80 \
  nginx:alpine

# 서비스 목록 확인
docker service ls

# 서비스 상세 정보
docker service ps web

2. 서비스 업데이트

1
2
3
4
5
6
7
8
9
10
11
12
13
# 레플리카 수 변경
docker service scale web=5

# 이미지 업데이트
docker service update \
  --image nginx:latest \
  web

# 롤링 업데이트 설정
docker service update \
  --update-parallelism 2 \
  --update-delay 10s \
  web

3. 서비스 제약 조건

1
2
3
4
5
6
7
8
9
# 특정 노드에만 배치
docker service create \
  --name db \
  --constraint node.role==manager \
  --constraint node.labels.disk==ssd \
  postgres:13

# 노드 라벨 추가
docker node update --label-add disk=ssd node1

Stack 배포

Stack vs Service 비교

graph LR
    subgraph "Service"
        S1[📦 단일 서비스]
        S2[직접 명령]
        S3[간단한 배포]
    end
    
    subgraph "Stack"
        ST1[📦📦📦 여러 서비스]
        ST2[YAML 파일]
        ST3[복잡한 배포]
        ST4[네트워크/볼륨 포함]
    end
    
    S1 --> D1[docker service create]
    ST1 --> D2[docker stack deploy]

1. docker-compose.yml 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# stack.yml
version: '3.8'

services:
  web:
    image: nginx:alpine
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
      placement:
        constraints:
          - node.role == worker
    networks:
      - webnet
    ports:
      - "80:80"

  app:
    image: myapp:latest
    deploy:
      replicas: 5
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
    networks:
      - webnet
      - backend
    environment:
      - NODE_ENV=production
      - DB_HOST=db

  db:
    image: postgres:13
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.db == true
      restart_policy:
        condition: any
    networks:
      - backend
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

  redis:
    image: redis:alpine
    deploy:
      replicas: 1
      update_config:
        parallelism: 1
        delay: 10s
    networks:
      - backend

networks:
  webnet:
    driver: overlay
  backend:
    driver: overlay
    internal: true

volumes:
  db-data:
    driver: local

secrets:
  db_password:
    external: true

2. Stack 배포

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Secret 생성
echo "mysecretpassword" | docker secret create db_password -

# Stack 배포
docker stack deploy -c stack.yml myapp

# Stack 목록
docker stack ls

# Stack 서비스 확인
docker stack services myapp

# Stack 제거
docker stack rm myapp

네트워킹

Swarm 네트워크 아키텍처

graph TB
    subgraph "Ingress Network"
        I1[Load Balancer]
        I2[Routing Mesh]
    end
    
    subgraph "Overlay Networks"
        O1[Frontend Network]
        O2[Backend Network<br/>Internal]
    end
    
    subgraph "Services"
        S1[Web Service]
        S2[API Service]
        S3[DB Service]
    end
    
    I1 --> I2
    I2 --> S1
    S1 -.-> O1
    S1 -.-> O2
    S2 -.-> O1
    S2 -.-> O2
    S3 -.-> O2
    
    style O2 fill:#ffcccc

1. Overlay 네트워크

1
2
3
4
5
6
7
8
9
10
11
12
# Overlay 네트워크 생성
docker network create \
  --driver overlay \
  --subnet 10.0.0.0/16 \
  --attachable \
  my-overlay

# 암호화된 Overlay 네트워크
docker network create \
  --driver overlay \
  --opt encrypted \
  secure-overlay

2. 서비스 디스커버리

1
2
3
4
5
6
7
8
9
10
11
# 서비스 간 통신
docker service create \
  --name backend \
  --network my-overlay \
  myapp:backend

docker service create \
  --name frontend \
  --network my-overlay \
  -e BACKEND_URL=http://backend:8080 \
  myapp:frontend

고가용성 구성

1. Manager 노드 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3개 이상의 홀수 Manager 권장
# Manager 1 (Leader)
docker swarm init --advertise-addr 192.168.1.100

# Manager 2
docker swarm join-token manager
# 생성된 토큰으로 조인

# Manager 3
docker swarm join-token manager
# 생성된 토큰으로 조인

# Quorum 확인
docker node ls

2. 서비스 가용성 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# high-availability.yml
version: '3.8'

services:
  web:
    image: nginx:alpine
    deploy:
      replicas: 6
      update_config:
        parallelism: 2
        delay: 10s
        failure_action: rollback
        monitor: 30s
        max_failure_ratio: 0.3
      rollback_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 120s
      placement:
        max_replicas_per_node: 2
        preferences:
          - spread: node.labels.zone

로드 밸런싱

1. Ingress 네트워크

1
2
3
4
5
6
7
8
9
10
11
12
13
# 서비스 생성 시 자동으로 로드 밸런싱
docker service create \
  --name web \
  --replicas 3 \
  --publish published=80,target=80,mode=ingress \
  nginx

# Host 모드 (로드 밸런싱 없음)
docker service create \
  --name web-host \
  --mode global \
  --publish published=8080,target=80,mode=host \
  nginx

2. 외부 로드 밸런서 연동

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# nginx.conf (외부 LB)
upstream docker_swarm {
    server swarm-node1:80;
    server swarm-node2:80;
    server swarm-node3:80;
}

server {
    listen 80;
    location / {
        proxy_pass http://docker_swarm;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

모니터링과 로깅

모니터링 스택 구성

flowchart LR
    subgraph "Data Collection"
        N1[Node Exporter]
        C1[cAdvisor]
        S1[Service Metrics]
    end
    
    subgraph "Storage"
        P[Prometheus]
    end
    
    subgraph "Visualization"
        G[Grafana]
        A[Alert Manager]
    end
    
    N1 -->|Node 메트릭| P
    C1 -->|Container 메트릭| P
    S1 -->|Service 메트릭| P
    
    P --> G
    P --> A
    
    G -->|Dashboard| U[사용자]
    A -->|Alert| U

1. 서비스 모니터링

1
2
3
4
5
6
7
8
9
10
# 서비스 로그
docker service logs web
docker service logs -f --tail 100 web

# 노드별 컨테이너 확인
docker node ps node1

# 전체 클러스터 상태
docker node ls
docker service ls

2. Prometheus 스택

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# monitoring-stack.yml
version: '3.8'

services:
  prometheus:
    image: prom/prometheus
    configs:
      - source: prometheus_config
        target: /etc/prometheus/prometheus.yml
    deploy:
      placement:
        constraints:
          - node.role == manager
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter
    deploy:
      mode: global
    networks:
      - monitoring

  cadvisor:
    image: gcr.io/cadvisor/cadvisor
    deploy:
      mode: global
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker:/var/lib/docker:ro
    networks:
      - monitoring

  grafana:
    image: grafana/grafana
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
    ports:
      - "3000:3000"
    networks:
      - monitoring

networks:
  monitoring:
    driver: overlay

configs:
  prometheus_config:
    file: ./prometheus.yml

보안

1. Secrets 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Secret 생성
docker secret create db_password password.txt
echo "mypassword" | docker secret create db_password -

# Secret 사용
docker service create \
  --name db \
  --secret db_password \
  -e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \
  postgres:13

# Secret 목록
docker secret ls

# Secret 제거
docker secret rm db_password

2. TLS 설정

1
2
3
4
5
6
7
8
9
10
11
# Swarm 통신 암호화 확인
docker swarm ca

# 인증서 로테이션
docker swarm ca --rotate

# 외부 CA 사용
docker swarm ca \
  --ca-cert /path/to/ca.crt \
  --ca-key /path/to/ca.key \
  --external-ca protocol=cfssl,url=https://ca.example.com

실전 예제: 마이크로서비스 배포

마이크로서비스 아키텍처

graph TB
    subgraph "External"
        U[사용자]
        LB[Load Balancer]
    end
    
    subgraph "API Gateway Layer"
        AG1[API Gateway 1]
        AG2[API Gateway 2]
        AG3[API Gateway 3]
    end
    
    subgraph "Service Layer"
        US1[User Service 1]
        US2[User Service 2]
        OS1[Order Service 1]
        OS2[Order Service 2]
        OS3[Order Service 3]
    end
    
    subgraph "Data Layer"
        UD[(User DB)]
        OD[(Order DB)]
        R[(Redis)]
        K[Kafka]
    end
    
    U --> LB
    LB --> AG1 & AG2 & AG3
    
    AG1 & AG2 & AG3 --> US1 & US2
    AG1 & AG2 & AG3 --> OS1 & OS2 & OS3
    
    US1 & US2 --> UD
    US1 & US2 --> R
    
    OS1 & OS2 & OS3 --> OD
    OS1 & OS2 & OS3 --> K
    
    style AG1 fill:#ccffcc
    style AG2 fill:#ccffcc
    style AG3 fill:#ccffcc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# microservices-stack.yml
version: '3.8'

services:
  api-gateway:
    image: myapp/api-gateway:latest
    deploy:
      replicas: 3
      update_config:
        parallelism: 1
        delay: 10s
      placement:
        constraints:
          - node.role == worker
    ports:
      - "80:8080"
    networks:
      - frontend
      - backend

  user-service:
    image: myapp/user-service:latest
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    networks:
      - backend
    environment:
      - DB_HOST=user-db
      - REDIS_HOST=redis

  order-service:
    image: myapp/order-service:latest
    deploy:
      replicas: 3
      placement:
        preferences:
          - spread: node.labels.zone
    networks:
      - backend
    environment:
      - DB_HOST=order-db
      - KAFKA_BROKERS=kafka:9092

  user-db:
    image: postgres:13
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.db == true
    networks:
      - backend
    volumes:
      - user-db-data:/var/lib/postgresql/data

  order-db:
    image: postgres:13
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.db == true
    networks:
      - backend
    volumes:
      - order-db-data:/var/lib/postgresql/data

  redis:
    image: redis:alpine
    deploy:
      replicas: 1
    networks:
      - backend

  kafka:
    image: confluentinc/cp-kafka:latest
    deploy:
      replicas: 1
    networks:
      - backend
    environment:
      - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181

  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    deploy:
      replicas: 1
    networks:
      - backend

networks:
  frontend:
    driver: overlay
  backend:
    driver: overlay
    internal: true

volumes:
  user-db-data:
  order-db-data:

트러블슈팅

문제 해결 체크리스트

문제 진단 명령어 해결 방법
🔴 서비스 시작 안됨 docker service ps --no-trunc 로그 확인, 이미지 검증
🟡 네트워크 통신 불가 docker network inspect 네트워크 재생성
🔵 노드 비정상 docker node ls 노드 재가입
🟣 로드 밸런싱 문제 docker service ps Ingress 네트워크 확인
⚫ 디스크 공간 부족 docker system df 클린업, 볼륨 이동

일반적인 문제 해결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 노드 상태 확인
docker node ls

# 서비스가 시작되지 않을 때
docker service ps web --no-trunc

# 네트워크 문제
docker network inspect overlay-network

# 노드 제거
docker node rm --force node-name

# Swarm 모드 해제
docker swarm leave --force

베스트 프랙티스

Swarm 클러스터 구성 가이드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 권장 구성
Production:
  Managers: 3 or 5 (홀수)
  Workers: 4+ (필요에 따라)
  
Development:
  Managers: 1
  Workers: 2+

# 노드 요구사항
Minimum:
  CPU: 2 cores
  RAM: 4GB
  Disk: 20GB SSD
  
Recommended:
  CPU: 4+ cores
  RAM: 8GB+
  Disk: 50GB+ SSD

프로덕션 체크리스트

  • 🔒 TLS 인증서 로테이션 설정
  • 📊 모니터링 스택 배포
  • 💾 백업 전략 수립
  • 🔄 롤링 업데이트 테스트
  • 🎯 로드 밸런서 구성
  • 🚨 알람 시스템 구축

마무리

Docker Swarm은 간단하면서도 강력한 오케스트레이션 도구입니다. 내장된 로드 밸런싱, 서비스 디스커버리, 롤링 업데이트 등의 기능으로 프로덕션 환경에서도 충분히 사용할 수 있습니다.

다음 편 예고

  • 일반적인 Docker 문제와 해결법
  • 디버깅 기법
  • 성능 최적화
  • 트러블슈팅 도구

Docker 문제를 현명하게 해결하는 방법을 배워봅시다! 🔧

📚 Docker 마스터하기 시리즈

🐳 기초편 (입문자용 - 5편)

  1. Docker란 무엇인가?
  2. Docker 설치 및 환경 설정
  3. 첫 번째 컨테이너 실행하기
  4. Docker 이미지 이해하기
  5. Dockerfile 작성하기

💼 실전편 (중급자용 - 6편)

  1. Docker 네트워크 기초
  2. Docker 볼륨과 데이터 관리
  3. Docker Compose 입문
  4. 멀티 컨테이너 애플리케이션
  5. Docker Hub 활용하기
  6. Docker 보안 베스트 프랙티스

🚀 고급편 (전문가용 - 9편)

  1. Docker 로그와 모니터링
  2. Docker로 Node.js 애플리케이션 배포
  3. Docker로 Python 애플리케이션 배포
  4. Docker로 데이터베이스 운영
  5. Docker 이미지 최적화
  6. Docker와 CI/CD
  7. Docker Swarm 기초 ← 현재 글
  8. 문제 해결과 트러블슈팅
  9. Docker 생태계와 미래
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.