重要なDocker-composeの設定 - Docker Composeのための実践的な演習その2

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

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

#stop_grace_period（猶予期間の終了）
[https://docs.docker.com/compose/compose-file/#stop_grace_period](https://docs.docker.com/compose/compose-file/?spm=a2c65.11461447.0.0.126a2d0c3oRKsd#stop_grace_period) より

>デフォルトでは、DockerはSIGKILLを送信する前にコンテナが終了するまで10秒待ちます。

>SIGKILLを送信する前に、コンテナがSIGTERM(またはstop_signalで指定した停止信号)を処理しない場合に、コンテナを停止しようとしたときにどのくらい待つかを指定します。

これまで私の docker-compose ファイルにはこの設定 ( stop_grace_period ) がありませんでした。そのため、以下のように -t 0 を指定する必要がありました。

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

-t 0 は、docker-compose がコンテナを終了させる前に 0 秒待機しなければならないことを指定します。タイムアウトのデフォルト値は10秒です。つまり、docker-composeを起動するたびに、コンテナが最終的に終了して再び起動されるまで10秒待たなければならないということです。

`docker-compose`ファイルのstop_grace_periodでは、タイムアウト=0の値を指定することができます。

を使用して、以下を docker-compose.yml に追加します。

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

```
version: "3.7"
services:
  alpine:

    image: alpine:3.8

    command: sleep 600

    stop_grace_period: 0s
```

実行します。

```
docker-compose up -d 
```

スリープタイムアウトの値を少し変更して、実行

```
docker-compose up -d
```

スリープタイムアウトの値を少し変更して、再度実行

```
docker-compose up -d
```

毎回ほぼ瞬時に再作成されるのを見てください。

stop_grace_periodをデフォルトの10秒に変更します。

```
docker-compose up -d 
```

スリープタイムアウトの値を少し変更して実行

```
docker-compose up -d
```

スリープタイムアウトの値を少し変更して、再度実行

```
docker-compose up -d
```

レクリエーションに毎回10秒かかっている様子を見てみましょう。

**docker events**の出力を見てみると詳細がわかると思います。

**stop_grace_period. 0s**

```
2018-11-05T14:46:34.968709389+02:00 container kill  .... lots of information ...  signal=15
2018-11-05T14:46:34.984262101+02:00 container kill .... lots of information ...  signal=9
```

**stop_grace_period. 10s**

```
2018-11-05T14:47:49.486907072+02:00 container kill .... lots of information ... signal=15
2018-11-05T14:47:59.510613956+02:00 container kill .... lots of information ... signal=9
```

signal＝15

SIGTERM は終了信号です。プロセスを終了させますが、クリーンアップルーチンを実行させます。

signal= 9

SIGKILLはキルシグナルです。Kill the process: 直ちに。プロセスはシグナルをキャッチして処理することができず、クリーンアップすることができません。

上で出力されたイベントに基づいて、stop_grace_period. 0sはSIGKILLに行く前にSIGTERMから0.02秒待ちます。

上記のイベント出力に基づいて、stop_grace_period. 10sはSIGKILLになる前にSIGTERMから10秒以上待ちます。

これらのチュートリアルの残りの部分については、すべての docker-compose ファイルに次のように記述されます: stop_grace_period. 0s

注: 本番環境の作業環境に適した stop_grace_period を決定する必要があります。これは、アプリによって異なります。

#sysctls

コンテナに設定するカーネルパラメータの設定にはsysctlsを使用します。

カーネルパラメータとは？

Linux ではカーネルパラメータを使ってリソースの制限を設定することができます。カーネルパラメータはrootを含むすべての人に適用されます。

公式のリファレンス情報は [https://www.kernel.org/doc/Documentation/sysctl/kernel.txt](https://www.kernel.org/doc/Documentation/sysctl/kernel.txt?spm=a2c65.11461447.0.0.126a2d0cy0eeRA&file=kernel.txt) で確認できます。

Linux シェルで **man sysctl** を実行すると、実行時にカーネルパラメータを設定する方法が詳しく書かれています。

コンテナを再び起動してみましょう。

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

を使ってコンテナを入力します。

```
docker exec -it compose-tuts_alpine_1 /bin/sh
```

表示されたプロンプトで以下の 3 つのコマンドを入力します。これら3つのカーネルパラメータの現在の実際の値が表示されます。

```
cat /proc/sys/net/core/somaxconn
cat /proc/sys/kernel/msgmax
cat /proc/sys/kernel/shmmax
```

期待される出力 .

```
/ # cat /proc/sys/net/core/somaxconn
128
/ # cat /proc/sys/kernel/msgmax
8192
/ # cat /proc/sys/kernel/shmmax
18446744073692774399
/ # exit
```

この3つの値を変更して、それらの値が適用されているかどうかを確認するためにコンテナを調査します。

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

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

その内容が、

```
version: "3.7"
services:
  alpine:

    image: alpine:3.8

    command: sleep 600

    sysctls:

      net.core.somaxconn: 512
      kernel.shmmax:  18102030100020003000
      kernel.msgmax:  4000
```

実行します。

```
docker-compose up -d -t 0 
docker exec -it compose-tuts_alpine_1 /bin/sh
```

表示されたプロンプトで以下の3つのコマンドを入力します。これら3つのカーネルパラメータの現在の新しい実際の値が表示されます。

```
cat /proc/sys/net/core/somaxconn
cat /proc/sys/kernel/msgmax
cat /proc/sys/kernel/shmmax
```

期待される出力 .

```
/ # cat /proc/sys/net/core/somaxconn
512
/ # cat /proc/sys/kernel/msgmax
4000
/ # cat /proc/sys/kernel/shmmax
18102030100020003000
/ # exit
```

見ての通り、これら3つのカーネルパラメータは全て変更されています。

Dockerでは、コンテナ単位でカーネルのパラメータを調整できることがわかりました。

[https://docs.docker.com/compose/compose-file/#sysctls](https://docs.docker.com/compose/compose-file/?spm=a2c65.11461447.0.0.126a2d0cPbvcea#sysctls) より

>このオプションは、(バージョン3の)Composeファイルを使用してスウォームモードでスタックをデプロイする場合には無視されます。

#namespace付きのsysctls
( 前節からの続きで、重要な見出しを追加しました )

[https://docs.docker.com/engine/reference/commandline/run/#configure-namespaced-kernel-parameters-sysctls-at-runtime](https://docs.docker.com/engine/reference/commandline/run/?spm=a2c65.11461447.0.0.126a2d0cvYOILG#configure-namespaced-kernel-parameters-sysctls-at-runtime) より

**重要：**

>すべての sysctls がネームスペースになっているわけではありません。
>Dockerはホストシステムを変更するコンテナ内の sysctlsの変更に対応していません。

>現在サポートされているSYSCTLS

>kernel.msgmax、kernel.msgmnb、kernel.msgmni、kernel.sem,
>kernel.shmall, kernel.shmmax, kernel.shmmni, >kernel.shm_rmid_forced

>fs.mqueue.* で始まる Sysctls

>net.* で始まる Sysctls

この重要な文章は [https://docs.docker.com/compose/compose-file/#sysctls](https://docs.docker.com/compose/compose-file/?spm=a2c65.11461447.0.0.126a2d0cwtrMxT#sysctls) のドキュメントにあるはずです。

fs.file-maxを変更しようとしたところ、このエラーが出ました。

```
ERROR: for compose-tuts_alpine_1  Cannot start service alpine: OCI runtime create failed: sysctl "fs.file-max" is not in a separate kernel namespace: unknown
```

これで理解できました: **fs.file-max** はネームスペースではありません。DockerはHOSTシステムを変更するコンテナ内のsysctlsの変更をサポートしていません。

**fs.file-max** の変更 (コンテナ内での) は、HOST サーバの設定を変更することになりますが、これは許可されていません。

ネームスペースは Linux 上のコンテナの基本的な側面です。[https://en.wikipedia.org/wiki/Linux_namespaces](https://en.wikipedia.org/wiki/Linux_namespaces?spm=a2c65.11461447.0.0.126a2d0coquXO8) を参照してください。

ネームスペースは、コンテナが孤立したバブル環境で存在することを可能にします。ネームスペースは、コンテナが完全なLinuxディストロであり、サーバ上で単独で動作していると思わせます。

**fs.file-max** は Linux の機能の一例ですが、現在のところコンテナ内ではネームスペースを分離することができません。

そのため、ネームスペースを持たないカーネルパラメータは HOST サーバ上で調整しなければなりません - その上で実行されるすべてのコンテナに適切なものになるようにしなければなりません。

#限界
Ulimit はシェルが利用できるリソース (サイズ、CPU 時間、優先度など) とシェルが起動するプロセスを制御します。

これを使うことで、バグのあるアプリケーションが過負荷をかけてサーバをクラッシュさせないようにすることができます。

Docker のコンテキストでは、コンテナ内で実行されるアプリケーションを制限するためにコンテナ内でこれを使うことができます。

シェルプロンプトで**man ulimit**を使って公式のドキュメントを読んでください。

ulimitがコンテナ内で実行されることを証明してみましょう。最大プロセス数と最大オープンファイル数を無茶苦茶に低く設定してコンテナを起動してみましょう。

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

```
nano docker-compose.yml
# add this content
```

```
version: "3.7"
services:
  alpine:

    image: alpine:3.8

    command: sleep 60171

    stop_grace_period: 0s

    ulimits:
     nproc: 2

     nofile:
      soft: 2
      hard: 4
```

コンテナを起動してみます。

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

期待される出力 .

```
Recreating compose-tuts_alpine_1 ... error

ERROR: for compose-tuts_alpine_1  Cannot start service alpine: OCI runtime create failed: container_linux.go:348: starting container process caused "open /proc/self/fd: too many open files": unknown
```

予想通り: 開いているファイルが多すぎてエラーになります。コンテナは小さいですが、起動するには4つ以上のファイルを開く必要があります。

nproc ( プロセス数を 1 に変更。両方のnofileの制限を40以上に変更。

再実行します。

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

これは完璧に起動します（私のCentOS 7サーバで）。

nproc を制限するにはカーネル 4.3 以上が必要です - 私のサーバのカーネルバージョンは 3.10.0-327.el7.x86_64 です。uname -r を実行してカーネルのバージョンを取得してください。

ulimit を設定してみましょう: fsize - 最大ファイルサイズ (KB)

を使用して、以下を docker-compose.yml に追加します。

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

```
version: "3.7"
services:
  alpine:

    image: alpine:3.8

    command: sleep 60171

    stop_grace_period: 0s

    ulimits:
     fsize: 10
```

実行します。

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

docker exec -it compose-tuts_alpine_1 /bin/sh
```

10MBのファイルを作成して、ファイルサイズの制限である10KBを超えてみましょう。

```
/ # dd if=/dev/zero of=/tmp/output.dat bs=1M count=10
```

コンテナが存在するだけで、シェルセッションがクラッシュしました。

別の時間に私はエラーメッセージを得ました。

```
dd if=/dev/zero of=/tmp/output.dat  bs=1M  count=10
File size limit exceeded (core dumped)
```

#Configs
Configs は、コンテナ内のアプリケーションに必要な設定ファイルを宣言します。通常 /etc や F/opt の中にあるような設定ファイルが必要です。

docker-compose secrets は秘密の情報を保存するためのものです。

まず、docker-compose.yml の中で参照できるように、2つの小さな設定ファイルを作成する必要があります。

まず最初の設定ファイルを作成します。

```
nano config_data

'# config data
```

次に、2つ目のconfigファイルを作成します。

```
nano my_second_config.config

'# my_second_config.config の内容
```

ここで、DockerがこのSECOND FILEのみをmy_second_configFという名前のコンフィグとして作成する必要があります。

```
docker config create my_second_config my_second_config.config
```

これら2つの異なるConfigは、Configsの2つの異なる使い方をデモします。

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

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

```
version: "3.7"
services:
  alpine:
    image: alpine:3.8
    command: sleep 600

    configs:
      - my_first_config
      - my_second_config

configs:

    my_first_config:
      file: ./config_data

    my_second_config:
      external: true
```

トップレベルの configs 宣言 (下の 5 行目) は、このスタック内のサービスに付与される 2 つの config を定義しています。

サービスレベルの config (7 行目から 10 行目あたり) は、コンテナに 2 つの config へのアクセスを許可します。

両方の設定宣言を使用しなければなりません。

my_second_configはexternal: trueと定義されていることに注意してください。これはDockerのconfigオブジェクトとして存在します。

**docker-compose up**は'configs'設定をサポートしていません。スウォームへのデプロイには**docker stack deploy**を使用する必要があります。

```
docker swarm init

docker stack deploy -c docker-compose.yml  mystack

docker ps -a
```

期待される出力 .

```
 CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
ab50c7daf979        alpine:3.8          "sleep 600"         14 seconds ago      Up 13 seconds                           mystack_alpine.1.jq3buvzkf2a3hpn7mwb0e43om
```

自動生成されたコンテナ名を取得するために docker ps を実行しなければなりません。

ランダムに生成されたコンテナ名が手に入ったので、それを exec で入力します: コンテナのランダムな配列は異なります。自分のコンテナ名を使って実行してください。

```
docker exec -it mystack_alpine.1.jq3buvzkf2a3hpn7mwb0e43om /bin/sh
```

期待される出力 .

```
/ # ls
bin               lib               my_second_config  sbin              usr
dev               media             proc              srv               var
etc               mnt               root              sys
home              my_first_config   run               tmp
/ # cat my_first_config
'# config data
/ # cat my_second_config
'# my_second_config.config contents
/ # exit
```

これらの設定は / ディレクトリにマウントされていることに注意してください。

Linux 管理者が config を見つけることができると思われるディレクトリにマウントしましょう。

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

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

```
version: "3.7"
services:
  alpine:
    image: alpine:3.8
    command: sleep 600

    configs:
      - source: my_first_config
        target: /etc/my_first_config

      - source: my_second_config
        target: /opt/my_second_config

configs:

  my_first_config:
    file: ./config_data

  my_second_config:
    external: true
```

実行します。

```
docker stack rm mystack
```

2つの異なるターゲットパスに注意してください。/etc と /opt です。

```
docker stack deploy -c docker-compose.yml mystack
```

期待される出力 .

```
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
01156eb13576        alpine:3.8          "sleep 600"         4 seconds ago       Up 2 seconds                            mystack_alpine.1.vg2m0ge161anuoz31c2mdgf1k
```

コンテナに入って、設定が要求されたディレクトリにあるかどうかを調べてみましょう。

```
docker exec -it mystack_alpine.1.vg2m0ge161anuoz31c2mdgf1k /bin/sh
```

期待される出力 .

```
/ # ls
bin    etc    lib    mnt    proc   run    srv    tmp    var
dev    home   media  opt    root   sbin   sys    usr
/ # cat /etc/my_first_config
'# config data
/ # cat /opt/my_second_config
'# my_second_config.config contents
/ # exit
```

ls は設定が / ディレクトリにないことを確認します。

2つの cat コマンドは、設定が要求されたディレクトリの中にあることを示しています。

#secrets
secretsは上記で説明したように設定と非常によく似た働きをします。大きな違いは、secretsの内容が暗号化されていることです。

を使って docker-compose.yml に以下を追加してください。

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

```
version: "3.7"
services:
  alpine:
    image: alpine:3.8
    command: sleep 600

    secrets:
      - my_secret

secrets:

    my_secret:
      external: true
```


既存のスタックをすべて閉じます。

```
docker-compose down -t 0
docker stack rm mystack
docker container prune -f
Let's create my_secret:
```

```
echo a secret password | docker secret create my_secret -
```

最後のハイフンは、docker が標準入力 (この場合はエコーテキスト) から読み込まなければならないことを意味します。

ここで docker secret ls を実行すると、秘密がリストアップされています。

```
docker stack deploy -c docker-compose.yml mystack
```

自動生成されたコンテナ名を取得するためにdocker psを実行する必要があります。

```
docker ps -a
```


これでランダムに生成されたコンテナ名を exec で入力することができるようになりました。あなたのコンテナ名を使って実行してください。

```
docker exec -it mystack_alpine.1.xrgtrrfnwn2qet5pevj5n9 /bin/shwne
```

示されているようにコマンドを実行します。

```
- df to show /run/secrets/my_secret exist - in tmpfs - in ram.
- cat /run/secrets/my_secret ... to see the secret.
```


```
/ # df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/mapper/docker-253:1-388628-c16342a3e1f1bfcdcebb82872fa626a5f35a2bea4e535aa9a889069b85c63332
                         10.0G     37.3M     10.0G   0% /
tmpfs                    64.0M         0     64.0M   0% /dev
tmpfs                   492.6M         0    492.6M   0% /sys/fs/cgroup
/dev/mapper/centos00-root
                         12.6G      5.5G      7.1G  43% /etc/resolv.conf
/dev/mapper/centos00-root
                         12.6G      5.5G      7.1G  43% /etc/hostname
/dev/mapper/centos00-root
                         12.6G      5.5G      7.1G  43% /etc/hosts
shm                      64.0M         0     64.0M   0% /dev/shm
tmpfs                   492.6M      4.0K    492.6M   0% /run/secrets/my_secret
tmpfs                   492.6M         0    492.6M   0% /proc/acpi
tmpfs                    64.0M         0     64.0M   0% /proc/kcore
tmpfs                    64.0M         0     64.0M   0% /proc/keys
tmpfs                    64.0M         0     64.0M   0% /proc/timer_list
tmpfs                    64.0M         0     64.0M   0% /proc/timer_stats
tmpfs                    64.0M         0     64.0M   0% /proc/sched_debug
tmpfs                   492.6M         0    492.6M   0% /proc/scsi
tmpfs                   492.6M         0    492.6M   0% /sys/firmware

/ # cat /run/secrets/my_secret
a secret password
/ # exit
```

以下のコマンドを実行すると、シークレットそのものが見えなくなります。

```
docker inspect my_secret
```


```
[
    {
        "ID": "vjvqnag6nu0p87xc0o94p315g",
        "Version": {
            "Index": 386
        },
        "CreatedAt": "2018-11-06T12:05:40.984748215Z",
        "UpdatedAt": "2018-11-06T12:05:40.984748215Z",
        "Spec": {
            "Name": "my_secret",
            "Labels": {}
        }
    }
]
```

サーバー上のすべてのsecretsのリストを見るには、以下を実行します。

```
docker secret ls
```


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