1. 为什么需要自定义systemd服务
每次重启服务器后都要手动启动应用?写了个Python脚本却不知道怎么让它24小时稳定运行?这些烦恼其实用一个简单的systemd服务就能解决。作为Linux系统的服务管家,systemd不仅能管理现有服务,更能让我们轻松把任何程序变成系统服务。
我最早接触systemd是在部署一个Node.js应用时。当时用nohup启动应用,结果终端一关服务就挂,日志也没地方看。后来学会用systemd后,不仅实现了开机自启,还能自动重启崩溃的服务,查看完整日志,管理起来特别省心。
现代Linux发行版(如Ubuntu 18.04+、CentOS 7+)都默认使用systemd。相比传统的init.d脚本,它有这些优势:
- 自动重启:服务崩溃后可以配置自动恢复
- 依赖管理:确保网络就绪后再启动你的服务
- 日志集成:直接用journalctl查看所有日志
- 资源控制:可以限制服务的内存、CPU使用
- 状态监控:随时查看服务运行状态
2. 编写第一个服务单元文件
2.1 文件存放位置
systemd服务文件通常放在三个目录:
/usr/lib/systemd/system:软件包安装的服务/etc/systemd/system:系统管理员自定义的服务(推荐)/run/systemd/system:运行时临时服务
建议把自己的服务放在/etc/systemd/system,这样不会被系统更新覆盖。比如我们要创建一个监控脚本的服务:
sudo vim /etc/systemd/system/monitor.service2.2 基础文件结构
一个最简单的服务文件包含三个部分:
[Unit] Description=我的监控服务 [Service] ExecStart=/usr/bin/python3 /home/user/monitor.py [Install] WantedBy=multi-user.target这个例子中:
Description:服务的简短描述ExecStart:启动服务的命令WantedBy:指定服务在哪个系统目标下启动
2.3 实际案例:Python脚本服务化
假设我们有个监控服务器温度的Python脚本/home/user/temp_monitor.py:
#!/usr/bin/python3 import time while True: print("当前温度:36.5℃") time.sleep(60)对应的服务文件可以这样写:
[Unit] Description=服务器温度监控服务 [Service] User=user WorkingDirectory=/home/user ExecStart=/usr/bin/python3 /home/user/temp_monitor.py Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target这里新增了几个实用配置:
User:指定运行用户WorkingDirectory:设置工作目录Restart:失败时自动重启RestartSec:重启间隔时间
3. 详解三大配置区块
3.1 [Unit]区块:定义服务元信息
这个区块控制服务的基本属性和依赖关系。常用选项:
| 参数 | 说明 | 示例 |
|---|---|---|
| Description | 服务描述 | Description=MySQL服务 |
| After | 指定依赖服务 | After=network.target |
| Requires | 强依赖关系 | Requires=mysql.service |
| Wants | 弱依赖关系 | Wants=redis.service |
| Conflicts | 冲突服务 | Conflicts=old-service |
实际案例:一个需要网络和数据库的服务配置
[Unit] Description=订单处理服务 After=network.target postgresql.service Wants=redis.service Conflicts=legacy-order.service3.2 [Service]区块:核心服务配置
这部分决定了服务如何运行。关键参数:
基础配置:
Type:服务类型(simple, forking, oneshot等)User/Group:运行用户和组WorkingDirectory:工作目录
进程控制:
ExecStart:启动命令ExecStop:停止命令ExecReload:重载配置命令Restart:重启策略
资源管理:
MemoryLimit:内存限制CPUQuota:CPU配额
完整示例:
[Service] Type=simple User=appuser Group=appgroup WorkingDirectory=/opt/myapp ExecStart=/opt/myapp/start.sh ExecStop=/opt/myapp/stop.sh ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=5 MemoryLimit=500M3.3 [Install]区块:安装配置
这部分定义如何"安装"服务(即启用/禁用):
| 参数 | 说明 | 示例 |
|---|---|---|
| WantedBy | 被哪些目标需要 | WantedBy=multi-user.target |
| RequiredBy | 被哪些目标强依赖 | RequiredBy=other.service |
| Alias | 服务别名 | Alias=myapp.service |
典型配置:
[Install] WantedBy=multi-user.target Alias=app.service4. 实战:将Go应用注册为系统服务
4.1 准备Go应用
假设我们有个Go编写的Web应用,编译后位于/opt/myapp/app,需要监听8080端口。
4.2 编写服务文件
创建/etc/systemd/system/myapp.service:
[Unit] Description=Go Web应用服务 After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple User=appuser Group=appgroup WorkingDirectory=/opt/myapp ExecStart=/opt/myapp/app Restart=always RestartSec=10 Environment="PORT=8080" EnvironmentFile=/etc/myapp.conf StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target关键点说明:
- 使用
Environment设置环境变量 EnvironmentFile加载配置文件StandardOutput和StandardError将日志输出到journal
4.3 设置权限和配置文件
sudo useradd -r -s /bin/false appuser sudo chown -R appuser:appgroup /opt/myapp sudo chmod 750 /opt/myapp创建配置文件/etc/myapp.conf:
DB_HOST=localhost DB_PORT=54324.4 启动并测试服务
sudo systemctl daemon-reload sudo systemctl start myapp sudo systemctl enable myapp journalctl -u myapp -f # 查看实时日志5. 高级配置与故障排查
5.1 资源限制配置
防止服务占用过多资源:
[Service] MemoryLimit=1G CPUQuota=150% LimitNOFILE=655355.2 多命令组合
复杂启动流程可以这样配置:
[Service] ExecStartPre=/bin/mkdir -p /var/run/myapp ExecStartPre=/bin/chown appuser /var/run/myapp ExecStart=/opt/myapp/start.sh ExecStopPost=/bin/rm -rf /var/run/myapp5.3 常见问题排查
服务启动失败:
sudo systemctl status myapp journalctl -xe配置文件重载:
sudo systemctl daemon-reload sudo systemctl restart myapp依赖检查:
systemctl list-dependencies myapp.service资源监控:
systemd-cgtop systemctl show myapp --property MemoryCurrent,CPUUsageNSec5.4 安全加固建议
- 使用专用系统用户运行服务
- 限制服务权限:
[Service] PrivateTmp=true ProtectSystem=full NoNewPrivileges=true- 启用沙盒:
[Service] RestrictAddressFamilies=AF_INET AF_INET6 RestrictNamespaces=true掌握这些技巧后,你可以把任何脚本、程序变成可靠的系统服务。我在生产环境用这套方法管理着数十个自定义服务,从没出现过服务意外退出的情况。