从代码 Push 到 K8s 滚动更新 —— 前端 CI/CD 全链路拆解
一、引子前端部署为什么链路这么长很多开发者在初次接触前端部署时看到 Jenkins 日志里冒出一堆docker build、helm upgrade、kubectl rollout难免一头雾水。明明后端部署就是打个 jar 包扔上去java -jar就完事了前端怎么拐了这么多弯其实前后端在 K8s 这套体系下跑的是完全相同的流水线逻辑只是集装箱里装的东西不一样。本文以前端项目的真实 Jenkins 日志为线索把每一步拆开讲清楚。二、完整链路一览Git 代码 Push │ ▼ [GitLab Runner] npm install → npm run build → 产出 dist.zip │ ▼ [Jenkins] 下载 zip → 解压 → docker build → docker push 到 Harbor │ ▼ [Jenkins] 调 Helm 通知 K8s用新镜像升级 │ ▼ [K8s] 拉新镜像 → 启动新 Pod → 健康检查通过 → 旧 Pod 下线 → 完成下面逐步展开。三、GitLab Runner 构建产物代码 push 到 GitLab 后第一棒是 GitLab Runner。它做的是纯粹的体力活——拉代码、装依赖、跑构建gitclone仓库npminstallnpmrun build# 产出 dist/ 文件夹zip-rdist.zip dist/# 压缩# 上传至文件服务器生成下载链接最终产出是一个 zip 文件和它的下载地址。为什么不是 Jenkins 来构建因为 Jenkins 的 Agent 节点是通用机器不一定装了 Node 环境。让 GitLab Runner 来构建环境与代码仓库绑定职责更清晰——这就是关注点分离。Jenkins 只管编排不干具体的构建活儿。四、Jenkins 打包 Docker 镜像Jenkins 拿到 zip 下载链接后wget-Odist.ziphttps://s.longhu.net/.../purchse-evaluate-web-UAT.zipunzipdist.zip-d./distdockerbuild-tpurchse-evaluate-web-uat:263526-f./h5/dockerfile ./实际 Dockerfile 长这样FROM nginx:latest # 基础镜像自带 nginx COPY ./h5/nginx.conf /etc/nginx/nginx.conf # 换 nginx 配置 COPY ./dist /usr/share/nginx/dist # 把前端静态文件塞进去 EXPOSE 80这个镜像本质上就是“nginx 程序 前端静态文件”打包成的一个标准单元。K8s 只认镜像不认裸文件——没有镜像它根本不知道该怎么启动你。五、推送到 Harbor 镜像仓库dockerpush harbor.longfor.com/longhu-devops-k8s-sit/purchse-evaluate-web-uat:263526镜像仓库是必需品。Jenkins 构建镜像的机器和 K8s 集群的机器不是同一台镜像必须上传到 Harbor企业级 Docker RegistryK8s 各节点才能通过docker pull拉下来运行。类比GitLab Runner 产出 zip原材料Jenkins 把它装进保温箱Docker 镜像Harbor 就是中央仓库货架K8s 各节点按需取货。六、Helm 通知 K8s 升级helm upgrade--setimage.tag263526purchse-evaluate-web-uat-39856 /Docker/helm/h5Helm 是 K8s 的包管理器类似 yum / npm。它用 chart 模板把部署配置镜像名、副本数、域名、端口打包一条命令完成升级。这条命令翻译成人话“用第 263526 号版本的新镜像替换掉现在跑着的旧版本的 purchse-evaluate-web-uat 这个服务”。七、K8s 滚动更新机制这是整个链路最精华的部分。Helm 下发指令后K8s 执行滚动更新Rolling Update时间线 ────────────────────────────────────────→ 阶段 1旧 Pod 正常运行 ┌──────────┐ │ 旧版本 │ ← 正在接收用户请求 └──────────┘ 阶段 2K8s 拉新镜像启动新 Pod ┌──────────┐ │ 旧版本 │ ← 仍在接请求用户流量没断 └──────────┘ ┌──────────┐ │ 新版本 │ ← 启动中…拉镜像 → nginx 进程跑起来 └──────────┘ 阶段 3健康检查Readiness Probe K8s 访问 http://新Pod:80/ → 返回 200 → ✅ Ready 阶段 4切换流量淘汰旧 Pod 旧 Pod 收到 SIGTERM → 不再接收新请求 → 处理完手头连接 → 退出 ┌──────────┐ │ 新版本 │ ← 所有流量切过来了 └──────────┘ 阶段 5旧 Pod 删除滚动更新完成 ✅核心原则整个过程中用户请求不中断。K8s 保证至少有一个 Pod 在接客这就是零停机部署。八、常见问题部署超时真实场景中的典型错误日志Waiting for deployment rollout to finish: 1 old replicas are pending termination... timeout has been exceeded: ABORTED这不是代码问题而是 K8s 集群层面的问题常见原因有三个新 Pod 没 Ready。健康检查一直不过比如 nginx 配置错了、镜像拉不下来K8s 不敢关旧的——关了服务就断了。旧 Pod 僵死。SIGTERM 发过去但进程不响应一直卡在 Terminating 状态。节点资源不足。集群没有余力拉新镜像或启动新容器。排查方式kubectl-ndevops get pods|greppurchse-evaluate-web-uat kubectl-ndevops describe podpod名# 重点看 Events 字段大多数情况下部署最终是成功的只是 Jenkins 的 3 分钟超时等不及了。九、前后端流程对比把前后端放在同一个框架下看逻辑完全一致步骤后端 jar前端拉代码构建Maven/Gradle compilenpm run build产出物app.jardist.zip打包Dockerfile java 基础镜像Dockerfile nginx 基础镜像部署Helm → K8sHelm → K8s运行时JVM 启动 Spring Bootnginx 提供静态文件唯一的区别是集装箱里谁当服务员——后端自带 Tomcat前端得配 nginx。这一点在第二篇详细展开。