systemd for Administrators, Part III
systemd吧
全部回复
仅看楼主
level 13
顶帖帝 楼主
How Do I Convert A SysV Init Script Into A systemd Service File?
Traditionally, Unix and Linux services (daemons) are startedvia SysV init scripts. These are Bourne Shell scripts, usuallyresiding in a directory such as /etc/rc.d/init.d/ which whencalled with one of a few standardized arguments (verbs) such asstart, stop or restart controls,i.e. starts, stops or restarts the service in question. For startsthis usually involves invoking the daemon binary, which then forks abackground process (more precisely daemonizes). Shell scriptstend to be slow, needlessly hard to read, very verbose andfragile. Although they are immensly flexible (after all, they are justcode) some things are very hard to do properly with shell scripts,such as ordering parallized execution, correctly supervising processesor just configuring execution contexts in all detail. systemd providescompatibility with these shell scripts, but due to the shortcomingspointed out it is recommended to install native systemd service filesfor all daemons installed. Also, in contrast to SysV init scriptswhich have to be adjusted to the distribution systemd service filesare compatible with any kind of distribution running systemd (whichbecome more and more these days...). What follows is a terse guide howto take a SysV init script and translate it into a native systemdservice file. Ideally, upstream projects should ship and installsystemd service files in their tarballs. If you have successfullyconverted a SysV script according to the guidelines it might hence bea good idea to submit the file as patch to upstream. How to prepare apatch like that will be discussed in a later installment, suffice tosay at this point that the daemon(7)manual page shipping with systemd contains a lot of useful informationregarding this.
So, let*s jump right in. As an example we*ll convert the initscript of the ABRT daemon into a systemd service file. ABRT is astandard component of every Fedora install, and is an acronym forAutomatic Bug Reporting Tool, which pretty much describes what itdoes, i.e. it is a service for collecting crash dumps. Its SysV script I have uploadedhere.
The first step when converting such a script is to read it(surprise surprise!) and distill the useful information from theusually pretty long script. In almost all cases the script consists ofmostly boilerplate code that is identical or at least very similar inall init scripts, and usually copied and pasted from one to theother. So, let*s extract the interesting information from the scriptlinked above:
A description string for the service is "Daemon to detectcrashing apps". As it turns out, the header comments include aredundant number of description strings, some of them describing lessthe actual service but the init script to start it. systemd servicesinclude a description too, and it should describe the service and notthe service file.
The LSB header[1] contains dependencyinformation. systemd due to its design around socket-based activationusually needs no (or very little) manually configureddependencies. (For details regarding socket activation see the originalannouncement blog post.) In this case the dependency on$syslog (which encodes that abrtd requires a syslog daemon),is the only valuable information. While the header lists anotherdependency ($local_fs) this one is redundant with systemd asnormal system services are always started with all local file systemsavailable.
The LSB header suggests that this service should be started inrunlevels 3 (multi-user) and 5 (graphical).
The daemon binary is /usr/sbin/abrtd
And that*s already it. The entire remaining content of this115-line shell script is simply boilerplate or otherwise redundantcode: code that deals with synchronizing and serializing startup(i.e. the code regarding lock files) or that outputs status messages(i.e. the code calling echo), or simply parsing of the verbs (i.e. thebig case block).
From the information extracted above we can now write our systemd service file:
[Unit]Description=Daemon to detect crashing appsAfter=syslog.target[Service]ExecStart=/usr/sbin/abrtdType=forking[Install]WantedBy=multi-user.target
A little explanation of the contents of this file: The[Unit] section contains generic information about theservice. systemd not only manages system services, but also devices,mount points, timer, and other components of the system. The genericterm for all these objects in systemd is a unit, and the[Unit] section encodes information about it that might beapplicable not only to services but also in to the other unit typessystemd maintains. In this case we set the following unit settings: weset the description string and configure that the daemon shall bestarted after Syslog[2], similar to what is encoded in theLSB header of the original init script. For this Syslog dependency wecreate a dependency of type After= on a systemd unitsyslog.target. The latter is a special target unit in systemdand is the standardized name to pull in a syslog implementation. Formore information about these standardized names see the systemd.special(7). Notethat a dependency of type After= only encodes the suggestedordering, but does not actually cause syslog to be started when abrtdis -- and this is exactly what we want, since abrtd actually worksfine even without syslog being around. However, if both are started(and usually they are) then the order in which they are is controlledwith this dependency.
The next section is [Service] which encodes informationabout the service itself. It contains all those settings that applyonly to services, and not the other kinds of units systemd maintains(mount points, devices, timers, ...). Two settings are used here:ExecStart= takes the path to the binary to execute when theservice shall be started up. And with Type= we configure howthe service notifies the init system that it finished starting up. Sincetraditional Unix daemons do this by returning to the parent processafter having forked off and initialized the background daemon we setthe type to forking here. That tells systemd to wait untilthe start-up binary returns and then consider the processes stillrunning afterwards the daemon processes.
The final section is [Install]. It encodes informationabout how the suggested installation should look like, i.e. underwhich circumstances and by which triggers the service shall bestarted. In this case we simply say that this service shall be startedwhen the multi-user.target unit is activated. This is aspecial unit (see above) that basically takes the role of the classicSysV Runlevel 3[3]. The setting WantedBy= haslittle effect on the daemon during runtime. It is only read by thesystemctl enable command, which is the recommended way toenable a service in systemd. This command will simply ensure that ourlittle service gets automatically activated as soon asmulti-user.target is requested, which it is on all normalboots[4].
And that*s it. Now we already have a minimal working systemdservice file. To test it we copy it to/etc/systemd/system/abrtd.service and invoke systemctldaemon-reload. This will make systemd take notice of it, and nowwe can start the service with it: systemctl startabrtd.service. We can verify the status via systemctl statusabrtd.service. And we can stop it again via systemctl stopabrtd.service. Finally, we can enable it, so that it is activatedby default on future boots with systemctl enableabrtd.service.
The service file above, while sufficient and basically a 1:1translation (feature- and otherwise) of the SysV init script still has room forimprovement. Here it is a little bit updated:
[Unit]Description=ABRT Automated Bug Reporting ToolAfter=syslog.target[Service]Type=dbusBusName=com.redhat.abrtExecStart=/usr/sbin/abrtd -d -s[Install]WantedBy=multi-user.target
So, what did we change? Two things: we improved the descriptionstring a bit. More importantly however, we changed the type of theservice to dbus and configured the D-Bus bus name of theservice. Why did we do this? As mentioned classic SysV servicesdaemonize after startup, which usually involves double forkingand detaching from any terminal. While this is useful and necessarywhen daemons are invoked via a script, this is unnecessary (and slow)as well as counterproductive when a proper process babysitter such assystemd is used. The reason for that is that the forked off daemonprocess usually has little relation to the original process started bysystemd (after all the daemonizing scheme*s whole idea is to removethis relation), and hence it is difficult for systemd to figure outafter the fork is finished which process belonging to the service isactually the main process and which processes might just beauxiliary. But that information is crucial to implement advancedbabysitting, i.e. supervising the process, automatic respawning onabnormal termination, collectig crash and exit code information andsuchlike. In order to make it easier for systemd to figure out themain process of the daemon we changed the service type todbus. The semantics of this service type are appropriate forall services that take a name on the D-Bus system bus as last step oftheir initialization[5]. ABRT is one of those. With this setting systemdwill spawn the ABRT process, which will no longer fork (this isconfigured via the -d -s switches to the daemon), and systemdwill consider the service fully started up as soon ascom.redhat.abrt appears on the bus. This way the processspawned by systemd is the main process of the daemon, systemd has areliable way to figure out when the daemon is fully started up andsystemd can easily supervise it.
And that*s all there is to it. We have a simple systemd servicefile now that encodes in 10 lines more information than the originalSysV init script encoded in 115. And even now there*s a lot of roomleft for further improvement utilizing more features systemdoffers. For example, we could set Restart=restart-always totell systemd to automatically restart this service when it dies. Or,we could use OOMScoreAdjust=-500 to ask the kernel to pleaseleave this process around when the OOM killer wreaks havoc. Or, wecould use CPUSchedulingPolicy=idle to ensure that abrtdprocesses crash dumps in background only, always allowing the kernelto give preference to whatever else might be running and needing CPUtime.
For more information about the configuration options mentionedhere, see the respective man pages systemd.unit(5),systemd.service(5),systemd.exec(5). Or,browse all ofsystemd*s man pages.
Of course, not all SysV scripts are as easy to convert as thisone. But gladly, as it turns out the vast majority actually are.
That*s it for today, come back soon for the next installment in our series.
2014年09月27日 14点09分 1
1