主题
配套文件与源码
两份文档都有配套的可直接使用文件。这里提供在线浏览和打包下载两种方式。
原始 HTML 版本
这两份文档最初是以单文件 HTML 形式产出的:自包含、无外部依赖、可离线打开,版式也是照着 HTML 定制的。VitePress 版本方便在线查阅和检索,HTML 版本适合下载存档或转发。
两个版本的关系
内容一致。VitePress 版把长文档拆成了带侧边栏锚点导航的单页,并支持全文搜索与暗色模式;HTML 版保留了原始的彩色卡片式版式。看正文用 VitePress 版,存档转发用 HTML 版。
打包下载
两份文档的配套文件都打好了包,直接下载解压即可使用:
| 压缩包 | 内容 | 用途 |
|---|---|---|
| argocd-files.zip | patch-argocd.sh、project-ruoyi.yaml、apps-all.yaml、argocd-server-ingress.yaml、Jenkinsfile.ci.gateway、clean.py、README.md | ArgoCD 改造方案第 4~7 章要用到的清单与脚本 |
| demo.zip | server/(长连接服务端 + Dockerfile)、client/(启动器)、manifest/(版本清单模板)、k8s/(Rollout 清单)、README.md | 客户端/服务端发布学习用的最小 demo |
关于脚本的说明
argocd-files 里的脚本是改造方案的配套操作脚本(原文档第 4~7 章明确写出并解释过每条命令)。
而 demo 里刻意没有放任何构建/发布脚本——因为脚本会把过程藏起来,学习阶段需要逐条敲命令。demo 里的文件全是「被发布的软件本体」:server.js 是服务端程序、launcher.js 是客户端程序、Dockerfile 和 k8s 清单是它们的构建与部署定义。
目录结构
argocd-files
text
argocd-files/
├── README.md 使用说明(含执行顺序与备份命令)
├── patch-argocd.sh Argo CD 安装后的统一补丁:节点固定 / 拉取凭据 / 资源限额
├── project-ruoyi.yaml AppProject:约束 Git 源、命名空间、资源类型
├── apps-all.yaml 9 个 Application(config / 6 个后端 / gateway / ingress)
├── argocd-server-ingress.yaml 暴露 Argo CD Web UI(复用现有 nginx IngressClass)
├── Jenkinsfile.ci.gateway 改造后的 Jenkinsfile:仅 CI + 触发 CD
└── clean.py 清洗从集群导出的 YAML,剥离运行时字段demo
text
demo/
├── README.md 操作手册:为什么这里没有脚本
├── server/
│ ├── server.js **服务端程序**:版本闸门 / 双协议 / 功能开关 / 分桶放量 / 摘流排空
│ ├── package.json 只依赖 ws
│ └── Dockerfile 注意 CMD 必须用 exec 形式,否则收不到 SIGTERM
├── client/
│ ├── launcher.js **客户端程序**:更新决策 / 差量补丁 / SHA256 校验 / 协议握手
│ └── package.json 零第三方依赖(内置极简 WebSocket 客户端)
├── manifest/
│ └── manifest.json 版本清单模板(含每个字段的说明)
└── k8s/
└── rollout-canary.yaml Argo Rollouts 金丝雀 + nginx 流量切分 + drain 配置demo 里做进去的 9 个机制
server/server.js 不是「一个能跑的服务」,而是把这类项目最关键的机制都实现了一遍。读代码时可以对照文档第 1 章的术语:
| 代码位置 | 对应概念 | 文档章节 |
|---|---|---|
MIN_PROTOCOL / CURRENT_PROTOCOL | 协议兼容窗口 | 1.4 |
握手检查 msg.protocol → 返回 reject | 版本闸门 | 1.4 |
握手检查 msg.configVersion | 配置表版本校验 | 1.8 |
FEATURE_FLAGS 环境变量 | Feature Flag(功能开关) | 1.3 |
bucketOf() 函数 | 分桶 + 哈希雪崩混淆 | 1.5 |
ROLLOUT_PERCENT | 放量百分比 | 1.5 |
ready + /healthz 返回 503 | 摘流(Drain) | 1.6 |
/livez 始终返回 200 | readiness 与 liveness 分离 | 1.6 |
drain() 函数 | 优雅停机 + 通知重连 | 1.6 |
快速跑通 demo
bash
# 让 Node 可用(Node 装在 /root/node/bin)
export PATH=/root/node/bin:$PATH
# 1. 起服务端
cd demo/server
npm config set registry https://registry.npmmirror.com
npm install
FEATURES=new_map,new_shop SERVER_VERSION=1.2.0 node server.js
# 2. 另开终端,逐个验证四个机制
cd demo/client
node launcher.js --user 1001 --protocol 5 # 新客户端:通过 + 拿到功能
node launcher.js --user 1001 --protocol 4 # 老客户端:通过但功能为空 ← 最重要的一条
node launcher.js --user 1001 --protocol 3 # 太老:被版本闸门拒绝
node launcher.js --user 1001 --config cfg-x # 配置表不一致:要求先热更bash
# 3. 验证摘流 + 优雅排空
curl -s http://127.0.0.1:3000/healthz; echo # 200
curl -s 'http://127.0.0.1:3000/drain?grace=3000'; echo # 触发摘流
curl -s http://127.0.0.1:3000/healthz; echo # 503 ← 摘流生效
curl -s http://127.0.0.1:3000/livez; echo # 200 ← 没被判定卡死同时看客户端终端:应该收到 {"type":"reconnect", ...},而不是异常断开。
Dockerfile 里的一个致命细节
dockerfile
CMD ["node", "server.js"] # ✅ node 是 PID 1,能收到 SIGTERM
CMD node server.js # ❌ /bin/sh 是 PID 1,会吞掉 SIGTERM写成 shell 形式的话,K8s 发的 SIGTERM 会被 /bin/sh 吃掉,drain() 永远不会执行,Pod 到宽限期上限被 SIGKILL 硬杀,在线玩家全部异常掉线。这个细节决定了你的优雅停机是真的还是假的。