本文系统讲解systemd的核心概念、服务管理、自定义Unit文件编写,以及常见问题排查。
前言
如果你还在用service xxx start或者写init.d脚本,是时候全面拥抱systemd了。
systemd是现代Linux的标准初始化系统,管理着系统启动、服务运行、日志记录等核心功能。
今天来彻底搞懂它。
一、systemd基础
1.1 什么是systemd
systemd是Linux系统的初始化系统和服务管理器,取代了传统的SysVinit。
主要功能:
- 系统初始化
- 服务管理
- 日志管理(journald)
- 定时任务(timer)
- 设备管理
1.2 核心概念:Unit
systemd管理的基本单位是Unit,有多种类型:
| 类型 | 后缀 | 说明 |
|---|---|---|
| Service | .service | 服务进程 |
| Socket | .socket | 套接字激活 |
| Timer | .timer | 定时器(替代cron) |
| Mount | .mount | 挂载点 |
| Target | .target | 启动目标(类似runlevel) |
| Path | .path | 路径监控 |
1.3 Unit文件位置
/etc/systemd/system/ # 系统管理员创建的(优先级最高)
/run/systemd/system/ # 运行时创建的
/lib/systemd/system/ # 软件包安装的
/usr/lib/systemd/system/ # 发行版提供的
优先级:/etc > /run > /lib
二、常用命令
2.1 服务管理
bash
# 启动服务
systemctl start nginx
# 停止服务
systemctl stop nginx
# 重启服务
systemctl restart nginx
# 重载配置(不重启进程)
systemctl reload nginx
# 查看状态
systemctl status nginx
# 开机自启
systemctl enable nginx
# 取消开机自启
systemctl disable nginx
# 开机自启并立即启动
systemctl enable --now nginx
2.2 查看服务
bash
# 列出所有服务
systemctl list-units --type=service
# 列出启用的服务
systemctl list-unit-files --type=service --state=enabled
# 列出失败的服务
systemctl list-units --failed
# 查看服务依赖
systemctl list-dependencies nginx
# 查看服务属性
systemctl show nginx
2.3 系统管理
bash
# 重启系统
systemctl reboot
# 关机
systemctl poweroff
# 进入救援模式
systemctl rescue
# 重载systemd配置
systemctl daemon-reload
三、编写Service Unit
3.1 基本结构
ini
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
Documentation=https://example.com/docs
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/start.sh
ExecStop=/opt/myapp/bin/stop.sh
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
3.2 [Unit]段详解
ini
[Unit]
# 描述
Description=My Web Application
# 文档链接
Documentation=man:nginx(8)
Documentation=https://nginx.org/
# 启动顺序(在...之后)
After=network.target mysql.service
# 依赖(需要...存在)
Requires=mysql.service
# 弱依赖(希望...存在,但不强制)
Wants=redis.service
# 冲突(不能和...同时运行)
Conflicts=apache2.service
3.3 [Service]段详解
ini
[Service]
# 服务类型
Type=simple # 默认,ExecStart进程就是主进程
Type=forking # 父进程退出,子进程成为主进程(传统daemon)
Type=oneshot # 一次性任务
Type=notify # 服务启动完成后通知systemd
Type=idle # 其他任务执行完后再启动
# 运行用户/组
User=www-data
Group=www-data
# 工作目录
WorkingDirectory=/opt/myapp
# 启动命令
ExecStart=/opt/myapp/start.sh
# 启动前执行
ExecStartPre=/opt/myapp/check.sh
# 启动后执行
ExecStartPost=/opt/myapp/notify.sh
# 停止命令
ExecStop=/opt/myapp/stop.sh
# 重载命令
ExecReload=/bin/kill -HUP $MAINPID
# 重启策略
Restart=no # 不重启
Restart=always # 总是重启
Restart=on-success # 正常退出时重启
Restart=on-failure # 异常退出时重启
Restart=on-abnormal # 信号终止时重启
# 重启间隔
RestartSec=5
# 环境变量
Environment=NODE_ENV=production
Environment=PORT=3000
EnvironmentFile=/etc/myapp/env
# 资源限制
LimitNOFILE=65535
LimitNPROC=4096
3.4 [Install]段详解
ini
[Install]
# 被哪个target启用(开机自启)
WantedBy=multi-user.target # 多用户模式(常用)
WantedBy=graphical.target # 图形界面模式
# 别名
Alias=myapp.service
四、实战案例
4.1 Node.js应用
ini
# /etc/systemd/system/node-app.service
[Unit]
Description=Node.js Application
After=network.target
[Service]
Type=simple
User=node
WorkingDirectory=/home/node/app
ExecStart=/usr/bin/node /home/node/app/server.js
Environment=NODE_ENV=production
Environment=PORT=3000
Restart=on-failure
RestartSec=10
# 日志
StandardOutput=journal
StandardError=journal
SyslogIdentifier=node-app
[Install]
WantedBy=multi-user.target
4.2 Java应用
ini
# /etc/systemd/system/java-app.service
[Unit]
Description=Java Spring Boot Application
After=network.target
[Service]
Type=simple
User=java
WorkingDirectory=/opt/java-app
ExecStart=/usr/bin/java -jar /opt/java-app/app.jar
ExecStop=/bin/kill -SIGTERM $MAINPID
# JVM参数
Environment=JAVA_OPTS=-Xms512m -Xmx1024m
Restart=on-failure
RestartSec=10
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
4.3 Python应用
ini
# /etc/systemd/system/python-app.service
[Unit]
Description=Python Flask Application
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/flask-app
ExecStart=/opt/flask-app/venv/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
4.4 组网客户端自启
如果使用组网软件(如星空组网),可以配置开机自启:
ini
# /etc/systemd/system/xingkong.service
[Unit]
Description=Xingkong Network Client
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/opt/xingkong/client
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
bash
# 启用
systemctl daemon-reload
systemctl enable xingkong
systemctl start xingkong
这样每次开机都会自动连接组网,方便远程管理。
五、Timer定时任务
5.1 Timer vs Cron
| 特性 | Timer | Cron |
|---|---|---|
| 日志 | 完整的journal日志 | 需要自己重定向 |
| 依赖 | 可以依赖其他服务 | 无 |
| 随机延迟 | 支持 | 不支持 |
| 错过执行 | 可以配置补执行 | 无 |
5.2 Timer示例
ini
# /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
ini
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
[Timer]
# 每天凌晨3点
OnCalendar=*-*-* 03:00:00
# 随机延迟0-30分钟(避免同时执行)
RandomizedDelaySec=1800
# 错过的执行在下次启动时补上
Persistent=true
[Install]
WantedBy=timers.target
bash
# 启用
systemctl enable backup.timer
systemctl start backup.timer
# 查看定时器
systemctl list-timers
5.3 OnCalendar语法
ini
# 每分钟
OnCalendar=*-*-* *:*:00
# 每小时
OnCalendar=*-*-* *:00:00
OnCalendar=hourly
# 每天凌晨
OnCalendar=*-*-* 00:00:00
OnCalendar=daily
# 每周一
OnCalendar=Mon *-*-* 00:00:00
OnCalendar=weekly
# 每月1号
OnCalendar=*-*-01 00:00:00
OnCalendar=monthly
# 工作日每天9点
OnCalendar=Mon..Fri *-*-* 09:00:00
六、日志管理
6.1 journalctl基础
bash
# 查看所有日志
journalctl
# 查看指定服务日志
journalctl -u nginx
# 实时跟踪
journalctl -u nginx -f
# 最近100行
journalctl -u nginx -n 100
# 今天的日志
journalctl -u nginx --since today
# 指定时间范围
journalctl -u nginx --since "2024-01-01" --until "2024-01-02"
# 内核日志
journalctl -k
# 系统启动日志
journalctl -b
6.2 日志级别
bash
# 只看错误
journalctl -u nginx -p err
# 级别:
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
6.3 日志清理
bash
# 查看日志占用空间
journalctl --disk-usage
# 保留最近1周
journalctl --vacuum-time=1week
# 限制大小
journalctl --vacuum-size=500M
# 配置文件
# /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
MaxRetentionSec=1week
七、故障排查
7.1 服务启动失败
bash
# 查看详细状态
systemctl status myapp -l
# 查看完整日志
journalctl -u myapp -n 50
# 常见问题:
# 1. 权限问题:检查User/Group
# 2. 路径问题:检查WorkingDirectory/ExecStart
# 3. 依赖问题:检查After/Requires
7.2 检查Unit文件语法
bash
# 验证语法
systemd-analyze verify /etc/systemd/system/myapp.service
# 查看服务属性
systemctl show myapp
# 查看启动时间
systemd-analyze blame
7.3 重载配置
bash
# 修改Unit文件后必须执行
systemctl daemon-reload
# 然后重启服务
systemctl restart myapp
八、高级特性
8.1 资源限制
ini
[Service]
# CPU限制(100%=1核)
CPUQuota=50%
# 内存限制
MemoryMax=512M
MemoryHigh=400M
# IO限制
IOWeight=100
IOReadBandwidthMax=/dev/sda 10M
# 进程数限制
TasksMax=100
8.2 安全隔离
ini
[Service]
# 只读文件系统
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp
# 私有/tmp
PrivateTmp=true
# 禁止网络
PrivateNetwork=true
# 禁止提权
NoNewPrivileges=true
# 能力限制
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
8.3 Socket激活
ini
# /etc/systemd/system/myapp.socket
[Unit]
Description=MyApp Socket
[Socket]
ListenStream=8080
Accept=false
[Install]
WantedBy=sockets.target
启用后,服务只在有连接时才启动,节省资源。
九、总结
systemd服务管理要点:
- 基本操作:start/stop/restart/enable/status
- Unit文件:[Unit]/[Service]/[Install]三段结构
- 服务类型:simple最常用,forking用于传统daemon
- 重启策略:on-failure适合大多数场景
- 定时任务:Timer替代cron,功能更强
- 日志管理:journalctl查看,定期清理
快速模板:
ini
[Unit]
Description=XXX
After=network.target
[Service]
Type=simple
User=xxx
WorkingDirectory=/path/to/app
ExecStart=/path/to/app/start
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
参考资料
- systemd官方文档:https://systemd.io/
- Arch Wiki - systemd:https://wiki.archlinux.org/title/Systemd
- man systemd.service
💡 建议:把常用的服务都改成systemd管理,比写脚本放/etc/rc.local优雅多了。