Jenkins + NGINX + SSL + Docker で同じURLドメインのパスでルーティングする
背景
NGINXをリバースプロキシとして、複数のコンテナに異なるドメインで振り分ける構成の記事は見つけたのですが、パスで異なるコンテナにルーティング & SSL通信する記事が見当たらなかったので備忘として残しておきます。もし同じような構成にする方がいればご参考いただけると幸いです。
(間違ってるところがあればご指摘お願いいたします)
制限事項
Jenkinsを使ってビルドする際に、同じドメインを使っているアプリケーションのコンテナを再起動したりすると、その再起動中はJenkinsもHTTP通信が途絶えます...。(いい方法があったら教えてください🙇♂️)
環境
OS
GCP の Container-Optimized OS
cos-81-lts
Docker
19.03.6
※ docker-composeを使います。
docker/compose:1.24.0
アーキテクチャ
アプリサーバーのコンテナがあるインスタンス上に、Jenkinsのコンテナも配置します。/ci
というパスでJenkinsコンテナにルーティングして、それ以外はアプリケーションのコンテナに向くようにします。
構築手順
Jenkins
Jenkinsのserviceは以下のようになりました。
version: "3"
services:
jenkins:
image: jenkins/jenkins:lts
environment:
- VIRTUAL_HOST=ci.example.jp # (1)
- VIRTUAL_PORT=8080
- JENKINS_OPTS="--prefix=/ci"
ports:
- "8080:8080"
- "50000:50000"
volumes:
- ./jenkins_home:/var/jenkins # (2)
networks:
- reverse-proxy
networks:
reverse-proxy: # (3)
external: true
(1) VIRTUAL_HOST
を環境変数にセットすることで、後述するNGINXコンテナnginx-proxy
が振り分けれるようにしてます。
(2) ホストの./jenkins_home
ディレクトリとJenkinsコンテナ上の/var/jenkins_home
ディレクトリを同期してます。Jenkinsのデータを永続化するためです。
(3) ネットワークは既存のものを使うようにしています。以降のコンテナもすべて同じネットワークを使います。
NGINX
リバースプロキシです。
nginx:
image: jwilder/nginx-proxy
container_name: nginx-proxy
environment:
- NGINX_PROXY_CONTAINER=nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- certs:/etc/nginx/certs:ro
- vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./proxy:/etc/nginx/vhost.d
- ./proxy/app.example.jp:/etc/nginx/vhost.d/app.example.jp # (1)
- ./proxy/ci.example.jp:/etc/nginx/vhost.d/ci.example.jp # (2)
restart: always
labels:
- "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=true"
networks:
- reverse-proxy
volumes:
certs:
vhost:
nginx-html:
アプリケーションコンテナのVIRTUAL_HOST
と同じ名前のバーチャルホストの設定ファイル(ここではapp.example.jp
とci.example.jp
)をマウントすると、自動でinclude
してくれます。ためしに見てみます。
$ docker exec -it nginx-proxy /bin/bash
NGINXプロキシのコンテナにはいって、default.conf
をのぞくと
# cat /etc/nginx/conf.d/default.conf
server {
server_name ci.example.jp;
# ...
include /etc/nginx/vhost.d/ci.example.jp;
# ...
}
server {
server_name app.example.jp;
# ...
include /etc/nginx/vhost.d/app.example.jp;
# ...
}
include
されてました。
app.example.jp
(1) アプリケーションのバーチャルホスト設定 Jenkinsのリファレンスを参考に、バーチャルホストの設定ファイルは以下のようにしました。
## (1-1) Lets encrypt(SSL)のための設定
location ^~ /.well-known/acme-challenge/ {
auth_basic off;
auth_request off;
allow all;
root /usr/share/nginx/html;
try_files $uri =404;
break;
}
## (1-2) Jenkinsのための設定
### (1-2-1) トレイリングスラッシュあり
location ^~ /ci/ {
sendfile off;
### (1-2-2) upstream(=VIRTUAL_HOST) と同じにする
proxy_pass http://ci.example.jp;
proxy_redirect default;
proxy_http_version 1.1;
# Required for Jenkins websocket agents
### (1-2-3) 変数をあわせる
proxy_set_header Connection $proxy_connection;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_max_temp_file_size 0;
#this is the maximum upload size
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffering off;
proxy_request_buffering off; # Required for HTTP CLI commands
proxy_set_header Connection ""; # Clear for keepalive
}
(1-1) Let's encrypt(SSL)のための設定
本来は、後述のLet's encrypt companionコンテナを起動すると自動で書き出される設定なのですが、SSL設定したいドメインのバーチャルホスト設定ファイル(ここではapp.example.jp
)をマウントすると、設定されなかったのであらかじめ記載しました。(ほかにやり方が思いつきませんでした...)
(1-2) Jenkinsのための設定
(1-2-1) トレイリングスラッシュあり
末尾のスラッシュをつけることで、/ci
のパスを無視するようにしてます。
(1-2-2) upstream(=VIRTUAL_HOST) と同じにする
振り分け先のドメインにあわせてhttp://ci.example.jp
にしました。自動生成されていたnginx設定ファイルdefault.conf
の該当箇所は以下です。
# ci.example.jp
upstream ci.example.jp {
## Can be connected with "reverse-proxy" network
# jenkins
server 172.18.0.3:8080;
}
(1-2-3) 変数をあわせる
WebSocketのための設定です。Jenkinsのリファレンスでは$connection_upgrade
という変数名で書かれてますが、自動生成されるnginx設定ファイルdefault.conf
をみると、$proxy_connection
となっていたので、そちらにあわせてます。
map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
ci.example.jp
(2) Jenkinsのバーチャルホスト設定 静的ファイルへのアクセスはJenkins側のバーチャルホスト設定ファイルとして別で定義しました。
location ~ "^/static/[0-9a-fA-F]{8}\/(.*)$" {
rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last;
}
location /userContent {
root /var/jenkins_home/;
if (!-f $request_filename){
rewrite (.*) /$1 last;
break;
}
sendfile on;
}
Let's encrypt
Let's encryptを使ってSSL証明書を発行してくれるコンテナです。
SSL証明書を発行する際に検証のためにつかうディレクトリや、証明書自体を保存しておくボリュームをvolumes
に定義しています。ここがNGINXコンテナと記載があってないと、SSL証明書発行に失敗します。
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
container_name: letsencrypt
depends_on:
- app
volumes:
- certs:/etc/nginx/certs:rw
- vhost:/etc/nginx/vhost.d
- nginx-html:/usr/share/nginx/html
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: always
networks:
- reverse-proxy
アプリケーション
最後にアプリケーションのコンテナです。environment
でバーチャルホストやSSL証明書発行ための環境変数をセットしてます。
app:
container_name: app
environment:
- VIRTUAL_HOST=app.example.jp
- LETSENCRYPT_HOST=app.example.jp
- LETSENCRYPT_EMAIL=test@test.test
depends_on:
- nginx
networks:
- reverse-proxy
以上です。
Discussion