2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rubyスクリプトのサービス化(systemd + rvm + ユーザ権限で実行)

Last updated at Posted at 2020-07-19

はじめに

Raspberry Pi 3でソフトUARTを利用 で作ったGPSログ用のスクリプトが思いのほか不安定なので、例外でプログラムが終了しても再起動するようにsystemd を使ってみます。

前提

  • rvm環境
  • ユーザ権限でrubyスクリプトをサービス化する
  • systemd を利用する

Goal

  • GPSログ用スクリプトが不慮のエラーで落ちても再起動すること

rvmの公式ドキュメントを見る

ググって公式の以下のページができたのですが、ざっくり説明すぎてよくわからなかったので手探りでやってみました。

なお、本手順はcronなどでも使えるはずです(試してないけど:sweat_drops:

手順

基本的には以下の手順となります。

  • 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.....

案の定エラーが出ますがきちんと以下のように作成できたのでよしとしましょう:sweat_smile:

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 が大切:relaxed:

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どっちが楽なんだろう?
  • 公式のドキュメントがざっくり過ぎてわからないとかあるんだな…
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?