GitHubじゃ!Pythonじゃ!

GitHubからPython関係の優良リポジトリを探したかったのじゃー、でも英語は出来ないから日本語で読むのじゃー、英語社会世知辛いのじゃー

Yelp

dumb-init – Linuxコンテナ用の最小限のinitシステム

投稿日:

Linuxコンテナ用の最小限のinitシステム https://engineeringblog.yelp.com/2016…

ダム・イニット

dumb-initは、最小限のコンテナ環境( Dockerなど)でPID 1として動作するように設計された、単純なプロセス管理者およびinitシステムです。 これは、Cで書かれた小さな静的リンクバイナリとしてデプロイされます。

軽量コンテナは、 systemdsysvinitのような通常のinitシステムを使わずに単一のプロセスやサービスを実行するという考え方を普及させました。 しかし、initシステムを省略すると、プロセスとシグナルの誤った取り扱いにつながり、正常に停止できないコンテナや破棄されたはずのコンテナなどの問題が発生することがあります。

dumb-init使用すると、単にコマンドのdumb-initを付けることができます。 これはPID 1として機能し、受信した信号を適切に処理して転送するように注意しながら、子プロセスとしてのコマンドをすぐに生成します。

なぜあなたはinitシステムが必要なのですか?

通常、Dockerコンテナを起動すると、実行中のプロセスはPID 1になり、コンテナのinitシステムとしての役割と責任が与えられます。

これには2つの共通の問題があります。

  1. ほとんどの場合、信号は適切に処理されません。

    Linuxカーネルは、PID 1として実行されるプロセスに特殊なシグナル処理を適用します。

    プロセスが通常のLinuxシステム上でシグナルを送信すると、カーネルはプロセスがそのシグナルに対して登録したカスタムハンドラを最初にチェックし、そうでない場合はデフォルトの動作(例えばSIGTERMプロセスを終了)に戻ります。

    しかし、シグナルを受け取るプロセスがPID 1であれば、カーネルによって特別な処理が行われます。 シグナル用のハンドラを登録していない場合、カーネルはデフォルトの動作に戻りません。何も起こりません。 言い換えれば、プロセスがこれらのシグナルを明示的に処理しない場合、 SIGTERMを送信することはまったく効果がありません。

    一般的な例はdocker run my-container script CIジョブです。 docker runプロセスにSIGTERMを送信すると、通常、 docker runコマンドは終了しますが、コンテナはバックグラウンドで実行されたままになります。

  2. 孤立したゾンビのプロセスは適切に収穫されません。

    プロセスが終了するとゾンビになり、親プロセスがwait()システムコールのバリエーションを呼び出すまでゾンビのままになります。 プロセステーブルには、「無効」プロセスとして残ります。 通常、親プロセスはすぐにwait()を呼び出し、長命のゾンビを避けます。

    親がその子の前に出ると、その子は「孤立している」ことになり、PID 1で再親子化されます。したがって、initシステムは孤立したゾンビプロセスのwait()行います。

    もちろん、ほとんどのプロセス 、プロセスにランダムなプロセスが加わるのをwait() ことはありません。そのため、コンテナは多くの場合、PID 1に基づく数十のゾンビで終了します。

どんなdumb-initがしますか

dumb-initはPID 1として動作し、単純なinitシステムのように動作します。 1つのプロセスを起動し、受信したすべてのシグナルをその子プロセスをルートとするセッションにプロキシします。

あなたの実際のプロセスはもはやPID 1ではないので、 dumb-initからシグナルを受け取ると、デフォルトのシグナルハンドラが適用され、あなたのプロセスは期待通りに動作します。 プロセスがdumb-initすると、まだ残っている可能性のある他のプロセスも慎重に処理して、 dumb-initも終了します。

セッションの振る舞い

デフォルトモードでは、 dumb-initは子プロセスのルートとなるセッションを確立し、シグナルをプロセスグループ全体に送信します。 これは、(シェルスクリプトのような)ふさわしくない子がいて、子供が死ぬ前に通常は伝えない場合に便利です。

これは実際にdaemontoolsやシェルスクリプトを監督するsupervisordのような通常のプロセス管理者のDockerコンテナの外で役に立ちます。 通常、シェルが受け取ったSIGTERMようなSIGTERMはサブプロセスに転送されません。 代わりに、シェルプロセスだけが終了します。 dumb-initを使用すると、shebangにdumb-initを使ってシェルスクリプトを書くことができます:

#!/usr/bin/dumb-init /bin/sh
my-web-server &  # launch a process in the background
my-other-server  # launch another process in the foreground

通常、シェルに送られたSIGTERMはシェルを殺しますが、それらのプロセスは実行し続けます(バックグランドとフォアグラウンドの両方)。 dumb-initを使用すると、サブプロセスはシェルが行うのと同じシグナルを受信します。

直接の子にのみシグナルを送るには、 --single-child引数で実行するか、 dumb-init実行するときに環境変数DUMB_INIT_SETSID=0設定します。 このモードでは、dumb-initは完全に透過的です。 dumb-init dumb-init echo 'oh, hi' )複数の文字列を組み合わせることもできます。

信号の書き換え

dumb-initは、着信信号をプロキシする前に書き換えることができます。 これは、標準シグナル(SIGTERMなど)を常に送信するDockerスーパーバイザー(MesosやKubernetesなど)がある場合に便利です。 一部のアプリでは、正常なクリーンアップを行うために別の停止信号が必要です。

たとえば、シグナルSIGTERM(15番)をSIGQUIT(3番)に--rewrite 15:3には、コマンドラインに--rewrite 15:3を追加するだけです。

信号を完全にドロップするには、特殊な番号0書き換えることができます。

信号書き換え専用ケース

setsidモードで実行すると、ほとんどの場合、 SIGTSTP / SIGTTIN / SIGTTOUを転送するだけでは不十分です。プロセスがこれらのシグナル用のカスタムシグナルハンドラを追加していない場合、カーネルはデフォルトのシグナルハンドリング動作を適用しませんプロセスを中断する)、それは孤立したプロセスグループのメンバーであるためです。 このため、これらの3つの信号からデフォルトの書き換えをSIGSTOP設定します。 必要に応じて、信号を元の値に書き直すことで、この動作をオプトアウトすることができます。

ジョブ制御信号( SIGTSTPSIGTTINSIGTTOU )の場合、dumb-initは、シグナルを受信した後で、それを別のものに書き直したとしても、常に自身を中断します。

Dockerコンテナの中にインストールする

dumb-initを使用するためのいくつかのオプションがあります:

オプション1:内部aptサーバ(Debian / Ubuntu)経由でインストールする

内部aptサーバをお持ちの場合は、サーバに.debをアップロードすることをお勧めしdumb-init あなたのDockerfilesでは、簡単にapt-get install dumb-initapt-get install dumb-initだけで、利用可能になります。

DebianパッケージはGitHubリリースタブから入手するか、 make builddeb自分で実行make builddebことができます。

オプション2:手動で.debパッケージをインストールする(Debian / Ubuntu)

内部aptサーバがない場合は、 dpkg -iを使用して.debパッケージをインストールできます。 .debをどのようにコンテナに.debかを選択することができます(ディレクトリをマウントするか、いくつかのオプションがあります)。

1つの可能性は、あなたのDockerfileで次のコマンドを使うことです:

RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64.deb
RUN dpkg -i dumb-init_*.deb

オプション3:バイナリを直接ダウンロードする

dumb-initは静的にリンクされたバイナリとしてリリースされているので、通常はイメージに埋め込むことができます。 Dockerfileでこれを行う例を以下に示します:

RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64
RUN chmod +x /usr/local/bin/dumb-init

オプション4:PyPIからインストールする

dumb-initは完全にCで書かれていますが、バイナリをコンパイルしてインストールするPythonパッケージも提供しています。 pipを使用してPyPIからインストールできます。 最初にCコンパイラ(Debian / Ubuntuにapt-get install gccであれば十分です)をpip install dumb-initpip install dumb-init

1.2.0から、PyPIのパッケージは、あらかじめ組み込まれたホイールアーカイブとして利用でき、一般的なLinuxディストリビューションでコンパイルする必要はありません。

使用法

Dockerコンテナの中にインストールしたら、コマンドの前にdumb-initを付けるだけです( 推奨のJSON構文を使用しいることを確認してください)。

Dockerfile内では、dumb-initをコンテナのエントリポイントとして使用することをお勧めします。 “エントリポイント”は、 CMD命令の前に追加される部分的なコマンドであり、dumb-initにとって非常に適しています。

# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT ["/usr/bin/dumb-init", "--"]

# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]

CMD ["/my/script", "--with", "--args"]

エントリ・ポイントをベース・イメージに宣言すると、そこから降下したイメージでもdumb-initを宣言する必要はありません。 彼らはいつものようにCMDを設定することができます。

インタラクティブな一回限りの使用のために、手動で追加することができます:

$ docker run my_container dumb-init python -c 'while True: pass'

この同じコマンドをdumb-initなしで実行すると、 SIGKILLなしでコンテナを停止することができなくなりますが、 dumb-initではSIGTERMような人道的なシグナルを送ることができます。

CMDENTRYPOINT はJSON構文を使用することが重要です。 それ以外の場合、Dockerはコマンドを実行するシェルを起動し、dumb-initの代わりにシェルをPID 1にします。

起動前のフックにシェルを使用する

多くの場合、コンテナはビルド中に実行できないいくつかの事前起動作業を実行したいと考えています。 たとえば、環境変数に基づいていくつかの設定ファイルをテンプレートにすることができます。

これをdumb-initと統合する最良の方法は次のようなものです:

ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["bash", "-c", "do-some-pre-start-thing && exec my-server"]

エントリポイントとしてdumb-initを使用することによって、適切なinitシステムが常に存在します。

bashコマンドのexec部分は重要です。なぜなら、bashプロセスをサーバーに置き換えるからです。そのため、シェルは起動時に一時的にしか存在しません。

dumb-initのビルド

dumb-initバイナリをビルドするには、動作するコンパイラとlibcのヘッダとglibcのデフォルトが必要です。

$ make

ムスルとの建物

静的にコンパイルされたdumb-initはglibcのために700KBを超えていますが、現在はmuslがオプションです。 Debian / Ubuntu上でソースとラッパーをインストールするにはapt-get install musl-toolsをインストールしてください:

$ CC=musl-gcc make

静的にmuslでコンパイルすると、バイナリサイズは約20KBです。

Debianパッケージのビルド

ビルドの依存関係を指定するために標準的なDebianの規約を使用しています( debian/control見る)。 簡単に始めるには、 apt-get install build-essential devscripts equivs sudo mk-build-deps -i --removeを実行し、次にsudo mk-build-deps -i --removeを実行して、欠落しているビルドの依存関係をすべて自動的にインストールします。 make builddebを使って、dumb-init Debianパッケージをビルドmake builddebことができます。

Dockerを使って自動化されたDebianパッケージをビルドしたい場合は、単にmake builddeb-docker実行しmake builddeb-docker これは簡単ですが、マシン上でDockerを実行する必要があります。

も参照してください







-Yelp
-, , , , , ,

執筆者: