Traefik v2からv3移行で壊れる設定と対処法
Traefik v3で「incompatible deprecated static option found」エラーが出る原因は、static configのswarmModeまたはpilotです。削除して起動後、ruleを段階的に移行します。
Traefik v2 と v3 で変わる点
Traefik v2 から v3 への移行は、主に名称変更の作業です。特に有名な変更は、ipWhiteList middleware が ipAllowList に変わったことです。それ以外にも、v3 では router rule の構文が厳格化され、PathPrefix から正規表現機能がなくなり、一部の matcher は名称変更または削除されました。いくつかの provider と option も完全に削除されています。一方、entrypoints、ACME certificate の設定、Docker labels を使う運用、acme.json はそのまま引き継げます。v3 には互換モードもあり、v2 の rule 構文を引き続き使用できます。そのため、binary を先にアップグレードし、各サービスの rule を段階的に書き換えられます。リスクの高い作業を一晩で完了させる必要はありません。
このガイドでは、Traefik リバースプロキシガイドにある labels ベースの Docker Compose 構成を前提にします。そちらのページは v3 を前提としています。このページは、traefik:v2 tag を引き続き実行している環境を対象としています。
名称変更と削除
ipWhiteListは、HTTP ミドルウェアと TCP ミドルウェアの両方でipAllowListになりました。内部のオプションは変わっていないため、sourcerangeの意味は完全に同じです。v3.5 を含む現行の v3 リリースでは、旧名称を非推奨の別名として引き続き受け付け、リストの適用も継続します。そのため、この名称変更だけで切り替え時にサービスが停止することはありません。ただし、名称は変更してください。この別名は削除予定であり、非推奨リストから予告なく消えるためです。providers.docker.swarmMode=trueは削除されました。Swarm には専用のプロバイダーが用意され、providers.swarm.endpointとして設定します。pilotセクションは完全に削除されました。experimental.http3は削除されました。HTTP/3 は entrypoint で直接有効にします。tls.caOptionalは providers と forwardAuth ミドルウェアから削除されました。このミドルウェアの前段で 自己ホスト型の Authentik SSO を使用している場合、caOptionalの行を削除するだけで移行できます。forwardAuth のアドレス、信頼するヘッダー、およびその背後にある outpost の動作は、v3 でも変わらないためです。- InfluxDB v1 metrics provider、Rancher provider、および Marathon provider は削除されました。
- トレーシングは OpenTelemetry に移行しました。専用のトレーシングバックエンドは削除され、Jaeger と Zipkin の統合もその対象です。v3 は代わりに OTLP(OpenTelemetry protocol)をエクスポートします。
- headers ミドルウェア内の非推奨の
ssl*オプション(sslRedirect、sslHostなど)は削除されました。これらの代わりに、entrypoint のリダイレクトと redirectScheme ミドルウェアを使用します。
これらの削除は見た目以上に重要です。Traefik は、認識できないオプションを静的設定に含めたままでは起動しないためです。残った pilot または swarmMode の行があると、起動時にコンテナが停止し、残存している設定を示す incompatible deprecated static option found メッセージが出力されます。Traefik が一度も認識したことのないオプション(タイプミスや tls.caOptional など)の場合は、field not found で停止します。イメージタグを変更する前に、静的設定を整理してください。
Traefik が実際に認識できないミドルウェア名(タイプミスや、別名ではなく削除された名前など)は、別の形で失敗します。そのミドルウェアを参照する router はルートとしてではなくエラー状態で読み込まれ、dashboard にもその状態が表示され、API は middleware "offce@docker" does not exist を返します。router が起動しないため、その hostname へのリクエストは 404 になります。ただし、現在の v3 では ipwhitelist はこの分類に入りません。非推奨の別名として残っているため、名称を変更していない label も問題なく動作します。
ルール構文の変更
ルールで実際の書き換えを行います。v3 での変更点は次のとおりです。
- matcher 内の値はバッククォートで囲む必要があります。v2 ではダブルクォートも使用できましたが、v3 では使用できません。そのため、Host("app.example.com") は Host(
app.example.com) に変更する必要があります。 PathPrefixは正規表現や{id}形式のプレースホルダーを解釈しなくなりました。v2 の PathPrefix(/api/{version:v[0-9]+}) というルールは、Go の正規表現構文で記述したPathRegexpmatcher に変更する必要があります。- matcher が受け取る値は 1 つになりました。v2 では Host(
app.example.com,www.example.com) を使用できましたが、v3 では Host(app.example.com) || Host(www.example.com) と記述します。例外はHeader、HeaderRegexp、Query、QueryRegexpです。これらは引き続き名前と値を 1 つずつ受け取ります。 HeadersとHeadersRegexpは、それぞれHeaderとHeaderRegexpに名称変更されました。HostHeaderは削除されました。v3 で同じ対象に一致するHostを使用してください。- 2 つの matcher が追加されました。ルール内でクライアントアドレスに一致させる
QueryRegexpとClientIPです。
良い点として、バッククォートを使用して記述した単純な Host(app.example.com) ルールは、そのまま有効な v3 構文です。小規模な Compose 構成の多くはこの形式を使用しているため、ほとんどのラベルはルールを編集せずに移行できます。
作業開始前にラベルを監査する
検索を 1 回実行するだけで、移行規模を測定できます。破壊的なラベル変更はすべて、grep で検出できるパターンを残すためです。
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.yml検出結果 1 件につき、1 行を修正します。ipwhitelist は ipallowlist に変更します。HostHeader は Host に変更します。Headers は Header に変更します。PathPrefix 内の {...} プレースホルダーは、PathRegexp マッチャーに変更します。Host() 内のコンマは、|| で結合した 2 つの Host() マッチャーに変更します。検出結果が 0 件なら、ラベルはすでに v3 構文として有効です。この場合、移行対象は静的設定と image tag だけになります。検出結果が画面いっぱいに表示される場合は、このサーバーに引き続きこのプロキシが適しているかを検討するよい機会でもあります。Traefik と Nginx、Caddy の比較では、この書き換え作業の負担を、他の 2 つのプロキシがアプリごとに求める作業と比較しています。
変更されないもの
エントリポイントと HTTP から HTTPS へのリダイレクト、両方のチャレンジ方式に対応する ACME resolver、exposedByDefault、router と service のラベル、loadbalancer.server.port、および dashboard は、v2 と同様に v3 でも動作します。証明書も引き継がれます。v3 は v2 が書き込んだ acme.json を引き続き読み込むためです。作業を始める前に、いずれにしてもこのファイルをバックアップしてください。バックアップなしでロールバックすると、Let's Encrypt の重複証明書レート制限に直ちに達する可能性があります。
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 は非推奨の別名として古い名前を引き続き認識するため、リストの適用は継続されます。午前 2 時に対応するのではなく、次の作業で修正してください。)
Step 4: image タグを切り替えます。 Traefik の image を執筆時点での現在の 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 が起動した時点から、そのアプリのコンテナを再作成するまで停止します。1 台のホストであれば、docker compose up -d がリストを処理する数秒間です。ルートを一瞬も停止できない場合は、切り替え前にその router の middlewares ラベルから名前を変更した middleware を削除し、切り替え後に再追加してください。その間の 1 分間、IP allow list なしで運用できるかどうかも、事前に判断しておきます。
Step 5: サービスごとにルールを移行します。 アプリを 1 つずつ処理します。ルールを v3 構文に書き換え、docker compose up -d app でそのサービスだけを再作成し、次へ進む前にテストします。まだ書き換えられないルールを持つサービスがある場合は、その router に escape hatch ラベル traefik.http.routers.app.ruleSyntax=v2 を付けて、作業を続けます。
Step 6: 互換モードを無効にします。 すべてのルールが v3 構文になったら、defaultRuleSyntax と ruleSyntax ラベルを削除して Traefik を再起動し、dashboard ですべての router が引き続き緑色で表示されることを確認します。互換モードを有効にしたまま運用し続けないでください。Traefik は v3.4 で両方のオプションを非推奨にしており、次のメジャーバージョンで削除するためです。互換モードは橋渡しであり、最終的な構成ではありません。
変更前と変更後: 1 つのサービスのラベル
ここでは、よくある変更を 1 つのアプリにまとめています。複数の値を持つ 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変更されたラベルは 2 つです。ルールでは、複数の値を持つ Host が || で連結された 2 つのマッチャーに分割され、プレースホルダーが PathRegexp に置き換えられています。ミドルウェアのラベルでは、ipwhitelist が ipallowlist に置き換えられています。エントリポイント、証明書リゾルバー、ルーターとミドルウェアの接続、サービスのポートは変わっていません。
ダッシュボードで各サービスをテストする
切り替えるたびに、ダッシュボードの HTTP routers ページを開きます。すべての router が緑色になっている必要があります。エラーバッジが付いた router には、問題の正確な内容が表示されます。通常は、新しい名前で存在しない middleware、または rule v3 で解析できないルールが原因です。次に、ホスト名を1つずつ外部から確認します。
curl -sI https://app.example.com/api/v1/status200 またはアプリの通常のリダイレクトが返れば、ルーティングと TLS はどちらも正常に動作しています。Traefik から 404 が返る場合は、router が起動していません。ダッシュボードに戻り、エラーを確認してください。作業中は別のターミナルで docker compose logs -f traefik を開いたままにします。コンテナの再起動時に発生した解析エラーは、すべてそこに直ちに記録されます。
ロールバックの正確な扱い
すべてのサービスが v3 経由でルーティングされ、実運用で動作確認を終えるまで、v2 の compose ファイル、その静的設定、acme.json のバックアップを保持してください。ロールバックでは、移行前のコミットを checkout して docker compose up -d を実行します。イメージタグだけではなく、ファイル全体を戻す必要があります。v3 専用のラベルは、v2 では v2 のラベルが v3 で誤っていたのと同じ理由で正しく動作しません。ipallowlist は v2 に存在せず、PathRegexp matcher も v2 では解析できません。途中で acme.json を失ったり破損させたりした場合は、v2 を起動する前にバックアップから復元してください。そうしないと、ロールバック時に Let's Encrypt のレート制限を消費し、5 個の証明書を同時に再発行することになります。
FAQ
Traefik v3 では、すべてのルーター規則を書き換える必要がありますか?
いいえ。バッククォートで記述した通常の Host(app.example.com) ルールは両方のバージョンで有効であり、ほとんどの Compose 構成をカバーします。書き換えが必要なのは、規則で v2 専用の機能を使用している場合だけです。具体的には、Path と PathPrefix 内の正規表現またはプレースホルダー、1 つの Host() 内に複数のホスト名を指定する記法、バッククォートではなく引用符を使う記法、削除された Headers、HeadersRegexp、HostHeader マッチャーが該当します。
Traefik v3 では ipWhiteList はどうなりましたか?
ipAllowList に名前が変更されました。設定内容は変わっていないため、v2 の traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24 というラベルは、ipallowlist を含む同じ行になります。v3.5 を含む現在の v3 リリースでは、古い名前も非推奨の別名として引き続き受け付けます。そのため、名前を変更していないラベルでも、allowlist は気付かないうちに適用され続けます。ただし、これは名前変更を省略する理由ではなく、猶予期間と考えてください。この別名は削除予定であり、Traefik が認識できない middleware 名を指定すると、代わりにルーターエラーと 404 が発生して明示的に失敗します。ダッシュボードにはエラーが表示され、そのホスト名へのリクエストは 404 を返します。
Traefik v3 は v2 の規則構文を引き続き読み取れますか?
はい。静的設定で core.defaultRuleSyntax: v2 を設定すると、移行中は 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 と表示され、原因となった設定が示されます。v3 が認識したことのない tls.caOptional などの設定については、ノードとともに field not found と表示されます。それぞれを削除または置き換えてから、コンテナを再度起動してください。