前言
现代 Linux(CentOS 7+/Debian 8+)的 1 号进程都是 systemd,服务管理早已是它的天下。systemctl 是每个运维每天都要敲的命令,这篇把常用姿势和进阶玩法一次讲透。
一、基础操作
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 # 自启 + 立即启动(一步到位)
systemctl is-active nginx # 是否运行中
systemctl is-enabled nginx # 是否自启
看懂 status 输出:
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled)
Active: active (running) since Sat 2023-09-09 10:00:00 CST; 3 days ago
Main PID: 1234 (nginx)
Tasks: 3 (limit: 4915)
Memory: 12.5M
CPU: 1.204s
CGroup: /system.slice/nginx.service
├─1234 nginx: master process
└─1235 nginx: worker process
Sep 09 10:00:00 web01 systemd[1]: Started nginx...
重点关注:Active 状态(active (running) / failed)、enabled 自启与否、Main PID、最近的日志行。failed 状态时把最后几行日志读完,八成原因就在里面。
二、Unit 文件结构
服务定义文件位于 /usr/lib/systemd/system/(软件包安装)或 /etc/systemd/system/(自定义,优先级更高)。
# /etc/systemd/system/myapp.service
[Unit]
Description=My Go Application # 描述
After=network-online.target # 在网络就绪后再启动
Wants=network-online.target # 弱依赖: 拉起它, 失败不阻塞
[Service]
Type=simple # 启动类型
ExecStart=/opt/myapp/bin/server --port 8080
ExecReload=/bin/kill -HUP $MAINPID # reload 时执行的命令
Restart=on-failure # 异常退出自动拉起
RestartSec=5s # 重启间隔
User=app # 运行用户(最小权限!)
WorkingDirectory=/opt/myapp
Environment=GIN_MODE=release # 环境变量
LimitNOFILE=65536 # 文件描述符上限
[Install]
WantedBy=multi-user.target # 挂到哪个启动级别
Type 四种取值:
| Type | 含义 | 适用 |
|---|---|---|
simple |
ExecStart 启动的进程就是主进程 | 前台程序(默认) |
forking |
进程会 fork 到后台,父进程退出 | 传统守护进程 |
notify |
进程主动发信号告知就绪 | 支持 sd_notify 的程序 |
oneshot |
跑完就退出 | 初始化脚本 |
修改 unit 后必须:
systemctl daemon-reload # 重载 unit 定义
systemctl restart myapp
三、依赖与启动顺序
After=/Before=:只控制顺序,不拉起对方Wants=:弱依赖,拉起失败也继续Requires=:强依赖,失败则自己跟着失败BindsTo=:绑定命运,对方停了自己也停
常见组合:数据库服务写 After=network-online.target + Wants=network-online.target,避免网络没好就启动连库失败。
四、journalctl:日志的正确打开方式
journalctl -u nginx # 某服务的全部日志
journalctl -u nginx -f # 实时跟踪(像 tail -f)
journalctl -u nginx -n 50 # 最近 50 行
journalctl -u nginx --since today
journalctl --since "2023-09-09 10:00" --until "2023-09-09 12:00"
journalctl -p err -b # 本次开机以来的 error 级日志
journalctl -b -1 # 上一次开机的日志(排查重启原因神器)
journalctl --disk-usage # 日志占用
journalctl --vacuum-size=500M # 收缩到 500M
五、实战:把 Go 程序做成系统服务
# 1. 二进制就位
install -m 755 /root/server /opt/myapp/bin/server
# 2. 写 unit(用上面 myapp.service 的模板)
vim /etc/systemd/system/myapp.service
# 3. 启用
systemctl daemon-reload
systemctl enable --now myapp
systemctl status myapp # active (running) 即成功
# 4. 验证开机自启与崩溃自愈
systemctl is-enabled myapp # enabled
kill -9 $(systemctl show -p MainPID --value myapp)
sleep 6 && systemctl is-active myapp # active —— Restart=on-failure 生效
六、其他常用单位类型
systemctl list-units --type=service --state=running # 正在跑的服务
systemctl list-unit-files --type=service # 全部服务及自启状态
systemctl list-timers # 定时任务(下篇细讲)
systemctl reboot / poweroff / suspend # 电源管理
systemctl isolate multi-user.target # 切到无图形界面
常见坑
| 现象 | 原因 |
|---|---|
| 改了 unit 不生效 | 忘了 daemon-reload |
| start 显示 active 但秒变 failed | 用 status 看退出码 + journalctl -u xx 看报错 |
| 程序跑了几小时被杀 | 没配 Restart;或内存超限被 OOM(dmesg | grep -i kill) |
| 中文日志乱码 | journalctl 按 locale 显示,export LANG=en_US.UTF-8 试试 |
| 服务以 root 跑被安全扫描告警 | unit 里加 User= 切普通用户 |
小结
- 日常三板斧:
status→journalctl -u→restart - 自定义服务记住模板:
[Unit]描述依赖、[Service]命令与自愈、[Install]挂载级别 enable --now、daemon-reload、-b -1是三个高频却总被忘的命令
本文是「Linux 运维」系列第 3 篇。