Runner in the High

技術のことをかくこころみ

docker-composeを使う際のprofiles, networks, volumes

go-cleanarchitectureにdocker-composeを導入した過程で学んだprofiles, volumes, networksの話。

https://github.com/IzumiSy/go-cleanarchitecture/blob/master/docker-compose.ymlgithub.com

profiles

ざっくり言うと、サービスをグループ化して部分的に起動できる。

r7kamura.com

そもそも、開発中のアプリケーションという"コロコロと中身の変化するもの"をDockerイメージにするのはあまりいいやり方ではない。少しでもコードが変更されるたびにdocker buildが必要で効率が悪い。なので、開発環境としてdocker-composeを使う場合にはミドルウェアだけをservicesとしてまとめて起動したほうがいい。

ただ、いかんともしがたい理由で開発中のアプリケーションもdocker-composeのサービスとして含めたいこともある。つまり、ローカルでアプリケーションを起動したいこともあればdocker-composeのイメージとしてアプリケーションを起動したいこともある、という場合にはprofilesが非常に便利。毎回、利用するprofileを指定する手間は若干あるが、逆に言えばそれだけでいい。

volumes

DockerでMySQLなどに対してボリュームを使いたい場合にはvolumesセクションで driver: local なボリュームをあらかじめ作っておくといい。

# 以下で作ったボリュームは "mysqlvolume:/var/lib/mysql" みたいな感じで使える。

volumes:
  mysqlvolume:
    driver: local
  redisvolume:
    driver: local

ネットに転がっている記事では "./tmp/data/db:/var/lib/mysql" のような記述でパス指定したボリュームをサービスに対して与えているものがあるが、この指定では ./tmp ディレクトリのownerがrootになってしまう。すると、全サービスをdocker-composeで再起動したときに permission denied なエラーがでて困る。

この権限周りとボリュームの話はよくあるエラーのようで、docker-composeを実行するときにユーザーを明示的に指定するなどの解決策があるらしい。が、最も簡単な解決策は明示的にホスト側のパスを指定せずボリュームを作ることだと思う。

元ネタとなる回答は以下のstackoverflowにある。

stackoverflow.com

networks

docker-composeはデフォルトでそれっぽいネットワーク名をつけてくれるが、明示的にネットワーク名を指定すると便利なこともある。

# 以下で作ったネットワークは各サービスで app-network として指定すれば使える

networks:
  default:
    external:
      name: bridge
  app-network:
    name: go-cleanarchitecture-network
    driver: bridge

たとえば自分のアプリケーションではマイグレーションにflywayを使っていて、ローカルにインストールせずDockerイメージとして起動するようにしている。この場合docker-composeで起動しているサービスに対してflywayのイメージが繋ぎにいくことになるわけだが、起動しているDockerコンテナ間で接続を行う際にはネットワーク名を指定しないといけない。

他にも、APIサーバに対するインテグレーションテストでdreddを利用していて、こちらも同様にDockerイメージとして利用している。

test/integration:
  docker run --net=go-cleanarchitecture-network --rm -it -v "$$(pwd):/app" -w /app apiaryio/dredd dredd \
      api-description.apib http://app:8080 --hookfiles=./dredd_hook.js

db/migrate:
  docker run --net=go-cleanarchitecture-network --rm -v "$$(pwd)/schemas/sql:/flyway/sql" -v "$$(pwd)/config:/flyway/config" \
      flyway/flyway -configFiles=/flyway/config/flyway.conf -locations=filesystem:/flyway/sql migrate

db/clean:
  docker run --net=go-cleanarchitecture-network --rm -v "$$(pwd)/schemas/sql:/flyway/sql" -v "$$(pwd)/config:/flyway/config" \
      flyway/flyway -configFiles=/flyway/config/flyway.conf -locations=filesystem:/flyway/sql clean

docker-composeでグルーピングされたサービスとしては扱いたくないが局所的にテストやマイグレーションなどで使用する別のDocker-basedなツールがある、というケースであればネットワーク指定はあったほうが便利だと思われる。docker-composeで自動で付与されるネットワーク名を予想してもいいが、自分で指定したほうが確実。