ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

dgreadiness v3.6 部署前就绪检查:从四项基本面到CI门禁

dgreadiness v3.6 部署前就绪检查:从四项基本面到CI门禁

简介:在Windows 10系统里,许多VMware Workstation用户开启Hyper-V后,运行Ubuntu虚拟机时常遇到“Device/Credential Guard不兼容”的报错,这通常是由Credential Guard占用虚拟化特性所引起。这一资源包正是解决该问题的系统工具,基于微软官方DG Readiness Tool v3.6,使用PowerShell脚本对Credential Guard执行禁用并自动重启,从而恢复VMware虚拟机的正常启动。压缩包内共包含6个文件,主要类型包括PowerShell脚本、XML配置文件、签名策略p7b文件以及说明文档;其中XML和p7b文件又分别对应审计与强制两种模式,便于使用者对照排查。整个压缩包大小约32KB,轻量便捷,下载后解压即可部署,适合系统管理员、虚拟化运维者以及在Windows与Ubuntu之间做开发的测试人员。另外,说明文档对命令参数、执行前提和重启逻辑有清晰交代,即使不熟悉相关工具也能按指引完成操作。目前已有3180人学习下载,该方案为因Credential Guard冲突而无法使用VMware的用户提供了直接有效的应对思路。

1. dgreadiness_v3.6.zip 到底是干什么的:发布前缺的那次“全身体检”

凌晨一点,发布脚本跑到一半报磁盘满,回滚花了四十分钟。这种事故很少是操作失误,多半是发布前少做了一次环境核验。

dgreadiness_v3.6.zip 就是解决这个问题的典型工具包:一个以 zip 形式分发、解压即可执行的服务器就绪检查集合。它把磁盘空间、端口占用、依赖服务、配置文件这几类最容易在发布前出问题的检查项固化成脚本,跑一条命令就输出一份带通过、警告、失败三级状态的报告。名称里的 dg 一般是项目代号,v3.6 是版本号,zip 是它的分发格式,三者合起来就是一个标准的“部署前 readiness 检查包”。

适合运维、SRE、发布负责人,以及需要自己管理服务器的后端开发。接下来我会把这个包从设计逻辑到落地流程、再到接 CI 和排坑,完整拆一遍。

2. 就绪检查该查什么:从四项基本面到 dgreadiness 的设计取舍

就绪检查不是把监控面板重新看一遍。监控回答的是“系统现在有没有问题”,readiness 回答的是“现在能不能安全地把新版本放上去”。这两者的阈值、时机和口径都不一样:监控看长期曲线,readiness 看发布前这一刻的静态快照。dgreadiness 这类工具的价值,就是把这个快照里的检查项固化成可重复执行的脚本,而不是靠发布负责人用记事本勾选。

2.1 磁盘、端口、进程、配置:readiness 检查的四项基本面

绝大多数部署失败都能归到四类问题上。

磁盘是第一类。应用写日志、写临时文件、写缓存镜像,磁盘空间和 inode 都可能被打满。空间用尽很好理解,inode 打满则是个隐蔽坑:小文件特别多的目录,df -h看着还剩十几 G,df -i已经 100%,文件照样写不进去。而且磁盘使用率必须按挂载点逐个看,根分区 60% 不代表 /data 没问题,检查项不能只取一个全局值。

端口是第二类。目标端口被占是发布失败最常见的原因,尤其 Java、Node 这类服务,端口冲突时启动进程可能直接退出。依赖端口则是另一类:MySQL 的 3306、Redis 的 6379 如果没在监听,应用起来也活不过三秒。这两类端口要分开配置——目标端口上线前必须空闲,依赖端口上线前必须处于监听状态,语义完全不同。

服务进程是第三类。检查依赖服务不能只看进程在不在,僵死的服务进程仍然占着 PID 和端口,systemctl is-active才能反映 systemd 视角下的真实状态。如果目标机没用 systemd,至少要确认 pid 文件存在且进程可响应,而不是只 grep 到一行进程名就放心。

配置文件是第四类。配置文件是否存在、权限是不是 644 或 640、应用账号能不能读到,三件事缺一不可。很多排查案例最终定位到配置文件权限 600、属主还是 root,应用服务用普通账号启动后一读就 permission denied。readiness 阶段就该把这些拦下来。

阈值怎么定,我用一张表说明常见做法:

检查项警告阈值失败阈值说明
磁盘空间使用率85%95%日志盘、临时目录按 75%/90% 收紧
inode 使用率85%92%缓存目录、消息队列目录重点看
目标端口占用不适用端口已被监听即失败Java/Node 服务端口冲突会直接退出
依赖服务监听不适用必需端口未监听即失败MySQL、Redis、Nginx 按环境区分
配置文件权限非 644/640 提示不存在或不可读即失败检查项还应覆盖目录可进入性

阈值不是越严越好。定太松,检查等于没做;定太紧,每次发布都被警告卡着,团队很快就会选择绕过工具。我的习惯是磁盘 85% 警告、95% 失败,日志盘单独收紧一档,inode 统一按 92% 失败。这些值必须写进配置,而不是写死在脚本里,否则换个环境就要改代码。

2.2 为什么用 zip 分发而不是裸脚本或 tar.gz

dgreadiness 的交付形态选 zip,这件事本身是经过权衡的。

zip 是跨平台支持最完整的打包格式:Linux 的 unzip、macOS 的归档工具、Windows 资源管理器双击直接打开,三端都不需要额外装软件。相比 tar.gz 到了 Windows 上默认打不开,zip 更像一个绿色免安装的分发包。对工具类脚本集来说,这个优势很实在——使用方拿过去解压就能跑,不会因为缺解压工具卡在第一步。

裸脚本也不是好选择。一个就绪检查工具往往有主脚本、checks 子目录、配置文件、报告模板,散成一堆文件,拷的时候漏一个 checks 脚本,跑出来永远是绿的,发布后照样翻车。打成 zip 至少保证目录结构完整,文件夹名里带上 v3.6 版本号,配合 sha256 校验值,能直接确认机器上跑的是哪个版本。这和 OTA 升级包先验版本再安装是同一个思路。

顺带说一句 zip 协议和 phar 协议的差别。phar 是 PHP 生态里的单文件打包格式,适合把类库压缩成一个文件让 PHP 直接加载;但 dgreadiness 这种既有 shell 脚本、又有 json 配置和报告模板的包,phar 既不能直接执行也不方便改配置。zip 在这里是更通用的分发协议,它不携带执行逻辑,但配合入口脚本的 shebang,解压后chmod +x就能跑。

2.3 dgreadiness_v3.6.zip 解压后的目录结构与一份最小配置

拿到 zip 包,先别急着跑。解压后花两分钟把目录结构看清,再决定改哪个文件。常见的就绪检查工具包目录长这样:

路径作用需要改吗
dgreadiness.sh主入口,负责解析参数、调度检查、汇总报告不用改
conf/dgreadiness.json检查项配置,阈值和检查对象都在这必须改
checks/*.sh具体检查脚本,每类检查一个文件一般不直接改
lib/report.sh报告生成与退出码处理不用改
README.md版本说明和参数说明不用改

最小配置长这样:

{ "checks": { "disk": { "mount_points": ["/", "/data"], "warn_percent": 85, "fail_percent": 95, "check_inode": true }, "ports": { "occupied_fail": [8080, 3306], "listen_required": [443] }, "services": { "required": ["mysqld", "redis-server", "nginx"] }, "config": { "paths": [ { "path": "/etc/myapp/app.yml", "must_exist": true, "max_size_kb": 512 } ] } } }

磁盘段里,mount_points 决定检查哪些挂载点,warn_percent / fail_percent 是两级阈值,check_inode 打开 inode 检查。端口段里,occupied_fail 是“上线前必须空闲”的端口,listen_required 是“上线前必须监听”的端口,这两类千万别混。services.required 是 systemd 视角的服务名。config.paths 是配置文件清单,max_size_kb 用来拦住配置文件被写爆这种少见但致命的情况。

occupied_fail 的典型值是应用自己的服务端口、管理端口、调试端口;listen_required 的典型值是数据库、缓存、注册中心。把 3306 写进 occupied_fail 是新手最容易犯的错——如果这台机器上的数据库是被别的应用连接的,3306 必须出现在 listen_required 里,否则每次检查都会失败。

提示:首次拿到配置模板,先全量保留默认值,只改 mount_points、ports 和 services 三处。其他字段等跑过一次报告之后再按需调整,别一上来就把阈值改得面目全非。

3. 在 Linux 服务器上跑通 dgreadiness 的最小流程:下载、校验、解压、读报告

单机跑通这个包,最快只需要五步:下载、校验、解压、授权、执行。这一章把每一步的命令和参数讲透,照着敲就能出第一份报告。

3.1 从内网源下载到出报告:最小三条命令

常见做法是把 dgreadiness_v3.6.zip 放到内网静态文件源,或者发布机本地目录。服务器上执行:

cd /opt wget http://packages.internal.example/dgreadiness_v3.6.zip unzip dgreadiness_v3.6.zip -d dgreadiness_v3.6 cd dgreadiness_v3.6 && chmod +x dgreadiness.sh checks/*.sh ./dgreadiness.sh --config conf/dgreadiness.json

wget从内网源拉包,比curl -O更适合脚本场景,断点续传和重试机制更成熟。unzip -d指定解压目录,避免把 checks 和 conf 就地散到当前目录,把环境搞乱。

zip 包不保留 Unix 执行位,所以解压后必须chmod +x。这一步漏了,入口脚本会报 permission denied,属于最常见的首跑失败。--config指定配置文件显式路径,不传时工具会去找 conf 目录下的默认文件;显式传路径的好处是以后接 CI 时,可以一套工具多套配置。

3.2 下载完先别解压:sha256 校验和 unzip -t 两道预检

zip 在传输过程中损坏是常态,不是偶发。跨网段下载、经代理缓存下载,文件长度可能没变但中间数据被改写。所以正式执行前先做两道预检:

cd /opt sha256sum dgreadiness_v3.6.zip # 将输出与发布方随包附带的 SHA256SUMS 文件或公告里的值比对 echo "9f2a3b... dgreadiness_v3.6.zip" | sha256sum -c - # 注意:这里是占位校验值,换成实际值 unzip -t dgreadiness_v3.6.zip

sha256sum -c会输出 OK 或 FAILED,能直接当作脚本判断条件。unzip -t只测试 zip 的中央目录结构,不落地文件,相当于解压前的体检。两道预检都过了,再走 3.1 的解压步骤。

这套先校验再解压的流程,和你在 Windows 上配置 Go、JDK 环境时下载 zip 包、验 sha256 再解压是同一套逻辑,环境不一样但套路通用。手动下载时坏了会立刻发现,自动化流水线里坏了会一直带着坏包跑,直到某个环节报出 invalid zip archive 才暴露。所以预检不能省。

3.3 读懂报告和退出码:全绿不是目标,可解释才是

第一次跑通后,别只盯着有没有 FAIL。把输出从头到尾读一遍,一次典型的报告长这样:

[2025-01-12 14:03:22] dgreadiness v3.6 check started [ OK ] disk / : 62% used (warn=85 fail=95) [WARN] disk /data : 88% used, inode 92% used [FAIL] port 8080 : occupied by process 27441 (java) [ OK ] service mysqld : active [FAIL] config /etc/myapp/app.yml : unreadable (permission 600) Result: FAILED (2 fatal, 1 warning), exit code 2

三级状态对应三种动作:OK 表示通过,WARN 表示超过警告阈值但没到失败线,FAIL 表示阻断发布。退出码约定是 0 全通过、1 有警告、2 有失败,这个约定必须和后面 CI 门禁对齐。

上例里 /data 88% 只是警告,但端口 8080 被占和配置文件不可读是失败,发布必须停。很多人拿到报告只看最后一行 Result,这是错的。WARN 项这次没阻断,下次就可能升级成 FAIL,每次都要读。

常用参数我列一张表:

参数作用使用时机
--config指定检查配置多环境多配置时必用
--json输出机器可读 JSON 报告接 CI、归档留证
--verbose展开每条检查的原始输出排查误报时用
--fail-fast遇到第一个 FAIL 立即停止调试时用,正式发布不要用
--dry-run只打印将执行的检查项,不真正执行升级版本后校验配置兼容性

注意--fail-fast只建议手工调试用。正式发布时应该让所有检查项都跑完,把整台机器的问题一次性暴露出来。只修一个问题就重跑,其他隐藏问题一个都发现不了。

4. 把 dgreadiness 接进发布流程:CI 门禁、失败定位与定时巡检

单机跑通只是第一步。真正让这个工具产生价值的是接进发布流程,让它从“手动工具”变成“发布门禁”。

4.1 在 Jenkins 流水线里把检查结果变成发布门禁

最常见的是 Jenkins 流水线,发布前加一个 stage,把 readiness 检查的退出码变成门禁:

stage('readiness check') { steps { script { def rc = sh( script: 'cd /opt/dgreadiness_v3.6 && ./dgreadiness.sh --config conf/dgreadiness.json --json report.json', returnStatus: true ) if (rc >= 2) { error("readiness check failed, rc=${rc}, fix the server first") } } } } stage('deploy') { // 正式发布步骤 }

returnStatus: true让 Jenkins 拿到退出码,而不是让 sh 步骤自己抛异常。rc >= 2表示有 FAIL,直接 error 终止流水线;rc == 1只有警告,放行但报告里保留。--json report.json把结构化结果写到工作区,发布完随构建产物一起归档,事后追溯有据可查。这段逻辑也能平移到 GitLab CI 的 script 段,把 error 换成 exit 1 即可。

设计选择上,为什么“有警告也放行”?因为警告项比如磁盘 85%,往往不阻断本次发布,但它是个环境恶化信号。如果警告也卡死,团队就会积累“绕过工具”的冲动。正确做法是警告放行并留存,等发布窗口结束再处理。

4.2 检查失败先别急着重跑:三个定位命令

readiness 报告说 FAIL 时,第一步不是重跑,而是定位是环境真有问题、还是检查配置写错了。我一般按顺序看三样东西:

# 1. 带 verbose 重跑一次,看每条检查的原始命令输出 ./dgreadiness.sh --config conf/dgreadiness.json --verbose # 2. 看工具自己的运行日志 tail -n 200 /var/log/dgreadiness/check.log # 3. 单独执行对应的检查脚本,把检查项从框架里剥出来 ./checks/port_check.sh --port 8080

第一步区分误报。比如端口检查脚本用ss -lntp抓监听,如果脚本执行权限不足,可能把现有监听也漏掉,报“端口空闲”,这属于误报。第二步看日志里的原始输出,很多检查脚本会把命令的 stdout 原样写进日志,比报告里的精简文案信息量大得多。第三步把单个检查脚本拿出来跑,排除了配置文件解析的问题——如果单跑正常、整体跑失败,问题大概率在配置解析或脚本间资源冲突。

这三个命令顺序别颠倒。直接重跑整体检查,看到的还是同一个 FAIL,解决不了任何信息增量问题。

4.3 定时巡检与结果归档:让 readiness 成为环境漂移的哨兵

发布前检查只能覆盖发布节点。环境在两轮发布之间出了问题怎么办?答案是定时巡检。cron 里加一行,把检查结果按日期归档:

# 每天凌晨 2 点跑一次 readiness,JSON 结果按日期命名 0 2 * * * cd /opt/dgreadiness_v3.6 && ./dgreadiness.sh --config conf/dgreadiness.json --json /var/lib/dgreadiness/reports/$(date +\%Y\%m\%d).json >> /var/log/dgreadiness/check.log 2>&1

cron 里的%必须写成\%,否则 date 命令会被 cron 截断。报告归档到独立目录,和日志分开存放。JSON 报告是结构化数据,后续做基线 diff 用得上。日志重定向到 check.log,避免 cron 把标准输出当成邮件发给 root,造成告警疲劳。

巡检周期按发布频率定。每周发一次版就每天巡,每天发多次版就每小时巡,原则是周期必须是发布间隔的十分之一以下。归档的价值在于:同一台机器有了历史报告,环境漂移就可以被 diff 出来,而不是等发布时才被动发现。

5. dgreadiness 实战避坑:zip 解压失败、误报、漏检与权限陷阱

这一章写我在实际使用就绪检查工具时反复踩过的五个坑,每一条都是现象、原因、解决三段式。

5.1 zip 包解压报错:invalid zip archive,could not find EOCD

现象:wget 下来的 dgreadiness_v3.6.zip 文件大小看着正常,unzip 却报 invalid zip archive with missing end-of-central-directory record,或者导入配置系统时提示 could not find EOCD。文件解压到一半中断,checks 目录缺了好几个脚本。

原因:EOCD 是 zip 格式结尾的一条中央目录记录,解压程序靠它确认文件完整。找不到 EOCD 几乎都是文件本身不完整或被改写。常见场景包括:下载被代理缓存截断、跨网段传输丢包、从 Windows 用 FTP 的 ASCII 模式传 zip 导致二进制数据被改写、杀毒软件在下载时扫描误删尾部数据。

解决:重新下载,别在同一路径续传,续传可能拿到同样的坏包;下载后用 sha256sum 和发布方给的校验值比对,不一致直接判失败;再保险一点,先跑unzip -t做结构测试。我见过最隐蔽的版本是手动下载没问题、CI 里偶发失败,最后定位到 CI 的缓存策略把 zip 缓存坏了,清掉缓存目录才恢复正常。

5.2 检查全绿发布却翻车:readiness 查的是“当时”,不是“之后”

现象:发布前跑 dgreadiness,磁盘、端口、服务全部 OK,发布脚本执行到一半却报磁盘空间不足,或者服务启动后端口冲突,现场一片乱。

原因:readiness 检查给出的是执行时刻的快照,检查到发布之间隔着时间窗口。日志系统在这几分钟里写满了剩余空间,另一个任务抢占了目标端口,这些都发生在检查之后、发布之前。另一个隐藏原因是阈值定得太宽,以为“还剩 15% 足够”,忽略了应用启动时解压临时文件可能瞬间吃几个 G。

解决:阈值按业务峰值收紧,磁盘空间警告改为 75% 而不是 85%,日志单独挂载点按 70% 看;发布脚本里保留一道轻量前置检查,df 和端口占用几行命令的事,别完全依赖独立工具;对大文件解压、日志聚合这类写密集场景,提前做限流或清理。核心教训是,readiness 降低的是概率,不是消灭风险。

5.3 root 跑全绿,普通用户跑全红:检查脚本的权限视角

现象:用 root 执行 dgreadiness 全部 OK,切到部署账号 appadmin 再跑,配置文件不可读、端口无法绑定、服务状态未知,报告一片红。

原因:检查脚本继承了执行者的权限视角。root 能读所有文件、能看到所有进程和端口;普通用户看ss -lntp只能看到自己权限范围内的监听,权限 600 的配置文件也根本打不开。工具本身没写错,是执行身份不对。

解决:从第一天就用发布账号跑 readiness,不要用 root。如果工具支持 run_as 配置,在 conf 里显式声明,脚本内部切换身份。检查配置文件的逻辑里,除了文件存在性,还要加“目录可进入”判断——文件权限 644 但父目录是 700,普通用户照样进不去。这个坑在新环境第一次发布时最常见,和“本地能跑线上不能跑”是同一个根源。

5.4 从 v3.5 升到 v3.6 后旧配置失效:配置兼容性不是默认保证的

现象:升级 dgreadiness 版本后,直接沿用旧 conf/dgreadiness.json,启动报 unknown field,或者某些检查项被静默跳过,报告里少了 config 检查这一行。

原因:新版本对配置 schema 做了严格校验,不认识的历史字段会直接报错。另一类更隐蔽:某些字段在校验时报了 warning 但没阻断,工具按默认值继续执行,而默认值在 v3.6 里变了,导致检查结果和 v3.5 明显不一致。

解决:升级后第一件事跑--dry-run,让它打印将要执行的检查项和默认值,再把打印结果和 README 里的变更说明对齐,确认没有字段被弃用。所有关键阈值写进配置而不是依赖脚本默认值,这样版本升级不会悄悄改变检查标准。我有一次升级后磁盘检查一直失败,查了半小时才发现新版默认把 systemd 挂载点也纳入了检查,旧配置没排除它。

5.5 误把 readiness 当 liveness:检查太重,发布被拖垮

现象:readiness 检查里塞了数据库压测、全量日志扫描、缓存预热,每次发布前要跑 20 分钟,偶发超时误报,发布窗口被频繁推迟。

原因:混淆了 readiness 和 liveness 的职责。readiness 回答“能不能启动”,liveness 回答“启动后活不活”。压测、预热、全链路验证属于后者,应该放到发布后的 smoke test 阶段。把它们塞进 readiness,检查时长和失败率都会失控。

解决:readiness 只保留轻量必检项——磁盘、端口、服务、配置。重量级验证拆到发布后的独立阶段,阶段之间用等待和重试策略链接,而不是都堆在发布前。给检查项加 optional 标记,默认跑必检项,可选项目跑在夜间巡检验证。这样发布门禁保持在 30 秒内,团队才愿意每次都跑。

6. 让 dgreadiness 真正省心:自定义检查、基线 diff 与环境漂移预警

readiness 检查跑得再顺,也只会覆盖工具内置的检查项。真正让它在团队里沉淀为“下一次发布也愿意跑”的,是把环境特有的检查补进去,并把历史报告变成基线。

6.1 给 dgreadiness 加一个自定义检查:目录可写性验证

很多这类工具支持把 checks 目录下的自定义脚本注册进配置。以目录写测试为例:

#!/usr/bin/env bash # checks/custom_writable.sh —— 验证指定目录对当前用户可写 TARGET="${1:-/data/tmp}" if touch "${TARGET}/.dgreadiness_probe" 2>/dev/null; then rm -f "${TARGET}/.dgreadiness_probe" echo "OK writable ${TARGET}" exit 0 else echo "FAIL not writable ${TARGET} by $(whoami)" exit 2 fi

然后在配置里加一段 custom 注册,把脚本路径、参数、报告名称对应起来。自定义检查的意义不只是补功能,它把团队里那些“上次踩过的坑”沉淀成代码。哪个目录曾经被写爆,就把这个检查加进去;哪个端口曾经被抢过,就加进 occupied_fail。readiness 检查做得越贴近业务,越能挡住真实事故。

6.2 用历史报告做基线 diff:环境漂移比事故更早出现

归档的 JSON 报告不只是给人看的,它还是环境漂移的证据链。两次发布之间,如果磁盘使用率涨了 30 个点、某个依赖服务换了监听地址,这些都该在发布前被发现:

# 对比上次全绿报告和本次报告,只看检查项的变化 diff <(jq -S '.checks' /var/lib/dgreadiness/reports/20250112.json) \ <(jq -S '.checks' /var/lib/dgreadiness/reports/20250119.json)

jq -S先按 key 排序再输出,保证 diff 的对比是结构化的,不受 JSON 字段顺序影响。变化项包括阈值逼近、端口占用者变化、服务状态切换,每一条都是发布前应该评估的信号。和监控告警的区别是,diff 给的是“环境和上次成功发布时的差异”,而不是“当前是否达标”——差异可以很小但致命,阈值只看趋势。

说句实在话,我早年发布前是在记事本里手动勾检查项的,漏过一次磁盘 inode 打满,回滚到凌晨两点。后来不管多急,发布前先跑一次 readiness,报告归档留痕,下次发布前 diff 一下基线。这套习惯救过我很多次。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进