在 Linux 系统中(如 Ubuntu、CentOS 7+、Debian 9+),systemctl 是 systemd 系统管理器的命令行工具,用于管理后台服务(service)。创建一个 systemctl 服务,本质是编写一个 systemd 服务配置文件(后缀 .service),定义服务的启动命令、运行方式、依赖关系等。
以下是详细的创建步骤、配置说明和实操示例,适用于绝大多数场景(如启动 Python/Java/Go 程序、Shell 脚本等)。
一、核心基础:systemd 服务文件的结构与位置
1. 服务文件存放位置(二选一)
- 系统级服务(所有用户可用,推荐):
/etc/systemd/system/(优先级高,自定义服务优先放这里) - 用户级服务(仅当前用户可用):
~/.config/systemd/user/(需加--user参数管理)
2. 服务文件核心结构
一个标准的 .service 文件包含 3 个核心区块:[Unit](单元描述)、[Service](服务核心配置)、[Install](安装配置)。
| 区块 | 作用 | 关键参数(必填 / 常用) |
|---|---|---|
[Unit] |
描述服务的元信息、依赖关系 | Description(服务描述)、After(启动顺序,如 network.target 表示网络启动后再启动) |
[Service] |
定义服务的运行规则(核心区块) | Type(服务类型)、ExecStart(启动命令)、ExecStop(停止命令)、User(运行用户) |
[Install] |
定义服务的安装方式(开机自启相关) | WantedBy(开机自启的目标级,如 multi-user.target 多用户命令行模式) |
二、 step-by-step:创建 systemctl 服务
以 启动一个 Python 脚本 为例(其他程序如 Java/Go/Shell 仅需修改 ExecStart 命令),全程实操:
准备工作:待管理的程序
假设我们有一个后台运行的 Python 脚本 (/opt/myapp/main.py),功能是打印日志到文件:
# /opt/myapp/main.py
import time
import logging
# 配置日志(输出到文件)
logging.basicConfig(
filename='/opt/myapp/app.log',
level=logging.INFO,
format='%(asctime)s - %(message)s'
)
if __name__ == "__main__":
while True:
logging.info("服务正在运行...")
time.sleep(5) # 每5秒输出一次日志
确保脚本可执行,且依赖已安装:
# 给脚本添加执行权限
chmod +x /opt/myapp/main.py
# 安装依赖(若有)
pip3 install logging # 示例,实际按需安装
步骤 1:创建服务文件
在 /etc/systemd/system/ 目录下创建服务文件(文件名自定义,后缀 .service,如 myapp.service):
sudo nano /etc/systemd/system/myapp.service
步骤 2:编写服务配置
将以下内容复制到文件中,根据实际情况修改关键参数(注释已说明):
[Unit]
# 服务描述(自定义,便于识别)
Description=My Python App Service
# 启动顺序:网络启动后、系统日志启动后再启动当前服务(按需调整)
After=network.target syslog.target
[Service]
# 服务类型:Simple(默认,前台运行,systemd 直接管理进程)
# 其他类型:forking(后台运行,程序启动后会 fork 子进程,父进程退出)
Type=Simple
# 运行用户(推荐用普通用户,避免 root 权限风险,如 ubuntu、www-data)
User=ubuntu
# 运行组(可选,与 User 一致即可)
Group=ubuntu
# 工作目录(程序运行的目录,按需修改)
WorkingDirectory=/opt/myapp
# 启动命令(必须用绝对路径!重点修改这里)
# 示例:/usr/bin/python3 是 Python 解释器路径,/opt/myapp/main.py 是脚本路径
ExecStart=/usr/bin/python3 /opt/myapp/main.py
# 停止命令(可选,若不写,systemd 会默认发送 SIGTERM 信号终止进程)
ExecStop=/usr/bin/pkill -f "/usr/bin/python3 /opt/myapp/main.py"
# 重启策略:always(服务退出时总是重启)
# 其他选项:on-failure(仅失败时重启)、no(不重启)、on-abort(异常终止时重启)
Restart=always
# 重启延迟(重启前等待 3 秒,避免频繁重启)
RestartSec=3
# 日志输出(将服务日志转发到系统日志,便于用 journalctl 查看)
StandardOutput=syslog
StandardError=syslog
# 日志标识(自定义日志前缀,便于过滤)
SyslogIdentifier=myapp
[Install]
# 开机自启的目标级:multi-user.target(多用户命令行模式,最常用)
# 若服务需要图形界面,用 graphical.target
WantedBy=multi-user.target
关键参数修改说明(必看)
ExecStart:必须写 绝对路径!可以用which python3查找 Python 解释器路径,which java查找 Java 路径。- 示例(Java 程序):
ExecStart=/usr/bin/java -jar /opt/myapp/app.jar - 示例(Shell 脚本):
ExecStart=/bin/bash /opt/myapp/start.sh
- 示例(Java 程序):
User:避免用root,若程序需要端口 80/443,可通过setcap授权(如sudo setcap 'cap_net_bind_service=+ep' /usr/bin/python3)。Type:若程序是后台运行(如 Java -jar 启动后不会占用终端),用Simple即可;若程序启动后会 fork 子进程(如部分老程序),用forking。
步骤 3:加载并启用服务
服务文件创建后,systemd 不会自动识别,需执行以下命令:
1. 重新加载 systemd 配置(修改服务文件后必须执行)
sudo systemctl daemon-reload
2. 启动服务
sudo systemctl start myapp.service
# 简写(服务名可省略 .service):sudo systemctl start myapp
3. 查看服务状态(验证是否启动成功)
sudo systemctl status myapp.service
- 成功状态:显示
active (running),且无报错。 - 失败状态:显示
failed,可通过日志排查问题(下文有日志查看方法)。
4. 设置开机自启(可选,按需开启)
sudo systemctl enable myapp.service
5. 其他常用命令
| 功能 | 命令 |
|---|---|
| 停止服务 | sudo systemctl stop myapp |
| 重启服务 | sudo systemctl restart myapp |
| 禁用开机自启 | sudo systemctl disable myapp |
| 查看服务是否开机自启 | sudo systemctl is-enabled myapp |
| 查看服务日志(实时) | sudo journalctl -u myapp -f |
步骤 4:验证服务是否正常运行
-
查看服务状态:
sudo systemctl status myapp正常输出示例:
● myapp.service - My Python App Service Loaded: loaded (/etc/systemd/system/myapp.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2025-08-15 16:30:00 CST; 5min ago Main PID: 12345 (python3) Tasks: 1 (limit: 4915) Memory: 10.0M CGroup: /system.slice/myapp.service └─12345 /usr/bin/python3 /opt/myapp/main.py -
查看日志(验证程序输出):
# 查看实时日志 sudo journalctl -u myapp -f # 查看历史日志(最近 100 行) sudo journalctl -u myapp -n 100 # 按时间过滤日志(2025-08-15 16:00 之后的) sudo journalctl -u myapp --since "2025-08-15 16:00" -
验证开机自启(可选):
# 重启系统 sudo reboot # 重启后查看服务状态 sudo systemctl status myapp
三、常见问题排查(避坑指南)
1. 服务启动失败:active (failed)
- 核心排查:查看日志,找到报错原因:
sudo journalctl -u myapp -f - 常见原因及解决:
ExecStart路径错误:用which 命令确认路径(如which python3),确保绝对路径正确。- 权限不足:检查程序文件、日志目录的权限(如
sudo chown -R ubuntu:ubuntu /opt/myapp)。 - 依赖缺失:程序依赖的库 / 环境未安装(如 Python 库、Java 环境)。
2. 开机自启不生效
- 检查
WantedBy参数:确保是multi-user.target(命令行模式)或graphical.target(图形界面)。 - 重新启用自启:
sudo systemctl disable myapp && sudo systemctl enable myapp - 检查服务是否被禁用:
sudo systemctl is-enabled myapp(输出enabled才是启用状态)。
3. 服务无法停止:ExecStop 失效
- 手动终止进程:
# 查找进程 ID ps aux | grep myapp # 杀死进程 sudo kill -9 进程ID - 优化
ExecStop命令:用进程名或路径精准匹配,避免误杀:# 示例:用 pkill 匹配脚本路径 ExecStop=/usr/bin/pkill -f "/opt/myapp/main.py" # 或用 killall(按进程名) ExecStop=/usr/bin/killall python3
4. 日志看不到程序输出
- 检查
StandardOutput和StandardError配置:确保设置为syslog或journal+console。 - 直接查看程序自身的日志文件(如示例中的
/opt/myapp/app.log)。
四、不同程序的服务配置示例(直接复用)
示例 1:Java 程序(Jar 包)
[Unit]
Description=My Java App Service
After=network.target
[Service]
Type=Simple
User=ubuntu
WorkingDirectory=/opt/myjavaapp
ExecStart=/usr/bin/java -jar app.jar --spring.profiles.active=prod
ExecStop=/usr/bin/pkill -f "java -jar app.jar"
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
示例 2:Shell 脚本(后台运行)
[Unit]
Description=My Shell Script Service
After=network.target
[Service]
Type=Simple
User=ubuntu
WorkingDirectory=/opt/myshellapp
ExecStart=/bin/bash start.sh
ExecStop=/bin/bash stop.sh
Restart=always
[Install]
WantedBy=multi-user.target
示例 3:Go 程序(编译后的二进制文件)
[Unit]
Description=My Go App Service
After=network.target
[Service]
Type=Simple
User=ubuntu
WorkingDirectory=/opt/mygoapp
ExecStart=/opt/mygoapp/myapp # 直接执行二进制文件
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
总结
创建 systemctl 服务的核心是 编写正确的 .service 配置文件,关键在于:
ExecStart必须用绝对路径;- 合理设置
Type和Restart策略; - 用普通用户运行服务(安全最佳实践);
- 修改配置后执行
systemctl daemon-reload。
通过以上步骤,你的程序可以被 systemd 统一管理,支持开机自启、故障自动重启、日志集中查看,适用于服务器后台程序的长期稳定运行。