Docker Swarm 으로 Migration

박우영·2025년 1월 27일
0

아키텍처

목록 보기
3/3

서버 추가

home server 를 1개 더 추가하게 됐는데 추가한 가장 큰 두가지 이유는
1. 가용성을 향상시키기 위함
2. 리소스 자체가 부족함
기존엔 모든 서비스가 단일 장애지점 이었습니다. 사실 개발을 하면서도 문제라는 걸 알고 있었고, 개선을 하고싶지만 지속적인 비용이 투자되어야 하는 문제라 고민이 됐지만, 이번에 서비스 목표를 설정하고 이를 달성 하기 위해 서버 증설을 진행 하기로 마음 먹었습니다.

제가 단일 host 로 구성되어 docker-compose 로 배포하던 방식을 docker swarm 으로 전환 한 경험 입니다.

Network 구성은 어떻게?

기존엔 Docker network 의 사용자 정의 bridge 방식으로 통신하고 있었습니다.

그림으로 표현하면 대략 이런그림이고 Traefik 이라는 webserver 를 ingress controller 로 사용하고 Spring Cloud Gateway 로 요청을 전달, Eureka 를 통해 Service Discovery 가 이루어지는 구조 입니다. 따라서 각 service 들은 외부에서 직접적으로 요청을 받을필요가 없어 port 를 노출할 필요가 없습니다.
여러개의 replica 가 생성될 수 있다는 점, 각 application 들의 포트를 고려할 필요가 없다 라고 생각한다면 이부분은 좋았던 점 이라고 생각 해요.

하지만 문제는 network 방식이 서버 증설을 할때 문제였습니다.

  • bridge 방식이란?
    Docker에서 브리지 네트워크는 소프트웨어 브리지를 사용하여 동일한 브리지 네트워크에 연결된 컨테이너가 통신할 수 있도록 하는 동시에 해당 브리지 네트워크에 연결되지 않은 컨테이너로부터 격리를 제공합니다. Docker 브리지 드라이버는 호스트 머신에 자동으로 규칙을 설치하여 서로 다른 브리지 네트워크에 있는 컨테이너가 서로 직접 통신할 수 없도록 합니다.

요약하자면 A machine 과 B machine 이 있다고 했을때 기존의 방식으론 A와 B의 유연한 통신이 제한된다.

제가 찾아본 솔루션은 다음과 같습니다.
1. Kubernetes 전환
2. Docker swarm 전환 (network overlay 로 수정)

결론적으론 Docker swarm 을 선택했는데요 그 이유는 기존의 docker-compose.yml 파일의 수정을 최소한으로 할 수 있다는점과 러닝커브가 제일 컸습니다.

물론 kubernetes 로 전환이 더욱 좋지만 장점인 auto-scailing 이 불필요하고 여러가지의 add-on에 대한 러닝커브가 프로덕트 관점으로 봤을때 필요한 기능에 비해 개발속도를 지연시킨다고 판단 했습니다.

Docker compose -> Docker swarm 으로 변경

stack, service, task 와 같은 swarm 아키텍처를 다루진 않습니다
개념적으론 제가 경험해봤던 kubernetes 와 매우 유사하다고 느꼈습니다. 크게 Manager, Worker 노드들이 있고 등록해주는 과정이 필요합니다.

Manager, Worker Node 등록

manager node 생성
docker swarm init

worker node
docker swarm join ~

가입 확인
docker node ls

root@pve:~# docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
3ag5b7p1m1vqsvs9jhopceffh     my         Ready     Active                          27.2.1
yeprklaua13ywnt70migw27ee *   pve        Ready     Active         Leader           27.5.1

Issue

전환 하며 큰 문제는 없었지만 자잘한 문제들은 있었습니다.

  • bind source path does not exist 문제
    모든 배포과정은 Github action 과 runner 를 사용하고 있었는데, manager node 를 통해서만 배포가 되다 보니 machine 간 config 들을 공유해야 할때 문제가 발생했고 별도의 config 를 생성해서 해결했어요.

  • 제약조건 설정 (Only Manager)
    Traefik, Prometheus 와 같은 application은 Manager Node 에서만 실행되어야 해요. 사용하려는 이미지의 공식문서를 참고 하길 바랍니다.

  • prometheus scrap 방식 docker swarm service discovery

    {
        "Name": "internal-network",
        "Id": "s6hby6rrclqg20j8ks6wxoqtt",
        "Created": "2025-01-26T04:03:06.582910074+09:00",
        "Scope": "swarm",
        "Driver": "overlay",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "10.0.2.0/24",
                    "Gateway": "10.0.2.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "1cdea0f8f29e7951e14d3a56263e6a06747d71394e8fae23e865600a755bd845": {
                "Name": "dev_kafka1.1.l5j2n7tqzoozx47l7igo9j428",
                "EndpointID": "a17cb29b58c16d832f3165374bb1bfa0e30248271ed709025ddc0a364c7326c8",
                "MacAddress": "02:42:0a:00:02:99",
                "IPv4Address": "10.0.2.153/24",
                "IPv6Address": ""
            },
            "1faf5c671997b8069f775ee1b89775b8eed929a0d81876a3423af91482699ab6": {
                "Name": "dev_prometheus.1.cbl0xp2knd91bq0illyexh6rq",
                "EndpointID": "4a4195960aaaa554581a8597cce44b1628f295646e0f250173b1adfb4dea547c",
                "MacAddress": "02:42:0a:00:02:8c",
                "IPv4Address": "10.0.2.140/24",
                "IPv6Address": ""
            },
            "2c7855cd27640b482e7b195f34dc695374b220a6fa37eaaf3d6fc76e0e0931cc": {
                "Name": "dev_find-my-pet-backend.1.5mrzh55qv8h2kmci1zwuje1os",
                "EndpointID": "0915825a45b83b8761685346073b5552fa57683a0ce16fb35438d762634cbe06",
                "MacAddress": "02:42:0a:00:02:dc",
                "IPv4Address": "10.0.2.220/24",
                "IPv6Address": ""
            },
            "371eea414a3e8e08182ad21b9256e9689a77e0b52c81db170494c1fe1c6ffcc7": {
                "Name": "dev_orchestrator.1.bjz1mdrr0npluzvjyah1adb6w",
                "EndpointID": "feb641a9422d0ea5e4c1dc12df159051c9f29b9ef24d80f7a08ba9559481a64e",
                "MacAddress": "02:42:0a:00:02:d8",
                "IPv4Address": "10.0.2.216/24",
                "IPv6Address": ""
            },
            "3d320834e1a6f2d9a9d2722875a4c60793aeb5a733016b3577a5524f5d10984b": {
                "Name": "dev_reverse-proxy.1.36mdvgqq5x6ms5uzkk7zmala0",
                "EndpointID": "11bb79abb6a6220b5ffea217e767b40864b4a1c6df7d063417ee17656ad02fc7",
                "MacAddress": "02:42:0a:00:02:51",
                "IPv4Address": "10.0.2.81/24",
                "IPv6Address": ""
            },
            "52beedbdbf19c1062a4a93132eecf13223f6d8891058dc5440b0caf5194bee1f": {
                "Name": "dev_node_exporter.yeprklaua13ywnt70migw27ee.nysydaoa1l41xsfmncswewrbr",
                "EndpointID": "ea79a733a1afa8b8eeed89b0dce4e32678d8ae6aafa77d5958bbd7762415e21d",
                "MacAddress": "02:42:0a:00:02:83",
                "IPv4Address": "10.0.2.131/24",
                "IPv6Address": ""
            },
            "54ec3ae873043808a1fec082e668de1a9254ba7a3a384a22e5733d3095328804": {
                "Name": "dev_redis.1.pr37j2qlpifr8xo9tdal7cfdy",
                "EndpointID": "ae1fc99a6ac5c499d5cdc67992625933d9053dd5865ff464d61b2a88c43b4ed6",
                "MacAddress": "02:42:0a:00:02:03",
                "IPv4Address": "10.0.2.3/24",
                "IPv6Address": ""
            },
            "81a300dbf61ff441c00a9f94e97672edb830c05cc416d4100dc2c66c1c4db075": {
                "Name": "dev_auth-server.1.s4f0rur25okr5qco2cf91ypxy",
                "EndpointID": "00f36381b013bb3796b3b7310137f2e37723be7e543640a8c4fa6e9606c5b164",
                "MacAddress": "02:42:0a:00:02:da",
                "IPv4Address": "10.0.2.218/24",
                "IPv6Address": ""
            },
            "a9b4302d7c8e0703fdb650202e09a02090392f141dbe8d643924d17a662a7bf9": {
                "Name": "dev_gateway.1.lj0lr2hs1xw8vqc3yyw726gde",
                "EndpointID": "262bb8688cf6ecd18594ad05639ee2dfe9ed4c46cdd3ae688a192c0240a0d421",
                "MacAddress": "02:42:0a:00:02:d6",
                "IPv4Address": "10.0.2.214/24",
                "IPv6Address": ""
            },
            "lb-internal-network": {
                "Name": "internal-network-endpoint",
                "EndpointID": "9827138b5890ebba2b0ec92975ef5644cb42b76510ea5ad01b7b4a261110be7b",
                "MacAddress": "02:42:0a:00:02:04",
                "IPv4Address": "10.0.2.4/24",
                "IPv6Address": ""
            }
        },

현재 사용중인 ipv4 의 ip는 다음과 같습니다. 10.0.2.x 대역을 사용하고 있으며, 약 254 개정도 사용 가능 할 것이라 생각되는데 현재 서비스 규모를 생각해보면 매우 여유있는 수치라고 판단했어요. 서비스가 더욱 성장해 IP 문제가 발생하길 바라며 Type A 방식으로 Instance 를 구분 했어요.

수정된 Docker-Compose.yml

version: '3.8'

services:
  auth-server:
    image: 'wy9295/auth-server:69285ae12c11ad0e7ee4aeb713338b3d072e4c78'
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - "node.hostname == pve"
      labels:
        - "prometheus.enable=true"
        - "prometheus.scrape=true"
        - "prometheus.path=/actuator/prometheus"
        - "prometheus.port=8080"
    networks:
      - internal-network
      - auth-server
      - barbellrobot-backend
    environment:
      - REDIS_PORT=6379
      - REDIS_HOST=redis
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - RDBMS_URL=barbell-robot-mysql
      - RDBMS_USERNAME=${RDBMS_USERNAME}
      - RDBMS_PASSWORD=${RDBMS_PASSWORD}
      - ACCESS_SECRET_KEY=${ACCESS_SECRET_KEY}
      - ACCESS_EXPIRE_MILLIS=${ACCESS_EXPIRE_MILLIS}
      - REFRESH_SECRET_KEY=${REFRESH_SECRET_KEY}
      - REFRESH_EXPIRE_MILLIS=${REFRESH_EXPIRE_MILLIS}
      - EUREKA_URI=http://eureka:8761/eureka
      - PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040
... other services
networks:
  barbellrobot-backend:
    driver: overlay
    name: barbellrobot-backend
    attachable: true
  find-my-pet-backend:
    driver: overlay
    name: find-my-pet-backend
    attachable: true
  internal-network:
    driver: overlay
    name: internal-network
    attachable: true
  auth-server:
    driver: overlay
    attachable: true
    name: auth-server
  gateway:
    driver: overlay
    name: gateway
    attachable: true

volumes:
  google-key:

결론

  • Docker Swarm 후기
    완벽하진 않지만 서비스 구성을 마치는데 거의 하루정도 걸린걸 보면 기존의 단일 machine 위에서 docker-compose 로 배포하던것과 큰 차이가 없었고 러닝커브 또한 높지 않다고 생각합니다.
    따라서 단일 환경에서 분산환경으로 빠른 전환이 필요하고 Docker 를 사용하고 있다면 좋은 솔루션 이라고 생각합니다.
    machine 별로 성능이 다르다보니 사용하는 리소스에 따라 node 구성을 하고자 했었는데 접근을 잘못 했다고 생각이 들었습니다.

  • 다음에 해야할 것은?
    서버가 이중화 됐지만 제일 중요한 database 는 개선되지 않았습니다.
    다음은 DB 이중화 전략과 이중과 개선작업을 진행 하고자 합니다.

0개의 댓글