方法论教程 · 一人公司部署上线 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 域名选择原则

本次实例: suhangcompany.site(火山引擎购入)


五、阶段 4 · Vercel 账号与 Token

5.1 注册

5.2 创建 Token

  1. 右上头像 → Account Settings
  2. 左栏 → Tokens
  3. Create Token - Name:deploy-<机器名>deploy-claude - Scope:选个人 team(通常是 <你的用户名>'s projects) - Expiration:1 day / 7 days / no expiration
  4. 复制并保存(vcp_xxxxx 开头,关闭页面就看不到了)

安全约束: - Token 等于账号全权限,不要提交到 git - 用 export VERCEL_TOKEN=xxx 放环境变量 - 或 ~/.zshrcexport 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.siteNET::ERR_CERT_COMMON_NAME_INVALID

原因: Vercel 签的证书只覆盖添加到项目的域名。加了 apex 没加 www,www 走默认泛证书就不匹配。

修复: 给项目 add www.<domain>,Vercel 自动签发独立证书。

坑 3 · 域名购买后 DNS 没生效

现象: dig 本地查出来是 198.18.1.18198.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 vercelcurl 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 上传,避开单文件编码问题。


十二、相关岗位与文档

相关 SOP: - 01_部门/开发部/全栈工程师/工作流程SOP.md — 开发 SOP - 09_系统内核/质量部/README.md — 上线前质量闸 4 - 09_系统内核/质量部/文档模板/闸4_上线前评审_checklist.md — 上线 checklist

版本: v1.0 · 2026-04-20 · 从 suhangcompany.site 首次上线过程沉淀