コンテナ環境でWebサービスを公開する際、リバースプロキシは不可欠な存在です。
これまで多くの方がnginx-proxy
とそのコンパニオンであるacme-companion
を利用して、手軽にSSL証明書の自動取得・更新を実現していることでしょう。
しかし、サービスの規模が大きくなるにつれて、より動的で高機能なリバースプロキシが欲しくなる場面も増えてきます。
そこでおすすめしたいのが Traefik です。
この記事では、既存のnginx-proxy
環境からTraefik
へ移行するメリットと、その具体的な設定方法をコードを交えて解説します。
なぜTraefikへ?移行のメリット
nginx-proxy
も素晴らしいツールですが、Traefik
はクラウドネイティブ時代のリバースプロキシとして、さらに多くの利点を提供します。
- 設定の自動検出と動的更新:
Traefik
はDockerやKubernetesなどのコンテナオーケストレーターと連携し、コンテナの起動・停止を検知してルーティング設定を自動で更新します。設定ファイルを手動で再読み込みする必要はありません。 - ラベルベースの直感的な設定:
docker-compose.yml
内にサービスのルーティング情報を「ラベル」として記述するだけで設定が完了します。どのコンテナがどのドメインで公開されているかが一目瞭然です。 - 強力な証明書管理 (ACME): Let's Encryptを利用したSSL証明書の取得・更新が非常に簡単です。
特に、CloudflareなどのDNSプロバイダと連携するDNS-01チャレンジに標準で対応しており、ワイルドカード証明書の取得や、ポート80
を外部に公開する必要がないといった利点があります。
この記事ではCloudflareを利用した内容となっています。 - 豊富なミドルウェア: 認証、リダイレクト、ヘッダー書き換え、IPホワイトリストなど、豊富な機能が「ミドルウェア」として提供されており、必要に応じて柔軟に組み合わせることができます。
- 美しいダッシュボード: 現在のルーティング設定、各サービスの状態、ミドルウェアの適用状況などを視覚的に確認できるダッシュボードが標準で付属しています。
既存環境(nginx-proxy)の確認
これまで稼働してきたnginx-proxy
とacme-companion
を使ったdocker-compose.yml
を見てみましょう。
shared/docker-compose.yml
version: "2"
services:
proxy:
image: nginxproxy/nginx-proxy
container_name: proxy
privileged: true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- /etc/localtime:/etc/localtime:ro
- ./certs:/etc/nginx/certs:ro
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- ./htpasswd:/etc/nginx/htpasswd
- ./proxy-settings.conf:/etc/nginx/conf.d/proxy-settings.conf
restart: always
logging:
options:
max-size: 5m
max-file: "10"
acme-companion:
image: nginxproxy/acme-companion
container_name: nginx-proxy-acme
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/etc/nginx/certs:rw
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- acme:/etc/acme.sh
volumes_from:
- proxy
restart: always
volumes:
html:
vhost:
acme:
networks:
default:
external:
name: shared
この構成では、proxy
サービスがリバースプロキシとして機能し、acme-companion
がSSL証明書の管理を行っています。
新しいサービスを公開するには、そのサービスのdocker-compose.yml
でVIRTUAL_HOST
やLETSENCRYPT_HOST
といった環境変数を設定する必要がありました。
Traefik環境の構築
次に、Traefik
を使った新しいリバースプロキシ環境を構築します。
ファイルはdocker-compose.yml
とtraefik.yml
の2つです。
1. traefik/docker-compose.yml
Traefik
自身をコンテナとして起動するためのファイルです。
services:
traefik:
image: traefik:latest
container_name: traefik
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./config:/etc/traefik/config
- ./acme:/etc/traefik/acme
environment:
# CloudflareのAPI情報を環境変数として渡す
- CLOUDFLARE_EMAIL=foo@example.com
- CLOUDFLARE_DNS_API_TOKEN=${CF_DNS_API_TOKEN} # .envファイルなどから読み込むのが安全です
- TZ=Asia/Tokyo
labels:
# --- Traefik自身のダッシュボード設定 ---
- "traefik.enable=true"
# ホスト名'traefik.your.domain'でアクセスできるようにする
- "traefik.http.routers.dashboard.rule=Host(`traefik.your.domain`)"
# Traefikの内部APIサービス(@internal)に接続
- "traefik.http.routers.dashboard.service=api@internal"
# TLSを有効化
- "traefik.http.routers.dashboard.tls=true"
# 'cloudflare'という名前のCertResolverを使用する
- "traefik.http.routers.dashboard.tls.certresolver=cloudflare"
networks:
- traefik-network
networks:
traefik-network:
external: true
注目すべきはlabels
セクションです。nginx-proxy
が環境変数で設定していたのに対し、Traefik
ではラベルを使ってルーティングを定義します。
ここではTraefik
自身のダッシュボードをtraefik.your.domain
というホスト名で公開する設定を記述しています。
Cloudflareと連携するために必要なAPIトークンは、セキュリティのため.env
ファイルに記述し、docker-compose
がそれを読み込むように構成するのが一般的です。
traefik-network
は以下のコマンドであらかじめ作成しておく必要があります:
docker network create traefik-network
2. traefik/traefik.yml
Traefik
の振る舞いを定義する中心的な設定ファイルです。
# APIとダッシュボードを有効にする
api:
dashboard: true
global:
sendAnonymousUsage: false
# エントリーポイント(入口)の定義
entryPoints:
# HTTP (80番ポート)
web:
address: ":80"
http:
# HTTPへのアクセスをHTTPS(websecure)へリダイレクトする
redirections:
entryPoint:
to: websecure
scheme: https
# HTTPS (443番ポート)
websecure:
address: ":443"
# 証明書リゾルバの定義 (Let's Encrypt)
certificatesResolvers:
# 'cloudflare'という名前のリゾルバを定義
cloudflare:
acme:
email: foo@example.com
storage: /etc/traefik/acme/acme.json
# DNS-01チャレンジを使用する
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 20
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
# プロバイダーの定義
providers:
# Dockerをプロバイダーとして使用する
docker:
endpoint: "unix:///var/run/docker.sock"
# デフォルトではコンテナを公開しない
exposedByDefault: false
# ファイルをプロバイダーとして使用する
file:
directory: /etc/traefik/config
watch: true
# ログ設定
log:
level: INFO # 本番環境ではINFOやWARNを推奨
accessLog:
format: json
このファイルでは、以下の重要な設定を行っています。
-
entryPoints
:Traefik
へのトラフィックの入口を定義します。web
(HTTP)とwebsecure
(HTTPS)を定義し、HTTPからHTTPSへの常時リダイレクトを設定しています。 -
certificatesResolvers
: SSL証明書をどのように取得するかを定義します。 - ここでは
cloudflare
という名前でリゾルバを作成し、ACMEのdnsChallenge
(DNS-01チャレンジ)を利用して証明書を取得するよう設定しています。 - これにより、サーバーが直接インターネットに
80
番ポートを公開していなくても証明書を取得できます。 -
providers
:Traefik
がどこからルーティング設定を読み込むかを定義します。 -
docker
プロバイダーを有効にすることで、コンテナのラベルを監視して動的に設定を反映させることができます。 -
exposedByDefault: false
は、明示的にラベルで許可したコンテナのみを公開するためのセキュリティ上重要な設定です。
アプリケーションをTraefik経由で公開する
設定が完了したら、あとはアプリケーションのdocker-compose.yml
にTraefik
用のラベルを追加するだけです。以下はwhoami
というシンプルなサービスを公開する例です。
services:
whoami:
image: traefik/whoami
container_name: whoami
restart: always
labels:
# --- Traefik用の設定 ---
- "traefik.enable=true"
# このサービスが属するネットワークを指定
- "traefik.docker.network=traefik-network"
# --- HTTPルーターの設定 ---
# 'whoami-http'という名前のルーターを定義
- "traefik.http.routers.whoami-http.entrypoints=web"
# 'whoami.your.domain'でアクセスされたらこのルーターに流す
- "traefik.http.routers.whoami-http.rule=Host(`whoami.your.domain`)"
# HTTPからHTTPSへのリダイレクトミドルウェアを指定
- "traefik.http.routers.whoami-http.middlewares=https-redirect"
# --- HTTPSルーターの設定 ---
# 'whoami-https'という名前のルーターを定義
- "traefik.http.routers.whoami-https.entrypoints=websecure"
# 'whoami.your.domain'でアクセスされたらこのルーターに流す
- "traefik.http.routers.whoami-https.rule=Host(`whoami.your.domain`)"
# TLSを有効にする
- "traefik.http.routers.whoami-https.tls=true"
# 'cloudflare'リゾルバを使って証明書を取得
- "traefik.http.routers.whoami-https.tls.certresolver=cloudflare"
# --- サービスの設定 ---
# 'whoami-svc'という名前のサービスを定義
- "traefik.http.services.whoami-svc.loadbalancer.server.port=80"
networks:
- traefik-network
networks:
traefik-network:
external: true
このコンテナを起動すると、Traefik
がラベルを検知し、whoami.your.domain
へのアクセスをこのコンテナのポート80
に転送するよう自動で設定します。同時に、certificatesResolvers
で定義したcloudflare
を使ってSSL証明書を申請・取得し、HTTPS通信を有効化します。
私の環境下において、nginx-proxyからtraefikへ移行した実際のサービスです。
- トップページ用のNginx
- Gitea
- Vaultwarden
- FreshRSS
- Growi
- WordPress
まとめ
nginx-proxy
からTraefik
への移行は、最初は少し複雑に感じるかもしれません。しかし、一度環境を構築してしまえば、その後のサービス追加や設定変更は驚くほど簡単になります。
- 設定の集約:
docker-compose.yml
のラベルにルーティング情報を集約できる。 - 運用の自動化: コンテナを起動/停止するだけで、ルーティングとSSL証明書が自動で管理される。
- 高い拡張性: 豊富なミドルウェアで、将来の要求にも柔軟に対応できる。
いままで動作してきたサービスがtraefikで上手くリバースプロキシとして稼働させるか不安で、なかなか手をつけなかったのですが、ようやく重い腰をあげて作業してみました。
Nginxを起動するとGiteaが起動しないといったGiteaとNginxが競合したトラブルに悩まされましたが解決後は全てのサービスが快適になりました。
なお、こちらのスライドもわかりやすいのでオススメです。
dashboadをBasic認証
上記の設定ではDashboardへの接続が公開されるので、誰でも閲覧することが出来るため、セキュリティ的には好ましくありません。
そこで、DashboardのみBasic認証の機能を追加しました。
.envにBasic認証の環境変数を追加
ユーザ名とパスワードでハッシュ値を生成
htpasswd -nb username newpassword
TRAEFIK_AUTH_HASH=admin:\$\$apr1\$\$NTbuvW.w\$\$/0gNXk8Q86009XxA.K/5J1
docker-compose.ymlにlabel追加
- "traefik.http.routers.dashboard.middlewares=dashboard-auth"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=${TRAEFIK_AUTH_HASH}"
traefikのコンテナを再起動でBasic認証が適用されます