Traefik v2에서 v3로 업그레이드 시 주의사항
Traefik v3에서 swarmMode나 pilot 옵션이 static config에 포함되어 있으면 실행이 되지 않습니다. 호환되지 않는 옵션 오류를 해결하고 ipAllowList 등 변경된 rule을 안전하게 마이그레이션하는 방법을 확인하세요.
Traefik v2와 v3의 차이점
Traefik v2에서 v3로의 마이그레이션은 주로 이름 변경 작업입니다. 대표적인 예로 ipWhiteList middleware가 ipAllowList로 변경되었습니다. 그 외에 v3는 router rule 구문을 강화했습니다(PathPrefix의 regex 기능이 삭제되었으며, 일부 matcher가 이름 변경되거나 삭제되었습니다). 또한 몇몇 provider와 option을 완전히 제거했습니다. 하지만 entrypoints, ACME certificate 설정, Docker labels 워크플로우 및 acme.json를 포함한 나머지 기능은 그대로 유지됩니다. v3에는 v2 rule 구문을 지원하는 compatibility mode가 포함되어 있습니다. 따라서 바이너리를 먼저 업그레이드한 후, 위험을 줄이기 위해 서비스를 하나씩 순차적으로 rule을 재작성할 수 있습니다.
이 가이드는 the Traefik reverse proxy guide의 label 기반 Docker Compose 설정을 기준으로 합니다. 해당 페이지는 v3 전용이며, 이 페이지는 traefik:v2 태그를 사용하는 환경을 위한 것입니다.
이름 변경 및 삭제 사항
- HTTP 및 TCP middleware 모두에서
ipWhiteList가ipAllowList으로 변경되었습니다. 내부 옵션은 변경되지 않았으므로sourcerange의 의미는 동일합니다. v3.5를 포함한 현재 v3 릴리스는 이전 이름을 deprecated alias로 허용하며 기존 목록을 유지하므로, 이 변경으로 인해 시스템이 중단되지는 않습니다. 하지만 이름 변경을 수행하십시오. 해당 alias는 삭제될 예정이며, 별도의 경고 없이 deprecation list에서 사라집니다. providers.docker.swarmMode=true가 삭제되었습니다. Swarm은providers.swarm.endpoint으로 구성되는 전용 provider를 사용합니다.pilot섹션이 완전히 삭제되었습니다.experimental.http3가 삭제되었습니다. HTTP/3는 entrypoint에서 직접 활성화합니다.- providers 및 forwardAuth middleware에서
tls.caOptional이 삭제되었습니다. - InfluxDB v1 metrics provider, Rancher provider, Marathon provider가 삭제되었습니다.
- Tracing 기능이 OpenTelemetry로 이동했습니다. Jaeger 및 Zipkin 통합을 포함한 전용 tracing backends는 삭제되었으며, v3는 대신 OTLP(OpenTelemetry protocol)를 내보냅니다.
- headers middleware 내부의 deprecated된
ssl*옵션(sslRedirect,sslHost등)이 삭제되었습니다. entrypoint redirections와 redirectScheme middleware가 이를 대체합니다.
이러한 삭제 사항은 예상보다 중요합니다. Traefik은 정적 설정(static configuration)에 알 수 없는 옵션이 포함되어 있으면 실행을 거부하기 때문입니다. 남겨진 pilot 또는 swarmMode 라인은 부팅 시 해당 옵션 이름을 명시하는 incompatible deprecated static option found 메시지와 함께 컨테이너를 중단시킵니다. Traefik이 아예 알지 못하는 옵션(오타 또는 tls.caOptional)은 대신 field not found 메시지와 함께 중단됩니다. 이미지 태그를 변경하기 전에 정적 설정을 정리하십시오.
Traefik이 실제로 알지 못하는 middleware 이름(오타 또는 alias가 아닌 삭제된 이름)은 다르게 실패합니다. 해당 middleware를 참조하는 router는 route 대신 에러를 발생시키며 로드되고, dashboard에는 표시되며, API는 middleware "offce@docker" does not exist를 보고합니다. router가 정상적으로 생성되지 않았으므로 해당 hostname으로의 요청은 404를 반환합니다. 현재 v3에서 ipwhitelist은 이 범주에 해당하지 않음에 유의하십시오. ipwhitelist은 deprecated alias로 유지되므로, 이름을 변경하지 않은 label도 문제없이 작동합니다.
규칙 구문 변경 사항
규칙은 실제 재작성이 발생하는 부분입니다. v3에서의 변경 사항은 다음과 같습니다.
- Matcher 내부의 값에는 백틱(backticks)이 필수입니다. v2는 이중 따옴표를 허용했으나, v3는 허용하지 않습니다. 따라서 Host("app.example.com")은 Host(
app.example.com)로 변경해야 합니다. PathPrefix은 더 이상 정규 표현식이나{id}스타일의 플레이스홀더를 이해하지 못합니다. PathPrefix(/api/{version:v[0-9]+})와 같은 v2 규칙은 Go 정규 표현식 구문으로 작성된PathRegexpmatcher로 변경해야 합니다.- Matcher는 이제 단일 값만 받습니다. v2는 Host(
app.example.com,www.example.com)를 허용했으나, v3는 Host(app.example.com) || Host(www.example.com) 형식을 사용해야 합니다. 예외로Header,HeaderRegexp,Query,QueryRegexp는 여전히 이름과 값을 함께 사용합니다. Headers과HeadersRegexp이Header및HeaderRegexp으로 이름이 변경되었습니다.HostHeader이 제거되었습니다. v3에서 동일한 대상을 매칭하는Host을 사용하십시오.- 두 개의 새로운 matcher가 추가되었습니다: 규칙 내에서 클라이언트 주소를 매칭하는
QueryRegexp및ClientIP입니다.
다행인 점은 백틱을 사용하여 작성된 일반적인 Host(app.example.com) 규칙은 이미 유효한 v3 구문이라는 것입니다. 대부분의 소규모 Compose 설정이 이 형식을 사용하므로, 대부분의 label은 규칙 수정 없이 그대로 마이그레이션할 수 있습니다.
시작하기 전에 label을 점검하십시오
grep 명령어를 사용하면 마이그레이션 규모를 한 번의 검색으로 측정할 수 있습니다. 모든 breaking label 변경 사항은 grep으로 찾을 수 있는 패턴을 남기기 때문입니다:
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.yml검색 결과가 나타나는 항목마다 수정이 필요합니다. ipwhitelist는 ipallowlist으로 변경됩니다. HostHeader은 Host으로 변경됩니다. Headers은 Header으로 변경됩니다. PathPrefix 내부의 {...} placeholder는 PathRegexp matcher로 변경됩니다. Host() 내부의 쉼표(comma)는 ||으로 연결된 두 개의 Host() matcher로 변경됩니다. 검색 결과가 없다면 현재 label은 이미 유효한 v3 syntax입니다. 이 경우 마이그레이션 범위는 static configuration과 image tag로 제한됩니다.
변경되지 않는 사항
Entrypoint 및 HTTP-to-HTTPS 리다이렉트, 두 가지 challenge 유형을 모두 지원하는 ACME resolver, exposedByDefault, router 및 service label, loadbalancer.server.port, 그리고 dashboard는 v2와 동일하게 v3에서도 작동합니다. v3는 v2가 작성한 acme.json를 그대로 읽으므로 인증서도 그대로 유지됩니다. 다만, 작업 시작 전 파일을 반드시 백업하십시오. 백업 파일이 유실된 상태에서 rollback을 수행하면 Let's Encrypt의 duplicate-certificate rate limit에 즉시 걸리게 됩니다.
cp ./letsencrypt/acme.json ./letsencrypt/acme.json.v2-backup마이그레이션 경로
Step 1: 현재 실행 중인 버전을 고정하십시오. traefik:latest 또는 traefik:v2 태그를 traefik:v2.11과 같이 현재 사용 중인 정확한 릴리스 버전으로 변경한 후, compose 디렉토리 전체를 git에 커밋하십시오. 이후의 모든 단계는 checkout을 통해 되돌릴 수 있습니다. docker compose up -d <service>을 사용하여 단일 서비스를 재생성하는 것이 익숙하지 않다면, Docker Compose 기초 가이드에서 이 마이그레이션에 필요한 작업들을 확인할 수 있습니다.
Step 2: 정적 설정을 정리하고 호환 모드를 활성화하십시오. v3에서 삭제된 모든 옵션(pilot, swarmMode, tls.caOptional, experimental.http3)을 제거한 다음, v3가 규칙을 기본적으로 v2 구문으로 처리하도록 설정하십시오. traefik.yml에서 다음과 같이 설정합니다:
core:
defaultRuleSyntax: v2또는 compose command: 목록에 플래그로 추가합니다: --core.defaultRuleSyntax=v2. 호환 모드는 규칙 구문에만 적용됩니다. 삭제된 옵션을 복구하거나 middleware 이름을 자동으로 변경해주지는 않습니다.
Step 3: middleware 이름 변경을 준비하십시오. compose 파일에서 이전 이름인 grep -rn ipwhitelist docker-compose*.yml을 검색하십시오. 모든 ipwhitelist 레이블을 ipallowlist으로 수정하되, 아직 변경 사항을 적용하지 마십시오. v2에는 새 이름이 존재하지 않기 때문입니다. 이 수정 사항은 다음 단계의 전환과 함께 적용됩니다. (수정 사항을 놓치더라도 현재 v3는 이전 이름을 deprecated alias로 인식하여 계속 작동하므로, 새벽에 작업하기보다 다음 작업 시점에 수정하십시오.)
Step 4: 이미지 태그를 전환하십시오. Traefik 이미지를 현재 v3 릴리스인 traefik:v3.5으로 설정한 후, 다음을 실행합니다:
docker compose up -d
docker compose logs -f traefik호환 모드가 활성화되어 있으므로 v2 규칙이 계속 매칭됩니다. 또한 up -d이 middleware 레이블 이름을 변경한 서비스들을 재생성했으므로, 해당 router들이 정상적으로 실행됩니다. 정상적인 로그에는 field not found 라인과 does not exist 라인이 나타나지 않습니다.
이 단계에서 발생하는 중단 시간에 대해 주의하십시오. v3가 인식할 수 없는 middleware 이름(오타 또는 삭제된 옵션)을 참조하는 router는 새 Traefik가 시작된 시점부터 해당 app container가 재생성될 때까지 작동하지 않습니다. 단일 서버의 경우 docker compose up -d이 목록을 처리하는 데 몇 초가 소요됩니다. 만약 경로(route)의 중단이 절대 허용되지 않는다면, 전환 전에 해당 router의 middlewares 레이블에서 이름이 변경된 middleware를 제거하고, 전환 후에 다시 추가하십시오. 또한 그 짧은 시간 동안 해당 경로가 IP 허용 목록 없이 작동해도 되는지 미리 결정하십시오.
Step 5: 서비스를 하나씩 마이그레이션하십시오. 한 번에 하나의 app씩 작업하십시오. 규칙을 v3 구문으로 다시 작성하고, docker compose up -d app을 사용하여 해당 서비스만 재생성한 뒤, 다음 단계로 넘어가기 전에 테스트하십시오. 아직 규칙을 다시 작성할 수 없는 서비스가 있다면, 해당 router에 traefik.http.routers.app.ruleSyntax=v2 탈출 레이블을 부여하고 계속 진행하십시오.
Step 6: 호환 모드를 비활성화하십시오. 모든 규칙이 v3 구문으로 변경되면 defaultRuleSyntax과 모든 ruleSyntax 레이블을 삭제하십시오. Traefik를 재시작하고 모든 router가 dashboard에서 정상(green) 상태인지 확인하십시오. 호환 모드를 계속 사용하지 마십시오. Traefik는 v3.4에서 두 옵션을 deprecated 처리했으며, 다음 major version에서 제거할 예정입니다. 이 옵션들은 일시적인 연결 수단일 뿐 최종 목적지가 아닙니다.
전후 비교: 서비스 레이블의 변화
다음은 여러 가지 주요 변경 사항이 동시에 적용된 앱의 예시입니다. 이 앱에는 multi-value Host, PathPrefix placeholder, 그리고 ipWhiteList middleware가 포함되어 있습니다. v2 설정 블록은 다음과 같습니다:
app:
image: app:1.4
restart: unless-stopped
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.app.rule=Host(`app.example.com`,`www.example.com`) && PathPrefix(`/api/{version:v[0-9]+}`)
- traefik.http.routers.app.entrypoints=websecure
- traefik.http.routers.app.tls.certresolver=le
- traefik.http.routers.app.middlewares=office
- traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24
- traefik.http.services.app.loadbalancer.server.port=8080v3로 마이그레이션된 동일한 서비스의 설정입니다:
app:
image: app:1.4
restart: unless-stopped
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.app.rule=(Host(`app.example.com`) || Host(`www.example.com`)) && PathRegexp(`^/api/v[0-9]+`)
- traefik.http.routers.app.entrypoints=websecure
- traefik.http.routers.app.tls.certresolver=le
- traefik.http.routers.app.middlewares=office
- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.0/24
- traefik.http.services.app.loadbalancer.server.port=8080두 개의 레이블이 변경되었습니다. rule은 multi-value Host를 ||로 연결된 두 개의 matcher로 분리했습니다. 또한 placeholder를 PathRegexp으로 교체하였으며, middleware 레이블은 ipwhitelist을 ipallowlist으로 변경했습니다. entrypoint, certificate resolver, router-to-middleware 연결, 그리고 service port는 변경되지 않았습니다.
대시보드로 각 서비스를 테스트합니다
설정을 변경할 때마다 대시보드의 HTTP routers 페이지를 엽니다. 모든 router가 녹색이어야 합니다. 에러 배지가 표시된 router는 정확한 원인을 나타냅니다. 대개 새 이름으로 존재하지 않는 middleware이거나 v3에서 파싱할 수 없는 rule이 원인입니다. 그 다음, 외부에서 호스트 이름을 하나씩 확인합니다:
curl -sI https://app.example.com/api/v1/status200 또는 앱의 일반적인 redirect가 나타나면 routing과 TLS가 모두 정상적으로 작동하는 것입니다. Traefik에서 404이 발생하면 router가 실행되지 않은 것입니다. 대시보드로 돌아가 에러 내용을 확인하십시오. 작업하는 동안 다른 터미널에 docker compose logs -f traefik을 열어 두십시오. 컨테이너가 재시작될 때 발생하는 모든 parsing failure는 즉시 해당 로그에 기록됩니다.
Rollback honesty
모든 서비스가 v3에서 정상적으로 라우팅되고 실제 작동이 확인될 때까지 v2 compose file, 정적 설정, 그리고 acme.json 백업을 유지하십시오. 롤백은 마이그레이션 이전의 commit을 checkout하고 docker compose up -d을 실행하는 것을 의미합니다. 이때 이미지 tag만 변경해서는 안 되며 파일 전체를 사용해야 합니다. v3 전용 label은 v2 환경에서 잘못 작동하기 때문입니다. 이는 v2 label이 v3 환경에서 잘못 작동했던 것과 동일한 방식입니다. 즉, v2에는 ipallowlist이 존재하지 않으며, PathRegexp matcher 또한 해당 환경에서 파싱되지 않습니다. 만약 과정 중에 acme.json이 유실되거나 손상되었다면, v2를 시작하기 전에 백업본을 복구하십시오. 그렇지 않으면 롤백 과정에서 Let's Encrypt의 rate limit을 소모하며 인증서 5개를 동시에 재발급받아야 하는 상황이 발생할 수 있습니다.
FAQ
Traefik v3에서 모든 router rule을 다시 작성해야 합니까?
아니요. 백틱(backticks)을 사용한 일반적인 Host(app.example.com) rule은 두 버전 모두에서 유효하며, 대부분의 Compose 설정에 적용됩니다. v2 전용 기능을 사용한 rule만 다시 작성해야 합니다. 해당 기능은 Path 및 PathPrefix 내부의 regex 또는 placeholder, 하나의 Host() 내 여러 hostname, 백틱 대신 따옴표를 사용한 경우, 그리고 삭제된 Headers, HeadersRegexp, HostHeader matcher입니다.
Traefik v3에서 ipWhiteList는 어떻게 되었습니까?
ipAllowList으로 이름이 변경되었습니다. 내부 설정은 동일하므로, traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24와 같은 v2 label은 ipallowlist을 포함한 동일한 라인이 됩니다. v3.5를 포함한 현재 v3 릴리스는 이전 이름을 deprecated alias로 여전히 허용하므로, 이름을 변경하지 않은 label도 허용 목록을 계속 적용합니다. 하지만 이는 임시 방편일 뿐이므로 이름을 변경해야 합니다. alias는 삭제될 예정입니다. Traefik이 인식하지 못하는 middleware name을 사용하면 router error와 함께 404를 반환하며 명확하게 실패합니다. dashboard에 에러가 표시되며, 해당 hostname으로의 요청은 404를 반환합니다.
Traefik v3에서 v2 rule syntax를 여전히 읽을 수 있습니까?
예. 마이그레이션하는 동안 v2 syntax를 기본값으로 유지하려면 static configuration에 core.defaultRuleSyntax: v2를 설정하십시오. 기본값을 다시 변경한 후 개별적으로 남은 항목에는 router별 ruleSyntax=v2 label을 사용하십시오. 두 방식 모두 임시 방식입니다. Traefik은 v3.4에서 이를 deprecated 처리했으며, 다음 major version에서 삭제할 예정입니다.
Let's Encrypt 인증서가 업그레이드 후에도 유지됩니까?
예. Traefik v3는 v2가 작성한 acme.json 파일을 계속 읽으므로, 바이너리가 변경되었다고 해서 인증서가 재발급되지는 않습니다. 하지만 작업 시작 전에 파일을 안전한 곳에 복사해 두십시오. 롤백을 수행하거나 acme.json이 포함된 volume이 삭제되면 모든 인증서가 한꺼번에 재발급되어야 합니다. Let's Encrypt는 동일한 hostname 세트에 대해 주당 5개의 중복 인증서만 허용합니다.
업그레이드 후 Traefik v3가 시작되지 않는 이유는 무엇입니까?
대부분 static configuration에 v3에서 삭제된 옵션이 포함되어 있기 때문입니다. Traefik은 인식할 수 없는 옵션이 있으면 실행을 거부합니다. 잘 알려진 잔여 옵션(pilot, providers.docker.swarmMode, experimental.http3)의 경우, 로그에 incompatible deprecated static option found이 표시되며 원인이 명시됩니다. tls.caOptional와 같이 v3에서 처음 보는 옵션의 경우, field not found과 함께 해당 노드가 표시됩니다. 각 옵션을 삭제하거나 교체한 후 컨테이너를 다시 시작하십시오.