前言

现代 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 篇。