Traefik v2에서 v3 마이그레이션 시 주의사항 및 해결법
Traefik v3로 업그레이드할 때 static config에 swarmMode나 pilot이 포함되어 있으면 실행되지 않습니다. incompatible deprecated static option 오류를 해결하고 라우터 규칙을 안전하게 마이그레이션하는 방법을 상세히 안내합니다.
Traefik v2와 v3의 변경 사항
Traefik v2에서 v3로의 마이그레이션은 대부분 명칭 변경 작업입니다. 가장 대표적인 예로 ipWhiteList 미들웨어가 ipAllowList로 변경되었습니다. 그 외에도 v3는 라우터 규칙 문법을 더 엄격하게 제한하며(PathPrefix에서 정규식 기능이 제거되고, 일부 매처의 이름이 변경되거나 삭제됨), 몇몇 프로바이더와 옵션을 완전히 제거했습니다. 하지만 entrypoints, ACME 인증서 설정, Docker 레이블 워크플로우, 그리고 귀하의 acme.json 등 나머지 기능은 그대로 유지됩니다. 또한 v3는 v2 규칙 문법을 계속 사용할 수 있는 호환 모드를 제공하므로, 바이너리를 먼저 업그레이드한 뒤 위험 부담을 안고 한꺼번에 수정할 필요 없이 서비스별로 규칙을 하나씩 다시 작성할 수 있습니다.
이 가이드는 Traefik 리버스 프록시 가이드의 레이블 기반 Docker Compose 설정을 기준으로 합니다. 해당 페이지는 v3 전용이며, 이 문서는 여전히 traefik:v2 태그를 실행 중인 서버를 위한 것입니다.
이름 변경 및 제거 사항
ipWhiteList는 HTTP 및 TCP 미들웨어 모두에서ipAllowList으로 변경되었습니다. 내부 옵션은 변경되지 않았으므로sourcerange은 기존과 동일한 의미를 유지합니다. v3.5를 포함한 현재 v3 릴리스에서는 이전 이름을 더 이상 사용하지 않는(deprecated) 별칭으로 여전히 허용하며 목록을 강제하므로, 이 이름 변경으로 인해 서비스가 중단되지는 않습니다. 하지만 별칭은 향후 제거될 예정이며, 제거 시 별도의 경고 없이 목록에서 사라지므로 미리 이름을 변경하십시오.providers.docker.swarmMode=true는 제거되었습니다. Swarm은 이제providers.swarm.endpoint으로 구성되는 자체 프로바이더를 사용합니다.pilot섹션은 완전히 제거되었습니다.experimental.http3는 제거되었습니다. HTTP/3는 이제 엔트리포인트에서 직접 활성화합니다.tls.caOptional은 프로바이더 및 forwardAuth 미들웨어에서 제거되었습니다. 해당 미들웨어가 자체 호스팅된 Authentik SSO 앞단에 위치한 경우, forwardAuth 주소, 신뢰할 수 있는 헤더, 그리고 그 뒤의 아웃포스트(outpost)가 v3에서도 동일하게 동작하므로caOptional줄을 삭제하는 것만으로 마이그레이션이 완료됩니다.- InfluxDB v1 메트릭 프로바이더, Rancher 프로바이더, Marathon 프로바이더가 제거되었습니다.
- 트레이싱은 OpenTelemetry로 이전되었습니다. Jaeger 및 Zipkin 통합을 포함한 전용 트레이싱 백엔드는 제거되었으며, v3는 대신 OTLP(OpenTelemetry 프로토콜)를 내보냅니다.
- headers 미들웨어 내부의 더 이상 사용되지 않는
ssl*옵션들(sslRedirect,sslHost및 나머지)이 제거되었습니다. 엔트리포인트 리다이렉션과 redirectScheme 미들웨어가 이를 대체합니다.
Traefik은 정적 구성에 알 수 없는 옵션이 포함되어 있으면 시작을 거부하므로, 이러한 제거 사항은 보기보다 중요합니다. 남아 있는 pilot 또는 swarmMode 줄은 부팅 시 해당 항목을 명시하는 incompatible deprecated static option found 메시지와 함께 컨테이너를 중단시킵니다. Traefik이 전혀 알지 못하는 옵션(오타 또는 tls.caOptional)은 field not found 메시지와 함께 중단시킵니다. 이미지 태그를 변경하기 전에 정적 구성을 정리하십시오.
Traefik이 인식하지 못하는 미들웨어 이름(오타이거나 별칭 없이 제거된 이름)은 다르게 실패합니다. 해당 미들웨어를 참조하는 라우터는 경로 대신 오류와 함께 로드되고, 대시보드에 표시되며, API는 middleware "offce@docker" does not exist을 보고합니다. 라우터가 활성화되지 않았으므로 해당 호스트 이름으로 들어오는 요청은 404를 반환합니다. 현재 v3에서 ipwhitelist은 이 범주에 속하지 않으며, 더 이상 사용되지 않는 별칭으로 유지되므로 이름을 변경하지 않은 라벨도 정상적으로 작동합니다.
규칙 구문 변경
규칙은 실제 재작성(rewriting)이 일어나는 곳입니다. v3에서 변경된 사항은 다음과 같습니다.
- 매처(matcher) 내부의 값에는 백틱(backtick)을 사용해야 합니다. v2에서는 큰따옴표도 허용했으나 v3에서는 허용하지 않으므로, Host("app.example.com")은 Host(
app.example.com)로 변경해야 합니다. PathPrefix는 더 이상 정규 표현식이나{id}스타일의 플레이스홀더를 이해하지 못합니다. PathPrefix(/api/{version:v[0-9]+})와 같은 v2 규칙은 Go 정규 표현식 구문으로 작성된PathRegexp매처로 변경해야 합니다.- 매처는 이제 단일 값만 취합니다. 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을 사용하십시오.- 두 가지 매처가 새로 추가되었습니다.
QueryRegexp과 규칙 내부에서 클라이언트 주소를 매칭하기 위한ClientIP입니다.
다행인 점은, 백틱을 사용하여 작성된 단순한 Host(app.example.com) 규칙은 이미 유효한 v3 구문이라는 것입니다. 대부분의 소규모 Compose 설정은 정확히 이 방식을 사용하므로, 대부분의 레이블은 규칙 수정 없이 마이그레이션됩니다.
시작 전 레이블 감사하기
모든 주요 변경 사항은 grep으로 찾을 수 있는 패턴을 남기므로, 단 한 번의 검색으로 마이그레이션 규모를 측정할 수 있습니다.
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.yml검색 결과에 나타나는 각 행은 수정 대상입니다. ipwhitelist은 ipallowlist로 바뀝니다. HostHeader는 Host으로 바뀝니다. Headers는 Header로 바뀝니다. PathPrefix 내부의 {...} 플레이스홀더는 PathRegexp 매처가 됩니다. Host() 내부의 쉼표는 ||로 연결된 두 개의 Host() 매처가 됩니다. 검색 결과가 없다면 레이블이 이미 유효한 v3 구문이라는 뜻이며, 마이그레이션은 정적 구성과 이미지 태그를 업데이트하는 수준으로 줄어듭니다. 검색 결과가 화면을 가득 채운다면 현재 프록시가 이 서버에 적합한지 다시 고민해 볼 시점입니다. Traefik과 Nginx 및 Caddy 비교 문서를 통해 재작성 비용과 다른 두 프록시가 애플리케이션별로 요구하는 설정을 비교해 보십시오.
동일하게 유지되는 사항
엔트리포인트와 HTTP-to-HTTPS 리다이렉트, 두 가지 챌린지 유형을 모두 지원하는 ACME 리졸버, exposedByDefault, 라우터 및 서비스 레이블, loadbalancer.server.port, 그리고 대시보드는 v2와 동일하게 v3에서도 작동합니다. v3는 v2가 작성한 acme.json를 그대로 읽어오므로 인증서 또한 그대로 사용할 수 있습니다. 다만, 롤백 과정에서 파일을 잃어버리면 Let's Encrypt의 중복 인증서 발급 제한(rate limit)에 걸릴 수 있으므로, 시작하기 전에 반드시 파일을 백업하십시오.
cp ./letsencrypt/acme.json ./letsencrypt/acme.json.v2-backup마이그레이션 경로
1단계: 현재 실행 중인 버전을 고정합니다. 모든 traefik:latest 또는 traefik:v2 태그를 현재 사용 중인 정확한 릴리스(예: traefik:v2.11)로 변경하고, 전체 compose 디렉터리를 git에 커밋합니다. 이후의 모든 단계는 checkout을 통해 되돌릴 수 있습니다. docker compose up -d <service>을 사용하여 단일 서비스를 재생성하는 작업이 아직 익숙하지 않다면, Docker Compose 기초 가이드에서 이 마이그레이션에 필요한 운영 방식을 확인하십시오.
2단계: 정적 설정을 정리하고 호환성 모드를 활성화합니다. v3에서 제거된 모든 옵션(pilot, swarmMode, tls.caOptional, experimental.http3)을 제거한 다음, v3가 기본적으로 규칙을 v2 문법으로 처리하도록 설정합니다. traefik.yml 파일에 다음을 추가합니다:
core:
defaultRuleSyntax: v2또는 compose command: 목록의 플래그로 --core.defaultRuleSyntax=v2를 사용합니다. 호환성 모드는 규칙 문법에만 적용됩니다. 제거된 옵션을 복구하거나 미들웨어 이름을 자동으로 변경해주지는 않습니다.
3단계: 미들웨어 이름 변경을 준비합니다. compose 파일에서 이전 이름인 grep -rn ipwhitelist docker-compose*.yml을 검색합니다. 모든 ipwhitelist 레이블을 ipallowlist로 수정하되, 아직 변경 사항을 적용하지 마십시오. v2에는 새로운 이름이 존재하지 않기 때문입니다. 이 수정 사항은 다음 단계의 전환과 함께 적용됩니다. (만약 수정하지 못한 항목이 있더라도, 현재 v3는 이전 이름을 deprecated된 별칭으로 인식하므로 규칙은 계속 적용됩니다. 새벽 시간에 급하게 수정하기보다는 다음 작업 시에 수정하십시오.)
4단계: 이미지 태그를 전환합니다. Traefik 이미지를 현재 v3 릴리스인 traefik:v3.5(작성 시점 기준)로 설정한 후 다음을 실행합니다:
docker compose up -d
docker compose logs -f traefik호환성 모드가 켜져 있으므로 v2 규칙은 계속 일치하며, up -d을 통해 미들웨어 레이블을 수정한 서비스들도 재생성되었으므로 라우터가 정상적으로 작동합니다. 정상적인 로그에는 field not found 줄이나 does not exist 줄이 나타나지 않습니다.
이 단계에서 발생하는 서비스 중단 시간에 대해 명확히 인지해야 합니다. v3가 인식하지 못하는 미들웨어 이름(오타 또는 제거된 옵션)을 참조하는 라우터는 새로운 Traefik이 시작되는 순간부터 해당 앱 컨테이너가 재생성될 때까지 중단됩니다. 단일 서버 환경에서는 docker compose up -d이 목록을 처리하는 몇 초 정도의 시간이 소요됩니다. 경로가 잠시라도 중단되어서는 안 된다면, 전환 전에 해당 라우터의 middlewares 레이블에서 이름을 변경한 미들웨어를 제거했다가 전환 후에 다시 추가하십시오. 그 사이 1분 동안 IP 허용 목록 없이 해당 경로를 운영할 수 있는지 미리 결정해야 합니다.
5단계: 서비스별로 규칙을 마이그레이션합니다. 한 번에 하나의 앱씩 작업을 진행합니다. 규칙을 v3 문법으로 다시 작성하고, 해당 서비스만 docker compose up -d app로 재생성한 뒤 테스트를 완료하고 다음으로 넘어갑니다. 아직 v3 문법으로 작성할 수 없는 규칙이 있다면, 해당 라우터에 traefik.http.routers.app.ruleSyntax=v2 레이블을 추가하여 예외 처리를 하고 다음 단계로 진행하십시오.
6단계: 호환성 모드를 끕니다. 모든 규칙이 v3 문법으로 변경되면 defaultRuleSyntax과 모든 ruleSyntax 레이블을 삭제하고 Traefik을 재시작합니다. 대시보드에서 모든 라우터가 녹색으로 표시되는지 확인하십시오. 호환성 모드를 계속 사용하는 것은 권장하지 않습니다. Traefik은 v3.4에서 두 옵션을 모두 deprecated 처리했으며 다음 메인 버전에서 제거할 예정이므로, 이는 목적지가 아닌 임시 다리 역할임을 명심하십시오.
변경 전후: 서비스 레이블 하나
다음은 여러 변경 사항이 동시에 적용된 애플리케이션의 예시입니다. 다중 값 Host, PathPrefix 플레이스홀더, ipWhiteList 미들웨어가 포함되어 있습니다. 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=8080동일한 서비스를 v3로 마이그레이션한 결과입니다.
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두 개의 레이블이 변경되었습니다. 규칙은 다중 값 Host를 ||로 연결된 두 개의 매처로 분리하고, 플레이스홀더를 PathRegexp로 교체했습니다. 또한 미들웨어 레이블은 ipwhitelist를 ipallowlist로 변경했습니다. 엔트리포인트, 인증서 리졸버, 라우터-미들웨어 연결, 서비스 포트는 변경되지 않았습니다.
대시보드로 각 서비스 테스트하기
매번 설정을 변경한 뒤에는 대시보드의 HTTP routers 페이지를 엽니다. 모든 라우터는 녹색으로 표시되어야 합니다. 오류 배지가 표시된 라우터는 정확한 문제 원인을 알려주는데, 대개 새로운 이름으로 존재하지 않는 미들웨어나 v3에서 구문 분석할 수 없는 규칙 때문입니다. 그 후 외부에서 호스트 이름별로 하나씩 확인합니다.
curl -sI https://app.example.com/api/v1/status200 또는 애플리케이션의 일반적인 리다이렉트가 발생한다면 라우팅과 TLS 모두 정상적으로 유지된 것입니다. Traefik에서 404이 반환된다면 라우터가 활성화되지 않은 것이므로, 대시보드로 돌아가 오류 내용을 확인하십시오. 작업하는 동안 두 번째 터미널에 docker compose logs -f traefik를 계속 띄워 두십시오. 컨테이너가 재시작되는 즉시 모든 구문 분석 실패 로그가 해당 터미널에 기록되기 때문입니다.
롤백의 무결성
모든 서비스가 v3에서 정상적으로 라우팅되고 실제 운영 환경에서 검증을 마칠 때까지 v2 compose 파일과 정적 설정, 그리고 acme.json 백업을 유지하십시오. 롤백이란 마이그레이션 이전 커밋으로 체크아웃하고 docker compose up -d을 실행하는 것을 의미합니다. 이때 이미지 태그만 변경해서는 안 되며 전체 파일을 되돌려야 합니다. v3 전용 레이블은 v2 환경에서 오류를 일으키며, 이는 v2 레이블이 v3에서 오류를 일으키는 것과 정확히 같은 방식입니다. v2에는 ipallowlist가 존재하지 않으며, PathRegexp 매처 또한 v2에서 구문 분석되지 않습니다. 만약 과정 중에 acme.json가 손실되거나 손상되었다면, v2를 시작하기 전에 백업본을 복원하십시오. 그래야 롤백 과정에서 Let's Encrypt의 속도 제한(rate limit)에 걸려 5개의 인증서를 한꺼번에 재발급받는 상황을 방지할 수 있습니다.
FAQ
Traefik v3를 위해 모든 라우터 규칙을 다시 작성해야 합니까?
아니요. 백틱으로 작성된 일반적인 Host(app.example.com) 규칙은 두 버전 모두에서 유효하며, 대부분의 Compose 설정은 이 규칙으로 충분합니다. 규칙을 다시 작성해야 하는 경우는 v2 전용 기능을 사용했을 때뿐입니다. 예를 들어 Path 및 PathPrefix 내부의 정규식이나 플레이스홀더, 하나의 Host() 안에 여러 호스트 이름을 넣은 경우, 백틱 대신 따옴표를 사용한 경우, 또는 제거된 Headers, HeadersRegexp, HostHeader 매처를 사용한 경우가 이에 해당합니다.
Traefik v3에서 ipWhiteList는 어떻게 되었습니까?
이름이 ipAllowList로 변경되었으며 내부 설정은 그대로 유지됩니다. 따라서 traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24과 같은 v2 레이블은 ipallowlist가 포함된 동일한 줄로 바뀝니다. v3.5를 포함한 현재 v3 릴리스는 여전히 이전 이름을 지원 중단된 별칭으로 받아들입니다. 따라서 이름을 바꾸지 않은 레이블도 허용 목록을 정상적으로 적용합니다. 하지만 이를 이름을 바꾸지 않아도 되는 이유로 삼지 말고, 유예 기간으로 간주하십시오. 해당 별칭은 향후 제거될 예정이며, Traefik이 인식하지 못하는 미들웨어 이름을 사용하면 라우터 오류와 함께 404 응답이 발생합니다. 대시보드에 오류가 표시되며 해당 호스트 이름으로 들어오는 요청은 404를 반환합니다.
Traefik v3에서 여전히 v2 규칙 구문을 읽을 수 있습니까?
네. 마이그레이션하는 동안 v2 구문을 기본값으로 유지하려면 정적 설정에서 core.defaultRuleSyntax: v2를 설정하십시오. 기본값을 다시 전환한 후 개별적으로 남은 항목은 라우터별 ruleSyntax=v2 레이블을 사용하십시오. 두 설정 모두 임시로 사용해야 합니다. Traefik은 v3.4에서 이들을 지원 중단했으며 다음 메이저 버전에서 제거할 예정입니다.
Let's Encrypt 인증서는 업그레이드 후에도 유지됩니까?
네. Traefik v3는 v2가 작성한 acme.json 파일을 계속 읽으므로 바이너리가 변경되었다고 해서 인증서가 재발급되지는 않습니다. 하지만 시작하기 전에 파일을 안전한 곳에 복사해 두십시오. 롤백을 수행하거나 acme.json이 포함된 볼륨이 삭제되면 모든 인증서를 한꺼번에 재발급해야 하기 때문입니다. Let's Encrypt는 동일한 호스트 이름 세트에 대해 주당 5개의 중복 인증서만 허용합니다.
업그레이드 후 Traefik v3가 시작되지 않는 이유는 무엇입니까?
거의 항상 정적 설정에 v3에서 제거된 옵션이 포함되어 있기 때문입니다. Traefik은 인식할 수 없는 옵션이 있으면 시작을 거부합니다. 잘 알려진 잔여 설정(pilot, providers.docker.swarmMode, experimental.http3)의 경우 로그에 incompatible deprecated static option found가 표시되며 원인이 되는 항목을 명시합니다. tls.caOptional과 같이 v3가 전혀 알지 못하는 설정의 경우 field not found와 함께 노드 정보가 표시됩니다. 각 항목을 삭제하거나 교체한 다음 컨테이너를 다시 시작하십시오.