Profile picture

[Docker Swarm] 도커스웜으로 배포해보기

JaehyoJJAng2024년 04월 03일

▶︎ 개요

도커 스웜으로 간단한 서비스를 배포해보자.


▶︎ 사설 레지스트리 서버 구축

퍼블릭 레지스트리 서버로 도커 허브등을 사용하는 방법이 있지만

오늘 포스팅에서는 개발 서버에서 이미지 레지스트리 서버를 도커를 사용하여 self-hosted 할 것이다.

그렇게해서 개발중인 애플리케이션을 도커라이징하여 구축한 이미지 레지스트리 서버에 배포하고

도커 스웜 클러스터에서 해당 이미지를 pull 받아 배포하면 된다.

도커를 이용한 사설 레지스트리 서버 구축 방법에 대해서는 링크된 게시글을 참고하도록 하자.


▶︎ 사전 준비

docker swarm에 참여한 서버간 Port를 오픈해줘야 한다.

docker swarm은 ingress network로 각각의 트래픽을 조절하고 각 노드를 관리하는데, 이 때 필요한 Port들이 존재한다.
(참고: 도커스웜 사전준비)


  • TCP
    • 2377
    • 클러스터 관리 통신용
  • TCP/UDP
    • 7946
    • 노드 간 통신
  • UDP
    • 4789
    • 오버레이 네트워크 트래픽용

▶︎ 서비스 배포

  • 실습에서 구현할 서비스는 fastapi 백엔드 기반 간단한 유저명 출력 서비스이다.

해당 서비스는 아래와 같이 매우 간단한 코드를 가진다.

from fastapi import FastAPI
import socket


app : FastAPI = FastAPI()

@app.get('/hostname')
def get_hostname() -> dict[str,str]:
    hostname = socket.gethostname()
    return {'hostname': hostname}

‣ 이미지 파일 배포

작성된 서비스를 도커 이미지로 만들어보자.

FROM python:3.10-slim-buster

WORKDIR /usr/src/app

COPY ./requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

dockerfile 작성이 끝났다면 해당 이미지를 빌드하고 사설 레지스트리 서버 구축에서 구축해놨던 레지스트리 url로 배포를 해보도록 하자.

# 이미지 빌드
docker build --tag yshrim12/swarm-fastapi .

# 빌드된 이미지 태깅
docker tag yshrim12/swarm-fastapi <registry 서버 도메인>/yshrim12/swarm-fastapi

# 레지스트리 서버로 이미지 배포
docker push <registry 서버 도메인>/yshrim12/swarm-fastapi

‣ docker compose 작성

  • 배포할 YAML을 정의해주기

services:
  api:
    image: yshrim12/swarm-fastapi
    hostname: fastapi
    ports:
      - "8000:8000"
    stop_grace_period: 30s
    stop_signal: SIGTERM
    healthcheck:
      test: ["CMD", "curl", "-l", "http://localhost:8000/hostname"]
      timeout: 5s
    deploy:
      mode: replicated
      replicas: 3
      resources:
        limits:
          memory: '1g'
          cpus: '1'
      update_config:
        order: start-first
        delay: 5s
        failure_action: rollback
      rollback_config:
        order: stop-first
        parallelism: 0
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    networks:
      - "api-net"

networks:
  api-net:
    external: false

주요 설정은 아래와 같습니다.

속성 설명
stop_grace_period graceful한 종료 시, 최대 timeout 시간 설정
stop_signal 컨테이너 종료 전 signal 설정.
SIGKILL은 바로 컨테이너를 종료하는 반면 SIGTERM은 프로세스에게 종료를 맡기기 때문에 graceful한 종료가 가능.
healthcheck test에 포함된 명령어가 정상적으로 수행되어야 healthy 상태가 될 수 있음. 해당 상태가 되어야 트래픽 요청 처리가 가능함.
deploy.mode 배포 상태 설정. global의 경우 각 노드마다 한 개의 replica를 만들어 냄.
반면에 replicated는 replica를 무작위로 배포 가능
deploy.replicas replica 개수 설정. 이 개수만큼 docker swarm이 replica 개수를 유지해준다.
deploy.resources 컨테이너 자원 관련 설정. limit으로 제한을 걸고 memory 1GB, cpu 1 core로 제한을 두었음.
deploy.update_config 롤링 업데이트 설정. 현재 순서는 새로운 컨테이너를 먼저 띄우고 배포 성공 후 5s의 지연 후 새 컨테이너를 이어서 배포함. 또한 실패 시 이전 컨테이너로 rollback
deploy.rollback_config 배포 실패시 rollback 설정. 실패 시 손상된 컨테이너를 바로 종료하여 손상된 컨테이너에 트래픽이 몰리지 않도록 함.
또한 모든 실패 컨테이너를 병렬로 rollback하여 최대한 배포 실패로 인한 문제가 없도록 함.
deploy.restart_policy 배포 실패 시, 재시도 횟수와 지연시간 설정

여기서 핵심은 health-checkdeploy 속성이라고 할 수 있겠다.

health-check가 성공해야지만 컨테이너에 트래픽 요청이 가능해지기 때문에

안정적으로 새로운 컨테이너 배포가 가능해지고, update 시 start_first로 되어있기 때문에 health-check가 성공해야만 기존 컨테이너의 트래픽이 끊기게 된다.

만약 배포에 실패한다면? 기존 컨테이너는 종료되지 않고 계속 운영될 것이다.

또한, 배포 실패 시 빠르게 손상된 컨테이너를 복구시켜주기 때문에 무중단 운영도 가능해진다.


‣ stack 명령으로 배포

그럼 이제 작성한 docker-compose.yaml 파일을 stack 명령어를 사용하여 배포해보도록 하자.

docker stack deploy -c <docker-compose.yaml> --with-registry-auth <stack_name>
  • --with-registry-auth
    • 레지스트리 서버에서 이미지를 가져올 때 인증 정보를 사용하도록 설정하는 옵션
    • 이 옵션 사용 시 도커 스웜 매니저가 워커 노드에게 레지스트리 인증 정보를 전파하여, 모든 노드가 해당 레지스트리에서 이미지를 pull 받아오도록 함.

‣ rolling update

이미지의 태그를 변경하여 다시 배포해준 뒤, service만 업데이트하면 롤링 업데이트가 된다.

docker service update --image <registry 서버 도메인>/yshrim12/swarm-fastapi

Loading script...