こんにちは。テクマトリックスの酒井です。
CI 環境の構築サービスやテクニカルサポートなどで日頃から様々なアプリケーションの検証や環境の構築のご要望をいただいております。そのようなご要望に応えるためにコンテナを使って検証用の環境を作成することが日常になってきました。
多くのアプリケーションには Web UI がありますが、本番環境ではこれらのフロントエンドとしてリバースプロキシやロードバランサーを利用するのが一般的です。フロントエンドとしてコンテナとの相性が良さそうな Traefik というプロキシがあることを最近知りましたので、今回は Traefik を使って簡単な CI サーバーを作成してみました。
目次
Traefik とは?
Traefik (「トラフィック」と発音します)は、マイクロサービスのデプロイを容易にする最新のリバースプロキシおよびロードバランサーです。 Traefik は、既存のインフラストラクチャコンポーネントと統合し、それ自体を自動的かつ動的に構成します。
Traefik & Docker によると、コンテナにラベルを付けたら、あとは Traefik にお任せください!とのことです。
作成する環境
1台の Docker ホストで Jenkins と Gitea のコンテナを実行します。それぞれのアプリケーションには次のようなポート番号のない URL でアクセスすることにします。
http://${HOSTNAME}/jenkins/
http://${HOSTNAME}/git/
なお ${HOSTNAME}
は Docker ホスト名を表します。
環境を作成する
コンテナを実行するために、 Docker および Docker Compose がインストールされたマシンで作業を行います。また HOSTNAME
環境変数に Docker ホスト名が設定されているものとします。
Traefik を起動
最初に Traefik を起動します。 Traefik は Docker ホストの外部からのリクエストをポート 80 番で待ち受け、 URL に応じてそれぞれのコンテナに振り分けます。
まずはリバースプロキシのサービスだけを定義します。以下の内容で docker-compose.yml
ファイルを作成します。
version: '3'
services:
reverse-proxy:
image: traefik:v2.9
command:
- --providers.docker.exposedByDefault=false
- --providers.docker.network=${COMPOSE_PROJECT_NAME}_default
- --entryPoints.web.address=:80
ports:
- 80:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
このファイルでは reverse-proxy
というサービス名で Traefik コンテナを実行するよう定義します。コマンドラインオプションで、この docker-compose.yml
ファイルで起動するコンテナにだけリクエストを転送可能とするよう制限します。 Docker ホストのポート 80 番へのリクエストを Traefik のエントリーポイント web
が待ち受けます。 Traefik が Docker ソケットにアクセスするために /var/run/docker.sock
をマウントする必要があります。
では Traefik を起動します。
$ docker compose up -d
コンテナイメージのダウンロードが終わればすぐに起動すると思います。起動後、 Traefik にアクセスしてみます。
$ curl http://${HOSTNAME}/
404 page not found
レスポンスが返ってきましたので、Traefik が稼働していることが確認できました。
Jenkins を起動
続いて Jenkins を起動します。先程作成した docker-compose.yml
ファイルに以下を追加します。
jenkins:
image: jenkins/jenkins:2.375.2-lts
command:
- --prefix=/jenkins
volumes:
- ./jenkins-data:/var/jenkins_home
labels:
- "traefik.enable=true"
- "traefik.http.routers.jenkins.entrypoints=web"
- "traefik.http.routers.jenkins.rule=PathPrefix(`/jenkins/`)"
この設定では、 Jenkins を jenkins
というサービス名で実行します。 Jenkins に http://${HOSTNAME}/jenkins/
という URL でアクセスするために、 command
で --prefix=/jenkins
コマンドラインオプションを渡す必要があります。 Docker ホストの外部から直接 Jenkins にアクセスする必要はありませんので、ポートを公開する設定( ports
)はしません。
そして labels
で Traefik の動作を設定するラベルを追加しています。これらのラベルでは、 Traefik のエンドポイント web
で受信してパスが /jenkins/
で始まるリクエストをこのサービス( Jenkins コンテナ)に転送する、という設定をしています。
では Jenkins を起動します。
$ docker compose up -d
Jenkins が起動するまで少し待ってからブラウザで http://${HOSTNAME}/jenkins/
にアクセスすると、 Unlock Jenkins のページが表示されます。リバースプロキシの設定ファイルを作成することなく Jenkins にアクセスできました。
Giteaを起動
さらに、 Git ホスティングサービスを提供する Gitea を起動します。 Gitea は MySQL と組み合わせて実行することにします。
Gitea のドキュメント を参考にして docker-compose.yml
に以下を追加します。
gitea:
image: gitea/gitea:1.18.1
environment:
- ROOT_URL=http://${HOSTNAME:?HOSTNAME not set}/git/
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=db:3306
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
restart: always
volumes:
- ./gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
- db
labels:
- "traefik.enable=true"
- "traefik.http.routers.gitea.entrypoints=web"
- "traefik.http.routers.gitea.rule=PathPrefix(`/git/`)"
- "traefik.http.routers.gitea.service=gitea-svc"
- "traefik.http.services.gitea-svc.loadbalancer.server.port=3000"
- "traefik.http.routers.gitea.middlewares=gitea-mw"
- "traefik.http.middlewares.gitea-mw.stripprefix.prefixes=/git"
db:
image: mysql:8
restart: always
environment:
- MYSQL_ROOT_PASSWORD=gitea
- MYSQL_USER=gitea
- MYSQL_PASSWORD=gitea
- MYSQL_DATABASE=gitea
volumes:
- ./mysql:/var/lib/mysql
この設定では、 Gitea を gitea
というサービス名で実行します。 Gitea に http://${HOSTNAME}/git/
という URL でアクセスするために、 gitea
コンテナに ROOT_URL
環境変数を設定する必要があります。また Gitea には Jenkins よりも多くのラベルを指定しました。ラベルで設定した内容は3種類あります。一つ目は Traefik のエンドポイント web
で受信してパスが /git/
で始まるリクエストを Gitea コンテナに転送します。二つ目は Traefik コンテナから Gitea コンテナへのプライベートな接続に使用するポートに 3000 番を指定します(指定しないと SSH のポートに接続しようとしてしまいました)。三つ目は Gitea コンテナへリクエストを転送する前に URL のパスの先頭にある /git
を取り除きます(指定しないと Gitea が 404 エラーを返してきました)。
また MySQL を db
というサービス名で実行します。 db
サービスにはラベルを設定しません。そのため db
サービスが Traefik によって外部に公開されることはありません。
それでは Gitea を起動します。
$ docker compose up -d
ブラウザで http://${HOSTNAME}/git/
にアクセスすると、 Gitea の初期設定のページが表示されます。無事 Gitea にアクセスできました。この時点でももちろん Jenkins にアクセスできます。
感想とまとめ
Traefik をリバースプロキシとして利用して、1台の Docker ホストで Jenkins と Gitea を実行する環境を作成しました。これまではリバースプロキシとして Nginx をよく利用していましたが、設定ファイルを記述した後、 Nginx を再起動して設定を反映する必要がありました。 Traefik は設定を記述する量が少なくて済み、またコンテナのラベルを設定すると自動的に Traefik に設定が反映されるため、コンテナで実行されるアプリケーションのリバースプロキシとして非常に使いやすいと思いました。
一方、 Gitea コンテナへリクエストを転送する前に URL のパスの先頭にある /git
を取り除く設定が必要な理由が未だ理解できていません。 Jenkins コンテナへは /jenkins
を取り除く必要がないので、アプリケーションごとに適切な設定を探す必要がありそうです。どのようなラベルが設定できるかは、 Routers, Services, HTTP Middleware など Traefik のドキュメントで確認できます。
参考
- 【入門】Traefikとは?基本的な使い方を解説 – エンジニアの本棚(最終確認日:2023年1月31日)
- Traefik Proxy Documentation – Traefik