1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
+ ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
99 lines
7.2 KiB
Markdown
99 lines
7.2 KiB
Markdown
# 22 · 服务域名迁移:dsh.alotbuy.com → alotbuy.com
|
||
|
||
- 日期:2026-09-10
|
||
- 触发:用户裁定「服务域名改为 alotbuy.com」,并在两条路径中选定 **路径 ②:走 Cloudflare 代理(橙云,零 DNS 改动)**
|
||
- 结论一句话:**已完成迁移** —— 门户 = `https://alotbuy.com`,用户实例 = `https://<用户名>.alotbuy.com`;旧域名 `dsh.alotbuy.com` 全部 **301** 到新域名(含用户名映射);证书 `alotbuy.com + *.alotbuy.com` 已签发且**续期演练参数已加固**。
|
||
- 状态:**已上线**;遗留:CF SSL 模式建议人工确认 Full (Strict)、全员需重新登录
|
||
|
||
> **TL;DR**|**结论**:域名迁移完成:门户 **`alotbuy.com`**、用户实例 **`<用户名>.alotbuy.com`**;旧域 `dsh.alotbuy.com` 全部 **301**(含用户名映射)。
|
||
> **关键**:走 Cloudflare 代理(橙云、零 DNS 改动);签 `alotbuy.com + *.alotbuy.com` 通配证书(**DNS-01 传播必须 60 s**)+ CF 真实 IP 还原。
|
||
> **状态**:✅ 已上线(含回滚步骤)
|
||
---
|
||
|
||
## 一、决策与前置侦察(实测)
|
||
|
||
| 项 | 结果 |
|
||
|---|---|
|
||
| `alotbuy.com` A 记录 | **已指向本机** `47.77.182.89`,经 **Cloudflare 代理(橙云)** |
|
||
| `alotbuy.com` 当时内容 | **nginx 404(空站)** → 根域未被占用,接管无冲突 ✅ |
|
||
| `*.alotbuy.com` | **通配记录已存在**(橙云)→ 用户子域天然可解析 ✅ |
|
||
| 同域其它记录 | `gao`/`mao`→154.40.35.3、`www`→apex、`work`→apex、`alimail/smtp`→mxhichina、MX/TXT(SPF) → **迁移不触碰这些** |
|
||
| CF token 权限 | **仅 DNS(Zone:DNS:Edit)**:能改记录、能签 DNS-01 证书;**不能读写 zone 设置**(SSL 模式需人工确认) |
|
||
| CF 回源端口(实测 `ss`) | 全部 **443**,无 80 连接 → CF 处于 Full(或 Strict)模式,**CF→源站已加密**,无明文风险 ✅ |
|
||
| 备案 | alotbuy.com 已在阿里云备案(否则子域无法访问)✅ |
|
||
|
||
## 二、执行步骤(含踩坑)
|
||
|
||
### 1. 签发通配证书(DNS-01)
|
||
|
||
```bash
|
||
certbot certonly --dns-cloudflare --dns-cloudflare-credentials /etc/cloudflare.ini \
|
||
--dns-cloudflare-propagation-seconds 60 --non-interactive --agree-tos \
|
||
--cert-name alotbuy.com -d alotbuy.com -d '*.alotbuy.com'
|
||
```
|
||
产物:`/etc/letsencrypt/live/alotbuy.com/{fullchain,privkey}.pem`(有效期至 2026-12-09,已配置自动续期)。
|
||
|
||
> ⚠️ **踩坑 1(重要)**:首次签发**失败**(`No TXT record found at _acme-challenge.alotbuy.com`)。certbot 日志显示 TXT 记录**创建成功**,只是**默认只等 10 秒传播**(CF 上实测 ~10 s 才可见,正好卡边界)。写临时 TXT 探测证实记录可被 Google/CF 解析到(无通配 CNAME 劫持),**把传播等待改为 60 s 后即成功**。
|
||
> ⚠️ **踩坑 2(续期)**:`certbot` 只把该参数写进**新证书**的 renewal conf;**旧证书(dsh.alotbuy.com)仍是默认 10 s** → 会在 ~60 天后续期失败。已给两个 renewal conf 都补上 `dns_cloudflare_propagation_seconds = 60`。
|
||
|
||
### 2. nginx vhost(新域名)
|
||
|
||
- 新增 `/www/server/panel/vhost/nginx/alotbuy.com.conf`:`server_name alotbuy.com www.alotbuy.com *.alotbuy.com`,80 段**代理**(不 301,兼容 CF 任一模式),443 段用新证书;
|
||
- 继承 dsh 站点的三项优化:`proxy_buffering off`(流式)、`gzip_proxied any`(930 KB bundle 压缩)、`/assets/` 长缓存;
|
||
- **CF 真实 IP 还原**:`set_real_ip_from <CF 全部回源段>` + `real_ip_header CF-Connecting-IP`(写在 server 块内,避免全局生效;否则日志/风控里全是 CF 机房 IP)。
|
||
> 踩坑 3:`set_real_ip_from` 写在 vhost 文件顶层会落到 **http 上下文作用于全局**,务必放进 server 块。
|
||
|
||
### 3. 切换域名配置(drain + 重启)
|
||
|
||
脚本:`/opt/dsh/switch-domain-alotbuy.sh`(幂等、带备份、**drain 用按 pid 结束而非 `pkill -f`**——后者会杀掉自身 ssh 会话,档案 20 已踩过)
|
||
```
|
||
env: DSHS_BASE_DOMAIN=alotbuy.com
|
||
DSHS_COOKIE_DOMAIN=.alotbuy.com (备份 /etc/dshs.env.bak-20260910-2332)
|
||
停服务 → 结束残留 dsh 子进程/scope → 起服务(残留实例数 = 0)
|
||
```
|
||
|
||
### 4. 旧域名 301(保留用户名映射)
|
||
|
||
`dsh.alotbuy.com.conf` 改为纯跳转,使用 **map + 通配 server_name**(正则 server_name 实测**不匹配**,踩坑 4):
|
||
```nginx
|
||
map $host $alotbuy_new_host {
|
||
default alotbuy.com;
|
||
~^(?<label>[^.]+)\.dsh\.alotbuy\.com$ $label.alotbuy.com;
|
||
}
|
||
server { listen 80; server_name dsh.alotbuy.com *.dsh.alotbuy.com; return 301 https://$alotbuy_new_host$request_uri; }
|
||
server { listen 443 ssl; server_name dsh.alotbuy.com *.dsh.alotbuy.com; ssl_certificate …dsh.alotbuy.com…; return 301 https://$alotbuy_new_host$request_uri; }
|
||
```
|
||
|
||
## 三、验证结果
|
||
|
||
| 检查 | 结果 |
|
||
|---|---|
|
||
| 门户(经 CF) | `https://alotbuy.com/login.html` → **200** |
|
||
| 用户子域(经 CF,未登录) | `https://admin.alotbuy.com/` → **401**(正常:需登录)✅ 说明编排器已把该 Host 识别为用户子域 |
|
||
| 旧域名跳转 | `dsh.alotbuy.com/...` → **301 → https://alotbuy.com/...**;`admin.dsh.alotbuy.com` → **301 → https://admin.alotbuy.com/**;`x.dsh.*` → `x.alotbuy.com`(通用映射) |
|
||
| 源站证书校验 | `ssl_verify_result=0`(链有效,CF Full (Strict) 亦可通过) |
|
||
| 服务状态 | `active`/`enabled`,编排器正常,残留实例 0(用户重新进入时按需拉起) |
|
||
| 自动续期 | 两个证书 renewal conf 均含 `dns_cloudflare_propagation_seconds = 60` |
|
||
|
||
## 四、回滚
|
||
|
||
| 项 | 回滚 |
|
||
|---|---|
|
||
| 域名配置 | `cp /etc/dshs.env.bak-20260910-2332 /etc/dshs.env` → 跑 drain(停服务→清实例→起服务) |
|
||
| nginx | `cp /www/server/panel/vhost/nginx/{dsh.alotbuy.com.conf.bak-*-pre-redirect,alotbuy.com.conf}` 还原/删除 → `nginx -t && nginx -s reload` |
|
||
| 证书 | 保留(不影响) |
|
||
|
||
## 五、遗留与注意事项
|
||
|
||
1. **CF SSL 模式建议人工确认为 Full (Strict)**:token 无 zone 设置权限,只能由用户在 Cloudflare 面板确认(当前实测回源走 443,已是 Full 级别;Strict 仅多一层源站证书校验,而我们的证书有效,可直接切换)。同时建议开启 **Always Use HTTPS**。
|
||
2. **全员需重新登录**:`COOKIE_DOMAIN` 由 `.dsh.alotbuy.com` 变为 `.alotbuy.com`,旧 Cookie 失效。
|
||
3. **Cookie 覆盖面扩大**:`.alotbuy.com` 意味着该 Cookie 会随请求发往 `alotbuy.com` 下**所有子域**(含 `gao`/`mao`/`work`/`www`)。若日后希望收窄,需改为在 `<用户名>.alotbuy.com` 上设置 host-only Cookie(涉及代码改动)。
|
||
4. **宝塔面板会重写 vhost**:在面板里保存站点配置可能抹掉 `proxy_buffering off` / `gzip_proxied any` / `real_ip` 等自定义指令 → 流式或真实 IP 异常时先回来检查。
|
||
5. 未完成项(独立跟踪):档案 21 §三 的 **A1 禁用 HMR**、**A2 catch-all 默认站点**。
|
||
|
||
## 六、红线遵守
|
||
|
||
- 未触碰 R1(不升级 dsh)与 R2(不改官方 dsh 主程序);仅改 nginx 站点配置、编排器环境变量与 DNS/证书。
|
||
- 全程保留可回滚副本(env / vhost / renewal conf 各有 `*.bak-*`)。
|
||
- 未改动 alotbuy.com 的 MX/邮件/TXT 记录与 `gao`/`mao`/`work` 等既有子域记录。
|