Systemd: Difference between revisions

From Leo's Notes
This page was last edited on 16 November 2023, at 16:49.
No edit summary
 
(11 intermediate revisions by one other user not shown)
Line 1: Line 1:
==Runlevels vs Targets==
==Cheat sheet==
Unlike the old init system with different runlevels, Systemd defines sets of services to run inside targets.
{| class="wikitable"
!Task
!Command
|-
|list all failed units on the system
|<code>systemctl list-units --failed</code>
|-
|Return code denotes if a service is down
|<code>systemctl is-failed nginx.service</code>
|-
|show the status of a service/unit/timer
|<code>systemctl status ssh.service</code>
|-
|show a service in detail
|<code>systemctl show vboxweb.service</code>
|-
|kernel messages
|<code>journalctl -k</code><code>journalctl -ab</code>
|-
|follow messages
|<code>journalctl -f</code>
|-
|logs for a specific unit. Eg. SSH
|<code>journalctl -u ssh.service</code>
|-
|logs for a specific syslog facility. Eg. authpriv and auth logs:
|<code>journalctl -q SYSLOG_FACILITY=10 SYSLOG_FACILITY=4</code>
|-
|Analyze why things are slow
|<code>systemd-analyze blame</code>
<code>systemd-analyze critical-chain</code>
|}


Instead of using {{code|init 5}}:
==Switch and change targets (runlevel)==
In the traditional init system such as SystemV, whether a system boots into a graphical interface or to a text-based console is determined by the default runlevel. In systemd, the idea of runlevels has been replaced by targets. A target is simply a collection of units (services) that should start. Now, to change a system's 'runlevel', we need to use tell systemd to change its target.
 
Instead of using {{code|init 5}} to start a graphical system:
{{highlight|lang=terminal|code=
{{highlight|lang=terminal|code=
# systemctl isolate graphical.target
# systemctl isolate graphical.target
}}
}}


or {{code|init 3}}:
or {{code|init 3}} for a text-based system:
{{highlight|lang=terminal|code=
{{highlight|lang=terminal|code=
# systemctl isolate multi-user.target
# systemctl isolate multi-user.target
}}
}}


You can see the default target:
{{Highlight
| code = # systemctl get-default
graphical.target
| lang = terminal
}}
You can change the default target:
{{Highlight
| code = # systemctl set-default multi-user.target
# systemctl set-default graphical.target
| lang = terminal
}}


==Journal==
==Journal==
Line 82: Line 131:
|<code>*-*-* *:0/10:00</code>
|<code>*-*-* *:0/10:00</code>
|Every 10 minutes
|Every 10 minutes
|-
|<code>*-*-* *:00:00</code>
|Every hour
|-
|<code>*-*-* */6:00:00</code>
|Every 6 hours
|-
|-
|<code>*-*-* 00:15:30</code>
|<code>*-*-* 00:15:30</code>
|Every day at 12:15:30 AM
|Every day at 12:15:30 AM
|-
|<code>*-*-* 9-17:00:00</code>
|Every day between 9 AM and 5 PM each hour, on the hour
|-
|<code>* *-01,04,07,10-01 00:00:00</code>
|Every quarter (on the first of January, April, July, October) at 00:00:00
|-
|<code>* *-01-01 00:00:00</code>
|Every year (on the first of January) at 00:00:00
|-
|-
|<code>Weekly</code>, or <code>Mon *-*-* 00:00:00</code>
|<code>Weekly</code>, or <code>Mon *-*-* 00:00:00</code>
|Every Monday at 00:00:00
|Every Monday at 00:00:00
|-
|<code>Mon...Fri *-*-* 00:00:00</code>
|Every weekday at 00:00:00
|-
|-
|<code>Mon *-05~03</code>
|<code>Mon *-05~03</code>
Line 97: Line 164:


===Creating a Service===
===Creating a Service===
Suppose you have a program that you want to start when the system boots. The program when executed doesn't fork and runs in the foreground. To have systemd start the program as a service, create a new service file in the {{code|/etc/systemd/system}} directory containing something like:
Creating a service in systemd requires writing a .service file. The service file defines the service, any dependencies, and what to actually run. Service files are typically placed in <code>/etc/systemd/system/</code>. The most basic service file will look something like this:


{{highlight|lang=terminal|code=
{{highlight|lang=text|code=
# cat /etc/systemd/system/mag-read.service
[Unit]
[Unit]
Description=Mag card reader
Description=Run a script that does not fork


[Install]
[Install]
Line 109: Line 175:
[Service]
[Service]
Type=simple
Type=simple
ExecStart=/root/scripts/mag.sh
ExecStart=/root/scripts/foreground-script.sh
Restart=always
Restart=always
}}
}}


<code>Type=simple</code> is used for services that don't fork. <code>Restart=always</code> will make systemd automatically restart when the program exits.  
We are using <code>Type=simple</code> because the bash script we're executing doesn't fork or daemonize itself. For programs that do daemonize or run in the background, use <code>Type=oneshot</code>.  


Once the file has been created, reload systemd and enable the service:
Other options that may be of interest are listed below.
{{highlight|lang=terminal|code=
{| class="wikitable"
!options
!behavior
|-
|Restart=always
|restart when the program exits, regardless of the exit code.
|-
|User=bob
Group=bob
|Run the service with a particular user / group
|}
Once the file has been created, reload systemd and enable the service: {{highlight|lang=terminal|code=
## Reloads the service files so systemd is aware of the new one
## Reloads the service files so systemd is aware of the new one
# systemctl daemon-reload
# systemctl daemon-reload
Line 123: Line 200:
===SystemV-like Init Scripts===
===SystemV-like Init Scripts===


If you have custom packages that make use of the old SystemV startup scripts, you can use the following files to make use of such scripts.
Systemd supports the old SystemV startup scripts that are placed in <code>/etc/rc.local</code> and the <code>/etc/rc.d</code> directories using the <code>rc-local.service</code>.  


In {{code|/usr/lib/systemd/system/startup.service}}
If you need to quickly run something at startup without wanting to deal with Systemd services, just edit the <code>/etc/rc.local</code> file and enable the service with <code>systemctl enable rc-local.service</code>.
 
==== Custom service script ====
Alternatively, create a custom systemd service that runs your startup scripts. For example, create a file at {{code|/usr/lib/systemd/system/startup.service}} with the following:


{{highlight|lang=text|code=
{{highlight|lang=text|code=
Line 145: Line 225:
}}
}}


 
A few things of interest in this service file:
A few things of interest.


*The {{code|ConditionFileIsExecutable}} will only run the script if it's executable. It basically does a {{code|test -x}} on the file before this continues.
*The {{code|ConditionFileIsExecutable}} will only run the script if it's executable. It basically does a {{code|test -x}} on the file before this continues.
Line 184: Line 263:
}}
}}


{{Navbox Linux}}
===Editing a service===
You can create overrides to an existing service by running <code>systemctl edit something.service</code>.
 
For example, to make Docker start after ZFS is ready, add the following overrides:
{{Highlight
| code = # systemctl edit docker.service
After=zfs-mount.service
Requires=zfs-mount.service
Wants=zfs-mount.service
BindsTo=zfs-mount.service
| lang = terminal
}}This will create an <code>override.conf</code> file under <code>/etc/systemd/system/docker.service.d/override.conf</code>.
 
=== Run something in a cgroup ===
Systemd makes it easy to run something within a new cgroup. For example:
{{Highlight
| code = # Use up to 2GB memory and begin swapping if it exceeds this limit
$ systemd-run --user --pty --property MemoryHigh=2G firefox
 
# Use up to 2GB memory and kill with OOM if it exceeds this limit
$ systemd-run --user --pty --property MemoryMax=2G firefox
| lang = terminal
}}
<br />{{Navbox Linux}}
[[Category:Linux]]
[[Category:Linux]]
[[Category:LinuxUtilities]]
[[Category:LinuxUtilities]]

Latest revision as of 16:49, 16 November 2023

Cheat sheet

Task Command
list all failed units on the system systemctl list-units --failed
Return code denotes if a service is down systemctl is-failed nginx.service
show the status of a service/unit/timer systemctl status ssh.service
show a service in detail systemctl show vboxweb.service
kernel messages journalctl -kjournalctl -ab
follow messages journalctl -f
logs for a specific unit. Eg. SSH journalctl -u ssh.service
logs for a specific syslog facility. Eg. authpriv and auth logs: journalctl -q SYSLOG_FACILITY=10 SYSLOG_FACILITY=4
Analyze why things are slow systemd-analyze blame

systemd-analyze critical-chain

Switch and change targets (runlevel)

In the traditional init system such as SystemV, whether a system boots into a graphical interface or to a text-based console is determined by the default runlevel. In systemd, the idea of runlevels has been replaced by targets. A target is simply a collection of units (services) that should start. Now, to change a system's 'runlevel', we need to use tell systemd to change its target.

Instead of using init 5 to start a graphical system:

# systemctl isolate graphical.target

or init 3 for a text-based system:

# systemctl isolate multi-user.target

You can see the default target:

# systemctl get-default
graphical.target

You can change the default target:

# systemctl set-default multi-user.target
# systemctl set-default graphical.target

Journal

Systemd keeps tracks of service logs.

Clearing Journal

Clear all the entries by running:

# journalctl --vacuum-time=1seconds

Or

# journalctl --vacuum-size=1M

Limiting Log Sizes

Edit /etc/systemd/journald.conf and define a value for SystemMaxUse. Eg. SystemMaxUse=100M.


Tasks

Creating a Timer

You can use systemd to create a timer which can periodically run something, similar to Cronjobs.

A systemd timer can trigger a service. To make use of a systemd timer, you will need to create the service definition (that tells systemd how to run what you want to run) and then the timer definition itself (which tells Systemd when to run it, and how often).

For our service component, we would want a one-shot service that just executes a script. Create a service at /etc/systemd/system/gather-metrics.service:

[Unit]
Description=Logs system statistics to the systemd journal
Wants=gather-metrics.timer

[Service]
Type=oneshot
ExecStart=/bin/gather-script.sh

[Install]
WantedBy=multi-user.target

For our timer component, we want to run this every 10 minutes. Create a timer at /etc/systemd/system/gather-metrics.timer:

[Unit]
Description=Gathers metrics from this server
Requires=gather-metrics.service

[Timer]
Unit=gather-metrics.service
OnCalendar=*-*-* *:0/10:00

[Install]
WantedBy=timers.target

The OnCalendar specification takes the form of "DOW YYYY-MM-DD HH:MM:SS". An asterisk is a wildcard and matches on any value. The timer will trigger on the next date that matches this format.

Other useful specifications are:

OnCalendar The job will run...
*-*-* *:*:00 Every minute
*-*-* *:0/10:00 Every 10 minutes
*-*-* *:00:00 Every hour
*-*-* */6:00:00 Every 6 hours
*-*-* 00:15:30 Every day at 12:15:30 AM
*-*-* 9-17:00:00 Every day between 9 AM and 5 PM each hour, on the hour
* *-01,04,07,10-01 00:00:00 Every quarter (on the first of January, April, July, October) at 00:00:00
* *-01-01 00:00:00 Every year (on the first of January) at 00:00:00
Weekly, or Mon *-*-* 00:00:00 Every Monday at 00:00:00
Mon...Fri *-*-* 00:00:00 Every weekday at 00:00:00
Mon *-05~03 On the next Monday which is 3 days from the end of May
Mon..Fri *-08~04 On the next weekday which is 4 days from the end of August

Creating a Service

Creating a service in systemd requires writing a .service file. The service file defines the service, any dependencies, and what to actually run. Service files are typically placed in /etc/systemd/system/. The most basic service file will look something like this:

[Unit]
Description=Run a script that does not fork

[Install]
WantedBy=multi-user.target

[Service]
Type=simple
ExecStart=/root/scripts/foreground-script.sh
Restart=always

We are using Type=simple because the bash script we're executing doesn't fork or daemonize itself. For programs that do daemonize or run in the background, use Type=oneshot.

Other options that may be of interest are listed below.

options behavior
Restart=always restart when the program exits, regardless of the exit code.
User=bob

Group=bob

Run the service with a particular user / group

Once the file has been created, reload systemd and enable the service:

## Reloads the service files so systemd is aware of the new one
# systemctl daemon-reload
# systemctl enable mag-read

SystemV-like Init Scripts

Systemd supports the old SystemV startup scripts that are placed in /etc/rc.local and the /etc/rc.d directories using the rc-local.service.

If you need to quickly run something at startup without wanting to deal with Systemd services, just edit the /etc/rc.local file and enable the service with systemctl enable rc-local.service.

Custom service script

Alternatively, create a custom systemd service that runs your startup scripts. For example, create a file at /usr/lib/systemd/system/startup.service with the following:

[Unit]
Description=Custom SystemV-like Startup
After=network.target
ConditionFileIsExecutable=/usr/local/sbin/custom-startup-initd

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/custom-startup-initd
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99

[Install]
WantedBy=multi-user.target

A few things of interest in this service file:

  • The ConditionFileIsExecutable will only run the script if it's executable. It basically does a test -x on the file before this continues.
  • RemainAfterExit will cause systemd to show the service as active even after the script has finished execution.

In /usr/local/sbin/custom-startup-initd:

#!/bin/sh
StartupDir="/usr/local/boot/startup"

# Run all scripts starting with 'S'
for i in `ls $StartupDir/S*` ; do
        ScriptName=`basename $i`
        sh $StartupDir/$ScriptName
done

# Success exit code.
exit 0

Make the directory:

# mkdir -p /usr/local/boot/startup/

Then place your scripts in the startup directory starting with the letter 'S'.

Enable the new service by running:

# systemctl enable startup
# systemctl start startup

Verify this:

# systemctl status startup.service

Editing a service

You can create overrides to an existing service by running systemctl edit something.service.

For example, to make Docker start after ZFS is ready, add the following overrides:

# systemctl edit docker.service
After=zfs-mount.service 
Requires=zfs-mount.service 
Wants=zfs-mount.service 
BindsTo=zfs-mount.service

This will create an override.conf file under /etc/systemd/system/docker.service.d/override.conf.

Run something in a cgroup

Systemd makes it easy to run something within a new cgroup. For example:

# Use up to 2GB memory and begin swapping if it exceeds this limit
$ systemd-run --user --pty --property MemoryHigh=2G firefox

# Use up to 2GB memory and kill with OOM if it exceeds this limit
$ systemd-run --user --pty --property MemoryMax=2G firefox