コンテナなしでも注目に値する Traefik リバースプロキシ・ロードバランサー
(j6b72.de)- Traefik はコンテナ環境で有名だが、単一の Go 実行ファイルとして配布できるため、コンテナエンジンなしでもリバースプロキシとして運用できる
- nginx/caddy/apache2 系のようなファイル配信サーバーというより、HAProxy に近いプロキシであり、リクエスト転送・レスポンス返却・ヘッダー調整に重点を置く
- Docker ラベルを使わなくても 設定ファイル provider で構成でき、静的設定と動的設定を分けてルーター・サービス・ミドルウェアを管理する
- TLS Passthrough と HAProxy の PROXY protocol の入出力をサポートするが、PROXY protocol は対象サービス側も対応している必要があり、apache2 と nginx は対応している
- 認証統合やユーザーエージェント・IP ブロックは基本機能だけでは不足する場合があり、ForwardAuth・oauth2-proxy・サードパーティプラグインの運用負荷まで含めて判断する必要がある
コンテナ外でも使える Traefik
- Traefik はここ数年、home-lab 系 YouTube 界隈で人気を集めており、主に Docker や Kubernetes のような コンテナインフラと一緒に紹介される
- 機能の性格は nginx/caddy/apache2 より HAProxy に近い
- リクエストをサービスへ転送し、レスポンスを返す
- ヘッダーやリクエスト・レスポンスの一部を変更できる
- ファイル配信はサポートしない
- コンテナ環境では Traefik 自体もコンテナとして実行し、Docker ソケットをマウントして他のコンテナを自動検出できる
- プロキシ動作はコンテナの Docker ラベルで設定できる
- 新しいコンテナを検出すると Let’s Encrypt の TLS 証明書を自動取得し、サービスを公開できる
単一バイナリと systemd デプロイ
- Traefik は Go で書かれており、単一実行ファイルとしてコンパイルされる
- バイナリ配布版は Traefik インストール文書 から入手できる
- コンテナエンジンなしで Traefik 自体と対象サービスの両方を実行できる
- systemd サービスユニットの例は Traefik リポジトリ にある
- 実運用では設定ファイルに加えて、専用ユーザーの作成や設定ファイルの権限設定も必要になる
設定ファイルベースの構成
- コンテナを使わない場合は Docker ラベルを使えないが、Traefik は ファイル provider で設定できる
- 設定は大きく 2 つに分かれる
- 静的設定: Let’s Encrypt のような証明書プロバイダーや、Traefik が待ち受けるポートであるエントリーポイントを置く
- 動的設定: ルーター、サービス、ミドルウェアを置く
- Traefik はファイルシステムイベントを検出して動的設定を ホットリロードできる
- ドキュメントは各方式に応じて、中心概念と設定例を提供している
- certificate provider、entrypoint、router、service、middleware といった用語は Traefik ドキュメント ですぐ確認できる
設定後の動作とデバッグ
- 設定が正しくない場合、Traefik は警告を表示する
- デフォルトのログはそれほど多くないが、リクエストがどの経路を通るのか理解しやすい
- 初期構成はすぐ終わり、特に問題にも遭遇しなかったという利用経験がある
TLS Passthrough と PROXY protocol
- Traefik は TLS Passthrough をサポートする
- プロキシで TLS を終端せず、独自の TLS 証明書を提供する Web サービスへトラフィックを転送できる
- サービスが Traefik を経由しつつ Let’s Encrypt 証明書を直接取得する構成も可能
- プロキシは転送内容を見ることができない
- 一般的な仮想ホスト選択は HTTP
Hostヘッダーで行われるが、TLS Passthrough ではそのヘッダーが暗号化された本文内にあるため使えない - TLS の SNI(Server Name Indication) で対象ホストを選択し、Traefik や複数の Web サーバー・プロキシがこの方式を使っている
- HAProxy の PROXY protocol も入出力をサポートする
- ユーザーがまずプロキシに到達することで失われる情報を、対象サービスへ渡す仕組みである
- 従来の
X-Forwarded-...ヘッダーよりセキュリティ面で扱いやすいと評価されている - 対象サービス側も PROXY protocol に対応している必要がある
- apache2 と nginx は対応しており、対応サービスの一覧も増えている
認証統合で残る不満
- nginx では Vouch Proxy を使って、一部サービスを Azure AD、現在の Microsoft Entra 認証で保護する構成が可能である
- Traefik は nginx の認証方式に近い ForwardAuth をサポートする
- Vouch Proxy はまだ Traefik では動作せず、関連 issue が公開されている
- Keycloak インスタンスを自前運用し、AAD と統合して ForwardAuth に使うこともできるが、初期設定やセキュリティ維持・更新の負担が大きい
- traefik-forward-auth はよく勧められるが、最終更新が 2020 年 6 月で、依存関係の更新も必要なため使いにくいと判断された
- oauth2-proxy は過去の経験が良くなく、プロキシの背後にさらにプロキシを置くと HTTP/2・HTTP/3、タイムアウト、本文サイズ、WebSocket 設定を各中間プロキシごとに合わせる必要があり、障害の可能性が高まる
- 2024-05-06 の更新によれば、oauth2-proxy は HTTP API でも統合できる
- nginx の auth_request をサポートする
- Traefik の ForwardAuth をサポートする
- この方式は望んでいる構成に近い選択肢に見える
ユーザーエージェントと IP ブロック
- 内部サービスが archive.org にアーカイブされることを望まないケースがある
- robots.txt や類似ヘッダーは Archive.org のブロックには効果がなく、クローラーを止める方法は
archive.org_botユーザーエージェントのブロック、または IP レンジのブロックである - Traefik でユーザーエージェントや IP アドレスをブロックするには、基本機能ではなく サードパーティプラグインが必要になる
- ユーザーエージェントブロック用プラグイン
- deny IP プラグイン
- サードパーティプラグインはアップデート時に注意が必要で、セキュリティ脆弱性の原因にもなり得るため好ましくない
- IPAllowList ミドルウェアで、ブロックしたい IP を除いた残りを許可する方式は可能である
- IP レンジの計算もできる
- 直接ブロックより極端に悪いわけではないが、残ったサブネットだけを見てもどの範囲がブロックされたのか分かりにくく、美しくない
設定例の構成
- 例は
/etc/traefik/traefik.ymlと/etc/traefik/dynamic.ymlに分かれている - 静的設定では次を構成する
:80と:443エントリーポイント- HTTP エンドポイントを例外なく HTTPS にリダイレクト
- TLS challenge を使った Let’s Encrypt 証明書発行
- 動的設定ファイル
/etc/traefik/dynamic.ymlの監視
- 動的設定には次の構成が含まれる
cloud.xx.xyz向けの TLS Passthrough TCP ルーティング- 別ホスト上の
10.33.1.2:4433サービスへ転送 - PROXY protocol version 2 を有効化
git.xx.xyzについてはローカルのhttp://127.0.0.1:3000へ TLS 終端後にプロキシhttps://xx.xyz/redirmeplsをhttps://google.comにリダイレクトするミドルウェアX-Robots-Tag: noindex, nofollow, nosnippet, noarchiveヘッダーを追加するミドルウェア
1件のコメント
Hacker News のコメント
Traefik はかなり良いが、Ansible と同じような厄介な問題を抱えている。ドキュメントや記事はたくさんあるのに、本当に必要なものが見つからない。
v1 から使ってきたにもかかわらず、ドキュメントの中でよく迷子になり、かなりいら立つ。小さなプロジェクトでは Caddy を使うことが多い。ドキュメントが Traefik ほど悪くないからだ。
技術ドキュメントを書く人に言うなら、例中心のドキュメントは、ざっと眺める初心者にしか向いていない。製品に慣れた人には、10本のチュートリアルページに散らばったフィールド説明ではなく、リファレンスドキュメントと漏れのない一覧が必要だ。オンボーディング手順だけに集中せず、毎日その製品を使う人に合わせるべきだ。
ここが一番気に障る点で、Ansible をあれほど嫌いになった理由でもあり、Traefik も程度は低いが似ている。
ごく単純な作業を、非常にフラットなデータ構造でだけ行うと想定しているが、現実の大半はそうではない。こうした言語をきちんと使うには、YAML をどう書くべきかについての暗黙のルール全体を理解する必要があるのに、ドキュメントではそれにほとんど触れない。
各モジュール固有の設定ドキュメントはあるが、標準設定をどう使うのか、返ってきたデータをどう扱うのか、あるいは少し複雑なものとどう組み合わせるのかは、ほとんど明確ではない。項目ごとに1段落の例を十数個、材料の山のように投げ渡して、ケーキを焼けと言われている感じだ。
これまで使った複数の YAML インタプリタシステムで、ほとんどいつも同じ経験をした。YAML を何十万行も書いてようやく作業をこなせるようになったが、ドキュメントは設定一覧以上の価値はあまりなかった。
それでも
ansible-docを使う中でかなり良いワークフローを見つけた。よく使うエイリアスはalias adl='ansible-doc --list'、alias adls='ansible-doc --list | less -S'、alias ad='ansible-doc'だ。まず
adlsで関連コマンドを素早く検索し、adでドキュメントを確認してから、ほぼ毎回末尾に直接ジャンプして(G)例を見る。たいていそこに必要な答えがある。Ansible のスクリプト作成はドキュメントへの依存度が非常に高いので、デフォルトの状態でもこうした参照作業をもっと支援し、大量のエイリアスを作らなくても素早く探せるインターフェースを提供すべきだと思う。
もちろん自分たちのプロジェクトではあるが、オブジェクトのメソッドを一つ探すたびに、長い散文のページをかき分けなければならない。時には単にソースを読んだほうが簡単だ。
本番環境でTraefikを2年間使ってきた。以前はNGINXを使っていたが、Let's Encryptの自動連携が理由でTraefikへ移行することにし、その決断を後悔している
Traefikのドキュメントは、私やチームには分かりにくい。扱いが難しく、まともなログもなく奇妙な動きをする
たとえば証明書を作り直そうとすると、断続的に失敗し、本番環境がいつ復旧するか分からない状態で落ちる。NGINXに戻しているところ
configuration.nixに数行追加するだけで設定でき、内部的にはlego(1)とletsencryptを使うsecurity.acme = { acceptTerms = true; defaults.email = "admin-email@provider.net"; certs."mydomain.example.com" = { domain = "*.mydomain.example.com"; dnsProvider = "cloudflare"; environmentFile = "/path/to/cloudflare/password"; }; };services.caddy.enable = true;services.caddy.virtualHosts."subdomain1.mydomain.example.com" = { extraConfig = '' reverse_proxy 127.0.0.1:1234 ''; useACMEHost = "mydomain.example.com"; };nginxで設定するのもかなり似た感じになりそう。 https://github.com/go-acme/lego
同じドメインレジストラ、同じAPI、同じDocker設定で、ログも全部有効にしていたのに、なぜ証明書が作られないのか一切情報がなかった。試みすらしなかったかのように、単に生成済みのデフォルト証明書へ戻っただけだった
2回のトラブルシューティングと数時間の検索の末、諦めて自己署名証明書ファイルを使うしかなかった。なぜ動かないのか情報がまったくなく、黙って失敗したあとフォールバック動作に移るのは本当に腹立たしい
全体として、Traefikの最大の問題はこれだった。動くときは素晴らしいが、動かないときは問題を切り分けたり、ドキュメントから必要な情報を見つけたりするのが常に難しかった。会社では年末ごろに本番環境でTraefikを使い始める予定なので、それまでにTraefikにもう少し慣れておきたい
一番良い点は、設定言語がないことだ。単にGoを書けばいい
後で分かったのは、DNSが正しく設定されていないと、検証の試行がN回失敗したあと、Let's EncryptがしばらくTLS-01検証を再度拒否するということだった。あなたが遭遇した問題もその類に聞こえる
DNS-01検証へ移行してからは、体験が急にずっと良くなった。公開されていないサービスの証明書も作れるし、TLS-01方式よりオーケストレーションもはるかに少なくて済む
DNSプロバイダーがまともなら、問題がある場合でもLet's Encryptがレコードを検証する前にAPIエラーを受け取れる可能性が高く、失敗状態はLet's Encryptの検査失敗によるバックオフよりずっと手前で発生する。今ではTraefik、Caddy、Nginx、その他のリバースプロキシのどれを使うにせよ、Let's EncryptはDNS-01ベースの検証だけを使うとほぼ決めているし、TLS-01を使う必要があるなら、Staging APIで最初から正しく合わせられているか必ず確認するつもりだ
ちなみにTraefikでLet's EncryptのStaging証明書を作ると、その証明書を無効化する良い方法がない。ACME JSONを編集して証明書を削除し、Traefikを再起動して変更を反映する必要がある。
SIGHUPでできるかもしれないが、試してはいない全体として奇妙なサイレント失敗や挙動は多いが、最大の苦痛は依存サービスのエラーを不透明にしてしまう点だ
TraefikよりCaddyを使っている。私の基準では、TraefikのYAML設定よりCaddyfileを管理する方がはるかに簡単で、ローカル・本番・オンプレミスデプロイ用のCaddyfileをそれぞれ維持している
優れたプラグインも多く、私たちはCaddy用のcoraza WAFプラグインを使っていて、うまく動いている
必要な機能はすべてありながら、はるかにシンプルだ
https://github.com/lucaslorentz/caddy-docker-proxy
外部ストレージを使うとき、ローカルストレージをどう扱うべきか、といったことだ。ステートレスとして扱えるとは言われているが、そうでないかもしれない
結局、必要なモジュールを作った人がきちんと理解していることを祈るしかない。そちらにもドキュメント標準がないからだ。管理者は、コア機能を提供しながらドキュメントがゼロのランダムなモジュール、たとえばS3ストレージバックエンドのようなものに歯止めをかける必要がある
Docker ホストが数台、コンテナが数十個程度というささやかな要件なら、Traefik を nginx より使う理由が何なのかと思う。私は https://github.com/NginxProxyManager/nginx-proxy-manager を使っているけれど、このくらいの小規模で Traefik に利点はあるのだろうか?
実のところ nginx の構文が好きではなく、Traefik が魅力的に見えたというのもある。Let's Encrypt の更新とコンテナがきっかけで使い始め、設定方法のおかげで使い続けている
Traefik にはあるが Nginx にはない機能が必要になった瞬間に、初めて移行を検討すると思う
Certbot はどこにでもあり、より多くのシナリオをサポートしているのに、自動 Let's Encrypt が売り文句というのも少し奇妙だ。Traefik と Caddy の売り文句は、すでに広くサポートされている代替手段より簡単にすることではないので、私にはあまりピンとこない
数週間前にリバースプロキシを選んでいて、結局シンプルさから Caddy に決めた。ただし Traefik の コンテナ自動検出とラベル参照はかなり良いし、Caddy にも同じことをするプラグインがある
記事は読んだが、自分にとって Traefik が Caddy より優れている点があるのかは、まだ確信が持てない。他の人にはあるかもしれないので、意見を聞きたい
Traefik がデフォルトで K3s に設定されている点も注目に値する。そのおかげで K3s はテスト用 K8s クラスタを最速で立ち上げる方法になり、クラスタも家畜のように扱えるようにしてくれる
デプロイと関連サービスを NodePort として追加するだけで、Ingress コントローラを気にせずアプリにアクセスできる
私はシェルスクリプトで K3s クラスタを立ち上げ、位置引数で指定したアプリを必要なときにテストしている。その際 ttl.sh の一時コンテナレジストリを活用している。同じスクリプトの最後でクラスタも削除する
セルフホスティング用のリバースプロキシを Traefik に移そうか悩んでいる。筆者と違って私は Docker Compose でコンテナ化されたワークロードを動かしており、今は優れた caddy-docker-proxy プラグインとともに Caddy を使っている
現在得られているのは、Docker ラベルベース設定のリバースプロキシ、新しいワークロードの自動検出、TLS 証明書、caddy-dynamicdns プラグインによる自動 DNS 設定だ。ISP が別の IP を割り当てても、アクセスを失うことをあまり気にしなくてよい
ただし、新しいワークロードが追加されたり再起動されたりするたびに Caddy 全体が再起動し、一時的にアクセスが途切れる。Caddy は既存の接続を新しいインスタンスへ引き継げない
また ワイルドカード証明書の利用が十分に単純ではない。すべてのワークロードが証明書透明性ログを通じて世界に公開されるのは望まないのでワイルドカード証明書を使っているが、そうするとホスト名ごとに証明書を使う単純な Caddyfile 構文を使えない。Caddy で対応中なのは知っているが、現時点ではそうだ
いずれにせよ k8s 環境で Traefik を使ったことがあり、かなり良かったので個人用途でも一度使ってみるつもりだ。この発言のせいで Caddy を試さない、ということはしないでほしい。Caddy も実際、本当に良い
localtest.meをホスト名と一緒に使うために動かしている一度試してみる価値はある。どちらも異なる領域で優れている
プラグインは実際には必要ない。VM にこれらすべてを設定し、compose ファイルのテンプレートまで作る Ansible プレイブックが 1 つあり、サーバーからデータとマウント以外をすべて削除する別のプレイブックもある。バックアップには、ファイルや複数の DB などを複数の場所へバックアップできるカスタムスクリプトと restic を使っている
以前は k3s をデプロイしていたが、セルフホスティング用途にはやりすぎで複雑すぎると気づいた。私はただ素早くデプロイし、証明書を自分で扱わずに済めばよい
たった今ローカルで確認してみたが、正常に動作している
Traefik はかなり使ってきたが、単にリバースプロキシを 1 つ置くためだけに docker-compose ラベルや階層、膨大な行数を扱うのに疲れた。そこで Caddy を見つけ、それ以来振り返っていない
おそらく私は Traefik のターゲットユーザーではなかったのだと思う。必要なのは HTTPS が有効なリバースプロキシか、基本認証のレイヤー程度だ。Caddy ではどちらも 1 行で済み、とても簡潔で、いまだに理解できていない階層もない
ここ数年、セルフホスティングのすべてで Traefik を使ってきた
ただし、動的ディスカバリと Docker ラベル機能はあまりに扱いづらく、デバッグも面倒だったのでやめた
代わりにテンプレートエンジンで静的設定ファイルを生成している。ほぼすべてのサービスが host/target/port の組み合わせなので、該当セクションを作るのはとても簡単で、TLS 処理以外には複雑なミドルウェアもない。リンク先の記事の筆者も同じ道を選んだように見える
設定は Ansible スクリプトで生成してから Traefik が動いているマシンにコピーし、Traefik はそのファイルがあるディレクトリを監視して、変更があると自動で再読み込みする。非常にうまく動いてきた
本番環境でコンテナと一緒に Traefik を使っていて、一番気に入っている点は設定がコンテナラベルに載ること。そのため、Traefik の設定自体を修正することはほとんどない
最大の欠点は、この名前をどう発音すべきかを突き止めること。普通の traffic のように読むらしいが、つい「trey-feek」みたいに呼びたくなる
Traefik と Docker Compose の組み合わせは、小規模なセルフホスティングが k8s に移行するほど大きくなる前、特に k8s の高可用性コントロールプレーンが使うサーバー数より少ないサーバーしかない場合の、ちょうどよい落としどころだと思う
同僚から受けたしかめ面や笑いの量はものすごく、製品の導入と利用の妨げになった
うちは「tray-feek」と呼んでいて、それならまだよかったのだが、公式サポートと話したところ、普通の「traffic」とまったく同じ発音だと言われた。なので、そのプロキシについて話すたびに「公開ロードバランサーで traffic を受け、traffic のネイティブなロードバランシングが traffic を traffic の pod に送る」みたいな文になる。間抜けに聞こえるが、実際に間抜けだ
そうでなければ、メンテナンスページや、非アクティブ後の最初のリクエストでコンテナを起動する、といった面白いことをもっと簡単にできたはず
なので、compose ファイルを保存している場所を把握し、そこから直接読み取れるプラグインを作ってみようかと考えている
aeはyやhiに最も近い。だから私の推測では Tryfik、そうでなければ Trayfik。ヨーロッパ式のfikなら feek かもしれない