强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

systemd 教程 / Unit 文件基础

Unit 文件基础

一、Unit 文件格式

systemd Unit 文件采用类似 INI 的格式,由多个段(section)组成:

# 注释以 # 开头
[Unit]
# Unit 段:通用元数据

[Service]
# 服务特定配置(仅适用于 .service 类型)

[Install]
# 安装指令(用于 enable/disable 操作)

💡 提示:Unit 文件的注释和空行会被忽略。行尾的反斜杠 \ 用于续行。


二、[Unit] 段详解

[Unit] 段是所有 Unit 类型通用的元数据段。

2.1 核心指令

指令说明示例
DescriptionUnit 描述(显示在 status 中)Description=OpenSSH server
Documentation文档链接Documentation=man:sshd(8)
After本 Unit 在哪些 Unit 之后启动After=network.target
Before本 Unit 在哪些 Unit 之前启动Before=nginx.service
Requires强依赖:依赖项失败则本 Unit 也失败Requires=network.target
Wants弱依赖:依赖项失败不影响本 UnitWants=sshd.service
Conflicts冲突:不能与列出的 Unit 同时运行Conflicts=iptables.service
BindsTo比 Requires 更强:依赖项停止则本 Unit 也停止BindsTo=docker.socket
PartOf级联操作:依赖项 restart/stop 时联动PartOf=nginx.service
Condition...启动条件检查ConditionPathExists=/etc/my.conf
Assert...断言检查(失败则进入 failed 状态)AssertPathIsDirectory=/data

2.2 依赖关系详解

Requires ──── 强依赖(失败则本服务也失败)
   │
   ├── Requisite ── 启动前检查(未运行则失败,不自动启动)
   │
   └── BindsTo ──── 最强绑定(停止则本服务也停止)

Wants ──────── 弱依赖(失败不影响本服务)

After/Before ── 顺序依赖(仅控制启动顺序,不控制依赖)

实际示例:

[Unit]
Description=My Web Application
Documentation=https://myapp.io/docs
After=network.target postgresql.service
Requires=postgresql.service
Wants=redis.service
Conflicts=apache.service

这段配置的含义:

  1. 在网络和 PostgreSQL 之后启动
  2. 必须有 PostgreSQL(没有则启动失败)
  3. 希望有 Redis(没有也可以正常运行)
  4. 与 Apache 冲突(不能同时运行)

⚠️ 注意After 只控制启动顺序,不建立依赖关系。Requires 只建立依赖关系,不控制顺序。通常需要 Requires + After 组合使用。

2.3 依赖关系的实际影响

指令自动启动?失败影响停止联动
Requires❌ 需配合 After本服务也失败
Requires + After✅ 自动启动本服务也失败
BindsTo❌ 需配合 After本服务也失败✅ 依赖停止则停止
Wants✅ 自动尝试启动无影响
PartOf无影响✅ 依赖 restart 则联动

三、条件检查(Condition)

Condition 指令用于在启动前检查系统状态,不满足条件时 Unit 默认跳过(不进入 failed 状态):

条件指令说明
ConditionPathExists=/path文件/目录存在
ConditionPathIsDirectory=/path路径是目录
ConditionPathIsSymbolicLink=/path路径是符号链接
ConditionFileNotEmpty=/path文件存在且非空
ConditionKernelCommandLine=opt内核命令行包含参数
ConditionVirtualization=虚拟化环境(vm/container/physical)
ConditionHost=主机名匹配
ConditionACPower=是否连接电源
ConditionMemory=最小内存(如 4G)
ConditionCPUs=最少 CPU 数量
ConditionSecurity=安全模块(selinux/apparmor)

示例:

[Unit]
Description=GPU Compute Service
# 仅在物理机上运行,不适用于容器
ConditionVirtualization=physical
# 需要至少 16GB 内存
ConditionMemory=16G
# 需要存在 CUDA 设备
ConditionPathExists=/dev/nvidia0

💡 提示Condition 检查失败时 Unit 会被静默跳过。如果需要检查失败时进入 failed 状态,使用 Assert 前缀(如 AssertPathExists=)。


四、Unit 文件语法检查

# 验证单个 Unit 文件语法
systemd-analyze verify /etc/systemd/system/myapp.service

# 验证所有已知 Unit 文件
systemd-analyze verify

# 检查 Unit 配置的安全性(安全评分 0-10)
systemd-analyze security myapp.service

systemd-analyze security 输出示例:

  NAME                                  DESCRIPTION                                                      EXPOSURE
✗ PrivateNetwork=                       Service has access to the host's network                         0.5
✗ User=/DynamicUser=                    Service runs as root user                                         0.4
✗ DeviceAllow=                          Service has no device ACL                                         0.2
✗ MemoryDenyWriteExecute=               Service may create writable executable memory mappings            0.2
✗ RestrictAddressFamilies=              Service may allocate sockets of all address families              0.2
...
→ Overall exposure level for myapp.service: 9.6 UNSAFE 😨

五、模板单元(Template Unit)

5.1 基本概念

模板单元使用 @ 符号实现一个 Unit 文件管理多个实例:

# 模板文件名格式
# /etc/systemd/system/[email protected]

# 实例化
systemctl start [email protected]
systemctl start [email protected]

5.2 模板示例

创建模板文件 /etc/systemd/system/[email protected]

[Unit]
Description=Web Application Instance %i
After=network.target

[Service]
Type=simple
User=www-data
ExecStart=/opt/webapp/server --port %i --config /etc/webapp/%i.conf
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

使用方式:

# 启动不同端口的实例
systemctl start [email protected]
systemctl start [email protected]

# 查看状态
systemctl status [email protected]

# 设为开机自启
systemctl enable [email protected]

5.3 模板替换符

符号含义
%i实例名称(小写)
%I实例名称(未转义)
%f实例名称(文件系统路径格式)
%pUnit 名称前缀(@ 之前的部分)
%PUnit 名称前缀(未转义)
%h用户主目录(用户级 Unit)
%u当前用户名

六、Drop-in 覆盖机制

6.1 为什么需要 Drop-in

直接编辑 /usr/lib/systemd/system/ 中的文件会在软件包更新时被覆盖。Drop-in 机制允许在不修改原始文件的情况下覆盖配置。

6.2 Drop-in 目录结构

/etc/systemd/system/
├── nginx.service              # 完全覆盖(不推荐)
└── nginx.service.d/
    ├── override.conf          # Drop-in 文件(推荐)
    └── custom-logging.conf    # 可以有多个 Drop-in

6.3 创建 Drop-in 文件

# 方法一:使用 systemctl edit(推荐)
systemctl edit nginx.service

这会创建目录 /etc/systemd/system/nginx.service.d/ 并打开编辑器,输入:

[Service]
# 覆盖启动命令的参数
Environment="NGINX_OPTS=--debug"

# 追加额外的指令
ExecStartPost=/usr/local/bin/notify-deploy.sh
# 方法二:手动创建
mkdir -p /etc/systemd/system/nginx.service.d/
cat > /etc/systemd/system/nginx.service.d/override.conf << 'EOF'
[Service]
Environment="NGINX_OPTS=--debug"
EOF
# 重载配置
systemctl daemon-reload

# 验证 Drop-in 已生效
systemctl cat nginx.service

6.4 覆盖规则

动作语法
覆盖已有值直接写同名指令(值替换)
追加列表值ExecStart= 清空后重写,或使用 ExecStartPre= 等追加
清空列表ExecStart=(空值)
设置为空值Environment=

⚠️ 注意ExecStart 等列表指令不支持简单的追加,需要先清空再重写。例如要修改启动命令:

[Service]
ExecStart=
ExecStart=/usr/bin/nginx -c /etc/nginx/custom.conf

6.5 查看最终生效配置

# 显示最终合并后的配置
systemctl show nginx.service

# 显示特定属性
systemctl show nginx.service -p ExecStart

# 查看配置来源(包含 Drop-in)
systemctl cat nginx.service

七、Unit 文件重载

每次修改 Unit 文件或 Drop-in 后,必须通知 systemd 重新加载:

# 重新加载所有 Unit 文件(推荐)
systemctl daemon-reload

# 重新加载后,如果需要,重启服务
systemctl restart myapp.service

💡 提示daemon-reload 只重新加载 Unit 文件定义,不会重启正在运行的服务。修改配置后需要手动 restart 或 reload 服务。

⚠️ 注意daemon-reload 会重新评估所有 Unit 的依赖关系,可能影响正在等待其他 Unit 的启动队列。生产环境中建议在维护窗口操作。


八、systemctl edit 命令详解

# 编辑(创建 Drop-in 覆盖)
systemctl edit nginx.service

# 编辑(完全覆盖整个文件,原始文件仍保留)
systemctl edit --full nginx.service

# 创建新的 Unit 文件
systemctl edit --force --full myapp.service

# 查看编辑结果
systemctl cat nginx.service
选项说明
--full编辑完整文件而非 Drop-in
--force文件不存在时创建新文件
--runtime编辑运行时路径(重启后丢失)
--stdin从 stdin 读取配置

示例:从 stdin 创建 Unit

cat << 'EOF' | systemctl edit --force --full myapp.service --stdin
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

九、生产场景

场景 1:自定义 Nginx 启动参数

# 不修改原始文件,通过 Drop-in 添加模块
systemctl edit nginx.service
[Service]
ExecStart=
ExecStart=/usr/sbin/nginx -g 'daemon off; master_process on;' -c /etc/nginx/nginx-custom.conf
LimitNOFILE=65535

场景 2:为服务添加环境变量

# 创建环境文件
cat > /etc/myapp/env << 'EOF'
DATABASE_URL=postgresql://user:pass@localhost/myapp
REDIS_URL=redis://localhost:6379
LOG_LEVEL=info
EOF

# 创建 Drop-in
systemctl edit myapp.service
[Service]
EnvironmentFile=/etc/myapp/env

场景 3:限制服务的重启频率

systemctl edit myapp.service
[Service]
# 10 秒内最多重启 3 次,超过则进入 failed 状态
StartLimitBurst=3
StartLimitIntervalSec=10
Restart=on-failure
RestartSec=5

十、命令速查表

操作命令
查看 Unit 配置systemctl cat <unit>
编辑 Unitsystemctl edit <unit>
完全编辑systemctl edit --full <unit>
重载 Unit 文件systemctl daemon-reload
语法检查systemd-analyze verify
安全分析systemd-analyze security <unit>
查看 Unit 属性systemctl show <unit>
查看依赖systemctl list-dependencies <unit>

十一、扩展阅读