”Docker Compose、チョットわかる”になるために知っておきたい頻出オプション - Compose file version 3(3.8) reference -

# 要旨

- dockerを使ってRailsやLaravelといったFWを使用した開発環境構築をいくつかやってみた
- その中で頻出だったオプションについて、公式ドキュメントレベルの理解をまとめてみた

# この記事の内容を知ると

- なんとなく書いていた内容の理解が深まった
- より読みやすく、変更しやすい`docker-compose.yml`が書ける気がした
- 他人が書いた`docker-compose.yml`を見て、"さっぱりわからん" -> "少し読める"に貢献できるかもしれません

**保守のモチベーションになりますので改善コメント、LGTMお願いいたします**



# Reference

Docker公式ドキュメント
[Compose file version 3 reference | Docker Documentation](https://docs.docker.com/compose/compose-file/)

# Compose file format

2020.9.4現在 version 3.8が最新です
（Docker Engine 19.03.0+ 対応）

```yml
version: "3.8"
```

最新は以下で確認ください
[Compose file versions and upgrading | Docker Documentation](https://docs.docker.com/compose/compose-file/compose-versioning/)

# Services

dockerイメージを実行する単位がservice
Composeではserviceごとにオプションを指定し、複数のserviceを管理実行できる

# Dockerイメージのビルドに関わるオプション

## build

### context

Dockerfileのpathを指定する

```yml
services:
  webapp:
    build: ./dir
```

以下は上記に同じ
buildするソースをpathで指定、URLやgitレポジトリも使えるらしい

> Either a path to a directory containing a Dockerfile, or a url to a git repository.
> [Compose file version 3 reference | Docker Documentation](https://docs.docker.com/compose/compose-file/#build)

```yml
services:
  webapp:
    build:
      context: ./dir
```

### dockerfile

Dockerfile以外の任意のファイル、ここでは`./dir/Dockerfile-alternate`でbuildする場合

```yml
services:
  webapp:
    build:
      context: ./dir
      dockerfile: Dockerfile-alternate
```

### args

**buildプロセスでのみ使用可能な**環境変数を定義する
imageのバージョンなどを`docker-compose.yml`に記述できると見通しが良くなりそう

`docker-compose.yml`で定義して

```yml
build:
  context: .
  args:
    - RUBY_VERSION="2.7.1-alpine"
```

`Dockerfile`で呼び出す

```dockerfile
ARG RUBY_VERSION
FROM ruby:$RUBY_VERSION
```



## image

dockerイメージからコンテナを起動する
image name, tag, image ID , リポジトリからイメージを指定できる

**`build`と`image`を併記した場合**

`image`単独で使用する場合と挙動が異なる
`./dir`のsource (Dockerfile)からbuildし、buildされたimageは`webapp`という名前になり、
`tag`でタグ付けされる

```yml
build: ./dir
image: webapp:tag
```

別のサービスでこのイメージを再利用する場合に
Dockerfileから再ビルドする時間が短縮できて便利そう

```yml
services:
  app:
    build: ./dir
    image: app
    ...
  webpacker:
    image: app # ここではbuildされたイメージを再利用
```



# コンテナの起動やプロセスの実行に関わるオプション

## command

dockerfileで指定したdefaultコマンドをoverrideする

```
command: bundle exec thin -p 3000
```

dockerfileのようなformatでも書ける
以下は上記と同じ

```
command: ["bundle", "exec", "thin", "-p", "3000"]
```



## depends_on

- `docker-compose up`の際に`depends_on`に基づいた順でサービスを**スタート**させる(db -> app)
- `docker-compose up SERVICE`とした場合、`depends_on`で指定したサービスも自動的に含まれる(`docker-compose up app`としたときに, "db"も起動する)
- `docker-compose stop`とした場合、依存関係に基づいてサービスを終了する(app -> db)

```yml
services:
  app:
    build: .
    depends_on:
      - db
  db:
    image: postgres
```

**注意**

- depends_onはサービスの開始順を定義しているだけで、サービスが利用可能になるのを待っているわけではない。これには別の配慮が必要。
  [Control startup and shutdown order in Compose | Docker Documentation](https://docs.docker.com/compose/startup-order/)

- `deploying a stack in swarm mode`では`depends_on`の内容が無視される（deploying a stack in swarm modeについては勉強不足）

  [deploying a stack in swarm mode](https://docs.docker.com/engine/reference/commandline/stack_deploy/)

## stdin_open, tty



```yml
stdin_open: true
tty: true
```



# ボリュームとマウントに関わるオプション

## tmpfs

 コンテナ内に` temporary file system`をマウントする

- hostのメモリ上にマウントされ高速可が期待できる
- コンテナをstopするとこのマウントは開放される
  [Use tmpfs mounts | Docker Documentation](https://docs.docker.com/storage/tmpfs/)
- `deploying a stack in swarm mode`ではこのオプションは無視される

```yml
tmpfs: /tmp
```

tmpfsのサイズ上限を指定できる（単位: byte）

```yml
- type: tmpfs
  target: /app
  tmpfs:
    size: 1000
```



## volumes

Hostのパスを指定しマウントする（コンテナから扱えるようにする）

### volumesの概要

以下少し自身の解釈を書きます

- コンテナ内のデータはコンテナの停止とともに消失してしまう
- コンテナ内のデータに対して、hostや、他のコンテナはアクセスすることができない

これを解消するためにvolumeでマウントする工程が必要

volumesマウントは三種類

- `/var/lib/postgresql/data`パスだけ書いてある場合、
  コンテナ内のパスを示しており、指定されたパスをボリュームとしてマウントしデータを永続化する。
- `host_path: container_path`の形式で書いてある場合、
  host_pathをボリュームとしてマウントしコンテナ内で扱えるようにする
- volume_name: container_pathの形式で書いてある場合、
  コンテナ内のpathを名前付きボリュームとしてマウントする。名前付きボリュームは他のコンテナと共有してアクセスすることができる。

（自己解釈おわり）



### Top-level optionとしての`volumes`と、Servicesのsuboptionとしての`volumes`

- 各サービスのsub-optionsとしてパスを指定する場合、当該のサービスのみに定義される
- その場合、top-levelのvolumes keyで再度指定する必要はない
- ただし、複数のサービスでボリュームを再利用したい場合は、top-levelのvolumes keyで名前付きvolumeを使う

ちょっとわかりにくいが

**そのサービスでしかそのボリュームを使わない場合**

- `services`以下のオプションとして書く
- path or named volumeのいずれで書いても良い
- named volumeで書くならtop-level = servicesと同列のvolumes: keyにも記述が必要

**他のサービスでもそのボリュームを使う場合**

- top-level = servicesと同列のvolumes: keyにnamed volumeで書こう

```yml
version: "3.8"
services:
  web:
    image: nginx:alpine
    #services のサブオプションとしてのvolumes key　（新書式 Added in version 3.2 file format.）
    volumes:
      - type: volume
        source: mydata
        target: /data
        volume:
          nocopy: true

  db:
    image: postgres:latest
    volumes:
    #pathで書くならtop-levelのvolumes:に書かなくて良い (旧書式)
      - "/var/run/postgres/postgres.sock:/var/run/postgres/postgres.sock"
    #"dbdata"はdbしか使っていないが、named volumeはtop-levelのvolumes:に書く
      - "dbdata:/var/lib/postgresql/data"

volumes: #top-levelのvolumes
  mydata:
  dbdata:
```



### SHORT SYNTAX（従来の書式）

- [SOURCE:]TARGET[:MODE]で記述
- 相対パス、絶対パスでどちらも指定可
- 相対パスは`docker-compose.yml`に対しての相対パスで指定

```yml
volumes:
  # Just specify a path and let the Engine create a volume
  - /var/lib/mysql

  # Specify an absolute path mapping
  - /opt/data:/var/lib/mysql

  # Path on the host, relative to the Compose file
  - ./cache:/tmp/cache

  # User-relative path
  - ~/configs:/etc/configs/:ro

  # Named volume
  - datavolume:/var/lib/mysql

```



### LONG SYNTAX（Added in [version 3.2](https://docs.docker.com/compose/compose-file/compose-versioning/#version-32) file format.）

```
version: "3.8"
services:
  web:
    image: nginx:alpine
    volumes:
      - type: volume
        source: mydata
        target: /data
        volume:
          nocopy: true

volumes:
  mydata:
```

- `type`: マウントするタイプ `volume`, `bind`, `tmpfs` or `npipe`
- `source`: ソースとなるhost側のpath, もしくはnamed volume, tmpfs mountには適応できない
- `target`: コンテナ側のパス、どこにマウントするのか
- `volume: nocopy`volumeを作成するときにコンテナ側からデータをコピーすることを許可しない（ちょっと使い所がわからない）
- `tmpfs: size`: tmpfs mountのサイズを指定 (bytes)
- `consistency`: 後述



### consistency

host - container間の同期性を犠牲にして、ボリュームマウントのパフォーマンスを向上させる
（Docker Desktop for Macでのみ適応される）

- `consistent`: defaultではこれ。host - container間は完全に一致。
- `cached`: host側の変更をコンテナから見たときに同期に遅延が生じうる
- `delegated`: コンテナ側の変更をhostから見て同期に遅延が生じうる

cachedはよく使っていますが、いまのところ問題になるような同期のズレはない

```yml
version: "3.8"
services:
  php:
    image: php:7.1-fpm
    volumes:
      - .:/var/www/project:cached
```



# ネットワークに関わるオプション

## expose

サービス間でのポート開放を定義、host - コンテナ間ではない

```yml
expose:
  - "3000"
  - "8000"
```



## ports

host - container間でポートマッピングする

- `HOST:CONTAINER`の書式で書く
- もしくはコンテナ内のポートだけ記述する（この場合exposeと同じ？コンテナの外からアクセスできないことは確認しました）
- HOST側のポートは60より小さくできない

```yml
ports:
  - "3000"
  - "8000:8000"
  - "127.0.0.1:80:80"
```



# 環境変数に関わるオプション

## env_file

- fileから環境変数を追加する
- `docker-compose -f FILE`で`env_file`を指定することも可能
- `env file`の中身は`VAR=VAL`(変数名=値)で記述

```
env_file: .env
```

複数ファイルも可
呼び出し順に注意、変数名が重複している場合最後に呼び出されたものを採用

```
env_file:
  - ./common.env
  - ./apps/web.env

```



## environment

`docker-compose.yml`に直接環境変数を定義できる

```yml
environment:
  RACK_ENV: development
  SHOW: 'true'
  SESSION_SECRET:
```

以下上記に同じ

```yml
environment:
  - RACK_ENV=development
  - SHOW=true
  - SESSION_SECRET
```

`SESSION_SECRET:`のようにkeyのみが指定されている場合、host側の環境変数を値として採用する

> Environment variables with only a key are resolved to their values on the machine Compose is running on, which can be helpful for secret or host-specific values.[Compose file version 3 reference | Docker Documentation](https://docs.docker.com/compose/compose-file/#environment)

**Note**
ここで定義する環境変数はbuild中は使用できない。buildプロセスで環境変数を使用したい場合は上述の"arg"オプションを使用する





## 環境変数について補足

composeではdocker-composeが動いているシェルの環境変数が使える
`POSTGRES_VERSION=9.3`が定義されているならば
以下のように呼び出せる

 書式は`$VARIABLE` と `${VARIABLE}` どちらでも可

```yml
db:
  image: "postgres:${POSTGRES_VERSION}"
```

この例でPOSTGRES_VERSIONが定義されていない場合
${POSTGRES_VERSION}は空、Copmoseから見ると`image: "postgres:"`になるので注意

`.env`に記述することで環境変数のデフォルト値を指定する事ができる
Composeは自動的に`.env`を参照し、シェルに環境変数が定義される場合はそれが優先される。

インラインでデフォルト値を指定することも可能（added in 2.1 file format）

- `${VARIABLE:-default}`: `VARIABLE`が未定義、もしくは空ならば `default`を採用
- `${VARIABLE-default}`:  `VARIABLE`が未定義ならば `default`を採用

Similarly, the following syntax allows you to specify mandatory variables:

- `${VARIABLE:?err}`:  `VARIABLE`が未定義、もしくは空ならば `err`メッセージを伴って終了
- `${VARIABLE?err}`:  `VARIABLE`が未定義ならば `err`メッセージを伴って終了