一、导读
2016年08月19日 Upstart 将被放弃,Ubuntu 投入 Systemd 怀抱
Canonical 的 Martin Pitt 宣布将不再使用他们自己的 Upstart 初始化系统来启动 Ubuntu 桌面会话,取而代之的是更现代化的、却仍有争议的 Systemd。
每次 Systemd 发布,我们都对发现这个所谓的“初始化系统”又做了比原来的设计目标还要多得多的工作。它慢慢地接管了 GNU/Linux 操作系统越来越多的内部组件的工作,甚至,我们毫不怀疑,它将会完全取代它们,而这一天并不远了,或许,将来你会看到 Systemd/Linux 操作系统——除了 Linux 内核,其它的都叫 Systemd。
Upstart 是 Canonical/Ubuntu 自己的项目,它同 Systemd 一样,目标都是取代传统的初始化系统,用在几乎所有的 Ubuntu Linux 上。然而,从 Ubuntu 15.04 开始,Ubuntu 开始逐步使用 Systemd 替代 Upstart 初始化系统,这让许多用户很愤怒。
争议归争议,然而是好是坏,从我这几天慢慢对systemd的接触,发现其确实有其魅力之处。
二、手把手带你写unit
2.1 bb service依赖aa service
下面,我带大家创建两个service类型unit,aa和bb,其中bb的启动依赖aa,即,如果你将bb启动,aa自然跟着启动,如果,此时将A关闭,B也会随之关闭。这个主要是要在B的unit文件中配置一个参数:Requires=aa.service
创建unit文件要点
- service unit最重要的是service区块,而且ExecStart参数很重要,这个参数写上你这个service要执行的脚本的路径
- ExecStart=/usr/libexec/aa 这个表示执行这个unit,需要执行的脚本。切记,chmod +x /usr/libexec/aa 加可执行权限
- type类型的选择,根据自己的需求,man 5 systemd.unit看区别
创建一个aa service
#vim /etc/systemd/system/aa.service [Unit] Description=aa After=syslog.target network.target nss-lookup.target Before=time-sync.target Wants=time-sync.target [Service] Type=simple ExecStart=/usr/libexec/aa RemainAfterExit=yes [Install] WantedBy=multi-user.target
#vim /usr/libexec/aa #!/bin/bash for i in `seq 1 100`;do DATE=`date` echo $DATE >> /var/log/aa sleep 0.01 done
创建一个bb service
#vim /etc/systemd/system/bb.service [Unit] Description=bb After=syslog.target network.target nss-lookup.target Before=time-sync.target Wants=time-sync.target Requires=aa.service [Service] Type=simple ExecStart=/usr/libexec/bb RemainAfterExit=yes [Install] WantedBy=multi-user.target
#vim /usr/libexec/bb #!/bin/bash DATE=`date` echo $DATE >> /var/log/bb
验证
当前aa bb都是stop的状态
#systemctl status bb aa
只启动bb
#systemctl start bb
发现aa bb都启动了,这是因为bb的启动依赖aa
#systemctl status bb aa ● bb.service - bb Loaded: loaded (/etc/systemd/system/bb.service; disabled; vendor preset: disabled) Active: active (exited) since Thu 2016-08-18 23:51:04 CST; 5s ago Process: 39662 ExecStart=/usr/libexec/bb (code=exited, status=0/SUCCESS) Main PID: 39662 (code=exited, status=0/SUCCESS) Aug 18 23:51:04 localhost systemd[1]: Started bb. Aug 18 23:51:04 localhost systemd[1]: Starting bb... ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; enabled; vendor preset: disabled) Active: active (exited) since Thu 2016-08-18 23:51:05 CST; 4s ago Process: 39663 ExecStart=/usr/libexec/aa (code=exited, status=0/SUCCESS) Main PID: 39663 (code=exited, status=0/SUCCESS) Aug 18 23:51:04 localhost systemd[1]: Starting aa... Aug 18 23:51:05 localhost systemd[1]: Started aa.
然后看看执行结果
#tail /var/log/aa Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016 Thu Aug 18 23:51:05 CST 2016
#tail /var/log/bb Thu Aug 18 17:35:03 CST 2016 Thu Aug 18 17:37:19 CST 2016 Thu Aug 18 17:40:06 CST 2016 Thu Aug 18 17:41:18 CST 2016 Thu Aug 18 23:49:02 CST 2016 Thu Aug 18 23:49:21 CST 2016 Thu Aug 18 23:51:04 CST 2016
三、几个牛逼的命令
查看aa unit文件
#systemctl cat aa
编辑aa unit文件
推荐使用这个 #systemctl edit --full aa
不推荐使用这个,这个是分片的方式改unit文件 #systemctl edit aa
其实systemctl status很强大
其实#systemctl status aa 查看一个unit的状态,这个命令的输出很强大,一开始,大家都不觉得,因为看不懂这个输出。什么exit,什么active,什么enable,实在是不知道这个unit到底是几个意思?
栗子输出
#systemctl status aa ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; enabled; vendor preset: disabled) Active: active (exited) since Thu 2016-08-18 23:51:05 CST; 7min ago Main PID: 39663 (code=exited, status=0/SUCCESS) Aug 18 23:51:04 localhost systemd[1]: Starting aa... Aug 18 23:51:05 localhost systemd[1]: Started aa.
#systemctl status aa ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; enabled; vendor preset: disabled) #loaded表示unit文件已经被systemd识别,并且加载到内存中; Active: active (exited) since Thu 2016-08-18 23:51:05 CST; 7min ago Main PID: 39663 (code=exited, status=0/SUCCESS) # exited表示,这个ExecStart后面的脚本已经执行结束,并且有返回值,后面有SUCCESS表示,这个脚本结束,没有问题。 #code 的值,可以有很多种,根据unit的类型输出; Aug 18 23:51:04 localhost systemd[1]: Starting aa... Aug 18 23:51:05 localhost systemd[1]: Started aa.
我分不清systemctl list-units 和systemctl list-unit-files
也许最让你迷惑的是
#systemctl list-units 显示所有active状态的unit文件 #systemctl list-units --all 显示所有包含inactive状态的unit文件 #systemctl list-unit-files 显示所有unit文件,不care systemd到底有没有关联到这些unit文件
- load: 这里的loaded表示unit文件被systemd识别,并且加载到内存中了,一般这个都是loaded,除非这个unit文件不存在;
- ACTIVE:基本可以根据这个判断
- SUB:可以认为输出是unit文件中ExecStart所指定的脚本或者二进制命令当前的状态,exit表示脚本执行完了,running表示或者命令还在执行
#systemctl list-units UNIT LOAD ACTIVE SUB DESCRIPTION rsyslog.service loaded active running System Logging Service sshd.service loaded active running OpenSSH server daemon ● staragentctl.service loaded failed failed SYSV: Staragent is a standard Monitoring UNIX program for xxxbaba syslog-ng.service loaded active running System Logger Daemon sysstat.service loaded active exited Resets System Activity Logs systemd-fsck-root.service loaded active exited File System Check on Root Device systemd-fsck@dev-disk-by\x2dlabel-\x5cx2fboot.service loaded active exited File System Check on /dev/disk/by-label/\x2fboot systemd-journal-flush.service loaded active exited Flush Journal to Persistent Storage systemd-journald.service loaded active running Journal Service systemd-logind.service loaded active running Login Service systemd-random-seed.service loaded active exited Load/Save Random Seed
这个输出,最需要注意的是static,经过测试,我对static的理解是:static表示一个service类型的unit文件中,没有Install区块,意味着,这个unit文件不能执行systemctl enable a
#systemctl list-unit-files syslog-ng.service enabled rsyncd.service disabled sshd-keygen.service static
比如:
#systemctl edit --full aa
#systemctl cat aa # /etc/systemd/system/aa.service [Unit] Description=aa After=syslog.target network.target nss-lookup.target Before=time-sync.target Wants=time-sync.target [Service] Type=oneshot ExecStart=/usr/libexec/aa RemainAfterExit=yes
#systemctl daemon-reload
#systemctl restart aa
#systemctl list-unit-files | grep aa aa.service static
staic文件最让人疑惑的一点是,现在A是static,B是enabled,然后B还是依赖A,现在A,B都是stop的状态下,启动B,A的/usr/libexec/aa 这个unit执行脚本或者命令会照常执行!!A虽然是static,但是static文件存在的意义之一,就是为了解决别人的依赖。
systemd难以琢磨的依赖关系&systend对static unit行为方式
有人问我rc-local.service这个service在7u上有被其他的东西依赖吗?其实,他还想问rc-local.service中的ExecStart是否会被执行?因为他知道rc-local.service 是一个static类型的unit文件,所以他很迷惑,迷惑的是systemd中staic文件的行为方式究竟是怎样的?我这个问题大家可以自己先分析一下答案;
下面看我的思路:
如果你看完我后面测试,你应该得出如下理解:
- 务必学会判断一个service类型的unit文件是的状态是static,enable,disable?
- 务必知道static的unit其实就是没有[Install]区块,但是[Unit] [Service]区块肯定还是有的。
- 如果A,B两个unit,A和B都是enable的unit的话,reboot后,A,B的ExecStart肯定会被执行;
- 如果A是static,B是enable,B After A,但是B中没有说明B Requires A或者B Wants A,那么A中的[Service]区块中的ExecStart是否执行,取决与A自己;
比如rc-local.service,rc-local.service是一个staic的unit,注意看这个unit文件的原生注释,# This unit gets pulled automatically into multi-user.target by systemd-rc-local-generator if /etc/rc.d/rc.local is executable. 所以rc-local.service 中的ExecStart=/etc/rc.d/rc.local start 肯定会执行!因为,有systemd-rc-local-generator将 rc-local.service推到了multi-user.target中!!
#systemctl cat rc-local.service # /usr/lib/systemd/system/rc-local.service # This file is part of systemd. # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # This unit gets pulled automatically into multi-user.target by # systemd-rc-local-generator if /etc/rc.d/rc.local is executable. [Unit] Description=/etc/rc.d/rc.local Compatibility ConditionFileIsExecutable=/etc/rc.d/rc.local After=network.target [Service] Type=forking ExecStart=/etc/rc.d/rc.local start TimeoutSec=0 RemainAfterExit=yes
可是我还是不服,于是,我自己写了2个unit aa bb,虽然我在aa的unit中写了ConditionFileIsExecutable=/usr/libexec/aa ,并且确保 /usr/libexec/aa 可执行,但是,当我 reboot后,ExecStart=/usr/libexec/aa start还是没有执行!也许systemd unit的行为方式令我们费解,但是,只有不断自己去测试,我们才能摸清,它的行为方式;
static文件aa
[root@localhost /home/ahao.mah] #systemctl cat aa # /etc/systemd/system/aa.service [Unit] Description=aa ConditionFileIsExecutable=/usr/libexec/aa Before=bb.service [Service] Type=simple ExecStart=/usr/libexec/aa start RemainAfterExit=yes
enable文件bb
[root@localhost /home/ahao.mah] #systemctl cat bb # /etc/systemd/system/bb.service [Unit] Description=bb After=syslog.target network.target nss-lookup.target aa.service [Service] Type=simple ExecStart=/usr/libexec/bb RemainAfterExit=yes [Install] WantedBy=multi-user.target
重启
[root@localhost /home/ahao.mah] #systemctl reboot
aa unit没有启动
[root@localhost /home/ahao.mah] #systemctl status aa ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; static; vendor preset: disabled) Active: inactive (dead)
bb unit已经启动
[root@localhost /home/ahao.mah] #systemctl status bb ● bb.service - bb Loaded: loaded (/etc/systemd/system/bb.service; enabled; vendor preset: disabled) Active: active (exited) since Fri 2016-08-19 10:52:15 CST; 2min 36s ago Process: 1046 ExecStart=/usr/libexec/bb (code=exited, status=0/SUCCESS) Main PID: 1046 (code=exited, status=0/SUCCESS) CGroup: /system.slice/bb.service Aug 19 10:52:15 localhost.localdomain systemd[1]: Started bb.
启动aa unit
#systemctl start aa
启动aa unit成功
[root@localhost /home/ahao.mah] #systemctl status aa ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; static; vendor preset: disabled) Active: active (exited) since Fri 2016-08-19 10:54:51 CST; 2s ago Process: 3837 ExecStart=/usr/libexec/aa start (code=exited, status=0/SUCCESS) Main PID: 3837 (code=exited, status=0/SUCCESS) Aug 19 10:54:51 localhost systemd[1]: Started aa. Aug 19 10:54:51 localhost systemd[1]: Starting aa...
怎么判断有哪些unit依赖我
如果你初接触systemd,那么你很快会从网络上知道,有下面这三种方法去判断有什么依赖一个unit
- 我用#systemctl list-dependencies aa.service 去判断
- 我用#systemctl cat rc-local.service 去判断
- 我在/usr/lib/systemd目录下,用#grep -nR ‘rc-local.service’ ./* 去判断
- 你以为第一种方法可以输出那些依赖aa.service的unit吗?答案是,这个输出并不是那么好看!
systemctl list-dependencies aa.service --after 输出的是依赖aa.service 和unit中有After=aa.service 的所有unit文件 systemctl list-dependencies aa.service --before 输出unit中 有Before=aa.service 的所有unit文件
- systemctl cat rc-local.service看不到什么依赖它
- grep -nR ‘rc-local.service’ /usr/lib/systemd/* 你发现所有的unit中都没有写,自己Requirs或者Wants rc-local.service ,而是,大家都是Afer rc-local.service,After 不意味着依赖它,仅仅是,如果rc-local.service 这个unit要启动,那么我会在rc-local.service启动后,我再启动;
#grep -nR 'rc-local.service' /usr/lib/systemd/* /usr/lib/systemd/system/autovt@.service:13:After=rc-local.service /usr/lib/systemd/system/plymouth-quit-wait.service:3:After=rc-local.service plymouth-start.service systemd-user-sessions.service grep: /usr/lib/systemd/system/dbus-org.freedesktop.network1.service: No such file or directory /usr/lib/systemd/system/multi-user.target.wants/plymouth-quit-wait.service:3:After=rc-local.service plymouth-start.service systemd-user-sessions.service /usr/lib/systemd/system/multi-user.target.wants/plymouth-quit.service:3:After=rc-local.service plymouth-start.service systemd-user-sessions.service /usr/lib/systemd/system/serial-getty@.service:14:After=rc-local.service /usr/lib/systemd/system/plymouth-quit.service:3:After=rc-local.service plymouth-start.service systemd-user-sessions.service /usr/lib/systemd/system/console-getty.service:13:After=rc-local.service /usr/lib/systemd/system/getty@.service:13:After=rc-local.service /usr/lib/systemd/system/console-shell.service:12:After=rc-local.service /usr/lib/systemd/system/container-getty@.service:12:After=rc-local.service Binary file /usr/lib/systemd/system-generators/systemd-rc-local-generator matches
target的依赖
其实我们知道一个target是一组unit的集合,要达到一个target需要执行这个target所包含的所有的unit,最常见的target是multi-user.target,arget依赖的unit都会放在一个目录里,如下,然而,如果B是一个service unit,B依赖A service,B不会创建一个目录来存放自己所有的依赖的,因为,毕竟service和target待遇还是有区别的!
#ll /etc/systemd/system/multi-user.target.wants/ total 0 lrwxrwxrwx 1 root root 30 Aug 19 10:07 bb.service -> /etc/systemd/system/bb.service lrwxrwxrwx 1 root root 37 Mar 21 15:36 crond.service -> /usr/lib/systemd/system/crond.service lrwxrwxrwx 1 root root 38 Mar 21 15:58 docker.service -> /usr/lib/systemd/system/docker.service lrwxrwxrwx 1 root root 36 Aug 16 03:01 ipmi.service -> /usr/lib/systemd/system/ipmi.service lrwxrwxrwx 1 root root 38 Mar 22 03:00 mcelog.service -> /usr/lib/systemd/system/mcelog.service lrwxrwxrwx 1 root root 39 Aug 18 17:55 ntpdate.service -> /usr/lib/systemd/system/ntpdate.service lrwxrwxrwx 1 root root 36 Mar 21 15:36 ntpd.service -> /usr/lib/systemd/system/ntpd.service lrwxrwxrwx 1 root root 39 Jun 1 09:30 postfix.service -> /usr/lib/systemd/system/postfix.service lrwxrwxrwx 1 root root 36 Mar 21 15:36 sshd.service -> /usr/lib/systemd/system/sshd.service lrwxrwxrwx 1 root root 41 Jul 28 17:12 syslog-ng.service -> /usr/lib/systemd/system/syslog-ng.service lrwxrwxrwx 1 root root 39 Mar 21 15:36 sysstat.service -> /usr/lib/systemd/system/sysstat.service
假设,我有两个unit A,B,A是一个static文件,B是一个enable的文件 那么,B依赖A的话,那么reboot后,A中的ExecStart是否执行?如果B,仅仅是AfterA,那么reboot后,A中的ExecStart是否执行?
自己测试
下面,我自己测试一下,首先创建两个service:aa ,bb, 仅仅在bb的unit中写bb 需要after bb;那么
情况1
情况1:当bb没有被enable(这意味着bb一定要有[Install]区块,这里我们肯定写WantedBy=multi-user.target,但是bb在/etc/systemd/system/multi-user.target.wants/目录下没有软链接),aa没有Install区块的时候(这是aa肯定是一个static文件)
#systemctl status aa ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; static; vendor preset: disabled) Active: inactive (dead)
#systemctl status bb ● bb.service - bb Loaded: loaded (/etc/systemd/system/bb.service; disabled; vendor preset: disabled) Active: inactive (dead)
#systemctl is-enabled bb disabled
#ll /etc/systemd/system/multi-user.target.wants/ total 0 lrwxrwxrwx 1 root root 37 Mar 21 15:36 crond.service -> /usr/lib/systemd/system/crond.service lrwxrwxrwx 1 root root 38 Mar 21 15:58 docker.service -> /usr/lib/systemd/system/docker.service lrwxrwxrwx 1 root root 36 Aug 16 03:01 ipmi.service -> /usr/lib/systemd/system/ipmi.service lrwxrwxrwx 1 root root 38 Mar 22 03:00 mcelog.service -> /usr/lib/systemd/system/mcelog.service lrwxrwxrwx 1 root root 39 Aug 18 17:55 ntpdate.service -> /usr/lib/systemd/system/ntpdate.service lrwxrwxrwx 1 root root 36 Mar 21 15:36 ntpd.service -> /usr/lib/systemd/system/ntpd.service lrwxrwxrwx 1 root root 39 Jun 1 09:30 postfix.service -> /usr/lib/systemd/system/postfix.service lrwxrwxrwx 1 root root 36 Mar 21 15:36 sshd.service -> /usr/lib/systemd/system/sshd.service lrwxrwxrwx 1 root root 41 Jul 28 17:12 syslog-ng.service -> /usr/lib/systemd/system/syslog-ng.service lrwxrwxrwx 1 root root 39 Mar 21 15:36 sysstat.service -> /usr/lib/systemd/system/sysstat.service
情况2
情况2:当bb被enable的时候(这意味着bb一定要有Install区块,这里我们肯定写WantedBy=multi-user.target,并且bb一定在/etc/systemd/system/multi-user.target.wants/目录下有链接),aa没有Install区块的时候(这是aa肯定是一个static文件)
#systemctl enable bb Created symlink from /etc/systemd/system/multi-user.target.wants/bb.service to /etc/systemd/system/bb.service.
#ll /etc/systemd/system/multi-user.target.wants/ total 0 lrwxrwxrwx 1 root root 30 Aug 19 10:07 bb.service -> /etc/systemd/system/bb.service lrwxrwxrwx 1 root root 37 Mar 21 15:36 crond.service -> /usr/lib/systemd/system/crond.service lrwxrwxrwx 1 root root 38 Mar 21 15:58 docker.service -> /usr/lib/systemd/system/docker.service lrwxrwxrwx 1 root root 36 Aug 16 03:01 ipmi.service -> /usr/lib/systemd/system/ipmi.service lrwxrwxrwx 1 root root 38 Mar 22 03:00 mcelog.service -> /usr/lib/systemd/system/mcelog.service lrwxrwxrwx 1 root root 39 Aug 18 17:55 ntpdate.service -> /usr/lib/systemd/system/ntpdate.service lrwxrwxrwx 1 root root 36 Mar 21 15:36 ntpd.service -> /usr/lib/systemd/system/ntpd.service lrwxrwxrwx 1 root root 39 Jun 1 09:30 postfix.service -> /usr/lib/systemd/system/postfix.service lrwxrwxrwx 1 root root 36 Mar 21 15:36 sshd.service -> /usr/lib/systemd/system/sshd.service lrwxrwxrwx 1 root root 41 Jul 28 17:12 syslog-ng.service -> /usr/lib/systemd/system/syslog-ng.service lrwxrwxrwx 1 root root 39 Mar 21 15:36 sysstat.service -> /usr/lib/systemd/system/sysstat.service
输出结果是
#systemctl status aa ● aa.service - aa Loaded: loaded (/etc/systemd/system/aa.service; static; vendor preset: disabled) Active: inactive (dead)
#systemctl status bb ● bb.service - bb Loaded: loaded (/etc/systemd/system/bb.service; enabled; vendor preset: disabled) Active: active (exited) since Fri 2016-08-19 10:22:03 CST; 50s ago Process: 1044 ExecStart=/usr/libexec/bb (code=exited, status=0/SUCCESS) Main PID: 1044 (code=exited, status=0/SUCCESS) CGroup: /system.slice/bb.service Aug 19 10:22:03 localhost.localdomain systemd[1]: Started bb.
#cat /var/log/bb Fri Aug 19 10:22:03 CST 2016
#cat /var/log/aa cat: /var/log/aa: No such file or directory
四、target的理解
Targets are special unit files that describe a system state or synchronization point. Like other units, the files that define targets can be identified by their suffix, which in this case is .target. Targets do not do much themselves, but are instead used to group other units together.
Listing Available Targets
#systemctl list-unit-files --type=target 可以看到available targets,但是只有状态是active 的,才表示这个target被执行了;
默认target
#systemctl get-default multi-user.target
#systemctl set-default graphical.target 设置默认target
target相关命令
#systemctl list-dependencies basic.target --after输出是什么? 输出的是,basic.target 这个target应该在其输出的target运行之后运行!!!
systemctl list-dependencies basic.target --before输出是什么? 输出的是,basic.target 这个target应该在其输出的target运行之前运行!!!
特殊的multi-user.target
#systemctl list-dependencies multi-user.target --before #systemctl list-dependencies multi-user.target --after
你会发现multi-user.target 这个target是在很多基础target搞定之后,才会运行multi-user.target,但是multi-user.target并不是最后一个运行的target,还有比他更晚的target,那就是graphical.target,在他后面运行的基本上不会被运行,红色标记不仅说明了这一切!
#systemctl get-default multi-user.target
[root@localhost /home/ahao.mah] #ll /etc/systemd/system/default.target lrwxrwxrwx 1 root root 37 Mar 18 16:48 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target
更是说明了multi-user.target 是OS启动过程中,最后一个要运行的target!!!
[root@localhost /home/ahao.mah] #systemctl list-dependencies multi-user.target --before multi-user.target ● ├─systemd-readahead-done.service ● ├─systemd-readahead-done.timer ● ├─systemd-update-utmp-runlevel.service ● └─graphical.target ● └─systemd-update-utmp-runlevel.service
五、参考
How To Use Systemctl to Manage Systemd Services and Units