サービスとして動かす
インストーラは systemd ユニットを有効にして起動したままにします。このページは
そのユニットを順に読み、操作方法を示し、二つの失敗の形を外から見たままに説明
します。起動しないサーバーと、起動中のサーバーが設定を拒否する場合です。
🧾 ユニットがすること
Section titled “🧾 ユニットがすること”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は意図的に置いていません。サービスアカウントのホームは/var/lib/pingclairなので、証明書は/var/lib/pingclair/.local/share/pingclairにあります——バイナリ自身の既定で、インストーラが作成・移行するディレクトリで、pingclair environが印字するパスです。ここでストアを名指しすれば、すでに 一つの答えがある問いに二つ目の答えを与えることになります。ExecStartPreでvalidateを走らせることは意図的にしていません。安全な検査 場所に見えますが、それが罠です。systemdがRestartPreventExitStatus=を 適用するのは主プロセスであって、失敗した事前コマンドではありません。そのため コンパイラが拒否する設定は、ユニットを failed のまま残す代わりに 5 秒ごとに 再試行されていました。サーバー自身が何かをバインドする前にファイルを コンパイルし、拒否するときは終了コード 1 で終わります。それが上の再起動 ポリシーが書かれた理由の終了コードで、pingclair runはそのプロセスになる ために存在します。ExecReloadはSIGUSR1を送ります。これはサーバーが「ファイルを読み直せ」と 解釈する信号です。SIGHUPは意図的に無視され、それを送っていたユニットは 古い設定が動き続けたまま成功を報告していました (issue #66)。systemdが観測できるのはkillの終了だけなので、サーバーはファイルをどう 扱ったかをユニットの status line に載せます——Serving (reloaded 1 listener(s) in 323.341µs)、あるいはReload rejected: …——そしてsystemctl statusがそれを表示します。詳しくは下の 再読み込みの節にあります。Restart=on-failureとRestartPreventExitStatus=1、RestartSec=5s: 終了 コード 1 は設定もしくは証明書ストアがそもそも使えなかったことを意味するので、 5 秒ごとに再試行せず、ユニットは運用者が見るためにfailedのまま残ります。 それ以外の失敗は再起動されます。ProtectSystem=full、PrivateTmp、NoNewPrivileges、LimitNPROC、LimitNOFILE: サーバーは必要なファイルシステムの見え方とプロセス上限だけを 受け取り、それ以上は受け取りません。
どちらの導入経路も同じファイルを書きます。一行インストールは
scripts/pingclair.service のバイト単位のコピーを埋め込んでおり——just repo-lint は両者がずれれば失敗します——curl | bash の新規導入とリポジトリ
からの導入は同じユニットを生み、
systemd-analyze verify /etc/systemd/system/pingclair.service はどちらの経路でも
このユニットについて何も報告しません。
🎛️ サービスの操作
Section titled “🎛️ サービスの操作”pc service はこのユニットに対する 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 はサーバーが送った準備完了行を含め、ユニット自身の見方を
表示します。
● 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 も
—— がその信号を代わりに送ります。ユニットの ExecReload は
/bin/kill -USR1 $MAINPID で、明らかな命令が動く命令になりました。SIGHUP を
送っていたユニットは成功を報告して何も適用しませんでした。それが
issue #66 に記録されて
います。
pingclair reload は Admin API 経由で同じコードに到達し、サーバーがファイルを
どう見たかを報告します。グローバルオプションの admin が必要です。
✅ Configuration reloaded successfullyError: ❌ Reload failed (400): HTTP/1.1 400 Bad Requestsystemctl reload が報告できるのは一つだけです。kill が信号を届けたこと。
サーバーはそのあとにファイルを読むので、その判断はユニットの 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 が追加・削除
されたアドレスを示します——再読み込みが適用するのはポリシーであり、新しい
待ち受けソケットではないからです。
ユニットは 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 シンクを設定し、
インストーラが作成してサービスユーザーに与える /var/log/pingclair の下に
書き出します。
⚠️ サービスが立ち上がらないとき
Section titled “⚠️ サービスが立ち上がらないとき”is-activeがactivatingのままでNRestartsが増え続ける。 導入された これは古いユニットで引退した挙動です。RestartPreventExitStatusを伴わないRestart=alwaysを積んでいたため、サーバーが拒否する設定は 5 秒ごとに再試行 され、「一度失敗したユニット」ではなく「いつまでも落ち着かないユニット」に 見えていました。原因はもう一つあります。validateをExecStartPreとして 走らせていたことで、RestartPreventExitStatusはそれを覆いません。導入される ユニットはRestart=on-failure+RestartPreventExitStatus=1を積み、 事前コマンドを持ちません。拒否された起動はis-activeをfailedのまま、NRestartsをゼロのままにします。古い導入では、デバッグの前にループを 止めます: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が導入済みユニットについてMissing '=', ignoring lineを出す。 古いワンライナー導入は、コメントが シェルに展開されたユニットを書いていました——--helpの出力 25 行で、systemdはそれを無視します。現在のインストーラで再導入するとユニットは そのまま書かれ、この報告は消えます。- ユニットは動いているのに何も応答しない。 リスナーはバインド済みで要求が 届いていません。インストール と同じく、まずプロバイダの ファイアウォール、次にホスト側を確認します。
🧭 次の手順
Section titled “🧭 次の手順”- アップグレードと削除: 再実行が何を保つか、そして全部 取り除く方法。
- HTTPS: 証明書、ストアの場所、そして
pingclair trustにPINGCLAIR_TLS_STOREが必要な理由。 log: このページが journal から読んでいる アクセスログの出力先。
