Systemd: Ruby Daemon Supervision
Since Ubuntu has migrated from upstart to systemd in order to unify basic Linux service behaviors across all distributions we now have to deal with systemd as a default service manager / supervisor. In this article we will write a systemd supervisor service for a simple Ruby script as an example.
Systemd services location:
/lib/systemd/system/*.service
Logs location:
/var/log/syslog
It's even more convinient to fetch logs with a special journalctl
utility which reads a systemd
journal:
$ journalctl --since "2017-02-25 00:00:00"
Let's create a simple Ruby script which writes numbers in a logfile infinitely:
$ cd /home/username/
$ cat mydaemon.rb
$stdout.reopen("/home/username/mydaemon.log", "a")
$stdout.sync = true
loop.with_index do |_, i|
puts i
sleep(3)
end
And start it as a daemon:
$ ruby mydaemon.rb &
[1] *pid*
Checking logs to make sure the daemon is running as expected:
$ tail -f mydaemon.log
0
1
2
3
^C
And stop it:
$ kill *pid*
Now add a systemd service which will start this daemon automatically and restart it whenever it was stopped or crashed.
$ cat /lib/systemd/system/mydaemon.service
[Unit]
Description=Simple supervisor
[Service]
User=username
Group=username
WorkingDirectory=/home/username
Restart=always
ExecStart=/usr/bin/ruby mydaemon.rb
In contrary to upstart systemd does not start new services automatically. You can check its status with:
$ systemctl status mydaemon
● mydaemon.service - Simple supervisor
Loaded: loaded (/lib/systemd/system/mydaemon.service; static; vendor preset: enabled)
Active: inactive (dead)
And start it with:
$ systemctl start mydaemon
Now the status should show that mydaemon.rb has been started and running with a process ID assigned:
$ systemctl status mydaemon
● mydaemon.service - Simple supervisor
Loaded: loaded (/lib/systemd/system/mydaemon.service; static; vendor preset: enabled)
Active: active (running) since *date*
Main PID: *pid* (ruby)
Tasks: 2
Memory: 4.0M
CPU: 52ms
CGroup: /system.slice/mydaemon.service
└─*pid* /usr/bin/ruby mydaemon.rb
If you now try to kill the process — supervisor should immediately restart it:
$ kill *pid*
$ systemctl status mydaemon
● mydaemon.service - Simple supervisor
Loaded: loaded (/lib/systemd/system/mydaemon.service; static; vendor preset: enabled)
Active: active (running) since *date*
Main PID: *new_pid* (ruby)
Tasks: 2
Memory: 4.0M
CPU: 49ms
CGroup: /system.slice/mydaemon.service
└─*new_pid* /usr/bin/ruby mydaemon.rb
Bigger systemd service example:
[Unit]
Description=My description here
After=another.target
After=some.service
[Service]
Type=simple # forking/oneshot/dbus/notify/idle
PIDFile=/absolute/file/name
User=username
Group=groupname
WorkingDirectory=/home/username/some/dir
Restart=on-failure
Environment=ONE='one' "TWO='two two' too" THREE=
EnvironmentFile=/path/to/file/with/variables
ExecStartPre=/run/some/command
ExecStartPre=/run/some/other/command
ExecStart=/usr/bin/node /opt/webhook/server.js
ExecStartPost=/lastly/run/this
[Install]
WantedBy=multi-user.target
Restart options:
Upstart <-> Systemd commands cheatsheet: