Profile picture

[Docker Swarm] Swarm 클러스터에 Reverse Proxy 도입하기 (Traefik)

JaehyoJJAng2024년 04월 04일

▶︎ 개요

운영 중인 서비스는 대부분 서비스 포트를 외부에 노출하지 않고, reverse proxy를 서버 앞단에 두어 80번 포트로 들어오는 요청을 각 서비스에 분산하는 경우가 많다.

위와 같이 리버스 프록시를 앞단에 두는 이유는 다음과 같은 장점이 있기 때문이다.

  • 처리율 제한 알고리즘을 구현하여 서버 부하에 대비 가능
  • 직접적으로 외부에 port 정보를 노출하지 않기에 DDoS 공격으로부터 비교적 안전할 수 있음.

리버스 프록시를 사용하는 환경을 그림으로 표현한다면 다음과 같다.
image


▶︎ 클러스터 환경에서 Nginx의 문제점

도커 스웜 환경에서는 클러스터로 운영되기 때문에,

reverse proxy가 도메인이나 서버를 라우팅 하기 위해 여러 domain 정보를 알고 있어야 한다.

리버스 프록시로 가장 많이 사용하는 Nginx의 경우 클러스터 환경에서 서버가 discovery, 삭제, 새로운 domain이 추가 됐을 때 무중단 운영이 되지 않는 단점이 존재한다.

또한, 서버가 이미지 기반으로 scaling 되어야 하지만, Nginx는 도메인 정보를 다 들고 있는 config 파일 때문에 볼륨을 사용하지 않고 배포하려면 복잡한 프로세스를 새로운 서버마다 만들어줘야 했다.

이 단점을 보완하기 위해, go 언어 기반의 오픈 소스 이면서 Docker, Kubernetes 환경에서만 지원해주는 reverse proxy인 Traefik을 도입해보려고 한다.


▶︎ Traefik


Traefik은 go 언어로 구현된 reverse proxy 역할을 하는 오픈 소스 프로젝트이다.

docker.sock에 접근하여 docker swarm 정보를 읽어들여 새로운 서버를 자동으로 discovery하고, 도메인이 새로 추가되더라도 label 기반으로 자동 라우팅을 지원한다.

한마디로 Nginx와는 다르게 Traefik은 클러스터 환경에서 무중단 운영을 가능하게 해주는 리버스 프록시인 셈이다.

또한 Traefik은 라우팅에 대한 세밀한 설정(domain, port, network ..)을 Traefik이 아닌 새로 추가된 컨테이너에 label을 추가해주면 Traefik이 자동으로 추적하기 때문에

Nginx처럼 새로운 설정을 매번 컨테이너에 주입 해줄 필요가 없다.

Traefik을 도입한 환경을 그림으로 표현한다면 다음과 같다.
image


‣ docker.sock

docker.sock은 docker API를 실행하는 주체인 docker daemon에 API 요청을 받는 클라이언트이다.

CLI에 docker 명령을 입력하면 Unix 소켓 기반인 docker.sock을 통해 daemon API를 호출하고 응답을 받는다.

Traefik은 이러한 요청들을 매번 감시하고 있다가 docker service가 새로 생기거나 제거 되는 이벤트가 발생하면 Traefik은 이를 감지하여 라우팅 대상에서 추가 또는 제외한다.


▶︎ Traefik 구축하기


구축 전 먼저 실습에서 사용할 overlay 네트워크를 하나 생성해주도록 하자.

docker network create --driver overlay traefik-network

그리고 아래와 같이 docker-compose-traefik.yaml을 작성해보자.

services:
  reverse-proxy:
    image: traefik:v3.0
    command: 
      - "--log.level=ERROR"
      - "--log.filepath=logs/traefik.log"
      - "--accesslog.filepath=logs/access.log"
      - "--api.dashboard=true"
      - "--api.insecure=true"
      - "--providers.swarm.network=traefik-network"
      - "--providers.swarm.endpoint=unix:///var/run/docker.sock"
      - "--providers.swarm.exposedByDefault=false"
      - "--entryPoints.http.address=:80"
      - "--entryPoints.https.address=:443"
    ports:
      - "80:80"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - "traefik-network"
    deploy:
      mode: replicated
      replicas: 2
      update_config:
        order: start-first
        delay: 10s
        failure_action: rollback
      rollback_config:
        order: stop-first
        parallelism: 0
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      placement:
        constraints:
          - node.role == manager
networks:
  traefik-network:
    name: traefik-network
    external: true
  • 실제 사용자들에게 서비스를 제공해주는 역할을 하므로 traefik도 2개가 동작하도록 replicas 개수를 조절하였다.
  • swarm.endpoint=unix:///var/run/docker.sock
    • docker swarm 모드로 실행중인 컨테이너들 대상으로만 라우팅 할 수 있도록 지정하였다.
  • swarm.exposedByDefault
    • 해당 설정은 false로 하는 것을 권장한다.
    • true인 경우 모든 docker 컨테이너들이 라우팅 대상으로 자동 지정되는데, 이를 수동으로 했을 경우에는 유동적으로 라우팅 대상 지정이 가능하다.
    • 보통 docker rising된 시스템은 유동적으로 라우팅 대상을 지정해야하는 컨테이너들도 생길 수 있기에 이러한 컨테이너들은 라우팅 대상에서 제거해야할 수도 있다.
  • entryPoints.http.address
    • 요청을 받을 http 포트 정보를 구성한다. 80번 포트로 들어오는 요청을 수신하여 web 이라는 entrypoint를 통해 라우팅하게 된다.
    • 요약하면 web 요청을 80으로 받아 처리할 수 있도록 하는 설정이다.
  • entryPoints.https.address
    • 요청을 받을 https 포트 정보를 구성한다. 443번 포트로 들어오는 요청을 수신하여 web 이라는 entrypoint를 통해 라우팅하게 된다.
    • 요약하면 web 요청을 443으로 받아 처리할 수 있도록 하는 설정이다.
  • swarm.network=traefik-network
    • 내부적으로 하나의 docker network 만을 사용하도록 규칙을 정해놨다면 다음과 같이 docker.network 설정을 통해 traefik에 직접 라우팅할 network를 지정해줄 수 있다.
    • 유연한 네트워크 망 사용을 원한다면 app 컨테이너 label에 network 정보를 지정해서 유동적으로 설정해줄 수 있다.
  • api.dashboard=true
    • 대시보드 활성화 유무
    • true로 설정 시 traefik 대시보드에 접근하여 라우너타 라우터에 연결된 서비스들을 모니터링할 수 있다.

‣ 도커 연동하기

docker-compose-app.yaml

services:
  myapp:
    image: yshrim12/swarm-fastapi:latest
    hostname: fastapi
    networks:
      - "traefik-network"
    deploy:
      mode: replicated
      replicas: 3
      resources:
        limits:
          memory: '1g'
          cpus: '1'
      labels:
        - traefik.enable=true
        - traefik.http.routers.myapp.rule=Host(`192.168.219.128`) # 또는 도메인 이름
        - traefik.http.services.myapp-service.loadbalancer.server.port=8080
networks:
  traefik-network:
    name: traefik-network
    external: true

domain rule을 부여하여 192.168.219.128(또는 도메인 이름)로 요청이 들어온다면 라우팅이 될 것이다.

또한 port 정보는 8080이라는 것을 traefik에게 알려주게된다.


▶︎ 마무리

이로써 도커 스웜과 traefik을 이용하여 무중단으로 서버를 운영하기 위한 설정이 끝났다.

실제로 무중단 운영이 가능한지 테스트를 진행해보기 위해서는 아래와 같은 테스트 도구를 사용하면 된다.
[jmeter]](https://jmeter.apache.org/)


Loading script...