【2020年1月版】Traefik 2.0 で Docker-Compose の"80"ポートを交通整理


ホストのポート番号、足りていますか?!

docker-compose でバンバンサービスを立ち上げて監視しつつ、開発しつつ、実験などやっていると、3000とか8080とかぶつかって8081とか8180とか、ポート番号をずらすのが大変になってきますよね?
ちょっと違いますが僕は先日、k3s上の何かのサービスのhttps用ポートで8443がぶつかってしまったので6443に変更し、 k3sのクラスタを丸ごと喪失させました。k3sが6443使ってるんだよね!

Dockerで多数のサービスがサクサク動かせるのは良いことなのですがホストのポート番号がマヂでヤヴァイのであります。

そんなあなたに!今回は Traefik と nip.io のワイルドカードDNS を使ってポート番号の衝突から解放される手順をご紹介します!

対象環境

OS: Ubuntu 18.04.3 LTS (Bionic Beaver)
docker: 19.03.5
docker-compose: v1.24.0
ip アドレス: 192.168.1.50

今回は ipアドレス 192.168.1.50 の Ubuntu に docker-compose でモリモリサービスを立ち上げてみます。

何をするのか

以下の手順で行う内容ですが、先にタネを明かしてしまいますと、nip.io のワイルドカードDNSを使って xxxxx.192.168.1.50.nip.ioyyyyy.192.168.1.50.nip.ioなどをすべて 192.168.1.50 に解決させてしまいます。
これでホスト名が変わっても192.168.1.50にすべてリクエストが行きますが、Traefikで "サブドメイン"でルーティングさせ て対応する Docker コンテナにリクエストを転送させるようにします。

また、Traefikの設定をコンテナ側で行うことで、コンテナをデプロイするだけで Traefik にサービスが自動的に登録されるようになります。

これによって Traefik コンテナだけで 80 番を待ち受けていれば、好きなコンテナを起動するだけでホスト名でのルーティングができるようになっている、ということが実現できます。

この手順を以下で紹介いたします!

1. Traefik の準備

まずは以下のような Traefik 起動用の docker-compose.yml を、traefikフォルダに作成いたします。

docker-compose.yml
version: '3'
networks:
  web:
    driver: bridge

services:
  reverse-proxy:
    image: traefik:v2.1 # The official Traefik docker image
    ports:
      - "80:80"     # The HTTP port
      - "8080:8080" # The Web UI (enabled by --api)
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
      - ./traefik.yml:/etc/traefik/traefik.yml
    networks:
      - web

まずは "network" に web というネットワークを定義しております。
後ほど解説いたしますがここでは単に、traefik 経由で外に出たいコンテナはwebネットワークに入ってね、ということだけ覚えておけばOKです。
また他のdocker-compose.ymlからこのwebネットワークを参照しますが、docker-composeの作成するネットワークは"フォルダ名_ネットワーク名"になるます。
今回は traefikフォルダでdocker-compsoeを起動しますので、他から参照する時はtraefik_webというネットワーク名となります。

続いて Traefik のポートの設定ですが、80:808080:8080 の設定で80番と8080番はホストとバインドさせて、Traefik に入るようにいたします。このマシンで使用する80番と8080番はTraefik専用、といたします。

続いて volumes で指定している ./traefik.yml:/etc/traefik/traefik.yml ですがこれに対応するように以下の設定ファイルを作成いたします。

traefik.yml
## traefik.yml

# https://docs.traefik.io/

# Docker configuration backend
providers:
  docker:
    exposedByDefault: false
    defaultRule: "Host(`{{ index .Labels \"traefik.host\" }}.192.168.1.50.nip.io`)"

entryPoints:
  http:
    address: ":80"
  # smtp:
  #   address: ":25"
  # pop3:
  #   address: ":110"
  # imap:
  #   address: ":143"
  # mysql:
  #   address: ":3306"
  # psql:
  #   address: ":5432"
  # mongo:
  #   address: ":27017"

# API and dashboard configuration
api:
  insecure: true

defaultRule で "Host({{ index .Labels \"traefik.host\" }}.192.168.1.50.nip.io)" を指定しております。
この "Host({{ index .Labels \"traefik.host\" }}.192.168.1.50.nip.io)" がキモの一つです。

以下の設定サンプルにある通り、コンテナにつけた名前と"ラベル"がルールに使用できます。

今回はコンテナ名とは別にtraefik.host ラベルで指定した文字列がサブドメインになるように指定いたしました。

続いてentryPointsの設定ですが http を対象としますので、:80のエントリーポイントを定義します。
また、例えばmongodbであれば 27017 を指定しておきます。ここで開けたポートは docker-compose.ymlportsでも忘れなく指定しておきましょう。
このように使いたいポートを"エントリーポイント"としてあらかじめ定義しておきます。

2. Traefik の起動

準備が整ったところで以下のコマンドで Traefikを起動します。

$ docker-compose up -d
...

しばらくすると http://192.168.1.50.nip.io:8080 にてTraefikのダッシュボードが立ち上がると思います。
立ち上がらなければ設定にミスなどある可能性大なので docker-compose logs reverse-proxy などでログを確認しましょう。

これでルーティングの準備は完了です。

3. サービスの起動

以下でいくつかのサンプルを上げてみましょう。

3-1. 例えば KeyCloak

それではサンプルとして最近お世話になっている KeyCloak を立ち上げてみましょうか。
いつもの docker-compose.yml に Traefik 用の設定を追加します。

docker-compose.yml
version : "3"
volumes:
  kc_postgres_data:
      driver: local

networks:
  web:
    external:
      name: traefik_web

services:
  kc-postgres:
    image: postgres
    volumes:
      - kc_postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
  keycloak:
    image: jboss/keycloak:7.0.1
    container_name: keycloak
    restart: always
    expose:
      - 8180
    environment:
      DB_VENDOR: POSTGRES
      DB_ADDR: kc-postgres
      DB_DATABASE: keycloak
      DB_USER: keycloak
      DB_SCHEMA: public
      DB_PASSWORD: password
      KEYCLOAK_USER: admin
      KEYCLOAK_PASSWORD: admin
    networks:
      - web
    command: |
      -Djboss.socket.binding.port-offset=100
    depends_on:
      - kc-postgres
    tty: true
    labels:
      traefik.enable: true
      traefik.host: keycloak
      traefik.http.routers.keycloak.entrypoints: http
      traefik.http.services.keycloak.loadbalancer.server.port: 8180
      traefik.docker.network: traefik_web

早速ですが、

networks:
  web:
    external:
      name: traefik_web

traefikwebネットワークをwebという名前で参照いたします。この外部ネットワーク参照については過去の記事も読んでいただけると幸いです

kc-postgres は"keycloak用のPostgreSQL"なのでここはスルーいたします。

さて本命のkeycloakです。
肝心なのは networksweb に参加していることと、labels の設定です。
以下に列挙します。

  • traefik.enable: このスイッチにより traefik でのルーティング対象とします。(traefik は基本的に全コンテナの起動と終了を監視しています。)
  • traefik.host: このラベルがサブドメイン名となります。ここでは keycloak.19.168.1.50.nip.io だったらこのコンテナが呼ばれる、ということになります。
  • traefik.http.routers.keycloak.entrypoints: 冒頭の traefik.ymlで指定したエントリポイントを選択します。ここでは 80のhttpを使用します。
  • traefik.http.services.keycloak.loadbalancer.server.port: 上記のhttpにマッピングされるこのコンテナのポートを指定します。keycloakが待ち受ける 8180を指定します。
  • traefik.docker.network: treafik 側からこのコンテナを見るときのネットワークを指定します。これがないとtraefikは defaultネットワーク経由でこのコンテナのIPを参照してしまうようです。

traefik.docker.networkの指定が若干、残念ですがネットワークはいくらでも定義できるのでtraefik側から判断するのは困難なのでしょう。
実は、traefik_web のネットワークに参加しなければならない大きな理由は、このtreafik側から見えるかどうかという問題です。先に立ち上がっているtraefikから、後で立ち上げたコンテナの名前を解決させるために同じネットワークに入れておく必要がある、ということなわけです。(結局、これも過去の記事と同じテーマなのです。。。)

これでブラウザから http://keycloak.192.168.1.50.nip.ioを開くと無事に Keycloakにアクセスできると思います!

3-2. 例えば Adminer

RDBに簡単につなぎに行けるWebクライアントとしては Adminerがもっともお手軽で強力だと思います。
docker-compose.ymlの追加部分はkeycloakと変わりませんのでサクッと立ち上げてしまいましょう。

docker-compose.yml
version: '3'

networks:
  web:
    external:
      name: traefik_web

services:
  adminer:
    image: adminer
    container_name: adminer
    restart: always
    ports:
      - 8080
    networks:
      - web
      - db-admin
    labels:
      traefik.enable: true
      traefik.host: adminer
      traefik.http.routers.adminer.entrypoints: http
      traefik.http.services.adminer.loadbalancer.server.port: 8080
      traefik.docker.network: traefik_web

設定内容は上記と同じです。ただし、routersservicesの名前は変えておく必要があります。
前回は traefik.http.routers.keycloak.entrypoints でしたが、今回は traefik.http.routers.adminer.entrypoints です。

この設定で http://adminer.192.168.1.50.nip.io で Adminer にアクセスできるようになります!
Keycloakよりシンプルだから先にこっちを挙げておけばよかったか。。。

あとはお好きなサービスでどうぞ!

トラブルシューティング

traefik のダッシュボードではエラーメッセージは一切出ないのが難点です。
コンテナ起動してもダッシュボードに反応がなければ docker-compose logs -f reverse-proxy で treafik のログを参照するしかないです。

traefik のエラーログではgoのエラーメッセージが出てしまうのでちょっとわかりにくいのがさらに厳しいです。
ソースコード見ても落ちている箇所はわかるけどどう設定すればいいの?みたいな場合が多々あります。
ここはダッシュボードで優しく教えてくれないかな・・・

まとめ

本家サイトのガイドではDockerを使った場合も設定ファイルにガリガリ記述させるので、コンテナ立ち上げるたびに設定直してたらキリがないな…とあまり興味がなかったのですが、以下のしれっとした記述でラベルで設定できるのを見つけたので、これ幸いと traefik を導入致しました。

いや、これあっさりしすぎでしょう。この機能、超重要ですよ?

とにかく、このようにワイルドカードDNSとTraefikを組み合わせることで、社内からアクセスできるサービスをdocker-composeでポート番号を気にせずにバンバン立ち上げることができました。

また、ワイルドカードDNSはlocalhostではないちゃんとしたホスト名なので KeycloakでのOAuth設定もばっちりです。社内サービスのSSO化もバリバリ捗ります。

いや~素晴らしい!

本日は以上といたします。