Dumb-Initは、最小限のコンテナ環境(Dockerなど)内でPID 1として実行するように設計された単純なプロセススーパーバイザーおよびINITシステムです。 Cで書かれた、静的にリンクされた小さなバイナリとして展開されます。
軽量コンテナは、SystemDやsysvinitなどの通常のINITシステムなしで単一のプロセスまたはサービスを実行するというアイデアを普及させています。ただし、INITシステムを省略すると、プロセスと信号の誤った処理につながることが多く、優雅に停止できないコンテナや破壊されるべきコンテナの漏れなどの問題につながる可能性があります。
dumb-init使用すると、コマンドをdumb-initで接頭するだけです。それはPID 1として機能し、すぐにあなたのコマンドを子プロセスとして生成し、受信したときに信号を適切に処理および転送するように注意します。
通常、Dockerコンテナを起動すると、実行しているプロセスがPID 1になり、コンテナのINITシステムであることに伴う癖と責任が与えられます。
これに提示する2つの一般的な問題があります。
ほとんどの場合、信号は適切に処理されません。
Linuxカーネルは、PID 1として実行されるプロセスに特別な信号処理を適用します。
プロセスが通常のLinuxシステムで信号を送信すると、カーネルは最初にプロセスがその信号に登録したカスタムハンドラーをチェックし、それ以外の場合はデフォルトの動作に戻ります(たとえば、 SIGTERMでプロセスを殺します)。
ただし、信号を受信するプロセスがPID 1である場合、カーネルによって特別な処理が行われます。信号のハンドラーを登録していない場合、カーネルはデフォルトの動作に戻ることはなく、何も起こりません。言い換えれば、プロセスがこれらの信号を明示的に処理しない場合、 SIGTERMを送信することはまったく効果がありません。
一般的な例は、 docker run my-container script : SIGTERMをdocker runプロセスに送信すると、通常はdocker runコマンドが殺されますが、コンテナを背景に走らせたままにします。
孤立したゾンビプロセスは適切に刈り取られていません。
プロセスはゾンビが出るとゾンビになり、親がwait()システムの呼び出しのバリエーションを呼び出すまでゾンビのままです。プロセステーブルには「削除」プロセスとして残ります。通常、親のプロセスはすぐにwait()を呼び出し、長年のゾンビを避けます。
親が子供の前に出る場合、子供は「孤児」であり、PID 1の下で再格闘されます。したがって、initシステムは、孤立したゾンビプロセスのwait() -ingの責任を負います。
もちろん、ほとんどのプロセスは、たまたま付着したランダムプロセスでwait()ません。そのため、コンテナはPID 1に根付いた数十のゾンビで終わります。
dumb-init何をしますかdumb-init PID 1として実行され、単純なinitシステムのように機能します。単一のプロセスを開始し、その子プロセスに根付いたセッションに受信したすべてのシグナルをプロキシします。
実際のプロセスはPID 1ではなくなっているため、 dumb-initから信号を受信すると、デフォルトの信号ハンドラーが適用され、プロセスが予想どおりに動作します。あなたのプロセスが死んだ場合、 dumb-initも死にます。
デフォルトモードでは、 dumb-init子供に根付いたセッションを確立し、プロセスグループ全体に信号を送信します。これは、死ぬ前に子どもを通常知らせない能力の低い子供(シェルスクリプトなど)がいる場合に役立ちます。
これは、実際には、DaemontoolsやSupervisordなどの通常のプロセススーパーバイザーのDockerコンテナ以外で、シェルスクリプトを監督することができます。通常、シェルによって受信されたSIGTERMのような信号は、サブプロセスに転送されません。代わりに、シェルプロセスのみが死にます。馬鹿げているので、シェバンに馬鹿げたイニットを備えたシェルスクリプトを書くことができます:
#!/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はシェルを殺しますが、それらのプロセスは実行されています(背景と前景の両方!)。馬鹿げたイニットでは、サブプロセスはあなたのシェルが行うのと同じ信号を受け取ります。
直接の子供にのみ信号を送信したい場合は、 --single-child引数で実行するか、環境変数DUMB_INIT_SETSID=0 dumb-initを実行するときに設定できます。このモードでは、ダムインは完全に透明です。複数の弦をつなぐこともできます( dumb-init dumb-init echo 'oh, hi'など)。
Dumb-Initを使用すると、着信信号を処理する前に書き換えることができます。これは、常に標準信号(Sigtermなど)を送信するDockerスーパーバイザー(MesosやKubernetesなど)がいる場合に役立ちます。いくつかのアプリは、優雅なクリーンアップを行うために異なる停止信号を必要とします。
たとえば、Signal Sigterm(Number 15)をSigquit(Number 3)に書き換えるには、コマンドラインに--rewrite 15:3追加します。
信号を完全にドロップするには、特別な番号0に書き換えることができます。
SetSIDモードで実行する場合、ほとんどの場合、 SIGTSTP / SIGTTIN / SIGTTOUを転送するだけでは不十分です。プロセスがこれらの信号にカスタム信号ハンドラーを追加していない場合、カーネルはデフォルトの信号処理動作を適用しません(プロセスを停止します)。このため、これらの3つの信号からデフォルトの書き換えをSIGSTOPに設定しました。必要に応じて、信号を元の値に書き戻すことで、この動作をオプトアウトできます。
この機能の1つの注意事項:ジョブコントロールシグナル( SIGTSTP 、 SIGTTIN 、 SIGTTOU )の場合、ダムインは、たとえ他の何かに書き換えたとしても、信号を受け取った後、常に自らを中断します。
dumb-initを使用するためのいくつかのオプションがあります:
多くの一般的なLinuxディストリビューション(Debian( stretch以降)を含む)およびUbuntu( bionic以降)などのDebian誘導体)には、公式のリポジトリに馬鹿げたパッケージが含まれています。
Debianベースのディストリビューションでは、他のパッケージをインストールするのと同じように、 apt install dumb-initを実行してdumb-initをインストールできます。
注: DumbInitのほとんどのディストリビューションが提供するバージョンは、提供するバージョンとは異なり、静的にリンクされていません(以下の他のオプションを参照)。これは通常完全には問題ありませんが、これらのバージョンのバージョンは一般に、他のLinuxディストリビューションにコピーされたときに機能しないことを意味します。
内部APTサーバーがある場合、 .debサーバーにアップロードすることがdumb-initを使用する推奨方法です。 dockerfilesでは、単にapt install dumb-initで利用できます。
Debianパッケージは、GitHubリリースタブから入手できます。または、 make builddeb自分で実行できます。
.debパッケージを手動でインストールする(debian/ubuntu)内部APTサーバーがない場合は、 dpkg -iを使用して.debパッケージをインストールできます。 .debコンテナに入れる方法を選択できます(ディレクトリを取り付けるか、 wget -ingがいくつかのオプションです)。
1つの可能性は、DockerFileに次のコマンドを使用することです。
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.debDumb-Initは静的にリンクされたバイナリとしてリリースされるため、通常、画像に陥ることができます。 DockerFileでそれを行う例は次のとおりです。
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-initdumb-initは完全にCで書かれていますが、バイナリをコンパイルしてインストールするPythonパッケージも提供します。 pipを使用してPypiからインストールできます。最初にCコンパイラ(Debian/Ubuntuで、 apt-get install gcc十分です)をインストールしてから、 pip install dumb-initだけです。
1.2.0の時点で、Pypiのパッケージは事前に構築されたホイールアーカイブとして利用でき、一般的なLinux分布でコンパイルする必要はありません。
Dockerコンテナ内にインストールしたら、コマンドをdumb-initで接頭するだけです(推奨されるJSON構文を使用していることを確認してください)。
DockerFile内では、コンテナのエントリポイントとしてDumbInitを使用することをお勧めします。 「エントリーポイント」は、 CMD命令に備えて準備される部分的なコマンドであり、ダムイニットに最適です。
# 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" ]ベース画像のエントリポイントを宣言する場合、そこから下る画像はダムシットを宣言する必要はありません。彼らはいつものようにCMDを設定することができます。
インタラクティブな1回限りの使用については、手動で準備することができます。
$ docker run my_container dumb-init python -c 'while True: pass'
dumb-initなしでこの同じコマンドを実行すると、 SIGKILLなしでコンテナを止めることができなくなりますが、 dumb-initを使用すると、 SIGTERMのような人道的な信号を送信できます。
JSON構文をCMDおよびENTRYPOINTに使用することが重要です。それ以外の場合は、Dockerがシェルを呼び出してコマンドを実行し、ダムインではなくPID 1としてシェルを実行します。
多くの場合、コンテナは、ビルド時間中に行えない事前に開始する作業を行いたいと考えています。たとえば、環境変数に基づいていくつかの構成ファイルをテンプレートすることをお勧めします。
それを馬鹿げたinitと統合する最良の方法は次のようなものです。
ENTRYPOINT [ "/usr/bin/dumb-init" , "--" ]
CMD [ "bash" , "-c" , "do-some-pre-start-thing && exec my-server" ]まだエントリポイントとしてdumb-initを使用することにより、常に適切なinitシステムが整っています。
Bashコマンドのexec部分は、Bashプロセスをサーバーに置き換えるため、重要です。
馬鹿げたバイナリを構築するには、作業コンパイラとLIBCヘッダーが必要であり、デフォルトはGLIBCになります。
$ make
GLIBCのため、静的にコンパイルされたDumb-Initは700kbを超えていますが、MUSLがオプションになりました。 Debian/ubuntu apt-get install musl-tools 、ソースとラッパーをインストールします。
$ CC=musl-gcc make
MUSLを静的にコンパイルすると、バイナリサイズは約20kbです。
ビルド依存関係を指定するために標準のDebian規則を使用します( debian/controlを参照)。開始する簡単な方法は、 apt-get install build-essential devscripts equivsであり、その後、 sudo mk-build-deps -i --removeいないすべての欠落しているビルド依存関係を自動的にインストールすることです。その後、 make builddebを使用して、Dumb-Init Debianパッケージを構築できます。
Dockerを使用して自動化されたDebianパッケージビルドを好む場合は、 make builddeb-docker実行するだけです。これは簡単ですが、Dockerをマシンで実行する必要があります。