# Systemd用法入门


{{< admonition type=info title="内容简介" open=true >}}
本文介绍Systemd的基本用法。
{{< /admonition >}}

## Systemd简介

Systemd 是一个用于 Linux 操作系统的系统和服务管理器，旨在替代传统的 init 系统。它不仅负责启动操作系统，还管理后台服务、结束进程、状态查询、日志归档、设备管理、电源管理和定时任务等多种职责。Systemd 提供了强大的并行化能力，使用套接字和 D-Bus 激活来启动服务，并支持按需启动守护进程。
一个标准的 .service 文件主要由三个核心部分组成：[Unit]（通用设置与依赖）、[Service]（核心运行逻辑）和 [Install]（安装与自启）。文件位置：
| 路径    | 说明   | 建议   |
| --------- | ----------- | ------ |
| /etc/systemd/system/ | 管理员自定义目录  | 推荐。优先级最高，系统更新不会覆盖。  |
| /usr/lib/systemd/system/ | 软件包默认安装目录  | 不推荐。通常由安装脚本自动写入，手动修改会被更新覆盖。  |
| /run/systemd/system/ | 运行时动态生成目录  | 禁止。重启后失效，仅供临时使用。  |

## service文件

[Unit]区块通常是配置文件的第一个区块，用来定义 Unit 的元数据，以及配置与其他 Unit 的关系。它的主要字段如下：

- Description：简短描述
- Documentation：文档地址
- Requires：当前 Unit 依赖的其他 Unit，如果它们没有运行，当前 Unit 会启动失败
- Wants：与当前 Unit 配合的其他 Unit，如果它们没有运行，当前 Unit 不会启动失败
- BindsTo：与Requires类似，它指定的 Unit 如果退出，会导致当前 Unit 停止运行
- Before：如果该字段指定的 Unit 也要启动，那么必须在当前 Unit 之后启动
- After：如果该字段指定的 Unit 也要启动，那么必须在当前 Unit 之前启动
- Conflicts：这里指定的 Unit 不能与当前 Unit 同时运行
- Condition：当前 Unit 运行必须满足的条件，否则不会运行
- Assert：当前 Unit 运行必须满足的条件，否则会报启动失败

[Service]区块用来 Service 的配置，只有 Service 类型的 Unit 才有这个区块。它的主要字段如下:

- Type：定义启动时的进程行为。它有以下几种值:
  - Type=simple：默认值，执行ExecStart指定的命令，启动主进程
  - Type=forking：以 fork 方式从父进程创建子进程，创建后父进程会立即退出
  - Type=oneshot：一次性进程，Systemd 会等当前服务退出，再继续往下执行
  - Type=dbus：当前服务通过D-Bus启动
  - Type=notify：当前服务启动完毕，会通知Systemd，再继续往下执行
  - Type=idle：若有其他任务执行完毕，当前服务才会运行
- ExecStart：启动当前服务的命令
- ExecStartPre：启动当前服务之前执行的命令
- ExecStartPost：启动当前服务之后执行的命令
- ExecReload：重启当前服务时执行的命令
- ExecStop：停止当前服务时执行的命令
- ExecStopPost：停止当其服务之后执行的命令
- RestartSec：自动重启当前服务间隔的秒数
- Restart：定义何种情况 Systemd 会自动重启当前服务，可能的值包括always（总是重启）、on-success、on-failure、on-abnormal、on-abort、on-watchdog
- TimeoutSec：定义 Systemd 停止当前服务之前等待的秒数
- Environment：指定环境变量

[Install]通常是配置文件的最后一个区块，用来定义如何启动，以及是否开机启动。它的主要字段如下:

- WantedBy：它的值是一个或多个 Target，当前 Unit 激活时（enable）符号链接会放入/etc/systemd/system目录下面以 Target 名 + .wants后缀构成的子目录中
- RequiredBy：它的值是一个或多个 Target，当前 Unit 激活时，符号链接会放入/etc/systemd/system目录下面以 Target 名 + .required后缀构成的子目录中
- Alias：当前 Unit 可用于启动的别名
- Also：当前 Unit 激活（enable）时，会被同时激活的其他 Unit

## 实战应用

开发了一个阿里云sls日志消费服务，使用systemd管理服务。
```bash
vim /etc/systemd/system/sls-consumer.service

[Unit]
Description=aliyun-sls-consumer Service
After=network.target

[Service]
ExecStart=/data/project/sls-consumer/consumer -c /data/project/sls-consumer/config.yaml
KillSignal=SIGTERM
Restart=on-failure
RestartSec=5s
TimeoutStopSec=5
StandardOutput=append:/data/project/sls-consumer/logs/sls-consumer.log
StandardError=append:/data/project/sls-consumer/logs/sls-consumer_error.log

[Install]
WantedBy=multi-user.target

#StandardOutput、StandardError用于指定标准输出和错误输出的日志文件，否则服务日志会默认输出到/var/log/messages文件中。此外这两个参数至少要 CentOS8 以上版本才支持。
```

编写完文件后，必须执行以下命令使其生效：
```bash
systemctl daemon-reload #新增文件后需要执行此命令，否则配置不生效。
systemctl start sls-consumer.service #启动服务
systemctl enable sls-consumer.service #设置服务开机自动运行
systemctl status sls-consumer.service #查看服务状态
systemctl stop sls-consumer.service #停止服务
```

如果服务不需要了，彻底清除服务，命令如下：
```bash
systemctl stop sls-consumer.service #停止服务
systemctl disable sls-consumer.service  #取消开机启动
systemctl reset-failed sls-consumer.service #重置失败状态，清除systemctl list-units中 "Active: failed" 的历史记录（如果有的话）
systemctl daemon-reload
```

**参考资料：**
1. [Systemd 入门教程](https://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html)

---

> 作者: [beneliu](https://github.com/beneliu)  
> URL: https://benelin.site/posts/devops/7414ca1/  

