方法论教程 · 一人公司部署上线 SOP 本文是公司级规章。 把 mirror/ 里的 markdown 变成全网可访问的网站,走完这份 SOP 就行。 版本:v1.0(2026-04-20 沉淀)· 路径:本地 → Vercel → 自定义域名 一、通路总览 mirror/*.md → build.py → site/*.html → Vercel → suhangcompany.site 源材料 静态构建 生成产物 托管 自定义域名 7 步完成一次完整上线: 步 动作 归属部门 产出 1 本地构建 开发部 · 全栈工程师 site/ 目录 230+ HTML 2 本地预览验收 质量部 确认无坏链、无污染内容 3 域名购买 财务部 + 法务部 .site / .cn / .com 域名 4 Vercel 注册与 Token 开发部 账号 + Personal Token 5 首次部署 开发部 *.vercel.app 临时访问地址 6 绑定自定义域名 开发部 apex + www 都在 Vercel 项目里 7 DNS 配置 开发部 A/CNAME 记录,SSL 自动签发 二、前置资源清单 本机安装: - Node.js ≥ 20(用 npx vercel 无需全局装) - Python 3.10+(build.py 用) - curl / dig(诊断用,macOS 默认有) 账号: - Vercel 账号(免费额度够个人用) - 域名注册商账号(阿里云 / 腾讯云 / 火山引擎 / Namesilo 等) 本地文件: 苏苏一人公司/ ├── mirror/ # 源 markdown(公司章程、部门 SOP、项目文档) ├── build.py # 构建脚本:mirror → site ├── style.css # 站点样式 ├── serve.py # 本地预览服务器 ├── site/ # 构建产物(git 忽略) │ └── vercel.json # Vercel 配置(cleanUrls) └── 方法论教程_部署上线SOP.md # 本文 三、阶段 1-2 · 本地构建与验收 3.1 构建 cd ~/Documents/苏苏一人公司 python3 build.py 产出: site/ 目录内 230 个 HTML + 1 个 index.html + 17 张图 + style.css build.py 做了什么: 1. 读 mirror/_sidebar.md 生成导航 2. markdown → HTML(苹果文档风) 3. .md 链接改写为 .html 4. 面包屑生成 5. URL 里的 + 空格 中文 都 percent-encode(避免 Vercel 把 + 当空格) 6. 拷贝所有 *.png/jpg/gif/svg 到同路径 7. 生成首页 index.html(深色 Linear 风 + 12 部门组织架构图) 3.2 本地预览 python3 serve.py # 默认 http://localhost:8766 验收清单(质量部 checklist): - [ ] 首页两圆旋转,12 部门卡片环绕 CEO"苏航" - [ ] 侧边栏部门可折叠展开 - [ ] 随机点 5 个内页,全部能打开、无 404 - [ ] 代码块、表格、blockquote 样式正常 - [ ] 面包屑层级正确 四、阶段 3 · 域名准备 4.1 选注册商 注册商 优点 注意 火山引擎 国内访问快、支付方便 DNS 解析一体化 阿里云 生态成熟 实名认证严格 腾讯云 同阿里 — Namesilo / Cloudflare 价格低、隐私好 国内访问可能受限 4.2 域名选择原则 .site / .online 便宜(首年 $1-3) .com / .cn 长期首选 个人主控台名字:<姓名拼音>company / <姓名拼音>.me 本次实例: suhangcompany.site(火山引擎购入) 五、阶段 4 · Vercel 账号与 Token 5.1 注册 5.2 创建 Token 右上头像 → Account Settings 左栏 → Tokens Create Token - Name:deploy-<机器名> 或 deploy-claude - Scope:选个人 team(通常是 <你的用户名>'s projects) - Expiration:1 day / 7 days / no expiration 复制并保存(vcp_xxxxx 开头,关闭页面就看不到了) 安全约束: - Token 等于账号全权限,不要提交到 git - 用 export VERCEL_TOKEN=xxx 放环境变量 - 或 ~/.zshrc:export VERCEL_TOKEN=...(私人机) 六、阶段 5 · 首次部署 6.1 准备 vercel.json { "cleanUrls": true, "trailingSlash": false } 放在 site/ 目录根下。cleanUrls 让 /xxx/README.html 可以用 /xxx/README 访问。 6.2 部署命令 cd site/ export VERCEL_TOKEN="vcp_你的token" npx -y vercel@latest deploy --prod --yes \ --token="$VERCEL_TOKEN" \ --scope="<你的 team slug>" \ --archive=tgz 参数说明: - --prod:直接发布到生产(不先出预览) - --yes:跳过所有交互确认(非交互环境必须) - --archive=tgz:把整个 site/ 打包上传(避开中文文件名单文件上传时的编码问题) 成功标志: 输出末尾有 Production: https://site-xxxxxxxx-<team>.vercel.app [8s] 这个 URL 就是临时访问地址,已经可以打开站点。 6.3 查项目/部署状态 # 列 team curl -H "Authorization: Bearer $VERCEL_TOKEN" https://api.vercel.com/v2/teams # 列项目 curl -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v9/projects?teamId=<teamId>" # 列该项目的部署 curl -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v6/deployments?projectId=<projectId>&teamId=<teamId>" 七、阶段 6 · 绑定自定义域名 # 绑根域 npx -y vercel@latest domains add suhangcompany.site <project-name> \ --token="$VERCEL_TOKEN" --scope="<team-slug>" # 绑 www 子域(重要,独立证书) npx -y vercel@latest domains add www.suhangcompany.site <project-name> \ --token="$VERCEL_TOKEN" --scope="<team-slug>" 易漏: Vercel 给 apex 签的证书不覆盖 www 子域,必须单独 add。否则用户访问 www.xxx.com 会看到 NET::ERR_CERT_COMMON_NAME_INVALID。 验证: curl -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v10/projects/<project>/domains/<domain>?teamId=<teamId>" # 返回里看 "verified": true 八、阶段 7 · DNS 配置(决定能否被访问) 8.1 获取 Vercel 推荐 IP curl -H "Authorization: Bearer $VERCEL_TOKEN" \ "https://api.vercel.com/v6/domains/<domain>/config?teamId=<teamId>" 返回里看 recommendedIPv4: { "rank": 1, "value": ["216.198.79.1", "64.29.17.1"] } Rank 1 是新 Anycast,Rank 2 是 legacy 76.76.21.21。 国内环境 两套都要试,哪套通用哪套。 8.2 方案 A · A 记录(推荐国内) 在 DNS 控制台加: 类型 主机 值 TTL A @ 216.198.79.1 10 分钟 A @ 64.29.17.1(同一条记录加第二个值,不是新建第二条) 10 分钟 CNAME www cname.vercel-dns.com. 10 分钟 ⚠️ 火山引擎坑: 不能创建两条 @ A 记录(报"记录已存在")。必须点已有那条的 "管理" → 滑到底部 "+ 添加记录值" → 在同一条记录里加第二个 IP。 多 IP 的意义: DNS 返回两个 IP,浏览器依次尝试;一个被屏蔽/路由差时另一个兜底。 8.3 方案 B · Nameserver 托管 域名注册商处把 NS 改成: - ns1.vercel-dns.com - ns2.vercel-dns.com 适用场景: 你只用这一个 Vercel 项目,不想管 DNS 细节。 不适用: 同一个域名还要做邮箱、其他子域指向别处。 8.4 SSL 证书 DNS 生效后,Vercel 自动签发 Let's Encrypt(1-5 分钟)。不用你动。 8.5 验证 # 公共 DNS 查真实解析(绕过本机 DNS 污染) dig @223.5.5.5 +short <domain> A dig @119.29.29.29 +short <domain> A # 访问 curl -sI https://<domain>/ # 期待:HTTP/2 200 九、日常更新工作流 每次改完文档: cd ~/Documents/苏苏一人公司 # 1. 改 mirror/ 里的 markdown # 2. 重新构建 python3 build.py # 3.(可选)本地预览验收 python3 serve.py # 打开 http://localhost:8766 # 4. 推到 Vercel cd site/ export VERCEL_TOKEN="vcp_xxx" npx -y vercel@latest deploy --prod --yes \ --token="$VERCEL_TOKEN" \ --scope="<team-slug>" \ --archive=tgz 大概 15-30 秒完成。 不需要重新配 DNS、不需要重新绑域名。 十、命令速查卡 目的 命令 本地构建 python3 build.py 本地预览 python3 serve.py 首次/更新部署 cd site/ && npx vercel@latest deploy --prod --yes --token=$VERCEL_TOKEN --scope=$TEAM --archive=tgz 加域名到项目 npx vercel@latest domains add <domain> <project> --token=$VERCEL_TOKEN --scope=$TEAM 查域名 DNS 推荐配置 curl -H "Authorization: Bearer $VERCEL_TOKEN" "https://api.vercel.com/v6/domains/<domain>/config?teamId=<teamId>" 查部署状态 curl -H "Authorization: Bearer $VERCEL_TOKEN" "https://api.vercel.com/v6/deployments?projectId=<pid>&teamId=<tid>" 公共 DNS 查解析 dig @223.5.5.5 +short <domain> 强制某 IP 访问测试 curl --resolve <domain>:443:<ip> https://<domain>/ 十一、踩坑档案(失败案例库) 坑 1 · URL 里的 + 被解码成空格 → 404 现象: 文件夹叫 前台+分诊,访问 /09_系统内核/前台+分诊/README 返回 Vercel 404 NOT_FOUND。 原因: URL 查询字符串里 + 是空格编码,Vercel CDN 把 path 里的 + 也按空格处理。 修复: build.py 生成 href 时对路径 percent-encode: def urlquote(path: str) -> str: url, _, frag = path.partition("#") url = urllib.parse.quote(url, safe="/.-_()") return url + (("#" + frag) if frag else "") 所有 href 都走这个函数,+ → %2B,中文 → %E4%B8%AD...。 坑 2 · www 子域证书报错 现象: https://suhangcompany.site 打得开,https://www.suhangcompany.site 报 NET::ERR_CERT_COMMON_NAME_INVALID。 原因: Vercel 签的证书只覆盖添加到项目的域名。加了 apex 没加 www,www 走默认泛证书就不匹配。 修复: 给项目 add www.<domain>,Vercel 自动签发独立证书。 坑 3 · 域名购买后 DNS 没生效 现象: dig 本地查出来是 198.18.1.18 或 198.18.0.39 这种奇怪 IP。 原因: 198.18.0.0/15 是保留测试段,看到这个就是本地 ISP / 代理在 DNS 劫持。 修复: 用公共 DNS 查真实解析 dig @223.5.5.5 +short <domain> # 阿里 dig @119.29.29.29 +short <domain> # 腾讯 真实解析对了,就是你本机网络环境问题,不是配错。 坑 4 · Vercel 新 IP 在国内不通 现象: 改用 Vercel 推荐的 216.198.79.1,浏览器 ERR_TIMED_OUT。 原因: 新 Anycast IP 在部分国内 ISP 路由差/被屏蔽。但 *.vercel.app 的其他 IP 段能通。 修复: 1. 首选:同一条 A 记录里加第二个值 64.29.17.1 2. 次选:改回 legacy 76.76.21.21(Rank 2) 3. 最终兜底:改 Nameserver 给 Vercel 坑 5 · 本地代理 503 / 超时 现象: npx vercel、curl api.vercel.com 全部卡住或超时。 原因: 本机 Clash / 小火箭 等代理挂了。所有外网请求都走代理(包括 npm registry、Vercel API)。 修复: 1. 重启代理客户端 / 换节点 2. 临时不走代理:unset HTTPS_PROXY HTTP_PROXY ALL_PROXY 3. 别在代理关闭期间部署,Token 有效但网络不通 坑 6 · 根域不能用 CNAME 现象: 想把 @ 设为 CNAME 到 cname.vercel-dns.com,DNS 报错。 原因: DNS 标准不允许 apex(zone apex)设 CNAME,会和 SOA/NS 记录冲突。 修复: - 火山引擎 / 阿里云 / DNSPod 可以用 "显性 MX 优先级" CNAME 展平(ALIAS / ANAME),少数支持 - 通用方案:apex 用 A 记录,www 用 CNAME 坑 7 · 中文文件名部署失败 现象: vercel deploy 上传到 20% 卡住或报 encoding 错误。 原因: HTTP 单文件上传对中文 filename 编码不一致。 修复: 加 --archive=tgz 参数,把整个目录打成 tar.gz 上传,避开单文件编码问题。 十二、相关岗位与文档 发起方:CEO(苏航)决定要上线 执行方:开发部 · 全栈工程师 验收方:质量部 资源方:财务部(买域名)、法务部(隐私政策页、ICP 备案判断) 相关 SOP: - 01_部门/开发部/全栈工程师/工作流程SOP.md — 开发 SOP - 09_系统内核/质量部/README.md — 上线前质量闸 4 - 09_系统内核/质量部/文档模板/闸4_上线前评审_checklist.md — 上线 checklist 版本: v1.0 · 2026-04-20 · 从 suhangcompany.site 首次上线过程沉淀