コンテナー内での複数サービス起動

コンテナーの主となる実行プロセスは、Dockerfileの最終部分に指定されるENTRYPOINTCMDです。 1 つのコンテナーには 1 つのサービスを割り当てるということにすれば、気にかける箇所が絞られるので、一般的にはこれが推奨されます。 ただそのサービスからは、複数のプロセスがフォークされることもあります(たとえば Apache ウェブサーバーでは複数のワーカープロセスが起動されます)。 マルチプロセスとなることは、まったく問題ありません。 一方で、アプリケーションが持ついくつもの役割を 1 つのコンテナーに持たせることは、Docker の優れた機能を利用する観点からは避けるべきです。 コンテナーを複数にするのであれば、ユーザー定義のネットワークや共有ボリュームを利用して接続します。

コンテナーのメインプロセスは、コンテナーそのものが起動させるプロセスすべてを管理するためにあります。 メインプロセスが十分に機能していないことが原因で、コンテナー終了時に子プロセスを適切に停止できないことがあります。 起動プロセスがこの手の事態に陥った場合は、コンテナー起動時に--initオプションを指定してみてください。 この--initフラグは、コンテナーのメインプロセスとして、非常に小さな初期化プロセスを埋め込みます。 この小さなプロセスが、コンテナー終了時の子プロセス停止を受け持つことになります。 子プロセスの扱いをこのようにするのは、本格的な初期化プロセス、たとえばsysvinitupstartsystemdに比べて、コンテナー内部のプロセスのライフサイクルを適切に扱うことができるからです。

1 つのコンテナー内に複数のサービスを起動させる必要があるなら、方法はいくつかあります。

  • 実行するコマンドをすべてラッパースクリプトに含めます。 あらかじめテストやデバッグは行っておきます。 そしてこのラッパースクリプトをCMDとして実行します。 以下は簡単な例です。 まずはラッパースクリプトを生成します。

    #!/bin/bash
    
    # 1つめのプロセスを起動
    ./my_first_process &
    
    # 2つめのプロセスを起動
    ./my_second_process &
    
    # いずれかが終了するのを待つ
    wait -n
    
    # 最初に終了したプロセスのステータスを返す
    exit $?
    

    Dockerfile は以下のような記述とします。

    # syntax=docker/dockerfile:1
    FROM ubuntu:latest
    COPY my_first_process my_first_process
    COPY my_second_process my_second_process
    COPY my_wrapper_script.sh my_wrapper_script.sh
    CMD ./my_wrapper_script.sh
    
  • 1 つのメインプロセスを起動させたら、そのまま起動し続ける場合です。 一時的に別のプロセスをいくつか起動する(そしておそらくはメインプロセスと通信を行う)とします。 この場合は bash のジョブ制御の機能を利用します。 まずはラッパースクリプトを生成します。

    #!/bin/bash
    
    # ジョブ制御を有効にします。
    set -m
    
    # 1つめのプロセスをバックグラウンドで実行します。
    ./my_main_process &
    
    # ヘルパープロセスを実行します。
    ./my_helper_process
    
    # この my_helper_process は自分の処理を開始して終了するためには、
    # 1つめのプロセスの動きを知っておく必要があるかもしれません。
    
    
    # ここで1つめのプロセスをフォアグラウンド実行に戻してそのままとします。
    fg %1
    
    # syntax=docker/dockerfile:1
    FROM ubuntu:latest
    COPY my_main_process my_main_process
    COPY my_helper_process my_helper_process
    COPY my_wrapper_script.sh my_wrapper_script.sh
    CMD ./my_wrapper_script.sh
    
  • supervisordのようなプロセスマネージャーを利用する場合です。 これは少々面倒な方法です。 これを行うためには、イメージ内にsupervisordパッケージとその設定を含める必要があります。 (あるいはsupervisordが含まれているイメージをベースとします。) さらにそのパッケージが管理する別のアプリケーションが必要になってきます。 その上でsupervisordを起動させてプロセス管理を行います。 以下はこの手法を利用する Dockerfile です。 supervisord.confmy_first_processmy_second_processの各ファイルは準備ができていて、Dockerfile と同一ディレクトリに存在しているとします。

    # syntax=docker/dockerfile:1
    FROM ubuntu:latest
    RUN apt-get update && apt-get install -y supervisor
    RUN mkdir -p /var/log/supervisor
    COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
    COPY my_first_process my_first_process
    COPY my_second_process my_second_process
    CMD ["/usr/bin/supervisord"]
    
docker, supervisor, process management