用 curl 做三层部署验证:命令、配置与两个坑

举报
云上小栈 发表于 2026/10/27 11:44:35 2026/10/27
【摘要】 为什么用 curl 做部署验证部署完成后的验证,很多团队依赖浏览器点一遍或者等监控报警。这两种方式都有延迟。curl 是每个镜像里基本都有的工具,脚本化成本低,退出码可以直接用来判断成败,适合放进 CI/CD 或者发布后的 post-deploy 步骤。下面按三层组织:入口层(负载均衡或网关)、应用层(服务实例直连)、依赖层(数据库、缓存、下游 HTTP 接口)。整体脚本约定:用 -s -o...

为什么用 curl 做部署验证

部署完成后的验证,很多团队依赖浏览器点一遍或者等监控报警。这两种方式都有延迟。curl 是每个镜像里基本都有的工具,脚本化成本低,退出码可以直接用来判断成败,适合放进 CI/CD 或者发布后的 post-deploy 步骤。下面按三层组织:入口层(负载均衡或网关)、应用层(服务实例直连)、依赖层(数据库、缓存、下游 HTTP 接口)。

整体脚本约定:用 -s -o /dev/null -w "%{http_code}" 只取状态码,配合 set -e 让失败即时中断。

check() {
  code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$1")
  [ "$code" = "$2" ] || { echo "FAIL $1 -> $code (want $2)"; exit 1; }
}

第一层:入口层验证

入口层要验证三件事:域名解析到预期 IP、HTTPS 证书链完整、后端路由通。命令分别如下:

curl -sI https://api.example.com/healthz
curl -o /dev/null -s -w "%{http_code} %{time_total}\n" https://api.example.com/healthz
curl -s --resolve api.example.com:443:10.0.3.21 https://api.example.com/healthz

第二条输出形如 200 0.183,可以把 time_total 超过 1 秒视为异常并打日志。第三条用 --resolve 绕过 DNS 直接打到指定后端,用来区分「DNS 问题」和「服务问题」。如果 --resolve 的请求通而普通请求不通,问题在 DNS 或 VIP,不在应用。

第二层:应用实例直连

入口正常不代表每个实例都健康。逐个实例直连,可以避免「负载均衡只把流量发到还活着的两个节点」导致的问题被掩盖。脚本里遍历实例 IP 列表:

for ip in 10.0.3.21 10.0.3.22 10.0.3.23; do
  curl -s -o /dev/null -w "$ip %{http_code}\n" \
    --max-time 3 -H "Host: api.example.com" \
    http://$ip:8080/healthz
done

注意用 -H "Host: ..." 保持虚拟主机一致,否则 Nginx 可能返回 404。这一层还要检查响应体里的关键字,例如构建版本号,确认新版本真的生效:

curl -s http://10.0.3.21:8080/healthz | grep -q '"build":"20240612-a1b2c3"'

第三层:依赖连通性

健康检查接口最好能透出依赖状态。如果没有,用 curl 直接探测下游 HTTP 依赖,例如内部用户服务:

curl -s -o /dev/null -w "%{http_code}\n" --max-time 3 \
  http://user-svc.internal:9090/ping
curl -s -o /dev/null -w "%{http_code}\n" --max-time 3 \
  -u "$CACHE_USER:$CACHE_PASS" http://cache-admin.internal:8090/ping

数据库和 Redis 不适合用 curl,但可以把它们的连通性检查放进应用的健康检查接口里,curl 只负责取这个接口的结果。这样第三层的验证统一成一次 HTTP 请求,脚本简单。

踩坑一:连接被复用,验证了旧节点

现象:滚动更新后立刻执行 curl,所有实例状态码都正确,但访问到的还是旧版本代码。原因是在同一个 curl 进程外,HTTP/1.1 的 keep-alive 连接被复用,或者中间有代理缓存了响应。

解决办法:在验证脚本里显式关闭连接复用、禁用缓存,并加一个唯一的查询参数:

curl -s -o /dev/null -w "%{http_code}\n" \
  -H "Connection: close" \
  -H "Cache-Control: no-cache" \
  "https://api.example.com/healthz?v=$(date +%s)"

另外把版本号比对放进断言,不要只看 200。只看状态码会在「旧版本也健康」时给出假阳性。

踩坑二:默认跟随重定向与超时缺失

现象有两个。第一,入口返回 301 跳转到登录页,不加 -L 时状态码是 301,脚本以为失败;加了 -L 后状态码变成 200,但其实是登录页,验证完全失去意义。第二,没有 --max-time 时,某个实例的网络半断开会让脚本卡住很久,发布流水线超时。

解决办法:验证脚本一律不加 -L,把 301/302 当成失败处理,因为健康检查端点和业务 API 都不应该重定向;同时对每个请求加 --max-time 3 和 --connect-timeout 2。如果确实需要跟随跳转,用 -w "%{url_effective}" 打印最终 URL 并在脚本里检查它是否等于预期值:

final=$(curl -sL -o /dev/null -w "%{url_effective}" "$URL")
[ "$final" = "$URL" ] || { echo "redirected to $final"; exit 1; }

小结

三层验证分别回答三个问题:入口能不能通、每个实例是不是新版本、下游依赖有没有断。命令要点是 -s -o /dev/null -w 取状态码、--resolve 定位 DNS 与 VIP、--max-time 防止挂起、-H "Connection: close" 防止复用旧连接、不加 -L 且断言响应体里的版本号。这些检查跑完通常在 3 秒以内,可以直接放进发布流程的最后一步。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。