一、从 init 到 systemd:现代 Linux 的服务管理范式
早期的 Linux 用 System V init 一套脚本逐个启动服务,启动顺序靠数字编号,并行能力差、管理命令混乱(service/rc 各不相同)。RHEL/CentOS 7、Debian 8、Ubuntu 16.04 起全面转向 systemd:它并行启动、统一用二进制 systemctl 管理、用 journald 日志、用 unit 文件描述服务,还支持依赖、资源控制、看门狗等高级特性。无论你用的是 Ubuntu/Rocky/AlmaLinux 还是 Debian,当前都在 systemd 体系下,掌握它是管理服务器的基础功。
二、最核心的 systemctl 日常命令
下面的命令覆盖 90% 的服务启停场景,务必记熟:
# 查看状态 systemctl status nginx systemctl is-active nginx # active/inactive systemctl is-enabled nginx # enabled/disabled,判断是否开机自启 # 启动/停止/重启/重载(服务未停止、只重读配置) sudo systemctl start nginx sudo systemctl stop nginx sudo systemctl restart nginx sudo systemctl reload nginx # 设置/取消开机自启 sudo systemctl enable nginx sudo systemctl enable --now nginx # 启用并立即启动 sudo systemctl disable nginx # 列出所有运行中或失败的服务 systemctl list-units --type=service --state=running systemctl list-units --type=service --state=failed
区分 restart 与 reload:reload 发送 SIGHUP 让进程重新读配置而不中断服务(Nginx、sshd、nginx reload 适合),restart 则是杀掉再起来、会短暂中断。能用 reload 就别轻易 restart。
三、编写一个自定义服务:实例演示
部署一个 Python 编写的 Web/定时任务时,我们希望它随系统启动、崩溃自动重启、还能开机制看日志。为此写一个 systemd unit 文件放在 /etc/systemd/system/myapp.service:
[Unit] Description=My Custom Python Application After=network.target # 等网络就绪后再启动 Wants=network-online.target [Service] Type=simple # 主进程直接前台运行即可 User=www-data # 以低权限用户运行,安全 WorkingDirectory=/opt/myapp ExecStart=/usr/bin/python3 /opt/myapp/app.py Restart=on-failure # 非正常退出自动重启 RestartSec=3 # 重启间隔 3 秒 Environment=PATH=/usr/bin:/usr/local/bin EnvironmentFile=-/etc/myapp/env # 可选的自定义环境变量文件,不存在不报错 [Install] WantedBy=multi-user.target # 多用户运行级即"开机自启"
写好后加载并启停:
sudo systemctl daemon-reload sudo systemctl enable --now myapp systemctl status myapp
Type 的选择很关键:simple(前台主进程)、forking(进程 fork 到后台、父进程退出)、oneshot(一次性任务,执行完即退出,适合开机脚本)。用错 Type 会导致 systemd 误判"服务是否已启动"。
四、用 journald 看日志与排错
systemd 把 stdout/stderr 和日志统一收进 journal,命令统一:
sudo journalctl -u myapp # 看该服务全部日志 sudo journalctl -u myapp -f # 实时跟踪 sudo journalctl -u myapp --since today # 从今天起 sudo journalctl -u myapp -p err # 只看错误级别 sudo journalctl -n 50 -u nginx # 最近 50 行
服务起不来的经典排查链:systemctl status 看状态与最近报错 → journalctl -xe 看上下文 → 检查 unit 文件权限(必须是 root 且不是组可写)→ daemon-reload 重新加载改动。ExecStart 里的路径错误、User 权限不足是最高频的两大坑。可用 systemd-analyze verify /etc/systemd/system/myapp.service 静态校验 unit 文件语法,用 systemd-analyze blame 查看开机耗时。
五、资源限制:给服务设置 CPU/内存上限
担心某服务失控吃满 CPU/内存?在 [Service] 段加资源控制指令即可(部分底层依赖 cgroups v2):
MemoryMax=512M # 内存上限,超限触发 OOM 杀进程 MemoryHigh=384M # 软上限,超过开始回收但尽量不杀 CPUQuota=50% # 最多占满半个核 TasksMax=200 # 限制进程/线程总数,防 fork 炸弹
重启后生效,也可用 systemctl show -p MemoryMax myapp 查看实际生效值。这一步在共享 VPS 或处理"僵尸进程刷爆系统"时极有用。
六、常见问题与最佳实践
把这几条坑提前规避,能省大量排错时间:
- unit 文件改完 必须执行
systemctl daemon-reload,否则不生效。 - 服务名对应 unit 文件名(去掉 .service),用 systemctl 时别写成可执行路径。
- 需要环境变量时用
Environment=或EnvironmentFile=-/path(前缀 - 表示文件缺失也不报错)。 - 希望开机自启,[Install] 段必须存在且执行过 enable。
- 日志持久化:默认 journal 在内存,重启会丢;可
journalctl --vacuum-size=200M控制大小或用 rsyslog 转发。
写在最后
systemd 是现在管理 Linux 服务器绕不开的一环。抓住三条主线——systemctl 命令族、unit 文件的编写、journalctl 日志排查,就足以应对绝大多数生产场景。再进阶到资源控制与 cgroups,你不仅能"把服务跑起来",还能做到"跑得稳、限得住、坏了能追"。从第一个自建服务开始,逐步告别"拿 nohup 裸奔"的做法。
