From 39e7f2fc11d1bb75563bade7b0123a1d5c947cd0 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Sat, 30 May 2026 17:09:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=9E=E6=BB=9A:rollback.sh=20+=20docs/?= =?UTF-8?q?=E5=9B=9E=E6=BB=9A=E6=89=8B=E5=86=8C.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- deploy/scripts/rollback.sh | 34 ++++++++++ docs/回滚手册.md | 123 +++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100755 deploy/scripts/rollback.sh create mode 100644 docs/回滚手册.md diff --git a/deploy/scripts/rollback.sh b/deploy/scripts/rollback.sh new file mode 100755 index 0000000..0072e44 --- /dev/null +++ b/deploy/scripts/rollback.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# rollback.sh —— 一键回滚:停 pangolin-edge 容器,恢复宿主 nginx(需 root)。 +# sudo bash /home/ec2-user/pangolin/scripts/rollback.sh +# 适用:切换失败、或切换后发现问题,要回到"宿主 nginx 接管 443/80"的原状态。 +# 注:singbox(Hy2/UDP443)与宿主 nginx 不冲突,默认保留运行;如需一并停,见文档。 +set -euo pipefail + +if [[ "$(id -u)" -ne 0 ]]; then + echo "请以 root 运行:sudo bash $0" >&2 + exit 1 +fi + +echo "==> [1/3] 停 edge 容器(释放 80/443)" +# 必须先停容器,否则宿主 nginx 会因端口占用起不来 +docker-compose -p pangolin stop edge 2>/dev/null || docker stop pangolin-edge 2>/dev/null || true + +echo "==> [2/3] 恢复宿主 nginx" +systemctl enable nginx >/dev/null 2>&1 || true +systemctl start nginx +systemctl is-active --quiet nginx && echo " 宿主 nginx 已启动" || { echo " !! 启动失败,见文档排查"; exit 1; } + +echo "==> [3/3] 健康检查" +sleep 1 +fail=0 +echo | timeout 10 openssl s_client -connect 127.0.0.1:443 -servername www.cloudflare.com >/dev/null 2>&1 \ + && echo " [REALITY] 443 握手 OK" || { echo " [REALITY] 握手失败"; fail=1; } +for d in blog jiu vpn; do + code="$(curl -ks -o /dev/null -w '%{http_code}' --resolve ${d}.51yanmei.com:443:127.0.0.1 https://${d}.51yanmei.com/ 2>/dev/null || echo 000)" + echo " [${d}] https -> ${code}" + [[ "$code" =~ ^[2-4][0-9][0-9]$ ]] || fail=1 +done + +[[ "$fail" == "0" ]] && echo "==> 回滚完成 ✅ 已回到宿主 nginx" \ + || { echo "==> ⚠️ 已回到宿主 nginx 但检查未全过,见文档"; exit 1; } diff --git a/docs/回滚手册.md b/docs/回滚手册.md new file mode 100644 index 0000000..161d6bf --- /dev/null +++ b/docs/回滚手册.md @@ -0,0 +1,123 @@ +# Pangolin 切换回滚手册(nginx 宿主 → 容器) + +> 适用对象:把宿主 nginx 切换到 `pangolin-edge` 容器(`cutover.sh`)的那一步。 +> 核心原理:**宿主 nginx 配置全程没动**(切换只是 `systemctl stop/disable nginx`), +> 所以回滚 = 停掉 edge 容器 + 重新启动宿主 nginx,即可秒级回到原状态。 + +--- + +## 0. 一句话回滚(绝大多数情况用这个) + +在你的 Mac 上执行(需输 sudo 密码): + +```bash +! ssh -t ec2 'sudo bash /home/ec2-user/pangolin/scripts/rollback.sh' +``` + +脚本会:① 停 `pangolin-edge` 释放 80/443 → ② `systemctl start nginx` → ③ 自检 REALITY + blog/jiu/vpn。 +看到 `回滚完成 ✅` 即恢复原状。 + +> `cutover.sh` 在切换失败时**会自动回滚**;本手册用于"自动回滚没生效"或"切换后才发现问题"。 + +--- + +## 1. 什么时候要回滚 + +- `cutover.sh` 报错且没自动恢复(罕见)。 +- 切换"成功"了,但发现某站点异常、代理不稳、证书报错等。 +- 任何你想退回宿主 nginx 的情况。 + +--- + +## 2. 手动分步回滚(脚本不可用时) + +逐条在 EC2 上执行(`ssh ec2` 后): + +```bash +# (1) 先停 edge 容器 —— 必须先停,否则它占着 443/80,宿主 nginx 起不来 +docker-compose -p pangolin stop edge # 或: docker stop pangolin-edge + +# (2) 恢复宿主 nginx(切换时被 disable 过,这里重新 enable+start,需 root) +sudo systemctl enable nginx +sudo systemctl start nginx +systemctl is-active nginx # 应为 active + +# (3) 验证 +echo | openssl s_client -connect 127.0.0.1:443 -servername www.cloudflare.com 2>/dev/null | head -1 +for d in blog jiu vpn; do + curl -ks -o /dev/null -w "$d -> %{http_code}\n" --resolve $d.51yanmei.com:443:127.0.0.1 https://$d.51yanmei.com/ +done +``` + +> `pangolin-singbox`(Hy2,UDP 443)与宿主 nginx(TCP)不冲突,**回滚时无需停它**,Hy2 线路可继续用。 + +--- + +## 3. 故障排查 + +### 3.1 宿主 nginx 起不来:`bind() to 0.0.0.0:443 ... Address already in use` +edge 容器没真正停掉,还占着端口: +```bash +docker ps | grep pangolin-edge # 看是否还在 +docker stop pangolin-edge +sudo ss -tlnp | grep -E ':443|:80' # 确认端口已释放(无 docker-proxy/nginx 占用) +sudo systemctl start nginx +``` + +### 3.2 宿主 nginx 配置报错(理论上不会,因为没动过) +```bash +sudo nginx -t # 看具体报错行 +sudo journalctl -u nginx -n 50 --no-pager +``` +若 `/etc/nginx/stream.conf` 或 `conf.d/*` 被误改,从备份恢复(切换流程未改这些文件,通常无需)。 + +### 3.3 edge 容器一直重启/起不来(切换阶段) +```bash +docker logs pangolin-edge --tail 50 # 看 nginx 报错 +docker run --rm \ + -v ~/pangolin/edge/nginx.conf:/etc/nginx/nginx.conf:ro \ + -v ~/pangolin/edge/stream.conf:/etc/nginx/stream.conf:ro \ + -v ~/pangolin/edge/conf.d:/etc/nginx/conf.d:ro \ + -v /etc/letsencrypt:/etc/letsencrypt:ro \ + -v /opt/jiu:/opt/jiu:ro -v /var/www/pay:/var/www/pay:ro \ + nginx:1.27-alpine nginx -t # 离线校验,定位问题 +``` +定位后直接回滚(第 0/2 节),不阻塞线上。 + +--- + +## 4. 验证回滚成功(从你的 Mac,外部视角) + +```bash +for d in blog jiu vpn; do + curl -s -o /dev/null -w "$d -> %{http_code}\n" --max-time 10 https://$d.51yanmei.com/ +done +# 应都为 200;Shadowrocket 代理应自动重连可用 +``` + +回滚后端口归位:`443/80` 由宿主 `nginx` 进程监听(非 docker-proxy)。 + +--- + +## 5. 彻底撤销 pangolin(如决定放弃本次方案) + +```bash +# 停并删除 pangolin 容器(edge / singbox) +docker-compose -p pangolin down # 在 ~/pangolin 下执行 +# 关闭 Hy2 的安全组入站(可选,本机 aws 凭据执行) +# aws ec2 revoke-security-group-ingress --region ap-southeast-1 \ +# --group-id sg-040cf4cd11bdf4d5b --protocol udp --port 443 --cidr 0.0.0.0/0 +# 宿主 nginx 已在第 2 节恢复;~/pangolin 目录可保留或删除 +``` + +--- + +## 6. 关键事实(为什么回滚是安全的) + +- 切换**只**对宿主做了 `systemctl stop/disable nginx`,**没改任何 nginx 配置文件**。 +- `/etc/letsencrypt` 证书只读挂载,从未被写。 +- edge 容器是独立 project(`-p pangolin`),停它不影响 marzban/jiu/billing/blog。 +- 因此恢复宿主 nginx 即 100% 回到切换前状态;唯一"残留"是 Hy2 容器(可留可停)。 + +> 证书续期提醒:回到宿主 nginx 后,原有的 `certbot --nginx` 续期方式继续可用(无需改动)。 +> 只有在**保持容器化**的情况下,才需要按 `deploy/README.md` 切换到 DNS-01 续期。