chv-cell: networkd 不管 ve + 单元内 DNS 修复

- systemd-networkd(/usr/lib 80-container-ve.network,匹配 ve-*)
  会在 veth 建好后几秒内刷掉静态 10.200.0.1、改 DHCP/链路本地,
  致单元网关不可达、TCP/UDP 全灭(FORWARD/NAT 计数器恒 0;
  早先一次 curl 403 是刷掉前的窗口期偶发)。修法:镜像内置
  /etc/systemd/network/79-chv-cell.network(Unmanaged=yes),
  须排在 80 之前(99 会被 80 抢走,已实测);build.sh copy-in
  + 校验、BASE_CONTENTS 与两处 DECLARED_SOURCES 登记。
- run_cell 配网后复核 ve 上确有 10.200.0.1(_iface_has_ip),
  不见即大声失败、不跑无网单元(第二道防线)。
- 单元内 DNS:/etc/resolv.conf 是 127.0.0.53 stub,单元独立
  netns 里自己的 lo 上没有 resolver;把上游
  /run/systemd/resolve/resolv.conf 绑到 /etc/resolv.conf
  (解析 symlink 后挂真实目标)。live 验证:DNS-UDP-OK、
  getaddrinfo 解析成功、curl https://gitea.tose.top 200。
- 单测:CellResolvTest + _iface_has_ip 真实 lo 断言 + 规约
  dns_resolv_conf 字段;P2 + 阶段 2 共 74 项全过。
This commit is contained in:
host committed 2026-11-08 09:45:00 +00:00
1 parent 4b5f2f124f
commit 1cbaf7c1d5
7 files changed
+48

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
# NOTE: must sort before /usr/lib/systemd/network/80-container-ve.network (systemd built-in container-veth config, matches ve-*), otherwise that file wins and networkd DHCPs the interface.
# chv-cell veth: managed entirely by chv-cell (static 10.200.0.1/24 + NAT).
# systemd-networkd must not touch it: it would DHCP/link-local the
# interface and flush the static address, breaking all cell networking
# (2026-11-07 live diagnosis: 10.200.0.1 vanished within seconds of
# the veth being created, cell gateway unreachable, FORWARD/NAT
# counters stayed 0 for both TCP and UDP).
[Match]
Name=ve-chv-cell
[Link]
Unmanaged=yes
+2
View File
@@ -306,6 +306,8 @@ VC_ARGS=(-a "$OUT/disk.raw"
--copy-in "$SCRIPT_DIR/chv-execd.py:/usr/local/bin/"
--copy-in "$SCRIPT_DIR/chv-syncd.py:/usr/local/bin/"
--run-command 'mv /usr/local/bin/chv-daemon.py /usr/local/bin/chv-daemon && mv /usr/local/bin/chv-execd.py /usr/local/bin/chv-execd && mv /usr/local/bin/chv-syncd.py /usr/local/bin/chv-syncd && chmod +x /usr/local/bin/chv-daemon /usr/local/bin/chv-execd /usr/local/bin/chv-syncd'
--copy-in "$SCRIPT_DIR/79-chv-cell.network:/etc/systemd/network/"
--run-command 'test -f /etc/systemd/network/79-chv-cell.network && grep -q "^Unmanaged=yes" /etc/systemd/network/79-chv-cell.network && grep -q "^Name=ve-chv-cell" /etc/systemd/network/79-chv-cell.network'
--run-command 'printf "runcmd:\n - systemctl restart ssh || service ssh restart || true\n - test -f /etc/chv/guest-env && chown chv:chv /etc/chv/guest-env || true\n - /usr/local/bin/chv-data-mount >>/var/log/chv-data-mount.log 2>&1 || true\n - /usr/local/bin/chv-sync boot >>/var/log/chv-sync.log 2>&1 || true\n" > /etc/cloud/cloud.cfg.d/99-chv-sync.cfg'
--run-command 'git config --system user.name chv && git config --system user.email chv@localhost && git config --system credential.helper store && git config --system init.defaultBranch main'
--run-command "$GT_INSTALL_CMD"
+3
View File
@@ -43,6 +43,9 @@ BASE_CONTENTS = [
"chv-data-mount",
# feat-chv-opt-landing(阶段 2):客机运行时三分服务。
"chv-daemon", "chv-execd", "chv-syncd",
# feat-chv-opt-landing(阶段 2):cell veth 的 networkd
# 不管配置(须排在 80-container-ve.network 之前)。
"79-chv-cell.network",
]
# 执行器默认内核 cmdline(config.py),构建产物须与它一起记录(B-12),
+3
View File
@@ -38,6 +38,9 @@ DECLARED_SOURCES = {
"chv-daemon": "repo_commit",
"chv-execd": "repo_commit",
"chv-syncd": "repo_commit",
# feat-chv-opt-landing(阶段 2):cell veth 的 networkd
# 不管配置,版本随 repo_commit。
"79-chv-cell.network": "repo_commit",
}
+17
View File
@@ -543,6 +543,19 @@ def _wait_for_iface(iface, timeout=10.0):
return False
def _iface_has_ip(iface, ip):
"""接口上是否有所给 IPv4。配网后复核:2026-11-07 实测
networkd(80-container-ve.network)会在接口建好后几秒内
刷掉静态 10.200.0.1、改 DHCP/链路本地,致单元网关不可达;
镜像里有 79-chv-cell.network(Unmanaged)兜底,这里是
第二道防线——地址不见即大声失败,不跑无网单元。"""
proc = subprocess.run(["ip", "addr", "show", iface],
capture_output=True, text=True)
if proc.returncode != 0:
return False
return ("inet %s/" % ip) in proc.stdout
def _run_cmds(cmds, what):
for cmd in cmds:
print("chv-cell: + %s" % " ".join(cmd), file=sys.stderr)
@@ -806,6 +819,10 @@ def run_cell(command, network="veth"):
"chv-cell: veth 接口 %s 未出现,拒绝回落共享网络"
% HOST_IFACE)
_run_cmds(cell_network_commands(cell_pid), "单元内配网")
if not _iface_has_ip(HOST_IFACE, HOST_IP):
raise RuntimeError(
"chv-cell: veth %s 上 %s 不见了(疑似 networkd "
"刷掉),拒绝跑无网单元" % (HOST_IFACE, HOST_IP))
# 放行单元 init 去 exec payload。
try:
os.write(go_w, b"1")
+3
View File
@@ -59,6 +59,9 @@ DECLARED_SOURCES = {
"chv-daemon": "repo_commit",
"chv-execd": "repo_commit",
"chv-syncd": "repo_commit",
# feat-chv-opt-landing(阶段 2):cell veth 的 networkd
# 不管配置,版本随 repo_commit。
"79-chv-cell.network": "repo_commit",
}
+8
View File
@@ -322,6 +322,14 @@ class CellNetworkTest(unittest.TestCase):
self.assertEqual(CELL.find_uplink_iface(run=fake_run), "tap0")
def test_iface_has_ip_uses_real_loopback(self):
# 真实接口断言(lo 一定有 127.0.0.1):校验 _iface_has_ip
# 的解析逻辑;不存在的接口/地址返回 False。
self.assertTrue(CELL._iface_has_ip("lo", "127.0.0.1"))
self.assertFalse(CELL._iface_has_ip("lo", "10.200.0.1"))
self.assertFalse(CELL._iface_has_ip("no-such-iface-xyz",
"127.0.0.1"))
class BrokerTest(unittest.TestCase):
def _fake_proc(self, root, mapping):