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

systemd 教程 / 生产环境最佳实践

生产环境最佳实践

在生产环境中使用 systemd 需要遵循一系列最佳实践,以确保服务的可靠性、安全性和可维护性。


1. Unit 文件编写规范

1.1 文件结构规范

# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Service
Documentation=https://myapp.example.com/docs

After=network.target postgresql.service
Requires=postgresql.service
Wants=redis.service

ConditionPathExists=/opt/myapp/bin/myapp

[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp

EnvironmentFile=-/etc/sysconfig/myapp

ExecStartPre=/opt/myapp/bin/pre-start.sh
ExecStart=/opt/myapp/bin/myapp --config /etc/myapp/config.yml
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/opt/myapp/bin/stop.sh

Restart=on-failure
RestartSec=10
StartLimitBurst=5
StartLimitIntervalSec=300

TimeoutStartSec=300
TimeoutStopSec=30
KillMode=mixed

LimitNOFILE=65535
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

1.2 文件放置规范

路径 用途 是否可编辑
/etc/systemd/system/ 管理员自定义服务
/usr/lib/systemd/system/ 发行版提供的服务 ❌(使用 override)
/run/systemd/system/ 运行时生成的服务 ❌(临时)

⚠️ 注意:永远不要直接修改 /usr/lib/systemd/system/ 下的文件,使用 systemctl edit 创建 override。


2. 服务依赖设计

2.1 After/Requires/Wants 选择

依赖类型 含义 使用场景
After= 启动顺序 必须在之后启动
Requires= 强依赖 依赖服务失败则本服务也失败
Wants= 弱依赖 依赖服务失败不影响本服务
Requisite= 必需依赖 依赖服务未运行则拒绝启动
Conflicts= 冲突 互斥服务

2.2 依赖设计示例

# 场景 1:Web 应用依赖数据库(强依赖)
[Unit]
After=network.target postgresql.service
Requires=postgresql.service    # 数据库必须运行
Wants=redis.service           # Redis 可选

# 场景 2:微服务架构(弱依赖避免级联失败)
[Unit]
After=network.target
Wants=auth-service.service inventory-service.service

3. 重启策略设计

3.1 重启参数配置

[Service]
# 重启条件
Restart=on-failure        # 失败时重启
Restart=on-abnormal       # 异常退出时重启
Restart=always            # 总是重启(不推荐)
Restart=no                # 不重启

# 重启间隔
RestartSec=10

# 启动限制(防止重启风暴)
StartLimitBurst=5         # 时间窗口内最多启动 5 次
StartLimitIntervalSec=300 # 5 分钟时间窗口

3.2 重启策略选择

场景 推荐策略 说明
Web 服务器 on-failure 正常退出不重启
后台守护进程 on-failure 正常退出不重启
一次性任务 no 执行一次即完成
关键服务 on-abnormal 仅异常退出时重启

3.3 看门狗(Watchdog)

[Service]
Type=notify
WatchdogSec=30
NotifyAccess=main
# 应用需要定期发送 WATCHDOG=1 通知 systemd

4. 日志策略

4.1 journald 配置

# /etc/systemd/journald.conf
[Journal]
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=1G
SystemMaxFileSize=100M
MaxRetentionSec=30day
Compress=yes
ForwardToSyslog=yes

4.2 日志轮转

# 手动清理旧日志
sudo journalctl --vacuum-time=30d
sudo journalctl --vacuum-size=2G

# 查看日志占用空间
journalctl --disk-usage

# 立即应用配置
sudo systemctl restart systemd-journald

5. 安全加固

5.1 最小权限原则

[Service]
User=myapp
Group=myapp
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ReadOnlyPaths=/etc/myapp
ReadWritePaths=/var/lib/myapp /var/log/myapp /run/myapp

5.2 Capabilities 限制

[Service]
# 仅授予必要权限
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_DAC_READ_SEARCH

# 或完全移除
CapabilityBoundingSet=

5.3 系统调用过滤

[Service]
SystemCallFilter=@system-service @network-io @file-system
SystemCallFilter=~@mount @reboot @swap @debug

5.4 网络限制

[Service]
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictSUIDSGID=true
RestrictRealtime=true

5.5 安全评分检查

# 评估服务安全等级
systemd-analyze security myapp.service
# 目标:< 2.0 (OK)

6. 资源限制(cgroups)

6.1 CPU 和内存

[Service]
CPUQuota=50%
CPUWeight=100
CPUAffinity=0 1 2 3
MemoryMax=1G
MemoryHigh=512M
OOMPolicy=continue

6.2 I/O 和进程

[Service]
IOWeight=500
IOReadBandwidthMax=/dev/sda 50M
TasksMax=100
LimitNOFILE=65535
LimitNPROC=65535

6.3 Slice 管理

# /etc/systemd/system/myapp.slice
[Unit]
Description=MyApp Slice

[Slice]
CPUQuota=80%
MemoryMax=2G
TasksMax=200

7. 监控集成

7.1 Prometheus node_exporter

# /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter
After=network.target

[Service]
Type=simple
User=prometheus
ExecStart=/usr/local/bin/node_exporter \
    --collector.systemd \
    --web.listen-address=:9100
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict

[Install]
WantedBy=multi-user.target

7.2 自定义健康检查

#!/bin/bash
# /opt/myapp/bin/healthcheck.sh

# 检查进程
if ! pgrep -x myapp > /dev/null; then
    echo "Process not running"; exit 1
fi

# 检查端口
if ! ss -tlnp | grep -q ":8080"; then
    echo "Port 8080 not listening"; exit 1
fi

# 检查 HTTP
if ! curl -sf http://localhost:8080/health > /dev/null; then
    echo "Health check failed"; exit 1
fi

echo "OK"

8. 配置管理集成

8.1 Ansible 示例

# deploy-service.yml
---
- name: Deploy systemd service
  hosts: webservers
  become: yes
  tasks:
    - name: Deploy systemd unit file
      template:
        src: templates/myapp.service.j2
        dest: "/etc/systemd/system/myapp.service"
      notify: Reload systemd

    - name: Enable and start service
      systemd:
        name: myapp
        enabled: yes
        state: started

  handlers:
    - name: Reload systemd
      systemd:
        daemon_reload: yes

8.2 Puppet 示例

file { '/etc/systemd/system/myapp.service':
  ensure  => file,
  content => template('myapp/myapp.service.erb'),
  notify  => Exec['systemctl-daemon-reload'],
}

exec { 'systemctl-daemon-reload':
  command     => '/bin/systemctl daemon-reload',
  refreshonly => true,
}

service { 'myapp':
  ensure => 'running',
  enable => true,
}

9. 生产 Checklist

9.1 部署前检查

检查项 命令 期望结果
Unit 文件语法 systemd-analyze verify myapp.service 无错误
安全评分 systemd-analyze security myapp.service < 2.0
依赖关系 systemctl list-dependencies myapp.service 所有依赖正常
用户存在 id myapp 用户存在
端口可用 ss -tlnp | grep :8080 端口未被占用

9.2 部署后检查

检查项 命令 期望结果
服务状态 systemctl status myapp.service active (running)
日志无错误 journalctl -u myapp.service -p err 无错误
端口监听 ss -tlnp | grep :8080 端口已监听
健康检查 curl http://localhost:8080/health 返回 OK

9.3 运维 Checklist

检查项 频率 说明
服务状态 每日 检查所有关键服务
日志检查 每日 检查错误和警告
资源使用 每周 CPU、内存、磁盘
安全更新 每月 更新 systemd
配置备份 每月 备份 Unit 文件

10. 故障恢复

10.1 快速回滚

# 使用版本化的 Unit 文件
# /etc/systemd/system/myapp.service -> myapp.service.v1.0

sudo cp /etc/systemd/system/myapp.service.v1.0 /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl restart myapp.service

⚠️ 注意事项

  1. 永远不要修改 /usr/lib/systemd/system/:使用 systemctl edit 创建 override
  2. 测试环境先验证:所有配置变更先在测试环境验证
  3. 配置版本控制:Unit 文件应纳入版本控制
  4. 监控告警:配置服务异常告警
  5. 定期审查:定期审查服务配置和安全设置

💡 提示

  • 使用 systemctl edit 创建 override 配置,保持原始文件不变
  • systemd-analyze security 可以快速评估服务安全等级
  • 使用 systemctl show 查看服务的所有配置属性
  • 使用 Slice 组织相关服务,便于资源管理
  • 配置 StartLimitBurstStartLimitIntervalSec 防止重启风暴

扩展阅读