pythonプログラムのDaemon化

RaspberryPiで常時プログラムを走らせたい

ということで、python プログラムのサービス化を行いたい。 バグで死んでも、ゾンビのように再び復活させるように。 以前、一度やったことはあるのですが、全く記憶にないのでメモりつつ。

仕組みとしては、プロセスIDを管理して、プロセスIDが取れなければ、再び実行…という仕組み?のようです。

こちらを参考にして進めました。。。。。 というかそのまま。Python3系に変更した以外は、↑サイトの通りで行けました。

今回作ったPythonソースは、Python3.X用。 標準の実行環境が、Python2.X系なのかサービススタート時にエラー。

  • Python3系で実行するように変更

ソース1行目を以下のように変更

#!/usr/bin/env python
↓
#!/usr/bin/env python3

python3 がインストール済み、かつPATHが通ってる前提です。 ただの変更ですが、エディタの文字コードによって、改行とかが入るとエラーになるようです。 私は、ソースコードWindowsのSpyder(エンコード UTF-8)で編集しましたが、改行コードが入って改行コードを消さないとエラーになりました。

あと、常識なのかもしれませんが、1行目必須。コメントを頭に書きたかったので5行目くらいに書いてましたがエラー。。。

無駄に時間を費やしました。

  • Python内のメイン関数へ追記

実行ファイル「checkLog.py」に以下を追記

# ---------------------------------------------
# デーモン化
# ---------------------------------------------
def daemonize():
    
    pid = os.fork()
    if pid > 0:
        pid_file = open('/var/run/checkLog.pid','w')
        pid_file.write(str(pid)+"\n")
        pid_file.close()
        sys.exit()
        
    if pid == 0:
        main_loop()
    
    
# ---------------------------------------------
# メイン関数
# ---------------------------------------------
if __name__ == '__main__':
    
    
    if RUN_ENV == "Windows":
        main_loop()
        
    # RaspberryPiで実行するときは、デーモン化
    else:
        while True:
            daemonize()

注意すべきは、pid_file の名称。 後のサービス登録時に使いますのでわかりやすい名称に。

私の場合、メインの動作確認は、WIndowsで行って、実環境はRaspberryPiだったので、Windows環境では、デーモン化プロセスに入らないように「RUN_ENV」で分岐作ってます。 実行環境を、PATH?とかから取れるのかもですが、調べておらず、手動です。(いいやり方あったら教えてください)

  • サービスへの登録

定義ファイルを作成(私の場合、ディレクトリもなかったので作成から。。。) 以下コマンドで、ディレクトリ・ファイルを作成

mkdir /usr/lib/systemd/system/
cd /usr/lib/systemd/system/
vi /usr/lib/systemd/system/checkLog.service

checkLog.service ファイルの中身は以下、

[Unit]
Description=check_log

[Service]
ExecStart=[実行するpythonファイル]
Restart=always
Type=forking
PIDFile=[pid_file指定したパス]

[Install]
WantedBy=multi-user.target

ここのPIDFileが先ほどの、pid_fileのパスと一致させる。 ファイル作成後、サービスとして登録

systemctl daemon-reload
systemctl status checkLog.service

設定ファイルの checkLog.service の内容を更新したら都度、リロードが必要。 サービス起動後、以下でエラーがないか確認。

systemctl start checkLog

最後に、RaspberryPi立ち上がりと同時に起動するように設定を変更

systemctl enable checkLog.service

RaspberryPiを再起動して、動作を確認