fix(chv): 阶段 2 syncd 改走 systemd RuntimeDirectory=/run/chv-syncd
tmpfiles 行 d /run/chv/syncd 0755 chv chv 没用:tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,属主解析失败整行被跳过, 客内 /run/chv/syncd 根本不存在(2026-11-07 真机实测:镜像 里有这行、目录没有,syncd 反复退出、restart counter 57)。 改走单元 RuntimeDirectory=chv-syncd(服务启动时由 systemd 按服务用户建目录,不依赖启动时序);状态写失败改为记日志 不杀进程;daemon 与驱动的 syncd 路径同步改到 /run/chv-syncd/。
This commit is contained in:
1 parent
8013431f63
commit
42b295fc71
4 files changed
+36
-16
No files matched your search
+2
-2
@@ -318,8 +318,8 @@ VC_ARGS=(-a "$OUT/disk.raw"
|
|||||||
--run-command 'printf "[Unit]\nDescription=CHV browser sub-agent (Muse P2)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv-browser\nGroup=chv-browser\nExecStart=/usr/bin/python3 /usr/local/bin/chv-browser\nRestart=on-failure\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-browser.service'
|
--run-command 'printf "[Unit]\nDescription=CHV browser sub-agent (Muse P2)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv-browser\nGroup=chv-browser\nExecStart=/usr/bin/python3 /usr/local/bin/chv-browser\nRestart=on-failure\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-browser.service'
|
||||||
--run-command 'printf "[Unit]\nDescription=CHV guest runtime daemon (stage 2: heartbeat/resources/snapshot)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 /usr/local/bin/chv-daemon\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-daemon.service'
|
--run-command 'printf "[Unit]\nDescription=CHV guest runtime daemon (stage 2: heartbeat/resources/snapshot)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 /usr/local/bin/chv-daemon\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-daemon.service'
|
||||||
--run-command 'printf "[Unit]\nDescription=CHV guest exec daemon (stage 2: shell/files/codex hosting)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 /usr/local/bin/chv-execd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-execd.service'
|
--run-command 'printf "[Unit]\nDescription=CHV guest exec daemon (stage 2: shell/files/codex hosting)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=/usr/bin/python3 /usr/local/bin/chv-execd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-execd.service'
|
||||||
--run-command 'printf "[Unit]\nDescription=CHV Gitea sync daemon (stage 2: resident pull/push loop)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv\nGroup=chv\nExecStart=/usr/bin/python3 /usr/local/bin/chv-syncd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-syncd.service'
|
--run-command 'printf "[Unit]\nDescription=CHV Gitea sync daemon (stage 2: resident pull/push loop)\nAfter=network.target\n\n[Service]\nType=simple\nUser=chv\nGroup=chv\nRuntimeDirectory=chv-syncd\nRuntimeDirectoryMode=0755\nExecStart=/usr/bin/python3 /usr/local/bin/chv-syncd\nRestart=always\nRestartSec=2\n\n[Install]\nWantedBy=multi-user.target\n" > /etc/systemd/system/chv-syncd.service'
|
||||||
--run-command 'printf "d /run/chv 0755 root root -\nd /run/chv/shots 0755 chv-browser chv-browser -\nd /run/chv/tasks 0755 root root -\nd /run/chv/syncd 0755 chv chv -\n" > /etc/tmpfiles.d/chv.conf && systemctl enable chv-broker chv-browser chv-daemon chv-execd chv-syncd || true')
|
--run-command 'printf "d /run/chv 0755 root root -\nd /run/chv/shots 0755 chv-browser chv-browser -\nd /run/chv/tasks 0755 root root -\n" > /etc/tmpfiles.d/chv.conf && systemctl enable chv-broker chv-browser chv-daemon chv-execd chv-syncd || true')
|
||||||
# P1 交接(Muse P1 的 vsock_notify.py):文件在仓库里存在时拷入
|
# P1 交接(Muse P1 的 vsock_notify.py):文件在仓库里存在时拷入
|
||||||
# 镜像(P1 分支才有,本分支尚无);不存在时只警告、不失败——
|
# 镜像(P1 分支才有,本分支尚无);不存在时只警告、不失败——
|
||||||
# 合并后自动生效,P2 不改该脚本内容。
|
# 合并后自动生效,P2 不改该脚本内容。
|
||||||
|
|||||||
@@ -41,11 +41,13 @@ DAEMON_SOCK = "/run/chv/daemon.sock"
|
|||||||
SNAPSHOT_READY_PATH = "/run/chv/snapshot-ready.json"
|
SNAPSHOT_READY_PATH = "/run/chv/snapshot-ready.json"
|
||||||
EXECD_SOCK = "/run/chv/execd.sock"
|
EXECD_SOCK = "/run/chv/execd.sock"
|
||||||
EXECD_TASKS = "/run/chv/execd-tasks.json"
|
EXECD_TASKS = "/run/chv/execd-tasks.json"
|
||||||
# syncd 的状态文件在它自己的 chv 所有子目录里(见 build.sh
|
# syncd 的状态文件在 systemd RuntimeDirectory 里(见 build.sh
|
||||||
# tmpfiles:/run/chv 本身 0755 root:root,chv 身份的 syncd
|
# 的 syncd 单元:RuntimeDirectory=chv-syncd,服务启动时由
|
||||||
# 写不进——2026-11-07 真机实测:状态文件写失败致 syncd
|
# systemd 按 chv 属主建 /run/chv-syncd;不要放回 /run/chv/
|
||||||
# 反复退出、systemctl 长期 activating)。
|
# 下——tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,带 chv
|
||||||
SYNC_STATUS = "/run/chv/syncd/sync-status.json"
|
# 属主的 tmpfiles 行会被跳过,目录根本不存在,2026-11-07
|
||||||
|
# 真机实测 syncd 因此反复退出、长期 activating)。
|
||||||
|
SYNC_STATUS = "/run/chv-syncd/sync-status.json"
|
||||||
WORKSPACE = "/home/chv/workspace"
|
WORKSPACE = "/home/chv/workspace"
|
||||||
HEARTBEAT_INTERVAL = 10.0
|
HEARTBEAT_INTERVAL = 10.0
|
||||||
MAX_REQUEST_BYTES = 65536
|
MAX_REQUEST_BYTES = 65536
|
||||||
|
|||||||
+15
-2
@@ -30,8 +30,14 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
SYNC_SOCK = "/run/chv/syncd/syncd.sock"
|
# systemd RuntimeDirectory(见 build.sh 的 syncd 单元):服务启动
|
||||||
STATUS_PATH = "/run/chv/syncd/sync-status.json"
|
# 时由 systemd 按服务用户(chv)建 /run/chv-syncd,启动时序
|
||||||
|
# 不依赖 cloud-init 先建好 chv 用户。不要放回 /run/chv/ 下——
|
||||||
|
# tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,带 chv 属主
|
||||||
|
# 的 tmpfiles 行会被跳过(2026-11-07 真机实测:目录根本没建,
|
||||||
|
# syncd 反复退出、长期 activating)。
|
||||||
|
SYNC_SOCK = "/run/chv-syncd/syncd.sock"
|
||||||
|
STATUS_PATH = "/run/chv-syncd/sync-status.json"
|
||||||
WORKSPACE = "/home/chv/workspace"
|
WORKSPACE = "/home/chv/workspace"
|
||||||
SYNC_BIN = "/usr/local/bin/chv-sync"
|
SYNC_BIN = "/usr/local/bin/chv-sync"
|
||||||
DEFAULT_INTERVAL = 300.0
|
DEFAULT_INTERVAL = 300.0
|
||||||
@@ -116,8 +122,15 @@ class Syncd:
|
|||||||
"consecutive_errors": 0, "cycles": 0}
|
"consecutive_errors": 0, "cycles": 0}
|
||||||
|
|
||||||
def _write_status(self):
|
def _write_status(self):
|
||||||
|
try:
|
||||||
_atomic_write(self.status_path,
|
_atomic_write(self.status_path,
|
||||||
(json.dumps(self._status) + "\n").encode())
|
(json.dumps(self._status) + "\n").encode())
|
||||||
|
except OSError as exc:
|
||||||
|
# 状态文件是可观测性,不是同步本身:写失败记一
|
||||||
|
# 行日志、不杀进程(2026-11-07 真机教训:写失败
|
||||||
|
# 抛到主线程致 syncd 反复退出、长期 activating)。
|
||||||
|
print("chv-syncd: status write failed: %s" % exc,
|
||||||
|
file=sys.stderr, flush=True)
|
||||||
|
|
||||||
def run_cycle(self):
|
def run_cycle(self):
|
||||||
"""一轮同步:先 pull 再 push;任一步失败即停住记 error。"""
|
"""一轮同步:先 pull 再 push;任一步失败即停住记 error。"""
|
||||||
|
|||||||
@@ -290,19 +290,24 @@ class SyncdTest(unittest.TestCase):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
def test_syncd_paths_under_chv_owned_subdir(self):
|
def test_syncd_paths_under_chv_owned_subdir(self):
|
||||||
# 2026-11-07 真机教训:/run/chv 是 0755 root:root,
|
# 2026-11-07 真机教训两则:① /run/chv 是 0755
|
||||||
# chv 身份的 syncd 写不进状态文件与 socket,反复退出、
|
# root:root,chv 身份的 syncd 写不进;② tmpfiles
|
||||||
# systemctl 长期 activating。socket 与状态文件须在
|
# 行 d /run/chv/syncd 0755 chv chv 也没用——
|
||||||
# chv 所有的 /run/chv/syncd/ 子目录里。
|
# tmpfiles-setup 跑在 cloud-init 建 chv 用户之前,
|
||||||
|
# 属主解析失败整行被跳过、目录根本没建,syncd 反复
|
||||||
|
# 退出、长期 activating。正解:systemd
|
||||||
|
# RuntimeDirectory(服务启动时由 systemd 按服务用户
|
||||||
|
# 建 /run/chv-syncd),状态写失败也不杀进程。
|
||||||
self.assertTrue(self.mod.SYNC_SOCK.startswith(
|
self.assertTrue(self.mod.SYNC_SOCK.startswith(
|
||||||
"/run/chv/syncd/"))
|
"/run/chv-syncd/"))
|
||||||
self.assertTrue(self.mod.STATUS_PATH.startswith(
|
self.assertTrue(self.mod.STATUS_PATH.startswith(
|
||||||
"/run/chv/syncd/"))
|
"/run/chv-syncd/"))
|
||||||
daemon = _load_guest_script("chv-daemon.py")
|
daemon = _load_guest_script("chv-daemon.py")
|
||||||
self.assertEqual(daemon.SYNC_STATUS, self.mod.STATUS_PATH)
|
self.assertEqual(daemon.SYNC_STATUS, self.mod.STATUS_PATH)
|
||||||
with open(_repo("chv-guest", "build.sh")) as fh:
|
with open(_repo("chv-guest", "build.sh")) as fh:
|
||||||
text = fh.read()
|
text = fh.read()
|
||||||
self.assertIn("d /run/chv/syncd 0755 chv chv -", text)
|
self.assertIn("RuntimeDirectory=chv-syncd", text)
|
||||||
|
self.assertNotIn("d /run/chv/syncd ", text)
|
||||||
|
|
||||||
def test_resolve_repo_zero_one_many(self):
|
def test_resolve_repo_zero_one_many(self):
|
||||||
repo, reason = self.mod.resolve_repo(self.mod.WORKSPACE)
|
repo, reason = self.mod.resolve_repo(self.mod.WORKSPACE)
|
||||||
|
|||||||
Reference in new issue
Block a user