虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

開発時に使えるDocker Composeテクニック

こんにちは、虎の穴ラボのはっとりです。

Docker Compose使ってますか?
開発環境をサクッと作ることが出来るDocker Composeですが、
今回は、個人的に便利だなーと思っている設定・機能をご紹介します。
皆さん、いくつご存知でしょうか。

目次

環境変数編

基本のやり方

アプリケーションの設定を環境変数で管理することも多いと思います。
Docker Composeで環境変数を扱うにはいくつかの方法があります。

  1. environment に値ごと直接書く
  2. environmentには環境変数名のみ書き、値は.envかホストPC側の環境変数に定義する
  3. env_file で指定したファイルに書く

1は最もシンプルでわかりやすいです。

# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    environment:
      - DATABASE_URL=mysql2://user:pass@database:3306
  database:
    # ....

2は機密情報や開発者間で異なる環境変数の値を設定する必要がある場合に使います。
※ そのため、.envはソース管理されないように除外設定しておいたほうがいいです。

# .env
API_TOKEN=toranoana
# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    environment:
      - API_TOKEN

docker-compose.yml内ではホストPCの環境変数による埋め込みが可能です。
ホストPC側に環境変数を定義しなくても、代わりに.envに定義してもdocker-composeコマンド実行時に環境変数として扱ってくれます。
environment に値なしで追加した場合、ホストマシン側の環境変数か.envで定義した環境変数が使われます。

ホストマシン側の環境変数と.envに定義した環境変数だと、前者の方が優先されます。
下記のように、API_TOKEN=toranoana2を直接与えた場合は.envの値よりも優先されます。

API_TOKEN=toranoana2 docker-compose up

3の方法だと環境変数を別ファイル管理できます。2との違いこちらはコンテナ内の環境変数にしか使えません。

# app.env
SECRET=toranoana-lab
# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    env_file:
      - ./app.env

https://matsuand.github.io/docs.docker.jp.onthefly/compose/environment-variables/

応用

環境変数が多くなると全部をファイルに記述するのは大変です。
特に指定がない場合はデフォルト値を使えるようにしておくと楽です。

docker-compose.ymlはシェルの変数展開が使えるので
下記のようにデフォルト値を持ちつつ .env の値で上書きするということもできます。
※ こちらの方法では env_fileで指定したファイル は使えません。

# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    environment:
      - API_URL=${API_URL:-http://example.com/api/v1/example}

変数展開を使えば環境変数に関わらず他の設定もデフォルト値を持ちつつ、.envファイルで上書きできます。

# .env
PORT=3001
# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    # PORT無指定時は 3000 ポートで起動し .envに指定があれば そのポートで起動する
    ports:
      - "${PORT:-3000}:3000"

ネットワーク編

docker-compose.yml に定義したコンテナ同士は service名をホスト名として使用できます。(v1では要links設定)

# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    ports:
      - "${PORT:-3000}:3000"
    # my-database をホスト名として使用している
    environment:
      - DATABASE_URL=mysql2://user:pass@my-database:3306
      - RAILS_ENV
  my-database:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    ports:
      - "${MYSQL_PORT:-3306}:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_USER=user
      - MYSQL_PASSWORD=pass

異なるdocker-compose.ymlで起動しているコンテナ同士で接続

異なるdocker-compose.ymlで定義したコンテナ同士でも同じ様にservice名をホスト名として使用する方法があります。

同じ名前のnetworkに所属させることにより、サービス名をホスト名とした通信が可能になります。

この場合、どちらかのnetworkにはexternal: trueをつけて、作成済みのnetworkに所属させるようにします。

# docker-compose.yml
version: '3'

services:
  api-app:
    build: ./
    networks:
      - default
      - my-shared-network
    ports:
      # ホストマシンへは8081でlistenさせている
      - '8081:8080'

networks:
  my-shared-network:
    name: my-shared-network
# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    environment:
      # コンテナ同士で接続しているので コンテナ内部のポートであることに注意
      - API_URL=http://api-app:8080/api/v1/example
    networks:
      - default
      - my-shared-network

networks:
  my-shared-network:
    name: my-shared-network
    external: true

external: true がついていない方を先に起動する必要があります。

network設定無しで接続する方法 ※Docker DeskTop限定

コンテナ内からlocalhostを使うと自身のコンテナを指してしまいますが、
host.docker.internalというホスト名を使うとホストマシンに向いてくれます。
これを使うと別のコンテナやホストマシンで起動しているアプリにも接続が可能になります。
※ただし、host.docker.internalはDocker DeskTopでしか使えません。

networkの設定がいらないので設定もシンプルになりやすいです。
ただし、host.docker.internalはホストマシンのホスト名なので、
接続するコンテナのlistenしているポートに合わせる必要はあります。

# docker-compose.yml
version: '3'

services:
  api-app:
    build: ./
    ports:
      # ホストマシンへは8081でlistenさせている
      - '8081:8080'
# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    environment:
      # ホストマシンへ接続しているので コンテナ内部からのポートでないことに注意
      - API_URL=http://host.docker.internal:8081/api/v1/example

ボリューム編

Docker for Macを使っていて、どうしても読み込みが遅いなと感じることが多々ありましたが、下記の記事で紹介されているボリュームマウントのチューニングを試したところ劇的に速くなりました。

https://qiita.com/ysKey2/items/346c429ac8dfa0aed892 https://docs.docker.com/docker-for-mac/

:delegated を付けるだけです。
コンテナ -> ホスト の変更に遅延が発生することを許容することでパフォーマンスが向上する仕組みのようです。
そのため、コンテナ内でコードを自動生成・修正する場合は注意が必要です。

# docker-compose.yml
version: '3'

services:
  api-app:
    build: ./
    volumes:
      - ./:/var/www/app:delegated

その他

command に tail -f /dev/null を指定してコンテナを起動しっぱなしにする。

Dockerコンテナはcommandに指定したプロセスが終了するとコンテナが停止します。
command に tail -f /dev/null  を指定すると停止せずに起動しっぱなしになってくれます。
※もちろんコンテナにtailがインストールされている必要はあります。

# docker-compose.yml
version: '3'

services:
  rails-app:
    build: ./
    command: tail -f /dev/null

docker-compose up -d でコンテナを起動しておいて docker-compose exec (サービス名) sh または docker-compose exec (サービス名) bash(※alpineの場合はbashの代わりにash)でコンテナ内に入れます。

設定ファイルを分割して定義する

docker-composeコマンドでは compose ファイル を指定することができます。
複数のcompose ファイルがマージされた状態で扱われます。
同じキーの場合はより後のファイルの設定で上書きされます。

docker-compose -f docker-compose1.yml -f docker-compose2.yml -f docker-compose3.yml up -d

https://docs.docker.jp/compose/reference/overview.html

コマンドの-fオプションを複数つけることで指定できますが、ホストマシンの環境変数や.envファイルでも指定することができます。
環境変数の場合は:で区切ります。(※Windowsで使う場合は : ではなく ; のようです)

COMPOSE_FILE=docker-compose1.yml:docker-compose2.yml:docker-compose3.yml docker-compose up -d

または

# .envには下記のように定義されている
# COMPOSE_FILE=docker-compose1.yml:docker-compose2.yml:docker-compose3.yml
docker-compose up -d

まとめ

「知らないのがいくつかあった」、「全部知ってたよ」いろいろあると思います。
Docker Composeの全ドキュメントを読んだわけではないですし、新しいバージョンで新しい機能もどんどん追加されているのできっとこの他にも私が知らない便利機能があるかしれません。
これは便利だなと思うものがあったら教えて頂けると嬉しいです!

P.S.

■採用情報
yumenosora.co.jp

■ToraLab.fmスタートしました!
メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。

anchor.fm

■Twitterもフォローしてくださいね!
ツイッターでも随時情報発信をしています
twitter.com