Traefikとは

Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience.

エッジルーターらしいです。まだクラウドとかエッジとかの知識がないのでよく分かりません。 私としてはサブドメインのページを手軽に作れるリバースプロキシーとして使用しています。

便利な点:

  • docker composeでlabelsを設定するだけで動く
    • なんならコンテナ起動するだけでいい
    • リバースプロキシーの設定が不要なので手軽
  • ポート番号をいちいち考えなくてもいい
    • リバースプロキシーがなければ、アプリごとにポート番号を割り当てる必要があり大変
  • ダッシュボードで設定が確認できる
    • どのアプリが起動しているかブラウザで一覧できる

ダッシュボードのスクリーンショット ダッシュボード

ルーターのスクリーンショット routers

前提

使用マシンは Mac mini (M1, 2020)で、Docker環境を Colima で構築しています。 が、他の環境でも同じでしょう。

設定

ここでは開発環境を想定した説明となります。そのためできるだけ対応が少ない方法をとっています。 ということでTraefikeのDockercompose.yamlを用意して起動します。

compose.yaml

services:
  traefik:
    image: traefik
    hostname: reverse-proxy
    restart: unless-stopped
    volumes:
      # dockerと連携するために必要
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command:
      # docker連携を有効にして、デフォルトのルールを設定
      - --providers.docker.defaultRule=Host(`{{ normalize .Name }}.localhost`, `{{ normalize .Name }}.test`)
      # entrypointsで対応したいポート番号を書く
      - --entrypoints.http.address=:80
      - --entrypoints.mongodb.address=:27017
      # apiを有効にするとダッシュボードが見れる(http://localhost:8080/)
      - --api.insecure=true
    # コンテナ間をhost経由でアクセスさせるためネットワークモードを指定
    network_mode: host

これを docker compose up -d で起動すると、http://localhost:8080/でTraefikのダッシュボードが確認できます。

defaultRule.localhost.testを追加しています。通常だと{{ normalize .Name }}だけなのでブラウザで簡単にアクセスできません。 entrypointsにポート番号を2つ指定しています。Traefikはhttpだけでなく、tcpにも対応しています。

--entrypoints.{任意の名前}.address=:{ポート番号}でポート番号を利用する入り口がつくられます。

分かりづらいですが、api.insecureを有効にするとダッシュボードも一緒に見れるようになります。 apiが何なのかは調べてないのでわからないですが本番では注意が必要だと思います。

このように設定を全て起動引数で指定すると、設定を変更したときにdocker compose up -dを再度行うだけで反映されます。 traefik.yamlを用意してマウントする方式だと一旦止めないと設定が反映されません。(ファイル保存だけで反映される方法があればぜひ教えて〜)

使用

コンテナ単体起動

docker run --rm -d --name caddyTest caddy

caddyが何かはよく知らないけど、どれかのサンプルで使われてて、「おめでとう!」と言ってくれるので出来た感が嬉しい。

それはそれとして、このコマンドで起動後にダッシュボードを見るとcaddyTest.localhostが追加されてるのが確認できます。

caddyTest

Chromeでhttp://caddyTest.localhostにアクセスすると、「おめでとう!」と言ってくれます。 大文字小文字の区別はありません。http://caddytest.localhostでもOK。

http://caddyTest.testでアクセスするためには、DNSの追加設定が必要です。次のセクションにGO!

NOTICE 注意 Safariだと.localhostの名前解決できません。Safariで確認が必要なら、ローカルDNSを立ち上げる必要があるようです。

ローカルDNS dnsmasq

safariで*.localhostでアクセスできるようにするのと、dockerコンテナ内からもホストにアクセスするために*.testを定義します。

dnsmasqについてはよく分かってないです。が次の手順で動きます。

  1. brew install dnsmasqでインストール

  2. /opt/homebrew/etc/dnsmasq.confに追記(/otp/homebrewはhomebrew --prefixで確認可能)

    # localhostを127.0.0.1に解決
    address=/.localhost/127.0.0.1
    # testをホストマシンのIPアドレスに解決(固定IPにしておく)
    address=/.test/10.0.0.101
    
  3. sudo brew services start dnsmasqで起動

  4. sudo mkdir /etc/resolverでDNS指定用のディレクトリ作成

  5. sudo vi /etc/resolver/localhostでlocalhostファイルを作る(ファイル名がドメイン名)

    nameserver 127.0.0.1
    options timeout:1
    
  6. sudo vi /etc/resolver/testでtestファイルを作る(ファイル名がドメイン名)

    nameserver 127.0.0.1
    options timeout:1
    
  7. ping -c 1 hoge.localhost, ping -c 1 fuga.testで動作確認

  8. Safariでhttp://caddyTest.localhost,http://caddyTest.testにアクセスすると、「おめでとう!」と言ってくれます。

ホストマシンのIPアドレスが固定できない場合は、変わるたびにdnsmasqの設定を書き換えるか、自分で簡易DNSサーバーを作る対応が必要です。 Dynamic DNSの仕組みでも解決できるかも?

ちゃんと見てないけど、簡易DNSサーバーなら簡単に作れそう。

compose起動

caddyさんには退場してもらいましょう。(docker stop caddyTest)

普段はオプションが面倒なのでcompose.yamlを利用しています。例えばMongoDBを例にしてみましょう。

name: my-proj

services:
  mongo:
    image: mongo
    hostname: mongo
    restart: unless-stopped
    ports:
      - 127.0.0.1::27017
    volumes:
      - mongodb-data:/data/db
      - mongodb-config:/data/configdb
    labels:
      # traefikの設定
      traefik.tcp.routers.mongo.entrypoints: mongodb
      traefik.tcp.routers.mongo.rule: HostSNI(`*`)
  mongo-express:
    image: mongo-express
    hostname: mongo-express
    restart: unless-stopped
    depends_on:
      - mongo
    ports:
      - 127.0.0.1::8081
    environment:
      ME_CONFIG_MONGODB_SERVER: mongodb.test

volumes:
  mongodb-data:
  mongodb-config:

これでhttp://mongo-express-my-proj.testでMongo Expressにアクセスできます。 これは、traefikのルールの.Nameがservice名(mongo-express)-COMPOSE_PROJECT_NAME(my-proj)となるためです。

MongoDBの方はというと、labels:の指定があります。 というのもデフォルトではHTTPプロトコルとして登録されるためで、TCP通信の場合は指定が必要です。 traefik.tcp.{任意の名前}.entrypointsで使用するポート番号を指定し(traefikの起動コマンドで書いたもの)、 traefik.tcp.{同上}.ruleで条件を指定します。TCPのルールはほとんど書けません。(TLSならドメイン名で分岐ができるらしい)

このサンプルではmongo-expressからmongoへのアクセスを無理やりホスト経由(mongodb.test)にしています。 (*.testを使うにはローカルDNSの設定が必要です。ここではhost.docker.internalでホストにアクセスも可能) ホスト側を127.0.0.1の自動で割り振りのポート番号を利用している127.0.0.1::8081)ので、本来はdocker compose psで確認できる大きい数字のポート番号でアクセスする必要があります。 間にTraefikが入ってくれて、いい感じにコンテナに転送してくれています。

ここでTraefikのダッシュボードを見てください。 TCPの方はいい感じに登録されています。 HTTPの方はEntrypointsにhttpとmongodbの両方が登録されてしまっています。 これはTraefikに登録しているentrypointsが全て割り当てられるためで、実はhttp://mongo-express-my-proj.test:27017でアクセスできる時もあります。(MongoDBが立ち上がってなくて、Mongo Expressが他のDBに繋がってる時) Traefik Version 1時代はdefaultEntrypointsで指定できたみたいですが、今のVersion 2だと無理みたいです。 ここでは開発用なので気にしないですが、本番ではちゃんと設定したほうがいいでしょう。

本番運用に向けて

本番(と言ってもアクセスが限定される社内)ではもう少し設定を書いて運用してます。

  • Traefikの設定をtraefik.yamlに書いて、/etc/traefik/traefik.yamlにマウント
  • providers.docker.exposedByDefault=falseにして、余計なものが公開されないようにする
    • compose.yamlのlabelsにエントリーポイントやルールを明記する
  • ルールにパスを見たり、クエリを見たり、ミドルウェアを利用してパスを変換したり(/apiを消すとか)、リダイレクトしたり、SSL利用したり使用方法はいろいろあります

まとめ

ローカルで複数のコンテナを起動する場合はTraefikがあるとかなり便利ですのでおすすめです。 とりあえず使う分には手軽ですので試してみると幸せになれるかもしれません。