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
の設定をそのサブドメインに宣言するだけでいける!!