runit ============================================================================ (January 2017) runit is a multi-platform init system. runit's process sueprvisor can be used on top of a non-runit init. Things that attracted me to runit: - People who use it in production say good things about it. - It offers the features one expects in a modern init system, and does it in a unixy way. - Busybox includes runit, which means a lot of little things will eventually use runit. `runit-init` replaces `/sbin/init`. The command `init 0` tells runit to halt the system. `init 6 reboots it. The `runsvdir` and `runsvchdir` programs handle runlevels. Service dependencies are resolved automatically. runit reads service configurations from subdirectories of `/service/`. runit also keeps state information (e.g. the service PID) in the `/service/` subdirectories. (Note: on some systems, `/service` might be `/etc/service` or even `/var/service`.) Several binaries make up runit: - `sv` controls and manages services monitored by `runsv`. - `runsv` starts and monitors a service and optionally an appendant log service. - `runsvdir` starts and monitors a collection of `runsv` processes. - `runsvchdir` changes the services directory of `runsvdir` (i.e. switches the "runlevel"). - `svlogd` is runit’s service logging daemon. - `chpst` runs a program with a changed process state (e.g. set the user id of a program, or renice a program) - `utmpset` modifies the user accounting database utmp to indicate that the user on the terminal line logged out. Init ---------------------------------------------------------------------------- When acting as the system init, runit has three stages: 1. runit starts `/etc/runit/1` and waits for it to terminate. This handle one-time system initialization tasks. 2. runit starts `/etc/runit/2`. This keeps running (if all goes well) until system reboot or halt. If stage two crashes, it restarts. 3. When stage two returns without errors (at system halt or reboot), `/etc/runit/3` runs tasks to cleanly bring down the machine. Each of these three stages may be _very_ simple shell scripts. Here the sample of `1` from Debian Sarge: #!/bin/sh # system one time tasks PATH=/command:/sbin:/bin:/usr/sbin:/usr/bin /etc/init.d/rcS /etc/init.d/rmnologin touch /etc/runit/stopit chmod 0 /etc/runit/stopit Here the sample of `2` from Debian Sarge: #!/bin/sh PATH=/command:/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin exec env - PATH=$PATH \ runsvdir -P /etc/service 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................' Here the sample of `2` from Debian Sarge: #!/bin/sh exec 2>&1 PATH=/command:/sbin:/bin:/usr/sbin:/usr/bin LAST=0 test -x /etc/runit/reboot && LAST=6 echo 'Waiting for services to stop...' sv -w196 force-stop /etc/service/* sv exit /etc/service/* echo 'Shutdown...' /etc/init.d/rc $LAST Not Init ---------------------------------------------------------------------------- runit can act as a pure process supervisor, started by another init system. The init system simply runs stage two of runit, typically by running `runsvdir` as a service. On SysV Init, for example, add an entry for `/sbin/runsvdir-start` to `/etc/inittab`. runit will need the `2` script copied to `/sbin/runsvdir-start` and the `/service` directory created: # install -m0750 /package/admin/runit/etc/2 /sbin/runsvdir-start # mkdir -p /service Supervise a Process ---------------------------------------------------------------------------- First, create a new service directory. Second, create a `run` script in the new service directory, and make it executable. # mkdir -p /etc/sv/myservice/ # touch /etc/sv/myservice/run # chmod a+x /etc/sv/myservice/run For use with runit, service daemon should not background themselves. In the `run` script, use `exec` to have the script replace itself with the newly run daemon. Here's an example `run` script to start `getty`: #!/bin/sh exec getty 38400 tty2 linux To tell runit about the new service, and run it on boot, create a link in the `/service/` directory. runnit should notice the next time it checks `/service/` (about every five seconds). # ln -s /etc/sv/myservice /service/ That's pretty much it. runnit can handle dependencies. Just have the service's `run` file check if the dependency is running yet using `sv`: #!/bin/sh sv start my-earlier-service || exit 1 exec my-later-service To add an appendant log, create a `/log/` subdirectory with another `run` script. # mkdir -p /etc/sv/myservice/log # touch /etc/sv/myservice/log/run # chmod a+x /etc/sv/myservice/log/run The `run` script should run a logging daemon, like: #!/bin/sh exec chpst -ulog svlogd -tt ./main Service Control ---------------------------------------------------------------------------- # sv hup myservice # sv status myservice # sv up myservice # sv down myservice [The notes below are slightly older, and contain some repetition of the above.] Process Supervision ---------------------------------------------------------------------------- Each service has a **service directory**. Each service runs as a child of a supervising `runsv` process that operates out of the service directory. This obviates the need to guess about service PID's or to write pidfiles. # sv up /service/myservice # sv status /service/myservice # sv down /service/myservice # sv restart /service/myservice # sv hup /service/myservice The `/service/` directory holds symlinked directories for each of the processes currently enabled. The symlinks point to `/etc/sv/`, which contains directories with configs for enabled and disabled services. (This is a bit like Apache2's `/etc/sites-enabled/` and `/etc/sites-available/`.) Each service directory contains a `run` file --- a simple shell script to start the process, like: #!/bin/sh exec /usr/local/sbin/exim -bdf -q30m See example `run` scripts at http://smarden.org/runit/runscripts.html. ## Service dependencies and ordering ## The `run` scripts handle this. If a `run` script returns non-zero, runit will try it again later. So, here's a `run` script for "bar" that makes sure "foo" has already started: #!/bin/sh sv start foo || exit 1 exec bar --myoption Runlevels ---------------------------------------------------------------------------- http://smarden.org/runit/runlevels.html Runlevels correspond to directories in `/etc/runit/runsvdir/`. Switching runlevels is done by switching the directory the runsvdir program is running in with `runsvchdir`: # runsvchdir single # runsvchdir default Create a custom runlevel by making a new directory in /etc/runit/runsvdir/`, and symlinking the directories of the services we want into that directory. LXC, busybox, and runit ---------------------------------------------------------------------------- New versions of busybox include runit. This makes busybox attractive as an LXC container template. Unfortunately, the version of busybox shipped with Debian Stretch is too old to include runit. Here's how to use a newer busybox to make a runit lxc container on Debian. - Download the latest busybox binary from https://busybox.net/downloads/binaries/. - Create an lxc container like `sudo lxc-create -n mybusybox -t busybox`. - Copy the new binary to the container, like `sudo cp ~/Download/busybox-x86_64 /var/lib/lxc/mybusybox/rootfs/bin/busybox`. - Create runit directories in the container, like: ``` # mkdir -p /etc/sv /etc/service /etc/runit/runsvdir # ln -s /etc/service /service ``` - Create symlinks in the container's `/bin/` for `chpst`, `runsv`, `runsvdir`, `sv`, and `svlogd`, like: ``` # busybox --list | awk '/runsv|chpst|svlog|^sv$/' | xargs -I{} ln -sv /bin/busybox /bin/{} ``` - Create (and make executable 750) the shell script `/sbin/runsvdir-start`: ``` #!/bin/sh PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin exec env - PATH=$PATH \ /bin/runsvdir /service 'log: ....................................................................................' ``` - Modify `/etc/initab`: ``` # cat >>/etc/inittab <&1 exec chpst -u foo /usr/local/bin/foo ``` Create a log directory (`/var/log/foo`). Create a `log` script, like: ``` #!/bin/sh exec chpst -u foo svlogd -tt /var/log/foo ``` ``` # sv status foo ``` Links ---------------------------------------------------------------------------- - http://smarden.org/runit/benefits.html - https://debian-administration.org/article/697/Using_runit_for_maintaining_services - https://wiki.archlinux.org/index.php/runit - http://smarden.org/runit/useinit.html - http://smarden.org/runit/replaceinit.html - https://webcache.googleusercontent.com/search?q=cache:046_INICmE8J:https://evasive.ru/50a3904206c52447aa1fa5d90a8382a3.html+&cd=1&hl=en&ct=clnk&gl=us - https://wiki.gentoo.org/wiki/Runit#OpenRC_supervision_service - http://www.mikeperham.com/2014/07/07/use-runit/ - https://kchard.github.io/runit-quickstart/