티스토리 뷰

반응형

운영 중인 서비스는 대부분 서비스 포트를 외부로 노출 시키지 않습니다. reverse proxy를 서버에 두고 80포트를 뚫어 요청을 받는경우가 많습니다. 이렇게 했을 때 다음과 같은 장점이 있기 때문인데요.

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

Flow chart로 보면 다음과 같은 구조를 띕니다.

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

보통 reverse proxy로 Nginx를 많이 사용하고 있습니다. 하지만 Docker Swarm 환경에서는 클러스터로 운영되고 있기 때문에 reverse proxy가 도메인이나 서버를 라우팅 하기 위해 여러 domain 정보를 알고 있어야 했습니다. 하지만  Nginx는 서버가 discovery되거나 remove 됐을 때 새로운 domain이 추가 됐을 때 무중단 운영이 안되는 단점이 있었습니다.

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

 

이 단점을 보완하기 위해서 구글 서칭을 해본 결과 go 언어 기반의 오픈 소스이면서 Docker Kubernetes 환경에서만 지원해주는 reverse proxy인 Traefik을 발견했습니다.

Traefik 이란?

Traefik은 go 언어로 구현된 reverse proxy 역할을 하는 오픈 소스로써 docker.sock에 접근하여 docker swarm에 정보를 읽어들여 새로운 서버를 자동으로 discovery하고 domain이 추가 되더라도 label 기반으로 자동 라우팅을 지원해주었습니다. 

한마디로 Nginx와는 다르게 이 작업들이 무중단으로 운영이 가능했습니다. 또한 라우팅에 대한 세밀한 설정 (domain, port, network 등)을 Traefik이 아닌 새로 추가된 서버 container에 label을 설정해주면 Traefik이 자동으로 찾아갈 수 있기 때문에 Nginx 처럼 새로운 세팅을 Nginx에 해줄 필요는 없었습니다. 

docker.sock이란?
docker API를 실행하는 주체인 docker daemon에 API요청을 받는 클라이언트라고 보면 됩니다. CLI에서 docker 명령어를 입력하면 Unix 소켓 기반인 docker.sock을 통해 daemon API를 호출하고 응답을 받습니다.

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

 

다음은 traefik 공식 문서에서 볼 수 있는 도식도 입니다.

도식도를 보면 요청이 들어오면 EntryPoint를 찾습니다. 그리고 EntryPoint를 찾게 되면  해당 EntryPoint에 설정된 라벨 정보를 읽어 docker Service로 라우팅합니다. 그리고 최종적으로 docker swarm에서 라우팅을 받아 로드밸런싱을 합니다. 

 

Flow chart로 보면 다음과 같습니다. 

Traefik 세팅하기

그럼 세팅하면서 직접 실습 해봅시다. 

 

docker-compose-traefik.yml

version: "3.8"

services:
    traefik:
    image: traefik:v2.10.3
    command:
      - --providers.docker
      - --providers.docker.swarmMode
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --providers.docker.network=golf
      - --api.dashboard=true
      - --ping.entryPoint=web
      - --accessLog={}
    ports:
      - "80:80"
      - "8080:8080"
    networks:
      - 
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    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:
  golf:
    external: true

traefik도 서비스가 하나로 동작하기엔 실제 사용자들에게 서비스를 제공해줘야합니다. 그렇기 때문에 replica 개수를 조절하여 하나가 장애로 죽더라도 계속 서비스가 될 수 있도록 했습니다. 

 

docker.swarmMode로 설정하여 docker swarm 모드로 실행중인 컨테이너들 대상으로만 라우팅 할 수 있게 지정합니다. 

 

exposedbydefault 설정은 false로 하는 것을 권장합니다. 사유는 true인 경우 모든 docker 컨테이너들이 라우팅 대상으로 자동으로 지정되는데 수동으로 했을 때 유동적으로 라우팅 대상을 지정할 수 있습니다. 보통 docker rising된 시스템은 유동적으로 라우팅 대상을 지정해야하는 컨테이너들도 생길 수 있기 때문에 이러한 컨테이너들은 라우팅 대상에서 제거해야할 수도 있습니다. 

web.address 설정은 요청을 받을 포트 정보를 구성합니다. 80포트로 들어오는 요청을 수신하여 web이라는 엔트리포인트를 통해 라우팅하게 됩니다. 요약하면 web 요청을 80으로 받아 처리할 수 있도록 하는 설정입니다.

 

다음은 network 설정입니다. 내부적으로 규칙을 하나의 docker network만을 사용하기로 했다면 다음과 같이 docker.network 설정을 통해 traefik에 직접 라우팅할 network를 지정해줄 수 있습니다. 유연한 네트워크 망 사용을 원한다면 app 컨테이너 label에 network 정보를 지정해서 유동적으로 설정해줄 수 있습니다. 

 

api.dashboard는 대시보드 활성화 유무 입니다. true로 설정하면 traefik 대시보드에 접근해 라우터나 라우터에 연결된 서비스들을 모니터링 할 수 있습니다. 대시보드를 활용하면 traefik이 docker service를 잘 감지하고 라우팅하는지에 대한 감시가 가능합니다.

 

ping.entrypoint는 traefik은 health check를 위한 /ping endpoint를 제공하는데 해당 API가 요청을 수신하기 위한 entrypoint를 지정합니다. web으로 설정하였으니 80 port의 /ping 요청이오면 health check에 필요한 응답 정보를 받을 수 있습니다.
(응답 spec은 200에 ok라는 plain text)

 

access.log={}는 접근 로그를 활성화 합니다. 모든 들어오는 요청을 log로 남기고 이는 장애 발생 시 유용한 정보들을 로그 모니터링을 통해 발견할 수 있습니다. (실제로 실수로 entrypoint를 중복으로 해서 배포하거나 routing 실패 하는 장애가 발생했을 때 로그를 분석하여 원인을 찾고 해결한 경험이 있습니다.)

 

자 그럼 traefik을 배포할 준비가 완료 되었습니다. 이제 docker swarm으로 운영되는 서비스들의 label을 설정하여 라우팅 받을 수 있게 합시다.

Docker 연동하기

docker-compose-app.yml

version: "3.8"

services:
    bank:
      image: ilgolf/bank:{version}
      hostname: kotlin-bank
      networks: 
        - golf

      # .. 중략

      deploy:
        mode: replicated
        replicas: 3
        resources: 
          limits:
            memory: '1g'
            cpus: '1'
        labels:
          - "traefik.enable=true"
          - "traefik.docker.network=golf"
          - "traefik.http.routers.golf-api.rule=Host(`golf.tistory.kr`)"
          - "traefik.http.routers.golf-api.entrypoints=web"
          - "traefik.http.services.golf-api.loadbalancer.server.port=8083"
          
        # .. 중략

networks:
  golf:
    external: true

traefik이 라우팅 가능하게 enable = true로 설정해 줍니다. 이후 domain rule을 부여하여 golf.tistory.kr로 요청이 들어온다면 라우팅이 될 것입니다. entrypoints=web으로 설정해주었기 때문에 80으로의 요청만 받습니다. 또한 port 정보는 8083이라는 것을 traefik에게 알려줍니다. 

 

마무리

docker swarm 과 traefik을 이용하여 무중단으로 서버를 운영하기 위한 세팅이 끝났습니다. 실제로 필자는 무중단 운영이 되는지 테스트를 진행했었는데 지표는 다음과 같았습니다. 

 

테스트 도구 : jmeter

환경 : 100명의 가상 유저가 0.1초마다 1개의 요청을 지속적으로 요청을 보냄

 

도메인 중간에 추가 하는 시나리오

  • Error rate : 0%
  • latetancy AVG : 0.09 ms per transaction
  • TPS : 101

rolling update 시나리오 

  • Error rate : 0%
  • latency AVG : 0.1 ms per transaction
  • TPS : 88

추가로 traefik도 Nginx처럼 처리율 제한 알고리즘을 구현할 수 있고 ssl 인증을 처리할 수 있습니다. 자세한 내용은 아래 글을 참고해보시기 바랍니다. 

https://doc.traefik.io/traefik/middlewares/http/ratelimit/

 

Traefik RateLimit Documentation - Traefik

RateLimit To Control the Number of Requests Going to a Service The RateLimit middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. It is based on a token bucket implementation. In this analogy, the

doc.traefik.io

https://doc.traefik.io/traefik/https/overview/

 

Traefik Proxy HTTPS & TLS Overview |Traefik Docs - Traefik

HTTPS & TLS Overview Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration: routers, and the TLS connection (and its underlying certificates). When a router has to handle HTTPS traffic, it should be specified with a tls field

doc.traefik.io

다음과 같이 운영 가능한 환경 까지 구축이 되었습니다. 하지만 정말 끝난걸까요? 아직은 기술 부채가 존재합니다.

  • 정적 파일들을 공유하기 위한 전략의 부재 
  • 각 노드의 metric 정보를 수집하기 위한 전략
  • docker swarm을 더 손쉽게 운영하기 위한 GUI Tool 

다음 편에서는 이를 해결해 나가기 위해 새로운 전략들을 소개해드리겠습니다. 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함