Edited at

【Docker】複数のコンテナ(サービス)を1サーバーで起動させてサブドメインで80ポートにアクセスする


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を設定することでサブドメインに応じてアクセスできるようになります。

使い方はこんな感じ。


docker-compose.yml

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-proxyservice-aservice-bを記載していますが、別々のyamlファイルに宣言することも可能です。ただし、nginx-proxyservice-aservice-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


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


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.environmentVIRTUAL_HOST

ここがRocket.Chat用のURLを宣言する箇所です。今回はhttp://rocketchat.localhostを指定します。


services.app.networks

appコンテナはnginx-proxyと同一ネットワークに存在する必要があるので、proxy-networkを指定しています。proxy-networknetworks.proxy-network.namenginx-proxyで作成したproxy_networkと同じであると定義しています。

また、appコンテナはrocketchat-networkにも所属するようにしています。これは後ろの方で宣言しているmongoコンテナと接続するためのネットワークです。mongoコンテナはnginx-proxyと直接接続する必要がないので敢えてネットワークを切り離しています。


wekan/docker-compose.yml


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.localhosthttp://wekan.localhostにアクセスしてみましょう!


あとがき

実際にドメインを取得してマネージドクラウドなんかにアプリをたてる場合も、VIRTUAL_HOSTの設定をそのサブドメインに宣言するだけでいける!!


References