はじめに
Raspberry Pi 3でソフトUARTを利用 で作ったGPSログ用のスクリプトが思いのほか不安定なので、例外でプログラムが終了しても再起動するようにsystemd
を使ってみます。
前提
- rvm環境
- ユーザ権限でrubyスクリプトをサービス化する
-
systemd
を利用する
Goal
- GPSログ用スクリプトが不慮のエラーで落ちても再起動すること
rvmの公式ドキュメントを見る
ググって公式の以下のページができたのですが、ざっくり説明すぎてよくわからなかったので手探りでやってみました。
なお、本手順はcronなどでも使えるはずです(試してないけど)
手順
基本的には以下の手順となります。
- rvmのwrapperを作る
- systemdのUnit定義ファイルを作る
- ExecStartでwrapperを介してスクリプトを実行する
rvmのwrapperを作る
まずはrmvのパスを確認します。
pi@raspberrypi:~ $ echo $rvm_path
/home/pi/.rvm
そのパス配下の wrappers
に新しいGemセット?を作成するので、念のために先に見ておきます。
pi@raspberrypi:~ $ ll ~/.rvm/wrappers/
total 0
lrwxrwxrwx 1 pi pi 33 Jul 18 16:22 default -> /home/pi/.rvm/wrappers/ruby-2.7.1
lrwxrwxrwx 1 pi pi 38 Jul 18 16:22 ruby-2.7.1 -> /home/pi/.rvm/gems/ruby-2.7.1/wrappers
lrwxrwxrwx 1 pi pi 45 Jul 18 16:22 ruby-2.7.1@global -> /home/pi/.rvm/gems/ruby-2.7.1@global/wrappers
以下のコマンドでGemセットを作成します。今回は gps
という名前で作成してみました。公式の記載に合わせたのですがなぜ gps
とアプリ名の指定が2個必要なのかわからず…
pi@raspberrypi:~ $ rvm alias create gps ruby-2.7.1@gps
Gemset 'gps' does not exist, 'rvm ruby-2.7.1 do rvm gemset create gps' first, or append '--create'.
Creating alias gps for ruby-2.7.1.....
案の定エラーが出ますがきちんと以下のように作成できたのでよしとしましょう
pi@raspberrypi:~ $ ll ~/.rvm/wrappers/
total 0
lrwxrwxrwx 1 pi pi 33 Jul 18 16:22 default -> /home/pi/.rvm/wrappers/ruby-2.7.1
lrwxrwxrwx 1 pi pi 33 Jul 19 20:01 gps -> /home/pi/.rvm/wrappers/ruby-2.7.1
lrwxrwxrwx 1 pi pi 38 Jul 18 16:22 ruby-2.7.1 -> /home/pi/.rvm/gems/ruby-2.7.1/wrappers
lrwxrwxrwx 1 pi pi 45 Jul 18 16:22 ruby-2.7.1@global -> /home/pi/.rvm/gems/ruby-2.7.1@global/wrappers
systemdのUnit定義ファイルを作る
あとは通常通り作成するだけです。今回の内容は以下の通り。 WorkingDirectory
でスクリプトのあるディレクトリを指定し、 ExecStart
でwrapperを介してbundleコマンドを実行しています。
Restart=always
が大切
pi@raspberrypi:~ $ cat /etc/systemd/system/gps.service
[Unit]
Description=Gps Service
After=network.target
[Service]
Type=simple
User=pi
Group=pi
WorkingDirectory=/home/pi/project/gps
ExecStart=/home/pi/.rvm/wrappers/gps/bundle exe ruby main.rb
Restart=always
[Install]
WantedBy=multi-user.target
スクリプトを少し改造する
やっつけ作業でloggerと例外で落ちる処理を入れてみました。
require 'serialport'
require 'nmea_plus'
require 'logger'
logger = Logger.new('gps.log')
logger.info '起動したよ'
puts `sudo rmmod soft_uart.ko`
sleep 0.1
puts `cd ~/project/soft_uart && sudo insmod soft_uart.ko`
sp = SerialPort.new('/dev/ttySOFT0', 9600, 8, 1, 0) # see: https://rubydoc.info/gems/serialport/SerialPort#set_modem_params-instance_method
trap 'SIGINT' do
sp.close if sp
exit
end
count = 0
source_decorder = NMEAPlus::SourceDecoder.new(sp)
source_decorder.each_complete_message do |message|
# カウントアップ
count +=1
# see: https://github.com/ianfixes/nmea_plus/blob/master/lib/nmea_plus/message/nmea/rmc.rb
if 'GPRMC' == message.data_type
logger.info message.utc_time
logger.info message.active? # false: データ無効
logger.info message.latitude
logger.info message.longitude
logger.info message.faa_mode # A: 単独測位(精度3m程度), D: 相対測位(精度0.4m程度)
end
if count >= 10
sp.close # とりあえず閉じてみる
logger.error '落とすよ'
raise '強引なエラー'
end
end
確認してみる
まずはsystemd自体をリロードしてからサービスを起動します。
sudo systemctl daemon-reload
sudo systemctl start gps.service
ログは…いい感じですね!
これで不安定なスクリプトがちょっとは強い子になりました。
pi@raspberrypi:~ $ tail -f ./project/gps/gps.log
I, [2020-07-19T20:59:05.521956 #6437] INFO -- : 起動したよ
I, [2020-07-19T20:59:06.651867 #6437] INFO -- : 2020-07-19 11:59:06 +0000
I, [2020-07-19T20:59:06.652419 #6437] INFO -- : true
I, [2020-07-19T20:59:06.652937 #6437] INFO -- : 33.725631666666665
I, [2020-07-19T20:59:06.653450 #6437] INFO -- : 131.64383833333332
I, [2020-07-19T20:59:06.654003 #6437] INFO -- : A
I, [2020-07-19T20:59:07.652485 #6437] INFO -- : 2020-07-19 11:59:07 +0000
I, [2020-07-19T20:59:07.653032 #6437] INFO -- : true
I, [2020-07-19T20:59:07.653515 #6437] INFO -- : 33.725631666666665
I, [2020-07-19T20:59:07.654019 #6437] INFO -- : 131.64384
I, [2020-07-19T20:59:07.654624 #6437] INFO -- : A
E, [2020-07-19T20:59:07.694013 #6437] ERROR -- : 落とすよ
I, [2020-07-19T20:59:12.530348 #6460] INFO -- : 起動したよ
I, [2020-07-19T20:59:13.660674 #6460] INFO -- : 2020-07-19 11:59:13 +0000
I, [2020-07-19T20:59:13.661174 #6460] INFO -- : true
I, [2020-07-19T20:59:13.661502 #6460] INFO -- : 33.72563
I, [2020-07-19T20:59:13.661792 #6460] INFO -- : 131.64383666666666
I, [2020-07-19T20:59:13.662027 #6460] INFO -- : A
I, [2020-07-19T20:59:14.660078 #6460] INFO -- : 2020-07-19 11:59:14 +0000
I, [2020-07-19T20:59:14.660613 #6460] INFO -- : true
I, [2020-07-19T20:59:14.660962 #6460] INFO -- : 33.72562833333333
I, [2020-07-19T20:59:14.661288 #6460] INFO -- : 131.64383666666666
I, [2020-07-19T20:59:14.661528 #6460] INFO -- : A
E, [2020-07-19T20:59:14.662166 #6460] ERROR -- : 落とすよ
感想
- スクリプトのサービス化はrbenvとrvmどっちが楽なんだろう?
- 公式のドキュメントがざっくり過ぎてわからないとかあるんだな…