Traefikを使ってみる機会があったので、Traefikの入門記事を書いてみます。
長い記事ですので興味のあるところを読んでいただければと思います。下の目次も活用して下さい。
Traefikについて
Traefikとは何か
Traefikはオープンソースのエッジルーター(いわゆるリバースプロキシ・ロードバランサーの役割)で、サービスを簡単な設定で公開することが出来るのが特徴です。
リバースプロキシとしてのNginxの代替手段となります。
既存の技術(すなわちNginx)のリバースプロキシでは複数の設定ファイルを管理しなければならなかったり、近年主流となってきているDocker, Docker Swarm, Kubernetesなどの技術への親和性がそこまで高くないという問題がありました。
Traefikは「シンプルな操作性を実現することを最大の目標としている」ため、その理念の通り自分で細かな設定をする必要がありません。展開されているサービスから関連する設定を自動的に取得し、「うまいこと」ルーティング等の設定をしてくれます。
また下記の通り、現在主流の技術をネイティブにサポートしており簡単に展開することが出来ます。
Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker, Docker Swarm, AWS, Mesos, Marathon, and the list goes on; and can handle many at the same time. (It even works for legacy software running on bare metal.)
↓↓↓
Traefikは、Kubernetes、Docker、Docker Swarm、AWS、Mesos、Marathonなど、あらゆる主要なクラスター技術にネイティブに対応しており、同時に多数のサービスを処理することができます。(ベアメタル上で動作するレガシーソフトウェアにも対応しています)
公式ドキュメントより
ポイント
Traefikは公式のDockerイメージを公開しており、それを使うのが基本です。本記事でも公式のDockerイメージを使うことを前提とします。
ココがポイント
TraefikはNginxの代替となるリバースプロキシである。Docker, k8sとの親和性が高いのが特徴。
とはいえNginxより全てにおいて優れているというわけではないのでそのあたりの解説をしていきます。
Traefikのメリット・デメリット
メリット
Traefikのメリットは以下のようなものが挙げられます。
メリット
- 一般的な用途であれば設定が簡単であること
- Docker, Kubernetesなどの技術との親和性が高いこと
- サービスの管理がしやすい(管理画面が見やすい)
- Datadogなどいくつかのメトリクスをネイティブにサポートしていること
複雑なことをせず、一般的なユースケースを想定したときには最小限の設定でサービスを展開することが出来ます。
例えば、Dockerコンテナ化したサービスをサーバー上に展開し、Let's Encryptを使ってTLS対応させたいとします。
Traefikを使うと少量の設定を書くだけでルーティングの設定、証明書の取得・自動更新、httpsへのリダイレクトなどをほぼ自動でやってくれます。
加えて以下のような管理画面でサービスの状態などを視覚的に確認することも出来ます。
メトリクスがネイティブにサポートされているというのも人によってはメリットになるかもしれません。
メトリクスに関しては以下の4つがサポートされています。
ココがポイント
DockerコンテナをデプロイしてLet's EncryptでTLS化するなど、一般的なケースなら簡単に実現出来る
デメリット
一方でTraefikのデメリットは以下のようなものが挙げられます。
デメリット
- 情報量が少ない
- ドキュメントが分かりづらい
- Traefikで出来ることはNginxでも出来る
Nginxは何をするにしてもすでに先人たちがやった記録が残っているので問題が起きても解決しやすいです。
一方でTraefikは情報量が少なく、問題が起きた時に特定が難しいです。実際私自身も設定を間違って一行消してしまっていただけで、結構ハマってしまい時間を無駄にしてしまいました。
Nginxのように動くサンプルがどこにでも転がっているというわけではないのです。
*あくまで参考ですがGoogle Trendsの傾向はこんな感じです。
また個人的な意見ですが、Traefikはドキュメントが分かりづらい(&見づらい)と思います。断片的にオプションの設定方法が書かれているだけなので構造を理解するまで分かりづらいですし、設定方法が複数あるのも理解を妨げる要因になっています。
すでにNginxをストレスなく十分に使いこなしている人があえてTraefikに移行する必要はないかもしれません。Traefikで出来ることはNginxでも出来ると思っていいと思います。
ココがポイント
情報量の少なさ、ドキュメントの分かりづらさがデメリット。
ただし一般的なユースケースの場合は設定が簡素化出来るという点で移行のメリットもあるかと思います。
メリット・デメリットを述べたところで、次はTraefikの基本的な概念や用語を解説していきます。
Traefikの基本
基本的な概念・用語
Traefikで知って置くべき概念・用語としては大きく言えば以下の5つです。
- Provider
- EntryPoint
- Router
- Middleware
- Service
それぞれの役割は以下の通りです。
Provider | インフラ内にあるサービスを検出する |
EntryPoint | 受け付けるポートを設定し、トラフィックをルーターに振り分ける |
Router | どのリクエストをどのサービスに届けるか決める |
Middleware | リクエストをサービスに届ける前に、認証やレート制限など何らかの処理をする |
Service | リクエストを実際のサービスに届ける。ロードバランスなどを行う |
ここに注意
「Traefikの用語としてのService」といわゆる一般的な意味での「Service」は指しているものが微妙に違います。混乱するかもしれません。
例 ↓↓
- Services forward the request to your services (load balancing, ...)
参考:
Traefikでの処理の流れを補足:参考図
これだけでは分からないと思いますので参考図を用意しました。
手書きですので分かりづらい方はドキュメントを読むとそれぞれ図解がされています。
ここまでで基本の概念について解説しましたので、Traefikの設定について解説していきます。
設定の仕方
Traefikの設定については大きく分けてStatic ConfigurationとDynamic Configurationの2種類があります。
Static Configuration
起動時に読み込まれる設定でProviderの設定やEntrypointの設定をします。
Static Configurationには3つの書き方があります。
- 設定ファイルを書く方法 (yamlファイルかtomlファイル)
- docker-composeなどでコマンドライン引数として指定する
- 環境変数として設定する
上から順に読み込まれますが、どれか一つしか使用できないので注意。基本的にはtraefik.ymlファイルに設定を書けばOK。
例えば1. の方法(traefik.yml)はこんな感じで書きます。
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
これをtraefikコンテナ内の/etc/traefik/
に配置します。
設定ファイルを置く場所については以下を参考に:
もしくは2.の方法はdocker-compose.ymlにこんな感じで書きます。
(commandの部分)
services:
reverse-proxy:
image: traefik:v2.5.3
ports:
- '80:80'
- '443:443'
command:
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
...略
基本的には1. の方法(traefik.ymlを書く方法)が一般的なようです。
ココがポイント
Static Configurationはtraefik.ymlに書くか、docker-compose.ymlに書く。ProviderやEntryPointの設定を書く。
Dynamic Configuration
ホットリロードされる設定で、リクエストがどのように処理されるか設定を書きます。Router, Middleware, Servicesの設定を書きます。
具体的な設定方法はProviderによって異なるのですが、例えばProviderにDockerを指定している場合は、docker-compose.ymlに以下のようにlabelsとして書きます。
version: "3.3"
services:
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
(↑ではRouterの設定をしています。)
他のProviderでは設定ファイル(yaml, toml)を用意する必要があったりします。
ココがポイント
Providerによって設定方法が異なる。Dockerの場合はcomposeファイルでlabelsとして設定。
色々説明しましたが、最低限動かすためにはそんなに沢山の設定を書く必要はありません。
今回の記事ではローカル環境でMERN構成を動かしてみます。
ローカル環境でTraefikを試してみる
準備
ReactとExpressのソースコードはこちらから。(Traefikのソースコードも含んでいます)
Reactはcreate-react-app, Expressの方はexpress-generatorを使ってプロジェクトの作成をし、簡単なAPI通信を実装しています。
TraefikでMERNを動かす
では実際にTraefikでMERNアプリが動くように設定していきます。
Traefik, フロントエンド、バックエンドのうちTraefikから設定していきます。
Traefik
まずはStatic Configurationを書きます。上で述べたようにStatic ConfigurationはProviderとEntryPointの設定を行います。
今回の例ではProviderはDocker, EntryPointは80番ポートに設定します。
reverse-proxy > config > traefik.yml
providers:
docker: {}
entryPoints:
web:
address: ':80'
entryPointsのwebはエントリーポイントの「名前」ですので自由に決めて構いません。
これが基本ですが、Providerの設定にいくつか追加でオプションを指定します。
providers:
docker:
exposedByDefault: false
network: web-services
まずexposedByDefault
ですが、これをfalse
に指定することでLabel付けしていないdockerサービスをTraefikが無視するようになります。
サービス側で以下のようにlabelsを指定しなければならなくなります。
labels:
- traefik.enable=true
逆に言えばデフォルトの状態では立ち上がっているdockerサービスを全てTraefikが見つけてしまい、管理がしづらい状態になっています。なのでこのexposedByDefaultはfalseにするのが一般的です。
もう一つnetwork: web-services
でTraefikが使うデフォルトネットワークを指定しています。「web-services」はDockerのカスタムネットワークなので名前は何でも構いませんが、このネットワーク設定は必ずしておきましょう。
(後でdocker network create web-services
で作ります。)
Traefikに管理されるサービスはTraefikから見つけられるネットワーク上になければなりません。直接Traefikとやりとりをするサービス全てのdocker-compose.ymlでこのカスタムネットワークを指定する必要があります。
最後にDashboardの有効化とログレベルの設定をします。
api:
insecure: true
log:
level: DEBUG
Traefikの管理画面(Dashboard)を有効化するために、insecure: true
を指定しています。このオプションによって自動的にDashboardが有効化されます。
これはローカル用の設定で、通常はセキュリティのため管理画面を使うにはベーシック認証などの認証機能を有効化する必要があります。
今回はローカルでテストするだけですので、簡易用の設定の方を使います。
最終的なtraefik.ymlのコードはこちら。
providers:
docker:
exposedByDefault: false
network: web-services
entryPoints:
web:
address: ':80'
api:
insecure: true
log:
level: DEBUG
次にTraefik自体のdocker-compose.ymlを書いていきます。ついでにMongoDBサービスも一緒に書いてしまいます。
reverse-proxy > docker-compose.yml
version: '3.8'
services:
reverse-proxy:
image: traefik:v2.5.3
restart: always
ports:
- '80:80' # http
- '8080:8080' # Dashboard用
networks:
- web-services # traefik.ymlで指定したネットワーク名であることを確認しておくこと
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 必須
- ./config:/etc/traefik/ # 設定ファイルをマウントするため
# MongoDBはTraefikで管理しない
mongo-db:
image: mongo:5.0.3-focal
container_name: mongo-example
restart: always
volumes:
- mongo-example:/data/db
environment:
# ローカルでのテストなのでユーザ名とパスワードは直に書いています
MONGO_INITDB_ROOT_USERNAME: mongoAdmin
MONGO_INITDB_ROOT_PASSWORD: mongoPassword
networks:
# Traefikで管理しないのでweb-servicesネットワークを指定する必要はない
- backend-db
volumes:
mongo-example:
# 事前にdocker network createで作っておくこと
networks:
web-services:
external: true # 他のcomposeファイルでも使うためexternalにしておいた方が便利
backend-db:
external: true
*ここではMongoDBのサービスも一緒に書いていますが、Traefikとは直接関係がないので別ファイルに分けてもOKです。
ポイントはVolumeの部分で、ProviderにDockerを指定する場合はTraefikコンテナとdocker sockを共有する必要があります。
また- ./config:/etc/traefik/
はStatic Configurationファイル、つまりtraefik.ymlをマウントするために必要です。
以下のコマンドでnetworkを作成した上で、docker-compose up
でサービスを立ち上げます。
$ docker network create web-services
$ docker network create backend-db
http://localhost:8080/dashboard/にアクセスすると以下のようにダッシュボードが表示されるはずです。
/dashboard/の最後のスラッシュは必須ですので注意して下さい。
以上でTraefik側の設定は完了です。
ここからは各サービスについて設定していきます。とはいえcomposeファイルにLabel付けをするだけなので簡単です。
バックエンド
サービスを立ち上げる場合はそのサービスについてのRouter, Middleware, Serviceの設定をDynamic Configurationとして書くことができます。
今回はローカルで動かすだけなのでRouterの設定を書くだけでOKです。
先で述べたようにDockerの場合、composeファイルのlabelsで設定を書きます。
backend > docker-compose.yml
version: '3.8'
services:
backend-api:
image: dummy-backend
build:
context: .
networks:
- web-services
- backend-db
labels:
- traefik.enable=true
- traefik.http.routers.backend-api.entrypoints=web # traefik.ymlで設定した名前と一致していること
- traefik.http.routers.backend-api.rule=Host(`api.localhost`)
networks:
web-services:
external: true
backend-db:
external: true
RouterのRuleとしてapi.localhostを指定しています。traefik.http.routers.backend-api.entrypointsの「backend-api」の部分はルーター名なので自由に決めて構いません。
traefik.ymlで設定したentrypointを指定することで80番ポートのみのアクセスを受け付けるように設定しています。
補足
今回はRouterの設定のみ書きましたが、Middlewareとしてレート制限を指定したり、ロードバランスの設定をすることも出来ます。
docker-compose up
でサービスを立ち上げhttp://api.localhostにアクセスするとJSONが返ってきます。
これでTraefikを介してExpressのAPIサーバーにアクセスすることが出来ました。
フロントエンド
最後にフロントエンド側の設定を書いていきます。バックエンド側と同様でcomposeファイルにlabel付けをするだけです。
version: '3.8'
services:
frontend-app:
image: dummy-frontend
build:
context: .
networks:
- web-services
labels:
- traefik.enable=true
- traefik.http.routers.frontend-nginx.entrypoints=web
- traefik.http.routers.frontend-nginx.rule=Host(`frontend.localhost`)
networks:
web-services:
external: true
フロントエンドはfrontend.localhostに設定しました。
http://frontend.localhostへアクセスし表示されるか確認します。
これでReactアプリもTraefikを介してアクセスすることが出来ました。
上の画像の”Hello World with MongoDB”の箇所はExpressのAPIから取得した文字列なので、フロントエンドとバックエンド間の通信も正常に動作していることが確認できます。
長々と説明しましたが、実際の設定は簡単に出来るということが分かっていただけたら嬉しいです。
ローカルだけでなく、実際にデプロイする際のLet's EncryptによるTLS対応もかなり簡単にできます。
ただ記事が長くなってしまいましたので、TLS化については次回の記事に持ち越しとさせて下さい。
注意点 & まとめ
注意点
ポートについて
Traefikはポートの検出に各サービスのDockerイメージでExposeされたポートを使います。
ポートを公開していない場合、もしくは複数のポートを公開している場合はtraefik.http.services.<service_name>.loadbalancer.server.port
を指定する必要があります。
参考:
よくあるエラー
502 Bad Gateway
やGateway Timeout
が起こることがあります。
問題が起こった時はログレベルをDEBUG
に指定した上で、ログを確認します。
connection refused
が帰ってきている場合はサーバー側のポート設定が間違っていることが考えられます。(例えば3000番ポートを開いているつもりが、環境変数などの兼ね合いで別のポートになっていたなど。)
connection refused
ではない場合、ネットワーク関連の設定が間違っている可能性があります。
例えばStatic Configurationでのデフォルトネットワークの設定を忘れていたり、サービス側のcomposeファイルでnetworkの指定が間違っているかもしれません。
docker network inspect ネットワーク名
などのコマンドできちんとネットワークが接続されているか確認してみましょう。Traefikの管理画面も参考になるかもしれません。
まとめ
今回はTraefikの大まかな説明、メリット・デメリット、ローカル環境でのサンプル動作を記事にしました。
長い記事になってしまいましたが、ソースコードを見ていただければTraefik関連の設定はほんの少しで済むと分かっていただけると思います。
この簡潔さに加え、ネイティブでのコンテナ技術への対応がTraefikを使う主な理由だと思います。
他にも色々出来ることはあるのですが、折を見て記事にしていこうかと思います。
とりあえず次回はLet's EncryptでTLS対応させる方法・管理画面に認証を加える方法について解説していきます。