コンテナ内で複数のサービスを実行

コンテナでメインとして実行するプロセスは、 Dockerfile の最後に書かれている ENTRYPOINTCMD か、あるいは両方によって指定します。一般的に推奨するのは、1つのサービスごとにコンテナを使い、懸念事項の領域を分ける方法です。サービスは複数のプロセスにフォークする場合があります(たとえば、Apache ウェブサーバ派、複数のワーカ・プロセスと共に起動します)。複数のプロセスを持つ必要があるならば、1つのコンテナがアプリケーション全体における複数の役割を持ったとしても、Docker の利点から外れることがないようにする必要があります。ユーザ定義ネットワークと、共有ボリュームを使い、複数のコンテナ間を接続できます。

コンテナのメイン・プロセスは、コンテナが開始する全てのプロセスを管理する責任があります。いくつかの場合、メイン・プロセスが適切に設計されていない場合があり、その場合はコンテナが停止しても、子プロセスを丁寧に「reaping」(停止)できない可能性があります。この類のプロセス失敗に対しては、コンテナ実行時に --init オプションを使って対応できます。 --init フラグは、メインプロセスとして小さな初期化プロセスをとしてコンテナに挿入することができ、コンテナの終了時に全てのプロセスの終了を扱います。各プロセスを sysvinitupstartsystemd のようなスーパーバイザを使うのと同じ方法で、コンテナ内のプロセス・ライフライムを扱えるようにもできます。

コンテナ内で複数のサービスを実行する必要があれば、いくつかの異なった方法で達成できます。

  • ラッパー・スクリプト(wrapper script)で、テストやデバッグ情報も含む全てのコマンドを記述する方法です。このラッパー・スクリプトを CMD として指定します。これはとても良く使われる手法です。まず、以下がラッパー・スクリプトです。

    #!/bin/bash
    
    # Start the first process
    ./my_first_process -D
    status=$?
    if [ $status -ne 0 ]; then
      echo "Failed to start my_first_process: $status"
      exit $status
    fi
    
    # Start the second process
    ./my_second_process -D
    status=$?
    if [ $status -ne 0 ]; then
      echo "Failed to start my_second_process: $status"
      exit $status
    fi
    
    # Naive check runs checks once a minute to see if either of the processes exited.
    # This illustrates part of the heavy lifting you need to do if you want to run
    # more than one service in a container. The container exits with an error
    # if it detects that either of the processes has exited.
    # Otherwise it loops forever, waking up every 60 seconds
    
    while sleep 60; do
      ps aux |grep my_first_process |grep -q -v grep
      PROCESS_1_STATUS=$?
      ps aux |grep my_second_process |grep -q -v grep
      PROCESS_2_STATUS=$?
      # If the greps above find anything, they exit with 0 status
      # If they are not both 0, then something is wrong
      if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
       echo "One of the processes has already exited."
       exit 1
      fi
    done
    
  • 1つのメイン・プロセスは一番初めに起動して、実行し続ける必要があるものの、一時的に他のプロセス(メインプロセスとやりとしるする場合もあるでしょう)も実行する必要がある場合は、bash のジョブ・コントロールでこれらを調整できます。まずはラッパー・スクリプトです。

    #!/bin/bash
    
    # turn on bash's job control
    set -m
    
    # Start the primary process and put it in the background
    ./my_main_process &
    
    # Start the helper process
    ./my_helper_process
    
    # the my_helper_process might need to know how to wait on the
    # primary process to start before it does its work and returns
    
    
    # now we bring the primary process back into the foreground
    # and leave it there
    fg %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 を起動すると、あなたにかわってプロセスを管理します。以下の Dockerfile 例はこのアプローチを使ったもので、あらかじめ supervisord.confmy_first_processsmy_second_process ファイルを記述し、全てが Dockerifle と同じディレクトリにあるものと想定しています。

    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"]
    

参考

Run multiple services in a container
https://docs.docker.com/config/containers/multi-service_container/