本文系统讲解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 服务管理
# 启动服务systemctl start nginx# 停止服务systemctl stop nginx# 重启服务systemctl restart nginx# 重载配置(不重启进程)systemctl reload nginx# 查看状态systemctl status nginx# 开机自启systemctlenablenginx# 取消开机自启systemctl disable nginx# 开机自启并立即启动systemctlenable--now nginx2.2 查看服务
# 列出所有服务systemctl list-units --type=service# 列出启用的服务systemctl list-unit-files --type=service --state=enabled# 列出失败的服务systemctl list-units --failed# 查看服务依赖systemctl list-dependencies nginx# 查看服务属性systemctl show nginx2.3 系统管理
# 重启系统systemctlreboot# 关机systemctl poweroff# 进入救援模式systemctl rescue# 重载systemd配置systemctl daemon-reload三、编写Service Unit
3.1 基本结构
# /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.target3.2 [Unit]段详解
[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.service3.3 [Service]段详解
[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=40963.4 [Install]段详解
[Install] # 被哪个target启用(开机自启) WantedBy=multi-user.target # 多用户模式(常用) WantedBy=graphical.target # 图形界面模式 # 别名 Alias=myapp.service四、实战案例
4.1 Node.js应用
# /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.target4.2 Java应用
# /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.target4.3 Python应用
# /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.target4.4 组网客户端自启
如果使用组网软件(如星空组网),可以配置开机自启:
# /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# 启用systemctl daemon-reload systemctlenablexingkong systemctl start xingkong这样每次开机都会自动连接组网,方便远程管理。
五、Timer定时任务
5.1 Timer vs Cron
| 特性 | Timer | Cron |
|---|---|---|
| 日志 | 完整的journal日志 | 需要自己重定向 |
| 依赖 | 可以依赖其他服务 | 无 |
| 随机延迟 | 支持 | 不支持 |
| 错过执行 | 可以配置补执行 | 无 |
5.2 Timer示例
# /etc/systemd/system/backup.service [Unit] Description=Daily Backup [Service] Type=oneshot ExecStart=/opt/scripts/backup.sh# /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# 启用systemctlenablebackup.timer systemctl start backup.timer# 查看定时器systemctl list-timers5.3 OnCalendar语法
# 每分钟 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基础
# 查看所有日志journalctl# 查看指定服务日志journalctl -u nginx# 实时跟踪journalctl -u nginx -f# 最近100行journalctl -u nginx -n100# 今天的日志journalctl -u nginx --since today# 指定时间范围journalctl -u nginx --since"2024-01-01"--until"2024-01-02"# 内核日志journalctl -k# 系统启动日志journalctl -b6.2 日志级别
# 只看错误journalctl -u nginx -p err# 级别:# 0: emerg# 1: alert# 2: crit# 3: err# 4: warning# 5: notice# 6: info# 7: debug6.3 日志清理
# 查看日志占用空间journalctl --disk-usage# 保留最近1周journalctl --vacuum-time=1week# 限制大小journalctl --vacuum-size=500M# 配置文件# /etc/systemd/journald.conf[Journal]SystemMaxUse=500MMaxRetentionSec=1week七、故障排查
7.1 服务启动失败
# 查看详细状态systemctl status myapp -l# 查看完整日志journalctl -u myapp -n50# 常见问题:# 1. 权限问题:检查User/Group# 2. 路径问题:检查WorkingDirectory/ExecStart# 3. 依赖问题:检查After/Requires7.2 检查Unit文件语法
# 验证语法systemd-analyze verify /etc/systemd/system/myapp.service# 查看服务属性systemctl show myapp# 查看启动时间systemd-analyze blame7.3 重载配置
# 修改Unit文件后必须执行systemctl daemon-reload# 然后重启服务systemctl restart myapp八、高级特性
8.1 资源限制
[Service] # CPU限制(100%=1核) CPUQuota=50% # 内存限制 MemoryMax=512M MemoryHigh=400M # IO限制 IOWeight=100 IOReadBandwidthMax=/dev/sda 10M # 进程数限制 TasksMax=1008.2 安全隔离
[Service] # 只读文件系统 ProtectSystem=strict ReadWritePaths=/var/lib/myapp # 私有/tmp PrivateTmp=true # 禁止网络 PrivateNetwork=true # 禁止提权 NoNewPrivileges=true # 能力限制 CapabilityBoundingSet=CAP_NET_BIND_SERVICE8.3 Socket激活
# /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查看,定期清理
快速模板:
[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优雅多了。