Introduction to systemd in SLES12

As discussed in the previous issue of Open Horizons Magazine, SLES 12 brings about some major changes. Some of these you will never notice unless you go looking for them, but others will change the way that you perform even the most basic administration task!

The first of these major changes is the move away from the venerable SysV init daemon. Yes, that's right, all the stuff that you've learned over the years about runlevels, scripts in /etc/init.d and the symlinks in rc3.d are a thing of the past (well, mostly, but we'll come back to that later). What do we have instead? The answer is systemd, a shiny new (to SLES) daemon that does everything that init did, but better, and a whole lot more besides.

So, where do we start with systemd? Well, we'll start with a quote from my good friend Richard Brown, now chairman of the opensuse board! He describes systemd as a "seven stage initialisation daemon". These stages are as follows:

  • Denial
  • Pain
  • Anger
  • Depression
  • Working Through
  • Acceptance
  • Excitement

Now, obviously, this list is a little tongue in cheek, but there's also some truth in it. What I want to do is to move you through these stages as quickly as possible to get to the good bits.

Compatibility with init (Denial)

To ease the transition from init to systemd, SUSE have provided a number of backwardly compatible commands that you can use if you really don't want to leave the init world. These commands do the same job as the ones that you've been using for years, but don't necessarily work in exactly the same way!

Some examples are

rcapache2 stop/start/status

service apache2 stop/start/status

reboot

shutdown

While these commands have familiar syntax, their behaviour is now to do the systemd equivalent of the original command. One thing to watch out for in particular is stop/start. These no longer give any status information to say whether the requested action actually happened! Instead, you're now going to have to run a status command as well to see what happened and, of course, the status output is completely different, so any kind of scripting that relies on these commands will need to be modified.

What do we take from this? Yes, you've got it - time to move on and learn the new commands to understand what's really happening under the hood.

So, how does this newfangled thingummy work? (Pain, Anger, Depression)

At this point, it's best if you just unlearn everything you knew about init and start with a blank brain. Well, not blank, but you get the idea. Let's start with the basics.

Systemd is based on the idea of units. A unit is a configuration file that defines something. I'm not being deliberately vague here, it's just that this is such a big change that it's going to take a bit of getting used to. Remember in Linux, everything is a file? Well in systemd, everything is a unit. A file system mounted from fstab is mounted by a unit; a TTY text console is started by a unit; a service is started by a unit; the equivalent of a runlevel (a target in system land) is defined by a unit.

At the most basic level, a unit is a configuration file (not a script) that defines how the unit should act. For simplicity, I'm only going to deal with services and targets (runlevels) in this article. Unit files have file extensions that define the type of unit, e.g. apache2.service, poweroff.target. When referring to these units in systemd commands, you can drop the .service part for simplicity. Inside these configuration files, the syntax harks back to the .ini style that you'll be familiar with if you've ever looked at a Samba configuration file. The basic structure is:

[Section]

variable = value

variable = value

Let's look at the unit file for the ntpd time daemon service:

[Unit]

Description=NTP Server Daemon

Documentation=man:ntpd(1)

After=nss-lookup.target

After=network.target

Wants=network.target

[Service]

Type=forking

PIDFile=/var/run/ntp/ntpd.pid

ExecStart=/usr/sbin/start-ntpd start

RestartSec=11min

Restart=always

[Install]

WantedBy=multi-user.target

You can see that the [Unit] section deals with information about the service being defined. There’s info on the service itself, and also variables that define what is required by this service (Wants), and ordering clues (After). The [Service] section deals with starting/stopping the service, as well as options that specify how to restart the service if it crashes! Lastly, the [Install] section specifies where in the configuration ntpd should be installed.

That deals with services, but what about runlevels? In systemd, targets replace runlevels. They do the same job but work very differently. In SysV init, each runlevel was defined in its own directory by creating symlinks to the actual scripts, prepended with S/K and a number used for ordering. A target is defined very differently. In the systemd world, a target is a defined state that the system should be in, rather than a list of things to be done in the hope of achieving a state.

This leaves systemd free to make decisions on how to get to there, and it may make decisions that surprise you. At this point I will give you some advice: don't fight it - you will almost certainly lose and end up battered and bruised! systemd is designed to get you to the system state that you've requested, don't worry about the nitty gritty details of how it gets there - it usually does the right thing... As with some of the commands above, SUSE have created some targets with familiar names to give you something to cling on to, but you've already deleted all SysV knowledge haven't you?? The main targets defined in SLES 12 are shown in Table 1

How is a target defined then? There is a basic configuration file e.g. multiuser.target:

[Unit]

Description=Multi-User System

Documentation=man:systemd.special(7)

Requires=basic.target

Conflicts=rescue.service rescue.target

After=basic.target rescue.service rescue.target

AllowIsolate=yes

This file doesn't really show much about which services should run though. Services are attached to a target either within their unit file, as seen with the install line in the ntpd unit file above, or using a symlink in the wants directory:

linux-u1kg:~ # ls /usr/lib/systemd/system/multi-user.target.wants/

after-local.service plymouth-quit-wait.service

dbus.service systemd-logind.service

getty.target systemd-update-utmp-runlevel.service

plymouth-quit.service systemd-user-sessions.service

The main thing to notice here is that each target only defines a very small part of the system. Unlike init, there will be many targets running at the same time, each doing their part:

linux-u1kg:~ # systemctl list-units -t target

UNIT                     LOAD   ACTIVE SUB    DESCRIPTION

basic.target             loaded active active Basic System

bluetooth.target         loaded active active Bluetooth

cryptsetup.target        loaded active active Encrypted Volumes

getty.target             loaded active active Login Prompts

graphical.target         loaded active active Graphical Interface

local-fs-pre.target      loaded active active Local File Systems (Pre)

local-fs.target          loaded active active Local File Systems

multi-user.target        loaded active active Multi-User System

network-online.target    loaded active active Network is Online

network.target           loaded active active Network

nss-lookup.target        loaded active active Host and Network Name Lookups

nss-user-lookup.target   loaded active active User and Group Name Lookups

paths.target             loaded active active Paths

remote-fs-pre.target     loaded active active Remote File Systems (Pre)

remote-fs.target         loaded active active Remote File Systems

slices.target            loaded active active Slices

sockets.target           loaded active active Sockets

sound.target             loaded active active Sound Card

swap.target              loaded active active Swap

sysinit.target           loaded active active System Initialization

time-sync.target         loaded active active System Time Synchronized

timers.target            loaded active active Timers

 

Where is everything and how do I use it (acceptance, finding your way)

There are two main directories used by systemd; /usr/lib/systemd/system and /etc/systemd/system. All unit files are defined in these directories. There is a very important rule to follow here, and it's very very simple: never touch anything in /usr/lib/systemd/system. These files will be overwritten by patches/service packs etc. If you want to make changes, you should use copies of the unit files in /etc/systemd/system. I'll cover that in the next section. Just a quick note before I move on: systemd also has a SysV init compatibility layer built in - if your third party backup software doesn't have a systemd unit file shipped along with it, don't worry, systemd will also look in /etc/init.d for service scripts.

Now that you have an idea how systemd works, I'd better show you the commands to use to interact with it. The main command that you'll use on a daily basis is systemctl. It will let you start, stop and check the status of services:

linux-u1kg:~ # systemctl stop ntpd

linux-u1kg:~ # systemctl start ntpd

linux-u1kg:~ # systemctl status ntpd

ntpd.service - NTP Server Daemon

Loaded: loaded (/usr/lib/systemd/system/ntpd.service; disabled)

Active: active (running) since Thu 2014-10-16 18:00:13 CEST; 5s ago

Docs: man:ntpd(1)

Process: 2497 ExecStart=/usr/sbin/start-ntpd start (code=exited, status=0/SUCCESS)

Main PID: 2510 (ntpd)

CGroup: /system.slice/ntpd.service

└─2510 /usr/sbin/ntpd -p /var/run/ntp/ntpd.pid -g -u ntp:ntp -i /v...

Oct 16 18:00:13 linux-u1kg ntpd[2510]: proto: precision = 0.201 usec

Oct 16 18:00:13 linux-u1kg ntpd[2510]: ntp_io: estimated max descriptors: 1...16

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listen and drop on 0 v4wildcard 0.0....23

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listen and drop on 1 v6wildcard :: U...23

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listen normally on 2 lo 127.0.0.1 UDP 123

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listen normally on 3 eth0 172.16.17....23

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listen normally on 4 lo ::1 UDP 123

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listen normally on 5 eth0 fe80::20c:...23

Oct 16 18:00:13 linux-u1kg ntpd[2510]: peers refreshed

Oct 16 18:00:13 linux-u1kg ntpd[2510]: Listening on routing socket on fd #2...es

As you can see, the systemctl command is a properly quiet Linux command; it doesn't give you much feedback unless you get the syntax wrong. You can see however that the status check is significantly more verbose than the SysV equivalent. As well as the basic status info, it also shows the unit file that was loaded to start the service, process ID/control group info and the most recent log messages that the daemon generated.

You can control the desired state of a service using systemctl:

systemctl enable ntpd

This sets the service to start automatically. To disable it use:

systemctl disable ntpd

However, a word of warning here; disabling a service does not stop it from running, it only sets it not to start automatically - if a service that depends on ntpd is started, then ntpd will be started as well! If you really want to stop a service from starting at all, use

systemctl mask ntpd

Bear in mind though that this can have repercussions if you try to start another service that needs ntpd.

To affect the overall state of the server, you're going to need to use target commands. Now, it's tempting to use:

systemctl start multi-user.target

However, this may not quite achieve what you want. If you're running the graphical target, then the multi-user target is also running, and the above command will have no effect at all! The command that you probably meant was

systemctl isolate mult-user.target

This tells systemd to stop any services that are not required by the multi-user target, i.e the graphical desktop.

Lastly, you might need to shut down or reboot the server. Try:

systemctl reboot

systemctl poweroff

The next stage (Working through, acceptance, Excitement)

Now that we know what the basics of systemd look like, it's time to show you a couple of really cool features. The first of these is the ability to modify unit files in a completely safe way, well two ways really!

Let's assume we'd like to change the behaviour of the ntpd service. Remember I said that you never modify anything in /usr/lib/systemd/system? Well all you need to do is to make a copy of the unit file into /etc:

cp /usr/lib/systemd/system/ntpd.service /etc/systemd/system

You can now go ahead and modify anything you like in that copy of the unit file. Systemd will use it in preference to the /usr/lib/systemd/system version. You can change any/all settings in there, and systemd will use it.

However, by doing this, you're overriding the whole unit file. What happens if a patch fixes something in the /usr/lib version? Well, those changes will be ignored, and your copy will always be used. There is a better way of course. Most of the time, we only want to change a couple of settings, and there's a much cleaner way of doing this.

systemd has the concept of drop-in overrides. Rather than redefining a unit, you can create a config file that overrides one or more settings. So, let's assume that we'd like ntp to be restarted quickly if it crashes. The default setting is to restart it after 11 minutes - that's a long time!

To create a drop-in override, you must first create a directory that matches the name of the unit file with .d appended:

mkdir /etc/systemd/system/ntpd.service.d

You can now create any number of config files in there, the only restriction is that they must end with .conf. To solve the slow ntp restart issue, create a file in this directory with the name fast-restart.conf with only the following lines:

[Service]

RestartSec = 10s

Once that's saved, we need to tell systemd that we've created it:

systemctl daemon-reload

Or stop and start the service.

Now when we look at the status of ntpd, we'll see that it has picked up our new file:

linux-u1kg:~ # systemctl status ntpd

ntpd.service - NTP Server Daemon

Loaded: loaded (/usr/lib/systemd/system/ntpd.service; disabled)

Drop-In: /etc/systemd/system/ntpd.service.d

└─fast-restart.conf

I hope that you can see the flexibility of drop-in overrides - no more wondering why a patch didn't fix what it was supposed to, and no hunting for .rpmnew files and trying to merge your changes back in!

The final cool features that we're going to investigate in this article allow us to analyse the boot process to get some idea of what happened.

To find out if any services failed during boot try:

systemctl --failed

This sure beats trolling through /var/log/boot.msg and /var/log/messages to see what happened!

The last three commands for this article let you look at the timings of what happened during boot.

Firstly

linux-u1kg:~ # systemd-analyze critical-chain

The time after the unit is active or started is printed after the "@" character.

The time the unit takes to start is printed after the "+" character.

graphical.target @9.735s

└─multi-user.target @9.735s

  └─cron.service @9.735s

     └─postfix.service @9.444s +290ms

       └─nss-lookup.target @9.442s

         └─network-online.target @9.442s

           └─network.target @9.440s

             └─wicked.service @2.452s +6.987s

               └─wickedd-nanny.service @2.413s +37ms

                 └─wickedd.service @2.360s +52ms

                   └─wickedd-dhcp4.service @2.189s +169ms

                     └─dbus.service @2.024s

                       └─basic.target @2.023s

                         └─timers.target @2.023s

                           └─systemd-tmpfiles-clean.timer @2.023s

                             └─sysinit.target @2.022s

                               └─apparmor.service @1.733s +289ms

                                 └─systemd-tmpfiles-setup.service @1.716s +16ms

                                   └─local-fs.target @1.715s

                                     └─boot-efi.mount @908ms +131ms

                                       └─dev-disk-by\x2duuid-FB80\x2dB57A.device

Secondly, let's name and shame the services that took up too much time

linux-u1kg:~ # systemd-analyze blame | head

6.987s wicked.service

2.006s kdump.service

1.348s systemd-udev-settle.service

1.013s display-manager.service

403ms rsyslog.service

297ms accounts-daemon.service

290ms postfix.service

Ah, wicked.service obviously sloped off for a quick smoke while everything else was going on.

Lastly, no article is complete without a fancy graphic (figure 1). To generate a timeline for the boot use:

linux-u1kg:~ # systemd-analyze plot > boot.svg

I hope that this has given you a good overview of systemd. There's a whole lot more to investigate of course so keep your eyes peeled for future deep dives on systemd.

Boot graphic created via systemd
Boot graphic created via systemd

27-sles12-boot-end-700x450

Leave a Reply