Files
pangolin/docs/ci-runner-domain-registration-plan.html
T
wangjia d094a53d7e docs(ci): W3/W4 落地记录——git 局域网直达 24ms + 华为防私接 DHCP 偏差
W3/W4 标记完成。W4 按计划(DSM 443 反代改 HTTPS+导入 ali LE 证书)。
W3 改方案:原定 NAS 接管 DHCP 被华为 Q2S 防私接 DHCP 挡死→放弃,改成
NAS 装 Synology DNS Server(git/nas/win 单主机主区+转发其余)+仅 runner mac
家里 Wi-Fi 手动 DNS=.200。§8 记完整过程/偏差/坑(mDNSResponder 负缓存需 flush)。
真相源 baize domains.yaml 家内分光 note;NAS 運維归 ~/code/nas。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEVUXAbFT6bF1Qw27RHWoD
2026-09-07 22:19:19 +08:00

286 lines
29 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CI Runner 走域名注册 + 局域网/外网无感切换 实现方案</title>
<style>
:root{
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
--border:#272c36; --mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
--sans:-apple-system,"PingFang SC","Helvetica Neue",Arial,sans-serif;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.7;font-size:15px}
.wrap{max-width:960px;margin:0 auto;padding:48px 24px 96px}
h1{font-size:29px;line-height:1.3;margin:0 0 8px;letter-spacing:-.01em}
.sub{color:var(--fg2);font-size:15px;margin:0 0 32px}
h2{font-size:21px;margin:52px 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
h3{font-size:16px;margin:28px 0 8px;color:var(--accent2)}
p{margin:10px 0}
code{font-family:var(--mono);font-size:.88em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
pre{background:#0a0c11;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow-x:auto;font-family:var(--mono);font-size:13px;line-height:1.55;color:#cdd3df}
pre .c{color:#6b7385}
pre .r{color:var(--bad)}
pre .g{color:var(--ok)}
pre .y{color:var(--warn)}
.tag{display:inline-block;font-size:12px;font-weight:600;padding:2px 9px;border-radius:999px;vertical-align:middle}
.tag.bad{background:rgba(224,106,106,.16);color:var(--bad)}
.tag.ok{background:rgba(94,194,122,.16);color:var(--ok)}
.tag.warn{background:rgba(224,184,79,.16);color:var(--warn)}
.tag.you{background:rgba(95,176,201,.16);color:var(--accent2)}
.tag.me{background:rgba(224,136,79,.16);color:var(--accent)}
.card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:16px 0}
.card.root{border-left:3px solid var(--accent)}
.card.warn{border-left:3px solid var(--warn)}
.card.ok{border-left:3px solid var(--ok)}
.card.bad{border-left:3px solid var(--bad)}
.card h3{margin-top:0}
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px}
th,td{text-align:left;padding:9px 12px;border-bottom:1px solid var(--border);vertical-align:top}
th{color:var(--fg2);font-weight:600;font-size:13px}
td code{font-size:.85em}
.ok-c{color:var(--ok)} .bad-c{color:var(--bad)} .warn-c{color:var(--warn)}
ul,ol{padding-left:22px;margin:10px 0}
li{margin:6px 0}
.lead{background:linear-gradient(180deg,rgba(224,136,79,.10),transparent);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin:0 0 8px}
.kbd{font-family:var(--mono);font-size:.85em;color:var(--accent)}
.small{color:var(--fg2);font-size:13px}
hr{border:none;border-top:1px solid var(--border);margin:40px 0}
a{color:var(--accent2)}
.diagram{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:20px;margin:18px 0;overflow-x:auto}
ol.steps{counter-reset:s;list-style:none;padding-left:0}
ol.steps>li{counter-increment:s;position:relative;padding:2px 0 2px 40px;margin:14px 0}
ol.steps>li::before{content:counter(s);position:absolute;left:0;top:0;width:26px;height:26px;border-radius:50%;background:var(--panel2);border:1px solid var(--border);color:var(--accent);font-family:var(--mono);font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center}
</style>
</head>
<body>
<div class="wrap">
<h1>CI Runner 走域名注册 + 局域网/外网无感切换</h1>
<p class="sub">gitea Actions self-hosted runner · 注册地址统一 <code>https://git.51yanmei.com</code> · 复用 pangolin 私有分流(private-split)达成家/外无感 · 2026-09-07</p>
<div class="lead">
<strong>一句话:</strong>daily 代理已从 Shadowrocket 换成 <strong>pangolin(全局 TUN)</strong>——TUN 抓所有二进制、<strong>不再像 Shadowrocket 的按 App SOCKS 那样掐断 Go 二进制</strong>,所以 <code>act_runner</code> 现在能<strong>直接连域名</strong><span class="tag bad">relay 淘汰</span>。runner 一律注册到域名 <code>https://git.51yanmei.com</code><code>git.51yanmei.com</code> 早已在 pangolin 的 <code>PANGOLIN_PRIVATE_SPLIT_DOMAINS</code> 里,靠 <code>dns-system</code>(底层物理网络 DNS)解析 —— <strong>在家</strong>局域网 DNS 覆盖成 <code>192.168.3.200</code> → 命中「LAN 直连」规则直达 NAS;<strong>在外</strong>公网 DNS 返回 ali 锚点 <code>182.92.213.171</code> → 命中「私有域名走隧道」→ 经 pangolin1 出口 → ali frp → NAS。同一个域名、同一份配置,家/外自动切换。
</div>
<div class="card ok">
<h3>✅ 已就绪(本次实测确认,无需改动)</h3>
<ul>
<li><b>pangolin 是当前活跃代理</b><code>com.pangolin.pangolin.PacketTunnel</code> 系统扩展 + <code>pangolin_vpn.app</code> 在跑;无 Shadowrocket。</li>
<li><b>域名已可直连</b>:从 runner mac 经 pangolin TUN<code>GET https://git.51yanmei.com/</code><code>/api/v1/version</code><span class="ok-c">HTTP 200</span>(解析 <code>182.92.213.171</code>~1.8s,走隧道→pangolin1→ali→frp→NAS)。<strong>relay 已不再必要。</strong></li>
<li><b>pangolin 配置已含 git</b>pangolin1 <code>/etc/pangolin/server.env</code> 第 31 行 <code>PANGOLIN_PRIVATE_SPLIT_DOMAINS=nas,git,win,brain.51yanmei.com</code><span class="tag ok">零改动</span></li>
<li><b>ali 侧外网入口已就绪</b><code>/etc/nginx/conf.d/git.conf</code> = <code>git.51yanmei.com</code> 443(LE 证书) → <code>proxy_pass 127.0.0.1:3000</code>(frp→NAS)<strong>无 IP 白名单</strong>(不像 brain 限 <code>103.119.13.48</code>),任意来源可达。</li>
</ul>
</div>
<div class="card bad">
<h3>❌ 差在「局域网直达」这条腿(本方案要补的)</h3>
<ul>
<li><b>无家内分光 DNS</b>:全网(含在家)解析 <code>git.51yanmei.com</code> 都得 <code>182.92.213.171</code>;群晖 <b>DNS Server 套件未安装</b><code>/var/packages/DNSServer</code> 不存在)。baize <code>domains.yaml</code> 里 nas/win/git 的「家内覆盖 192.168.3.200 待做」正是此项。<b>可行性已判定→支持</b>(见 §6)。</li>
<li><b>NAS 443 尚无 git 的 TLS 反代条目</b>NAS 监听 <code>0.0.0.0:443</code>(DSM nginx),需加 <code>git.51yanmei.com</code><code>gitea:3000</code> 的反代 + 证书。局域网直达 <code>https://git.51yanmei.com</code>=192.168.3.200:443)必须它成立。<b>可行性已判定→支持,证书需 DNS-01/同步</b>(见 §6)。</li>
<li><b>runner 侧仍是旧的坏配置</b>mac-pangolin-2(gitea_id=7)标签错(<code>nas</code>)、注册到直连公网域名(Shadowrocket 时代坏)jiu 的 mac-runner(id=3)仓库级、经 relay。均待收口成<strong>一个用户级 mac runner 走域名</strong></li>
</ul>
</div>
<h2>0 · 目标架构(一张图)</h2>
<div class="diagram">
<svg viewBox="0 0 900 430" width="100%" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,PingFang SC,sans-serif">
<defs>
<marker id="ar" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#5fb0c9"/></marker>
<marker id="arg" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#5ec27a"/></marker>
<marker id="ary" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0,0 L7,3 L0,6 Z" fill="#e0b84f"/></marker>
</defs>
<!-- runner mac -->
<rect x="20" y="170" width="150" height="90" rx="10" fill="#1d2129" stroke="#e0884f"/>
<text x="95" y="198" fill="#e6e8ee" font-size="13" font-weight="700" text-anchor="middle">runner mac</text>
<text x="95" y="218" fill="#a8afbd" font-size="11" text-anchor="middle">act_runner</text>
<text x="95" y="234" fill="#a8afbd" font-size="11" text-anchor="middle">pangolin TUN</text>
<text x="95" y="250" fill="#5fb0c9" font-size="10" text-anchor="middle">→ git.51yanmei.com</text>
<!-- DNS decision -->
<rect x="215" y="180" width="140" height="70" rx="10" fill="#171a22" stroke="#5fb0c9"/>
<text x="285" y="205" fill="#e6e8ee" font-size="12" font-weight="700" text-anchor="middle">私有分流判定</text>
<text x="285" y="222" fill="#a8afbd" font-size="10" text-anchor="middle">DNS 劫持 → dns-system</text>
<text x="285" y="237" fill="#a8afbd" font-size="10" text-anchor="middle">(底层物理网络 DNS)</text>
<line x1="170" y1="215" x2="212" y2="215" stroke="#5fb0c9" stroke-width="1.6" marker-end="url(#ar)"/>
<!-- LAN lane -->
<text x="430" y="60" fill="#5ec27a" font-size="12" font-weight="700">在家 · 局域网</text>
<rect x="410" y="75" width="170" height="66" rx="9" fill="#1d2129" stroke="#5ec27a"/>
<text x="495" y="99" fill="#e6e8ee" font-size="11.5" font-weight="700" text-anchor="middle">群晖 DNS Server</text>
<text x="495" y="116" fill="#5ec27a" font-size="10.5" text-anchor="middle">git → 192.168.3.200</text>
<text x="495" y="131" fill="#a8afbd" font-size="10" text-anchor="middle">命中「LAN 直连」规则</text>
<rect x="640" y="75" width="230" height="66" rx="9" fill="#171a22" stroke="#5ec27a"/>
<text x="755" y="99" fill="#e6e8ee" font-size="11.5" font-weight="700" text-anchor="middle">NAS 192.168.3.200</text>
<text x="755" y="116" fill="#a8afbd" font-size="10.5" text-anchor="middle">:443 反代(LE证书) → gitea:3000</text>
<text x="755" y="131" fill="#5ec27a" font-size="10" text-anchor="middle">直达,无隧道 · ~ms</text>
<path d="M300,180 C300,108 405,108 408,108" fill="none" stroke="#5ec27a" stroke-width="1.6" marker-end="url(#arg)"/>
<line x1="580" y1="108" x2="638" y2="108" stroke="#5ec27a" stroke-width="1.6" marker-end="url(#arg)"/>
<!-- WAN lane -->
<text x="430" y="300" fill="#e0b84f" font-size="12" font-weight="700">在外 · 公网</text>
<rect x="410" y="312" width="150" height="62" rx="9" fill="#1d2129" stroke="#e0b84f"/>
<text x="485" y="335" fill="#e6e8ee" font-size="11" font-weight="700" text-anchor="middle">公网 DNS</text>
<text x="485" y="351" fill="#e0b84f" font-size="10.5" text-anchor="middle">git → 182.92.213.171</text>
<text x="485" y="366" fill="#a8afbd" font-size="9.5" text-anchor="middle">命中「私有域名走隧道」</text>
<rect x="600" y="306" width="120" height="74" rx="9" fill="#171a22" stroke="#e0884f"/>
<text x="660" y="330" fill="#e6e8ee" font-size="11" font-weight="700" text-anchor="middle">pangolin1</text>
<text x="660" y="346" fill="#a8afbd" font-size="9.5" text-anchor="middle">隧道出口</text>
<text x="660" y="361" fill="#a8afbd" font-size="9.5" text-anchor="middle">103.119.13.48</text>
<rect x="760" y="306" width="120" height="74" rx="9" fill="#171a22" stroke="#e0b84f"/>
<text x="820" y="330" fill="#e6e8ee" font-size="11" font-weight="700" text-anchor="middle">ali nginx</text>
<text x="820" y="346" fill="#a8afbd" font-size="9.5" text-anchor="middle">:443 → frp</text>
<text x="820" y="361" fill="#5ec27a" font-size="9.5" text-anchor="middle">→ NAS:3000</text>
<path d="M300,250 C300,343 405,343 408,343" fill="none" stroke="#e0b84f" stroke-width="1.6" marker-end="url(#ary)"/>
<line x1="560" y1="343" x2="598" y2="343" stroke="#e0b84f" stroke-width="1.6" marker-end="url(#ary)"/>
<line x1="720" y1="343" x2="758" y2="343" stroke="#e0b84f" stroke-width="1.6" marker-end="url(#ary)"/>
</svg>
</div>
<p class="small">两条腿只有 <b>DNS 解析结果不同</b>驱动分叉:家内被 <code>dns-system</code> 解成私网 IP → 先命中 route 里更靠前的「LAN 直连」;在外解成公网锚点 → 落到「私有域名 → auto(隧道)」。<b>runner 侧完全不感知</b>,永远只认 <code>https://git.51yanmei.com</code></p>
<h2>1 · 为什么无感切换成立(机制,已在代码里且有测试)</h2>
<p>核心在 <code>server/internal/httpapi/clientconfig.go</code><code>PANGOLIN_PRIVATE_SPLIT_DOMAINS</code> 非空时注入三处,<b>顺序即优先级</b></p>
<table>
<tr><th>#</th><th>位置</th><th>作用</th></tr>
<tr><td></td><td>DNS server <code>dns-system</code>type=localline 370-373</td><td>私有域名不走公共 DNS(223.5.5.5/8.8.8.8),改用<strong>底层物理网络 DNS</strong>——在家=路由器/NAS 下发的局域网 DNS(含覆盖),在外=所在网络 DNS(公网记录)。</td></tr>
<tr><td></td><td>DNS rule <code>{domain: 私有域名, server: dns-system}</code>line 383-385</td><td>把私有域名的解析定向到 ①。排在 <code>geosite-cn</code> 之前,优先级最高。</td></tr>
<tr><td></td><td>route rule <code>{domain: 私有域名, outbound: auto}</code>line 239-242+ 前置 LAN 直连 <code>{ip_cidr:[10/8,172.16/12,192.168/16,127/8], outbound:direct}</code>line 230-233</td><td>在家:DNS 解出私网 IP → <strong>LAN 直连规则先命中</strong>(更靠前)→ 直达 NAS<b>不进隧道</b>。在外:解出公网 IP → 落到私有域名规则 → <code>auto</code>(隧道) → pangolin1 出口。<code>dns.reverse_mapping=true</code> 补回域名元数据,使按 IP 发起的连接仍能命中该规则。</td></tr>
</table>
<p class="small">这正是 <a href="private-dest-acl-design.html">私有服务 ACL 设计</a> / <a href="private-dest-acl-plan.html">实现计划</a> 已落地的家庭内网穿透机制(nas/win/brain 同款)。git 只是复用它,<b>无新代码</b></p>
<h2>2 · 分项实现(workstreams</h2>
<table>
<tr><th>#</th><th>工作项</th><th>动谁</th><th>状态</th></tr>
<tr><td>W1</td><td>pangolin 把 git 纳入 private-split</td><td>pangolin1 <code>server.env</code></td><td><span class="tag ok">已完成</span></td></tr>
<tr><td>W2</td><td>ali 外网入口(git nginx 443→frp→NAS,开放)</td><td>ali</td><td><span class="tag ok">已就绪</span></td></tr>
<tr><td>W3</td><td>家内<b>分光 DNS</b>git/nas/win → 192.168.3.200 / .88</td><td>NAS(群晖 DNS Server+ <s>DHCP 下发</s>→仅 runner mac 手动 DNS</td><td><span class="tag ok">已完成 2026-09-07(改法,见 §8</span></td></tr>
<tr><td>W4</td><td>NAS <b>443 TLS 反代</b> git → gitea:3000+证书)</td><td>NASDSM 反向代理 + 导入 ali LE 证书)</td><td><span class="tag ok">已完成 2026-09-07</span></td></tr>
<tr><td>W5</td><td>runner 收口:1 个用户级 mac runner 走<b>域名</b>;退役 id=3;删 relay</td><td>dev maclaunchd+ gitea</td><td><span class="tag ok">已完成 2026-09-07</span></td></tr>
<tr><td>W6</td><td>验证 + 回写 baize 台账</td><td></td><td><span class="tag warn">待做</span></td></tr>
</table>
<div class="card">
<p><b>降级路径(若暂不做 W3/W4):</b>runner <b>现在</b>就能改注册到域名并删 relay(W5 单独可行)——在家会走「隧道→pangolin1→ali→NAS」的<b>回环</b>路径(实测 ~1.8s/请求,能用但慢)。W3+W4 是把在家这条腿从「回环」优化成「局域网直达(~ms)」,即你要的「局域网域名直接解析成局域网地址」。<b>建议:W5 先落地拿掉 relay,W3/W4 作为随后的直连优化。</b></p>
</div>
<h3>W3 · 家内分光 DNS <span class="tag you">你确认/我执行</span></h3>
<p>目标:局域网内解析 <code>git/nas/win.51yanmei.com</code> → 私网 IP;其余照常公网。方案(二选一):</p>
<ul>
<li><b>群晖 DNS Server(推荐,baize 既定方向)</b>DSM 套件中心装 <b>DNS Server</b> → 建 Master Zone <code>51yanmei.com</code>(或用「解析区域 + A 记录覆盖」)→ 加 A:<code>git 192.168.3.200</code><code>nas 192.168.3.200</code><code>win 192.168.3.88</code>;开启 forwarders 把其余查询转公网(223.5.5.5)。<b>DHCP 下发 DNS 指向 NAS</b>——好消息:<b>NAS 本身就是 LAN 的 DHCP 服务器</b><code>DhcpServer</code> 套件已装、引擎 dnsmasq、段 .10.230),在 DSM DHCP 设置里把下发 DNS 改成 <code>192.168.3.200</code> 即可,<b>无需碰路由器</b></li>
<li><b>ContainerManager 容器(备选,已装)</b>:跑 AdGuard Home / dnsmasq 容器做 <code>git→192.168.3.200</code> rewriteDHCP 下发指向它。免装 DSM DNS Server 套件。</li>
</ul>
<p class="small"><b>注意</b>:装 DNS Server / 改 DHCP 下发 = 机器与网络改动,按规矩<strong>先经你确认</strong>再动。<code>dns-system</code>(type=local)吃的是 OS resolver → DHCP 下发的 DNS,所以 DHCP 那一步不可省(NAS 既是 DHCP 服务器,这步在 DSM 内完成)。</p>
<h3>W4 · NAS 443 为 git 供 TLS 反代 <span class="tag you">你确认/我执行</span></h3>
<p>局域网直达 <code>https://git.51yanmei.com</code> 即打到 <code>192.168.3.200:443</code>,须 NAS 在 443 用 <b>git.51yanmei.com 有效证书</b>反代到 <code>gitea:3000</code>。步骤:</p>
<ol class="steps">
<li><b>证实现状</b>(需 sudo,你在 NAS 上跑或授权我):看 DSM「控制面板 → 登录门户 → 高级 → 反向代理」是否已有 <code>git.51yanmei.com</code> 条目;<code>sudo ss -tlnp | grep :443</code> 看谁占 443。</li>
<li><b></b>DSM 反向代理新建 <code>git.51yanmei.com:443</code><code>localhost:3000</code>,勾 HTTP/2、WebSocket<code>client_max_body_size 0</code> 等价项在 DSM 里放开大包(git push)。</li>
<li><b>证书</b>DSM「安全性 → 证书」为 <code>git.51yanmei.com</code> 配 LE 证书。家内无 80 端口暴露→用 <b>DNS-01</b>(阿里云 DNSAK 在 Bitwarden「ali dev」),或从 ali <code>/etc/letsencrypt/live/git.51yanmei.com/</code> 定期同步导入。绑到反代条目。</li>
</ol>
<p class="small">gitea 的 <code>ROOT_URL</code> 已是 <code>https://git.51yanmei.com</code>(外网就这么跑),Host 头一致,反代到 3000 无需改 gitea。</p>
<h3>W5 · runner 收口到域名 + 删 relay <span class="tag me">我执行(经你确认)</span></h3>
<p>act_runner 的标签在<b>首次注册即固定</b>,改配置不重注册无效——所以必须重注册。目标态:<b>一个用户级 mac runner</b></p>
<ol class="steps">
<li><b>取用户级注册 token</b>gitea 网页 头像 → Settings → Actions → Runners → <b>Create new Runner</b>(用户级 reg-token 的 API 端点本版本 404,只能网页取)。<span class="tag you">你提供</span></li>
<li><b>注册</b>新 runner<code>instance = https://git.51yanmei.com</code><b>域名,不是 127.0.0.1:13000,不是 IP</b>),labels <code>mac,self-hosted,macos</code>name <code>mac</code></li>
<li><b>launchd 收敛</b>:新建 <code>com.yanmei.act-runner</code>(用户级常驻,配置目录 <code>~/.act_runner_mac_config.yaml</code>);<b>卸载</b> <code>com.pangolin.act-runner</code>(mac-pangolin-2) 与 <code>com.jiu.act-runner</code></li>
<li><b>删 relay</b><code>com.jiu.act-runner</code> 的 start 脚本里附带拉起 <code>~/bin/tcp_relay.py</code>——runner 走域名后 relay 无用,随该 launchd 一并退休(脚本、plist、13000 监听全清)。</li>
<li><b>gitea 退役</b>旧 runner:删 mac-pangolin-2(id=7) 与 mac-runner(id=3)。jiu 仓的 job 由新用户级 mac runner 接管(标签 <code>mac</code>;如 jiu workflow 用 <code>runs-on: mac</code> 直接兼容,否则补标签)。</li>
</ol>
<div class="card warn">
<h3>⚠️ relay 到底还要不要(结论)</h3>
<p>relay(<code>tcp_relay.py</code> 127.0.0.1:13000 → NAS 192.168.3.200:3000)当初只为绕开 <b>Shadowrocket 拦 act_runner 这个 Go 二进制的直连</b>Python 进程不被 Shadowrocket 拦)。现在 daily 代理是 pangolin 全局 TUN<b>TUN 不做按-App 拦截、对 Go 二进制透明</b>,实测域名直连 200 通。<strong>relay 已淘汰,随 W5 删除。</strong></p>
</div>
<div class="card ok">
<h3>✅ W5 执行记录(2026-09-07</h3>
<p>比预想更简单——<b>无需重注册、无需注册 token</b>mac-pangolin-2(id=7)<b>本就是用户级、start 脚本本就指向域名</b>,唯一 bug 是 label 错成 <code>nas</code>。根因是当年 Shadowrocket 掐断连接 → act_runner 的 <code>Declare(labels)</code> 从没上报成功。换 pangolin 后连通,做法:</p>
<ol>
<li><code>~/.act_runner_pangolin2_config.yaml</code><code>labels: nas:host → mac:host</code></li>
<li><code>launchctl kickstart -k gui/501/com.pangolin.act-runner</code> 重启 → 日志 <code>declare successfully, labels:[mac]</code>,并<b>立即接走 pangolin 队列 task 2299</b></li>
<li>退役 jiu<code>launchctl bootout</code> <code>com.jiu.act-runner</code>+watchdog(连带杀 relay)→ plist 改名 <code>.disabled</code> → repo API 删 id=3<code>DELETE …/repos/wangjia/jiu/actions/runners/3</code> = 204)。</li>
<li>删 relay<code>~/bin/tcp_relay.py</code> / jiu start 脚本 / jiu config 均 <code>.retired</code><code>:13000</code> 已释放。</li>
</ol>
<p class="small">现状:单一用户级 mac runnerid=7<code>com.pangolin.act-runner</code>label <code>mac</code>,域名直连、无 relay),服务全部 wangjia 仓。回写 baize <code>ci_runners</code>(commit 4b7ffe1)。<b>遗留(非阻塞)</b>label 仍单 <code>mac</code>(可后加 self-hosted/macos);launchd 名仍带 pangolin(拟后续中性化 <code>com.yanmei.act-runner</code>)。在家目前走隧道回环(~1.8s),W3/W4 后转局域网直达。</p>
</div>
<h2>3 · 迁移顺序(依赖)</h2>
<ol class="steps">
<li><b>W5 先行</b>(可独立):runner 改注册到域名 + 删 relay。此刻在家走回环(能跑绿),先把「多头 + relay + 坏标签」这堆乱账清掉。</li>
<li><b>W4</b>:NAS 443 反代 + 证书(先证实、再补建)。</li>
<li><b>W3</b>:家内分光 DNS + DHCP 指向 NAS。W3 生效后,家内解析即翻私网 IP,runner/所有设备访问 git/nas/win 自动走局域网直达。</li>
<li><b>W6</b>:验证(见下)+ 回写 baize(DNS 覆盖落地、relay 退役、runner 台账更新)。</li>
</ol>
<p class="small">W3 与 W4 是一对:只做 W3 不做 W4,家内会打到 192.168.3.200:443 但无有效 TLS → 失败;两者同批上。</p>
<h2>4 · 验证清单(W6</h2>
<pre><span class="c"># 在家(局域网)——应直达私网、无隧道回环</span>
dig +short git.51yanmei.com <span class="c"># 期望 192.168.3.200</span>
curl -sS -o /dev/null -w "%{remote_ip} %{time_total}\n" https://git.51yanmei.com/api/v1/version
<span class="c"># 期望 ip=192.168.3.200time≈ms 级</span>
<span class="c"># 在外(手机热点/换网)——应回落公网锚点、经隧道</span>
dig +short git.51yanmei.com <span class="c"># 期望 182.92.213.171</span>
curl ... /api/v1/version <span class="c"># 200ip=182.92.213.171</span>
<span class="c"># runner 状态</span>
launchctl list | grep act-runner <span class="c"># 只剩 com.yanmei.act-runner</span>
lsof -i :13000 <span class="c"># 空(relay 已死)</span>
</pre>
<p>gitea 网页 Runners:只余 <code>windows</code>(id=8) / <code>mac</code>(新,用户级) / <code>nas</code>(id=2,实例级不动)。触发一次 pangolin <code>build-macos</code> 确认被 <code>mac</code> runner 接走、跑绿。</p>
<h2>6 · NAS 可行性判定(read-only 实测 2026-09-07</h2>
<p>「涉及 NAS 先判可行性,别做一半发现一般 nas 不支持」——判定结论:<b>这台 DS925+ 不属于「不支持」的那类</b>。它是 Plus x86 机(<code>x86_64</code> / DSM <code>7.3.2</code>),关键能力齐备。</p>
<table>
<tr><th>能力</th><th>现状</th><th>判定</th></tr>
<tr><td>NAS 是否 LAN DHCP 服务器</td><td><code>DhcpServer</code> 套件<b>已装</b>,引擎正是 <b>dnsmasq</b><code>dnsmasq-2.x-virtual-dhcpserver</code>),租约段 <code>192.168.3.10230</code></td><td class="ok-c">✅ DNS 下发掌握在自己手里,无需改路由器</td></tr>
<tr><td>DNS Server(分光 A 记录)</td><td>套件未装,但 DS925+/DSM7.3 catalog 支持;另 <code>ContainerManager</code> <b>已装</b>可跑 AdGuard/dnsmasq 容器</td><td class="ok-c">✅ 两条路径(官方套件 / 容器)任选</td></tr>
<tr><td>443 TLS 反代</td><td>443 由 DSM nginx 持有;<b>DSM Reverse Proxy 已在用</b><code>/usr/syno/etc/www/ReverseProxy.json</code> 有条目);gitea 为 3000 上 native 进程(非容器)</td><td class="ok-c">✅ 加一条 SNI vhost git→localhost:3000 即可</td></tr>
<tr><td>证书签发</td><td>DSM 内置 LE 仅 HTTP-01,家里 git 的 :80 不对公网→<b>一键签不了</b>acme.sh 未装</td><td class="warn-c">⚠ 唯一非默认项:走 <b>acme.sh DNS-01</b>(阿里云 DNSAK 在 Bitwarden「ali dev」) 或<b>同步 ali 已有 LE 证书</b>(cron scp+DSM 导入,最省)</td></tr>
</table>
<div class="card ok">
<p><b>判定:W3 + W4 均可行。</b>唯一 caveat 是证书需 DNS-01 或同步(非 DSM 一键 LE),但两条路都成熟、不阻塞。DHCP 已由 NAS(dnsmasq) 承担、ContainerManager 与 Reverse Proxy 都在——不存在「一般 nas 不支持」的情形。<br><span class="small">未证实项(需 sudo 才能读,不阻塞判定):ReverseProxy.json 具体条目、certificate 目录内容、DHCP 当前下发的 DNS 指向谁。落地时一并处理。</span></p>
</div>
<h2>7 · 需要你拍板 / 提供的(机器改动,先确认再动)</h2>
<table>
<tr><th></th><th>属于</th><th>需要你</th></tr>
<tr><td>用户级 runner 注册 token(网页 Create new Runner 取)</td><td>W5</td><td>提供 token</td></tr>
<tr><td>装群晖 DNS Server + 改路由器 DHCP DNS</td><td>W3</td><td>确认动手(装套件+改网络)</td></tr>
<tr><td>NAS DSM 反向代理 + 证书(DNS-01 或同步 ali 证书)</td><td>W4</td><td>确认动手 / 或授权 sudo 让我先证实现状</td></tr>
<tr><td>卸载 <code>com.jiu.act-runner</code> / <code>com.pangolin.act-runner</code>、删 relay</td><td>W5</td><td>确认(会短暂影响 jiu CI,直到新 runner 接管)</td></tr>
</table>
<p class="small">相关台账:<code>~/code/baize/data/hosts.yaml</code> <code>ci_runners</code> 段(relay_note、mac runner state+ <code>domains.yaml</code>nas/win/git「家内覆盖 待做」)。W3/W5 落地后回写。</p>
<h2>8 · W3/W4 执行记录(2026-09-07,实际落地与偏差)</h2>
<div class="card ok">
<h3>✅ 结果:git 在家从隧道回环 ~1.8s → <b>局域网直达 24ms</b></h3>
<p><code>curl https://git.51yanmei.com/api/v1/version</code> 从 runner mac 经 pangolin<code>http=200 ip=192.168.3.200 time=0.025s</code>,证书受信。runner 重启后 <code>declare successfully</code>、无 <code>no such host</code></p>
</div>
<h3>W4NAS 443 反代)— 按计划</h3>
<ul>
<li>DSM 反向代理原有 <code>git.51yanmei.com</code> 条目从 <b>HTTP:80 改 HTTPS:443</b>,目的地仍 <code>localhost:3000</code></li>
<li>证书:把 ali 上 <code>/etc/letsencrypt/live/git.51yanmei.com/</code> 的 LE 证书(cert/privkey/chain)导入 DSM,绑到该反代服务。(我这边拉私钥被安全闸拦,改由用户在自己终端拉取 + DSM GUI 导入。)</li>
</ul>
<h3>W3(家内分光 DNS)— <span class="tag warn">改了方案</span></h3>
<p>原计划「NAS 接管 DHCP、下发 .200 当 DNS」<b>失败</b>——根因:家里网关是 <b>华为 Q2S 路由器,带防私接 DHCP</b>,把 NAS(dnsmasq,绑 :67 正常)发出的 DHCP 应答全丢,客户端永远拿不到 NAS 租约(华为 DHCP 关掉后 mac 也 DISCOVER 不到 NAS→一度 No-IP,靠重开华为 DHCP 兜底恢复)。NAS 不是路由器、改不了这个。</p>
<p>改成的可行方案:</p>
<ol class="steps">
<li>NAS 装 <b>Synology DNS Server</b><code>synopkg install_from_server DNSServer</code>):给 <code>git/nas/win.51yanmei.com</code> 各建<b>单主机主区</b>(只对这三名字权威→私网 IP+ <code>named.options.user.conf</code><code>forwarders{223.5.5.5;119.29.29.29}</code> 转发其余。故 <code>pay/jiu/api</code> 等兄弟子域照常公网。</li>
<li><b>只给 runner mac 的家里 Wi-Fi 手动指 DNS=<code>192.168.3.200</code></b>(macOS 按网络记,出门用别的网 DNS、不受影响)。别的家里设备不动(它们不需要 git 覆盖)。NAS 的 DhcpServer 已停,华为继续当 DHCP。</li>
<li>⚠ 改 DNS 后 mDNSResponder 负缓存旧失败(<code>dig</code> 通但 <code>curl</code> no-such-host)→ <code>sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder</code>runner 再 <code>launchctl kickstart -k</code> 重启。</li>
</ol>
<p class="small"><b>已知/待办</b>:① 只有 runner mac 走直达(本就够用,它才是 CI 需要的),非全网分光。② DNS=.200 仅设在家里 Wi-Fi;若 mac 换网上不了网就删这条。③ NAS DhcpServer 已 <code>stop</code>,如需永久别开机自启可在 DSM「DHCP Server」取消勾选。④ 台账真相源:baize <code>domains.yaml</code>『家内分光』note;NAS 详细運維归 <code>~/code/nas</code></p>
<hr>
<p class="small">关联:<a href="private-dest-acl-design.html">私有服务 ACL 设计</a> · <a href="private-dest-acl-plan.html">私有服务 ACL 实现计划</a> · <a href="control-plane-tls-tunnel.html">控制面 TLSTunnel 前置)</a> · <a href="index.html">← 文档索引</a></p>
</div>
</body>
</html>