본문 바로가기
WIKI 기술 지식 베이스

Traefik Docker Swarm 문서

원문 보기 위키 갱신

Traefik Docker Swarm 문서 (Traefik Docker Swarm Documentation)

출처: Traefik Docker Swarm Documentation

본문

Traefik & Docker Swarm

이 프로바이더는 Docker Swarm Mode와 함께 동작해요.

퀵 스타트는 Docker를 사용해요

아직 읽지 않았다면 Docker 프로바이더를 사용하는 퀵 스타트 가이드를 한번 훑어보는 걸 권장해요.

구성 예시 (Configuration Example)

아래에서 설명하는 것처럼 Swarm 프로바이더를 활성화할 수 있어요.

File (YAML)

providers:
  swarm: {}

File (TOML)

[providers.swarm]

CLI

--providers.swarm=true

Swarm 모드에서는 단일 서비스(컨테이너가 아닌)에 라벨을 붙여요(docker compose 파일에서). 서비스가 하나뿐이고 라우터가 서비스를 지정하지 않으면, 그 서비스가 라우터에 자동으로 할당돼요.

Labels

services:
  my-container:
    deploy:
      labels:
        - traefik.http.routers.my-container.rule=Host(`example.com`)
        - traefik.http.services.my-container-service.loadbalancer.server.port=8080

구성 옵션 (Configuration Options)

| Field | Description | Default | Required | | providers.providersThrottleDuration | 구성 리로드 이후, 새로운 구성 갱신 이벤트를 반영하기까지 기다리는 최소 시간. 이 시간 안에 여러 이벤트가 발생하면 가장 최근 이벤트 하나만 반영되고 나머지는 모두 버려져요. 이 옵션은 프로바이더별로 설정할 수 없지만, 스로틀링 알고리즘은 각 프로바이더에 독립적으로 적용됩니다. | 2s | No | | providers.swarm.endpoint | Docker API 엔드포인트를 지정해요. 자세한 내용은 여기. | unix:///var/run/docker.sock | Yes | | providers.swarm.username | Basic HTTP 인증을 위한 사용자 이름을 정의해요. Docker 데몬 소켓이 Basic HTTP 인증이 필요한 HTTP 프록시를 통해 노출될 때 사용해야 해요. | "" | No | | providers.swarm.password | Basic HTTP 인증을 위한 비밀번호를 정의해요. Docker 데몬 소켓이 Basic HTTP 인증이 필요한 HTTP 프록시를 통해 노출될 때 사용해야 해요. | "" | No | | providers.swarm.useBindPortIP | 컨테이너의 내부 네트워크 IP/포트 대신 컨테이너가 바인딩한 IP/포트를 사용하도록 Traefik에 지시해요. 자세한 내용은 여기. | false | No | | providers.swarm.exposedByDefault | Traefik을 통해 컨테이너를 기본적으로 노출해요. false로 설정하면 traefik.enable=true 라벨이 없는 컨테이너는 결과 라우팅 구성에서 제외됩니다. 자세한 내용은 여기. | true | No | | providers.swarm.network | 모든 컨테이너에 대한 연결에 사용할 기본 docker 네트워크를 정의해요. 이 옵션은 traefik.swarm.network 라벨로 컨테이너별로 오버라이드할 수 있어요. | "" | No | | providers.swarm.defaultRule | 라벨로 규칙이 정의되지 않은 컨테이너에 적용할 라우팅 규칙을 정의해요. 자세한 내용은 여기. | "Host({{ normalize .Name }})" | No | | providers.swarm.refreshSeconds | Swarm Mode의 폴링 간격을 정의해요. | "15s" | No | | providers.swarm.httpClientTimeout | HTTP 연결에 대한 클라이언트 타임아웃을 정의해요. 기간 문자열(예: "30s", "1m")을 받아들여요. 값이 0이면 타임아웃이 설정되지 않아요. | 0 | No | | providers.swarm.watch | Traefik이 Docker 이벤트를 감시할지 여부를 지시해요. | true | No | | providers.swarm.constraints | Traefik이 컨테이너 라벨에 대해 대조하여 해당 컨테이너에 라우트를 만들지 여부를 결정하는 표현식. 자세한 내용은 여기. | "" | No | | providers.swarm.allowEmptyServices | 해당 Docker 컨테이너의 건강 상태와 무관하게, Docker 컨테이너에 대해 정의된 서버 로드 밸런서를 만들도록 프로바이더에 지시해요. | false | No | | providers.swarm.tls.ca | Docker와의 보안 연결에 사용되는 인증 기관(CA)의 경로를 정의해요. 기본값은 시스템 번들(system bundle)이에요. | "" | No | | providers.swarm.tls.cert | Docker와의 보안 연결에 사용되는 공개 인증서의 경로를 정의해요. 이 옵션을 사용하려면 key 옵션도 설정해야 해요. | "" | Yes | | providers.swarm.tls.key | Docker와의 보안 연결에 사용되는 개인 키의 경로를 정의해요. 이 옵션을 사용하려면 cert 옵션도 설정해야 해요. | "" | Yes | | providers.swarm.tls.insecureSkipVerify | TLS 연결을 수립할 때 인증서가 커버하는 호스트 이름과 무관하게, Docker 서버가 제시하는 어떤 인증서든 수락하도록 프로바이더에 지시해요. | false | No |

endpoint

자세한 내용은 Docker Swarm API 액세스 섹션을 참고하세요.

docker.sock 사용하기

docker-compose 파일이 Traefik 컨테이너와 docker sock을 공유해요.

services:
  traefik:
     image: traefik:v3.1 # The official v3 Traefik docker image
     ports:
       - "80:80"
     volumes:
       - /var/run/docker.sock:/var/run/docker.sock

Traefik의 구성 파일에 docker.sock을 지정해요.

File (YAML)

providers:
  swarm:
    endpoint: "unix:///var/run/docker.sock"
     # ...

File (TOML)

[providers.swarm]
  endpoint = "unix:///var/run/docker.sock"
  # ...

CLI

--providers.swarm.endpoint=unix:///var/run/docker.sock
# ...

SSH 사용하기

Docker 18.09+를 사용하면 SSH로 Traefik을 데몬에 연결할 수 있어요. Traefik의 구성 파일에 SSH 호스트와 사용자를 지정해요. 서버가 인증에 공개 키를 요구한다면, Traefik을 실행하는 사용자가 그 키에 접근할 수 있어야 한다는 점을 유의하세요.

File (YAML)

providers:
  swarm:
    endpoint: "ssh://user@host:2022"
     # ...

File (TOML)

[providers.swarm]
  endpoint = "ssh://user@host:2022"
  # ...

CLI

--providers.swarm.endpoint=ssh://user@host:2022
# ...

HTTP 사용하기

Docker Engine API를 사용하면 HTTP로 Traefik을 원격 데몬에 연결할 수 있어요.

File (YAML)

providers:
  swarm:
    endpoint: "http://127.0.0.1:2375"
     # ...

Static Configuration (TOML)

[providers.swarm]
  endpoint = "http://127.0.0.1:2375"
  # ...

CLI

--providers.swarm.endpoint=http://127.0.0.1:2375
# ...

TCP 사용하기

Docker Engine API를 사용하면 TCP로 Traefik을 원격 데몬에 연결할 수 있어요.

File (YAML)

providers:
  swarm:
    endpoint: "tcp://127.0.0.1:2375"
     # ...

File (TOML)

[providers.swarm]
  endpoint = "tcp://127.0.0.1:2375"
  # ...

CLI

--providers.swarm.endpoint=tcp://127.0.0.1:2375
# ...

File (YAML)

providers:
  swarm:
    endpoint: "unix:///var/run/docker.sock"

File (TOML)

[providers.swarm]
  endpoint = "unix:///var/run/docker.sock"

CLI

--providers.swarm.endpoint=unix:///var/run/docker.sock

useBindPortIP

Traefik은 일치하는 컨테이너의 IP/포트로 요청을 라우팅해요. useBindPortIP=true를 설정하면 컨테이너의 내부 네트워크 IP/포트 대신 컨테이너가 바인딩한 IP/포트를 사용하라고 Traefik에 지시해요.

traefik.http.services..loadbalancer.server.port 라벨(특정 포트로 요청을 라우팅하라고 Traefik에 지시)과 함께 사용하면, Traefik은 traefik.http.services..loadbalancer.server.port 포트에서 바인딩을 찾으려고 해요. 그런 바인딩을 찾지 못하면 Traefik은 컨테이너의 내부 네트워크 IP로 폴백하지만, 라벨에 설정된 traefik.http.services..loadbalancer.server.port 포트는 여전히 사용해요.

다양한 상황에서의 usebindportip 예시.

| port label | Container's binding | Routes to | | - | - | IntIP:IntPort | | - | ExtPort:IntPort | IntIP:IntPort | | - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort | | LblPort | - | IntIp:LblPort | | LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort | | LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort | | LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort |

위 표에서:

  • ExtIp는 "바인딩에서 찾은 외부 IP"를 의미해요.
  • IntIp는 "내부 네트워크 컨테이너의 IP"를 의미해요.
  • ExtPort는 "바인딩에서 찾은 외부 포트"를 의미해요.
  • IntPort는 "내부 네트워크 컨테이너의 포트"를 의미해요.

File (YAML)

providers:
  swarm:
    useBindPortIP: true
    # ...

File (TOML)

[providers.swarm]
  useBindPortIP = true
  # ...

CLI

--providers.swarm.useBindPortIP=true
# ...

defaultRule

defaultRule 옵션은 라벨로 규칙이 정의되지 않은 컨테이너에 적용할 라우팅 규칙을 정의해요.

유효한 Go 템플릿이어야 하며 sprig 템플릿 함수를 사용할 수 있어요. 컨테이너 이름은 ContainerName 식별자로, 서비스 이름은 Name 식별자로 접근할 수 있어요. 템플릿은 Labels 식별자로 이 컨테이너에 정의된 모든 라벨에 접근할 수 있어요.

File (YAML)

providers:
  swarm:
    defaultRule: "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
    # ...

File (TOML)

[providers.swarm]
  defaultRule = "Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
  # ...

CLI

--providers.swarm.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
# ...

기본 규칙과 Traefik 서비스

Traefik 컨테이너의 노출은 기본 규칙 메커니즘과 결합될 때, 자기 자신을 대상으로 하는 라우터를 만들어 루프에 빠지게 할 수 있어요. 이 경우 무한 루프를 방지하기 위해 Traefik은 같은 라우터에서 온 요청이면 거부하는 내부 미들웨어를 추가합니다.

constraints

constraints 옵션은 Traefik이 컨테이너 라벨에 대해 대조하여 해당 컨테이너에 라우트를 만들지 여부를 결정하는 표현식으로 설정할 수 있어요. 컨테이너 라벨 중 그 표현식과 일치하는 것이 없다면 그 컨테이너에 대한 라우트는 만들어지지 않아요. 표현식이 비어 있으면 발견된 모든 컨테이너가 포함됩니다.

표현식 문법은 아래 예시에서 보여주듯이 Label("key", "value"), LabelRegex("key", "value") 함수와 일반적인 불리언 논리에 기반해요.

제약 조건 키 제한 사항 (Constraints key limitations)

traefik.*는 구성을 위한 예약된 라벨 네임스페이스이며 커스텀 제약 조건의 키로 사용할 수 없다는 점을 유의하세요.

제약 조건 표현식 예시 (Constraints Expression Examples)

# Includes only containers having a label with key `a.label.name` and value `foo`
constraints = "Label(`a.label.name`, `foo`)"
# Excludes containers having any label with key `a.label.name` and value `foo`
constraints = "!Label(`a.label.name`, `value`)"
# With logical AND.
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
# With logical OR.
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
# With logical AND and OR, with precedence set by parentheses.
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
constraints = "LabelRegex(`a.label.name`, `a.+`)"

자세한 내용은 서비스 디스커버리 범위 제한(Restrict the Scope of Service Discovery)을 참고하세요.

File (YAML)

providers:
  swarm:
    constraints: "Label(`a.label.name`,`foo`)"
    # ...

File (TOML)

[providers.swarm]
  constraints = "Label(`a.label.name`,`foo`)"
  # ...

CLI

--providers.swarm.constraints=Label(`a.label.name`,`foo`)
# ...

라우팅 구성 (Routing Configuration)

프로바이더로 Docker를 사용할 때 Traefik은 컨테이너 라벨을 사용해 라우팅 구성을 검색해요.

전용 라우팅 섹션에서 라벨 목록을 참고하세요.

라벨로 라우팅 구성 (Routing Configuration with Labels)

기본적으로 Traefik은 독립 실행형 Docker Engine에서 컨테이너 레벨 라벨을 감시해요.

Docker Compose를 사용할 때 라벨은 "services" 객체의 labels 지시어로 지정돼요.

Docker만은 아니에요

Nomad, Terraform, Ansible 등 라벨이 있는 Docker 컨테이너를 정의할 수 있는 어떤 도구든 Traefik 및 Swarm 프로바이더와 함께 동작할 수 있다는 점을 유의하세요.

Swarm Mode에서는 Traefik이 개별 컨테이너가 아닌 서비스에서 발견되는 라벨을 사용해요.

따라서 Swarm Mode에서 compose 파일을 사용한다면 라벨은 서비스의 deploy 부분에 정의되어야 해요.

이 동작은 docker-compose 버전 3+(Compose 파일 참조)에서만 활성화됩니다.

포트 감지 (Port Detection)

Traefik은 Docker API에서 컨테이너의 개인 IP와 포트를 검색해요.

Docker Swarm은 Traefik에 포트 감지 정보를 제공하지 않아요.

따라서 traefik.http.services..loadbalancer.server.port 라벨을 사용해 통신에 사용할 포트를 지정해야 해요(Swarm의 라우팅 섹션에서 이 라벨의 참조를 확인하세요).

호스트 네트워킹 (Host networking)

호스트 네트워킹으로 구성된 컨테이너를 노출할 때 호스트의 IP 주소는 다음과 같이 해석됩니다.

  • host.docker.internal 조회 시도
  • 조회가 실패하면 host.containers.internal 조회 시도(Podman의 host.docker.internal에 해당)
  • 그 조회도 실패하면 127.0.0.1로 폴백

Linux에서 Docker 20.10.0보다 오래된 버전의 경우, host.docker.internal이 정의되려면 --add-host 플래그를 사용해 Traefik 컨테이너에 extra_host로 제공해야 해요. 예를 들어 브리지 인터페이스(기본값 docker0)의 IP 주소로 설정하려면: --add-host=host.docker.internal:172.17.0.1

IPv4 & IPv6

IPv6을 사용하는 docker 스택을 사용할 때 Traefik은 IPv6 대신 IPv4 컨테이너 IP를 먼저 사용해요. 따라서 IPv6 Docker 스택에서는 Traefik이 IPv6 컨테이너 IP를 사용합니다.

Docker API 액세스

Traefik은 동적 구성을 얻기 위해 docker 소켓에 대한 접근이 필요해요.

endpoint 지시어로 사용할 Docker API 엔드포인트를 지정할 수 있어요.

Swarm API는 매니저 노드에서만 노출되므로, Traefik을 노드 "role"에 대한 제약 조건과 함께 배포해 Traefik이 매니저 노드에 스케줄되도록 해야 해요.

Docker CLI로

docker service create \
  --constraint=node.role==manager \
  #... \

Docker Compose로

services:
  traefik:
    # ...
    deploy:
      placement:
        constraints:
          - node.role == manager

워커 노드에 Traefik 스케줄링 (Scheduling Traefik on Worker Nodes)

이전 섹션 "Docker API 액세스"의 지침에 따라, TCP로 Docker API를 노출하면 TCP 소켓에 도달할 수 있는 한 Traefik을 어떤 노드에든 스케줄할 수 있어요.

보안 메모(Security Note)를 읽고 보안 영향을 고려하세요.

좋은 예는 Bret Fisher의 저장소에서 찾을 수 있어요.

보안 메모 (Security Note)

제한 없이 Docker API에 접근하는 것은 보안 우려가 있어요: Traefik이 공격받으면 공격자가 기본 호스트에 접근할 수 있게 될 수 있어요.

Docker Daemon Attack Surface 문서에서 설명한 것처럼:

인용 (Quote)

[...] 신뢰할 수 있는 사용자만 Docker 데몬을 제어하도록 허용해야 합니다 [...]

해결책 (Solutions)

기본 Unix 소켓 파일 대신 TCP 또는 SSH로 Docker 소켓을 노출하세요. 보안 평가에 따라 AAA(Authentication, Authorization, Accounting) 개념의 다양한 구현 수준을 허용합니다.

  • "Protect the Docker daemon socket."에 설명된 대로 클라이언트 인증서로 인증
  • Tecnativa Docker Socket Proxy로 가능한 동작을 제한하도록 요청을 인가·필터링
  • Docker Authorization Plugin 메커니즘으로 인가
  • 소켓을 Docker 개인 네트워크 안에서만 노출하여 네트워킹 레벨에서 회계(Traefik만 사용 가능)
  • Traefik이 아닌 다른 컨테이너에 소켓을 노출하여 컨테이너 레벨에서 회계. 매니저 노드에는 "소켓 노출자" 컨테이너만 두고 Traefik을 워커 노드에 스케줄할 수 있게 해줌
  • SELinux 같은 메커니즘으로 커널 호출을 강제하여 Traefik의 프로세스(또는 "소켓 노출자" 프로세스)에 대해 식별된 동작 집합만 허용함으로써 커널 레벨에서 회계
  • SSH 공개 키 인증(Docker > 18.09에서 SSH 지원)
  • Docker 데몬 소켓을 노출하는 HTTP 프록시를 통한 HTTP Basic 인증

더 많은 리소스와 예시 (More Resources and Examples)

  • "/var/run/docker.sock을 마운트하는 것에 대해 편집증적이신가요?"
  • Docker Captain Bret Fisher와의 Traefik과 Docker에 대한 논의
  • Liz Rice의 KubeCon EU 2018 기조연설, Running with Scissors
  • Docker 소켓을 노출하지 마세요(컨테이너에게도)
  • /var/run/docker.sock 파일 공유에 관한 Stack Overflow 스레드
  • DinD로 할까 말까
  • Docker 소켓 보안에 관한 Traefik 이슈 GH-4174
  • Socat으로 Docker 활동 검사하기
  • Traefik을 워커 노드에서 실행하기
  • Tecnativa의 Docker Socket Proxy

프로덕션에서 Traefik OSS를 사용하고 계신가요?

직장에서 Traefik을 사용하고 있다면 기업용 API 게이트웨이 기능이나 Traefik OSS에 대한 상용 지원을 고려해 보세요.

  • API 게이트웨이 데모 영상 보기
  • 24/7/365 OSS 지원 요청하기

Traefik OSS에 API 게이트웨이 기능을 추가하는 일은 빠르고 매끄러워요. 교체(rip and replace)가 필요 없고 모든 구성이 그대로 유지됩니다. 이 짧은 영상에서 실제 동작을 확인해 보세요.

더 알아보기 (Learn more)