コンテナ内で複数のプロセスを走らせたい
背景
コンテナ技術、Dcoker、K8Sは便利です。
しかし、フロント側で動いているプロセスのみ監視するため、複数プロセス動かしたい場合に色々と不便です。例えばK8Sなどで管理しようとした場合に、裏側のプロセスが1つ落ちたため、全てがうまく動かなくなってしまう事があります。
そこも含めて、実際にどんな方法があるかをここに記載しておきます。
例題
今回は、Pythonの処理を2つ実行するコンテナを作成するイメージで例を記載していきます。
main.py
main.py
は1秒に1回、1
という文字列を出力します。
import time
import sys
while 1:
print(1)
sys.stdout.flush()
time.sleep(1)
main2.py
main2.py
は2秒に1回、2
という文字列を出力します。
import time
import sys
while 1:
print(2)
sys.stdout.flush()
time.sleep(2)
Dockerfile
基本となるイメージはPythonを利用します。
/root/run.sh
などのファイルをコピーして、run.shの権限を変更し最後に実行するような形です。
from python:3
copy . /root/
run chmod +x /root/run.sh
cmd ["sh","-c","/root/run.sh"]
やり方
公式のドキュメントを確認したところ、下記の2種類の方法が推奨されているようです。
- バックグラウンド実行:
python3 main.py &
とかで、バックグラウンドで実行する。 - タスク管理ソフト利用:
supervisord
のようなタスク管理ソフトで管理する。
<参考>
1. バックグラウンド実行
この方法はあまりおすすめできません。
背景
で説明したとおり、裏側で実行されているタスクが起動中に終了した場合も検知できないためです。
#!/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
というソフトをインストールする処理を追加しました。
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つ起動するようにしています。
(自動起動・自動再起動有り)
#!/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