Depends_on、Volumes、Init Docker-composeオプション - Docker Composeのための実践演習その3

このチュートリアルでは、**Alibaba Cloud**上でコンテナを扱う際に**Docker Compose**を使用して実践的な経験を積むことに焦点を当てています。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/practical-exercises-for-docker-compose-part-3_594415)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

#depends_on
depends_onはサービス間の依存関係を宣言します。

depends_on docker-compose upでは、依存関係のある順にサービスを起動します。

依存関係にあるサービスは、メインサービスが起動する前に最初に起動します。

見ての通り、依存関係はランダムな順番で開始されます。また、docker-compose はメインサービスを起動する前に依存関係の実行を待つことはありません。

これを実際に見てみましょう。

docker-compose.yml に以下を追加します。

```
nano docker-compose.yml
```

```
version: "3.7"

services:

alpine:

image: alpine:3.8

command: sleep 600

depends_on:

- service-2

- service-3

- service-4

container_name: main-container

service-2:

image: alpine:3.8

container_name: service-2

command: sleep 600

service-3:

image: alpine:3.8

container_name: service-3

command: sleep 600

service-4:

image: alpine:3.8

container_name: service-4

command: sleep 600
```

依存関係はきちんと番号順に宣言されていることに注意してください。

このdocker-composeファイルを以下のようにして起動してみましょう。

```
docker-compose up -d -t 0
```

期待される出力 .

```
Creating service-3 ... done

Creating service-4 ... done

Creating service-2 ... done

Recreating compose-tuts_alpine_1 ... done
```

依存関係がランダムな順番で始まっていることに注意してください。メインサービスは最後に開始されますが、上記の出力には明確には表示されていません。

docker-compose ファイルに小さな編集を加えて (スリープ時間の編集など)、docker-compose をやり直すと、これらのサービスが毎回異なるシーケンスで作成されていることがわかります。試してみてください。

イベントのシーケンスをより明確に見るために、すべてのサービスをシャットダウンしてdocker-compose upをやり直してみましょう。

```
docker-compose down -t 0
```

期待される出力 .

```
Stopping main-container ... done

Stopping service-4  ... done

Stopping service-3  ... done

Stopping service-2  ... done

Removing main-container ... done

Removing service-4  ... done

Removing service-3  ... done

Removing service-2  … done
```
ネットワークcompose-tuts_defaultの削除
メインコンテナが最初に停止し、次に依存関係が停止することに注意してください。

ここで実行します。

```
docker-compose up -d -t 0
```

期待される出力 .

```
Creating network "compose-tuts_default" with the default driver

Creating service-3 ... done

Creating service-2 ... done

Creating service-4 ... done

Creating main-container ... done
```

最後に作成されたメインコンテナが明確に表示されます。

メインコンテナが起動する前に依存サービスをREADYにしておく必要がある場合は、 [https://docs.docker.com/compose/startup-order/](https://docs.docker.com/compose/startup-order/?spm=a2c65.11461447.0.0.2c582941CHdash) を勉強する必要があります。

#volumes
volumesは、名前付きvolumesのマウントに使用されます。

これを面白くするために、先ほどの例の4つのサービスを再利用します。

4 つのサービスはすべて demo-data-volume という名前付きvolumesを使用します。これはまだ存在しません。

docker-compose.ymlに以下を追加します。

```
nano docker-compose.yml
```

```
version: "3.7"

services:

alpine:

image: alpine:3.8

command: sleep 600

depends_on:

- service-2

- service-3

- service-4

container_name: main-container

service-2:

image: alpine:3.8

container_name: service-2

command: sleep 600

volumes:

- demo-data-volume:/root/dir1/dir2

service-3:

image: alpine:3.8

container_name: service-3

command: sleep 600

volumes:

- demo-data-volume:/root/dira/dirb

service-4:

image: alpine:3.8

container_name: service-4

command: sleep 600

volumes:

- demo-data-volume:/root/1/2/3/4

volumes:

demo-data-volume:
```

一番下の右側：これがトップレベルのボリュームキーです（Dockerのドキュメントでは名前が付けられています）。

トップレベルのキーはdocker-composeファイルの左端から始まります。

トップレベルのキーはdemo-data-volumeを定義しています。

実行時には

```
docker-compose up -d -t 0
```

**demo-data-volumeが作成されます(存在しない場合)。**

期待される出力は以下の通りです。

```
Creating network "compose-tuts_default" with the default driver

Creating volume "compose-tuts_demo-data-volume" with default driver

Creating service-2 ... done

Creating service-4 ... done

Creating service-3 ... done

Creating main-container ... done
```

2行目の注意点 compose-tuts_demo-data-volume "をデフォルトドライバで作成します。

実行します。

```
docker volume ls
```

期待される出力 .

```
DRIVER  VOLUME NAME

local  compose-tuts_demo-data-volume
```

これが名前付きvolumesです。この名前は、カレントディレクトリと docker-compose ファイルで指定したボリューム名を連結したものです。

docker-compose ファイルでは、この名前付きボリュームを 3 つのサービスの異なるマウントポイントで使用するように定義しました。

```
service-2:

volumes:

- demo-data-volume:/root/dir1/dir2

service-3:

volumes:

- demo-data-volume:/root/dira/dirb

service-4:

volumes:

- demo-data-volume:/root/1/2/3/4
```

ここでは、これら3つのサービスがすべて同じ名前のvolumesにアクセスできることを確認します。

service-2 を入力して、名前付きvolumesで作業してみましょう。

```
docker exec -it service-2 /bin/sh
```

ls はパスが存在することを示し、echo は myoutfile を作成します。そして終了します。

```
/ # ls /root/dir1

dir2

/ # echo from service 2 > /root/dir1/dir2/myoutfile

/ # exit
```

service-3に入って、名前のついたvolumesを調べてみましょう。

```
docker exec -it service-3 /bin/sh
```

ls はファイルが存在することを確認します。

```
/ # ls /root/dira/dirb

myoutfile

/ # cat /root/dira/dirb/myoutfile

from service 2

/ # exit
```

service-3では、volumesは/root/dira/dirbにマウントされていることに注意してください。

```
service-3:

volumes:

- demo-data-volume:/root/dira/dirb
```

サービス4では、volumesは/root/1/2/3/4にマウントされていることに注意してください。

```
service-4:

volumes:

- demo-data-volume:/root/1/2/3/4
```

ファイルがそこで利用可能であることを確認するために、service-4を入力します。

```
docker exec -it service-4 /bin/sh
```

期待される出力 .

```
/ # cat /root/1/2/3/4/myoutfile

from service 2

/ # exit
```

私たちの名前のついたボリュームは、この3つのコンテナの中で読み書きすることができます。これらのコンテナを停止しても存在し続けます。

他のコンテナ、サービス、スワームはこの名前付きボリュームの使用を許可されています。この名前付きボリュームは、これら3つのサービスとは特にリンクしていません。

この数分間で、あなたはおそらくDockerボリュームの機能の5%未満を探索しました。

[https://docs.docker.com/engine/extend/legacy_plugins/#volume-plugins](https://docs.docker.com/engine/extend/legacy_plugins/?spm=a2c65.11461447.0.0.2c582941nhxa8s#volume-plugins) には25以上のvolumesプラグインがリストアップされています。

公式ドキュメントは [https://docs.docker.com/storage/volumes/](https://docs.docker.com/storage/volumes/?spm=a2c65.11461447.0.0.2c582941pJ2JVj) と [https://docs.docker.com/storage/](https://docs.docker.com/storage/?spm=a2c65.11461447.0.0.2c582941YYW1dk) で読むことができます。

少なくともこれで、名前付きvolumesを実際に使ってみて、その仕組みをある程度理解したことになるでしょう。

幸いなことに、名前付きvolumesは最も理解しやすく、最も柔軟性があり、最も頻繁に使用されています。

#restart
restartはdocker-compose upを使ってサービスを起動する際に適用されます。

わかりやすい4つのオプションがあります。

1、restart:"no" ... デフォルト値
2、restart: always
3、restart: on-failure
4、restart：unless-stopped
それらを試してみましょう。

**restartのためのデモ:"no" ... the default value
以下を docker-compose.yml に追加します。

```
nano docker-compose.yml
```

```
version: "3.7"

services:

alpine:

image: alpine:3.8

command: sleep 1
```

サービスを起動します。

```
docker-compose up -d -t 0
```
その状態を確認します。

```
docker ps -a
```

sleep 1 は、コンテナをわずか 1 秒後に終了させます。

docker ps -a を何度も実行しても、終了したコンテナは 1 つしか表示されません。予想通り: restart = no はコンテナを再起動しません。

**restart のデモ: unless-stop**
を使用して、以下を docker-compose.yml に追加します。

```
nano docker-compose.yml
```

```
version: "3.7"

services:

alpine:

image: alpine:3.8

command: sleep 1

restart: unless-stopped
```

前のコンテナをプルーンして、クリーンなdocker ps -aで作業を行います。

```
docker container prune -f;docker ps -a
```

新しいコンテナを起動します。

```
docker-compose up -d -t 0
```

その状態を確認します。

```
docker ps -a
```

sleep 1 は、コンテナをわずか 1 秒後に終了させます。

このコマンドを繰り返し実行すると、コンテナが継続的に再起動していることがわかります。

docker container stop your-container-id を実行してもコンテナは停止しません。

docker-compose down は数秒以内にコンテナを削除します。

docker-compose up を使ってコンテナを起動した場合は、docker-compose down を使ってコンテナを削除します。

restart: unless-stopped は期待通りに動作します。

**restartのデモ: always**
を使用して、以下を docker-compose.yml に追加します。

```
nano docker-compose.yml
```

```
version: "3.7"

services:

alpine:

image: alpine:3.8

command: sleep 1

restart: always
```

新しいコンテナを起動します。

```
docker-compose up -d -t 0
```

その状態を確認します。

```
docker ps -a
```

sleep 1 は、コンテナをわずか 1 秒後に終了させます。

このコマンドを繰り返し実行すると、コンテナは継続的に再起動します。

```
docker container stop your-container-id does not stop it.

docker-compose down removes the container within seconds.
```

docker-compose up を使ってコンテナを起動した場合は、docker-compose down を使ってコンテナをダウンさせます。

restart:alwaysは終了コードがゼロ ( 成功 ) で終了したコンテナを常に再起動します。

Commandを置き換えると

```
command: sleep 1; exit 1
```

restart: always は 0 以外の ( 失敗した ) 終了コードで終了するコンテナを再起動することをテストできます。

**restart: on-failure のデモ**
新しいソフトウェアを学ぶには、ドキュメントを読んで、それを使うことが必要です。

経験値ゼロを意味します。

実験は経験を意味します。

ですから、上記で経験したことに基づいて、再起動のためのテストを設計して実行してください:**on-failure**

失敗した場合のコンテナの再起動をテストしなければなりません。

また、正常に終了した場合に再起動するかどうかもテストしなければなりません ( exit return code 0 )。

上記のテキストに基づいて、これは最大で5分かかるはずです。

#init: true の未設定
[https://docs.docker.com/compose/compose-file/#init](https://docs.docker.com/compose/compose-file/?spm=a2c65.11461447.0.0.2c582941CgvJvM#init) より

>コンテナ内で init を実行して、シグナルを転送したり、プロセスを再生したりします。デフォルトの init を使用するには、init を true に設定します。

コンテナ内のプロセスをクリーンにシャットダウンさせたい場合は、このオプションをtrueに設定することが重要です。

**docker container stop**を実行した場合、コンテナ内のプロセスはコンテナに転送された正しいシグナルを受信しなければなりません。そうすれば、アプリケーションのシャットダウンサブルーチンは正しくシグナルを受けてクリーンにシャットダウンされます。

コンテナ内で複数のプログラムやスレッドを実行している場合、それらは何らかの全体的な管理者プロセスによって正しく管理されたSTOPシグナルを受信しなければなりません。INIT はそのプロセスです。

以下の説明を実際の例で見てみましょう。

Init は最初に SIGTERM (シグナル 15) を送信し、プロセスがそのシグナルをキャッチしてクリーンシャットダウンルーチンを実行できるようにします。SIGTERM は - シャットダウンしなければならないが、最初にクリーンシャットダウンルーチンを実行することを意味します。

```
stop_grace_period = 10 seconds default.
```

stop_grace_period は、SIGKILL (シグナル9) を送信する前に、コンテナがクリーンに終了するのを10秒間待ちます。SIGKILL は直ちにプロセスを終了させます - ファイルが適切に閉じられていない - シャットダウンルーチンは実行されていません。SIGKILL は、ある瞬間にはプロセスが動いていることを意味し、次の瞬間には頭が吹っ飛びます。

init: true を指定した場合のみ、この重要なシグナルがコンテナ内に適切に伝わります。

まとめ : データの破損を防ぎたい場合は init: true を指定してください。

重要: このオプションはバージョン3.7のファイルフォーマットで追加されました。docker-compose.yml の最初の行は version. “3.7”

まず、init設定をしないとSTOPの扱いが悪くなることを確認します。

次に、init: trueを指定して違いを観察することで正しく処理します。

docker-compose.ymlに以下のように追加します。

```
nano docker-compose.yml
```

```
version: "3.7"

services:

alpine:

image: alpine:3.8
```

新しいコンテナを起動します。

```
docker-compose up -d -t 0
```

コンテナの状態を確認し、コンテナIDを取得します。

```
docker ps -a

docker exec -it your-container-id /bin/sh
```

それを入力してpsを実行します。

```
/ # ps

PID  USER  TIME  COMMAND

1 root  0:00 sleep 600

11 root  0:00 /bin/sh

16 root  0:00 ps

/ # exit
```

PIDプロセス1がinitされていないことに注意してください。

新しいシェルコンソールを開き、docker eventsを実行します。

元のシェルに戻って

```
docker container stop your-container-id
```

と表示されると、停止するのに10秒かかることがわかります。

重要: docker イベントのコンソール出力を見てください。

期待される出力 .

```
2018-11-12T09:51:46.112995194+02:00 container kill b676e3bf51f9f1f99edc531ffa04b0ab164834cb218fe9b78abb65b396782d6a (com.docker.compose.config-hash=c19f3ca3bb22826fee98596d3cb40c4f403f8a51cede518945f5ef37bb989589, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, image=alpine:3.8, name=compose-tuts_alpine_1, signal=15)

2018-11-12T09:51:56.134707392+02:00 container kill b676e3bf51f9f1f99edc531ffa04b0ab164834cb218fe9b78abb65b396782d6a (com.docker.compose.config-hash=c19f3ca3bb22826fee98596d3cb40c4f403f8a51cede518945f5ef37bb989589, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, image=alpine:3.8, name=compose-tuts_alpine_1, signal=9)

2018-11-12T09:51:56.307015745+02:00 container die b676e3bf51f9f1f99edc531ffa04b0ab164834cb218fe9b78abb65b396782d6a (com.docker.compose.config-hash=c19f3ca3bb22826fee98596d3cb40c4f403f8a51cede518945f5ef37bb989589, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, exitCode=137, image=alpine:3.8, name=compose-tuts_alpine_1)

2018-11-12T09:51:56.387969293+02:00 network disconnect 9802e2506ea80e7597f0b018450f86fa0c7dcc045a920702cc0e463aacfda84f (container=b676e3bf51f9f1f99edc531ffa04b0ab164834cb218fe9b78abb65b396782d6a, name=compose-tuts_default, type=bridge)

2018-11-12T09:51:56.454351535+02:00 container stop b676e3bf51f9f1f99edc531ffa04b0ab164834cb218fe9b78abb65b396782d6a (com.docker.compose.config-hash=c19f3ca3bb22826fee98596d3cb40c4f403f8a51cede518945f5ef37bb989589, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, image=alpine:3.8, name=compose-tuts_alpine_1)
```

最初のイベントはコンテナのSIGTERM - その行の最後にsignal15のテキストを右に送信します。

Dockerはコンテナがまだ適切にシャットダウンされていないと判断します。コンテナはsignal15を無視します。

2行目。KILLシグナルイベントは10秒後に発生します - その行の最後にsignal 9のテキストが表示されます。

Dockerはsignal 9 = KILLでコンテナーを強制終了します。

まとめ: initもなく、秩序あるシャットダウンもできず、killを使わなければなりませんでした。

#init: true
では、initがどのように停止を適切に処理するかを観察してみましょう。

次のようにして、docker-compose.ymlに以下を追加します。

```
nano docker-compose.yml
version: "3.7"

services:

alpine:

image: alpine:3.8

command: sleep 600

init: true
```

init: trueで新しいコンテナを起動します。

```
docker-compose up -d -t 0
```

状態を確認してコンテナIDを取得します。

```
docker ps -a

docker exec -it your-container-id /bin/sh
```

それを入力してpsを実行します。

```
/ # ps

PID  USER  TIME  COMMAND

1 root  0:00 /dev/init -- sleep 600

6 root  0:00 sleep 600

7 root  0:00 /bin/sh

12 root  0:00 ps

/ # exit
```

PID 1 は /dev/init を実行していることに注意してください。initはPID 1を実行しています：コンテナ内のすべてのプロセスへのシグナル送信を管理するための唯一の完璧な場所です。

docker eventsコンソールでCTRL-cを押して、イベントのリストが中断されるようにします。

このコンソールで再び docker events を実行します - 今度は INIT で管理されているシャットダウンイベントを観察します。

元のシェルに戻り、docker ps -a でコンテナ ID を取得します。

今実行した場合

```
docker container stop your-container-id
```

と表示されると、非常に早く停止することがわかります。

重要: docker イベントのコンソール出力を見てください。

期待される出力 .

```
2018-11-12T09:50:14.892684432+02:00 container kill 7973ccec4849cf8759821d613e1b4870c68d76f396fefcfb1cd1bf1f7a3bd509 (com.docker.compose.config-hash=8d5184879de73d9f624319983822eafca40959cbe9f91929ff4648ace18acc6a, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, image=alpine:3.8, name=compose-tuts_alpine_1, signal=15)

2018-11-12T09:50:15.019039627+02:00 container die 7973ccec4849cf8759821d613e1b4870c68d76f396fefcfb1cd1bf1f7a3bd509 (com.docker.compose.config-hash=8d5184879de73d9f624319983822eafca40959cbe9f91929ff4648ace18acc6a, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, exitCode=143, image=alpine:3.8, name=compose-tuts_alpine_1)

2018-11-12T09:50:15.104203647+02:00 network disconnect 9802e2506ea80e7597f0b018450f86fa0c7dcc045a920702cc0e463aacfda84f (container=7973ccec4849cf8759821d613e1b4870c68d76f396fefcfb1cd1bf1f7a3bd509, name=compose-tuts_default, type=bridge)

2018-11-12T09:50:15.149018229+02:00 container stop 7973ccec4849cf8759821d613e1b4870c68d76f396fefcfb1cd1bf1f7a3bd509 (com.docker.compose.config-hash=8d5184879de73d9f624319983822eafca40959cbe9f91929ff4648ace18acc6a, com.docker.compose.container-number=1, com.docker.compose.oneoff=False, com.docker.compose.project=compose-tuts, com.docker.compose.service=alpine, com.docker.compose.version=1.22.0, image=alpine:3.8, name=compose-tuts_alpine_1)
```

最初のイベントはコンテナのSIGTERM - その行の最後にsignal15のテキストを送信します。

Dockerはコンテナが適切にシャットダウンされていると判断します。コンテナはsignal15を適切に処理する。( コンテナの処理はsignal15をキャッチし、クリーンシャットダウンルーチンを呼び出します。)

コンテナのダイイベントは100ミリ秒後に発生します。

NO signal 9 = KILLが送信されました。

まとめ: init、高速な秩序あるシャットダウン、KILLは使用されません。

要するに、init: trueを使用することを強くお勧めします。これは簡単で高速で、破損したデータの手間を省くことができます。

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*