私はこれまで、枯れた技術達しか使わず、作るサイトはすべてLAMP環境を直接構築して運営するという方針でいきてきました。
しかし、開発環境でDockerを使うようになると、本番環境でサイトごとに別の環境を構築して公開するのが億劫になってきました。そこで最近ではやっとサイトをDocker上で運営するようになりました。
ここで問題になってくるのが複数のサイトをどうやって公開すればいいのか問題になります。そこで今回はサイト達のコンテナの前段としてNGINXで構築したリバースプロキシを通すことでマルチサイトを構築する方法をまとめようと思います。
構成
設定項目を書く前に今回の構成を書いておこうと思います。
基本的にはリバースプロキシを構成する1つのDocker(docker-compose構成)とその後ろに紐づくサイト群(それぞれdocker-compose構成)という形です。
外とつながる部分はNGINXのコンテナが担い、設定したドメインごとに後ろに紐づくWebサイトのコンテナに割り振ります。加えてLet’s Encryptのコンテナを使用することで自動的にHTTPSのアクセスに対応します。
今回はこの構成をVPS(ConoHa)上のDockerで構築していきます。ちなみに、ConoHa上でのDocker構築は以前記事にしましたので、まだ構築していない方は参考にしていただけると幸いです (ConoHaのDockerイメージを使ってもらっていいと思います)。
NGINXの設定
では早速NGINXの準備をしていきます。とはいえ、今回はDocker上で構築していくので下記のファイルを作成しコンテナを実行するだけで作業完了です。
version: '3'
services:
nginx-proxy:
build: jwilder/nginx-proxy
restart: on-failure
labels:
- com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy=jwilder/nginx-proxy
ports:
- 80:80
- 443:443
volumes:
- proxy:/etc/nginx/vhost.d
- proxy:/usr/share/nginx/html
- /var/run/docker.sock:/tmp/docker.sock:ro
- ./certs:/etc/nginx/certs:ro
network_mode: bridge
letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion
restart: on-failure
depends_on:
- nginx-proxy
volumes:
- proxy:/etc/nginx/vhost.d
- proxy:/usr/share/nginx/html
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/etc/nginx/certs:rw
network_mode: bridge
volumes:
proxy:
作用祭な説明は省略しますが…上記の設定でリバースプロキシの設定とLet’s Encryptでの証明書取得も自動で行ってくれます。なのであとはサイトを構築しているコンテナに「どのドメインで使用するか」の設定を追加して終わりです。
設置するサイトの設定
次にNGINXで割り振るサイト側のコンテナに設定を追加していきます。とはいえ設定する項目はかなりシンプルです。
version: '3'
services:
...
environment:
VIRTUAL_HOST: [サイトのドメイン]
LETSENCRYPT_HOST: [サイトのドメイン]
LETSENCRYPT_EMAIL: [Let's Encryptに使うメアド]
network_mode: bridge
...
上記の例のようにenvironment
とnetwork_mode
に項目を追加します。この設定を追加し、NGINXのコンテナを起動したあとにこのコンテナを実行すると、あとはサイトを表示するための設定と証明書の取得/設置まで自動的にやってくれます。
注意点/参考
証明書取得
初めNGINXの設定をミスしていて、コンテナの起動停止を繰り返してしまいました。すると、しばらくして証明書が正常に取得できなくなってしまいました。
原因はコンテナのエラーログを見れば一発でしたが、Let’s Encryptのリミットに到達していました。どうやらコンテナの起動時にドメインに対応する証明書を取得していて、試行錯誤中にそれを繰り返していたことでリミットに到達してしまったようです。
一度リミットにかかってしまうと、解除されるまで待つしかありません (ドメインやIPアドレスを変えるという荒業はありますが…)。というわけで私も解除されるまで1週間立ち往生しました。
NGINXコンテナの試行錯誤をする際はLet’s Encryptのコンテナは実行しないようにするといいと思います。
NGINX設定の追加
基本的にはイメージからコンテナを実行すればいい感じに動作してくれますが、サイトにより追加の設定が必要になると思います。
そんなときは設定ファイルに追加しておいてあげれば大丈夫です。私のサイトでもアップロード容量を多くしたかったので、下記のような設定を追加しています。
FROM jwilder/nginx-proxy
RUN { \
echo 'client_max_body_size 500m;'; \
} > /etc/nginx/conf.d/my_proxy.conf
あとはdocker-compose.yml
でのイメージ読み先をこのDockerfile
にしてあげれば勝手に設定が読み込まれます。ちなみに上記の設定ではNGINXで割り振る全サイトに設定されます。個別のサイトに設定したい場合はmy_proxy.conf
の代わりに{設定したいドメイン}.conf
のファイル名で作成してあげれば大丈夫とのことです。
ここらへんの説明は使用しているイメージのGitに書かれていそうです。
コンテナの名前解決をしてくれない
上記で載せているcomposeファイルはデフォルトのnetwork_mode: bridge
を使用しているため、名前解決の機能がありません。そのためコンテナ名を使用してのアクセスができます。
そこで、自分でネットワークを作成するパターンも載せておきます。まずは共通で使用するネットワークを作成します。
$ docker network create --driver bridge common_link
次にproxy用と各サイト用両方のdocker-compose.yml
の最下部に下記のネットワーク設定を追加します。
networks:
default:
external:
name: common_link
あとは通常通りに起動してみればコンテナ名で名前解決してくれるようになります。
サイトごとにBasic認証をかけたい
開発中のサイトやツール用で使用する場合はBASIC認証をかけたい場合があると思います。そんなときもパスワードファイルを作成するだけなのでとても簡単です。
まずはdocker-compose.yml
に下記の設定を追加します。ホスト側のディレクトリはどこでもいいのですが、コンテナ側のディレクトリは/etc/nginx/htpasswd
に設定してください。
services:
nginx-proxy:
......
volumes:
......
- ./htpasswd:/etc/nginx/htpasswd # ← この行を追加
設定ができたらこのディレクトリにBASIC認証用のID/PWファイルを設置します。このときのファイル名はBASIC認証をかけたいドメイン名と同じにします。つまり本サイトに認証をかけるのであればkoneta.click
になり、サブドメインがある場合もそのままsubdomain.koneta.click
で作成します。作成には下記コマンドが使用できます(オンラインの生成サービスで問題ないです)。
$ htpasswd -c /path/to/htpasswd [BASIC認証 ID]
New password: [BASIC認証 PASSWORD]
Re-type new password: [もう一度]
最後にコンテナを再起動すれば作業完了です。
終わりに
というわけで簡単に書いてきました。なんと言ってもNGINXのDockerコンテナを実行するだけであとは「良しなに」やってくれるのでとてつもなく簡単でした。血反吐を吐きながらApacheの設定ファイルをこねくり回して壊しまくっていたあの頃が懐かしいです。
まぁコンテナまるごとデプロイできる環境が普通になりつつあるので、この記事のないようも古い感じもしますがそこはおいといて色々やっていきたいと思います。