suin.io

docker-compose.ymlが環境別に複数ある場合はCOMPOSE_FILEを定義しておくと幸せになれる

suin2016年7月26日

Docker Composeは複数のコンテナからなるサービスの構成方法をYAMLで定義しておくと、構築や実行といった管理が自動化されて便利なツールですが、難点がひとつあります。

Composeファイルが複数あるとたちまち不便に…

僕が携わっている現場ではRailsやPHPアプリのサービス構築をdocker-composeでやっていますが、開発者個人の開発環境(development)、みんなの成果物を統合してみるステージング環境(staging)、そしてユーザにサービス提供するためのプロダクション環境(production)の3つの環境があり、それぞれにdocker-compose.ymlがあります。

同じdocker-compose.ymlで全ての環境を網羅できればいいのですが、開発環境はDockerコンテナのMySQLやRedisを使うが、プロダクション環境ではAWSのRDSやElastiCacheを使うといったように、環境によって構成がバラバラだったりします。そのため、各環境用にComposeファイルを作る必要があり、結果的に次のように3つのファイルができています。

  • docker-compose.development.yml ... 開発環境用の構成
  • docker-compose.staging.yml ... ステージング環境用の構成
  • docker-compose.production.yml ... プロダクション環境用の構成

こうなってくると、docker-composeコマンドを実行するときにYAMLファイルを自動で認識してくれなくなり、毎度毎度 docker-compose -f docker-compose.development.yml up -d のようにファイル名を-fオプションで指定しないとならなくなりました。

docker-composeコマンドをよく使うのは開発環境だったので、開発環境用のYAMLだけdocker-compose.ymlとしてみたこともありましたが、オペミスが増えたのでやめました。ついついステージング環境などで-f docker-compose.staging.ymlを付け忘れて開発環境が起動してしまう、といった問題があったのです。なので、あえて開発環境でも-fを指定するようにしましたが、これもこれで面倒だなと思います。

COMPOSE_FILEを指定すると-fオプション地獄から開放される

Dockerのドキュメントを精読してみると、docker-composeのデフォルトComposeファイルを指定できる環境変数にCOMPOSE_FILEがあるのを見つけました。

COMPOSE_FILE
Specify the path to a Compose file. If not provided, Compose looks for a file named docker-compose.yml in the current directory and then each parent directory in succession until a file by that name is found.

CLI Environment Variables

「Composeファイルのパスを指定します。もし指定されていなければ、Composeはdocker-compose.ymlという名前のファイルをカレントディレクトから親ディレクトリまで探していきます」ということなので、COMPOSE_FILEをセットしておくことでdocker-compose.yml以外のファイルを-fオプションなしに読み込んでくれるようになります。

COMPOSE_FILEがドキュメントどおりに使えるか実際に試してみようと思います。僕のdocker-composeのバージョンは1.8.0-rc2でした。

$ docker-compose version
docker-compose version 1.8.0-rc2, build c72c966
docker-py version: 1.9.0-rc2
CPython version: 2.7.9
OpenSSL version: OpenSSL 1.0.2h  3 May 2016

試しに2種類のComposeファイルを作ります。

$ tree
.
├── docker-compose.development.yml
└── docker-compose.production.yml
docker-compose.development.yml
# 開発環境用Composeファイル
app:
  image: alpine
  environment:
    APP_ENV: development
docker-compose.production.yml
# プロダクション用Composeファイル
app:
  image: alpine
  environment:
    APP_ENV: production

この状態でdocker-compose configしてみます。やはり、Composeファイルを見つけられません。

$ docker-compose config
ERROR:
        Can't find a suitable configuration file in this directory or any
        parent. Are you in the right directory?

        Supported filenames: docker-compose.yml, docker-compose.yaml

環境変数にCOMPOSE_FILEをセットしてみます。

$ export COMPOSE_FILE=docker-compose.development.yml
$ echo $COMPOSE_FILE
docker-compose.development.yml

docker-compose configを実行すると開発環境用のComposeファイルが読み込まれるようになっているのが分かります。

$ docker-compose config
networks: {}
services:
  app:
    environment:
      APP_ENV: development
    image: alpine
    network_mode: bridge
version: '2.0'
volumes: {}

今のところCOMPOSE_FILEの定義はプロジェクトごとがいい?

COMPOSE_FILEをセットすれば-fオプションから開放されることは分かったものの、更に課題が見つかりました。それは、Composeファイルの候補は1つしか指定できないという点です

Dockerのドキュメントを見るとCOMPOSE_FILE=docker-compose.yaml:docker-compose.prod.ymlのように:で区切ると複数読み込まれるような記載があって期待しましたが、これは「コロンで区切ったうち、どれかが見つかれば」という意味ではなく、「コロンで区切った全てのComposeファイルを読み込む」という意味合いになるようです。:がパスセパレータなので"OR"の意味かなと思いましたが、"AND"の意味だそうです。COMPOSE_FILE:が"OR"の意味であれば、.bashrcなどに書いておこうかなと思いましたが、これではそうもいきませんね…^^;

この解決策はベストなものが見つかりませんでしたが、僕が携わっているプロジェクトでは誰でも開発環境をコマンド1つで起動できるよう、開発環境の起動をシェルスクリプトとtmuxinatorで自動化しています。なので今回はtmuxinator.ymlに書いておく方法で対処することにしました。

tmuxinator.yml
name: app
root: .

pre_window: >
  eval $(dinghy env)
  && export COMPOSE_FILE=docker-compose.development.yml

windows:
  - ? git:
    - git fetch --prune
    - git branch -a
    - git status -s

  - ? rails-console:
    - docker-compose exec rails /app/bin/rails console

  - ? docker-log:
    - docker-compose logs -f --tail=50

参加しているプロジェクトすべてがdocker-compose.development.ymlを使っていれば、.bashrcにexport COMPOSE_FILE=...を書くだけですが、プロジェクトによって違ってくる場合は、このようにCOMPOSE_FILEの定義はプロジェクトごとにしておくのが良さそうですね。

まとめ

環境ごとにComposeファイルがある場合、環境ごとにComposeファイルを固定できるCOMPOSE_FILEという環境変数について紹介しました。複数のComposeファイル候補を設定することはできませんが、-fオプション地獄からは開放されるためにはまあまあいい解決策なのではないでしょうか。

RELATED POSTS