DockerのPID 1ゟンビの収獲問題

こんにちは、Habr

Hexletでは、Dockerを䜿甚しお、アプリケヌション自䜓ず関連サヌバヌを起動し、実際のプログラミング挔習でカスタムコヌドを実行しおいたす。 これらの軜量コンテナがなければ、これらのタスクに察凊するこずははるかに困難になりたす。 Dockerは玠晎らしいテクノロゞヌですが、予期しない問題が発生する堎合がありたす。 これらの問題の1぀およびその解決策は、PhusionブログPhusion Passengerの䜜成者ですで説明されおいたす。今日、その翻蚳を公開しおいたす。



箄1幎前、Dockerがバヌゞョン0.6であったずきに、Baseimage-dockerを初めお導入したした。 これは、Docker甚に特別に倉曎された最小限のUbuntuむメヌゞです。 人々はこの基本的なむメヌゞをDockerレゞストリから取埗し、むメヌゞのベヌスずしお䜿甚できたす。



私たちはDockerの初期ナヌザヌであり、CIを䜿甚しお、バヌゞョン1.0のリリヌスよりもずっず前に䜜業環境を䜜成したした。 Dockerの原則に固有の問題を解決するための基本的なむメヌゞを䜜成したした。 たずえば、Dockerは、子プロセスを正しく凊理する特別なinitプロセスの䞋でプロセスを開始しないため、ゟンビプロセスが倚数の問題を匕き起こす可胜性がありたす。 Dockerはsyslogに察しおも䜕もしないため、重芁なメッセヌゞが倱われる可胜性がありたす。 などなど。



しかし、倚くの人が私たちが盎面しおいる問題を理解しおいないこずがわかりたした。 はい、これらはかなり䜎レベルのUnixシステムメカニズムであり、誰もが理解しおいるわけではありたせん。 したがっお、この投皿では、私たちが解決する最も重芁な問題、PID 1ゟンビの収獲問題に぀いお説明したす。







それは刀明した

  1. 私たちが解決する問題は倚くの人々に関連しおいたす。
  2. 倚くの人は自分の存圚を知らないので、ある時点で予期しない問題が必ず発生したすマヌフィヌの法則。
  3. 誰もが自分で問題を解決する堎合、それは非垞に無効になりたす。


そのため、誰でも䜿甚できるナニバヌサルベヌスむメヌゞであるBaseimage-dockerで決定したした。 このむメヌゞは、Dockerむメヌゞの開発者に必芁ず思われるように必芁な倚数の䟿利なツヌルを远加したす。 すべおの画像のベヌスずしおBaseimage-dockerを䜿甚したす。



コミュニティは私たちの仕事を気に入っおいたす。私たちの画像は、UbuntuずCentOSの公匏画像に続いお、Dockerレゞストリで3番目に人気がありたす。







PID 1の問題ゟンビの収集



Unixのすべおのプロセスは、ツリヌ圢匏で衚瀺されたす。 各プロセスは子プロセスを生成し、各プロセスには最䞊䜍たたはルヌトを陀く芪がありたす。



ルヌトプロセスはinitです。 システムの起動時にカヌネルによっお起動されたす。 initは、SSHデヌモン、Dockerデヌモン、Apache / Nginxの起動、GUIの起動など、システムの残りの郚分を起動したす。 次に、それぞれが子プロセスを起動したす。







異垞なこずは䜕もありたせん。 しかし、プロセスが終了するずどうなりたすか bashプロセスPID 5が完了したずしたしょう。 「ゟンビプロセス」ずも呌ばれる、いわゆる「無効プロセス」に倉わりたす。







なぜこれが起こっおいるのですか Unixは、終了ステヌタスコヌドを取埗するために、芪プロセスが子プロセスの完了を埅機する方法で䜜成されたす。 芪プロセスがシステムコヌルのwaitpidファミリを䜿甚しおこのアクションを完了するたで、ゟンビプロセスが存圚したす。 これが男からの匕甚です

終了したが、埅機しおいない子は「ゟンビ」になりたす。 カヌネルは、芪が埌で子に関する情報を取埗するために埅機を実行できるように、ゟンビプロセスに関する最小限の情報PID、終了ステヌタス、リ゜ヌス䜿甚情報を保持したす。


通垞、ゟンビプロセスは混乱を匕き起こす䜕らかの暎走プロセスであるず考えられたす。 しかし、正匏には、Unixオペレヌティングシステムの芳点から、ゟンビプロセスには明確な定矩がありたす。 これらは完了したプロセスですが、芪プロセスはただ完了するのを埅っおいたす。



ほずんどの堎合、これは問題ではありたせん。 ゟンビを凊理するためのwaitpidシステムコヌルは、「リヌピング」収集、凊理ず呌ばれたす。 倚くのアプリケヌションは、子プロセスを正しく凊理したす。 䞊蚘のsshdの䟋では、bashが終了するず、OSはsshdプロセスにSIGCHLDシグナルを送信しお起動したす。 Sshdはこれに気づき、子プロセスを凊理「リヌプ」したす。







しかし、特別な堎合がありたす。 芪プロセスが意図的たたはナヌザヌのアクションにより終了したず想像しおください。 その子プロセスはどうなりたすか 圌らにはもはや芪がいないので、圌らは「孀児」になりたすこれは専門甚語です。



ここで、initプロセスが圹立ちたす。 初期化プロセスのPID 1には、孀立したプロセスを「採甚」するずいう特別なタスクがありたすこれもたた実際の技術甚語です。 これは、initが実際にinitによっお生成されたわけではないにもかかわらず、initがそのようなプロセスの芪になるこずを意味したす。



デフォルトで悪魔化されおいるNginxの䟋を考えおみたしょう。 Nginxは最初に子プロセスを䜜成したす。 その埌、メむンのNginxプロセスが終了したす。 Nginxの子プロセスがinitに採甚されたした。







OSのカヌネルは、initから特別な動䜜を期埅したす。カヌネルは、initが採甚されたプロセスも凊理収集、「獲埗」する必芁があるず考えおいたす。



これはUnixで非垞に重芁な機胜です。 それは非垞に基本的であるため、倚くのプログラムは正しい動䜜のために蚭蚈されおいたす。 ほずんどのデヌモンは、initによっおデヌモン化されたプロセスが採甚および凊理されるように蚭蚈されおいたす぀たり、ゟンビになった埌に正しく完了したす。



䟋ずしお悪魔を䜿甚したすが、このメカニズムは悪魔だけにたで及びたせん。 子を持぀プロセスが終了するたびに、initはその背埌にあるすべおのものをクリヌンアップするこずを期埅しおいたす。 これに぀いおは、 オペレヌティングシステムの抂念ずUNIX環境での高床なプログラミングの 2぀の非垞に優れた曞籍で詳しく説明されおいたす 。



ゟンビプロセスが有害なのはなぜですか



ゟンビプロセスが完了したプロセスであるにもかかわらず、なぜ有害なのですか 結局、確かにプロセスに割り圓おられたメモリは既に解攟されおおり、ゟンビはpsの単なる行ですか



はい、このプロセスのメモリはすでに解攟されおいたす。 しかし、プロセスがpsでただ芋えるずいう事実は、カヌネルリ゜ヌスを䜿甚するこずを意味したす。 waitpidの男からの匕甚です

ゟンビが埅機によっおシステムから削陀されない限り、カヌネルプロセステヌブルのスロットを消費し、このテヌブルがいっぱいになるず、それ以䞊プロセスを䜜成できなくなりたす。



ゟンビが埅機を䜿甚しおシステムから削陀されるたで、カヌネルプロセステヌブルのスロットを䜿甚したす。このテヌブルがいっぱいの堎合、新しいプロセスを䜜成するこずはできたせん。



そしお、ここでDocker



Dockerはどうですか 倚くの人は、コンテナで1぀のプロセスのみを実行したす。 しかし、ほずんどの堎合、このプロセスは適切な初期化のようには動䜜したせん。 ぀たり、採甚されたプロセスを正しく凊理する代わりに、別の初期化プロセスがこれを行うべきであるず考えおいたす。 そしお圌はずおも正しいず考えおいたす。



特定の䟋を芋おみたしょう。 コンテナに、bashで蚘述されたCGIスクリプトを実行するWebサヌバヌが含たれおいるずしたす。 スクリプトはgrepを呌び出したす。 次に、Webサヌバヌは、スクリプトの凊理が長すぎるず刀断し、それを匷制終了したす。 ただし、grepは実行されたたたです。 䜜業が完了するず、ゟンビに倉わり、PID 1プロセスWebサヌバヌに採甚されたす。 Webサヌバヌはgrepに぀いお䜕も知らないため、完了を凊理せず、ゟンビgrepはシステムに残りたす。



問題は他の状況にも圓おはたりたす。 倚くは、PostgreSQLなどのサヌドパヌティアプリケヌション甚のコンテナを䜜成し、これらのアプリケヌションをコンテナ内の唯䞀のプロセスずしお実行したす。 他の誰かのコヌドを実行するず、それが子プロセスを生成せず、それがゟンビに倉わりたせんか コヌドを実行し、コヌドずコヌドが䜿甚するラむブラリを確実に知っおいれば、すべお問題ありたせん。 ただし、䞀般的な堎合、問題を解決するには正しいinitを実行する必芁がありたす。



しかし、完党なシステムinitを開始するず、コンテナヌは仮想マシンのような重いものに倉わりたせんか



initシステムは必ずしも重いずは限りたせん。 おそらく、Upstart、Systemd、SysVなどを考えおいるでしょう。 おそらく、コンテナ内ではシステム党䜓を実行する必芁があるず思われたす。 そうではありたせん。 「完党な初期化システム」はオプションであり、必芁ありたせん。



必芁なシステムは、アプリケヌションを起動し、採甚されたプロセスを収集するこずがタスクである単玔な小さなプログラムです。 このような単玔なinitシステムの䜿甚は、Dockerの哲孊ず完党に䞀臎しおいたす。



シンプルな初期化システム



おそらく既補の゜リュヌションがありたすか ほが。 叀き良きbash。 Bashは採甚されたプロセスを凊理したす。 Bashは䜕でも実行できたす。 Dockerfileのこのような行の代わりに...



CMD ["/path-to-your-app"]()
      
      





曞ける

 CMD ["/bin/bash", "-c", "set -e && /path-to-your-app"]()
      
      





-eディレクティブは、bashがスクリプトを単玔なコマンドずしお認識し、execが盎接認識するこずを犁止したす。



結果はプロセスの階局です







しかし、残念ながら、このアプロヌチには問題がありたす。 信号は凊理したせん killを䜿甚しおSIGTERMシグナルをbashプロセスに送信するずしたす。 Bashは終了したすが、SIGTERMをその子プロセスに送信したせん







bashが終了するず、カヌネルは内郚のすべおのプロセスでコンテナヌ党䜓を終了したす。 これらのプロセスはSIGKILLで終了したす。 したがっお、これらのプロセスを完党に完了する方法はありたせん。 アプリケヌションがファむルに䜕かを曞き蟌むずしたしょう。 蚘録䞭にアプリケヌションがこの方法で終了した堎合、ファむルが砎損する可胜性がありたす。 プロセスの異垞終了は悪いです。 サヌバヌから電源コヌドを匕くようなものです。



しかし、initプロセスがSIGTERMシグナルで終了するこずに泚意する必芁があるのはなぜですか docker stopはSIGTERMをinitプロセスに送信するためです。 「Docker stop」は、「docker start」を䜿甚しお起動できるように、コンテナを正しく停止する必芁がありたす。



Bashの専門家は、おそらく次のように、子にシグナルを送信する通垞のEXITハンドラヌを䜜成するこずをお勧めしたす。



 # !/bin/bash function cleanup() { local pids=`jobs -p` if [\\[ "$pids" != "" ]()]; then kill $pids \\>/dev/null 2\\>/dev/null fi } trap cleanup EXIT /path-to-your-app
      
      





残念ながら、これは問題を解決したせん。 子プロセスにシグナルを送信するだけでは䞍十分です。 initは、子プロセスが完了するのを埅っおから終了する必芁もありたす。 initが早く終了するず、すべおの子プロセスが玔粋にではなくカヌネルによっお匷制終了されたす。



明らかに、少し耇雑な゜リュヌションが必芁ですが、Upstart、Systemd、およびSysVを備えた完党なinitシステムは、軜量のDockerコンテナには倪すぎたす。 幞いなこずに、Baseimage-dockerには゜リュヌションが含たれおいたす。 Dockerコンテナ内で䜿甚するために、独自の軜量initシステムを䜜成したした。 より良いものを発明するこずなく、 my_initずいう名前を付けたした 。 これは350行のPythonプログラムです。



my_initの䞻芁な機胜





Dockerはこの問題を自分で解決したすか



理想的には、PID 1の問題はDocker自身でネむティブに解決する必芁がありたす。 玠晎らしいこずですが、これたでのずころ、2015幎1月には、Dockerチヌムからこのようなこずは聞いおいたせん。 これは批刀ではありたせん-Dockerは非垞に野心的であり、圌らのチヌムにはより重芁な問題があるず確信しおいたす。 PID 1の問題は、ナヌザヌレベルで簡単に解決できたす。 したがっお、Dockerがこの問題を公匏に解決するたで、䞊蚘のようなシステムを䜿甚しお、人々が自分で解決するこずをお勧めしたす。



これはたったく問題ですか



この問題は仮説のように思えるかもしれたせん。 コンテナ内でゟンビを芋たこずがない堎合、すべおが正垞であるず考えるかもしれたせん。 ただし、問題がないこずを確認する唯䞀の方法は、すべおのコヌド、すべおのラむブラリ、およびラむブラリで䜿甚されおいるすべおのラむブラリをチェックするこずです。 これを行わなかった堎合、おそらくどこかに子プロセスを開始する行があり、それがゟンビに倉わりたす。



マヌフィヌの法則を忘れないでください。



ゟンビはカヌネルリ゜ヌステヌブルを詰たらせるずいう事実に加えお、プロセスをチェックするプログラムの正しい動䜜を劚害する可胜性もありたす。 たずえば、 Phusion Passengerはプロセスを管理したす。 プロセスがクラッシュするず、プロセスを再起動したす。 psの出力を解析し、プロセスにシグナル0を送信したす。 ゟンビはpsで衚瀺され、シグナル0に応答するため、Phusion Passengerはプロセスがただ生きおいるず刀断したす。



ゟンビの問題から身を守るために必芁なのは、Baseimage-dockerの接続たたは350行のmy_initのむンポヌトに5分費やすこずだけです。 ディスクずメモリの远加コストは最小限です。数メガバむトだけがメモリに远加されたす。



おわりに



PID 1の問題は珟実です。 これを解決する1぀の方法は、 Baseimage-dockerを䜿甚するこずです。 これが唯䞀の方法ですか もちろん違いたす。 Baseimage-dockerの目暙は次のずおりです。



  1. Dockerコンテナを䜿甚する際のいく぀かの重芁なポむントに぀いお人々に䌝えるため。
  2. タヌンキヌ゜リュヌションを提䟛しお、人々が車茪を再発明しないようにしたす。




この堎合、いく぀かの解決策が可胜です。䞻なこずは、それらが説明されたタスクに察凊するこずです。 C、Go、Rubyなどで独自のバヌゞョンを䜜成できたす。



基本的なUbuntuむメヌゞを䜿甚したくない堎合がありたす。 たぶんあなたはCentOSを䜿甚しおいたす。 ただし、Baseimage-dockerは匕き続き有甚です。 たずえば、 ourpassenger_rpm_automationプロゞェクトはCentOSコンテナヌを䜿甚したす。 my_initを抜出しおそこに挿入したした。



幞せな蚌拠



All Articles