チカラの技術

電子工作やプログラミング

Raspberry PiでプログラムをOS起動時に実行させる [Systemd]

こんにちは、元気です!

本記事は指定のプログラムをRaspberry Pi 起動時に自動実行する方法の解説になります。

私の環境: Raspberry Pi 3B , Raspbian version 9.4 (CUI)

自動起動の方法としては/etc/rc.localを用いるシンプルな方法がありますが古い方法で、かつRaspbianの環境によっては動かない事があったため、今回はSystemdを使う方法を説明します。

起動時に実行するターゲットのプログラムは「/home/project/get-ryme/」ディレクトリに作成した「get-ryme.js」とします。 (適宜、自分のプログラムに読み替えてください)

以下の2通りの起動時実行方法を解説します。

  1. 実行のみ行う、シンプルな起動時実行

  2. 起動したプログラムがエラーでクラッシュしたとき自動で再起動してくれるNode.js用ライブラリ、【forever】を利用した起動時実行

1.シンプルな起動時実行

1-1. スクリプトファイルの作成

まず、起動時に実行するスクリプトファイル[**.sh]を作成します。

sudo nano /opt/get-ryme-simple.sh

内容を記述します。

#!/bin/sh
sudo node /home/project/get-ryme/get-ryme.js
sudo iwconfig wlan0 power off

1行目の#!/bin/shスクリプトインタプリタがshであることをOSに通知しています。
2行目で自動起動したいプログラムを実行します。
3行目の「iwconfig wlan0 power off」はwifiの省電力モードをオフにしています。
 これはget-ryme.jsはプログラム中でインターネット通信を行っていて、省電力機能によるラグや切断を避けるためです。
(ちなみにスクリプトファイル内のコマンドはsudoを省略してもOS起動時にはroot権限で実行されます)

作成したスクリプトに実行権限を与えます。

sudo chmod a+x /opt/get-ryme-simple.sh
1-2. Unitファイル(サービス設定)の作成

次に/etc/systemd/systemディレクトリにサービス設定ファイル[**.service]を作成します。

sudo nano /etc/systemd/system/get-ryme-simple.service

内容を記述します。

[Unit]
Description = simple service
After=network.target

[Service]
Type=simple
ExecStart= /opt/get-ryme-simple.sh

[Install]
WantedBy = multi-user.target

上述の通り、今回はインターネットに接続するプログラムのため 「After=network.target」を指定してインターネット接続が行われるまでサービスの起動を待機させます。
「Type=」は起動方法の指定で今回はsimpleです。(シンプルではない起動は2章で解説)
「ExecStart=」に上記のスクリプトのパスを指定します。
「WantedBy=」はサービスの実行環境を設定します。
今回はCUIのRasbianですのでmulti-user.targetに設定していますが、
GUI環境(X Window System)の場合はgraphical.targetを指定するそうです。

1-3. サービスの確認、登録

ファイルの準備が出来たため、サービスが動くか確認します。
起動

sudo systemctl start get-ryme-simple

動作状態確認

sudo systemctl status get-ryme-simple

以下の画像のようにActive:activeとなっていれば起動は成功です。
f:id:powerOfTech:20180823151423p:plain 最後に起動時実行サービスとして登録します。

sudo systemctl enable get-ryme-simple

以上の手順で次回以降OS起動時に指定のプログラムが実行されるはずです。

2.foreverを用いたNode.jsプログラムの起動時実行

Node.js用ライブラリ、foreverでstartさせたプログラムはエラーによるクラッシュ時に自動で再起動されます。
以下の手順でOSの起動時にforever startされるようにします。

まずforeverをgrobalインストールします。

sudo npm install -g forever
2-1. スクリプトファイルの作成

スクリプトファイル[**.sh]を作成します。

sudo nano /opt/get-ryme-forever.sh

内容を記述します。

#!/bin/sh
sudo forever start /home/project/get-ryme/get-ryme.js
sudo iwconfig wlan0 power off

2行目でforever startでNode.jsプログラムを起動させるようにしています。

作成したスクリプトに実行権限を与えます。

sudo chmod a+x /opt/get-ryme-forever.sh
2-2. Unitファイル(サービス設定)の作成

サービス設定ファイル[**.service]を作成します。

sudo nano /etc/systemd/system/get-ryme-forever.service

内容を記述します。

[Unit]
Description = forever service
After=network.target

[Service]
Type=forking
ExecStart= /opt/get-ryme-forever.sh

[Install]
WantedBy = multi-user.target

「Type=」はforkingに設定しています。foreverはフォークで子プロセスを生成し、対象のNode.jsのプログラムを実行するためです。

2-3. サービスの確認、登録

前章と同じように動作確認をした後、サービスを登録します。

sudo systemctl enable get-ryme-forever

OSの再起動後、以下のコマンドでforever上で動作していることを確認しましょう。

sudo forever list

※sudoを省略すると(起動したrootと実行者が違うので)listに表示されないので注意してください。
f:id:powerOfTech:20180823151512p:plain log fileのパスにコンソールに出力されたログが保存されています。

トラブルシュート

うまく起動しない場合、まず、スクリプトファイル[ **.sh ]が正常に動作しているか確認しましょう。

./opt/get-ryme-simple.sh

問題なければサービスも実行して確認しましょう。

sudo systemctl start get-ryme-simple
sudo systemctl status get-ryme-simple

通常の実行(node /home/project/get-ryme/get-ryme.js)で動くにも関わらず、シェルスクリプトの実行やサービスで動かない場合は以下の可能性も検討してみてください。

  • 実行時のカレントディレクトリの違い  →Node.jsコードの中でファイルやプログラムの指定に相対パスを使ってる部分が無いか確認する。(require("")はパス解決されています。)絶対パスを指定するときは__dirnameを使うと便利です。

  • 実行権限(user→root)の違い  →例えばpuppeteerはroot権限で実行する場合、以下のようにargs:オプションの指定が必要になります。

puppeteer.launch({
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });

参考させて頂いたサイト

より詳しくsystemdの設定を解説されてます。 qiita.com

サービスが起動しない場合のログの確認方法です。 qiita.com

foreverについてオプションも含め解説されています。 qiita.com