⛓️

コンテナ内で複数のプロセスを走らせたい

2021/12/19に公開

背景

コンテナ技術、Dcoker、K8Sは便利です。
しかし、フロント側で動いているプロセスのみ監視するため、複数プロセス動かしたい場合に色々と不便です。例えばK8Sなどで管理しようとした場合に、裏側のプロセスが1つ落ちたため、全てがうまく動かなくなってしまう事があります。
そこも含めて、実際にどんな方法があるかをここに記載しておきます。

例題

今回は、Pythonの処理を2つ実行するコンテナを作成するイメージで例を記載していきます。

main.py

main.pyは1秒に1回、1という文字列を出力します。

main.py
import time
import sys
while 1:
  print(1)
  sys.stdout.flush()
  time.sleep(1)

main2.py

main2.pyは2秒に1回、2という文字列を出力します。

main2.py
import time
import sys
while 1:
  print(2)
  sys.stdout.flush()
  time.sleep(2)

Dockerfile

基本となるイメージはPythonを利用します。
/root/run.shなどのファイルをコピーして、run.shの権限を変更し最後に実行するような形です。

Dockerfile
from python:3
copy . /root/
run chmod +x /root/run.sh
cmd ["sh","-c","/root/run.sh"]

やり方

公式のドキュメントを確認したところ、下記の2種類の方法が推奨されているようです。

  1. バックグラウンド実行:python3 main.py &とかで、バックグラウンドで実行する。
  2. タスク管理ソフト利用:supervisordのようなタスク管理ソフトで管理する。

<参考>
https://docs.docker.jp/config/container/multi-service_container.html

1. バックグラウンド実行

この方法はあまりおすすめできません。
背景で説明したとおり、裏側で実行されているタスクが起動中に終了した場合も検知できないためです。

run.sh
#!/bin/sh
echo "start run.sh"
python /root/main.py &
python /root/main2.py

ビルド・実行

下記のコマンドで、ビルドして実行する事ができます。

docker build -t mpro .
docker run -it mpro
>start run.sh
>1
>2
>1
>1
>2
>1

内部のタスクを止めてみる

内部のPythonを止めてみます。

# コンテナ名を取得します。
docker ps
>CONTAINER ID   IMAGE     COMMAND                CREATED         STATUS         PORTS     NAMES
>b0942b0a57d0   mpro      "sh -c /root/run.sh"   9 seconds ago   Up 7 seconds             wizardly_taussig
# 該当のコンテナ内にexeにて入ります。
docker exec -it wizardly_taussig bash
# コンテナ内のタスク一覧を取得します。
ps -a
>  PID TTY          TIME CMD
>    8 pts/0    00:00:00 run.sh
>    9 pts/0    00:00:00 python
>   10 pts/0    00:00:00 python
>   18 pts/1    00:00:00 ps
# プロセスID9番のものが古いと思われるため、これを停止させます。
kill -9 9
# プロセスID9番が停止されているか確認します。
ps -a
>  PID TTY          TIME CMD
>    8 pts/0    00:00:00 run.sh
>   10 pts/0    00:00:00 python
>   19 pts/1    00:00:00 ps

片方のPythonが停止していることが確認できました。
コンテナ自体は正常動作し続けている事がわかります。

2. タスク管理ソフト利用

タスク管理ソフトで設定をしてみます。
Dockerfileにはsupervisorというソフトをインストールする処理を追加しました。

Dockerfile
from python:3
copy . /root/
run apt update && apt -y install supervisor
run chmod +x /root/run.sh
cmd ["sh","-c","/root/run.sh"]

run.shは大幅に変更しています。
/etc/supervisor/conf.d/supervisord.confにスーパーバイザーの設定を記載しています。
具体的にはPythonそのソフトを2つ起動するようにしています。
(自動起動・自動再起動有り)

run.sh
#!/bin/sh
echo "start run.sh"

echo "
[supervisord]
nodaemon=true
[program:python1]
command=python /root/main.py 
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autostart=true
autorestart=true
[program:python2]
command=python /root/main2.py 
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autostart=true
autorestart=true
">/etc/supervisor/conf.d/supervisord.conf

/usr/bin/supervisord

ビルド・実行

下記のコマンドで、ビルドして実行する事ができます。

docker build -t mpro .
docker run -it mpro   
>start run.sh
>/usr/lib/python3/dist-packages/supervisor/options.py:474: UserWarning: Supervisord is running as root and it is searching for its configuration file in default locations (including its current working directory); you probably want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
>  self.warnings.warn(
>2021-12-19 08:46:36,402 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
>2021-12-19 08:46:36,402 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
>2021-12-19 08:46:36,418 INFO RPC interface 'supervisor' initialized
>2021-12-19 08:46:36,419 CRIT Server 'unix_http_server' running without any HTTP authentication checking
>2021-12-19 08:46:36,421 INFO supervisord started with pid 9
>2021-12-19 08:46:37,425 INFO spawned: 'python1' with pid 10
>2021-12-19 08:46:37,430 INFO spawned: 'python2' with pid 11
>1
>2
>1
>2021-12-19 08:46:38,494 INFO success: python1 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
>2021-12-19 08:46:38,495 INFO success: python2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

スーパーバイザーのログが色々出ているのがわかります。

内部のタスクを止めてみる

内部のPythonを止めてみます。

# コンテナ名を取得します。
docker ps
>CONTAINER ID   IMAGE          COMMAND                CREATED         STATUS         PORTS     NAMES
>a375a2920094   mpro           "sh -c /root/run.sh"   4 minutes ago   Up 4 minutes             happy_pasteur
>b0942b0a57d0   2acca65f73e3   "sh -c /root/run.sh"   2 hours ago     Up 2 hours               wizardly_taussig
# 該当のコンテナ内にexeにて入ります。
docker exec -it happy_pasteur bash
# コンテナ内のタスク一覧を取得します。
ps -aux
>USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
>root         1  0.0  0.0   2420   580 pts/0    Ss+  08:46   0:00 sh -c /root/run.sh
>root         8  0.0  0.0   2420   520 pts/0    S+   08:46   0:00 /bin/sh /root/run.sh
>root         9  0.1  1.1  29480 24220 pts/0    S+   08:46   0:00 /usr/bin/python3 /usr/bin/supervisord
>root        10  0.0  0.4  12896  9108 pts/0    S    08:46   0:00 python /root/main.py
>root        11  0.0  0.4  13016  9056 pts/0    S    08:46   0:00 python /root/main2.py
>root        12  0.3  0.1   5992  3820 pts/1    Ss   08:51   0:00 bash
>root        20  0.0  0.1   8592  3196 pts/1    R+   08:51   0:00 ps -aux
# プロセスID 11番がPythonであるため、これを停止させます。
kill -9 11
# プロセスID 11番が停止されているか確認します。
ps -aux
>USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
>root         1  0.0  0.0   2420   580 pts/0    Ss+  08:46   0:00 sh -c /root/run.sh
>root         8  0.0  0.0   2420   520 pts/0    S+   08:46   0:00 /bin/sh /root/run.sh
>root         9  0.1  1.1  29480 24284 pts/0    S+   08:46   0:00 /usr/bin/python3 /usr/bin/supervisord
>root        10  0.0  0.4  12896  9108 pts/0    S    08:46   0:00 python /root/main.py
>root        12  0.1  0.1   5992  3820 pts/1    Ss   08:51   0:00 bash
>root        21  0.3  0.3  10672  6680 pts/0    S    08:51   0:00 python /root/main2.py
>root        22  0.0  0.1   8592  3292 pts/1    R+   08:51   0:00 ps -aux
# プロセスID 21番に変わっています。再起動されていることがわかります。

タスクが終了しなくなったことがわかります。
ただし、再起動を施行しつづけるので、利用は十分に気をつけてください。
タスクを終了したタイミングで、Dockerのログには下記のような内容で再起動のログも残ります。

1
1
2021-12-19 08:51:43,681 INFO exited: python2 (terminated by SIGKILL; not expected)
1
2021-12-19 08:51:44,066 INFO spawned: 'python2' with pid 21
2
1
2021-12-19 08:51:45,071 INFO success: python2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)

これで、複数プロセスのコンテナもうまく作れるようになりました。

Discussion