SRE 2 Advent Calendar 2018 (https://qiita.com/advent-calendar/2018/sre2) 12/9 の記事です。
今回は Docker コンテナを起動する際の --init
オプションと PID 1 問題について調べていくうちに、各コンテナオーケストレーションサービスでの対応について気になり、それについて調べてみました。
Docker の init process
Docker の init process については上記記事で紹介されていますが、 Docker の場合、特に考慮せずプロセスを実行させるとエントリーポイントとして設定したプロセスが PID 1 で動作します。
Linux では起動時に最初に起動する init プロセスに PID 1 が割り振られ、PID 1 のプロセスについては特殊な考慮がされ、デフォルトでシグナルを無視する設定となっているようです。
そのため、 docker stop
コマンドなどで SIGTERM シグナルを送ってもプロセスが停止されず、一定時間後にタイムアウトして SIGKILL で強制終了します。
ちなみに SIGTERM から SIGKILL に切り替わるタイムアウト値については、 -t
オプションで変更することが可能で、docker stop -t 30 <コンテナID>
等でタイムアウト値の変更をすることができます。
https://docs.docker.com/engine/reference/commandline/stop/
ローカルで開発している場合などは大きな問題にならないかと思いますが、大規模なアプリケーションをコンテナで動かそうとしている場合は、余計な時間がかかったり graceful にシャットダウンされないなど問題があります。
そういった問題に対応するため、Docker で使用できて、シグナルを適切に処理する init 用プロセスがいくつか作成されています。
- dumb-init (https://github.com/Yelp/dumb-init)
- tini (https://github.com/krallin/tini)
例えば datadog agent では tini が使われていますし、(https://github.com/DataDog/docker-dd-agent/blob/master/Dockerfile-alpine) consul では dumb-init が使われています (https://github.com/hashicorp/docker-consul/blob/master/0.X/docker-entrypoint.sh#L1)。
Docker 1.13 からは --init
オプションが追加されました。
これは、実行時にオプションを設定することで、tini (https://github.com/krallin/tini) と同等の処理が実行されるようになるものです。
--init
オプションのありなしで init プロセスがラップされ、SIGTERM が正しく処理されるようになります。
コンテナオーケストレーションでの対応について
AWS ECS, Kubernetes のコンテナオーケストレーションサービスでも上記の問題については対応を考える必要があります。
すべてのコンテナで tini
, dumb-init
等の init process についての対応をできればいいですが、その場合コンテナイメージを作成するたびにそのための対応をする必要があります。
せっかくコンテナで実行させるアプリケーションに集中したいのにもかかわらずそれ以外の部分に気を配る必要となります。
そのため、可能ならば --init
オプション等で一律対応できたほうが管理上も楽です。
AWS ECS の場合
https://github.com/aws/amazon-ecs-agent/issues/852
ECS agent v1.15.0 から --init
オプション相当の対応が入っています。
タスク定義にて、 Linux パラメータの initProcessEnabled
を設定することで、--init
オプション相当のものを設定して実行する事ができるようになっています。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html
Kubernetes の場合
Kubernetes equivalent of docker run --init
Kubernetes でも同様のことができるのかについて、StackOverflow でも質問が投げられています。 また、こちらのブログでは、Kubernetes での pause コンテナについて紹介されています。
これは Pod 内での namespace を共有する機能を使用し、pause コンテナというものを作成するといったものです。 pause コンテナは Pod 内の他のコンテナの親コンテナとしで PID 1 で起動し、他のコンテナについては PID 1 以外で起動するようになります。 この方法で、SIGINT, SIGTERM 等のシグナルは pause コンテナで適切に処理されるようになるようです(が、まだ完全には理解できてない)
pause コンテナの機能については、
- v1.7 でデフォルトで ON
- v1.8 ではデフォルトで OFF
- v1.10 からは alpha サポートとなり、
PodShareProcessNamespace
という feature フラグが追加されました
こちらに Feature フラグのデフォルト値についてまとめられていますが、
- v1.10 では
PodShareProcessNamespace
はデフォルト false - v1.12 からは Beta 扱いとなりデフォルト true
となっており、2018/12/09 現在、GKE で指定できる最新バージョンは v1.11.4
、 Docker for Mac では v1.10.3
、AWS EKS では v1.10.3
が利用されているようなので、上記機能はデフォルトで false となっています。
Share Process Namespace between Containers in a Pod
こちらで紹介されている設定にて、
apiVersion: v1 kind: Pod metadata: name: nginx spec: shareProcessNamespace: true containers: - name: nginx image: nginx - name: shell image: busybox securityContext: capabilities: add: - SYS_PTRACE stdin: true tty: true
を Docker for mac の Kubernetes 環境で試してみましたが、
$ kubectl attach -it nginx -c shell If you don't see a command prompt, try pressing enter. / # ps aux PID USER TIME COMMAND 1 root 0:00 sh 9 root 0:00 ps aux
となってしまい、namespace 共有はされていませんでした。(なぜだ)
GKE でも v1.11.4
のクラスタを立ち上げて試してみましたがこちらでも同様に確かめることができませんでした。(なぜだろう)
デフォルトの挙動を変えるためには、Kubernetes の Feature gates を設定する必要があります。 その場合、kubelet にオプションを指定して起動する必要があります。 Docker for mac, GKE 等の環境ではFeature gates を変更する方法は用意されておらず、現状使えないようです。 そのため、現状では GKE のアルファクラスタを使用する方法や、自分で Kubernetes 環境を作っている場合 kubelet のパラメータを変更して起動する方法しかなさそうに見えます。
Kubernetes 自体は AWS ECS と違い rkt のサポートもされており、広くプラットフォームとして使われることを想定されていることから、 Docker オプションの直接的なサポート等がされていないように感じました。
まとめ
Docker の --init
オプションから、コンテナオーケストレーション環境での同等な設定方法について AWS ECS/Kubernetes での方法を調査しました。
現状 Docker のみを対象とするのであれば、--init
オプションを付けてアプリケーションを起動させ、アプリケーション作成に集中したほうがよさそうです。
とはいえ、Kubernetes 等を使うのを想定するのであれば、 tini
等を設定した Docker イメージを作成したほうがよいかもしれません。