blog.takurinton.dev

dumb-init とは

2021-08-18

はじめに

こんにちは、どうも僕です。

Dockerfile を読んだり書いたりしてる時に出てきた dumb-init を知らなくて気になったので調査しました。

dumb-init とは

dumb-init とは Linux コンテナ用の最小限の init システムで、PID が 1 になるように作られています。

最小限のコンテナとは、Docker などの小さめの環境のことを指していています。C で書かれていて、静的リンクしたバイナリとしてデプロイされます。

デフォルトのままだと 700KB ありますが、musl と静的リンクさせて自分でビルドをすると 20KB まで抑えられるようです。(参考:[https://github.com/Yelp/dumb-init#building-with-musl](https://github.com/Yelp/dumb-init#building-with-musl))

  • GitHub
  • yelp engineering blog
  • dumb-init はコマンドとして実行することができます。

    これはPID 1 として機能し、受信したシグナルを適切に処理して転送しつつ、子プロセスとしてのコマンドを生成します。

    軽量コンテナは、通常の init システムを使わずに単一のプロセスやサービスを実行するという考え方を売りにして普及してきた(らしい)ですが、扱い方を間違えるとコンテナが破壊されたり正常に終了することができなかったりします。大学の授業でも実際にゾンビを作ってやっつけようみたいなものがありましたが、あれが実務で飛んできたらちょっと厳しいかなと思うのでそういうのはできれば避けたいです。

    Docker における init システム

    上で PID が 1 で起動されるという話はしましたが、そもそも Docker では指定されたコマンド(RUN で実行されるやつ)が PID 1 で起動されています。複数実行されたらまずそう。

    実際そうで、どうやら使い方によってはシグナルのハンドリングができないことがあるらしいです。

    それによってプロセスを終了できなかったリソースがメモリリークを起こしたりするので注意する必要があります。

    Unix プロセスと Docker の罠 - けちゃぶろぐ

    で解説されていますが、親が死んだら子も死ぬのでリクエストをハンドリングしきれない、子が死ぬとゾンビプロセスが生まれてメモリリークを起こすと言ったようになります。授業でやったやつだ。

    そして、リクエストをハンドリングしきれない問題やゾンビが生まれる問題は割と頻繁に起き、崩壊するみたいな感じです。

    dumb-init で解決できること

    dumb-init では単純な init システムに加えてデフォルトのシグナルハンドラが適用されて期待通りにリクエストをハンドリングしつつ、ゾンビと化したプロセスも適切に処理して dumb-init も終了します。

    さらに以下の特徴があります。

    セッション

    セッションとは、[セッション](https://man7.org/linux/man-pages/man2/setsid.2.html) のことを指しています。

    dumb-init はデフォルトでは子プロセスのルートとなるセッションを確立し、シグナルをプロセスグループ全体に送信します。

    これは不適切な子プロセスがいて、shell script などの通常は死ぬ前にシグナルを送らないものがある時に役立ちます。

    例えば以下のような sh ファイルがあるとします。

    #!/usr/bin/dumb-init /bin/sh
    my-web-server &  # バックグラウンドで実行されるプロセス
    my-other-server  # フォアグラウンドで実行されるプロセス
    

    これを実行した際、PID 1 に対して複数のプロセスが起動するため、中にいる子プロセスなどは死にます。

    しかしこのような時に dumb-init を使用します。通常、shell が受信した SIGTERM のようなシグナルは、サブプロセスに転送されません。代わりに、shell のプロセスのみが停止します。

    もう少し実用に近い話をすると、daemontools などの通常のプロセスのデーモンの Docker コンテナーの外部で実行される `supervisord` コマンドを使用する際などに役立ちます。

    シグナルの書き換え

    dumb-init は受信したシグナルをプロキシする前に書き換えることができます。

    これは、標準シグナル(SIGTERMなど)を常に送信する Docker supervisor(Mesos や Kubernetes などのサービス)がある場合に便利です。

    たとえば、SIGTERM(15)を SIGQUIT(3)に書き換えたい場合にはコマンドラインに `--rewrite 15:3` を追加することで実現することができます。

    Dockerfile に書く

    普通に `apt-get` で install することができます。

    意外とすんなり利用することができるみたいです。

    FROM ubuntu:20.04
    
    RUN \
      apt-get update && \
      apt-get install -y -q  dumb-init \
      ... other lib 
    
    ENTRYPOINT ["/usr/bin/dumb-init", "--"]
    CMD ["start"]
    

    まとめ

    適切なプロセスの管理などに役立つことがわかりました。

    子プロセスがゾンビになったりしてリークしてしまうのは良くないですし、実際の Dockerfile ではランタイムで色々な処理がめんどくさく交差してることもわかりました。

    自分で手を動かした方が早いと思うので適当に素振りをしておこうと思います。

    P.S. Linux を学び直す必要があると感じました。