Linux Systemd 服务注册指南

一、什么是 Systemd

Systemd 是 Linux 系统的初始化系统和服务管理器,负责管理系统启动过程中的所有服务(称为"单元")。每个服务对应一个 .service 单元文件,通过 systemctl 命令进行管理。

二、创建服务单元文件

服务单元文件通常放在以下两个目录之一:

目录 说明
/etc/systemd/system/ 推荐,管理员自定义服务,优先级最高
/usr/lib/systemd/system/ 软件包安装时自动放置,不建议手动修改

创建命令:

shell 复制代码
sudo vim /etc/systemd/system/myapp.service

文件名建议与你的服务名保持一致,后缀必须为 .service

三、编写服务配置

一个标准的 .service 文件包含三个区块:[Unit]、[Service]、[Install]。

3.1 基本模板

ini 复制代码
[Unit]
Description = My Application Service
After = network.target

[Service]
Type = simple
ExecStart = /usr/bin/python3 /opt/myapp/app.py
WorkingDirectory = /opt/myapp
User = www-data
Group = www-data
Restart = on-failure
RestartSec = 5
StandardOutput = journal
StandardError = journal

[Install]
WantedBy = multi-user.target

3.2 字段详解

Unit 区块 --- 描述与依赖

字段 说明 示例
Description 服务描述 Description=My Web App
After 在哪些服务之后启动 After=network.target
Requires 强依赖(依赖失败则本服务也终止) Requires=postgresql.service
Wants 弱依赖(依赖失败不影响本服务) Wants=network-online.target

Service 区块 --- 运行方式

字段 说明 可选值/示例
Type 服务类型 simple(默认,前台进程)、forking(守护进程)、oneshot(一次性任务)
ExecStart 启动命令(必须用绝对路径) ExecStart=/usr/bin/node /opt/app/server.js
ExecStop 停止命令(可选,默认发送 SIGTERM) ExecStop=/bin/kill -SIGTERM $MAINPID
ExecReload 重载配置命令 ExecReload=/bin/kill -HUP $MAINPID
WorkingDirectory 工作目录 WorkingDirectory=/opt/myapp
User / Group 运行用户和组(建议非 root) User=www-data
Restart 重启策略 no、on-failure、always、on-abnormal
RestartSec 重启间隔秒数 RestartSec=5
Environment 环境变量 Environment="NODE_ENV=production"
EnvironmentFile 从文件读取环境变量 EnvironmentFile=/opt/myapp/.env
StandardOutput 标准输出方式 journal、file:/var/log/myapp.log
StandardError 标准错误输出方式 journal、file:/var/log/myapp-error.log
LimitNOFILE 文件描述符限制 LimitNOFILE=65535

Install 区块 --- 安装目标

字段 说明 示例
WantedBy 关联到哪个 target WantedBy=multi-user.target
RequiredBy 强依赖安装目标 RequiredBy=graphical.target

常见 target:

  • multi-user.target:多用户文本模式(服务器常用)
  • graphical.target:图形界面模式

四、注册与启动服务

修改或创建 .service 文件后,按以下步骤操作:

shell 复制代码
# 1. 重载 systemd 配置(每次修改 .service 文件后必须执行)
sudo systemctl daemon-reload

# 2. 启动服务
sudo systemctl start myapp

# 3. 设置开机自启
sudo systemctl enable myapp

# 4. 查看服务状态
systemctl status myapp

快捷方式:sudo systemctl enable --now myapp 可以一步完成"启用 + 启动"

五、常用管理命令

shell 复制代码
# 启动服务
sudo systemctl start myapp

# 停止服务
sudo systemctl stop myapp

# 重启服务
sudo systemctl restart myapp

# 重载配置(不重启进程,需要 Service 中配置了 ExecReload)
sudo systemctl reload myapp

# 设置开机自启
sudo systemctl enable myapp

# 取消开机自启
sudo systemctl disable myapp

# 查看服务状态
systemctl status myapp

# 重载 systemd 配置
sudo systemctl daemon-reload

# 查看服务是否已启用
systemctl is-enabled myapp

# 查看服务是否正在运行
systemctl is-active myapp

六、日志查看

Systemd 使用 journald 统一管理日志:

shell 复制代码
# 实时查看服务日志(类似 tail -f)
journalctl -u myapp -f

# 查看最近 100 行日志
journalctl -u myapp -n 100

# 查看最近 1 小时的日志
journalctl -u myapp --since "1 hour ago"

# 查看今天的日志
journalctl -u myapp --since today

# 查看指定时间范围的日志
journalctl -u myapp --since "2024-01-01 00:00:00" --until "2024-01-02 00:00:00"

# 查看错误级别以上的日志
journalctl -u myapp -p err

# 查看上次启动以来的日志
journalctl -u myapp -b

七、常见问题排查

问题 1:服务启动失败

shell 复制代码
# 查看详细错误日志
journalctl -u myapp -xe

# 查看服务状态
systemctl status myapp

常见原因:

  • 路径错误: ExecStart 必须使用绝对路径
  • 权限不足: 检查 User 是否有权限访问工作目录和可执行文件
  • 端口占用: 检查端口是否已被其他服务占用
  • 依赖服务未启动: 检查 After 和 Requires 中的依赖服务是否正常运行

问题 2:修改配置不生效

每次修改 .service 文件后,必须执行:

shell 复制代码
sudo systemctl daemon-reload
sudo systemctl restart myapp

问题 3:脚本无法执行

确保脚本有可执行权限:

shell 复制代码
chmod +x /opt/myapp/start.sh

如果 ExecStart 中需要使用管道、重定向等 shell 语法,需要用 /bin/bash -c 包裹:

shell 复制代码
ExecStart=/bin/bash -c "/opt/myapp/start.sh | tee /var/log/myapp.log"

问题 4:服务反复重启

检查 Restart 和 RestartSec 配置,适当增加重启间隔:

shell 复制代码
Restart=on-failure
RestartSec=10

八、完整示例

示例 1:Python Flask 应用

ini 复制代码
[Unit]
Description = Flask Web Application
After = network.target

[Service]
Type = simple
User = www-data
Group = www-data
WorkingDirectory = /opt/flask-app
Environment = "FLASK_ENV=production"
ExecStart = /opt/flask-app/venv/bin/python /opt/flask-app/app.py
Restart = on-failure
RestartSec = 5
StandardOutput = journal
StandardError = journal

[Install]
WantedBy = multi-user.target

示例 2:Node.js 应用

ini 复制代码
[Unit]
Description = Node.js Application
After = network.target

[Service]
Type = simple
User = node
Group = node
WorkingDirectory = /opt/node-app
Environment = "NODE_ENV=production"
ExecStart = /usr/bin/node /opt/node-app/server.js
Restart = on-failure
RestartSec = 5
LimitNOFILE = 65535
StandardOutput = journal
StandardError = journal

[Install]
WantedBy = multi-user.target

示例 3:Shell 脚本服务

shell 复制代码
[Unit]
Description=My Shell Script Service
After=network.target

[Service]
Type=simple
User=root
ExecStart=/opt/scripts/backup.sh
Restart=on-failure
RestartSec=10
StandardOutput=file:/var/log/backup.log
StandardError=file:/var/log/backup-error.log

[Install]
WantedBy=multi-user.target

示例 4:Java Spring Boot 应用

shell 复制代码
[Unit]
Description=Spring Boot Application
After=network.target

[Service]
Type=simple
User=java
Group=java
WorkingDirectory=/opt/spring-app
ExecStart=/usr/bin/java -jar /opt/spring-app/app.jar
Restart=on-failure
RestartSec=10
Environment="JAVA_OPTS=-Xms512m -Xmx1024m"
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

示例 5:Go 开发的可执行二进制应用

shell 复制代码
[Unit]
Description=Pulse Agent
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/pulse-agent
Restart=on-failure
RestartSec=5s
Environment="PULSE_SERVER=$SERVER_URL"
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

快速操作清单

shell 复制代码
# 1. 创建服务文件
sudo vim /etc/systemd/system/myapp.service

# 2. 重载配置
sudo systemctl daemon-reload

# 3. 启用并启动
sudo systemctl enable --now myapp

# 4. 查看状态
systemctl status myapp

# 5. 查看日志
journalctl -u myapp -f

提示:将上述模板中的 myapp 替换为你的实际服务名,将路径和命令替换为你的实际配置即可直接使用。