나의 공부기록

[Docker] 07. Docker Swarm 본문

CS/Docker

[Docker] 07. Docker Swarm

나의 개발자 2025. 3. 25. 17:28

Docker Swarm

  • 여러 Docker 호스트를 클러스터로 묶어 고가용성 및 확장성을 제공하는 오케스트레이션 도구
    • 컨테이너 오케스트레이션 툴
      👉 Docker Swarm, k8s ...
    • 오케스트레이션 : 지휘자 + 다수의 연주자 ➡️ clustering
  • 여러 개의 컨테이너를 관리하는 기술(스케일링, 헬스체크, 트래픽 제어 = 배포)

✅ Docker VS Docker Compose VS Docker Swarm

Docker : 단일 호스트, 단일 컨테이너
Docker Compose : 단일 호스트, 여러 종류의 컨테이너
Docker Swarm : 다수의 호스트(manager, worker..), 여러 종류의 컨테이너

 

 

Docker Swarm 실습 - 기본 구성

  • 다른 호스트의 컨테이너끼리는 통신이 불가능하기 때문에
    ➡️ Overlay Network를 구성해서 통신이 가능하도록 해줌
더보기

1. 서버 생성

  • ubun-tem 3개 복제(clone)
    • manager(211.183.3.210) / worker1(211.183.3.220) / worker2(211.183.3.230)

2. Docker 설치

root@worker2:~# curl -fsSL https://get.docker.com -o get-docker.sh

root@worker2:~# ls
get-docker.sh  snap

root@worker2:~# chmod +x get-docker.sh

root@worker2:~# ./get-docker.sh 
# Executing docker install script, commit: 4c94a56999e10efcf48c5b8e3f6afea464f9108e
+ sh -c apt-get -qq update >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get -y -qq install ca-certificates curl >/dev/null
+ sh -c install -m 0755 -d /etc/apt/keyrings
+ sh -c curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" -o /etc/apt/keyrings/docker.asc
+ sh -c chmod a+r /etc/apt/keyrings/docker.asc
+ sh -c echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu focal stable" > /etc/apt/sources.list.d/docker.list
+ sh -c apt-get -qq update >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get -y -qq install docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-ce-rootless-extras docker-buildx-plugin >/dev/null

 

Docker Swarm - Manager(=Master Node) 구성

  • control plane ➡️ 
더보기

1. 클러스터 초기화

  • 해당 작업을 하면 control plane이 되는 것
  • 아무 옵션을 주지 않으면 default 값으로 초기화됨 
     ➡️ port : 2377
root@manager:~# docker swarm init
Swarm initialized: current node (0zmf33tzc9uoufogwuzt3itji) is now a manager.

# 🌟worker node가 되기 위한 명령어 명시
To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-043ptfe5y08fava3t5nx0cgdrayz9i63pmlk6zeb5z8u92az42-70lth11d2v49iud517u603sm5 211.183.3.210:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

 

➕ worker node 추가(하단의 "Docker Swarm - Worker Node 구성" 먼저 진행)

 

2.  cluster node 확인

  • HOSTNAME은 중복되면 안됨
root@manager:~# docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
0zmf33tzc9uoufogwuzt3itji *   manager    Ready     Active         Leader           28.0.2
ojwwis4dzo1kfpng0rmrmrt5x     worker1    Ready     Active                          28.0.2
ian8j5eibt513rqoxkuvzdwpp     worker2    Ready     Active                          28.0.2

 

Docker Swarm - Worker Node 구성

  • data plane ➡️ 실제 컨테이너가 생성되는 곳
더보기

1. Manager의 클러스터에 Join

  • worker1
root@worker1:~# docker swarm join --token SWMTKN-1-043ptfe5y08fava3t5nx0cgdrayz9i63pmlk6zeb5z8u92az42-70lth11d2v49iud517u603sm5 211.183.3.210:2377
This node joined a swarm as a worker.
  • worker2
root@worker2:~# docker swarm join --token SWMTKN-1-043ptfe5y08fava3t5nx0cgdrayz9i63pmlk6zeb5z8u92az42-70lth11d2v49iud517u603sm5 211.183.3.210:2377
This node joined a swarm as a worker.

Docker Swarm - docker service : 한 가지 종류의 컨테이너를 여러 개 생성

  • docker run으로 컨테이너를 생성하는 것과 비슷
더보기

💡Manager에서 실행

 

1. 서비스 생성 및 클러스터에 배포

# --replicas : 컨테이너 개수
# etoile0320/ipnginx : 이미지 이름
root@manager:~# docker service create --replicas 2 -p 7979:80 --name myweb oolralra/ipnginx
12cb80vnw9vn52r0h9osrml3g
overall progress: 2 out of 2 tasks 
1/2: running   
2/2: running   
verify: Service 12cb80vnw9vn52r0h9osrml3g converged

 

2. 서비스 목록 확인

root@manager:~# docker service ls
ID             NAME      MODE         REPLICAS   IMAGE                     PORTS
12cb80vnw9vn   myweb     replicated   2/2        oolralra/ipnginx:latest   *:7979->80/tcp

 

3. 서비스 배포 결과 확인

  • 로드밸런싱이 잘 되는 것을 확인 가능
  • 외부에서 들어오는 트래픽을 아래 그림과 같이 분산해서 처리함 - 개념적인 그림
  • 컨테이너 분산이 되어 있음을 확인 가능 - 실제 분산 확인
root@manager:~# docker service ps myweb
ID             NAME      IMAGE                     NODE      DESIRED STATE   CURRENT STATE                ERROR     PORTS
ij71cmtde56r   myweb.1   oolralra/ipnginx:latest   worker2   Running         Running about a minute ago             
r87h19qenruu   myweb.2   oolralra/ipnginx:latest   worker1   Running         Running about a minute ago

 

  • docker swarm을 하면 자동으로 overlay network를 구성한 것을 확인 가능
root@manager:~# docker network ls
NETWORK ID     NAME              DRIVER    SCOPE
720eb18977c9   bridge            bridge    local
b07f37b1b8d6   docker_gwbridge   bridge    local
e57d3909e5b8   host              host      local
9fwclx5zwhab   ingress           overlay   swarm # DRIVER : overlay ➡️ overlay network 생성
43cbf02a57ec   none              null      local

root@manager:~# docker network inspect ingress
[
    {
        "Name": "ingress",
        "Id": "9fwclx5zwhabkhi33i0qgqztl",
        "Created": "2025-03-25T01:22:54.524035513Z",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv4": true,
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            # 🌟 도커 네트워크 대역
            "Config": [
                {
                    "Subnet": "10.0.0.0/24",
                    "Gateway": "10.0.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": true,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "ingress-sbox": {
                "Name": "ingress-endpoint",
                "EndpointID": "702736f4896aa8713ccd1038a33bb95ae57f62ff389f47050a3598a098d93da5",
                "MacAddress": "02:42:0a:00:00:02",
                "IPv4Address": "10.0.0.2/24",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.driver.overlay.vxlanid_list": "4096"
        },
        "Labels": {},
        # 🌟 Peer를 통해 Overlay Network 자동 구성
        "Peers": [
            {
                "Name": "70b1307bad6a",
                "IP": "211.183.3.210"
            },
            {
                "Name": "1f5bff428822",
                "IP": "211.183.3.220"
            },
            {
                "Name": "d77ec580c606",
                "IP": "211.183.3.230"
            }
        ]
    }
]

 

4. 컨테이너 개수 증가 - 업데이트

  • 분산 배치된 것을 확인 가능
root@manager:~# docker service scale myweb=3
myweb scaled to 3
overall progress: 3 out of 3 tasks 
1/3: running   
2/3: running   
3/3: running   
verify: Service myweb converged

root@manager:~# docker service ps myweb
ID             NAME          IMAGE                     NODE      DESIRED STATE   CURRENT STATE                ERROR     PORTS
4xstysy6y5uu   myweb.1       oolralra/ipnginx:latest   manager   Running         Running 41 seconds ago                 
ij71cmtde56r    \_ myweb.1   oolralra/ipnginx:latest   worker2   Shutdown        Shutdown 35 seconds ago                
r87h19qenruu   myweb.2       oolralra/ipnginx:latest   worker1   Running         Running 10 minutes ago                 
7ak0ejk9yyo2   myweb.3       oolralra/ipnginx:latest   manager   Running         Running about a minute ago

 

Docker Swarm - docker stack deploy : 클러스터 내에서 애플리케이션 스택을 배포

  • docker compose up과 비슷
  • docker-compose.yml 파일 필요
더보기

1. docker-compose.yml 파일 생성

  • 기존의 compose파일에 추가된 내용 존재
    ➡️ 노드 배치, 컨테이너 수 등..
root@manager:~# mkdir stack_deploy
root@manager:~# cd stack_deploy/
root@manager:~/stack_deploy# vi docker-compose.yml

services:
  ip-nginx:
    image: oolralra/ipnginx
    deploy:
    # deploy : 배포
      replicas: 2
      # 컨테이너 수 : 2
      placement:
      # 비치 -  어떤 노드에 둘지
        constraints: [node.role != manager]
        # constraints : 제약 조건
        # node = host

 

2. docker stack deploy

  • 네트워크 명시 ❌ ➡️ 네트워크 자동 생성
# docker stack deploy -c <컴포즈파일> <stack이름>
root@manager:~/stack_deploy# docker stack deploy -c docker-compose.yml ip-stack

 

3. 결과 확인

  • manager node를 제외하고 분산 배치되었음
root@manager:~/stack_deploy# docker stack ps ip-stack
ID             NAME                  IMAGE                     NODE      DESIRED STATE   CURRENT STATE            ERROR     PORTS
o6rfo5qzd9iv   ip-stack_ip-nginx.1   oolralra/ipnginx:latest   worker2   Running         Running 24 seconds ago             
k9sskkn4rsvj   ip-stack_ip-nginx.2   oolralra/ipnginx:latest   worker1   Running         Running 24 seconds ago
  • 검증 - 컨테이너 개수 수정
# 🌟 컨테이너 개수 수정
root@manager:~/stack_deploy# vi docker-compose.yml 

services:
  ip-nginx:
    image: oolralra/ipnginx
    deploy:
    # deploy : 배포
      replicas: 4
      # 컨테이너 수 : 2
      placement:
      # 비치 -  어떤 노드에 둘지
        constraints: [node.role != manager]
        # constraints : 제약 조건
        # node = host
  • 검증 - docker stack 업데이트
root@manager:~/stack_deploy# docker stack deploy -c docker-compose.yml ip-stack
Since --detach=false was not specified, tasks will be created in the background.
In a future release, --detach=false will become the default.
Updating service ip-stack_ip-nginx (id: xcjcz9nizdjp9wbve4de2wzzy)
  • 검증 - stack 확인
    ➡️ manager는 제외하고 생성된 것을 확인 가능
root@manager:~/stack_deploy# docker stack ps ip-stack 
ID             NAME                  IMAGE                     NODE      DESIRED STATE   CURRENT STATE            ERROR     PORTS
o6rfo5qzd9iv   ip-stack_ip-nginx.1   oolralra/ipnginx:latest   worker2   Running         Running 4 minutes ago              
k9sskkn4rsvj   ip-stack_ip-nginx.2   oolralra/ipnginx:latest   worker1   Running         Running 4 minutes ago              
z3xt0g6qqw4f   ip-stack_ip-nginx.3   oolralra/ipnginx:latest   worker2   Running         Running 27 seconds ago             
9rtq6bb24g3d   ip-stack_ip-nginx.4   oolralra/ipnginx:latest   worker1   Running         Running 27 seconds ago

 

4. Docker Stack 삭제

root@manager:~/stack_deploy# docker stack rm ip-stack
Removing service ip-stack_ip-nginx
Removing network ip-stack_default

 

호스트에 띄워진 컨테이너 시각화하는 앱 배포

더보기

1. docker-compose.yml 생성

  • 각 호스트에 띄워진 컨테이너를 시각화하기 위한 정보는 manager node에 있기 때문에,
    manager node에 띄움
root@manager:~/stack_deploy# vi visual.yml

services:
  visual:
    image: dockersamples/visualizer
    ports:
    - '5656:8080'
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      placement: 
        constraints: [node.role == manager]

 

2. docker stack 배포

root@manager:~/stack_deploy# docker stack deploy -c visual.yml visual
Since --detach=false was not specified, tasks will be created in the background.
In a future release, --detach=false will become the default.
Creating network visual_default
Creating service visual_visual

 

 3. 결과 확인

  • 사이트 연결 불가
  • dockersamples/visualizer라는 이미지가 없기 때문에, health check를 해서 삭제와 생성을 반복함
root@manager:~/stack_deploy# docker stack ps visual
ID             NAME                  IMAGE                             NODE      DESIRED STATE   CURRENT STATE             ERROR                              PORTS
bpc2jiq7lxx9   visual_visual.1       dockersamples/visualizer:latest   manager   Ready           Preparing 2 seconds ago                                      
1j0grfc59uj6    \_ visual_visual.1   dockersamples/visualizer:latest   manager   Shutdown        Rejected 3 seconds ago    "No such image: dockersamples/…"   
j1ai4zm062sn    \_ visual_visual.1   dockersamples/visualizer:latest   manager   Shutdown        Rejected 8 seconds ago    "No such image: dockersamples/…"   
crm1vi173nsa    \_ visual_visual.1   dockersamples/visualizer:latest   manager   Shutdown        Rejected 13 seconds ago   "No such image: dockersamples/…"   
78dq9rr1ad7l    \_ visual_visual.1   dockersamples/visualizer:latest   manager   Shutdown        Rejected 18 seconds ago   "No such image: dockersamples/…"

 

➕ 실습 - 02에서 연달아서 실습할 예정

 

실습 - 01

문제

ECR 퍼블릭 갤러리의 이미지로 wordpress - mysql:8을 docker stack deploy 해보세요. 단, DB는 manager에 1개만, wordpress는 worker 노드에 2개 띄워보세요. 12345  포트로 접속 가능하도록 만드세요.
ECR 퍼블릭 갤러리 - https://gallery.ecr.aws/

풀이

더보기

0. 이미지 선택

1. compose 파일 생성

root@manager:~/stack_deploy# vi wp-my-stack.yml 

  wp:
    image: public.ecr.aws/docker/library/wordpress:php8.1-apache
    ports:
    - '12345:80'
    environment:
    - WORDPRESS_DB_HOST=dbdb
    - WORDPRESS_DB_USER=wpuser
    - WORDPRESS_DB_PASSWORD=1234
    - WORDPRESS_DB_NAME=wpdb
    depends_on:
    - dbdb
    deploy:
      replicas: 1
      placement:
        constraints: [node.role != manager]
  dbdb:
    image: public.ecr.aws/docker/library/mysql:8.0.41-bookworm
    restart: always
    environment:
    - MYSQL_DATABASE=wpdb
    - MYSQL_USER=wpuser
    - MYSQL_PASSWORD=1234
    - MYSQL_ROOT_PASSWORD=1234
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]

 

2. docker stack 배포

root@manager:~/stack_deploy# docker stack deploy -c wp-my-stack.yml wp-stack

 

3.  docker stack 확인

root@manager:~/stack_deploy# docker stack ps wp-stack
ID             NAME              IMAGE                                                   NODE      DESIRED STATE   CURRENT STATE           ERROR     PORTS
ogto33syadz6   wp-stack_dbdb.1   public.ecr.aws/docker/library/mysql:8.0.41-bookworm     worker1   Running         Running 8 seconds ago             
mzlj7pcmqhw9   wp-stack_wp.1     public.ecr.aws/docker/library/wordpress:php8.1-apache   worker2   Running         Running 4 seconds ago

 

4. 결과 확인

 

실습 - 02

문제

root@manager:~/stack_deploy# curl 61.254.18.30:5000/v2/_catalog
{"repositories":["nginx","visualizer"]}

위의 사설 저장소에서 visualizer를 가져와서  호스트에 띄워진 컨테이너 시각화하는 앱 배포 해보세요.

풀이

더보기

1. HTTP 설정 허용

root@worker2:~/stack_deploy# vi /etc/docker/daemon.json 

{
        "insecure-registries":["61.254.18.30:5000"]
}

root@worker2:~/stack_deploy# systemctl restart docker

 

2. compose 파일 생성

root@manager:~/stack_deploy# vi visual.yml 

services:
  visual:
    image: 61.254.18.30:5000/visualizer
    ports:
    - '5656:8080'
    volumes:
    - /var/run/docker.sock:/var/run/docker.sock
    deploy:
      placement:
        constraints: [node.role == manager]

 

3. docker stack 배포

root@manager:~/stack_deploy# docker stack deploy -c visual.yml visual
Since --detach=false was not specified, tasks will be created in the background.
In a future release, --detach=false will become the default.
Creating network visual_default
Creating service visual_visual

 

 

 

4. 결과 확인

 

실습 - 03

문제

https://github.com/pcmin929/mon이라는 레포지토리에
간단한 텔레그래프 설정파일과 그라파나 대시보드 파일이 있습니다.
그라파나이미지 grafana/grafana를 제 사설저장소 61.254.18.30:5000/grafana로 올리겠습니다.
텔레그래프이미지 telegraf:1.3을 제 사설저장소 61.254.18.30:5000/telegraf:1.3로 올리겠습니다.
텔레그래프이미지 influxdb:1.2를 제 사설저장소 61.254.18.30:5000/influxdb:1.2로 올리겠습니다.
텔레그래프 설정파일은 /etc/telegraf/telegraf.conf에 위치해야 합니다.
또한, 텔레그래프 설정파일을 보면 아시겠지만
vm_metrics와 docker_metrics라는 두 개의 데이터베이스에 수집된 자료를 저장시킨다.

위 내용을 토대로 docker stack deploy를 해서
grafana를 통해 각 docker swarm 호스트 및 컨테이너의 정보를 시각화해 보세요.

그라파나 대시보드는 dashboard.json의 내용을 복붙 해서 넣으시면 됩니다.
➡️ https://github.com/pcmin929/mon/blob/main/dashboard.json

풀이

더보기

1. 파일 다운로드

root@manager:~/stack_deploy# git clone https://github.com/pcmin929/mon

 

2. 이미지 pulling

  • 만약에 워커노드에 컨테이너가 띄워진다면, 각 워커노드들 전부 사설저장소에 대한 insecure 설정이되어 있어야 함
root@worker2:~/stack_deploy# vi /etc/docker/daemon.json 

{
        "insecure-registries":["61.254.18.30:5000"]
}

 

3. compose 파일 생성

  • volumes 옵션을 사용하려면 해당 디렉토리와 파일이 실행되는 호스트에 존재해야 함
    ➡️ configs 옵션(설정파일을 컨테이너에 삽입 가능)을 사용하는 것을 추천
  • 컨테이너를 호스트에 마운트하기 위해서는 해당 컨테이너가 띄워지는 호스트에 마운트가 된다.
    ➡️ manager 노드에 파일이 존재해도, 워커노드에 컨테이너가 띄워진다면 manager 노드에 있는 파일에 접근불가 (telegraf에서 manager에 mount를 바로 할 수 없어서, worker1과 worker2에 mount point가 존재해야 한다.)
    ➡️ 워커노드에도 파일을 복사하거나 접근 가능하게 해야한다.
root@manager:~/stack_deploy# vi grafana.yml 

version: '3.8'
  
services:
  grafana:
    image: 61.254.18.30:5000/grafana
    volumes:
      - grafana_data:/var/lib/grafana
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - "3000:3000"
    deploy:
      placement:
        constraints:
          - node.role == manager

  influx:
    image: 61.254.18.30:5000/influxdb:1.2
    deploy:
      placement:
        constraints:
          - node.role == manager
    depends_on:
      - telegraf
    volumes:
      - influx_data:/var/lib/influxdb
      - /var/run/docker.sock:/var/run/docker.sock
    ports:
      - "8086:8086"

  telegraf:
    image: 61.254.18.30:5000/telegraf:1.3
    volumes:
      - /root/stack_deploy/mon/telegraf.conf:/etc/telegraf/telegraf.conf:ro
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      - influx
    deploy:
      mode: global
    restart: always

volumes:
  grafana_data:
  influx_data:

 

➕ configs 옵션

  • 해당 옵션을 사용하면 각 호스트마다 볼륨 구성을 하지 않아도 된다.

 

4. telegraf.conf 파일 수정

root@worker1:~/stack_deploy# vi mon/telegraf.conf 

[global_tags]
environment="swarm"

# Read metrics about CPU usage
[[inputs.cpu]]
  percpu = false
  totalcpu = true
  fieldpass = [ "usage*" ]
  name_suffix = "_vm"

# Read metrics about disk usagee
[[inputs.disk]]
  interfaces = [ "eth0" ]
  fielddrop = [ "icmp*", "ip*", "tcp*", "udp*" ]
  name_suffix = "_vm"

# Read metrics about memory usage
[[inputs.mem]]
  name_suffix = "_vm"

# Read metrics about swap memory usage
[[inputs.swap]]
  name_suffix = "_vm"

# Read metrics about system load & uptime
[[inputs.system]]
  name_suffix = "_vm"

# Read metrics from docker socket api
[[inputs.docker]]
  endpoint = "unix:///var/run/docker.sock"
  container_names = []
  name_suffix = "_docker"

# 데이터베이스 컨테이너 서비스명 수정
[[outputs.influxdb]]
  database = "vm_metrics"
  urls = ["http://influx:8086"]
  namepass = ["*_vm"]

[[outputs.influxdb]]
  database = "docker_metrics"
  urls = ["http://influx:8086"]
  namepass = ["*_docker"]

 

5. docker stack 배포

root@manager:~/stack_deploy# docker stack deploy -c grafana.yml monitor

 

6. 데이터베이스 생성

  • 경로는 <서비스명> 사용
    ➡️ 내부에서 찾아가는 것

 

7. Dashboard 생성

 

8. 결과 확인