现在越来越多的Linux发行版将init系统从upstart(or other)换成了systemd,当然systemd相比于旧的init系统更加灵活和高效,所以本文介绍一下systemd的常用命令。目前已经切换为systemd的常见发行版有:Ubuntu 15.04、Debian 8、CentOS 7、Fedora 15及其这些发行版的高版本。本文使用的是Ubuntu 16.04 LTS,用户为root,非root执行时需要加sudo.当然最全的文档还是man文档,所以细节可以查看man文档。本文主要包括三部分:

  1. systemctl常用命令,很多情况下我们会使用systemctl来管理自己的系统就够了。
  2. systemd的文件语法介绍,有兴趣的可以了解一下。

systemd管理的基本对象称之为unit,这个unit可以是很多类型,,比如.service.socket.device.mount.swap.automount等,其实任何系统知道如何操作和管理的资源systemd都可以去管理。不过我们最常见的类型是service(一般后缀为.service),本文第二部分的学习总结也主要是这个,所以本文提到的unit和服务指的是同一个东西,不再细分。本文中所。当然,systemd和upstart的区别本文没有涉及,这里推荐一篇文章,有兴趣的可以看一下:《浅析 Linux 初始化 init 系统,第 3 部分: Systemd》。

systemctl常用命令

我们主要使用systemctl命令来管理使用systemd的系统,下面以nginx为例进行说明。先安装一个nginx:

apt install nginx    # Ubuntu 16.04开始不需要使用apt-get了

装好以后,我们就可以使用systemctl来管理nginx服务了:

  1. 服务的启动、停止、重启、重新加载配置文件依次为startstoprestartreload

    systemctl start/stop/restart/reload nginx.service
  2. 开机自启/关闭开机自启动分别为enabledisable

    systemctl enable/disable nginx.service
  3. 列出所有已经启动(active)的unit,即已经被加载到内存的unit:

    systemctl list-units

    当然可以省略掉list-units,因为systemctl默认就执行的是上述命令。如果想查看所有的已启动和未启动的unit(已加载到内存,但未启动),在后面加上--all即可。

  4. 列出系统已经安装的所有unit,包括那些没有被加载到内存的unit:

    systemctl  list-unit-files
  5. journaldsystemd中专门收集日志的模块,我们可以用他来查看日志。
    查看所有日志(从最旧的开始显示):

    journalctl

    如果journald配置了保存上次启动的日志的话, 这个默认显示的自系统上次启动到现在的所有日志。有的发行版可能没有配置(比如我现在正在用的Ubuntu 16.04,其他的不知道),我们可以在/etc/systemd/journald.conf中将Storage设置为persistent,或者创建/var/log/journal目录。当然如果我们只想查看本次启动之后的日志,可以加上-b标志:

    journalctl -b

    如果只想查看内核日志的话,可以加上-k(可以和-b联合使用):

    journalctl -k
  6. 查看unit的状态:

    systemctl status nginx.service
  7. 查看服务的日志:

    journalctl -u nginx.service    # 还可以配合`-b`一起使用,只查看自本次系统启动以来的日志
  8. 查看unit文件等信息:

    # 查看unix文件
    systemctl cat nginx.service
    
    # 查看unit所有依赖
    systemctl list-dependencies nginx
    
    # 递归的列出所有依赖
    systemctl list-dependencies --all nginx.service
    
    # 列出unit的详细信息
    systemctl show nginx.service
  9. 修改unit文件:

    systemctl edit nginx.service

    这样会创建一个新的配置文件,我们做的修改会覆盖默认的。当然我们想直接修改原来的配置文件的话,可以加上--full参数。必须注意的是,如果修改了unit文件,一定要执行systemctl daemon-reload让修改的配置生效。

  10. 使用Target。我们都知道Linux有个运行级别(Runlevel),对应不同的模式,比如Ubuntu一般运行在5上面。在systemd中,这个运行级别就是Target

    # 查看所有target下的unit
    systemctl list-unit-files --type=target
    
    # 查看默认target,即默认的运行级别。对应于旧的`runlevel`命令
    systemctl get-default
    
    # 设置默认的target
    systemctl set-default multi-user.target
    
    # 查看某一target下的unit
    systemctl list-dependencies multi-user.target
    
    # 切换target,不属于新target的unit都会被停止
    systemctl isolate multi-user.target
  11. 管理主机:

    systemctl poweroff    # 关机
    systemctl reboot       # 重启
    systemctl rescue    # 进入rescue模式

systemd的配置文件

文件位置

这里我们先要说明一下unit的文件位置,一般主要有三个目录:

  • /lib/systemd/system
  • /run/systemd/system
  • /etc/systemd/system

这三个目录的配置文件优先级依次从低到高,如果同一选项三个地方都配置了,优先级高的会覆盖优先级低的。系统安装时,默认会将unit文件放在/lib/systemd/system目录。如果我们想要修改系统默认的配置,比如nginx.service,一般有两种方法:

  1. /etc/systemd/system目录下创建nginx.service文件,里面写上我们自己的配置。
  2. /etc/systemd/system下面创建nginx.service.d目录,在这个目录里面新建任何以.conf结尾的文件,然后写入我们自己的配置。推荐这种做法。

/run/systemd/system这个目录一般是进程在运行时动态创建unit文件的目录,一般很少修改,除非是修改程序运行时的一些参数时,即Session级别的,才在这里做修改。

文件语法

上面我们安装了nginx,其实装完以后就会在/lib/systemd/system有一个nginx.service文件,内容如下:

# Stop dance for nginx
# =======================
#
# ExecStop sends SIGSTOP (graceful stop) to the nginx process.
# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control
# and sends SIGTERM (fast shutdown) to the main process.
# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends
# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
#
# nginx signals reference doc:
# http://nginx.org/en/docs/control.html
#
[Unit]
Description=A high performance web server and a reverse proxy server
After=network.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

unit文件由一些Section组成,Section的名字用中括号括起来,而且是区分大小写的。每个Section的作用域到下一个Section开始或者到文件尾。Section的通用格式如下:

[Section]
Directive1=value
Directive2=value

. . .

虽然Section之间是没有先后顺序之分的,但unit文件一般都把[Unit]作为第一部分(Section),[Install]作为最后一部分,然后中间是每种类型特性的部分,比如Service就是[Service],当然我们也可以自定义自己的Section,不过需要在前面加上X-前缀。本文就只介绍Service这种类型的,下面依次介绍。

[Unit]

Unit一般是第一部分,用来描述unit文件元数据以及服务的依赖关系。常用的命令有:

  • Description=:这里一般写服务简短的描述。
  • Documentation=:这里一般是服务文档的链接等。
  • Requires=:这里写本服务依赖的其他服务,启动本服务时,一般会并行的启动该服务和它所依赖的服务,如果它依赖的服务启动失败了,本服务将无法启动成功。
  • Wants=:这个命令和Requires=类似但是相对宽松一些,即使依赖的服务启动失败了,本服务也可以继续正常启动。一般的依赖都推荐使用这个命令。
  • BindsTo=:和Requires=类似,但是如果依赖的服务停止了,本服务也会停止。
  • Before=After=:这两个需要和上面描述依赖关系的命令一起使用,表示依赖的当前服务与依赖的服务启动的先后顺序:Before=表示当前服务启动成功后才可以启动依赖服务,After=相反。
  • Conflicts=:这个命令后面跟的服务将不能和当前服务同时运行,如果当前服务运行则会导致该命令列举的服务被停止。
  • Condition...=:这个命令往往和许多其他命令一起使用,用来测试一些条件,比如测试当前的操作系统。如果条件不满足,则跳过当前服务的启动。
  • Assert...=:和Condition...=类似,但是如果条件检测不满足会导致失败。

[Install]

Install一般是最后一部分,用来描述unit的行为或者是否开机自启动等,是可选的。而且只有可以开机自启的(即可以被enable)的才会有这个Section。一般常用的命令有:

  • WantedBy=:这个命令是最通用的用来指定服务如何被enable,即在哪些target/runlevel下被设置为开机自启动。我们可以通过这个命令来指定服务捡的依赖关系,有点像[Unit]部分的Wants=,但是这个只是辅助性的。当一个unit被enable后,就会在/etc/systemd/system目录下创建以.wants为后缀的目录,比如当前unit文件里面写了WantedBy=multi-user.target,那么enable当前unit后,就会在/etc/systemd/system目录下创建multi-user.target.wants目录,并且将当前unit及其依赖的unit的符号链接放在新创建的目录里面。disable该unit之后,它的软连接及其依赖的unit的软连接都将被删除。
  • RequiredBy=:和WantedBy=类似,但是它指定的依赖条件如果不满足,就会导致服务启动失败。如果enable的话,创建的是.requires结尾的目录。
  • Alias=:给服务创建别名。
  • Also=:将多个unit设置为一个组,可以一起操作。

[Service]

我们之前说了,unit对象有很多种类型,其中devicetargetsnapshotscope这几种类型没有对应该类型的Section,其他的都有,比如service这种类型特有的section就是[Service],也就是本节要介绍的。[Service]有一个必须的命令就是Type,它根据进程的行为将服务分为好多类别,不同的类别管理不是不太一样:

  • simple:这种是最普遍的类型,在启动行(使用ExecStart=指定)指定进程,如果Type=Busname=没有设置,但是ExecStart=却指定了的话,那默认就是这种类型。
  • forking:这种类别指的是那种fork出来子进程后,父进程就马上退出的情况。这种类型下,父进程退出后,systemd仍然认为进程是OK的。而且可以使用PIDFile=命令来指定存放主子进程pid的文件。Nginx就属于这种类型。
  • oneshot:这种一般用在存活时间不长的一次性任务的进程上,它告诉systemd应该等待进程退出后再接着去处理其它的unit。
  • dbus:这种类别的告诉systemd该unit会在D-Bus上面获取一个名字。
  • notify:这种类别的服务会在启动完之后发出一个消息,systemd必须等到接收到这个消息后才可以接着去处理其它unit。
  • idle:这种类别表示在收到所有任务前,服务都不会运行。

OK,上面就是Type=可取得值。下面介绍除Type=以外的其他命令:

  • ExecStart=:用来指定进程文件(必须是绝对路径)和启动参数,一般该命令只能指定一次。有一个特殊的用法就是比如其他文件里面已经设置了,我们现在想在优先级更高的地方覆盖它,就可以先写一行ExecStart=(前面的表示清空之前的设置),然后再在另外一行写上完整的命令ExecStart=***。另外,如果在命令之前加上-的话表示进程如果以非0退出,也不算失败。
  • ExecStartPre=ExecStartPost=:看名字就看出来了,通过这两个指令可以指定在进程运行前和运行后执行的命令,同样也可以加-,表示接受非0的退出状态。
  • ExecReload=:重新加载服务的配置。
  • ExecStop=:指定停止服务的命令,如果未指定,服务停止后将使用kill来杀掉进程。
  • ExecStopPost=:指定服务停止后运行的命令。
  • RestartSec=:如果服务的自动重启设置了的话,这个命令指定多久重启。
  • Restart=:指定systemd在何种状态下重启服务,可用值有:"always", "on-success", "on-failure", "on-abnormal", "on-abort", "on-watchdog".
  • TimeoutSec=:指定systemd在标记服务失败多久前强制杀掉进程。也可以分别指定TimeoutStartSec=TimeoutStopSec=

更多systemd和systemctl的信息可以查看Linux man文档。

参考: