1. 为什么需要Systemd管理服务
在Ubuntu 18.04之前的版本中,我们习惯用/etc/rc.local或者update-rc.d来管理开机启动项。但自从Ubuntu 16.04开始引入Systemd后,这套机制发生了根本性变化。Systemd作为新一代初始化系统,提供了更强大的服务管理能力。
我刚开始接触Systemd时也踩过不少坑。记得有一次部署Web服务,按照老方法配置rc.local,结果重启后服务死活起不来。后来才发现是Systemd的服务依赖关系没处理好。Systemd不仅能管理服务启停,还能精确控制启动顺序、依赖关系、资源限制等,这对生产环境尤为重要。
2. 创建你的第一个Systemd服务
2.1 服务文件存放位置
Systemd的服务配置文件主要存放在两个位置:
- /lib/systemd/system/:系统安装包默认存放位置
- /etc/systemd/system/:管理员自定义服务的首选位置
我强烈建议将自定义服务放在/etc/systemd/system/下,这样即使系统升级也不会被覆盖。比如我们要创建一个名为myapp的服务:
sudo vim /etc/systemd/system/myapp.service2.2 基础服务配置模板
一个最简单的服务文件长这样:
[Unit] Description=My Custom Application [Service] ExecStart=/usr/bin/python3 /opt/myapp/main.py Restart=always User=myuser [Install] WantedBy=multi-user.target这个配置做了几件事:
- 定义服务描述
- 指定启动命令(以myuser身份运行Python脚本)
- 设置崩溃后自动重启
- 声明在multi-user.target时启动
3. 详解Systemd服务配置
3.1 [Unit]区块配置
[Unit]区块定义了服务的元数据和依赖关系。几个常用参数:
[Unit] Description=High-performance web server After=network.target mysql.service Requires=mysql.service Wants=redis.service Conflicts=old-webserver.service- After:指定在哪些服务之后启动
- Requires:强依赖,依赖服务失败则本服务不会启动
- Wants:弱依赖,依赖服务失败不影响本服务
- Conflicts:指定不能共存的服
3.2 [Service]区块核心参数
[Service]区块控制服务运行行为:
[Service] Type=simple User=www-data Group=www-data WorkingDirectory=/var/www/myapp Environment="DB_HOST=localhost" "DB_PORT=3306" ExecStartPre=/bin/mkdir -p /var/log/myapp ExecStart=/usr/bin/python3 server.py ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=5s TimeoutStopSec=30特别说明几个关键参数:
- Type:有simple、forking、oneshot等类型,根据程序特性选择
- Environment:设置环境变量,支持多个变量
- ExecStartPre:主程序启动前执行的命令
- RestartSec:重启间隔时间
3.3 [Install]区块配置
[Install]决定服务如何安装到系统:
[Install] WantedBy=multi-user.target Alias=myapp.service Also=myapp-logger.serviceWantedBy是最重要的参数,指定服务所属的target。常见的target有:
- multi-user.target:多用户命令行模式
- graphical.target:图形界面模式
- network-online.target:网络就绪后
4. 实战:部署需要sudo权限的服务
4.1 处理sudo权限问题
对于需要root权限的服务,有几种解决方案:
- 直接以root用户运行(不推荐):
[Service] User=root- 通过sudoers文件授权(推荐):
# 在/etc/sudoers.d/下创建文件 echo "myuser ALL=(root) NOPASSWD: /path/to/command" | sudo tee /etc/sudoers.d/myapp然后在服务文件中:
[Service] User=myuser ExecStart=/usr/bin/sudo /path/to/command4.2 完整示例:Nginx服务
[Unit] Description=The NGINX HTTP and reverse proxy server After=syslog.target network-online.target remote-fs.target nss-lookup.target Wants=network-online.target [Service] Type=forking User=nginx Group=nginx ExecStartPre=/usr/sbin/nginx -t ExecStart=/usr/sbin/nginx ExecReload=/usr/sbin/nginx -s reload ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true Restart=on-failure RestartSec=5s TimeoutStopSec=5s [Install] WantedBy=multi-user.target5. 服务管理实操指南
5.1 常用systemctl命令
# 启用开机启动 sudo systemctl enable myapp.service # 立即启动服务 sudo systemctl start myapp # 查看服务状态 sudo systemctl status myapp # 重新加载配置 sudo systemctl daemon-reload # 查看日志 journalctl -u myapp -f5.2 调试技巧
当服务启动失败时,我常用的排查步骤:
- 检查服务配置语法:
systemd-analyze verify /etc/systemd/system/myapp.service- 手动运行命令:
sudo -u myuser /path/to/command- 查看详细日志:
journalctl -u myapp -xe --no-pager- 测试服务超时设置:
timeout 10s systemctl start myapp6. 高级配置技巧
6.1 资源限制
Systemd可以方便地限制服务资源:
[Service] MemoryLimit=500M CPUQuota=50% IOWeight=100 LimitNOFILE=655356.2 环境变量管理
推荐使用EnvironmentFile而不是直接在服务文件中写变量:
[Service] EnvironmentFile=/etc/myapp/envenv文件内容示例:
DB_HOST=localhost DB_PORT=33066.3 多命令服务
对于需要执行多个命令的服务:
[Service] ExecStartPre=/bin/mkdir -p /var/run/myapp ExecStart=/usr/bin/myapp --daemonize ExecStartPost=/bin/echo "Service started at $(date)" >> /var/log/myapp.log7. 生产环境最佳实践
根据我在多个项目中的经验,总结几点建议:
- 为每个服务创建专用用户:
sudo useradd -r -s /bin/false myappuser- 做好日志管理:
[Service] StandardOutput=syslog StandardError=syslog SyslogIdentifier=myapp- 设置合理的重启策略:
Restart=on-failure RestartSec=5s StartLimitInterval=100s StartLimitBurst=5- 使用模板服务文件(适用于多个相似实例):
sudo cp /etc/systemd/system/myapp@.service /etc/systemd/system/myapp@production.service- 定期检查服务状态:
systemd-cgtop systemd-analyze critical-chain myapp.service