Background
1サーバーに複数のコンテナ(サービス)を綺麗に乗せられるDockerってすごい便利です。こんなことがしたい。

四角で囲まれている部分が1サーバー、例えばlocalhostだったりAWSやGCPの1インスタンスだったりということです。
service-a.hoge.comにアクセスするとcontainer Aのサービスを利用できて、service-b.hoge.comにアクセスするとcontainer Bのサービスが利用できるという世界。
nginx-proxy
GitHub - jwilder/nginx-proxy: Automated nginx proxy for Docker containers using docker-gen
こちらのDocker imageを使わせてもらいます。
上の図でいうところのcontainer A、container BにそれぞれVIRTUAL_HOSTを設定することでサブドメインに応じてアクセスできるようになります。
使い方はこんな感じ。
services:
nginx-proxy:
image: jwilder/nginx-proxy
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
service-a:
image: xxxxxxxxxx
environment:
- VIRTUAL_HOST=service-a.hoge.com
service-b:
image: xxxxxxxxxx
environment:
- VIRTUAL_HOST=service-b.hoge.com
基本の型はこんな感じです。
実際はサービスごとにdocker-compose.ymlつくりますよね。
例えば、「Rocket.Chat」と「Wekan」をlocalhostで展開することを考えます。それぞれのサービスのdocker-compose.yml自体が複雑になったりもするので、それらを一つのyamlファイルとして管理するのは抵抗がありました。上のyamlファイルの例では1つのyamlファイルにnginx-proxy、service-a、service-bを記載していますが、別々のyamlファイルに宣言することも可能です。ただし、nginx-proxyとservice-a、service-bが同じネットワークに存在していないと互いに通信ができないのでその設定をしてあげることが注意点です。
ちょうど、「Rocket.Chat」と「Wekan」を例に出したので、これをサンプルにhttp://rocketchat.localhostに接続したら「Rocket.Chat」、http://wekan.localhostに接続したら「Wekan」にアクセスできるようにしてみます。
Directory structure
/
├ nginx-proxy
| └ docker-compose.yml
├ rocketchat
| └ docker-compose.yml
└ wekan
└ docker-compose.yml
nginx-proxy/docker-compose.yml
version: "3.5"
services:
nginx-proxy:
container_name: nginx-proxy
image: jwilder/nginx-proxy
ports:
- 80:80
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- proxy-network
networks:
proxy-network:
name: proxy_network
最初の例と大きく違うところはnetworksを指定したところです。ちょっと上の方で注意点としてあげましたが、この後に宣言する「Rocket.Chat」と「Wekan」のアプリケーションのコンテナとこの「nginx-proxy」のコンテナは同じネットワークに存在しなければなりません。同じネットワークに存在させるためにproxy_networkという名前のネットワークに所属することを宣言しています。
ちなみにnetworksの方で宣言しているnameというプロパティですが、versionが3.xと宣言されていないと使えないらしいので、今回の例ではversionを3.5としています。
rocketchat/docker-compose.yml
version: "3.5"
services:
app:
container_name: rocketchat_app
image: rocketchat/rocket.chat:latest
command: bash -c 'for i in `seq 1 30`; do node main.js && s=$$? && break || s=$$?; echo "Tried $$i times. Waiting 5 sec..."; sleep 5; done; (exit $$s)'
restart: unless-stopped
volumes:
- ./uploads:/app/uploads
environment:
- PORT=3000
- ROOT_URL=http://rocketchat.localhost
- MONGO_URL=mongodb://mongo:27017/rocketchat
- MONGO_OPLOG_URL=mongodb://mongo:27017/local
- VIRTUAL_HOST=rocketchat.localhost
depends_on:
- mongo
networks:
- proxy-network
- rocketchat-network
mongo:
container_name: rocketchat_db
image: mongo:4.0
restart: unless-stopped
volumes:
- ./data/db:/data/db
- ./data/dump:/dump
command: mongod --smallfiles --oplogSize 128 --replSet rs0 --storageEngine=mmapv1
networks:
- rocketchat-network
mongo-init-replica:
image: mongo:4.0
command: 'bash -c "for i in `seq 1 30`; do mongo mongo/rocketchat --eval \"rs.initiate({ _id: ''rs0'', members: [ { _id: 0, host: ''localhost:27017'' } ]})\" && s=$$? && break || s=$$?; echo \"Tried $$i times. Waiting 5 secs...\"; sleep 5; done; (exit $$s)"'
depends_on:
- mongo
networks:
- rocketchat-network
networks:
proxy-network:
name: proxy_network
rocketchat-network:
name: rocketchat_network
Rocket.Chatを起動させるための基本的なdocker-compose.ymlについては説明を省略します。(「大人の事情でSlackが使えないのでRocket.ChatをBasic認証ありでdocker-composeで起動した - Qiita」で書いてますのでよろしければ参考に〜)
ここで肝になる部分をいくつかご紹介。
services.app.environmentのVIRTUAL_HOST
ここがRocket.Chat用のURLを宣言する箇所です。今回はhttp://rocketchat.localhostを指定します。
services.app.networks
appコンテナはnginx-proxyと同一ネットワークに存在する必要があるので、proxy-networkを指定しています。proxy-networkはnetworks.proxy-network.nameでnginx-proxyで作成したproxy_networkと同じであると定義しています。
また、appコンテナはrocketchat-networkにも所属するようにしています。これは後ろの方で宣言しているmongoコンテナと接続するためのネットワークです。mongoコンテナはnginx-proxyと直接接続する必要がないので敢えてネットワークを切り離しています。
wekan/docker-compose.yml
version: "3.5"
services:
app:
container_name: wekan_app
image: wekanteam/wekan:latest
links:
- mongo
environment:
- MONGO_URL=mongodb://wekandb/wekan
- ROOT_URL=http://wekan.localhost
- VIRTUAL_HOST=wekan.localhost
depends_on:
- mongo
networks:
- proxy-network
- wekan-network
mongo:
container_name: wekan_db
image: mongo:3.2.14
volumes:
- ./data:/data/db
networks:
- wekan-network
networks:
proxy-network:
name: proxy_network
wekan-network:
name: wekan_network
注目ポイントはRocket.Chatの宣言と同じなので省略。
Check
全てのdocker-compose.ymlを起動してみて、サブドメインでアクセスするアプリケーションが制御されているか確認してみましょう!
nginx-proxy/、rocketchat/、wekan/のそれぞれのディレクトリで$ docker-compose up -dコマンドを実行し、各コンテナが完全に立ち上がったらhttp://rocketchat.localhostとhttp://wekan.localhostにアクセスしてみましょう!
あとがき
実際にドメインを取得してマネージドクラウドなんかにアプリをたてる場合も、VIRTUAL_HOSTの設定をそのサブドメインに宣言するだけでいける!!