0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

编写一个生产级的Service配置文件

马哥Linux运维 来源:马哥Linux运维 2026-02-25 14:24 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

一、概述

1.1 背景介绍

systemctl start xxx敲了无数遍,但真要从零写一个 Service 文件丢到生产环境跑,很多人就开始心虚了。网上抄一段配置,Type=simple还是forking搞不清楚,Restart=always往上一贴就觉得万事大吉,结果进程挂了不重启、OOM 了没人管、日志把磁盘写爆了才发现 journald 根本没配轮转。

Systemd 从 2010 年诞生到现在,已经不只是一个 init 系统了。它是 Linux 世界的 PID 1,是服务管理器、日志系统、定时任务调度器、设备管理器、网络配置工具的集合体。2026 年的主流发行版(Ubuntu 24.04/RHEL 9/Debian 12)全部默认使用 systemd 256+,cgroup v2 也已经是标配。不管你是跑 Java 微服务、Go 二进制、Python 脚本还是 Nginx,最终都要落到一个.service文件上。

这篇文章的目标很明确:从 systemd 的架构讲起,把 Unit 文件的每个 Section、每个关键指令都讲透,最后给出一个经过生产验证的完整 Service 配置模板。看完之后,你应该能独立编写一个带资源限制、安全加固、健康检查、日志管理的生产级 Service 文件。

1.2 技术特点

体系化:从架构到配置到实战,一条线串起来,不是零散的参数罗列

生产导向:每个配置项都说明"为什么要这么配",而不只是"可以这么配"

安全优先:覆盖 ProtectSystem、PrivateTmp、NoNewPrivileges 等安全加固指令

现代技术栈:基于 systemd 256+、cgroup v2、journald 的 2026 年最佳实践

1.3 适用场景

场景一:需要将自研服务部署到 Linux 服务器,编写规范的 Service 文件

场景二:现有 Service 配置过于简陋,需要补充资源限制和安全加固

场景三:想用 systemd timer 替代 cron,实现更可靠的定时任务管理

场景四:需要理解 systemd 的依赖管理机制,解决服务启动顺序问题

1.4 环境要求

组件 版本要求 说明
操作系统 Ubuntu 24.04 LTS / RHEL 9.x 内核 6.8+,cgroup v2 默认启用
systemd 256+ 支持本文涉及的所有特性
journald 随 systemd 版本 日志管理组件
cgroup v2 资源限制依赖 cgroup v2

二、Systemd 架构和核心概念

2.1 Systemd 不只是 init

很多人对 systemd 的认知停留在"启动服务的工具",这个理解太窄了。Systemd 是一整套系统管理框架,PID 1 进程(/usr/lib/systemd/systemd)是它的核心,但远不是全部。

            +------------------+
            |  PID 1 (systemd) |
            +--------+---------+
                |
     +----------+-----------+-----------+----------+
     |     |      |      |     |
  +---------+ +--------+ +--------+ +--------+ +--------+
  | journald| | logind | | udevd | | networkd| | resolved|
  +---------+ +--------+ +--------+ +--------+ +--------+
   日志管理  会话管理  设备管理  网络管理  DNS解析

  +----------+----------+----------+
  | systemd-tmpfiles  | systemd-sysctl | systemd-modules-load |
  +----------+----------+----------+
   临时文件管理     内核参数      内核模块加载

PID 1 负责的核心工作:解析 Unit 文件、管理依赖关系、启动/停止/监控服务进程、处理 cgroup 资源分配。其他组件各司其职,通过 D-Bus 与 PID 1 通信

2.2 三个核心概念:Unit、Target、Slice

2.2.1 Unit(单元)

Unit 是 systemd 管理的基本对象。一个 Unit 对应一个配置文件,文件后缀决定了 Unit 的类型:

类型 后缀 用途 典型示例
Service .service 管理守护进程 nginx.service
Socket .socket 套接字激活 sshd.socket
Timer .timer 定时任务 logrotate.timer
Mount .mount 挂载点 home.mount
Target .target 逻辑分组 multi-user.target
Slice .slice 资源分组 user.slice
Path .path 文件监控 cups.path
Device .device 设备管理 由 udev 自动生成

日常打交道最多的就是前三个:Service、Socket、Timer。

2.2.2 Target(目标)

Target 是一组 Unit 的逻辑集合,类似于 SysVinit 时代的 runlevel,但更灵活。Target 本身不做任何事情,它只是把一堆 Unit 聚合在一起,表示"系统到达了某个状态"。

# 查看当前活跃的 target
systemctl list-units --type=target --state=active

# 常见 target 对应关系
# multi-user.target ≈ runlevel 3(多用户命令行)
# graphical.target  ≈ runlevel 5(图形界面)
# rescue.target   ≈ runlevel 1(单用户模式)

服务启动顺序的核心就是围绕 target 来编排的。比如大多数网络服务都声明After=network-online.target,意思是"等网络就绪了再启动我"。

2.2.3 Slice(切片)

Slice 是 cgroup 的 systemd 抽象层,用于对一组服务进行资源分配。默认的 slice 层级结构:

-.slice (根 slice)
├── system.slice  # 系统服务(nginx、mysql 等)
├── user.slice   # 用户会话
│  ├── user-1000.slice
│  └── user-1001.slice
└── machine.slice # 虚拟机和容器

可以自定义 slice 来实现资源隔离。比如把所有业务服务放到同一个 slice 里,统一限制 CPU 和内存上限,防止业务进程把系统服务挤死。

2.3 Unit 文件的存放位置

Unit 文件有三个存放位置,优先级从高到低:

路径 优先级 用途
/etc/systemd/system/ 最高 管理员自定义配置
/run/systemd/system/ 运行时动态生成
/usr/lib/systemd/system/ 最低 软件包安装的默认配置

实际操作原则:永远不要直接修改/usr/lib/systemd/system/下的文件,包管理器更新时会覆盖掉。自定义配置放/etc/systemd/system/,覆盖默认配置用systemctl edit创建 drop-in 文件。

# 用 drop-in 方式覆盖某个参数,不动原始文件
# 会创建 /etc/systemd/system/nginx.service.d/override.conf
sudo systemctl edit nginx.service

# 查看某个 unit 的最终生效配置(合并所有 drop-in)
systemctl cat nginx.service

2.4 Unit 文件结构:三个 Section

一个标准的.service文件由三个 Section 组成:

[Unit]
# 描述信息和依赖关系
Description=My Application Service
Documentation=https://docs.example.com
After=network-online.target postgresql.service
Wants=network-online.target
Requires=postgresql.service

[Service]
# 服务运行参数
Type=notify
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yaml
Restart=on-failure
RestartSec=5s
User=myapp
Group=myapp

[Install]
# 安装信息(enable/disable 时使用)
WantedBy=multi-user.target

[Unit] Section:定义 Unit 的元信息和依赖关系。Description是给人看的,After/Before控制启动顺序,Requires/Wants控制依赖强度。

[Service] Section:这是.service文件独有的,也是最核心的部分。定义了进程怎么启动、怎么停止、怎么重启、以什么身份运行、资源限制多少。

[Install] Section:定义systemctl enable时的行为。WantedBy=multi-user.target的意思是"当系统进入多用户模式时,把我也带上"。执行enable时,systemd 会在multi-user.target.wants/目录下创建一个指向这个 service 文件的符号链接。

# enable 的本质就是创建符号链接
sudo systemctlenablemyapp.service
# 等价于:
# ln -s /etc/systemd/system/myapp.service 
#  /etc/systemd/system/multi-user.target.wants/myapp.service

# 查看一个 unit 是否 enabled
systemctl is-enabled myapp.service

2.5 Service Type 详解

Type=是 [Service] Section 里最关键的一个参数,它决定了 systemd 如何判断"服务已经启动成功"。选错了 Type,轻则systemctl start超时报错,重则服务状态判断错乱、重启策略失效。

2.5.1 五种主要 Type

Type 启动判定 适用场景 典型程序
simple ExecStart 进程启动即视为就绪 前台运行的程序 Go 二进制、Node.js
exec ExecStart 进程成功执行(exec()返回)即就绪 同 simple,但更严格 同 simple
forking 主进程 fork 子进程后退出,子进程接管 传统 daemon Nginx、MySQL
oneshot ExecStart 进程退出后才视为就绪 一次性任务 初始化脚本、数据迁移
notify 进程主动发送 sd_notify 通知就绪 支持 sd_notify 的程序 systemd 自身组件、部分 Go 服务

simple vs exec:simple是默认值,进程被 fork 出来就算启动成功,哪怕二进制文件路径写错了,systemctl start也可能返回成功(因为 fork 本身成功了)。exec更严格,它会等到exec()系统调用真正执行成功才算就绪。systemd 256+ 推荐用exec替代simple。

forking 的坑:传统 daemon 程序(如 Nginx 默认配置)启动时会 fork 子进程,父进程退出。用Type=forking时,systemd 需要知道哪个是"主进程",通常通过PIDFile=指定 PID 文件路径来追踪。如果 PID 文件写入不及时或路径配错,systemd 就会丢失对进程的追踪。

# forking 类型的典型配置(Nginx 为例)
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID

notify 的优势:这是生产环境最推荐的 Type。进程在完成所有初始化工作(加载配置、连接数据库、预热缓存)之后,主动调用sd_notify(0, "READY=1")告诉 systemd "我准备好了"。这样 systemd 对服务状态的判断是最准确的。

// Go 程序中使用 sd_notify 的示例
import"github.com/coreos/go-systemd/v22/daemon"

funcmain(){
 // 初始化工作...
  loadConfig()
  connectDB()
  warmupCache()

 // 通知 systemd 服务就绪
  daemon.SdNotify(false, daemon.SdNotifyReady)

 // 开始服务主循环
  serve()
}

2.5.2 选型决策树

你的程序启动后会 fork 并退出父进程吗?
├── 是 → Type=forking + PIDFile=
└── 否 → 程序支持 sd_notify 吗?
  ├── 是 → Type=notify(最佳选择)
  └── 否 → 程序是一次性任务吗?
    ├── 是 → Type=oneshot(可选 RemainAfterExit=yes)
    └── 否 → Type=exec(推荐)或 Type=simple

2.6 启动依赖管理

服务之间的依赖关系是 systemd 的核心能力之一。这里有两个维度需要区分清楚:启动顺序依赖强度

2.6.1 启动顺序:After / Before

After和Before只控制顺序,不控制依赖。声明After=postgresql.service意味着"如果 postgresql 也要启动,那先启动它,再启动我"。但如果 postgresql 根本没有被激活,这条声明不会自动把它拉起来。

[Unit]
# 正确:先等网络和数据库就绪,再启动本服务
After=network-online.target postgresql.service redis.service

2.6.2 依赖强度:Requires / Wants / BindsTo

指令 强度 行为
Wants= 弱依赖 尝试启动依赖,依赖失败不影响本服务
Requires= 强依赖 依赖启动失败,本服务也不启动
BindsTo= 绑定 依赖停止/重启,本服务也跟着停止/重启
Requisite= 前置断言 依赖必须已经在运行,否则立即失败

生产建议:大多数场景用Wants=+After=的组合就够了。Requires=看起来更"安全",但它有一个副作用——如果被依赖的服务后来挂了,本服务也会被连带停止。这在微服务架构下往往不是你想要的行为。

[Unit]
Description=My Web Application
After=network-online.target postgresql.service
# 用 Wants 而不是 Requires,数据库临时不可用时服务自己处理重连
Wants=network-online.target postgresql.service

2.6.3 网络依赖的正确写法

这是一个高频踩坑点。很多人写After=network.target,结果服务启动时网络还没通。原因是network.target只表示"网络管理器已启动",不代表网络已经可用。正确的写法:

[Unit]
After=network-online.target
Wants=network-online.target

同时需要确保systemd-networkd-wait-online.service或NetworkManager-wait-online.service是启用的,否则network-online.target会被立即视为已达成。

2.7 进程管理:重启策略与健康检查

2.7.1 Restart 策略

Restart=控制进程退出后是否自动重启:

行为
no 不重启(默认值)
on-success 仅在正常退出(exit code 0)时重启
on-failure 非正常退出时重启(非0退出码、被信号杀死、超时、看门狗超时)
on-abnormal 被信号杀死、超时、看门狗超时时重启(不含非0退出码)
on-abort 仅被未捕获信号杀死时重启
always 无论什么原因退出都重启

生产建议:大多数守护进程用Restart=on-failure。不要无脑用always——如果程序是正常退出(比如收到 SIGTERM 后优雅关闭),你通常不希望它被自动拉起来。always适合那些"只要没在跑就是不正常"的核心服务。

2.7.2 重启频率控制

光有Restart=on-failure还不够,还需要控制重启的节奏,防止进程反复崩溃导致 CPU 空转:

[Service]
Restart=on-failure
RestartSec=5s       # 每次重启前等待 5 秒
RestartSteps=5       # 重启间隔逐步递增的步数(systemd 256+)
RestartMaxDelaySec=60s   # 递增的最大间隔(systemd 256+)
StartLimitIntervalSec=300 # 在 300 秒的窗口内
StartLimitBurst=5     # 最多重启 5 次,超过则放弃

systemd 256+ 新增了RestartSteps和RestartMaxDelaySec,可以实现指数退避式重启。第一次重启等 5 秒,第二次等 16 秒,逐步递增到 60 秒封顶。这比固定间隔更合理——如果是瞬时故障,快速重启能尽快恢复;如果是持续性故障,拉长间隔避免雪崩。

2.7.3 超时控制

[Service]
TimeoutStartSec=30s  # 启动超时,超过 30 秒未就绪则判定失败
TimeoutStopSec=30s   # 停止超时,超过 30 秒未退出则发 SIGKILL
TimeoutAbortSec=60s  # 收到 abort 信号后的超时(用于生成 core dump)

TimeoutStopSec特别重要。systemctl stop时,systemd 先发 SIGTERM,等TimeoutStopSec秒后如果进程还没退出,就发 SIGKILL 强杀。Java 应用通常需要把这个值调大一些(比如 60s),给 JVM 足够的时间做优雅关闭。

2.7.4 Watchdog 看门狗

Watchdog 是 systemd 提供的进程健康检查机制。服务进程需要定期向 systemd 发送心跳,如果超时没收到,systemd 就认为进程卡死了,按照 Restart 策略处理。

[Service]
Type=notify
WatchdogSec=30s      # 每 30 秒需要收到一次心跳
WatchdogSignal=SIGABRT   # 超时后发送的信号(默认 SIGABRT,可生成 core dump)

程序端需要配合发送心跳:

// Go 程序中发送 watchdog 心跳
import"github.com/coreos/go-systemd/v22/daemon"

funcwatchdogLoop(){
  interval, _ := daemon.SdWatchdogEnabled(false)
 ifinterval ==0{
   return// watchdog 未启用
  }
  ticker := time.NewTicker(interval /2)// 以一半间隔发送,留足余量
 forrangeticker.C {
    daemon.SdNotify(false, daemon.SdNotifyWatchdog)
  }
}

Watchdog 解决的是"进程还活着但已经卡死"的问题——进程没崩溃、PID 还在、端口还监听着,但内部死锁了或者陷入无限循环,外部健康检查可能还没来得及发现。Watchdog 从进程内部检测这种状态,比外部探测更及时。

三、资源限制、安全加固与日志管理

3.1 资源限制(cgroup v2)

不做资源限制的服务就是在裸奔。一个内存泄漏的进程可以把整台机器的内存吃光触发 OOM Killer,一个死循环可以把所有 CPU 核心打满。systemd 通过 cgroup v2 提供了细粒度的资源限制能力,直接在 Service 文件里配置就行,不需要手动操作 cgroup 文件系统。

3.1.1 CPU 限制

[Service]
# CPU 配额:200% 表示最多使用 2 个核心
CPUQuota=200%

# CPU 权重:默认 100,范围 1-10000
# 只在 CPU 竞争时生效,空闲时不限制
CPUWeight=50

# 绑定到特定 CPU 核心(可选,通常不需要)
AllowedCPUs=0-3

CPUQuota是硬限制,不管 CPU 是否空闲都不会超过这个值。CPUWeight是软限制,只在多个服务竞争 CPU 时按权重分配,CPU 空闲时不起作用。生产环境建议两个都配:CPUWeight保证公平调度,CPUQuota兜底防止单个服务吃满所有核心。

3.1.2 内存限制

[Service]
# 内存硬上限:超过直接 OOM Kill
MemoryMax=2G

# 内存软上限:超过后内核会优先回收该 cgroup 的内存
MemoryHigh=1536M

# 最低内存保障:内存紧张时至少保留这么多
MemoryMin=256M

# 禁用 swap(推荐)
MemorySwapMax=0

MemoryMax和MemoryHigh的区别很关键。MemoryHigh是软限制,超过后内核会加大内存回收力度(进程会变慢但不会被杀);MemoryMax是硬限制,超过直接触发 OOM Kill。生产环境建议MemoryHigh设为正常峰值的 120%,MemoryMax设为 150%,给一个缓冲区间。

3.1.3 IO 限制

[Service]
# IO 权重:默认 100,范围 1-10000
IOWeight=50

# 针对特定设备的带宽限制
IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 20M

# IOPS 限制
IOReadIOPSMax=/dev/sda 1000
IOWriteIOPSMax=/dev/sda 500

IO 限制在数据库服务和日志密集型服务上特别有用。一个疯狂写日志的服务可以把磁盘 IO 打满,影响同机器上的其他服务。

3.1.4 其他资源限制

[Service]
# 最大文件描述符数
LimitNOFILE=65536

# 最大进程/线程数
LimitNPROC=4096

# core dump 大小(0 表示禁用)
LimitCORE=infinity

# 最大打开文件锁数
LimitLOCKS=infinity

# 任务数上限(cgroup 级别,比 NPROC 更准确)
TasksMax=4096

LimitNOFILE是高并发服务的必配项。Linux 默认的 1024 对于任何生产服务都太小了,Nginx、Redis、数据库类服务通常需要 65536 甚至更高。

3.2 安全加固

Systemd 提供了一整套沙箱机制,可以在不修改应用代码的情况下大幅收窄进程的权限范围。这些配置的成本几乎为零,但安全收益很高。

3.2.1 文件系统保护

[Service]
# 将 /usr 和 /boot 挂载为只读
ProtectSystem=strict

# 为进程创建独立的 /tmp,与其他进程隔离
PrivateTmp=yes

# 将 /home、/root、/run/user 设为不可访问
ProtectHome=yes

# 只允许读写指定目录
ReadWritePaths=/var/lib/myapp /var/log/myapp
ReadOnlyPaths=/etc/myapp

# 禁止访问指定目录
InaccessiblePaths=/var/lib/mysql

ProtectSystem=strict是最严格的模式,整个文件系统变成只读,只有通过ReadWritePaths显式声明的目录才可写。这意味着即使应用被攻破,攻击者也无法篡改系统文件。

3.2.2 权限收窄

[Service]
# 禁止获取新的特权(防止 setuid 提权)
NoNewPrivileges=yes

# 以非 root 用户运行
User=myapp
Group=myapp
DynamicUser=yes  # 自动创建临时用户(systemd 256+ 推荐)

# 移除所有 Linux capabilities
CapabilityBoundingSet=
# 如果需要绑定低端口,只给 NET_BIND_SERVICE
# CapabilityBoundingSet=CAP_NET_BIND_SERVICE

# 限制系统调用(白名单模式)
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM

NoNewPrivileges=yes是零成本的安全加固,没有任何副作用,所有服务都应该加上。DynamicUser=yes是 systemd 的一个巧妙设计——它会在服务启动时动态分配一个 UID/GID,服务停止后自动回收,不需要手动创建系统用户。

3.2.3 网络和内核隔离

[Service]
# 创建独立的网络命名空间(完全断网)
PrivateNetwork=yes
# 如果需要网络但想限制,用 RestrictAddressFamilies
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

# 禁止加载内核模块
ProtectKernelModules=yes

# 禁止修改内核参数
ProtectKernelTunables=yes

# 禁止访问内核日志
ProtectKernelLogs=yes

# 禁止修改系统时钟
ProtectClock=yes

# 禁止创建设备节点
PrivateDevices=yes

3.2.4 一键查看安全评分

systemd 提供了一个内置的安全审计工具,可以对 Service 文件进行安全评分:

# 查看某个服务的安全评分
systemd-analyze security myapp.service

# 输出示例(分数越低越安全,10 分最不安全)
# → Overall exposure level for myapp.service: 2.1 OK

这个工具会逐项检查所有安全相关的配置,给出评分和改进建议。新写的 Service 文件跑一遍这个命令,把能加的安全配置都加上,目标是控制在 3 分以内。

3.3 日志管理

3.3.1 journald 基础

Systemd 服务的标准输出和标准错误默认会被 journald 捕获。不需要在应用里配置日志文件路径,直接往 stdout/stderr 写就行,journald 会自动加上时间戳、服务名、PID 等元数据。

# 查看某个服务的日志
journalctl -u myapp.service

# 实时跟踪日志(类似 tail -f)
journalctl -u myapp.service -f

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

# 按优先级过滤(0=emerg 到 7=debug)
journalctl -u myapp.service -p err

# 输出 JSON 格式(方便程序处理)
journalctl -u myapp.service -o json-pretty

3.3.2 Service 文件中的日志配置

[Service]
# 日志输出目标
StandardOutput=journal
StandardError=journal

# 设置日志标识符(默认是服务名)
SyslogIdentifier=myapp

# 设置日志级别过滤
LogLevelMax=info  # 只记录 info 及以上级别

# 限制日志速率(防止日志风暴)
LogRateLimitIntervalSec=30s
LogRateLimitBurst=10000  # 30 秒内最多 10000 条

LogRateLimitIntervalSec和LogRateLimitBurst是生产环境的保命配置。见过太多次应用出 bug 后疯狂打日志,每秒几万条,把磁盘 IO 打满、把 journald 撑爆的情况。

3.3.3 journald 全局配置和日志轮转

编辑/etc/systemd/journald.conf:

[Journal]
# 持久化存储(默认是 volatile,重启丢失)
Storage=persistent

# 磁盘占用上限
SystemMaxUse=2G     # 日志总大小上限
SystemMaxFileSize=128M  # 单个日志文件上限
SystemKeepFree=4G    # 至少保留 4G 磁盘空间

# 运行时(内存中)日志限制
RuntimeMaxUse=256M

# 日志保留时间
MaxRetentionSec=30day

# 压缩
Compress=yes

journald 的日志轮转是自动的,不需要像 logrotate 那样配置 cron。当日志总量超过SystemMaxUse或单文件超过SystemMaxFileSize时,journald 会自动删除最旧的日志。

# 手动清理日志
sudo journalctl --vacuum-size=1G  # 只保留 1G
sudo journalctl --vacuum-time=7d  # 只保留 7 天

# 查看日志占用空间
journalctl --disk-usage

四、Timer 定时任务

4.1 为什么用 Timer 替代 Cron

Cron 用了几十年,能跑但问题不少:没有日志集成(输出靠邮件或重定向)、没有依赖管理、没有资源限制、错过的任务不会补执行、多实例并发没有保护。Systemd Timer 解决了这些问题,而且和 Service 文件共享同一套管理体系。

特性 Cron Systemd Timer
日志 无(靠重定向) journald 自动记录
依赖管理 支持 After/Requires
资源限制 完整 cgroup 支持
错过补执行 不支持 Persistent=yes
并发保护 天然单实例
随机延迟 RandomizedDelaySec
精度 分钟级 秒级甚至微秒级

4.2 Timer 文件结构

一个 Timer 由两个文件组成:.timer文件定义触发时间,.service文件定义要执行的任务。两个文件同名(后缀不同),systemd 自动关联。

# /etc/systemd/system/db-backup.timer
[Unit]
Description=Database Backup Timer

[Timer]
# 每天凌晨 2 点执行
OnCalendar=*-*-* 0200

# 如果错过了(比如机器当时关机),开机后补执行
Persistent=yes

# 随机延迟 0-15 分钟,避免多台机器同时执行
RandomizedDelaySec=15min

# 精度(默认 1min,设小一点更准时)
AccuracySec=1s

[Install]
WantedBy=timers.target
# /etc/systemd/system/db-backup.service
[Unit]
Description=Database Backup Job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-db.sh
User=backup
Group=backup

# 资源限制和安全加固同样适用
MemoryMax=512M
CPUQuota=50%
ProtectSystem=strict
PrivateTmp=yes
NoNewPrivileges=yes
ReadWritePaths=/var/backups

4.3 OnCalendar 时间表达式

OnCalendar的语法比 cron 更直观:

# 格式:星期 年-月-日 时:分:秒
OnCalendar=Mon..Fri *-*-* 0900  # 工作日每天 9 点
OnCalendar=*-*-* *:00/15:00     # 每 15 分钟
OnCalendar=*-*-01 0000      # 每月 1 号零点
OnCalendar=weekly           # 每周一零点
OnCalendar=hourly           # 每小时整点

# 验证时间表达式
systemd-analyze calendar"*-*-* 0200"
# 输出下次触发时间,确认表达式写对了

也可以用相对时间触发:

[Timer]
# 系统启动 5 分钟后执行
OnBootSec=5min

# 上次执行完成后 30 分钟再执行
OnUnitActiveSec=30min

4.4 Timer 管理命令

# 启用并启动 timer
sudo systemctlenable--now db-backup.timer

# 查看所有 timer 的状态和下次触发时间
systemctl list-timers --all

# 手动触发一次(不影响定时计划)
sudo systemctl start db-backup.service

# 查看 timer 的执行历史
journalctl -u db-backup.service --since"7 days ago"

五、Socket 激活

5.1 什么是 Socket 激活

Socket 激活是 systemd 的一个精巧设计:由 systemd 预先监听端口,当第一个连接请求到达时,再启动对应的服务进程,并把 socket 文件描述符传递给它。服务进程启动后接管 socket,后续请求直接由服务处理。

这个机制带来三个好处:

启动加速:系统启动时不需要等所有服务都起来,端口先占着,请求来了再启动

按需启动:不常用的服务平时不占资源,有请求才拉起来

零停机重启:重启服务时,systemd 继续持有 socket,新连接排队等待,服务重启完成后继续处理,客户端感知不到中断

5.2 Socket 激活配置示例

# /etc/systemd/system/myapp.socket
[Unit]
Description=My Application Socket

[Socket]
# 监听地址和端口
ListenStream=0.0.0.0:8080

# 也可以监听 Unix Socket
# ListenStream=/run/myapp/myapp.sock

# 连接队列长度
Backlog=4096

# Socket 文件权限(Unix Socket 时有效)
# SocketMode=0660
# SocketUser=myapp
# SocketGroup=myapp

# 接受连接后传递给哪个服务(默认同名 .service)
# Service=myapp.service

[Install]
WantedBy=sockets.target

对应的 Service 文件不需要特殊修改,只要程序能从文件描述符 3 接收 socket 即可。Go 标准库的net包、systemd 的sd_listen_fds()API 都支持这种模式。

# 启用 socket 激活(注意:启动的是 .socket 不是 .service)
sudo systemctlenable--now myapp.socket

# 此时 myapp.service 还没启动,但端口已经在监听
ss -tlnp | grep 8080
# 输出:systemd 在监听

# 发送第一个请求,触发 myapp.service 启动
curl http://localhost:8080/health

六、生产级 Service 文件完整示例

6.1 Go Web 服务(推荐模板)

这是一个经过生产验证的完整 Service 文件,覆盖了前面讲到的所有关键配置。可以作为模板,根据实际需求增删参数。

# /etc/systemd/system/myapp.service
# 生产级 Go Web 服务配置模板

# ============================================================
# [Unit] 元信息和依赖
# ============================================================
[Unit]
Description=My Application API Server
Documentation=https://docs.example.com/myapp
After=network-online.target postgresql.service redis.service
Wants=network-online.target
# 用 Wants 而非 Requires,依赖服务临时不可用时由应用自行处理重连
Wants=postgresql.service redis.service

# 条件检查:配置文件必须存在才启动
ConditionPathExists=/etc/myapp/config.yaml

# ============================================================
# [Service] 核心运行参数
# ============================================================
[Service]
Type=notify
NotifyAccess=main

# --- 启动命令 ---
ExecStartPre=/usr/local/bin/myapp validate --config /etc/myapp/config.yaml
ExecStart=/usr/local/bin/myapp serve --config /etc/myapp/config.yaml
ExecReload=/bin/kill -s HUP $MAINPID

# --- 运行身份 ---
User=myapp
Group=myapp

# --- 工作目录和环境 ---
WorkingDirectory=/var/lib/myapp
EnvironmentFile=-/etc/myapp/env  # 减号表示文件不存在时不报错
Environment=GOMAXPROCS=4
Environment=GIN_MODE=release

# --- 重启策略 ---
Restart=on-failure
RestartSec=5s
RestartSteps=5
RestartMaxDelaySec=60s
StartLimitIntervalSec=300
StartLimitBurst=5

# --- 超时和看门狗 ---
TimeoutStartSec=30s
TimeoutStopSec=60s
WatchdogSec=30s

# --- 资源限制 ---
CPUQuota=200%
CPUWeight=100
MemoryMax=2G
MemoryHigh=1536M
MemorySwapMax=0
TasksMax=4096
LimitNOFILE=65536
LimitNPROC=4096

# --- 安全加固 ---
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectKernelLogs=yes
ProtectClock=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
ReadWritePaths=/var/lib/myapp /var/log/myapp
ReadOnlyPaths=/etc/myapp

# --- 日志 ---
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp
LogRateLimitIntervalSec=30s
LogRateLimitBurst=10000

# ============================================================
# [Install] 安装信息
# ============================================================
[Install]
WantedBy=multi-user.target

6.2 部署流程

写好 Service 文件后,部署流程如下:

# 1. 创建运行用户
sudo useradd --system --no-create-home --shell /usr/sbin/nologin myapp

# 2. 创建必要目录
sudo mkdir -p /var/lib/myapp /var/log/myapp /etc/myapp
sudo chown myapp:myapp /var/lib/myapp /var/log/myapp

# 3. 部署二进制和配置文件
sudo cp myapp /usr/local/bin/myapp
sudo chmod 755 /usr/local/bin/myapp
sudo cp config.yaml /etc/myapp/config.yaml

# 4. 部署 Service 文件
sudo cp myapp.service /etc/systemd/system/myapp.service

# 5. 重新加载 systemd 配置
sudo systemctl daemon-reload

# 6. 启用并启动服务
sudo systemctlenable--now myapp.service

# 7. 验证服务状态
systemctl status myapp.service
journalctl -u myapp.service -n 50 --no-pager

6.3 Java 服务示例

Java 服务和 Go 服务的主要区别在于:JVM 启动慢需要更长的超时时间、内存模型不同需要调整限制策略、不支持 sd_notify 通常用Type=exec。

# /etc/systemd/system/myapp-java.service
[Unit]
Description=My Java Application
After=network-online.target
Wants=network-online.target

[Service]
Type=exec

ExecStart=/usr/bin/java 
  -Xms512m -Xmx1536m 
  -XX:+UseZGC 
  -jar /opt/myapp/myapp.jar 
  --spring.config.location=/etc/myapp/

User=myapp
Group=myapp
WorkingDirectory=/opt/myapp

Restart=on-failure
RestartSec=10s
StartLimitIntervalSec=300
StartLimitBurst=3

# JVM 启动慢,给足时间
TimeoutStartSec=120s
# 优雅关闭需要时间(Spring Boot shutdown hook)
TimeoutStopSec=60s

# 内存限制要考虑 JVM 堆外内存,设为 Xmx 的 1.5 倍左右
MemoryMax=3G
MemoryHigh=2560M
MemorySwapMax=0
CPUQuota=400%
LimitNOFILE=65536
TasksMax=4096

# 安全加固
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ReadWritePaths=/var/lib/myapp /var/log/myapp /tmp

StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp-java

[Install]
WantedBy=multi-user.target

Java 服务的MemoryMax不能简单等于-Xmx。JVM 除了堆内存还有 Metaspace、线程栈、直接内存、JIT 编译缓存等堆外开销,实际内存占用通常是-Xmx的 1.3 到 1.8 倍。MemoryMax设太小会导致 JVM 被 OOM Kill,设太大又失去了限制的意义。建议用-Xmx的 1.5 倍作为起点,再根据实际监控数据调整。

七、systemctl 常用命令速查

7.1 服务生命周期管理

# 启动/停止/重启/重载
sudo systemctl start myapp.service
sudo systemctl stop myapp.service
sudo systemctl restart myapp.service
sudo systemctl reload myapp.service    # 发送 SIGHUP,不中断服务
sudo systemctl reload-or-restart myapp.service # 支持 reload 就 reload,否则 restart

# 开机自启
sudo systemctlenablemyapp.service    # 设置开机自启
sudo systemctldisablemyapp.service   # 取消开机自启
sudo systemctlenable--now myapp.service # 设置自启并立即启动

# 彻底屏蔽服务(防止被其他服务拉起)
sudo systemctl mask myapp.service
sudo systemctl unmask myapp.service

7.2 状态查看和诊断

# 查看服务状态(最常用)
systemctl status myapp.service

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

# 查看服务是否启动失败
systemctl is-failed myapp.service

# 查看所有失败的服务
systemctl --failed

# 查看服务的完整配置(合并 drop-in)
systemctl cat myapp.service

# 查看服务的所有属性
systemctl show myapp.service

# 查看某个具体属性
systemctl show myapp.service -p MainPID -p MemoryCurrent -p CPUUsageNSec

7.3 配置管理和分析

# 修改后重新加载配置(不重启服务)
sudo systemctl daemon-reload

# 用 drop-in 方式修改配置(推荐)
sudo systemctl edit myapp.service
# 编辑完整的 service 文件(不推荐,会覆盖原文件)
sudo systemctl edit --full myapp.service

# 分析启动耗时
systemd-analyze             # 总启动时间
systemd-analyze blame          # 各服务启动耗时排序
systemd-analyze critical-chain myapp.service # 关键路径分析

# 安全审计
systemd-analyze security myapp.service

# 验证 unit 文件语法
systemd-analyze verify /etc/systemd/system/myapp.service

# 查看服务的依赖树
systemctl list-dependencies myapp.service
systemctl list-dependencies --reverse myapp.service # 反向:谁依赖我

7.4 日志查看速查

# 查看某个服务的日志
journalctl -u myapp.service

# 实时跟踪(tail -f 模式)
journalctl -u myapp.service -f

# 最近 N 条
journalctl -u myapp.service -n 100

# 时间范围
journalctl -u myapp.service --since"2026-02-06 0000"--until"2026-02-06 1200"
journalctl -u myapp.service --since"30 min ago"

# 按级别过滤
journalctl -u myapp.service -p err   # error 及以上
journalctl -u myapp.service -p warning # warning 及以上

# 输出格式
journalctl -u myapp.service -o json-pretty # JSON 格式
journalctl -u myapp.service -o short-iso  # ISO 时间格式

# 查看上一次启动的日志(排查重启前的崩溃原因)
journalctl -u myapp.service -b -1

# 查看内核 OOM Kill 记录
journalctl -k | grep -i"oom|killed"

7.5 资源监控

# 查看服务的实时资源占用
systemctl status myapp.service
# 输出中包含 Memory: 和 CPU: 行

# 查看 cgroup 级别的详细资源数据
systemctl show myapp.service -p MemoryCurrent -p MemoryPeak -p CPUUsageNSec

# 查看所有服务的资源占用排序
systemd-cgtop

# 查看某个服务的 cgroup 路径
systemctl show myapp.service -p ControlGroup

# 直接查看 cgroup 文件(更详细)
cat /sys/fs/cgroup/system.slice/myapp.service/memory.current
cat /sys/fs/cgroup/system.slice/myapp.service/cpu.stat

八、总结

8.1 技术要点回顾

Unit/Target/Slice是 systemd 的三个核心抽象:Unit 是管理单元,Target 是逻辑分组,Slice 是资源分组

Service Type 选型:优先用notify(程序支持的话),其次exec,传统 daemon 用forking,一次性任务用oneshot

依赖管理:After/Before控制顺序,Wants/Requires控制强度,生产环境优先用Wants+After组合

重启策略:Restart=on-failure覆盖大多数场景,配合RestartSteps实现指数退避,用StartLimitBurst防止无限重启

Watchdog:解决"进程活着但卡死"的问题,需要程序端配合发送心跳

资源限制:MemoryMax硬限制兜底,MemoryHigh软限制缓冲,CPUQuota防止单服务吃满 CPU

安全加固:NoNewPrivileges=yes零成本必加,ProtectSystem=strict+ReadWritePaths最小化文件系统权限

日志管理:用 journald 统一管理,配置LogRateLimitBurst防日志风暴,配置SystemMaxUse防磁盘写爆

Timer 替代 Cron:日志集成、依赖管理、资源限制、错过补执行,全面优于 cron

Socket 激活:按需启动、零停机重启,适合低频访问或需要平滑重启的服务

8.2 Service 文件编写 Checklist

写完一个 Service 文件后,对照这个清单检查一遍:

[ ] Type 选对了吗?程序的启动行为和 Type 匹配吗?
[ ] 依赖关系配了吗?After 和 Wants 写对了吗?
[ ] 用非 root 用户运行了吗?User/Group 配了吗?
[ ] Restart 策略配了吗?RestartSec 和 StartLimitBurst 配了吗?
[ ] 超时时间合理吗?TimeoutStartSec/TimeoutStopSec 够用吗?
[ ] 内存限制配了吗?MemoryMax 和 MemoryHigh 设了合理的值吗?
[ ] CPU 限制配了吗?CPUQuota 设了上限吗?
[ ] LimitNOFILE 够大吗?高并发服务至少 65536
[ ] NoNewPrivileges=yes 加了吗?
[ ] ProtectSystem=strict 加了吗?ReadWritePaths 列全了吗?
[ ] 日志速率限制配了吗?LogRateLimitBurst 设了吗?
[ ] systemd-analyze security 跑过了吗?评分在 3 分以内吗?

8.3 进阶学习方向

Service 文件写好只是起点,systemd 的能力远不止于此。以下几个方向在生产环境中有明确的落地价值,值得持续跟进。

1. systemd-nspawn 轻量级容器

systemd-nspawn 可以理解为"systemd 原生的容器运行时"。它不需要 Docker 或 containerd,直接用一个目录树作为根文件系统就能启动一个隔离的 Linux 环境。典型场景是构建环境隔离和遗留应用封装——比如在 RHEL 9 的宿主机上跑一个 CentOS 7 的 nspawn 容器来编译老项目,或者把一个不方便容器化的传统 Java 应用丢进 nspawn 里做资源隔离。machinectl命令管理 nspawn 实例,systemd-nspawn@.service模板让它和普通 Service 一样被 systemctl 管理。相比 Docker,nspawn 的优势在于和 systemd 生态的深度集成——日志走 journald、资源限制走 cgroup slice、网络走 systemd-networkd,运维工具链完全统一。

2. Portable Services

Portable Services 是 systemd 240+ 引入的特性,目标是在"传统 Service 文件"和"完整容器化"之间找一个平衡点。它把应用和依赖打包成一个 OS 镜像(通常是 raw 或 squashfs 格式),通过portablectl attach挂载到宿主机上,自动生成对应的 Service/Timer 文件。应用运行时共享宿主机内核但使用自己的用户空间库,既解决了依赖冲突问题,又不需要完整的容器编排栈。对于边缘计算节点、嵌入式网关这类资源受限且不适合跑 K8s 的场景,Portable Services 是一个务实的选择。

3. systemd-sysext 和 Composefs

systemd 254+ 的 sysext(System Extensions)机制允许在不可变根文件系统上叠加扩展层,配合 Composefs 实现内容寻址的只读文件系统叠加。这个方向和 Flatcar Container Linux、Fedora CoreOS 等不可变基础设施操作系统密切相关。如果团队在推进不可变基础设施或 GitOps 驱动的节点管理,sysext 是绕不开的技术点。

8.4 参考资料

systemd 官方文档- 最权威的参数说明

systemd.service(5)- Service 文件完整参数列表

systemd.exec(5)- 执行环境配置(安全加固参数在这里)

systemd.resource-control(5)- 资源限制参数

Arch Wiki - systemd- 社区维护的实用指南

systemd-nspawn(1)- nspawn 容器完整参数说明

Portable Services 文档- Portable Services 设计文档和使用指南

systemd-sysext(8)- 系统扩展层管理工具

六、总结

6.1 技术要点回顾

回头看整篇文章,有几个核心认知需要钉死:

Unit 文件三段式结构([Unit]/[Service]/[Install])是基础中的基础。[Unit]管依赖和描述,[Service]管运行行为,[Install]管启用方式。写 Service 文件的第一步不是去查参数,而是先把这三个 Section 的职责分清楚。搞混了职责,参数放错 Section,systemd 不会报错但也不会生效,排查起来浪费时间。

Service Type 选择直接决定 systemd 对进程生命周期的判定逻辑。大多数现代应用(Go 二进制、Node.js、Python 脚本)直接用simple就够了,进程在前台跑,PID 1 直接追踪。传统 daemon 类程序(比如老版本的 MySQL、Nginx)会 fork 子进程后父进程退出,这种必须用forking并配合PIDFile。一次性初始化脚本(建目录、改权限、跑迁移)用oneshot,配合RemainAfterExit=yes让 systemd 认为服务处于 active 状态。Type 选错了,轻则systemctl status显示状态不对,重则 systemd 误判进程已死反复重启。

资源限制和安全加固不是锦上添花,是生产级配置的标配。CPUQuota防止单个服务吃满所有核心拖垮整机,MemoryMax在 OOM 之前主动干掉失控进程,ProtectSystem=strict把根文件系统锁成只读,PrivateTmp=yes隔离临时目录防止跨服务信息泄露。这些配置的成本几乎为零,但缺了它们,一个失控的服务就能把整台机器拉下水。

Timer 单元完全可以替代 cron,且在所有维度上更优。cron 的问题在于:没有日志集成(输出丢了就是丢了)、没有依赖管理(不能声明"等网络就绪再跑")、没有资源限制(定时脚本跑飞了没人管)、错过的任务不会补执行。systemd Timer 把这些问题全部解决了,Persistent=yes一个参数就能处理机器关机期间错过的任务,OnCalendar的语法比 crontab 的五星表达式可读性强得多。2026 年了,新项目没有理由再用 cron。

6.2 进阶学习方向

systemd 的能力边界远不止 Service 文件。以下三个方向在生产环境中有明确的落地价值,值得持续跟进。

1. systemd-nspawn 轻量级容器

systemd 自带的容器运行时,不依赖 Docker 或 containerd,直接用一个目录树作为根文件系统就能拉起隔离环境。典型场景是构建环境隔离和遗留应用封装。和 Docker 相比,nspawn 的核心优势在于和 systemd 生态的深度集成——日志走 journald、资源限制走 cgroup slice、网络走 systemd-networkd,运维工具链完全统一,不需要额外引入一套容器编排体系。对于不需要镜像分发能力、只需要本地隔离的场景,nspawn 比 Docker 更轻量也更省心。

2. Portable Services(可移植服务单元)

systemd 240+ 引入的特性,定位在"裸 Service 文件"和"完整容器化"之间。把应用和依赖打包成 OS 镜像,通过portablectl attach挂载到宿主机,自动生成对应的 Service/Timer 文件。应用共享宿主机内核但使用自己的用户空间库,既解决依赖冲突又不需要完整的容器编排栈。对于边缘计算节点、嵌入式网关这类资源受限且不适合跑 K8s 的场景,Portable Services 是一个务实的选择。

3. systemd-homed 用户目录管理

systemd 245+ 引入的用户目录管理方案,把用户的家目录封装成一个可加密、可迁移的独立单元(LUKS 加密镜像或 fscrypt 目录)。用户登录时自动挂载解密,登出时自动卸载锁定。对于多用户共享的开发服务器或需要满足数据加密合规要求的场景,homed 提供了一种比传统/home+ LDAP 更现代的方案。homectl命令管理用户,用户记录以 JSON 格式存储,支持跨机器迁移。

6.3 参考资料

systemd 官方文档- 所有 man page 的在线版本,参数说明以这里为准

Lennart Poettering - The systemd for Administrators Blog Series- systemd 作者本人写的系列博客,从设计哲学到具体用法都有覆盖,虽然部分内容写于早期版本,但核心思路至今适用

Arch Wiki - systemd- 社区维护的实用指南,示例丰富,更新及时,遇到具体问题时往往比官方文档更容易找到答案

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11854

    浏览量

    219828
  • JAVA
    +关注

    关注

    20

    文章

    3015

    浏览量

    117028
  • 文件
    +关注

    关注

    1

    文章

    599

    浏览量

    26171

原文标题:Systemd 入门到精通:编写一个生产级的 Service 配置文件

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    基于libconfig进行配置文件解析

    本文介绍基于libconfig进行配置文件解析
    的头像 发表于 06-08 10:18 3300次阅读
    基于libconfig进行<b class='flag-5'>配置文件</b>解析

    探讨PROE的配置文件——系统配置文件config.pro

    PROE的配置文件让不少初学者感到烦恼,尽管不少教材里都会提到关于PROE的配置文件。但大多数显得过于理论化,而不便于初学者理解,可操作性不强。本文力求以通俗的语言结合
    发表于 09-28 15:50 0次下载

    ICD配置文件的详细介绍和配置内容的详细概述

    配置文件配置文件是利用SCL语言描述变电站设备对象模型后生成的文件,用于在不同厂商的配置工具之间交换配置信息。
    的头像 发表于 06-02 11:16 2w次阅读
    ICD<b class='flag-5'>配置文件</b>的详细介绍和<b class='flag-5'>配置</b>内容的详细概述

    SHARC音频模块:配置文件对裸机框架进行配置的重要性

    详细介绍如何从配置文件配置裸机框架的重要方面。
    的头像 发表于 06-27 06:02 3438次阅读
    SHARC音频模块:<b class='flag-5'>配置文件</b>对裸机框架进行<b class='flag-5'>配置</b>的重要性

    Keil的黑色界面配置文件配置方法

    本文档的主要内容详细介绍的是Keil的黑色界面配置文件配置方法。
    发表于 12-03 15:05 26次下载

    FreeRTOS_004_FreeRTOSConfig.h配置文件

    FreeRTOS_004_FreeRTOSConfig.h配置文件
    的头像 发表于 03-14 11:18 3946次阅读
    FreeRTOS_004_FreeRTOSConfig.h<b class='flag-5'>配置文件</b> (<b class='flag-5'>一</b>)

    AD8283评估板设计和配置文件

    AD8283评估板设计和配置文件
    发表于 05-31 16:04 9次下载
    AD8283评估板设计和<b class='flag-5'>配置文件</b>

    labview读写配置文件实例分享

    labview读写配置文件实例分享
    发表于 11-01 16:05 49次下载

    SpringBoot配置文件application

    Map配置 YML配置文件: sys-num: mymap: "{'a':'aaa','b':'bbb'}" 方法内: public class learnMap { @Value
    的头像 发表于 01-13 15:28 1249次阅读

    KT142C语音芯片配置文件总是不起作用?配置文件的问题集中归纳

    KT142C语音芯片配置文件总是不起作用?配置文件的问题集中归纳
    的头像 发表于 10-20 15:04 1832次阅读
    KT142C语音芯片<b class='flag-5'>配置文件</b>总是不起作用?<b class='flag-5'>配置文件</b>的问题集中归纳

    linux修改网卡ip配置文件

    Linux是种开源的操作系统,因此,它给用户提供了很高的自由度,可以根据个人需要进行各种定制和配置。其中,修改网络接口配置文件是常见的操作,可以通过修改网卡ip配置文件来设置网络接口
    的头像 发表于 11-17 10:51 3894次阅读

    ROS编写参数配置文件示例程序

    _config.yaml这三文件中,这三文件均位于下图所示的目录下,下面依次进行详细的介绍 1、编写user_config.yaml参
    的头像 发表于 11-26 17:35 3291次阅读
    ROS<b class='flag-5'>编写</b>参数<b class='flag-5'>配置文件</b>示例程序

    springboot的全局配置文件有几种

    Spring Boot是种快速开发框架,其通过提供配置文件来实现对应用程序的配置。全局配置文件在Spring Boot中起着非常重要的作用,可以用于
    的头像 发表于 12-03 15:28 3080次阅读

    zookeeper的核心配置文件是什么

    Zookeeper是常用的分布式协调服务,它被广泛应用于大型分布式系统中。Zookeeper的核心配置文件是zoo.cfg,它包含了Zookeeper服务器的各种配置参数,可以通过
    的头像 发表于 12-04 10:33 1900次阅读

    php的配置文件是什么

    PHP的配置文件种用于配置PHP解释器的文本文件。它包含了系列的指令和选项,用于影响PHP的行为和性能。通过修改
    的头像 发表于 12-04 15:55 2575次阅读