Raspberry Piで自動起動する方法
参考の記事によると大きく5つがあるようです。
本記事はsystemedを使った自動起動を試行し
print関数の内容表示やstop時に例外処理を実行する方法も併せてメモしておきます。
- /etc/rc.local
- autostart ←別の記事
- crontab @reboot
- /etc/init.d
- systemd ←今回の記事
紹介内容
- RaspberryPiを起動した際に、プログラムをsystemedで自動起動させる。
- print関数の内容を表示
- stopコマンド時に例外処理実行
1.自動起動の手段
systemdが主流、推奨のようなのでsystemdで実装。
ココのサイトの手順で実行。
[Unit]
Description=Play sound effects at startup
After=network.target
[Service]
ExecStart=/usr/bin/python3 start_app.py
WorkingDirectory=/home/pi/myapp
[Install]
WantedBy=multi-user.target
2.応用編
上記のファイルを追記、改変して
- print関数の内容をstatusで表示。 →
.service
に記載 - stop時に例外処理を実行。 →
.py
に記載
(リソースの開放などができず、再度起動した際にエラーになることを防ぐため) - 実行条件の指定
マウントが完了した後に実行など
をやってみます。
追加したsystemファイルは下記のようになります。
[Unit]
Description = A sample unit file.
After=local-fs.target #ローカルファイルシステムへのマウントが完了した後に実行
ConditionPathExists=/opt/RasPiSample/bin
[Service]
ExecStart=/opt/RasPiSample/bin/autoexec.sh
Environment=PYTHONUNBUFFERED=1 #print関数の内容をログに残す
Restart=no
Type=simple
StandardOutput=journal+console #コンソールにも出力を表示させる
[Install]
WantedBy=multi-user.target
解説をしていきます。
1.print関数の内容をログに残す
[Service]セクションに以下の行を追加すると print 関数の内容をログに残すことができます。
[Service]
Environment=PYTHONUNBUFFERED=1
※ systemdで実行中のPythonのprint関数の出力をログに出力させてみた
2.stopコマンドを検知し、例外処理を実行
リファクタリングはしてません。
Ctl+C で.pyプログラムを終了できるコード例
import subprocess
import time
import signal
class TerminatedExecption(Exception):
pass
def raise_exception(*_):
raise TerminatedExecption()
def end_process():
try:
print('--- start end process ---')
time.sleep(2)
except Exception as e:
raise
print('--- success end process ---')
if __name__ == '__main__':
# set signal handler to detect to be stopped by systemd
signal.signal(signal.SIGTERM, raise_exception)
print('--- start main process ---')
subprocess.run(['aplay', 'start.wav'])
try:
print('--- start main loop ---')
count=0
while True:
count+=1
print('count= ',count)
time.sleep(1)
# (1) if ctrl-C is pushed, stop program nomally
except KeyboardInterrupt:
print("KeyboardI]nterrupt: stopped by keyboard input (ctrl-C)")
time.sleep(1)
pass
# (2) if stopped by systemd, stop program nomally
except TerminatedExecption:
print("TerminatedExecption: stopped by systemd")
time.sleep(1)
pass
end_process()
main関数の最初で、SIGTERMを受信した際の動作を、raise_exception関数を呼び、TerminatedExecptionをraiseするように変更。
下記サイトがわかりやすくまとめられていました。
※【systemd】Pythonでstopを検知する&例外時に自動で再起動する
3.実行条件の指定
3.1 ローカルファイルシステムへのマウントが完了した後に実行
通常, 組込Linuxの場合は, サードパーティーのプログラムを導入して自動起動させるはずですので, /opt/パッケージ名のディレクトリは, 存在することを期待して良いと考えられますが, 念のため, ローカルファイルシステムへのマウントが完了した後に実行されることを保証するため, [unit]内に下記を追記します。
[unit]
After=local-fs.target
ローカルファイルシステムへのマウントが完了した後に実行されることを保証する
After=には, 以下のような実行条件を記載することが出来ます.
-
local-fs.target
装置に直接繋がったストレージのマウントが完了した後で実行する -
network-online.target
ネットワークを利用可能になった後で実行する -
remote-fs.target
ネットワークファイルシステム(NFSなど)を利用可能になった後で実行する -
nss-lookup.target
DNSなどのホスト/ネットワークサービスを利用可能になった後で実行する
上記の場合, local-fs.targetの完了後, つまり, 装置に直接繋がったストレージのマウントが完了した後でプログラムを実行する指示になります.
3.2 ファイルシステムのマウントに失敗した場合に正しくエラーを返す
ファイルシステムのマウントに何らかの理由で失敗した場合に正しくエラーを返せるようにする
[unit]
ConditionPathExists=/opt/RasPiSample/bin
3.3 systemdにコンソールにも出力を表示させるように指示する方法
下記を記載することでコンソール出力とSystemD内のログの双方にprint文の内容が出力されます.
[Service]
StandardOutput=syslog+console
4.serviceに実行権限を与える
必要に応じてPython のコードに対して実行権限を与えます。
sudo chmod 755 /opt/***.py
その後、systemd で Python のコードを実行してみます。
sudo systemctl daemon-reload
sudo systemctl start ***.service
systemd 実行後に
jornalctl -u ***.service
を実行してログの中にHello Worldと表示されれば成功です。
5.サービスの確認
sudo systemctl enable start_app.service
sudo systemctl disable start_app.service
sudo systemctl status start_app.service
参考情報
既にある.service
ファイルを編集する場合
cd /etc/systemd/system
sudo vim start_app.service
定刻でのプログラム自動起動
1.timerが始動していることを確認
sudo systemctl status myscript.timer
2.タイマーの一覧と設定状況
sudo systemctl list-timers
上で起動した****.timerの次回実行時刻、前回実行からの経過時間などが確認できます。
- 実行ログの確認
journalctl -e
systemdのログ全般は journalctl コマンドを使用します。
4.無効化
sudo systemctl disable ****.timer
※ Raspberry Pi で systemd を使ってプログラムを自動実行する
※ Systemdのtimer設定チートシート(コマンドまとめ)
以上
参考サイト
- クラゲのブログ systemdを使ってスクリプト自動起動
- systemdを用いたプログラムの自動起動
-
Raspberry Pi で systemd を使ってプログラムを自動実行する
- 定刻でのプログラム自動起動
- pythonスクリプトをdaemonにする[systemd編]
- Systemdのtimer設定チートシート(コマンドまとめ)
- systemdで実行中のPythonのprint関数の出力をログに出力させてみた
- 【systemd】Pythonでstopを検知する&例外時に自動で再起動する
- 【systemd】プログラム自動起動の設定時にハマったこと(ユーザ指定・Pythonパス指定・ウィンドウ表示)
- systemdとsystemctlコマンド:サービスの自動起動、停止、再起動