概要
Pythonアプリのデーモンをシャットダウン時に安全に終了するためのメモ。
背景と目的
Pythonアプリのデーモンを、システムシャットダウン時に安全に終了するための基本的な実装方法を確認する。
詳細
0. 環境
1. 実装
Python標準モジュールのsignalを使うことで、システムシャットダウン時に、デーモンにSIGNALが送られたのを、アプリが受信できる。
以下、具体的な実装をメモ。
1.1 Pythonアプリ
- SIGTERMを受け取ったら、handlerという関数が呼ばれるようにする。
- SIGTERM受信したら、フラグを立てる。
- while ループでフラグを調べて、立っていたら抜ける。→アプリが終了
test_signal.py
# coding: utf-8 import signal import time x = { "end": False, "signum": None, "frame": None } def handler(signum, frame): x["end"] = True # 終了させる x["signum"] = signum x["frame"] = frame print("test_signal start!") signal.signal(signal.SIGTERM, handler) print("test_signal loop!") while True: time.sleep(1) if x["end"]: print("test_signal handler! {},{}".format(x["signum"], x["frame"])) break
1.2 呼び出し用シェルスクリプト
Pythonアプリを直接デーモンとして実行してもいいが、起動用シェルスクリプトをかませることが多いので、試しにシェルスクリプトから呼ぶ。
test_signal.sh
#!/bin/bash HERE=$(cd $(dirname $0); pwd) echo "test_signal sh start" python3 $HERE/test_signal.py echo "test_signal sh end"
1.3 サービスファイル
test_signal.shが呼ばれるようにしておく。
test_signal.service
[Unit] Description=test_signal After=multi-user.target [Service] ExecStart=/bin/bash /path/to/test_signal.sh Restart=on-failure Type=simple [Install] WantedBy=multi-user.target
配置してデーモン有効化、起動。
cp ./test_signal.service /etc/systemd/system systemctl daemon-reload systemctl enable test_signal systemctl start test_signal
2. 動作確認
2.1 手動で止めてみる
systemctl stop test_signal
ログを見ると、以下の感じでちゃんと止まった。 shから呼ばれたPythonアプリでも、SIGTERMを受信して、Pythonアプリで処理できている。
Mar 10 07:14:27 ****** bash[2048]: start! Mar 10 07:14:27 ****** bash[2048]: handler! 15,<frame at 0x7690fc30, file '/r Mar 10 07:14:27 ****** bash[2048]: terminate! Mar 10 07:14:27 ****** systemd[1]: Stopped test_signal Application.
2.2 システムシャットダウン
シャットダウンしてみた。再起動後、syslogを確認したところ、以下。ちゃんと止まった模様。 ただし、なぜかハンドラ関数が2回呼ばれる(SIGTERMを2回受信?)ようなので、念のため、ハンドラ関数内で2回実行されると困るものはやらないようにするほうがいいかもしれない。今回のように、メインループを抜けるためのフラグを立てるだけみたいな単純なものであれば問題ないだろう。
Mar 10 07:40:07 ********** systemd[1]: Started test_signal Application. Mar 10 07:40:07 ********** bash[2550]: test_signal sh start Mar 10 07:40:13 ********** systemd[1]: Stopping test_signal Application... Mar 10 07:40:14 ********** bash[2550]: test_signal start! Mar 10 07:40:14 ********** bash[2550]: test_signal loop! Mar 10 07:40:14 ********** bash[2550]: test_signal handler! 15,<frame at 0x769bbc30, file '/root/test/test_signal.py', line 28, code <module>> Mar 10 07:40:14 ********** bash[2550]: test_signal end! Mar 10 07:40:14 ********** systemd[1]: Stopped test_signal Application.
まとめと今後の課題
とりあえず、安全にデーモンを終了させられそう。