DockerComposeの設定ファイルのフォーマットをVersion1からVersion2に変更した際に、いくつかはまったところがあったので残しておきます。間違ってる点や、もっといい方法があれば教えてもらえると助かります。
システム構成
今回移行したシステムは、Railsアプリ2つとリバースプロキシ1つを持つ構成になっています。Railsアプリはどちらも同じイメージをベースとしており、設定だけが少し異なっています。実際はもうちょっとコンテナは多いですが、あまり関係ないので省略してます。
また、移行前後でDocker Engine, Docker Composeのバージョンアップも行っています。
移行前 | 移行後 | |
---|---|---|
Docker Engine | 1.10 | 1.12 |
Docker Compose | 1.6 | 1.8 |
移行前の設定
移行前の各アプリケーションのdocker-compose.ymlは以下のようになってます。imageは公式のものではなく独自に作っているものなので名前は適当に変えてます。
リバースプロキシとRailsアプリは通信できるようにしていますが、Docker Composeのexternal_linksは使ってません。これを使うと、リバースプロキシ配下のアプリを停止した時に、リバースプロキシまで停止してしまい、他のアプリへの接続ができなくなってしまったためです。1年くらい前に構築した時の情報なので、今は違うかもしれません。。。
- リバースプロキシ(一部抜粋)
nginx:
image: hoge/nginx:latest
ports:
- "80:80"
- Railsアプリ1(一部抜粋)
redis:
image: hoge/redis:latest
rails:
image: hoge/rails:latest
links:
- redis:redis
- Railsアプリ2(一部抜粋)
redis:
image: hoge/redis:latest
rails:
image: hoge/rails:latest
links:
- redis:redis
移行後の設定(失敗1回目)
まずは、単純に書式を変更してみました。
- リバースプロキシ(一部抜粋)
version: '2'
services:
nginx:
image: hoge/nginx:latest
ports:
- "80:80"
- Railsアプリ1(一部抜粋)
version: '2'
services:
redis:
image: hoge/rails:latest
rails:
image: hoge/rails:latest
depends_on:
- redis
- Railsアプリ2(一部抜粋)
version: '2'
services:
redis:
image: hoge/redis:latest
rails:
image: hoge/rails:latest
depends_on:
- redis
単純に書式を変更しただけだと、リバースプロキシアプリからRailsアプリのコンテナに接続できなくなりました。Version1の時はこの設定で全てのコンテナが同じサブネットに属していたので接続できましたが、上記の設定だとそれぞれのアプリが別のネットワークに属するようになったことが原因です。そこで、全てのアプリが同じネットワークに属するように専用のネットワークを設定しました。
移行後の設定(失敗2回目)
- リバースプロキシ(一部抜粋)
version: '2'
services:
nginx:
image: hoge/nginx:latest
ports:
- "80:80"
networks:
- proxy_network
networks:
proxy_network:
external: true
- Railsアプリ1(一部抜粋)
version: '2'
services:
redis:
image: hoge/redis:latest
networks:
- proxy_network
rails:
image: hoge/rails:latest
depends_on:
- redis
networks:
- proxy_network
networks:
proxy_network:
external: true
- Railsアプリ2(一部抜粋)
version: '2'
services:
redis:
image: hoge/redis:latest
networks:
- proxy_network
rails:
image: hoge/rails:latest
depends_on:
- redis
networks:
- proxy_network
networks:
proxy_network:
external: true
アプリケーション共通のネットワークとしてproxy_network
を定義しました。ネットワークの作成は次のコマンドを実行しました。
$ docker network create --driver bridge proxy_network
共通のネットワークに属することで、リバースプロキシから各アプリケーションに接続できるようになりました。
新しい問題の発生
これで解決と思いましたが、今度はどちらか一方のRailsアプリがredisに接続ができなくなる現象が発生しました。Rails1アプリのredisコンテナのIPアドレスが172.21.0.2
となっているのに、Rails1アプリのrailsコンテナからredisにpingを打つと172.21.0.5
に向けて打っていました。この172.21.0.5は何かを調べるとRails2アプリのredisコンテナ
でした。Railsアプリではredisコンテナに接続するための接続先ホスト名を、DockerComposeで設定したサービス名redis
としていましたが、すべてのアプリケーションを共通ネットワークに属するようにしたことで、サービス名redis
がRails1,Rails2の2つのアプリに含まれることになり、サービス名が重複したことで、コンテナ内蔵DNSが異なるIPアドレスを返すようになっていたことが原因のようです。この現象の調査では、Railsアプリを一つずつ動かすと正常に接続ができたため、原因を見つけるまでに結構時間がかかってしまいました。
そこで、この問題を解決するために2つの方法を考えました。
- サービス名が重複しないように変更する
- 外部アプリケーションからアクセス不要なコンテナは共通ネットワークではない別のネットワークに属するようにする
アプリケーションが増えるたびに新しいネットワークを追加するのは構成が複雑になるかな、と思いましたので今回は1つ目の方法で対応することにしました。今考えると、ネットワークを分けた方が素直で楽だったかもしれません。
移行後の設定(失敗3回目)
version: '2'
services:
nginx:
image: hoge/nginx:latest
ports:
- "80:80"
networks:
- proxy_network
networks:
proxy_network:
external: true
- Railsアプリ1(一部抜粋)
version: '2'
services:
rails1_redis:
image: hoge/redis:latest
networks:
- proxy_network
rails1:
image: hoge/rails:latest
depends_on:
- rails1_redis
networks:
- proxy_network
networks:
proxy_network:
external: true
- Railsアプリ2(一部抜粋)
version: '2'
services:
rails2_redis:
image: hoge/redis:latest
networks:
- proxy_network
rails2:
image: hoge/rails:latest
depends_on:
- rails2_redis
networks:
- proxy_network
networks:
proxy_network:
external: true
これでrails1コンテナからrails1_redisコンテナ、rails2コンテナからrails2_redisコンテナのIPアドレスを正しく認識できるようになりました。
問題発生、再び
今度こそ解決だ、と思ったのですが今度はRailsアプリケーションでrails1_redisにアクセスしようとするとInvalidURIError
が発生するようになりました。どうやらホスト名に_
が含まれることが原因のようです。そこで、サービス名の_
を-
に変更しました。DockerComposeとは直接関係ない問題ですが、サービス名の変更によるトラブルなので、一緒に書いておきます。
移行後の設定(最終形)
version: '2'
services:
nginx:
image: hoge/nginx:latest
ports:
- "80:80"
networks:
- proxy_network
networks:
proxy_network:
external: true
- Railsアプリ1(一部抜粋)
version: '2'
services:
rails1-redis:
image: hoge/redis:latest
networks:
- proxy_network
rails1:
image: hoge/rails:latest
depends_on:
- rails1-redis
networks:
- proxy_network
networks:
proxy_network:
external: true
- Railsアプリ2(一部抜粋)
version: '2'
services:
rails2-redis:
image: hoge/redis:latest
networks:
- proxy_network
rails2:
image: hoge/rails:latest
depends_on:
- rails2-redis
networks:
- proxy_network
networks:
proxy_network:
external: true
これでやっと全てが正常に動くようになりました。
DockerCompose便利なんですけど、更新スピードが早くてついていくのが大変ですね。こまめにチェックしておかないといけないですね。