我们运行 Linux 服务器的主要目的是通过运行程序提供服务,比如 MySQL、web server等。因此管理 Linux 服务器主要工作就是配置并管理上面运行的各种服务程序。在 Linux 系统中服务程序的管理主要由 init 系统负责。如同笔者在《初识 systemd》一文中的介绍,Linux 的 init 系统已经从最初的 sysvinit 进化到了如今的 systemd。本文主要介绍在 systemd 环境中如何编写运行服务的配置文件。
unit(单元)的配置文件 Unit 是 systemd 进行任务管理的基本单位,我们在前文中已经介绍过,service 类型的 unit 代表一个后台服务进程。接下来我们就详细的介绍如何配置 service 类型的 unit。下面我们先来看一个简单的服务配置:
[Unit] Description=Prometheus Server Documentation=https://prometheus.io/docs/introduction/overview/ After=network.target
[Service] User=prometheus Restart=on-failure WorkingDirectory=/usr/local/share/prometheus/ ExecStart=/usr/local/share/prometheus/prometheus -config.file=/usr/local/share/prometheus/prometheus.yml
[Install] WantedBy=multi-user.target 这是笔者主机上 prometheus 服务的配置文件。把上面的内容保存到文件 /lib/systemd/system/prometheus.service 中,然后就可以使用 systemctl 命令管理 prometheus 服务了。注意,服务类型的配置文件名称必须以 .service 结尾。 查看上面配置信息的详细内容,我们会发现整个配置的内容分为三个部分: [Unit] unit 本身的说明,以及与其它有依赖关系的服务的设置,包括在什么服务之后才启动此 unit 之类的设置。 [Service] 不同的 unit 类型就得要使用相对应的设置项目,比如 timer 类型的 unit 应该是 [Timer],socket 类型的 unit 应该是 [Socket]。服务类型的 unit 就是 [Service],这个项目内主要在规范服务启动的脚本、环境配置文件文件名、重新启动的方式等等。 [Install] 这个部分主要设置把该 unit 安装到哪个 target 。
服务类型 unit 的详细配置 配置文件分为三个部分,每个部分中都可以提供详细的配置信息。为了精确的控制服务的运行方式,我们需要了解这些详细的配置选项,并最终让服务以我们期望的方式运行。
[Unit] 部分 Description 关于该 unit 的简易说明。 Documentation 文档相关的内容,如 Documentation=https://prometheus.io/docs/introduction/overview/ Documentation=man:sshd(8) Documentation=file:/etc/ssh/sshd_config After 说明本 unit 是在哪个服务启动之后才启动的意思。仅是说明服务启动的顺序而已,并没有强制要求 。 Before 与 After 的意义相反,在指定的服务启动前最好启动本个服务的意思。仅是说明服务启动的顺序而已,并没有强制要求 。 Requires 本 unit 需要在哪个服务启动后才能够启动!就是设置服务间的依赖性。如果在此项设置的前导服务没有启动成功,那么本 unit 就不会被启动! Wants 与 Requires 刚好相反,规范的是这个 unit 之后还要启动什么服务,如果这 Wants 后面接的服务如果没有启动成功,其实不会影响到这个 unit 本身! Conflicts 这个项目后面接的服务如果有启动,那么本 unit 就不能启动!如果本 unit 启动了,则指定的服务就不能启动。
[Service] 部分 Type 说明这个服务的启动方式,会影响到 ExecStart,主要有下面几种类型: simple:默认值,这个服务主要由 ExecStart 设置的程序来启动,启动后常驻于内存中。 forking:由 ExecStart 指定的启动的程序通过 spawns 产生子进程提供服务,然后父进程退出。 oneshot:与 simple 类似,不过这个程序在工作完毕后就结束了,不会常驻在内存中。 dbus:与 simple 类似,但这个服务必须要在取得一个 D-Bus 的名称后,才会继续运行!因此设置这个项目时,通常也要设置 BusName= 才行。 idle:与 simple 类似,意思是,要执行这个服务必须要所有的工作都顺利执行完毕后才会执行。这类的服务通常是开机到最后才执行即可的服务。 notify:与 simple 类似,但这个服务必须要收到一个 sd_notify() 函数发送的消息后,才会继续运行。
ExecStart 就是实际执行此服务的程序。接受 "命令 参数 参数..." 的格式,不能接受 <, >, >>, |, & 等特殊字符,很多的 bash 语法也不支持。所以,要使用这些特殊的字符时,最好直接写入到脚本里面去!
ExecStartPre 和 ExecStartPost 分别在服务启动前后,执行额外的命令。
ExecStop 用来实现 systemctl stop 命令,关闭服务。 ExecReload 用来实现 systemctl reload 命令,重新加载服务的配置信息。
Restart 当设置为 Restart=1 时,如果服务终止,就会自动重启此服务。 RestartSec 与 Restart 配合使用,在服务终止多长时间之后才重新启动它。默认是 100ms。
KillMode 可以是 process, control-group, none 中的一种,如果是 process 则服务终止时,只会终止主要的程序(ExecStart接的后面那串指令),如果是 control-group 时,则由此 daemon 所产生的其他 control-group 的程序,也都会被关闭。如果是 none 的话,则没有程序会被关闭。
TimeoutSec 若这个服务在启动或者是关闭时,因为某些缘故导致无法顺利 "正常启动或正常结束" 的情况下,则我们要等多久才进入 "强制结束" 的状态!
RemainAfterExit 当设置为 RemainAfterExit=1 时,则当这个服务所属的所有程序都终止之后,此服务会再尝试启动。这对于 Type=oneshot 的服务很有帮助!
环境变量的设置对很多程序来说都是十分重要的,下面的配置则可以以不同的方式为服务程序设置环境变量: Environment 用来设置环境变量,可以使用多次:
[Service] # Client Env Vars Environment=ETCD_CA_FILE=/path/to/CA.pem Environment=ETCD_CERT_FILE=/path/to/server.crtEnvironmentFile 通过文件的方式设置环境变量,可以把下面的内容保存到文件 testenv 中:
AAA_IPV4_ANCHOR_0=X.X.X.X BBB_IPV4_PRIVATE_0=X.X.X.X CCC_HOSTNAME=test.example.com然后这样设置:
[Service] EnvironmentFile=/testenv接下来就可以在 ExecStart 配置中使用在文件中设置的环境变量,如:
ExecStart=/xxx --abc=xx${AAA_IPV4_ANCHOR_0}yy[Install] 部分 WantedBy 这个设置后面接的大部分是 *.target unit。意思是,这个 unit 本身是附挂在哪个 target unit 下面。 Also 当目前这个 unit 被 enable 时,Also 后面接的 unit 也要 enable 的意思。 Alias 当 systemctl enable 相关的服务时,则此服务会进行链接文件的创建!
Timer 类型 unit 的详细配置 Timer 类型的 unit 主要用来执行定时任务,并有可能取代 cron 服务。由于 timer 类型的 unit 经常与服务类型的 unit 一起使用,所以本文也附带介绍一下 timer unit 的配置。与服务类型的 unit 不同,timer unit 配置文件中的主要部分是 [Timer],下面是其主要的配置项: OnActiveSec 当 timers.target 启动后多久才执行这个 unit。 OnBootSec 当开机后多久才执行这个 unit。 OnStartupSec 当 systemd 第一次启动后多久才执行这个 unit。 OnUnitActiveSec 这个 timer 配置文件所管理的那个 unit 服务在最后一次启动后,隔多久后再执行一次。 OnUnitInactiveSec 这个 timer 配置文件所管理的那个 unit 服务在最后一次停止后,隔多久后再执行一次。 Unit 一般不需要设置,基本上我们设置都是 服务名称.server 服务名称.timer。如果你的服务名称和 timer 名称不相同,就需要在 .timer 文件中通过 Unit 项指定服务的名称。 OnCalendar 使用实际时间(非循环时间)的方式来启动服务。 Persistent 当使用 OnCalendar 的设置时,指定该功能要不要持续执行。
通过上面的介绍,相信大家对 systemd 服务类型和 timer 类型的 unit 配置已经有了基本的理解,下面让就让我们配置两个实际的例子。
配置 Redis 服务 在 Ubuntu 上我们一般会手动编译并安装 redis。在安装完成后需要把 redis 配置为 systemd 管理的服务,下面介绍具体的配置过程。 添加 redis 配置文件 首先手动创建 /etc/redis 目录并添加配置文件:
$ sudo mkdir /etc/redis并把代码目录中的配置文件 redis.conf 拷贝到 /etc/redis 目录中:
$ sudo cp /tmp/redis-4.0.0/redis.conf /etc/redis/然后修改配置文件 /etc/redis/redis.conf 中的 supervised 为 systemd: supervised systemd
接着继续在配置文件 /etc/redis/redis.conf 中配置工作目录,把 dir ./ 修改为: dir /var/lib/redis
配置由 systemd 管理 redis 服务 创建 /etc/systemd/system/redis.service 文件
$ sudo vim /etc/systemd/system/redis.service编辑其内容如下:
[Unit] Description=Redis In-Memory Data Store After=network.target
[Service] User=redis Group=redis ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf ExecStop=/usr/local/bin/redis-cli shutdown Restart=always
[Install] WantedBy=multi-user.target 启动服务并配置为开机启动:
sudo systemctl start redis sudo systemctl enable redis
通过脚本定时备份文件 备份文件的 bash 脚本:
#!/bin/bashmydate(){ date " %Y%m%d%H%M%S"}backupdate=
$ sudo chmod x /usr/local/bin/backupdir.sh然后创建 service unit 配置文件:
[Unit] Description=nick backup learn dir service
[Service] User=nick Group=nick Type=simple ExecStart=/usr/local/bin/backupdir.sh
[Install] WantedBy=multi-user.target 把上面的 unit 配置保存到文件 /etc/systemd/system/nickbak.service。 然后执行下面的命令测试服务的执行情况:
sudo systemctl daemon-reload sudo systemctl start nickbak.service这样的备份任务只会在执行 sudo systemctl start nickbak.service 时执行一次。下面我们通过 timer unit 把它配置为定时执行。创建 timer unit 配置文件:
[Unit] Description=nick backup learn dir timer
[Timer] OnCalendar=*:0/15 Persistent=true Unit=nickbak.service
[Install] WantedBy=multi-user.target 把上面的 unit 配置保存到文件 /etc/systemd/system/nickbak.timer。配置中 OnCalendar=*:0/15 表示每 15 分钟执行一次 nickbak.service 服务。
执行下面的命令把 nickbak.timer 设置为开机启动,并启动 nickbak.timer:
sudo systemctl daemon-reload sudo systemctl enable nickbak.timer
$ sudo systemctl status nickbak.timer
从现在开始 nickbak.timer 会每隔 15 分钟执行一次 nickbak.service 服务。
总结 systemd 提供了服务管理(其实是 unit 管理)的方方面面,我们需要做的就是写好服务 unit 的配置文件,然后利用 systemd 来管理我们的服务。这是一个看似简单实则繁琐的任务(很多的配置项其实需要我们在实践中不断的调整并优化)。希望本文对大家来说是个简单的入门。
参考: 鸟哥的Linux私房菜-基础学习篇(第四版)高清完整书签PDF版 http://www.linuxidc.com/Linux/2016-02/128220.htm