Docker Swarm 가이드 #2 (Stack)

 

1. Docker Stack


Docker Stack = Swarm을 파일(yaml) 레벨에서 관리하는 기능

 

Docker Swarm 클러스터의 초기 구축은 여러모로 귀찮은 작업들을 수반합니다. 터미널에서 직접 입력해야 하는 docker swarm/service ~ 명령어도 많구요.

 

Swarm뿐만 아니라, 단일 애플리케이션(ex., nginx)를 Docker 환경에 띄울 때도 마찬가지였습니다. 하지만 docker-compose라는 기능 덕에 여러 컨테이너를 하나의 설정 파일(yaml)에 정의해두고 여러 번 반복적으로 돌려 쓸 수 있었죠.

 

마찬가지로, Docker service에 관한 모든 요소를 yaml 파일에 담아 관리할 수 있는 ‘docker stack‘ 이라는 기능이 존재합니다.Service*를 구성할 컨테이너 이미지, 네트워크 연결 방식, 공유 스토리지인 *Volume, 그리고 보안 정보인 Secret 등의 요소들을 한 번에 세팅할 수 있는 것입니다.

 

2. docker-stack.yml 파헤쳐보기


빌드 옵션

.yml 파일 안에 작성하는 설정 데이터의 유형은 크게 5가지가 존재합니다.

version:
services:
networks:
volumes:
secrets: 
  • version: 지원하는 yaml 파일의 버전
  • services: 구동시킬 Service의 리스트
  • networks: 클러스터에서 사용할 네트워크의 종류와 유형(bridge, overlay, host network 등)을 정의
  • volumes: Service끼리 공유할 수 있는 저장공간
  • secrets: 각종 보안 파라미터를 저장하는 파일 경로를 명시

 

2-1. Version

docker-compose와 동일하게 Yaml 파일의 syntax 버전을 상단에 명시합니다. 현재 설치된 Docker Engine과 호환되는 version 정보를 아래 링크에서 확인하세요.

version: "3.9"

https://docs.docker.com/compose/compose-file/compose-file-v3/

 

Compose file version 3 reference

Find a quick reference for Docker Compose version 3, including Docker Engine compatibility, memory limitations, and more.

docs.docker.com

 

 

2-2. Services

클러스터에서 동작할 Service의 이름과, Service를 구성하는 주된 컨테이너 이미지, 컨테이너 실행 환경 설정 정보 등을 명시하는 영역입니다.

services:
  redis:
    image: redis:alpine
    networks:
      - frontend
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

    db:
    image: postgres:9.4
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
    environment:
      - POSTGRES_HOST_AUTH_METHOD=trust
    deploy:
      placement:
        constraints: [node.role == manager]

...

각 service의 하위 요소에는 다음과 같은 것들이 존재합니다.

  • image: 빌드하려는 컨테이너 이미지
  • deploy: Swarm 내에서 Service를 배포하는 규칙 정의
  • environment: 컨테이너 이미지 별로 빌드 시에 필요한 초기 설정 정보
  • networks: Service에서 사용할 네트워크 정보
  • volumes: Service 동작 시에 사용할 공유 스토리

 

대체로 세부 구성이 docker-compose 때와 크게 다르지 않은 것을 알 수 있습니다. 단 하나, ‘deploy’만 제외한다면 말이죠. 설명에도 나와 있듯이, deploy는 Swarm 클러스터 상에서 Service 스케줄링 방식을 결정짓는 영역이기 때문에 deploy 옵션들을 잘 알아둬야 겠죠.

 

Deploy의 세부 옵션들

*mode:* 컨테이너를 모든 Swarm Node마다 하나씩 배치하는 옵션(global)과 일정 개수만큼 replica를 만들어서 몇몇 Node에게 자동 분배하는 옵션(replicated)가 있습니다.

deploy:
  mode: global

 

placement: 컨테이너가 배치될 Node*의 제약 조건을 붙입니다. 아래 예시는 *Manager 역할을 부여받은 Node에게만 작업을 할당하는 경우에 사용되는 배치 조건입니다.

deploy:
  placement:
    constraints: [node.role == manager]

 

replicas: 해당 Service에 대해 배포할 사본(replica)의 개수입니다. 앞서 replicated mode와(default가 replicated이긴 합니다) 동시에 활용하는 옵션입니다.

deploy:
  mode: replicated
  replicas: 3

 

resources: Service가 사용할 컴퓨팅 자원(CPU, 메모리 등)의 제한을 설정합니다. 아래 예시에서는 CPU 코어 0.5개(싱글 코어의 절반만큼)와 50MB 만큼의 메모리 자원을 최대치로 제한하고 있습니다.

deploy:
  resources:
    limits:
      cpus: '0.50'
      memory: 50M

 

restart_policy: Service*가 언제 재실행되는지, 또 어떤 방식으로 재실행할 것인지를 정의합니다. 예를 들어 *Service 최초 배포가 실패했을 경우, 5초마다 간격을 두어 최대 3번 재시도를 하도록 설계하려면 아래와 같이 옵션을 설정해두면 됩니다.

deploy:
  restart_policy:
    condition: on-failure
        delay: 5s
    max_attempts: 3

 

update_config: Service 업데이트를 진행하는 방식을 정의합니다. 하위 옵션인 parallelism은 업데이트가 동시에 이뤄지는 컨테이너의 개수를 의미합니다.

deploy:
  update_config:
    parallelism: 2
    delay: 10s

 

2-3. Networks

전체 클러스터에서 사용하는 네트워크 리스트입니다. services: 영역에서 컨테이너가 속해 있을 네트워크를 정의합니다.

networks:
  frontend:
  backend:
        driver: default
    config:
      - subnet: "172.16.238.0/24"

 

Swarm 클러스터에서 사용할 수 있는 네트워크 드라이버(driver) 유형 크게 Bridge, Overlay, host 세 가지가 존재합니다.

 

Bridge Network

기본 (default) 네트워크 옵션입니다. 같은 Bridge Network에 속한 Service끼리 통신이 가능할 뿐더러, 외부에서 들어오는 트래픽도 포트 포워딩을 통해 수용할 수 있습니다.

networks:
  mynet:
        driver: default

 

클러스터 운영 시에 Bridge Network를 대체로 1개 보다는 많이 구성합니다. 각 Service의 역할을 고려하여 커뮤니케이션이 필요한 Service끼리 효율적으로 묶어 줄 수 있기 때문입니다. 쉽게 이해해보자면 고객사 A와 B 사이의 연락망을 구축할 것 같으면 대표 번호를 사용하지, 굳이 양측 구성원 전체 전화번호부를 공유하지는 않는 것처럼 말이죠.

 

Bridge Network 내에서도 API 등을 이용해 외부와 소통하는 Service들은 따로 있습니다. 웹 플랫폼을 Swarm 클러스터로 운영하고자 한다면, 아래와 같이 프론트(frontend)와 백엔드(backend) 네트워크를 나누는 시도가 일반적일 것입니다.

services:
  redis:
    image: redis:alpine
    networks:
      - frontend
    ...

    db:
    image: postgres:9.4
    networks:
      - backend
        ...

networks:
  frontend:
  backend: 

 

Overlay Network

Overlay NetworkSwarm 클러스터에 속하는 모든 Node간의 통신을 보장합니다. 실제로 각 Node는 물리적으로 다른 네트워크 대역(ex., 172.31.x.x, 192.168.x.x)에 속하지만, Docker 엔진은 가상 터널(ex., 10.0.0.0/24)을 만들어 사설 IP로 Node간 네트워킹을 활성화합니다.

networks:
  mynet:
        driver: overlay

 

Host Network

Docker에서 제공하는 172.31.x.x와 같은 사설 IP 체계가 아니라, 그냥 Service가 실행되는 Node의 IP 주소를 그대로 사용하는 경우를 뜻합니다. 말 그대로 localhost에다가 애플리케이션을 올리는 셈입니다.

networks:
  hostnet:
    external: true
    name: host

 

그러나, Service에 발생한 보안 취약점으로 인해 공격이 발생하는 경우에는 호스트의 IP가 그대로 노출되어 해당 Node의 네트워크까지 직접적으로 피해를 입을 수 있기 때문에 주의가 필요합니다.

 

2-4. Volumes

Service끼리 공유할 **Volume*** 자원을 설정하고, *Volume 안에서도 각 Service가 데이터를 읽고/쓰는 경로를 지정합니다. Volume에 관한 자세한 설명은 링크에서 확인해보세요.

services:
  db:
    image: postgres:9.6
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data: 

 

2-5. Secrets

Secret은 DB 암호, SSL 인증서/키와 같이 민감한 데이터를 포함하는 오브젝트입니다. Docker Swarm은 Secret 열람 권한을 몇몇 Service에게 부여할 수 있습니다. 이로써, 귀중한 정보들이 암호화가 되지 않은 채 로컬에 남아있거나 다른 네트워크로 전송이 되는 일은 미연에 방지할 수 있습니다.

services:
  db:
        image: postgres:9.6
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/psql-pw
    secrets:
      - psql-pw

secrets:
  psql-pw:
    file: psql-password.txt

 

3. Docker stack 배포


 

yml 파일 다운로드

Docker stack을 학습해보기 좋은 예시를 소개해드립니다. Udemy의 “docker mastery” 강좌에 나온 투표 서비스를 구축하는 Yaml 파일을 예제로 삼았습니다. 필요하시면 아래 링크에서 raw 파일을 다운받아 보세요.

 

Swarm 클러스터 구축

실습을 위해 가상머신으로 Swarm Node를 구성해보고자 한다면 아래 글도 같이 참고해보면 좋습니다.

Docker Swarm 가이드 #1 (Service 생성)

 

Docker Swarm 가이드 #1 (Service 생성)

1. 사전 준비물 Docker Swarm으로 클러스터를 운영하기 위해서 다음과 같은 요소들이 필요합니다. 2개 이상의 PC Docker Swarm은 기본 Docker 시스템과 달리, 멀티 노드로 확장하는 클러스터 운영 프레임워

pyromaniac.me

 

3.1 Stack 실행

Yaml 파일명을 docker-stack.yml으로 바꾼 뒤 다음과 같이 Stack을 실행해보았습니다.

# Stack 생성
$ sudo docker stack deploy -c docker-stack.yml testapp

# Stack 조회
$ sudo docker stack ls

# Service 목록 조회
$ sudo docker stack services testapp

 

# Container 목록 조회
docker stack ps testapp

 

docker stack이 실행된 Node에서 브라우저를 키고 localhost:5000으로 접속해봅시다. 아무 곳에나 투표 해보고 이번에는 localhost:5001에 접속하여 결과를 확인해보세요.

 

Service가 정상 동작하고 있습니다. 여기서 더 확인해봐야 할 것은 Swarm 클러스터가 Service 중단과 같은 오류를 해결하고 오케스트레이션(Orchestration)을 성공적으로 이뤄내는가 입니다.

 

3.2 Service Failure 대응 확인

이와 관련하여 docker-stack.yml 파일에 restart_policy라는 Service 재실행 옵션을 명시해뒀었는데요. 확인 차 투표 앱의 인터페이스를 담당하는 vote Service를 강제 종료시켜보겠습니다.

services:
  vote:
    image: bretfisher/examplevotingapp_vote
    ports:
      - 5000:80
    networks:
      - frontend
    depends_on:
      - redis
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
      restart_policy:
        condition: on-failure # <<< 이 부분이 잘 작동할까요?

 

먼저 vote Service를 담당하는 컨테이너를 직접 제거해보면, 동작 중인 컨테이너 목록에서 vote app이 잠깐 사라지긴 하나, 아래 2번째 사진처럼 얼마 안 되어 새로운 컨테이너가 배정되는 것을 확인할 수 있습니다.

# Worker Node 1에서
$ sudo docker container rm [votingapp ID] -f

 

이번에는 아예 Worker Node 중에 하나를 제거해봤습니다. 보이는 바와 같이 worker1에서 동작하던 컨테이너는 아예 Shutdown 상태가 됬습니다.

# Worker Node 1에서
$ sudo shutdown now

 

잠시 뒤, 놀랍게도 worker1에서 중단된 Service가 이번에는 Master Node에 할당된 것을 볼 수 있습니다.

 

vote Service는 2개의 replica를 요구합니다. 원래는 worker1worker2에 각각 하나씩 replica가 생성되었지만, 지금 worker1이 없는 시점에 결국 하는 수 없이 master Node에 새로운 replica를 배정한 것으로 볼 수 있죠.

services:
  vote:
    ...
    deploy:
      replicas: 2
        ...

 

결과적으로, Swarm 클러스터는 컨테이너 및 Node가 중단되는 등의 시스템 에러에 잘 대응한 셈입니다.

반응형