从零到一:手把手教你编写systemd服务单元文件
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] WantedBymulti-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] Useruser WorkingDirectory/home/user ExecStart/usr/bin/python3 /home/user/temp_monitor.py Restarton-failure RestartSec5s [Install] WantedBymulti-user.target这里新增了几个实用配置User指定运行用户WorkingDirectory设置工作目录Restart失败时自动重启RestartSec重启间隔时间3. 详解三大配置区块3.1 [Unit]区块定义服务元信息这个区块控制服务的基本属性和依赖关系。常用选项参数说明示例Description服务描述DescriptionMySQL服务After指定依赖服务Afternetwork.targetRequires强依赖关系Requiresmysql.serviceWants弱依赖关系Wantsredis.serviceConflicts冲突服务Conflictsold-service实际案例一个需要网络和数据库的服务配置[Unit] Description订单处理服务 Afternetwork.target postgresql.service Wantsredis.service Conflictslegacy-order.service3.2 [Service]区块核心服务配置这部分决定了服务如何运行。关键参数基础配置Type服务类型simple, forking, oneshot等User/Group运行用户和组WorkingDirectory工作目录进程控制ExecStart启动命令ExecStop停止命令ExecReload重载配置命令Restart重启策略资源管理MemoryLimit内存限制CPUQuotaCPU配额完整示例[Service] Typesimple Userappuser Groupappgroup WorkingDirectory/opt/myapp ExecStart/opt/myapp/start.sh ExecStop/opt/myapp/stop.sh ExecReload/bin/kill -HUP $MAINPID Restarton-failure RestartSec5 MemoryLimit500M3.3 [Install]区块安装配置这部分定义如何安装服务即启用/禁用参数说明示例WantedBy被哪些目标需要WantedBymulti-user.targetRequiredBy被哪些目标强依赖RequiredByother.serviceAlias服务别名Aliasmyapp.service典型配置[Install] WantedBymulti-user.target Aliasapp.service4. 实战将Go应用注册为系统服务4.1 准备Go应用假设我们有个Go编写的Web应用编译后位于/opt/myapp/app需要监听8080端口。4.2 编写服务文件创建/etc/systemd/system/myapp.service[Unit] DescriptionGo Web应用服务 Afternetwork.target postgresql.service Requirespostgresql.service [Service] Typesimple Userappuser Groupappgroup WorkingDirectory/opt/myapp ExecStart/opt/myapp/app Restartalways RestartSec10 EnvironmentPORT8080 EnvironmentFile/etc/myapp.conf StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target关键点说明使用Environment设置环境变量EnvironmentFile加载配置文件StandardOutput和StandardError将日志输出到journal4.3 设置权限和配置文件sudo useradd -r -s /bin/false appuser sudo chown -R appuser:appgroup /opt/myapp sudo chmod 750 /opt/myapp创建配置文件/etc/myapp.confDB_HOSTlocalhost DB_PORT54324.4 启动并测试服务sudo systemctl daemon-reload sudo systemctl start myapp sudo systemctl enable myapp journalctl -u myapp -f # 查看实时日志5. 高级配置与故障排查5.1 资源限制配置防止服务占用过多资源[Service] MemoryLimit1G CPUQuota150% LimitNOFILE655355.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] PrivateTmptrue ProtectSystemfull NoNewPrivilegestrue启用沙盒[Service] RestrictAddressFamiliesAF_INET AF_INET6 RestrictNamespacestrue掌握这些技巧后你可以把任何脚本、程序变成可靠的系统服务。我在生产环境用这套方法管理着数十个自定义服务从没出现过服务意外退出的情况。