以服务方式运行
安装器留下一个已启用、正在运行的 systemd unit。本页逐条读这个 unit,演示如何
操作它,并说明两种故障从外面看是什么样子:起不来的服务,以及运行中的服务器拒绝
新配置。
🧾 这个 unit 做了什么
Section titled “🧾 这个 unit 做了什么”systemctl cat pingclair真正重要的键是这些:
[Service]Type=notifyNotifyAccess=mainUser=pingclairGroup=pingclairAmbientCapabilities=CAP_NET_BIND_SERVICECapabilityBoundingSet=CAP_NET_BIND_SERVICEEnvironment="RUST_LOG=info"ExecStart=/usr/local/bin/pingclair run /etc/Pingclair/PingclairfileExecReload=/bin/kill -USR1 $MAINPIDWorkingDirectory=/var/lib/pingclairRestart=on-failureRestartPreventExitStatus=1RestartSec=5sLimitNOFILE=1048576LimitNPROC=512ProtectSystem=fullPrivateTmp=trueNoNewPrivileges=true按顺序读:
Type=notify与NotifyAccess=main:服务器在监听器绑定完成时通知systemd,所以systemctl start等到的是代理真的能应答,而不是进程存在。User=pingclair加上AmbientCapabilities=CAP_NET_BIND_SERVICE:服务以非特权 用户运行,同时仍能绑定 80 和 443。- 这里刻意不写
PINGCLAIR_TLS_STORE。服务账号的 home 是/var/lib/pingclair, 所以证书放在/var/lib/pingclair/.local/share/pingclair:二进制自己的默认值、 安装器创建并迁移的目录,也是pingclair environ打印的路径。在这里另写一个 存储路径,等于给已经有答案的问题再答一次。 - 这里刻意不写
ExecStartPre去跑validate。那看起来是放检查的安全位置,恰恰 也是陷阱:systemd的RestartPreventExitStatus=作用于主进程,不作用于失败 的前置命令,所以编译器拒绝的配置会每五秒被重试一次,而不是让 unit 停在 failed。服务器自己在绑定任何东西之前编译文件,拒绝时以退出码 1 结束——这正是 上面的重启策略为之而写的退出码,pingclair run的存在就是为了成为那个进程。 ExecReload发送SIGUSR1,也就是服务器理解为「重新读文件」的信号。SIGHUP被刻意忽略,而过去发送它的 unit 会在旧配置继续服务的同时报成功 (issue #66)。systemd只能观察到kill退出了,所以服务器把对文件的判断发布到 unit 的 status line 上——Serving (reloaded 1 listener(s) in 323.341µs),或者Reload rejected: …——systemctl status会显示它。下面的 重载意味着什么 是详细版本。Restart=on-failure配合RestartPreventExitStatus=1和RestartSec=5s: 退出码 1 意味着配置或证书存储根本用不了,所以 unit 会停在failed等运维 人员来看,而不是每五秒重试一次。其他失败会重启。ProtectSystem=full、PrivateTmp、NoNewPrivileges、LimitNPROC、LimitNOFILE:服务器只拿到它需要的文件系统视野和进程上限,不多拿。
两条安装路径写的是同一个文件。一行安装内嵌了 scripts/pingclair.service 的
逐字节副本——两者一旦分叉 just repo-lint 就会失败——所以全新的 curl | bash
安装和从仓库安装产出同一个 unit;两条路径下 systemd-analyze verify /etc/systemd/system/pingclair.service 都不会就这个 unit 说任何话。
🎛️ 操作服务
Section titled “🎛️ 操作服务”pc service 就是这个 unit 的 systemctl 包装,两者可以互换:
| 目的 | 用 pc |
用 systemctl |
|---|---|---|
| 启动 | sudo pc service start |
sudo systemctl start pingclair |
| 停止 | sudo pc service stop |
sudo systemctl stop pingclair |
| 重载配置 | sudo pc service reload |
sudo systemctl reload pingclair |
| 改动监听器或进程级设置后重启 | sudo pc service restart |
sudo systemctl restart pingclair |
| 查看状态 | pc service status |
systemctl status pingclair |
| 跟踪日志 | — | journalctl -u pingclair -f |
pc service status 显示的就是 unit 自己的视角,包括服务器上报的就绪行:
● pingclair.service - Pingclair High-Performance Web Server Loaded: loaded (/etc/systemd/system/pingclair.service; enabled; preset: enabled) Active: active (running) Docs: https://github.com/dorianverlaine/pingclair Main PID: 1808 (pingclair) Status: "Serving (reloaded 1 listener(s) in 323.341µs)"🔁 重载意味着什么
Section titled “🔁 重载意味着什么”改过的 /etc/Pingclair/Pingclairfile 通过一个信号到达正在运行的服务器,发送它
的有两条命令。
SIGUSR1 是重载信号,本身不需要任何配置:
sudo kill -USR1 "$(systemctl show -p MainPID --value pingclair)"pc service reload——或者同一次调用的 sudo systemctl reload pingclair——替你
发送这个信号。unit 的 ExecReload 是 /bin/kill -USR1 $MAINPID,那条理所当然的
命令现在就是能用的命令。过去发送 SIGHUP 的 unit 报成功却什么都不应用,这一点
记录在 issue #66。
pingclair reload 通过 Admin API 走到同一段代码,并报告服务器对文件的判断,
需要在全局选项块里写 admin:
✅ Configuration reloaded successfullyError: ❌ Reload failed (400): HTTP/1.1 400 Bad Requestsystemctl reload 只能报告一件事:kill 把信号送到了。服务器是在那之后才读
文件的,所以它的判断落在 unit 的 status line 和日志里。pc service reload 就
这么说,而不是声称配置已经生效:
$ sudo pc service reload✅ Reload signal delivered to pingclair.serviceℹ️ The result lands a moment later: `systemctl status pingclair` or `journalctl -u pingclair -n 20`$ systemctl status pingclair --no-pager | grep Status Status: "Serving (reloaded 1 listener(s) in 323.341µs)"运行中的服务器无法应用文件所要求的内容时,旧配置会继续服务,status line 会指出
被拒的是哪一处改动。把站点从 :80 挪到 :8080 是最常见的情形,因为监听器拓扑
是启动时连套接字一起建的:
Status: "Reload rejected: listener topology changed (added: ["[::]:8080"], removed: ["[::]:80"]); restart Pingclair to rebuild H1, H2, H3, and TLS together"无论走哪条路,编译不过的配置都会让旧配置继续运行。先校验:
sudo pingclair validate /etc/Pingclair/Pingclairfile例外是与整个进程有关的策略。像 trusted_proxies 这样在启动时确立的选项,只有
重启后才生效:sudo pc service restart。改动监听器的配置也会被同样地拒绝——
status line 会列出新增和移除的地址——因为重载应用的是策略,不是新的监听
套接字。
unit 设置 RUST_LOG=info,把所有内容送进 journal:
sudo journalctl -u pingclair -fsudo journalctl -u pingclair --since '10 min ago'启动、重载、证书工作,以及每个请求一行访问日志都会出现在那里:
INFO pingclair::run: 🚀 Starting Pingclair v0.2.0-rc.3INFO pingclair::run: 📄 Loaded configuration from: /etc/Pingclair/PingclairfileINFO pingclair::run: 🔔 Received SIGUSR1, reloading configuration from: /etc/Pingclair/PingclairfileINFO pingclair::run: 📋 Step 1/3: Validating configuration...INFO pingclair::run: ✅ Configuration reload completed successfully in 323.341µsINFO pingclair::run: 📊 1 listener(s) updatedINFO pingclair_proxy::server: 📝 Access request_id="65c09fa25d457-6" method="GET" host="localhost" path="/" status=200 bytes=18747 duration_ms=0 remote_ip=::1 user_agent="curl/8.18.0"服务器拒绝的重载也以同样的方式记录,带上原因和「什么都没变」的说明:
ERROR pingclair::run: ❌ Configuration reload rejected after 414.491µs: listener topology changed (added: ["[::]:8080"], removed: ["[::]:80"]); restart Pingclair to rebuild H1, H2, H3, and TLS together kind=RestartRequiredERROR pingclair::run: 💡 Previous configuration remains active, unchanged想要独立的、带轮转的日志,就配置 log sink,写到安装器创建并交给服务用户的
/var/log/pingclair 下。
⚠️ 服务起不来时
Section titled “⚠️ 服务起不来时”is-active一直显示activating,NRestarts不断上涨。 这是安装出来的 旧 unit 已经退役的行为:它带着Restart=always却没有RestartPreventExitStatus,所以服务器拒绝的配置每五秒被重试一次,看起来不是 一个「失败一次」的 unit,而是「永远不收敛」的 unit。还有第二个原因:它把validate当作ExecStartPre跑,而RestartPreventExitStatus不覆盖它。 现在安装出来的 unit 带Restart=on-failure+RestartPreventExitStatus=1, 没有前置命令,被拒绝的启动会让is-active停在failed、NRestarts停在 0。在旧安装上,调试前先停掉循环:sudo systemctl stop pingclair,改好文件, 然后sudo systemctl reset-failed pingclair。Job for pingclair.service failed because the control process exited with error code。 服务器在绑定任何东西之前拒绝了配置,编译器的原因在 journal 里,例如Error: ❌ Configuration Error: Compile error: Unsupported feature: `encode br`: Brotli is not implemented for proxied responses; use `encode zstd gzip`。TLS store /var/lib/pingclair/.local/share/pingclair is not writable: Permission denied。 存储属于服务账号。用sudo ls -ld /var/lib/pingclair/.local/share/pingclair确认属主是pingclair。systemd-analyze verify对已安装的 unit 报Missing '=', ignoring line。 旧的一键安装写出的 unit 里,注释被 shell 展开过——25 行--help输出,systemd会忽略它们。用当前安装器重装会把 unit 原样写入,这条报告就消失了。- unit 在跑但外面没有任何应答。 监听器已绑定,请求没到达。和 安装 一节一样,先查服务商防火墙,再查主机自身规则。
