在 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
  • 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:验证服务是否正常运行

  1. 查看服务状态:

    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
    
  2. 查看日志(验证程序输出):

    # 查看实时日志
    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"
    
  3. 验证开机自启(可选):

    # 重启系统
    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 配置文件,关键在于:

  1. ExecStart 必须用绝对路径;
  2. 合理设置 Type 和 Restart 策略;
  3. 用普通用户运行服务(安全最佳实践);
  4. 修改配置后执行 systemctl daemon-reload

通过以上步骤,你的程序可以被 systemd 统一管理,支持开机自启、故障自动重启、日志集中查看,适用于服务器后台程序的长期稳定运行。