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

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.serviceactive (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 防止重启风暴

扩展阅读