Skip to content
Published on

Traefik 고급 — 미들웨어 설계, TLS 옵션, 멀티 프로바이더

Authors

들어가며

Traefik을 단순한 리버스 프록시로만 쓰다가, 어느 순간 "인증을 모든 라우트에 공통으로 걸고 싶다", "특정 클라이언트만 mTLS로 받고 싶다", "신규 버전을 트래픽 5퍼센트만 흘려보고 싶다" 같은 요구가 쏟아지기 시작하면 기본 설정만으로는 한계에 부딪힙니다. 이 글은 그 다음 단계, 즉 Traefik을 프로덕션에서 본격적으로 운영할 때 필요한 고급 기능을 다룹니다.

실무에서 마주치는 전형적인 상황은 이렇습니다. 마이크로서비스가 수십 개로 늘어나면서 각 서비스마다 인증 로직을 중복 구현하는 것이 비효율적이라는 판단이 섭니다. 동시에 보안팀은 외부 파트너 연동 구간에 상호 TLS 인증을 요구하고, 플랫폼팀은 무중단 배포를 위해 카나리와 블루그린을 표준화하고 싶어 합니다. 이 모든 요구가 Traefik의 미들웨어, TLSOption, 서비스 고급 기능, 멀티 프로바이더 조합으로 해결됩니다.

2026년 현재 Kubernetes의 Ingress API는 사실상 동결 상태입니다. 새로운 기능은 더 이상 Ingress에 추가되지 않고, 후속 표준인 Gateway API로 무게 중심이 옮겨가고 있습니다. Traefik은 IngressRoute라는 자체 CRD와 더불어 Gateway API 프로바이더를 모두 지원하므로, 이 글에서 다루는 고급 패턴이 Gateway API의 필터 및 정책 리소스와 어떻게 대응되는지도 함께 짚어보겠습니다.

이 글의 목표는 단순한 기능 나열이 아니라, 각 기능을 언제 어떻게 조합해야 하는지에 대한 설계 감각을 전달하는 것입니다. 따라서 개념 설명 뒤에는 실제로 적용 가능한 CRD YAML과 명령어를 충분히 담았습니다.

미들웨어의 기본 개념

미들웨어는 요청이 백엔드 서비스에 도달하기 전, 또는 응답이 클라이언트로 돌아가기 전에 끼어들어 동작을 변형하는 처리 단위입니다. 헤더 추가, 인증, 속도 제한, 경로 재작성, 압축 등 횡단 관심사를 라우트 정의에서 분리해 재사용할 수 있게 해줍니다.

Traefik에서 미들웨어는 Kubernetes CRD인 Middleware로 선언하고, IngressRoute의 라우트 규칙에서 참조합니다. 핵심은 미들웨어가 순서대로 적용되는 체인이라는 점입니다. 체인의 앞쪽 미들웨어가 요청을 거부하면 뒤쪽 미들웨어와 백엔드는 호출되지 않습니다.

요청 흐름

Client
  |
  v
[EntryPoint :websecure]
  |
  v
[Router 매칭]  --- Host(api.example.com) && PathPrefix(/v1)
  |
  v
[Middleware 체인]
   1. rate-limit        (속도 제한)
   2. forward-auth      (SSO 인증)
   3. strip-prefix      (경로 재작성)
   4. secure-headers    (보안 헤더)
  |
  v
[Service]  --- weighted / mirroring / sticky
  |
  v
Pod (백엔드)

미들웨어를 설계할 때 가장 중요한 원칙은 순서입니다. 인증보다 속도 제한을 먼저 두면 인증 실패 트래픽도 속도 제한 카운트에 포함되어 공격 표면을 줄이는 데 도움이 됩니다. 반대로 경로 재작성은 인증 이후에 두어야 백엔드가 기대하는 경로로 전달됩니다.

미들웨어 체인 설계 패턴

여러 미들웨어를 매번 라우트마다 나열하는 것은 실수와 중복을 부릅니다. Traefik은 chain 타입 미들웨어를 제공하여 자주 쓰는 조합을 하나의 이름으로 묶을 수 있게 합니다.

먼저 개별 미들웨어를 정의합니다.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: rate-limit
  namespace: edge
spec:
  rateLimit:
    average: 100
    burst: 50
    period: 1s
    sourceCriterion:
      ipStrategy:
        depth: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: secure-headers
  namespace: edge
spec:
  headers:
    browserXssFilter: true
    contentTypeNosniff: true
    frameDeny: true
    stsSeconds: 31536000
    stsIncludeSubdomains: true
    stsPreload: true
    customResponseHeaders:
      X-Robots-Tag: "noindex, nofollow"
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: strip-api-prefix
  namespace: edge
spec:
  stripPrefix:
    prefixes:
      - /v1

이제 이들을 chain으로 묶습니다.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: api-edge-chain
  namespace: edge
spec:
  chain:
    middlewares:
      - name: rate-limit
      - name: forward-auth-sso
      - name: strip-api-prefix
      - name: secure-headers

라우트에서는 chain 하나만 참조하면 됩니다.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: api-route
  namespace: edge
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`api.example.com`) && PathPrefix(`/v1`)
      kind: Rule
      middlewares:
        - name: api-edge-chain
      services:
        - name: api-backend
          port: 8080
  tls:
    secretName: api-example-com-tls

이 구조의 장점은 명확합니다. 공통 정책을 한 곳에서 관리하므로 보안 헤더 정책을 바꿀 때 모든 라우트에 즉시 반영됩니다. 또한 라우트 정의가 간결해져 가독성과 리뷰 효율이 올라갑니다.

forwardAuth로 SSO 구현하기

중앙 인증 서버에 인증 판단을 위임하는 패턴이 forwardAuth입니다. Traefik은 요청을 백엔드로 보내기 전에 지정된 인증 서버로 사본을 보내고, 인증 서버가 2xx를 반환하면 통과시키고 그 외에는 인증 서버의 응답을 그대로 클라이언트에 돌려줍니다.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: forward-auth-sso
  namespace: edge
spec:
  forwardAuth:
    address: http://auth-service.auth.svc.cluster.local:4180/oauth2/auth
    trustForwardHeader: true
    authResponseHeaders:
      - X-Auth-Request-User
      - X-Auth-Request-Email
      - X-Auth-Request-Groups
    authRequestHeaders:
      - Cookie
      - Authorization

여기서 authResponseHeaders가 핵심입니다. 인증 서버가 검증 결과로 사용자 정보를 헤더에 담아 돌려주면, Traefik이 그 헤더만 추출해 백엔드로 전달합니다. 백엔드는 토큰 검증을 직접 하지 않고도 신뢰할 수 있는 사용자 식별 정보를 받게 됩니다.

oauth2-proxy 같은 인증 게이트웨이와 결합하면 OIDC 기반 SSO가 완성됩니다. 인증되지 않은 요청은 oauth2-proxy가 로그인 페이지로 리다이렉트하도록 별도의 sign_in 경로를 라우팅해 주어야 합니다.

forwardAuth 동작 순서

Client --(1) 요청--> Traefik
Traefik --(2) /oauth2/auth 검증--> auth-service
auth-service --(3a) 200 OK + 사용자 헤더--> Traefik --(4) 백엔드 호출--> Backend
auth-service --(3b) 401/302--> Traefik --(5) 동일 응답 반환--> Client

trustForwardHeader를 true로 두면 X-Forwarded-* 헤더를 신뢰하는데, 이는 Traefik 앞단을 신뢰할 수 있는 경우에만 켜야 합니다. 외부에 직접 노출된 엔트리포인트라면 클라이언트가 위조한 헤더를 그대로 믿게 되므로 주의가 필요합니다.

TLSOption으로 TLS 정교하게 제어하기

기본 TLS 설정만으로는 부족한 경우가 많습니다. 특정 라우트는 TLS 1.3만 허용하고, 다른 라우트는 레거시 클라이언트를 위해 더 넓은 cipher suite를 열어야 할 수 있습니다. TLSOption CRD가 이 세밀한 제어를 담당합니다.

apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: modern-tls
  namespace: edge
spec:
  minVersion: VersionTLS13
  sniStrict: true
  alpnProtocols:
    - h2
    - http/1.1
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: intermediate-tls
  namespace: edge
spec:
  minVersion: VersionTLS12
  cipherSuites:
    - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  curvePreferences:
    - CurveP521
    - CurveP384

minVersion으로 최소 TLS 버전을 강제하고, sniStrict를 true로 두면 SNI가 없는 연결을 거부합니다. 주의할 점은 TLS 1.3에서는 cipherSuites 설정이 무시된다는 것입니다. cipher suite 제어는 TLS 1.2 구간에서만 의미가 있으므로, 최신 보안 정책을 적용하려면 minVersion을 VersionTLS13으로 올리는 편이 단순하고 안전합니다.

라우트에서 TLSOption을 참조하는 방법은 다음과 같습니다.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: secure-route
  namespace: edge
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`secure.example.com`)
      kind: Rule
      services:
        - name: secure-backend
          port: 8443
  tls:
    secretName: secure-example-com-tls
    options:
      name: modern-tls
      namespace: edge

mTLS와 TLSStore

상호 TLS 인증은 서버뿐 아니라 클라이언트도 인증서를 제시하도록 요구합니다. 외부 파트너 연동, 서비스 간 보안 통신, 제로 트러스트 환경에서 흔히 쓰입니다. TLSOption의 clientAuth 섹션에서 설정합니다.

apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: mtls-required
  namespace: edge
spec:
  minVersion: VersionTLS13
  clientAuth:
    secretNames:
      - partner-ca-bundle
    clientAuthType: RequireAndVerifyClientCert

clientAuthType의 값에 따라 동작이 크게 달라집니다. 아래 표로 정리합니다.

clientAuthType클라이언트 인증서 요구검증 수행인증서 없을 때
NoClientCert안 함안 함통과
RequestClientCert요청만 함안 함통과
RequireAnyClientCert강제안 함거부
VerifyClientCertIfGiven선택제시되면 검증통과
RequireAndVerifyClientCert강제항상 검증거부

프로덕션에서 진정한 mTLS를 원한다면 RequireAndVerifyClientCert를 선택해야 합니다. RequestClientCert는 검증을 하지 않으므로 단순히 인증서를 수집하는 용도로만 적합합니다.

TLSStore는 기본 인증서를 정의하는 리소스입니다. SNI에 매칭되는 인증서가 없을 때 사용할 폴백 인증서를 지정합니다. 클러스터 전체의 기본값은 이름이 default인 TLSStore로 설정합니다.

apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
  name: default
  namespace: edge
spec:
  defaultCertificate:
    secretName: wildcard-example-com-tls

이 설정이 없으면 매칭되지 않는 SNI 요청에 Traefik이 자체 서명한 기본 인증서를 제시하여 브라우저 경고를 유발할 수 있습니다.

서비스 고급 기능

Traefik의 서비스는 단순한 백엔드 지정 이상을 표현할 수 있습니다. 가중치 기반 라운드로빈, 미러링, 스티키 세션이 대표적입니다.

가중치 기반 라우팅 (weighted)

여러 백엔드에 비율을 정해 트래픽을 분배합니다. 카나리 배포의 기반이 됩니다.

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
  name: api-weighted
  namespace: edge
spec:
  weighted:
    services:
      - name: api-stable
        port: 8080
        weight: 90
      - name: api-canary
        port: 8080
        weight: 10

stable에 90, canary에 10의 가중치를 주면 대략 10퍼센트의 트래픽이 신규 버전으로 흐릅니다. IngressRoute에서는 이 TraefikService를 일반 서비스처럼 참조합니다.

미러링 (mirroring)

실제 트래픽의 사본을 별도 백엔드로 복제해 보냅니다. 응답은 무시되므로 클라이언트에 영향을 주지 않고 신규 버전을 실 트래픽으로 테스트할 수 있습니다.

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
  name: api-mirrored
  namespace: edge
spec:
  mirroring:
    name: api-stable
    port: 8080
    mirrors:
      - name: api-shadow
        port: 8080
        percent: 20

위 설정은 트래픽의 20퍼센트를 api-shadow로 복제합니다. 섀도우 트래픽 테스트는 신규 버전이 실제 부하 패턴에서 어떻게 동작하는지 안전하게 검증하는 강력한 도구입니다.

스티키 세션 (sticky)

같은 클라이언트를 같은 백엔드로 고정합니다. 세션 상태를 메모리에 두는 레거시 애플리케이션에 필요합니다.

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
  name: api-sticky
  namespace: edge
spec:
  weighted:
    services:
      - name: api-stable
        port: 8080
        weight: 1
    sticky:
      cookie:
        name: traefik_backend
        secure: true
        httpOnly: true
        sameSite: strict

가능하다면 애플리케이션을 무상태로 설계해 스티키 세션 의존을 없애는 편이 확장성과 복원력 면에서 유리합니다.

멀티 프로바이더 조합

Traefik은 여러 설정 소스를 동시에 읽어들이는 멀티 프로바이더 아키텍처를 가집니다. Kubernetes CRD, Kubernetes Ingress, 파일, Consul, Gateway API 등을 함께 활성화할 수 있습니다.

providers:
  kubernetesCRD:
    allowCrossNamespace: false
    allowExternalNameServices: false
  kubernetesGateway: {}
  file:
    directory: /etc/traefik/dynamic
    watch: true

각 프로바이더는 고유한 네임스페이스 접두사를 가진 라우터와 서비스를 생성합니다. 한 클러스터에서 CRD 기반 라우팅을 주로 쓰면서, 클러스터 외부의 레거시 서비스는 파일 프로바이더로 정적 정의하고, 신규 표준 도입을 위해 Gateway API를 점진적으로 병행하는 식의 전략이 가능합니다.

프로바이더주 용도동적 갱신비고
kubernetesCRDIngressRoute 등 Traefik 네이티브가장 기능 풍부
kubernetesGatewayGateway API 표준미래 표준, 이식성 높음
kubernetesIngress표준 Ingress 호환API 동결, 신규 기능 없음
file클러스터 외부 정적 정의예 (watch)레거시 연동에 유용

멀티 프로바이더를 쓸 때 주의할 점은 라우터 우선순위입니다. 서로 다른 프로바이더가 겹치는 호스트를 정의하면 우선순위 규칙에 따라 충돌이 발생할 수 있으므로, 호스트와 경로 네임스페이스를 프로바이더별로 명확히 분리하는 것이 좋습니다.

Gateway API와의 대응 관계

2026년의 방향성을 고려하면 Gateway API로의 이전 경로를 알아두는 것이 중요합니다. Traefik의 고급 미들웨어 기능 상당수가 Gateway API의 필터와 정책 리소스로 대응됩니다.

Traefik 기능Gateway API 대응
Middleware (헤더 조작)HTTPRoute의 RequestHeaderModifier 필터
Middleware (경로 재작성)HTTPRoute의 URLRewrite 필터
Middleware (리다이렉트)HTTPRoute의 RequestRedirect 필터
weighted TraefikServiceHTTPRoute backendRefs의 weight
TLSOptionGateway listener의 TLS 설정 및 정책
forwardAuthExtAuth 관련 확장 (구현체별 상이)

주의할 것은 Gateway API의 코어 사양이 아직 forwardAuth나 rate limit 같은 모든 고급 미들웨어를 표준으로 포괄하지는 못한다는 점입니다. 이런 기능은 구현체별 확장이나 ExtensionRef 필터로 처리되며, Traefik도 일부를 ExtensionRef로 자사 Middleware를 연결하는 방식으로 지원합니다. 따라서 완전한 이식성을 원한다면 코어 필터로 표현 가능한 기능만 사용하고, 고급 기능은 구현체 종속성을 감수하는 절충이 필요합니다.

Yaegi 플러그인

Traefik은 Yaegi라는 Go 인터프리터를 내장하여, 컴파일 없이 Go로 작성한 미들웨어 플러그인을 동적으로 로드할 수 있습니다. 사내 정책을 코드로 구현해야 할 때 유용합니다.

정적 설정에서 플러그인을 선언합니다.

experimental:
  plugins:
    customHeader:
      moduleName: github.com/example/traefik-custom-header
      version: v0.2.0

플러그인의 핵심 골격은 다음과 같은 Go 코드입니다.

package customheader

import (
	"context"
	"net/http"
)

type Config struct {
	HeaderName  string `json:"headerName,omitempty"`
	HeaderValue string `json:"headerValue,omitempty"`
}

func CreateConfig() *Config {
	return &Config{}
}

type CustomHeader struct {
	next        http.Handler
	name        string
	headerName  string
	headerValue string
}

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
	return &CustomHeader{
		next:        next,
		name:        name,
		headerName:  config.HeaderName,
		headerValue: config.HeaderValue,
	}, nil
}

func (c *CustomHeader) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	req.Header.Set(c.headerName, c.headerValue)
	c.next.ServeHTTP(rw, req)
}

선언한 플러그인은 Middleware CRD에서 plugin 타입으로 참조합니다.

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: add-custom-header
  namespace: edge
spec:
  plugin:
    customHeader:
      headerName: X-Internal-Trace
      headerValue: edge-gateway

Yaegi는 인터프리터이므로 컴파일된 Go보다 실행 속도가 느립니다. 핫 패스에서 무거운 연산을 수행하는 플러그인은 지연 시간에 영향을 줄 수 있으므로, 성능이 중요한 로직은 별도 사이드카나 정식 빌드 미들웨어로 분리하는 것을 권장합니다.

카나리와 블루그린 배포

앞서 본 weighted 서비스를 활용하면 카나리 배포를 손쉽게 구현할 수 있습니다. 핵심은 가중치를 점진적으로 조정하는 것입니다.

# 1단계: 카나리에 5퍼센트만 분배
kubectl patch traefikservice api-weighted -n edge --type merge \
  -p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":95},{"name":"api-canary","port":8080,"weight":5}]}}}'

# 메트릭 관찰 후 문제 없으면 25퍼센트로 증가
kubectl patch traefikservice api-weighted -n edge --type merge \
  -p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":75},{"name":"api-canary","port":8080,"weight":25}]}}}'

# 최종 전환: 100퍼센트
kubectl patch traefikservice api-weighted -n edge --type merge \
  -p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":0},{"name":"api-canary","port":8080,"weight":100}]}}}'

블루그린은 두 환경을 미리 띄워두고 라우터의 서비스 참조를 한 번에 전환하는 방식입니다. 가중치 0과 100을 즉시 교체하거나, 라우트의 서비스명을 blue에서 green으로 바꾸면 됩니다.

카나리 진행 곡선

트래픽 비율
100% |                          ____ green/canary
     |                      ___/
 50% |              ______/
     |        _____/
  5% |  _____/
   0% |_/_______________________________ 시간
       T0   T1   T2   T3   T4   T5
       각 단계마다 메트릭/에러율 게이트 통과 확인

각 단계 사이에 반드시 메트릭 게이트를 두어야 합니다. 에러율, 지연 시간, 포화도 같은 지표가 임계치를 넘으면 자동 또는 수동으로 롤백하는 절차를 정해두는 것이 안전한 운영의 기본입니다.

관측: 메트릭과 트레이싱

고급 라우팅을 운영하려면 무엇이 어디로 흐르는지 보이게 만들어야 합니다. Traefik은 Prometheus 메트릭과 OpenTelemetry 트레이싱을 기본 지원합니다.

metrics:
  prometheus:
    addEntryPointsLabels: true
    addRoutersLabels: true
    addServicesLabels: true
    buckets:
      - 0.1
      - 0.3
      - 1.2
      - 5.0
tracing:
  otlp:
    http:
      endpoint: http://otel-collector.observability.svc.cluster.local:4318/v1/traces

addServicesLabels를 켜면 서비스별로 요청 수와 지연 시간을 분리해 볼 수 있어, 카나리 배포 시 stable과 canary의 성능을 직접 비교할 수 있습니다. 다만 라벨 카디널리티가 높아지면 Prometheus 부하가 커지므로, 라우터가 매우 많은 환경에서는 addRoutersLabels를 신중하게 켜야 합니다.

트레이싱은 forwardAuth 같은 외부 호출이 끼어든 요청 경로에서 병목을 찾는 데 특히 유용합니다. 인증 서버 응답이 느린지, 백엔드가 느린지를 스팬 단위로 구분해 볼 수 있습니다.

프로덕션 함정

실제 운영에서 자주 마주치는 함정을 정리합니다.

첫째, 미들웨어 네임스페이스 참조 누락입니다. 미들웨어를 다른 네임스페이스에서 참조할 때는 이름만으로는 찾지 못합니다. provider 접두사 형식인 이름at네임스페이스at프로바이더 규칙을 따르거나 같은 네임스페이스에 두어야 합니다. 크로스 네임스페이스 참조는 allowCrossNamespace 설정에도 의존합니다.

둘째, TLS 1.3에서 cipherSuites가 무시되는 문제입니다. 보안 스캐너가 특정 cipher를 지적해 cipherSuites를 조정했는데도 변화가 없다면, 십중팔구 연결이 TLS 1.3으로 협상되고 있기 때문입니다.

셋째, forwardAuth의 헤더 위조 위험입니다. trustForwardHeader를 켠 채 엔트리포인트를 외부에 직접 노출하면 클라이언트가 X-Forwarded-User 같은 헤더를 위조할 수 있습니다. 신뢰 경계 밖에서는 인증 관련 헤더를 반드시 제거하는 미들웨어를 앞단에 두어야 합니다.

넷째, 스티키 세션과 무중단 배포의 충돌입니다. 스티키 쿠키가 특정 파드에 고정되어 있으면, 그 파드가 롤링 업데이트로 종료될 때 세션이 끊깁니다. 세션 드레이닝과 graceful shutdown을 함께 설계해야 합니다.

다섯째, 미러링의 부작용입니다. 미러링 대상이 쓰기 작업을 수행하는 백엔드라면 데이터가 이중으로 변경될 수 있습니다. 미러는 반드시 읽기 전용이거나 격리된 환경이어야 합니다.

트러블슈팅

문제가 생겼을 때 확인 순서를 정해두면 진단이 빨라집니다.

# 라우터, 미들웨어, 서비스가 정상 등록되었는지 API로 확인
kubectl port-forward -n edge deploy/traefik 8080:8080
curl -s http://localhost:8080/api/http/routers | jq '.[] | {name, status, rule}'
curl -s http://localhost:8080/api/http/middlewares | jq '.[] | {name, status}'

# CRD 적용 상태와 이벤트 확인
kubectl describe ingressroute api-route -n edge
kubectl get events -n edge --sort-by=.lastTimestamp

# TLS 핸드셰이크 및 인증서 확인
openssl s_client -connect secure.example.com:443 -servername secure.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject

# mTLS 클라이언트 인증서로 호출 테스트
curl -v --cert client.crt --key client.key https://secure.example.com/health

대시보드 API의 status 필드가 enabled가 아니라면 설정 오류가 있다는 뜻입니다. 미들웨어가 warning 상태라면 참조 대상이 없거나 네임스페이스가 맞지 않는 경우가 대부분입니다.

로그 레벨을 일시적으로 DEBUG로 올리면 라우터 매칭과 미들웨어 적용 과정을 상세히 추적할 수 있습니다. 다만 프로덕션에서는 로그량이 폭증하므로 진단 후 반드시 원래대로 되돌려야 합니다.

마치며: 프로덕션 체크리스트

Traefik의 고급 기능은 강력하지만, 잘못 조합하면 디버깅이 어려운 장애로 이어집니다. 아래 체크리스트로 배포 전 점검을 권장합니다.

  • 미들웨어 체인의 순서가 의도대로인가 (속도 제한 → 인증 → 재작성 → 보안 헤더)
  • forwardAuth의 trustForwardHeader가 신뢰 경계 안에서만 켜져 있는가
  • 외부 노출 엔트리포인트에서 인증 관련 헤더를 정리하는 미들웨어가 앞단에 있는가
  • TLSOption의 minVersion이 보안 정책에 맞게 설정되었는가 (TLS 1.3 권장)
  • mTLS 구간은 RequireAndVerifyClientCert로 검증까지 수행하는가
  • TLSStore에 default 인증서가 지정되어 매칭 실패 시 경고를 방지하는가
  • 카나리 단계마다 메트릭 게이트와 롤백 절차가 정의되어 있는가
  • 미러링 대상이 읽기 전용이거나 격리되어 데이터 이중 변경이 없는가
  • 스티키 세션 사용 시 graceful shutdown과 세션 드레이닝이 설계되었는가
  • Prometheus 라벨 카디널리티가 관리 가능한 수준인가
  • Yaegi 플러그인이 핫 패스의 지연을 유발하지 않는가
  • 멀티 프로바이더 간 호스트/경로 충돌이 없는가
  • Gateway API 이전 시 코어 필터로 대체 가능한 기능과 종속 기능을 구분했는가

이 항목들을 습관처럼 점검하면, Traefik을 단순한 프록시에서 신뢰할 수 있는 프로덕션 게이트웨이로 끌어올릴 수 있습니다. 다음 단계로는 cert-manager와의 자동 인증서 발급 연동, 그리고 Gateway API 기반 라우팅으로의 점진적 이전을 검토해 보시기를 권합니다.

참고 자료