Traefik이란?
Traefik은 클라우드 네이티브 환경에 최적화된 리버스 프록시 겸 로드밸런서다. Nginx와 달리 설정 파일을 수동으로 수정하고 reload할 필요 없이, Docker·Kubernetes·Consul 등에서 서비스가 뜨면 자동으로 라우팅 규칙을 감지하고 적용한다. Let’s Encrypt 인증서도 자동 발급·갱신한다.
이 글에서는 Traefik의 핵심 아키텍처, Provider 별 동적 설정, Middleware 체이닝, TLS 자동화, 그리고 Docker Compose·Kubernetes 환경의 실전 패턴을 심화하여 다룬다.
핵심 아키텍처: EntryPoint → Router → Middleware → Service
┌──────────────┐
│ EntryPoint │ ← 포트 리스닝 (80, 443)
└──────┬───────┘
│
┌──────▼───────┐
│ Router │ ← 요청 매칭 (Host, Path, Header)
└──────┬───────┘
│
┌──────▼───────┐
│ Middleware │ ← 요청 변환 (인증, Rate Limit, Redirect)
│ (체이닝) │
└──────┬───────┘
│
┌──────▼───────┐
│ Service │ ← 백엔드 서버 (로드밸런싱)
└──────────────┘
- EntryPoint: Traefik이 수신하는 네트워크 진입점 (포트)
- Router: 요청을 규칙에 따라 분기 (Host, PathPrefix, Headers 등)
- Middleware: 요청/응답을 가공 (인증, 압축, 헤더 추가 등)
- Service: 실제 트래픽을 받는 백엔드 서버 그룹
정적 설정: traefik.yml
EntryPoint와 Provider는 정적 설정에서 정의한다. 이 설정은 Traefik 시작 시 한 번만 로드된다.
# traefik.yml
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
# Let's Encrypt 자동 인증서
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /etc/traefik/acme.json
httpChallenge:
entryPoint: web
# Provider 설정
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false # 명시적 label만 노출
watch: true
file:
directory: /etc/traefik/dynamic
watch: true
# Dashboard
api:
dashboard: true
insecure: false
# 로깅
log:
level: INFO
accessLog:
filePath: /var/log/traefik/access.log
format: json
fields:
headers:
defaultMode: drop
names:
User-Agent: keep
X-Forwarded-For: keep
Docker Provider: Label 기반 동적 라우팅
Docker 환경에서 Traefik은 컨테이너의 label을 읽어 라우팅을 자동 설정한다. 컨테이너가 시작/중지되면 실시간으로 반영된다.
# docker-compose.yml
version: "3.8"
services:
traefik:
image: traefik:v3.1
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./acme.json:/etc/traefik/acme.json
labels:
# Dashboard 라우팅
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=auth"
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$xyz"
api:
image: myapp-api:latest
labels:
- "traefik.enable=true"
# Router 규칙
- "traefik.http.routers.api.rule=Host(`api.example.com`)"
- "traefik.http.routers.api.entrypoints=websecure"
- "traefik.http.routers.api.tls.certresolver=letsencrypt"
# Service (포트 매핑)
- "traefik.http.services.api.loadbalancer.server.port=3000"
# Middleware 체이닝
- "traefik.http.routers.api.middlewares=rate-limit,compress,security-headers"
frontend:
image: myapp-web:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`example.com`)"
- "traefik.http.routers.web.entrypoints=websecure"
- "traefik.http.routers.web.tls.certresolver=letsencrypt"
- "traefik.http.services.web.loadbalancer.server.port=80"
고급 Router 규칙
Router는 다양한 매칭 규칙을 논리 연산자로 조합할 수 있다.
# Host + Path 조합
- "traefik.http.routers.api-v2.rule=Host(`api.example.com`) && PathPrefix(`/v2`)"
# Header 기반 라우팅 (A/B 테스트, Canary)
- "traefik.http.routers.canary.rule=Host(`api.example.com`) && Headers(`X-Canary`, `true`)"
# Query Parameter 매칭
- "traefik.http.routers.debug.rule=Host(`api.example.com`) && Query(`debug=true`)"
# 정규식 PathPrefix
- "traefik.http.routers.user.rule=Host(`api.example.com`) && PathPrefix(`/users/{id:[0-9]+}`)"
# 우선순위 제어 (높을수록 먼저 매칭)
- "traefik.http.routers.specific.priority=100"
- "traefik.http.routers.catchall.priority=1"
Middleware 실전 패턴
1. Rate Limiting
# 동적 설정 파일: /etc/traefik/dynamic/middlewares.yml
http:
middlewares:
rate-limit:
rateLimit:
average: 100 # 초당 평균 요청 수
burst: 200 # 버스트 허용량
period: 1s
sourceCriterion:
ipStrategy:
depth: 1 # X-Forwarded-For 첫 번째 IP 기준
2. IP Whitelist + BasicAuth 조합
http:
middlewares:
admin-access:
chain:
middlewares:
- admin-ip
- admin-auth
admin-ip:
ipAllowList:
sourceRange:
- "10.0.0.0/8"
- "172.16.0.0/12"
admin-auth:
basicAuth:
users:
- "admin:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
removeHeader: true # 백엔드에 Auth 헤더 전달 안 함
3. 보안 헤더 일괄 적용
http:
middlewares:
security-headers:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
customResponseHeaders:
X-Robots-Tag: "noindex,nofollow"
Referrer-Policy: "strict-origin-when-cross-origin"
contentSecurityPolicy: "default-src 'self'"
4. Circuit Breaker
http:
middlewares:
circuit-breaker:
circuitBreaker:
expression: "LatencyAtQuantileMS(50.0) > 1000 || NetworkErrorRatio() > 0.10"
checkPeriod: 10s
fallbackDuration: 15s
recoveryDuration: 30s
5. 요청 변환 (StripPrefix, AddPrefix)
# /api/users → 백엔드에서는 /users 로 수신
http:
middlewares:
strip-api:
stripPrefix:
prefixes:
- "/api"
# 반대로 prefix 추가
add-v1:
addPrefix:
prefix: "/v1"
Weighted Round Robin: 카나리 배포
# docker-compose.yml
services:
api-stable:
image: myapp-api:1.0
labels:
- "traefik.enable=true"
- "traefik.http.services.api-stable.loadbalancer.server.port=3000"
api-canary:
image: myapp-api:1.1-rc
labels:
- "traefik.enable=true"
- "traefik.http.services.api-canary.loadbalancer.server.port=3000"
# 동적 설정으로 가중치 분배
# dynamic/canary.yml
http:
services:
api-weighted:
weighted:
services:
- name: api-stable
weight: 90
- name: api-canary
weight: 10
routers:
api:
rule: "Host(`api.example.com`)"
service: api-weighted
entrypoints:
- websecure
Kubernetes IngressRoute (CRD)
Kubernetes에서 Traefik은 표준 Ingress 대신 IngressRoute CRD를 사용하면 더 세밀한 제어가 가능하다.
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-route
namespace: production
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.example.com`) && PathPrefix(`/v2`)
kind: Rule
priority: 10
middlewares:
- name: rate-limit
namespace: traefik
- name: security-headers
namespace: traefik
services:
- name: api-service
port: 3000
weight: 90
strategy: RoundRobin
- name: api-canary
port: 3000
weight: 10
tls:
certResolver: letsencrypt
domains:
- main: api.example.com
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
namespace: traefik
spec:
rateLimit:
average: 100
burst: 200
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: compress
namespace: traefik
spec:
compress:
excludedContentTypes:
- text/event-stream
TCP/UDP 라우팅
Traefik은 HTTP 외에 TCP/UDP 프로토콜도 라우팅할 수 있다. DB 프록시나 gRPC에 유용하다.
# traefik.yml - EntryPoint 추가
entryPoints:
postgres:
address: ":5432"
mysql:
address: ":3306"
# 동적 설정: TCP 라우팅
tcp:
routers:
postgres:
entryPoints:
- postgres
rule: "HostSNI(`db.example.com`)"
tls:
passthrough: true
service: postgres-backend
services:
postgres-backend:
loadBalancer:
servers:
- address: "db-primary:5432"
- address: "db-replica:5432"
Health Check와 Failover
http:
services:
api:
loadBalancer:
servers:
- url: "http://api-1:3000"
- url: "http://api-2:3000"
- url: "http://api-3:3000"
healthCheck:
path: /health
interval: 10s
timeout: 3s
scheme: http
headers:
X-Health-Check: "traefik"
sticky:
cookie:
name: server_id
secure: true
httpOnly: true
Metrics·Tracing 연동
# traefik.yml
metrics:
prometheus:
entryPoint: metrics
addEntryPointsLabels: true
addRoutersLabels: true
addServicesLabels: true
buckets:
- 0.01
- 0.05
- 0.1
- 0.5
- 1.0
- 5.0
tracing:
otlp:
grpc:
endpoint: "otel-collector:4317"
insecure: true
entryPoints:
metrics:
address: ":8082"
운영 Best Practices
| 항목 | 권장 설정 | 이유 |
|---|---|---|
| exposedByDefault | false | 의도하지 않은 서비스 노출 방지 |
| Docker Socket | 읽기 전용 (:ro) | 컨테이너 조작 권한 제한 |
| acme.json 권한 | chmod 600 | 인증서 키 보호 |
| Access Log | JSON 포맷 | 구조화 로그로 분석 용이 |
| Dashboard | BasicAuth + IP 제한 | 관리 인터페이스 보안 |
마무리
Traefik은 동적 서비스 디스커버리가 핵심이다. Docker label이나 Kubernetes CRD만으로 라우팅을 선언하면 Traefik이 실시간으로 반영한다. Nginx처럼 설정 파일을 수정하고 reload하는 과정이 없어 마이크로서비스 환경에서 운영 부담이 크게 줄어든다.
실무 적용 시 핵심 체크리스트:
- Middleware 체이닝: Rate Limit → Auth → Security Headers 순서로 조합
- Let’s Encrypt 자동화: HTTP Challenge로 인증서 발급·갱신 자동화
- Weighted Service: 카나리 배포 시 가중치로 트래픽 점진적 이동
- Health Check: 백엔드 장애 시 자동 failover
K8s Gateway API 라우팅 심화 글에서 Kubernetes 표준 라우팅과 비교하고, HAProxy 로드밸런싱 심화와 L4/L7 프록시 선택 기준을 함께 참고하면 좋다.