【Linux】Linux 中 .service 文件核心介绍

【Linux】Linux 中 .service 文件核心介绍

  • 一、基本概念
    • [1.1 存放路径](#1.1 存放路径)
    • [1.2 核心结构](#1.2 核心结构)
    • [1.3 关键补充说明](#1.3 关键补充说明)
  • 二、详细介绍
    • [2.1 [Unit] 节(通用配置)](#2.1 [Unit] 节(通用配置))
    • [2.2 [Service] 节(服务运行核心)](#2.2 [Service] 节(服务运行核心))
    • [2.3 [Install] 节(开机自启配置)](#2.3 [Install] 节(开机自启配置))
  • [三、如何编写一个 .service 文件?](#三、如何编写一个 .service 文件?)
  • [四、如何使用 .service 文件?](#四、如何使用 .service 文件?)

一、基本概念

systemd 是 Linux 操作系统的系统与服务管理器,是现代 Linux 发行版(CentOS 7+、Ubuntu 16.04+、Debian 9+ 等)的默认初始化系统(init system),替代了传统的 SysVinit。

它的核心目标是并行启动系统服务、统一管理系统进程、简化服务配置与运维,提供了进程管理、日志收集(journald)、设备挂载、定时任务、套接字激活等全套系统能力。

1.1 存放路径

  • 系统级服务:/usr/lib/systemd/system/(系统安装的软件自带服务,如 nginx、mysql。 禁止手动修改

  • 用户自定义服务:/etc/systemd/system/(优先级更高,推荐 存放自定义 .service 文件)

  • 运行时服务:/run/systemd/system/(仅由 systemd 自动生成, 禁止手动修改

  • 核心作用:告诉 systemd 如何启动、停止、重启服务,以及服务的依赖关系、运行环境、日志配置等。

  • 文件格式 :纯文本配置文件,采用 INI 风格,由多个节(Section) 组成,每个节包含键值对配置。

1.2 核心结构

.service 文件分为三个核心节,部分可选节:

[Unit] :描述服务的元数据、依赖关系、启动顺序、文档说明等全局配置,是服务与系统服务管理器交互的基础配置。

  • 绝大多数场景下建议保留,即使不写任何配置项,systemd 也会使用默认值;
  • 理论上可以完全省略(空段或不写),但会缺失依赖、描述等信息,不推荐。
bash 复制代码
# /etc/systemd/system/my-service.service
# 注释推荐独占一行
[Unit]
Description=我的自定义服务      # 服务描述(必写,便于管理)
After=network.target         # 依赖:网络启动后再启动本服务
Wants=network-online.target  # 弱依赖:有网络更好,没有也没关系,但是我更希望你存在。
Requires=network.target      # 强依赖:网络服务失败则本服务也失败(一般不使用)

[Service] :定义服务的 核心运行逻辑 ,包括启动/停止/重启命令、执行用户、工作目录、重启策略、标准输出 / 错误日志等,是服务能正常运行的核心。

  • 必须存在!!!

  • 如果没有这个段,systemd 无法知道如何启动服务,会直接启动失败。

bash 复制代码
[Service]
Type=simple                  # 服务类型(simple 为默认,前台进程)
# 启动命令(唯一必填配置, 并且可执行程序必须指定绝对路径,不能使用相对路径,且 Exec* 后面不能跟注释)
ExecStart=/usr/bin/myapp     
User=ubuntu                  # 运行用户
Restart=on-failure           # 重启策略:进程异常退出时重启
WorkingDirectory=/opt/myapp  # 工作目录

[Install] :定义服务的安装配置 ,核心用于配置 开机自启 、服务所属的 target(运行级别),是 systemctl enable/disable 命令的核心依赖。

  • 可选,取决于你的需求。
  • 如果你不需要开机自启 :可以完全省略 [Install] 段;
  • 如果你需要开机自启 :必须添加该段,且至少包含 WantedByRequiredBy 配置。
bash 复制代码
[Install]
WantedBy=multi-user.target  # 多用户图形/命令行环境下开机自启

可选节 :如 [Install] 之外,还有 [Timer](配合定时器使用)、[Socket](套接字激活服务)等。

如何使用?

bash 复制代码
sudo systemctl daemon-reload  # 重载配置
sudo systemctl start my-service.service  # 手动启动
sudo systemctl stop my-service.service   # 手动停止

# 设置开机启用并立即启动(如果没有 [Install] 段,则无法使用 enable/disable)
sudo systemctl enable --now my-service.service  

# 禁止开机自启
sudo systemctl disable my-service.service

1.3 关键补充说明

  1. 默认值机制

    systemd 对未配置的段 / 配置项会使用内置默认值,例如:

    • 省略 [Unit] 段:服务描述为 (unknown),依赖为默认值;
    • [Service] 段省略 Type:默认值为 simple
    • 省略 User:默认以 root 用户运行。
  2. [Install] 段的核心配置

    只有 WantedBy=RequiredBy= 是核心配置,用于指定服务所属的 target,是开机自启的关键。

  3. systemd 服务文件的语法规则

    • 配置项为 键=值 格式,等号两侧 不能有空格(老版系统)
    • 注释以 # 开头,建议注释独占一行 。Exec 开头的命令行,行尾绝对不能加注释,比如:不可以写在 ExecStart 等执行的指令后面,他会把ExecStart后面的所有值作为参数传递给服务,如果有注释就会引发报错;
    • 配置文件路径:系统服务存 /etc/systemd/system/,第三方服务建议存此目录,而非 /usr/lib/systemd/system/
  4. 注意点

    推荐 等号(=) 前后不能有空格。

    如果有注释,注释单独占一行。不要写在以 Exec 开头的指令的后面,否则会报错。

    自定义的 .service 文件放在 /etc/systemd/system 目录下。

    Exec 指令的后面必须写指令的绝对路径,不能写相对路径,否则会报错。

段名 强制要求 核心作用 适用场景
[Unit] 推荐保留 元数据、依赖、启动顺序 所有生产环境服务
[Service] 必须 启动命令、运行配置、重启策略 所有 systemd 服务
[Install] 可选 开机自启、target 关联 是否需要开机自启 systemctl enable

二、详细介绍

2.1 [Unit] 节(通用配置)

配置项 作用 示例
Description 服务的简短描述 Description=My Custom Service
Documentation 服务文档地址 Documentation=https://example.com/docs
After 控制我在谁之后启动(仅控制启动顺序,不强制服务启动成功) After=network.target(网络启动后再启动本服务)
Before 控制我在谁之前启动(仅控制启动顺序,不强制服务启动成功) Before=nginx.service
Requires 强依赖(依赖的服务启动失败,本服务也会启动失败) Requires=network.target
Wants 弱依赖(依赖的服务启动失败,不影响本服务启动) Wants=network-online.target
  • Description: 对服务进行简洁描述,可以使用中文,可以有空格。 推荐写上。

  • Documentation: 服务的文档地址,类似于使用说明。可以是man手册(适用于系统自带的服务), 可以是网页URL(适用于自己写的服务),也可以是本地文件路径。文档地址可以写多个,使用空格分开。 自定义的服务一般不用写这个参数。

  • After/Before: 仅控制启动顺序,不控制依赖关系。我必须要在谁之前/之后启动,但是你是否成功启动与我无关, 我只管启动的先后顺序。

  • Requires=/Wants: 仅控制 依赖关系 ,不控制启动顺序。我的启动需要有你这个服务作为依赖,分为强依赖和弱依赖。systemd 默认并行启动服务,除非你使用( Before/After )显示指定启动顺序。两者可以组合使用,形成 "先启动谁、再启动谁、谁失败会影响谁"。

  • Requires 强依赖: 依赖的服务必须存在且成功启动,否则本服务会被停止。如果你添加了强依赖,那么强依赖 必须 成功运行。这个参数一般不使用。

  • Wants 弱依赖: 依赖的服务(是否存在/是否启动成功)不影响本服务,弱依赖只是作为一个辅助参数, 锦上添花 ,有更好,没有也无妨,一点不影响本服务的启动。存在的意义:表达弱依赖关系,让 systemd 知道这个服务对我很重要,但是你启动失败了也没关系,不影响我的启动。

Ubuntu 的防火墙 ufw 为例:

bash 复制代码
# /usr/lib/systemd/system/ufw.service
[Unit]
# 对 ufw 服务的简洁描述
Description=Uncomplicated firewall
# 文档信息
Documentation=man:ufw(8)
# 我的服务(ufw)在该服务(local-fs)启动之后启动(仅控制启动顺序,不强制服务启动成功)
After=local-fs.target
# 我的服务(ufw)在该服务(network-pre)启动之前启动(仅控制启动顺序,不强制服务启动成功)
Before=network-pre.target

# 这个Requires是我自己加的默认没有,这里做演示
# 表示:"ufw" 服务需要 "local-fs" 服务成功启动,本服务才能启动,是强依赖,本服务的启动强依赖于requires后面的服务。
# 如果 "local-fs" 服务启动失败,那么"ufw" 服务也不会启动成功。(启动时无先后顺序,本服务和Requires后面跟的服务,并行启动。强调requires后跟的服务是否成功。如果成功,则本服务可能成功,也可能失败,如果失败,则本服务,也启动失败)
# Requires=local-fs.target

# 与requires 差不多,这是弱依赖。我需要这两个目标(多个目标用空格分开)达成,但不强求必须成功。即使这两个都是失败了,也不影响我启动。
# 你有更好,没有也无所谓,也不影响我启动。
Wants=network-pre.target local-fs.target

# 是否关掉 systemd 默认给你加的一堆依赖?
# 这个是系统更底层的基础服务。有的系统服务需要添加这个
# 个人自己写的服务一定不要加这个,否则可能会启动失败
DefaultDependencies=no

2.2 [Service] 节(服务运行核心)

定义服务的执行方式,包括启动/停止/重启命令、执行用户、工作目录、重启策略、标准输出 / 错误日志等,是服务能正常运行的核心。必须一个服务要想启动必须要有 [Service]

配置项 作用 示例
Type 服务类型,决定 systemd 如何判断服务启动成功 - simple(默认):主进程是前台进程,启动后不退出 - forking:进程会 fork 子进程,父进程退出,适合守护进程 - oneshot:一次性执行(执行完就退出)
User / Group 服务运行的用户 / 组 User=www-dataGroup=www-data
WorkingDirectory 服务的工作目录 WorkingDirectory=/opt/myapp
Environment 环境变量 Environment=PATH=/usr/local/bin:/usr/bin
ExecStart 必须配置:服务启动的命令(绝对路径) ExecStart=/usr/bin/python3 /opt/myapp/app.py
ExecStop 服务停止时执行的命令 ExecStop=/usr/bin/kill -9 $MAINPID
ExecReload 服务重启 / 重载时执行的命令 ExecReload=/bin/kill -HUP $MAINPID
Restart 重启策略(推荐生产环境配置) - no(默认,不重启) - on-failure(异常退出时重启) - always(任何退出都重启)
RestartSec 重启间隔(秒) RestartSec=5
StandardOutput/StandardError 日志输出配置 StandardOutput=journal+console(输出到系统日志 + 控制台)
bash 复制代码
# /usr/lib/systemd/system/ufw.service
[Service]
# 服务只需要执行一次,执行完 → 进程退出 → 服务状态变成 inactive (dead)
Type=oneshot
# 与Type=oneshot 组合使用
RemainAfterExit=yes

# 服务启动时,执行的指令
ExecStart=/usr/lib/ufw/ufw-init start quiet

# 服务关闭时执行的指令(一般不用写)
ExecStop=/usr/lib/ufw/ufw-init stop

Type 参数介绍:

Type类型 特点 进程状态 适用于
Type=simple 启动后一直运行(如果不写,默认就是simple) 常驻内存 自定义服务一般都是这个。 或者你不写,使用默认值。
Type=oneshot 跑一次就退出。执行完 → 进程退出 → 服务状态变成 inactive (dead) 执行完就死 初始化脚本、或者一次性脚本/任务
Type=forking 程序启动后会自己创建子进程,然后父进程会自己退出,子进程再后台运行。 常驻内存 Nginx、MySQL、Apache、 传统老服务

RemainAfterExit 参数介绍:

RemainAfterExit=yes 表示:就算进程退出了,systemd 也认为服务是成功的,不会把它标记为失败。一般是执行一次性任务,与 Type=oneshot 组合使用,只要你的服务是一次性任务,执行完就退出,就用它。

User / Group 参数介绍:

用来给服务设置权限,保证安全!就是当服务启动时,以哪个用户的身份运行这个服务,启动后该服务是谁(User)在运行?所属哪个组(Group)?如果不写,默认都是 root 用户,权限过大,不安全,生产环境中禁止!!一般自己写的服务就写当前使用的用户名(普通用户)就行,不推荐使用 root 。比如:当前用户是:zzq,以用户 zzq 的身份运行这个服务,你可以写:

bash 复制代码
User=zzq
Group=zzq

另外,由于每个用户都默认属于一个同名的主组,所以你可以只写一个 User ,systemd 会自动把 Group 设成和 User 一样。

WorkingDirectory 参数介绍:

服务的当前工作目录。服务启动前先 cd 进去的目录,所有相对路径,都是相对于它!如果不写,默认是根目录 /

比如:使用 Java 运行 myapp.jar 这个 jar 包,这个jar 包会产生日志文件,日志文件存放的根路径就是你定义的 WorkingDirectory 路径。所有文件都是相对于这个路径存放,如果没设置,那么日志文件就会由 root 用户在根路径创建。

通常 myapp.jar 需要文件的绝对路径 /home/ubuntu/myapp.jar

bash 复制代码
ExecStart=/usr/bin/java -jar /home/ubuntu/myapp.jar

配置了 WorkingDirectory 可以将 /home/ubuntu 目录省略,直接使用相对路径,更简洁明了:

bash 复制代码
WorkingDirectory=/home/ubuntu
ExecStart=/usr/bin/java -jar myapp.jar

Environment 参数介绍:

就是给程序设置环境变量。

比如,在终端执行:

bash 复制代码
java -jar myapp.jar --server.port=8888

写入服务,设置环境变量:

bash 复制代码
Environment="SERVER_PORT=8888"
ExecStart=/usr/bin/java -jar myapp.jar --server.port=${SERVER_PORT}

ExecStart 参数介绍:

服务启动的指令, 必须配置 ,当你执行 sudo systemctl start ufw 时,systemd 就会跑这一行 ExecStart=/usr/lib/ufw/ufw-init start quiet 。表示的是:启动 ufw 防火墙的脚本,并且安静启动(不输出多余日志),这个更偏向底层,比较复杂,我们换一个简单点的指令进行介绍:

bash 复制代码
ExecStart=/usr/bin/java -jar /home/ubuntu/myapp.jar

这个指令表示:在服务启动时,使用 java 运行一个叫 myapp.jar 的 jar 包。

注意:

  • ExecStart 后面跟的程序的指令( java )必须是绝对路径,不能是相对路径。 文件的指令可以是相对路径(前提是必须存在 WorkingDirectory )。
  • java 程序的路径: /usr/bin/java (绝对路径), 参数: -jar ,启动的 jar 包: /home/ubuntu/myapp.jar (绝对路径)。
  • Exec* 后面不能跟注释,否则会报错。在运行指令时,systemd 会把 ExecStart 后面的所有内容当作指令传给可执行程序,注释可能会被当成参数传递,可执行程序检测到不是正确的参数就会报错。

ExecStop 参数介绍:

服务关闭时,要执行的命令。与 ExecStart 类似。ExecStop 通常是给 Type=oneshot 脚本服务使用,常驻服务 Type=simple 不需要写。

ExecReload 参数介绍:

服务重载配置时执行的命令。适用于支持热重载配置的服务(指:不重启、只重新加载配置的服务),不支持就不写 ExecReload 参数,自定义的服务一般不写。比如:nginx 支持热重载配置(nginx -s reload)在 /usr/lib/systemd/system/nginx.service 中就有 ExecReload , 我们平时写的 Java 程序不支持也不需要写 ExecReload。

Restart 参数介绍:

配置重启策略(推荐生产环境配置)。systemd 会根据 Restart= 的值,决定要不要重启。

参数值 作用说明 适用场景
Restart=no 默认值 ,服务退出 / 崩溃后,不自动重启 一次性脚本、临时任务
Restart=on-failure 服务非正常退出(崩溃、报错、被强制终止、非 0 退出码)时重启 Java 应用、FRP、Nginx、数据库服务
Restart=always 无论服务是正常退出、崩溃还是被终止,永远自动重启 核心业务服务、必须常驻的进程
以下两个不常用:
Restart=on-success 仅服务正常退出(退出码 0)时重启,失败则不重启 极少使用,适配特殊成功退出场景
Restart=on-abnormal 非正常退出 时重启,与on-failure功能基本一致(部分系统下无区别) 通用服务,替代on-failure

RestartSec 参数介绍:

Restart 搭配使用,服务异常退出后,延迟重启的时间(单位:秒),避免疯狂重启占用资源。RestartSec=3 表示:服务退出后,3秒后重启。

StandardOutput/StandardError 参数介绍:

这两个参数是 systemd 服务配置 中,用于控制服务 标准输出(stdout)标准错误输出(stderr) 重定向方式的核心配置,决定了服务的日志如何存储、如何查看。

参数 作用 取值 说明
StandardOutput 控制标准输出(程序正常打印的日志)的输出方式 journal(默认) 日志写入 systemd 日志,可通过 journalctl 查看
journal+console 同时写入日志 + 输出到控制台(仅调试场景)
null 丢弃所有标准输出,不记录日志
绝对路径(如 /var/log/app.log 输出日志到指定文件
StandardError 控制标准错误输出(程序报错、异常信息)的输出方式 取值与 StandardOutput 完全一致 单独配置错误日志的输出路径 / 方式

如果不写,默认是 journal ,使用以下指令查看日志:

bash 复制代码
journalctl -u myapp.service -f
  • -u myapp.service :unit,筛选服务,只看这个服务。
  • -f : follow 实时追踪,实时刷新(跟着输出)。

写绝对路径,输出 / 错误分别写入不同文件:

bash 复制代码
[Service]
ExecStart=/home/ubuntu/myapp.jar
StandardOutput=/var/log/myapp/output.log
StandardError=/var/log/myapp/error.log

2.3 [Install] 节(开机自启配置)

用于配置服务的安装和启用规则,执行 systemctl enable 时生效。如果你想配置开机自启,就加这个 [Install]

配置项 作用 示例
WantedBy 定义服务在哪个目标(target)下启用,最常用 WantedBy=multi-user.target(多用户命令行环境,服务器通用)
RequiredBy 强依赖的目标 RequiredBy=graphical.target
bash 复制代码
# /usr/lib/systemd/system/ufw.service
[Install]
# 多用户命令行环境,大部分情况下都选这个
WantedBy=multi-user.target
  • WantedBy = 弱依赖安装(推荐 99% 场景)。当系统进入多用户模式(Linux 默认就是多用户模式)时,顺便启动我的服务,我挂了不影响系统。

  • RequiredBy = 强依赖安装(几乎不用)。系统必须等我服务启动成功,才能进入多用户模式,我挂了系统起不来!!

  • 它们只在你执行 systemctl enable 时生效。

三、如何编写一个 .service 文件?

以 java 程序为例,如何以服务的方式运行 myapp.jar 程序?

创建一个 service 文件 myapp.service

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

写入配置文件:

bash 复制代码
[Unit]
# 服务描述
Description=我的 java 程序服务 myapp.service
# 依赖网络启动后再启动本服务(本地测试,无需连接外网)
After=network.target
# 弱依赖网络就绪
Wants=network-online.target

[Service]
# 服务类型(有默认值,可以不写)
Type=simple
# 运行该服务的用户(修改为你自己的用户名)
User=ubuntu
# 工作目录
WorkingDirectory=/home/ubuntu
# 设置环境变量(例如:JVM参数、应用程序端口(8081端口)等)
Environment="JAVA_OPTS=-Xms512m -Xmx1024m"
Environment="SERVER_PORT=8081"
# 启动命令------绝对路径(修改为你自己的 jar 包名字)
ExecStart=/usr/bin/java ${JAVA_OPTS} -jar myapp.jar --server.port=${SERVER_PORT}
# 停止命令:优雅地终止进程
ExecStop=/usr/bin/kill -TERM $MAINPID
# 异常退出时自动重启
Restart=on-failure
# 重启间隔3秒
RestartSec=3
# 日志输出到系统日志(有默认值,可不写)
StandardOutput=journal
StandardError=journal

[Install]
# 多用户环境下启用(不需要开机自启,则移除此项)
WantedBy=multi-user.target

网络补充:

  • network.target :网络初始化完成,网卡亮了,但 IP 还没拿到,不能上网!相当于自己windows 电脑没有网络,但是可以使用mysql, http:localhost:8080 等。
  • network-online.target :网络真正准备好了(IP 拿到、DNS 正常、能上网)。相当于自己windows电脑连接了网络,可以使用远程服务器上的 OSS 对象存储,MySQL 数控库等。 适用于生产环境
  • 使用那一个? 如果仅仅使用本地的网络服务,就使用 network.target , 如果需要连外部服务(OSS, Redis, MQ, MySQL等)使用 network-online.target

省略非必要参数,最基础的启动 java 程序服务:

bash 复制代码
[Service]
ExecStart=/usr/bin/java -jar /home/ubuntu/myapp.jar

四、如何使用 .service 文件?

修改 / 新增 .service 文件后,必须重载 systemd 配置,让系统识别新服务:

bash 复制代码
sudo systemctl daemon-reload

启动服务:

bash 复制代码
# sudo systemctl start myapp.service
# 可以简写为myapp(省略 .service)
sudo systemctl start myapp

查看服务状态:

bash 复制代码
sudo systemctl status myapp

设置开机自启(service 文件中必须有[Install]):

bash 复制代码
sudo systemctl enable myapp

禁止开机自启:

bash 复制代码
sudo systemctl disable myapp

停止服务:

bash 复制代码
sudo systemctl stop myapp

重启服务:

bash 复制代码
sudo systemctl restart myapp

查看服务日志:

bash 复制代码
# 查看当前服务日志
sudo journalctl -u myapp
# 实时跟踪日志(类似 tail -f)
sudo journalctl -u myapp -f
# 查看最近10分钟的日志
sudo journalctl -u myapp --since "10 minutes ago"

End

你好,少年,未来可期~

相关推荐
色空大师2 小时前
网站搭建实操(二)后台管理(1)登录
java·linux·数据库·搭建网站·论坛
朱一头zcy2 小时前
在CentOS7环境下安装MySQL详细步骤
linux·mysql
不知名。。。。。。。。2 小时前
网络层———IP
服务器·网络·tcp/ip
kobe_OKOK_2 小时前
docker run 一系列中间件命令
运维·docker·容器
拾贰_C2 小时前
【Ubuntu】安装Nginx(nVidia驱动未安装成功阻止版)
linux·运维·服务器·ubuntu
会飞的大可2 小时前
前后端一体化CI/CD设计与实现:告别手动部署,实现全链路自动化交付
运维·ci/cd·自动化
克莱因3587 小时前
Linux CentOS7 进程基础知识
linux·运维·服务器
Skilce8 小时前
ZrLog 高可用部署
运维·服务器·数据库·阿里云·maven
我爱学习好爱好爱10 小时前
Ansible 常用模块详解:yum、service/systemd、copy实战
linux·服务器·ansible