分类: 技术教程

  • systemd 服务管理实战:从 systemctl 到编写自己的 service 单元

    systemd 服务管理实战:从 systemctl 到编写自己的 service 单元

    配好一台 Linux 服务器,装完软件只是第一步 —— 真正决定服务器长期健康运行的,是把服务管好:开机自启、异常自动拉起、日志可查、配置可改不重启。在主流发行版(Ubuntu、Debian、RHEL、Arch)上,这套管理工作几乎都由 systemd 接管。它既是 init 进程,又是服务管家,还是日志中心。本文从日常操作讲到手写 service 单元,全是能直接上手的实战内容。

    systemd 是什么:三个核心概念

    systemd 是如今 Linux 世界的标准初始化系统(PID 1),它把系统的每一个”进程单元”抽象成 unit(单元)。先记住这三个词,后面全都会用到:

    • service:服务单元,后台常驻进程,如 nginx.servicesshd.service
    • target:目标的组合,相当于传统 SysV 的”运行级别”,如 multi-user.target(命令行)和 graphical.target(图形界面)
    • journald:systemd 自带的日志守护进程,用 journalctl 查看,统一收纳所有服务的标准输出和系统日志

    你不需要知道每个发行版用哪个 systemd 版本也能照常使用本文所有命令。当前主流:Ubuntu 24.04 LTS 用 systemd 255,Debian 12 用 systemd 252,Ubuntu 22.04 用 249,命令语法完全兼容。

    日常管理:systemctl 一页速查

    管理服务就是 systemctl + 动作 + 服务名,.service 后缀可以省略:

    systemctl start nginx        # 启动
    systemctl stop nginx         # 停止
    systemctl restart nginx      # 重启
    systemctl reload nginx       # 重载配置(不中断进程,前提是服务支持)
    systemctl status nginx       # 状态 + 最近日志,排查问题第一步
    systemctl enable nginx       # 开机自启
    systemctl disable nginx      # 取消开机自启
    systemctl enable --now nginx # 一键"开机自启 + 立即启动",最常用
    

    检查状态的三种姿势

    systemctl is-active nginx    # active / inactive / failed —— 适合脚本判断
    systemctl is-enabled nginx   # enabled / disabled
    systemctl is-failed nginx    # active / failed
    

    批量查看

    systemctl list-units --type=service --state=running   # 所有运行中的服务
    systemctl list-units --type=service --state=failed    # 挂掉的服务
    systemctl --failed                                    # 全部失败的 unit,开机后必查
    systemctl list-timers --all                           # 定时器及其下次触发时间
    systemctl list-unit-files --type=service              # 所有已安装的 unit 文件
    

    启动调优与系统控制

    systemd-analyze              # 开机总耗时
    systemd-analyze blame        # 每个服务分别耗时多少,找开机慢的元凶
    systemd-analyze critical-chain  # 启动关键路径,看卡在哪一环
    
    systemctl reboot             # 重启系统
    systemctl poweroff           # 关机
    systemctl get-default        # 当前默认 target(multi-user 或 graphical)
    systemctl set-default multi-user.target   # 命令行模式
    

    避坑enable 只设置自启不立即启动,start 只启动不自启。生产环境部署新服务,请永远使用 enable --now,别漏一半。

    排查故障:从 status 到 journalctl

    服务起不来是运维日常。标准的排查链是:

    systemctl status myapp          # 第一眼:状态、PID、退出的 exit code
    journalctl -u myapp -xeu myapp  # 日志全文:-x 带解释,-e 翻到末尾,-u 指定服务
    

    常见 exit code 含义:

    退出码 含义 常见原因
    203 Executable 不存在 ExecStart 路径写错或没权限执行
    200 加载 unit 失败 文件语法错误,先 daemon-reload
    216 用户/组不存在 User=/Group= 指向了不存在的账户

    修改了 unit 文件后必须执行 systemctl daemon-reload,否则 systemd 用的还是旧配置 —— 这是新手最常踩的坑。

    systemctl daemon-reload
    systemctl reset-failed myapp   # 清除 failed 状态,反复重启失败的会触发 rate limit
    

    实战:编写自己的 service 单元

    业务脚本、爬虫、API 服务,不要再用 nohup ... & 裸跑 —— 退出后没人帮你拉起来,开机也不自启。手写一个 service 单元只需三步。

    一个完整的示例

    假设你有一个 Python 应用 /opt/myapp/app.py,创建 /etc/systemd/system/myapp.service

    [Unit]
    Description=My Python Application
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=simple
    User=app
    Group=app
    WorkingDirectory=/opt/myapp
    EnvironmentFile=/etc/myapp/env.conf
    ExecStart=/usr/bin/python3 /opt/myapp/app.py
    Restart=on-failure
    RestartSec=3
    LimitNOFILE=65535
    
    [Install]
    WantedBy=multi-user.target
    

    然后一行启用:

    sudo systemctl daemon-reload
    sudo systemctl enable --now myapp
    

    关键字段逐条讲

    [Unit]

    • Description:给人类看的说明,systemctl status 时会显示
    • After=:声明依赖顺序,等网络就绪再启动,防止启动即报”连不上网”

    [Service] —— 核心:

    • Type=simple:默认值,ExecStart 起的进程就是主进程,多数场景选它
    • Type=forking:进程启动后会 fork 出子进程并自己退出(老派守护进程,如某些 Java 应用),需要配合 PIDFile=
    • User= / Group=永远用专用用户跑服务,不要用 root
    • Restart=on-failure:崩溃/非正常退出时自动拉起;线上服务建议配合 RestartSec=3 防抖。always 连正常退出也会拉起,慎用
    • EnvironmentFile=:把敏感配置(密码、token)放独立文件,权限收紧到 600,改配置不用动 unit
    • LimitNOFILE=:打开文件数上限,高并发服务必须调大,默认 1024 往往不够

    [Install]:只有写了 WantedBy= 的 unit 才能被 enable,开机自启就是把自己挂到对应 target 下。

    改配置不重启:drop-in 覆盖

    官方推荐用 systemctl edit 加覆盖片段,不改动系统原文件,系统升级后配置也不会被冲掉:

    sudo systemctl edit myapp
    # 在弹出的编辑器中写入:
    [Service]
    RestartSec=5
    

    保存后自动 daemon-reload,再 systemctl restart myapp 生效。查看合并后的最终配置:

    systemctl cat myapp    # 显示原文件 + 所有 drop-in 合并结果
    

    统一日志:journalctl 实战

    journald 把服务 stdout/stderr 和系统日志收进同一套日志,再也不用 cd /var/log 翻文件。

    常用过滤

    journalctl -u myapp              # 只看某个服务的日志
    journalctl -u myapp -f           # 实时跟踪,等价于 tail -f,排查首选
    journalctl -n 50                 # 最近 50 条
    journalctl -k                    # 内核日志
    journalctl -b                    # 本次开机以来的日志
    journalctl -b -1                 # 上次开机(需持久化,见下文)
    journalctl --list-boots          # 列出所有开机记录
    journalctl -p err                # 只看 err 及以上级别
    journalctl --since "1 hour ago"  # 时间过滤:today / "30min ago" / 具体时间点
    journalctl -u myapp --since today --until "10 minutes ago"   # 组合过滤
    journalctl --grep="OutOfMemory"  # 按正则搜索内容
    

    日志持久化:重启后日志不丢

    默认日志存在 /run/log/journal(内存盘),重启即清空。想要跨重启保留,只需创建持久化目录,systemd 检测到后自动切换:

    sudo mkdir -p /var/log/journal
    sudo systemd-tmpfiles --create --prefix /var/log/journal
    sudo systemctl restart systemd-journald
    

    磁盘占用控制

    日志跑久了会占磁盘,定期清理是基本功:

    journalctl --disk-usage               # 看占了多少
    sudo journalctl --vacuum-size=500M    # 清到 500MB 以下
    sudo journalctl --vacuum-time=30d     # 只保留最近 30 天
    

    更进一步:在 /etc/systemd/journald.conf.d/ 下建 00-storage.conf[Journal] + Storage=persistent / SystemMaxUse=200M,一次性限制日志上限,比反复手工清理省心。

    进阶:用 systemd timer 替代 cron

    cron 的痛点是没有统一日志、错过时间不补跑。timer 在 systemd 生态里日志、依赖、失败追踪全都有。示例:每天 3 点跑备份脚本。

    创建 /etc/systemd/system/backup.service

    [Unit]
    Description=Nightly backup job
    
    [Service]
    Type=oneshot
    ExecStart=/usr/local/bin/backup.sh
    

    创建 /etc/systemd/system/backup.timer

    [Unit]
    Description=Run backup daily at 3am
    
    [Timer]
    OnCalendar=daily
    Persistent=true
    
    [Install]
    WantedBy=timers.target
    

    启用方式与 service 相同:

    sudo systemctl enable --now backup.timer
    systemctl list-timers backup.timer   # 查看下次触发时间
    journalctl -u backup.service         # 查看每次执行日志
    

    Persistent=true 是杀手锏:机器当时关机没执行,下次开机自动补跑 —— 这是 cron 做不到的。OnCalendar= 语法与 cron 类似但更灵活,如 Mon..Fri *-*-* 09:00:00 表示工作日早上 9 点。

    实战总结

    • 部署服务:写 unit → daemon-reloadenable --nowsystemctl status 确认
    • 排查问题:systemctl status 看退出码 → journalctl -xeu 看日志
    • 服务异常:Restart=on-failure 自动拉起,别再用 nohup 裸跑
    • 改配置:systemctl edit 加 drop-in,别直接动 /usr/lib/systemd/
    • 开机必查:systemctl --failed,把红灯消灭在平时

    从”能跑”到”跑得稳”,systemd 就是那道分水岭。

    参考资料

  • Ubuntu 手动使用 SteamCMD 搭建幻兽帕鲁(Palworld)专属服务器

    Ubuntu 手动使用 SteamCMD 搭建幻兽帕鲁(Palworld)专属服务器

    想和三五好友拥有一个完全属于自己、随时可以调参数、不怕官方服挤爆的帕鲁世界?自己开一个专属服务器(Dedicated Server)是最稳妥的方案。本教程带你从零开始,在 Ubuntu 上用 SteamCMD 手动搭建帕鲁服务器 —— 不需要拥有游戏本体,服务端完全免费。

    准备工作

    • 一台 Ubuntu 18.04+ 的服务器(VPS / 家用机 / 云主机均可)
    • root 或 sudo 权限
    • 建议配置:8GB 内存起步(4 人以内勉强够用),16GB 内存是 8~16 人服的安全线
    • 硬盘至少 30GB 可用空间(服务端约 8~15GB,加上存档和更新余量)

    帕鲁 1.0(2026 年 7 月)更新后服务端加入了多线程支持,CPU 主频越高、单核性能越强,流畅度越好 —— 内存不足是绝大多数闪退、卡顿的元凶,预算优先给内存。

    第一步:安装 SteamCMD

    sudo apt update && sudo apt upgrade -y
    sudo add-apt-repository multiverse -y
    sudo dpkg --add-architecture i386
    sudo apt update
    
    # 预先同意 Steam 协议,避免安装时卡交互提示
    echo steam steam/question select "I AGREE" | sudo debconf-set-selections
    echo steam steam/license note '' | sudo debconf-set-selections
    
    sudo apt install steamcmd -y
    

    如果 apt 源里没有 steamcmd(部分最小化系统),也可以直接用 Valve 官方的压缩包:

    sudo apt install -y lib32gcc-s1
    cd /opt && sudo curl -sL https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz | sudo tar -xz
    

    第二步:创建专用运行用户(强烈建议)

    永远不要用 root 跑游戏服务端。创建一个专用用户,即使服务器被攻破,攻击者也拿不到 root 权限:

    sudo adduser --disabled-password --gecos "" palworld
    sudo su - palworld
    

    第三步:下载帕鲁服务端

    帕鲁专属服务器在 Steam 上的 App ID 是 2394010,匿名登录即可下载:

    mkdir -p ~/palworld-server
    steamcmd +force_install_dir /home/palworld/palworld-server \
      +login anonymous \
      +app_update 2394010 validate \
      +quit
    

    首次下载大约 8~15GB,取决于网速需要 5~15 分钟。以后更新服务端也是重跑这条命令,会增量下载。

    第四步:首次启动,生成配置文件

    cd /home/palworld/palworld-server
    ./PalServer.sh
    

    运行 10~30 秒后按 Ctrl+C 停掉 —— 这一步是为了让服务端生成配置目录 Pal/Saved/Config/LinuxServer/

    第五步:配置服务器参数

    关键点:服务端真正读取的是 Pal/Saved/Config/LinuxServer/PalWorldSettings.ini不是 DefaultPalWorldSettings.ini。改错文件是最常见的翻车点。

    cp DefaultPalWorldSettings.ini Pal/Saved/Config/LinuxServer/PalWorldSettings.ini
    nano Pal/Saved/Config/LinuxServer/PalWorldSettings.ini
    

    常用配置项说明

    整个配置都在一行 OptionSettings=(...) 里,不要重排格式,找到想改的项修改即可:

    配置项 说明 示例
    ServerName 服务器显示名称 ServerName=彩云之端帕鲁服
    ServerDescription 服务器简介 描述文字
    ServerPassword 连接密码(留空=公开) ServerPassword=123456
    AdminPassword 管理员密码(游戏内管理命令必需) AdminPassword=Admin@2026
    ServerPlayerMaxNum 最大玩家数 ServerPlayerMaxNum=16
    PublicPort 游戏端口(默认 8211,UDP) PublicPort=8211
    RCONEnabled 开启 RCON 远程管理 RCONEnabled=True
    RCONPort RCON 端口 RCONPort=25575
    ExpRate / PalCaptureRate 经验倍率 / 帕鲁捕捉概率 ExpRate=2.0
    DeathPenalty 死亡惩罚(0=无掉落) DeathPenalty=None

    修改配置的注意事项

    修改配置前必须先停服,改完再启动,否则会被覆盖。

    第六步:注册为 systemd 服务,开机自启

    创建服务文件 /etc/systemd/system/palworld.service

    [Unit]
    Description=Palworld Dedicated Server
    After=network.target
    
    [Service]
    Type=simple
    User=palworld
    WorkingDirectory=/home/palworld/palworld-server
    ExecStart=/home/palworld/palworld-server/PalServer.sh -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS -publiclobby
    Restart=on-failure
    RestartSec=15
    LimitNOFILE=100000
    
    [Install]
    WantedBy=multi-user.target
    

    启动参数说明

    • -useperfthreads:优化 CPU 利用率
    • -NoAsyncLoadingThread:修复部分 Linux 内核上的加载问题
    • -UseMultithreadForDS:1.0 新增,多核服务器必备
    • -publiclobby:出现在社区服务器列表(纯私服可去掉)

    然后注册并启动服务:

    sudo systemctl daemon-reload
    sudo systemctl enable palworld   # 开机自启
    sudo systemctl start palworld
    sudo systemctl status palworld   # 查看状态
    journalctl -u palworld -f        # 实时查看日志
    

    第七步:开放防火墙端口

    注意:游戏端口是 UDP,不是 TCP,只开 TCP 是连不上的:

    sudo ufw allow 22/tcp               # SSH,务必保留
    sudo ufw allow 8211/udp             # 游戏主端口
    sudo ufw allow 27015/udp            # Steam 社区列表查询端口
    # RCON 只对管理员 IP 开放,千万别暴露给公网
    sudo ufw allow from 你的管理员IP to any port 25575
    

    连接你的服务器

    游戏内:加入多人游戏 → 直接连接,输入 服务器IP:8211 即可(有密码就填密码)。

    更新与备份

    更新服务端

    客户端和服端版本必须一致,否则提示版本不匹配。更新流程:

    sudo systemctl stop palworld
    sudo su - palworld -c "steamcmd +force_install_dir /home/palworld/palworld-server +login anonymous +app_update 2394010 validate +quit"
    sudo systemctl start palworld
    

    备份存档

    整个世界存档在 Pal/Saved 目录下,定期打包另存:

    tar -czf ~/palworld-backup-$(date +%F).tar.gz /home/palworld/palworld-server/Pal/Saved
    

    常见问题

    服务器开起来了但好友连不上?

    先查防火墙 8211/udp 是否放行,再确认云服务商的安全组/网络策略是否也放行了该端口(很多云主机有两层防火墙)。

    内存只有 8GB,频繁闪退怎么办?

    8GB 开 4 人以上的服很勉强。关闭其他服务、设置 swap,或直接升级内存 —— 这是性价比最高的方案。

    找不到服务器存档?

    存档在 Pal/Saved/SaveGames/0/ 下,每个世界一个目录,改名换服时整个目录一起搬。

    参考资料