devops系列(七 结案篇) DevOps 综合实战:从 Git 提交到 K8s 部署的全链路
DevOps 综合实战:从 Git 提交到 K8s 部署的全链路
这是本系列的最后一篇,咱们把前面学的 Docker、K8s、Jenkins 这些"散件"串成一条完整的交付链路。
一、问题引入:技术都会,就是不会发版
你有没有遇到过这种情况?
团队里小王会写 Dockerfile,小李会搭 Jenkins,老张会玩 Kubernetes。但一到发版那天,群里还是炸锅:
- “我代码提交了,谁去服务器上打包?”
- “这个镜像 tag 是啥?
latest还是v1.2.3?” - “K8s 上怎么还没更新?是不是 YAML 写错了?”
- “完了,线上出问题了,回滚回哪个版本?”
说白了,单点技术大家都会,但没人能把它们串成一条自动化流水线。结果就是:发版靠手工、沟通靠吼、回滚靠猜。
我当年也在这个坑里摸爬滚打过。那时候我们团队刚转型 DevOps,学了 Docker 觉得真香,学了 K8s 觉得高大上,结果真正发版的时候,我还是得半夜 SSH 上服务器,手动docker build、docker push,再kubectl apply。一套操作下来,手心全是汗。
那么问题来了:怎么把 Git 提交、自动构建、镜像推送、K8s 部署这四件事连成一条自动化的链路?
这就是今天要聊的主题。
二、方案分析:先画一张大图
在动手之前,咱们先把整条链路想清楚。我一开始想的是:能不能让 Jenkins 定时去拉代码?可以,但不够优雅。后来改成了Git Webhook 触发,代码一推就自动干活,这才对味。
最终的架构大图是这样的:
┌─────────────┐ push ┌─────────────┐ │ 开发者本地 │ ────────────> │ GitLab/GitHub│ │ (git push) │ │ 代码仓库 │ └─────────────┘ └──────┬──────┘ │ webhook ▼ ┌─────────────┐ │ Jenkins │ │ CI/CD 引擎 │ └──────┬──────┘ │ 触发 Pipeline ▼ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 拉代码 │ │ 跑测试 │ │ 构建镜像 │ │ (git) │ │ (mvn) │ │(docker) │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ └───────────────┼───────────────┘ ▼ ┌─────────────┐ │ 推送镜像 │ │ Harbor/ │ │ Docker Hub │ └──────┬──────┘ │ ▼ ┌─────────────┐ │ K8s 集群 │ │ 更新部署 │ │(kubectl/helm)│ └──────┬──────┘ │ ▼ ┌─────────────┐ │ 应用对外 │ │ 提供服务 │ └─────────────┘这张图就是咱们今天的"作战地图"。核心链路只有四步:
- Git 提交触发 Webhook
- Jenkins 自动构建 + 测试
- Docker 镜像推送到私有仓库
- K8s 更新 Deployment
接下来,咱们一步一步拆解。
三、实现过程:Step by Step
Step 1:Git Webhook 触发 Jenkins
这一步的目标很简单:代码一提交,Jenkins 就知道该干活了。
在 GitLab 里配置 Webhook 地址:
http://your-jenkins-server:8080/project/my-springboot-app然后在 Jenkins 里创建一个 Pipeline 项目,勾选 “Build when a change is pushed to GitLab”。
Jenkinsfile 的开头长这样:
pipeline{agent any// 关键:定义环境变量,后面到处都要用environment{IMAGE_NAME="harbor.company.com/app/my-service"// 坑1预警:千万别用 latest,后面细说IMAGE_TAG="${BUILD_NUMBER}-${GIT_COMMIT.take(7)}"}stages{stage('Checkout'){steps{// 拉取最新代码git branch:'main',url:'https://gitlab.company.com/app/my-service.git'}}// ... 后面继续}}关键点:IMAGE_TAG我用的是构建号 + Git 短哈希,比如42-a3f8d2e。这样每个镜像都有唯一身份,出了问题一眼就能定位到代码版本。
Step 2:Jenkins 里构建和测试
代码拉下来了,先跑测试。别嫌麻烦,这一步是救命用的。我吃过亏:有次为了赶时间跳过测试,结果把一个有 NPE 的代码推到了线上,半夜被老板打电话骂醒。
stage('Build & Test'){steps{sh''' # Maven 打包,跳过测试?不,咱们要跑测试! mvn clean package -DskipTests=false '''}}测试通过后,开始构建 Docker 镜像。Dockerfile 不用写得多复杂,核心就这几层:
# 第一阶段:用 Maven 镜像编译 FROM maven:3.9-eclipse-temurin-17-alpine AS builder WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn clean package -DskipTests # 第二阶段:用 JRE 镜像运行,体积更小 FROM eclipse-temurin:17-jre-alpine WORKDIR /app # 关键:只复制构建产物,不复制源码 COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]关键点:多阶段构建能大幅减少最终镜像体积。第一阶段用 Maven 镜像(几百 MB),第二阶段只用 JRE 镜像(几十 MB),生产环境只跑第二阶段。
Step 3:推送镜像到 Harbor
镜像构建好了,得放到私有仓库里。咱们用的是 Harbor,企业级首选。
stage('Push Image'){steps{sh""" # 登录 Harbor(凭据提前在 Jenkins 里配好) docker login harbor.company.com -u \${HARBOR_USER} -p \${HARBOR_PASS} # 打标签并推送 docker build -t \${IMAGE_NAME}:\${IMAGE_TAG} . docker push \${IMAGE_NAME}:\${IMAGE_TAG} # 顺手也推一个 latest?不,后面告诉你为什么最好别这么干 """}}Step 4:更新 K8s Deployment
到了最激动人心的一步:让 K8s 用上咱们的新镜像。
这里有两种做法:
做法 A:直接 kubectl set image(简单直接)
stage('Deploy to K8s'){steps{sh""" # 关键:更新 Deployment 的镜像版本 kubectl set image deployment/my-service \ my-service=\${IMAGE_NAME}:\${IMAGE_TAG} \ -n production # 等待滚动更新完成 kubectl rollout status deployment/my-service -n production """}}做法 B:用 Helm 升级(更规范)
stage('Helm Upgrade'){steps{sh""" helm upgrade --install my-service ./helm-chart \ --set image.tag=\${IMAGE_TAG} \ --namespace production """}}我推荐做法 B。因为 Helm 可以把 K8s 的所有配置(Deployment、Service、Ingress、ConfigMap)打包管理,升级和回滚都特别方便。
四、基础设施即代码(IaC)是啥?什么时候用?
链路跑通了,但还有一个问题:Jenkins、Harbor、K8s 集群本身是怎么搭起来的?总不能每次新建环境都手动点鼠标吧?
这时候就要请出IaC(Infrastructure as Code,基础设施即代码)了。
Ansible:配置管理的利器
Ansible 更像一个"远程执行脚本"的工具。你写一份 playbook,它能帮你批量安装 Java、配置 Nginx、启动服务。
# 伪代码:用 Ansible 安装并启动 Docker-hosts:k8s_nodestasks:-name:安装 Dockeryum:name:docker-cestate:present-name:启动 Docker 服务service:name:dockerstate:startedenabled:yes什么时候用 Ansible?
- 服务器已经有了,需要批量配置软件环境
- 部署应用、改配置文件、重启服务
Terraform:云资源的"建筑师"
Terraform 专注的是"从无到有"。你想在阿里云上创建 10 台 ECS、一个 VPC、一个 SLB?Terraform 一行命令搞定。
# 伪代码:用 Terraform 创建阿里云 ECS resource "alicloud_instance" "k8s_node" { count = 3 image_id = "centos_7_9_x64_20G_alibase_20231231.vhd" instance_type = "ecs.c7.xlarge" tags = { Name = "k8s-node-${count.index}" } }什么时候用 Terraform?
- 需要创建云资源(虚拟机、数据库、负载均衡)
- 多环境(开发/测试/生产)保持一致
简单总结:Terraform 负责"造房子",Ansible 负责"装修房子"。两者可以搭配使用。
五、DevOps 文化与 SRE 实践
技术链路搭好了,但 DevOps 不只是工具链,更是一种文化。这里分享几个我们在实践中坚持的 SRE 理念。
1. Secrets 安全管理
K8s 的 Secret 千万别硬编码在 YAML 里!我见过有团队把数据库密码直接写在 Git 仓库的deployment.yaml里,结果代码一开源,密码全网裸奔。
正确做法是:
# 错误示范 ❌env:-name:DB_PASSWORDvalue:"123456"# 千万别这么干!# 正确做法 ✅env:-name:DB_PASSWORDvalueFrom:secretKeyRef:name:db-secret# 提前用 kubectl create secret 创建key:password更进一步,可以用Vault或External Secrets Operator,把密钥存在专门的密钥管理系统里,K8s 只存引用。
2. SLA / SLO / SLI
这些词听着高大上,其实很好理解:
- SLA:对外承诺的服务等级,比如"全年可用性 99.9%"
- SLO:内部目标,比如"接口 P99 延迟 < 200ms"
- SLI:具体指标,比如"错误率 < 0.1%"
说白了,就是先定义清楚"什么叫好",再用监控数据验证是不是真的好了。
3. 故障演练(Chaos Engineering)
咱们团队每个月会搞一次"故障演练":随机杀掉一个 Pod,看看服务能不能自动恢复;或者把某个依赖服务搞挂,看看熔断降级有没有生效。
工具推荐Chaos Mesh,在 K8s 上搞破坏特别方便:
# 伪代码:随机杀掉一个 Pod,测试服务恢复能力apiVersion:chaos-mesh.org/v1alpha1kind:PodChaosmetadata:name:pod-kill-examplespec:action:pod-killmode:oneselector:namespaces:-productionlabelSelectors:app:my-service4. 容量规划
别等到线上卡死了才想着扩容。平时就要关注:
- CPU / 内存使用率趋势
- QPS 和响应延迟
- 数据库连接池是否吃紧
Prometheus + Grafana 这套组合基本成了标配,提前设好告警阈值,防患于未然。
六、踩坑记录:那些年我流过的泪
坑 1:镜像版本管理混乱,回滚找不到北
场景:早期我们图省事,所有镜像都打成latest标签。有一天线上出了问题,我想回滚,结果发现latest已经被新构建覆盖掉了,根本不知道上一个稳定版本是哪个。
教训:
- 永远不要用
latest作为生产环境的镜像标签 - 每个镜像标签必须唯一,推荐
构建号-Git短哈希或语义化版本v1.2.3 - Harbor 里开启镜像保留策略,定期清理过期镜像
坑 2:K8s Secrets 硬编码在 YAML 里,密码泄露
场景:有同事为了方便,直接把数据库密码写在了 Git 仓库的 YAML 文件里。后来仓库权限调整,被实习生误操作公开了,密码直接暴露。
教训:
- 敏感信息绝不进 Git
- 用
kubectl create secret或 Vault 管理密钥 - Git 仓库里只放配置的"骨架",不放"血肉"
坑 3:Jenkins 和 K8s 网络不通,部署失败
场景:Jenkins 跑在公司的内网服务器上,K8s 集群在阿里云上。Pipeline 最后一步kubectl apply死活连不上,报错Unable to connect to the server。
排查过程:
- 先检查 Jenkins 服务器能不能
curl到 K8s API Server —— 不通 - 发现是安全组规则没开 Jenkins IP 的白名单
- 更深层的问题是:Jenkins 直接操作 K8s,耦合太紧
最终方案:
- 在 K8s 集群里部署 Jenkins Agent(用 K8s Plugin),让构建和部署都在集群内部完成
- 或者改用 GitOps 方案(比如 ArgoCD),Jenkins 只负责构建镜像,部署由 ArgoCD 监听 Git 变更自动完成
七、验证与总结
咱们来回顾一下,今天这条链路打通之后,发版变成了什么样:
以前:
- 本地手动打包
- SSH 上服务器
- 上传 jar 包
- 手动改配置
- 重启服务
- 祈祷别出问题
现在:
git push- 去喝杯咖啡
- 收到 Jenkins 通知:部署成功
这就是 CI/CD 流水线的魅力。不是让你更忙,而是让你更闲。
核心知识点回顾
| 环节 | 技术/工具 | 核心作用 |
|---|---|---|
| 代码管理 | Git + GitLab/GitHub | 版本控制 + Webhook 触发 |
| 持续集成 | Jenkins / GitLab CI | 自动构建、测试、打包 |
| 容器化 | Docker | 环境一致性,一次构建到处运行 |
| 镜像仓库 | Harbor | 安全存储和分发镜像 |
| 容器编排 | Kubernetes | 自动化部署、扩缩容、故障恢复 |
| 包管理 | Helm | K8s 应用的标准化打包和发布 |
| 基础设施 | Terraform / Ansible | 环境创建和配置的自动化 |
| 可观测性 | Prometheus + Grafana | 监控、告警、容量规划 |
| GitOps | ArgoCD | 声明式持续部署 |
学习路径建议
如果你也想系统学习 DevOps,我建议按这个顺序来:
- 先学 Git 工作流(Git Flow / Trunk-Based)
- 再学 Docker(镜像、容器、Dockerfile、网络)
- 接着上 K8s(Pod、Deployment、Service、Ingress)
- 然后搭 Jenkins Pipeline(把构建和部署串起来)
- 最后补监控和 GitOps(Prometheus、ArgoCD)
别想着一口吃成胖子,一步一步来,每打通一个环节都是实实在在的进步。
八、写在最后
到这里,咱们的 DevOps 系列就正式收官了。从 Nginx、Git,到 Docker、Jenkins、Prometheus、K8s,最后到今天的 DevOps 全链路,一路走来不容易。
如果你也跟着读到了这里,恭喜你,已经具备了从开发到运维的完整视野。
当然,技术这条路没有终点。今天的方案也不是银弹,可能你团队用的是 GitLab CI 而不是 Jenkins,用的是 AWS 而不是阿里云,用的是 ArgoCD 而不是 Helm —— 都没关系,核心思路是相通的。
最后想问问你:
- 你们团队的发版流程是怎样的?
- 有没有遇到过比我更离谱的踩坑经历?
- 对于 CI/CD 流水线,你有什么更好的实践想分享?
欢迎在评论区交流,咱们一起进步!
