1つのDockerコンテナで複数のサービスを実行する方法

Dockerは、スタックのコンポーネントを分離されたコンテナとしてパッケージ化する技術です。各プロセスを独自のコンテナで実行し、コンポーネント間をきれいに分割するのが一般的です。これにより、モジュール性が向上し、コンテナ化によるスケーラビリティの恩恵を受けることができます。

1つのコンテナ内で複数のサービスを実行したい状況は、依然としてあり得ます。これはDockerのエコシステムでは自然なことではありませんが、1つ以上の長寿命プロセスを持つコンテナを作成するために使用できる、いくつかの異なるアプローチを紹介します。

問題の特定

Dockerコンテナは、1つのフォアグラウンドプロセスを実行します。これはイメージ’のENTRYPOINTとCMD命令によって定義されます。ENTRYPOINTはイメージ’のDockerfile内で設定され、CMDはコンテナ作成時に上書きすることができます。コンテナは、フォアグラウンドプロセスが終了すると自動的に停止します。

CMD から他のプロセスを起動することはできますが、コンテナは、元のフォアグラウンド プロセスが生きている間だけ実行されます。ENTRYPOINT/CMD メカニズムを使用して、2 つの独立したサービスを組み合わせた寿命までコンテナの動作を維持することは、直接的には不可能です。

複数のプロセスを1つのエントリーポイントでラッピングする

ラッパースクリプトは、この問題に対する最もシンプルな解決策です。すべてのプロセスを開始し、それらのプロセスが終了するのを待つスクリプトを書くことができます。このスクリプトをDocker ENTRYPOINTとして設定すると、コンテナ’のフォアグラウンドプロセスとして実行され、ラップされたスクリプトのいずれかが終了するまでコンテナが実行されたままになります。

#!/bin/bash
/opt/first-process &
/opt/second-process &
wait -n
exit $?

このスクリプトは、コンテナ内の /opt/first-process と /opt/second-process のバイナリを起動します。b}を使用すると、各プロセスの終了を待たずにスクリプトを続行できます。waitは、いずれかのプロセスが終了するまでスクリプトを中断するために使用します。その後、スクリプトは終了したスクリプトが発行するステータスコードで終了します。

このモデルでは、コンテナはファーストプロセスとセカンドプロセスの両方を、どちらかが終了するまで実行することになります。その時点で、もう一方のプロセスがまだ実行中であっても、コンテナは停止します。

このスクリプトを使用するには、Dockerイメージ’のENTRYPOINTとCMDを変更して、コンテナ’のフォアグラウンドプロセスとします。

ENTRYPOINT ["/bin/sh"]
CMD ["./path/to/script.sh"]

コンテナ・オプション –init

コンテナプロセスを管理する上での一つの課題は、終了時に効果的にクリーンアップすることです。DockerはCMDをプロセスID 1として実行し、シグナルの処理とゾンビの排除を担当させます。スクリプトにこれらの機能がない場合、コンテナ内に孤立した子プロセスが残ってしまう可能性があります。

docker run コマンドには –init フラグがあり、エントリポイントを変更して PID 1 として tini を使用します。これは、CMDを実行し、シグナル転送を処理し、ゾンビを継続的に回収する最小限のinitプロセス実装です。

多くのプロセスを生成することが予想され、手動でクリーンアップを処理したくない場合、–initを使用する価値があります。Tini はコンテナ用に設計された軽量の init フレーバーです。systemd や upstart のような本格的な代替品よりもずっと小さいです。

専用プロセスマネージャの使用

多くのプロセスを管理する場合、手動スクリプトはすぐに最適でなくなります。プロセスマネージャを採用することは、Dockerコンテナ内で複数のサービスを実行するためのもう一つの方法です。プロセスマネージャはあなたのENTRYPOINTとなり、ワーカープロセスの起動、維持、後始末の責任を持ちます。

supervisordは、/etc/supervisor/conf.d/supervisord.confファイルで簡単に設定できる一般的な選択肢です。

[program:apache2]
command=/usr/sbin/apache2 -DFOREGROUND

[program:mysqld]
command=/usr/sbin/mysqld_safe

この設定ファイルは、supervisord が Apache と MySQL を起動するように設定します。Dockerコンテナで使用するには、必要なパッケージを全てイメージに追加してから、supervisordの設定ファイルを正しい場所にコピーしてください。コンテナ起動時に自動的に実行されるように、supervisord をイメージ’の CMD として設定します。

FROM ubuntu:latest
RUN apt-get install -y apache2 mysql-server supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
ENTRYPOINT ["/bin/sh"]
CMD ["/usr/bin/supervisord"]

supervisordは継続的に実行されるため、監視対象プロセスの1つが終了したときにコンテナを停止することはできません。代替オプションとして、s6-overlayがあり、この機能を備えています。これは、サービススクリプトを直接 /etc/services.d に配置する宣言型サービスモデルを使用します。

# Add s6-overlay to your image
ADD https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz

RUN printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run
RUN chmod +x /etc/services.d/first-service/run

# Use s6-overlay as your image's entrypoint
ENTRYPOINT ["/init"]

サービスディレクトリ内に実行可能な finish スクリプトを追加することで、 docker stop によるコンテナの停止に対応できます。s6-overlay は stop コマンドによりプロセスが TERM シグナルを受け取ると、自動的にこれらのスクリプトを実行します。

Finishスクリプトは、そのサービスの終了コードを最初の引数として受け取ります。このコードは、キャッチできないシグナルによってサービスが強制終了されたときに256に設定されます。スクリプトは最終的な終了コードを /run/s6-linux-init-container-results/exitcode に書き込む必要があります; s6-overlay はこのファイルを読み、その値で終了し、そのコードがコンテナ’の停止コードとして使用されるようにします。

#!/bin/sh
echo "$1" > /run/s6-linux-init-container-results/exitcode

コンテナで複数のプロセスを実行すべき場合とは?

このテクニックは、独立したコンテナとして実行するために分離することができない、緊密に結合されたプロセスで使用するのが最適です。例えば、バックグラウンドのヘルパーユーティリティに依存するプログラムや、個々のプロセスを独自に管理するモノリシックなアプリケーションなどがこれにあたります。このようなタイプのソフトウェアをコンテナ化するには、上記のテクニックを使用します。

コンテナ内で複数のプロセスを実行することは、可能な限り避けなければなりません。単一のフォアグラウンド・プロセスにこだわることで、分離を最大化し、コンポーネント同士が干渉するのを防ぎ、特定の部分のデバッグやテストの能力を向上させることができます。コンテナ・オーケストレーターを使用すれば、コンポーネントを個別にスケールすることができます。これにより、最もリソースを消費するプロセスのインスタンスをより多く実行する柔軟性を得ることができます。

まとめ

コンテナには通常1つのフォアグラウンド・プロセスがあり、それが生きている限り実行されます。このモデルは、コンテナ化のベストプラクティスと一致しており、この技術から最大の利益を得ることができます。

状況によっては、コンテナ内で複数のプロセスを実行する必要がある場合があります。すべてのイメージは最終的に単一のエントリポイントを持つので、ターゲットバイナリの起動に責任を持つラッパースクリプトを書くか、プロセスマネージャを追加する必要があります。

プロセスマネージャは必要なものをすべて提供してくれますが、余分なパッケージや設定でイメージを肥大化させてしまいます。Wrapperスクリプトはよりシンプルですが、ゾンビプロセスの増殖を防ぐためにDocker’の–initフラグとペアにする必要があるかもしれません。

Scroll to Top