{T}

前言:什么是 CICD 与 为什么要学 CICD

版本说明:本系列基于 Docker 20.10 / Kubernetes 1.23 / CentOS 7 编写。当前环境建议:Docker 27.x、Kubernetes 1.31+、操作系统使用 Ubuntu 24.04 或 Rocky Linux 9(CentOS 7 已于 2024-06 EOL)。CI/CD 核心概念与 K8s 资源编排方式不变,仅安装命令和版本号需对应更新。

什么是 CI/CD

在开发阶段,许多编译工具会将我们的源码编译可使用的文件。例如 vue-cli 的项目会被 webpack 打包编译为浏览器的文件,Java 项目会被编译为 .class/jar 文件以供服务器使用。

但是,开发人员过多关注构建和部署过程是很浪费时间的。以之前古老的的构建部署流程为例子,需要经历以下步骤:

  1. 开发人员将源代码,经过编译、压缩等一系列流程打包为制品(意思为打包后的成品)
  2. 将制品上传到服务器。
  3. 在服务器将编译后的文件,手动可用的容器服务内(例如 Nginx,Tomcat,Apache 等服务)

显而易见,这种流程不仅繁琐,且容易出错,是非常影响开发效率的。开发人员要花一些时间浪费在这上面。那么有没有高效率,简单便捷一些的方式呢?

这就要提到 CI/CD 了。CI 的意思是 持续构建 。负责拉取代码库中的代码后,执行用户预置定义好的操作脚本,通过一系列编译操作构建出一个 制品 ,并将制品推送至到制品库里面。常用工具有 Gitlab CI,Github CI,Jenkins 等。这个环节不参与部署,只负责构建代码,然后保存构建物。构建物被称为 制品,保存制品的地方被称为 “制品库”

CD 则有2层含义: 持续部署(Continuous Deployment)持续交付(Continuous Delivery)持续交付 的概念是:将制品库的制品拿出后,部署在测试环境 / 交付给客户提前测试。 持续部署 则是将制品部署在生产环境。可以进行持续部署的工具也有很多: Ansible 批量部署, Docker 直接推拉镜像等等。当然也包括我们后面要写到的 Kubernetes 集群部署。

为什么要学 CI/CD

了解其用途后,常见的疑问有:

  • 这不是运维干的活吗?
  • 好像和业务代码不相关,那我了解它有何意义?
  • 全是服务器知识,我不了解相关知识怎么学习?

这些疑问很常见。那学习 CI/CD 的意义在哪里?

但是当我通过学习这些知识和在团队中实践这些流程后,我在知识面上得到了很大的扩展。对操作系统,对实际的构建部署,甚至对工程化拥有了全新的认识。甚至可以提出建议,如何更好的优化这些流程。这些都是你可以获得成长和学习的地方。你也可以选择将这部分知识点写入你的简历,作为面试和筛选的加分项。从更高的角度看整个项目的全貌,往往产生思考的维度是和一般的角度不同的。你会成长更快,渐渐地突破思维天花板。

当然,如果你对 Linux 操作系统不是很熟悉,建议先补习下基础的系统安装,操作命令,基础概念等知识(系统推荐 CentOS / Ubuntu ),在本系列中将不会对基础Linux命令有过多解释。当然,如果遇到部分不懂的现场搜索也可以,相信你学起来这部分知识可以更加得心应手。

系列整体架构设计

在开始学习之前,我们先来了解下本系列的整体内容技术架构设计:

上面是一张全景架构图,系列内容和章节将围绕该图展开编写内容。其中不包含单元测试和代码扫描环节,只关注构建和部署环节。

换成文字叙述就是这样的:

  1. 你写完了代码,提交到了 Git 代码库
  2. 随后,代码库配置的 WebHook 钩子或人工手动启动了 Jenkins 的构建流程
  3. Jenkins 启动构建流程。按照你之前配置好的构建脚本,将代码编译成功。
  4. 编译成功后,将编译后的文件打包为 docker 镜像,并将镜像上传到私有镜像库。
  5. 随后,使用 kubectl 指定远程的k8s集群,发送镜像版本更新指令
  6. 远程的k8s集群接收到指令后,去镜像库拉取新镜像
  7. 镜像拉取成功,按照升级策略(滚动升级)进行升级,此时不会停机。
  8. 升级完毕。

服务器搭配方案

学习本系列,动手能力要具备,当然服务器资源也要准备好。这里推荐几种服务器搭配方案用来学习测试使用:

系统建议选用 Ubuntu 24.04 LTS 或 Rocky Linux 9(CentOS 7 已于 2024-06 EOL)

1. 全本地虚拟机 / 全上云

这里所有主机都必须为云服务器/本地虚拟机。要保持统一

配置技术栈类型标签
2核4GJenkins + Nexus + Docker本地虚拟机 / Cloud构建机
2核4GDocker + Kubernetes本地虚拟机 / CloudKubernetes Master
1核1GDocker + Kubernetes本地虚拟机 / CloudKubernetes Node

2. 半云半本地虚拟机

构建机器放本地,要部署的机器放云上面。否则的话构建机找不到要部署的机器
缺点:无法使用 Git 的 Webhook

配置技术栈类型标签
2核4GJenkins + Nexus + Docker本地虚拟机构建机
2核4GDocker + KubernetesCloudKubernetes Master
1核1GDocker + KubernetesCloudKubernetes Node

进入 CD 的世界

在前几章,我们部署了 Docker + Jenkins,并走通了构建镜像的流程,接下来的几章,我们将专注于 CD(持续部署的讲解)。内容包括:

  • 如何安装一套 Kubernetes 集群?
  • 如何在 Kubernetes 集群内部署自己的服务?
  • 如何实现灰度发布和滚动发布?
  • 如何更好地管理自己应用的状态?
  • 如何存储服务内的机密信息?
  • 如何更好的调度你的服务部署?
  • 如何管好你的环境变量?

在后面的几章,我们会全方位地了解到一个项目如何部署,应该怎样部署,如何更好部署。可以更好地支撑我们服务运行和维护。

Kubernetes 污点与容忍:更好地分配集群资源

前言

前面的部分,我们已经可以从工程角度合理地去部署一个应用了。可是场景总是复杂的,有时候还会遇到以下问题:

自动调度集群节点部署很不错。但我其中几台服务器计划只给后端服务准备使用,这要怎么调度呢? > 后端服务依赖的服务器配置都很高,让前端服务也能调度过去显然不合适。如何干预 Pod 部署到指定的其中几个服务器上去呢?.......

这种问题在实际情况中还比较常见的。因为架构设计,前端服务器所需资源低一些是常事。而资源强占总是不合理的。

这时候我们就需要借助 Kubernetes 中的污点与容忍度去实现了

什么是污点?容忍度又是什么?

我们一般说污点,一般指生活中的脏东西。但是在 Kubernetes 中,污点的意义却有所不同。

Kubernetes 中, Pod 被部署到 Node 上面去的规则和逻辑是由 Kubernetes 的调度组件根据 Node 的剩余资源,地位,以及其他规则自动选择调度的。但是有时候在设计架构时,前端和后端往往服务器资源的分配都是不均衡的,甚至有的服务只能让特定的服务器来跑。

在这种情况下,我们选择自动调度是不均衡的,就需要人工去干预匹配选择规则了。这时候,就需要在给 Node 添加一个叫做污点的东西,以确保 Node 不被 Pod 调度到。

当你给 Node 设置一个污点后,除非给 Pod 设置一个相对应的**容忍度,**否则 Pod 才能被调度上去。这也就是污点和容忍的来源。

污点的格式是 key=value,可以自定义自己的内容,就像是一组 Tag 一样。**

给 Node 设置污点

在给 Node 设置污点之前,我们再创建一个新的 deployment 版本,版本是 v3 。并将之前的 pod 名称, service 名称从 v2 也改成 v3 :

shell
cp ./v2.yaml v3.yaml
vim v3.yaml # 里面的Pod名称,service改成v3
kubectl apply -f ./v3.yaml

随后,我们用 kubectl get pods 命令获取Pod列表,用 kubectl describe pod 命令看下 Pod3 的运行详情: 我们看 Node 一栏, k8s 将我们新创建的 Pod 调度部署到了新增加的 Node2 节点上。接下来,我们给 Node2 设置污点,让 Pod 不会调度到 Node2 节点上。

当然,给 Node 设置污点是第一步操作,只有设置了污点 Pod 才不会被调度上去。给 Node 添加污点的命令很简单,我们只需要使用 kubectl taint 命令即可给 Node 设置一个污点:

yaml
kubectl taint nodes [Node_Name] [key]=[value]:NoSchedule

其中,Node_Name 为要添加污点的 node 名称;key 和 value 为一组键值对,代表一组标示标签;NoSchedule 则为不被调度的意思,和它同级别的还有其他的值:PreferNoSchedule 和 NoExecute (后面我们会写到)

我们给 Node3 添加完一个污点后,提示报 node/node2 tainted 代表添加成功: 我们删除掉已经创建的 v3 版本的 Pod ,让 Kubernetes 重建 Pod ,看看新 Pod 还会不会被调度到 node2 上面去:

shell
kubectl delete pod [POD_NAME]
kubectl describe pod [POD_NAME]

这时候我们看到, Pod 被调度到了 Node1 上面去。因为 Node2 添加了污点,不会被调度到 Node2 上面去。此时污点生效。

给 Pod 设置容忍度

可以看到,给 Node 添加完污点后,新创建的 Pod 都不会调度到添加了污点的 Node 上面。所以我们想让 Pod 被调度过去,需要在 Pod 一侧添加相同的容忍度才能被调度到。

我们编辑 front-v3 的 deployment 配置文件,在 template.spec 下添加以下字段:

yaml
tolerations:
- key: "KEY"
  operator: "Equal"
  value: "VALUE"
  effect: "NoSchedule"

字段的含义是在给 Pod 设置一组容忍度,以匹配对应的 Node 的污点。 key 和 value 是你配置 Node 污点的 key 和 valueeffect 是 Node 污点的调度效果,和 Node 的设置项也是匹配的。

operator 是运算符,equal 代表只有 key 和 value 相等才算数。当然也可以配置 exists ,代表只要 key 存在就匹配,不需要校验 value 的值

修改保存后,我们使用 kubectl apply -f 命令让配置项生效,接着删除已存在的 Pod,查看下新Pod的调度结果: 可以看到,在容忍度的作用下, Pod 重新被调度到了 Node2 节点上。

修改/删除 Node 的污点

修改污点的方式也很简单,像创建一个污点一样,我们依然使用 kubectl taint 命令就可以完成修改:

yaml
kubectl taint nodes [Node_Name] [key]=[value]:NoSchedule --overwrite

这里的 key 依然是要修改的 key 值,只需要对 value 和作用的值进行修改即可。后面添加参数 --overwrite 代表覆盖之前的数据。 ** 删除污点也依然很简单。我们只需要加个 - 号就可以删除污点:

yaml
kubectl taint nodes [Node_Name] [key]-

当提示: node/[NODE_NAME] untainted 代表删除成功。

总结:结束语

知识回顾

本系列共 15 章,覆盖了从零搭建一套完整 CI/CD 流程的全部环节:

阶段章节核心内容
CI 持续构建01-04Docker + Jenkins 环境搭建、镜像构建、私有镜像库(Nexus)
CD 持续部署05-08Kubernetes 集群搭建、应用部署、灰度/滚动发布策略
K8s 资源管理09-13探针、Secret、DNS、ConfigMap、污点与容忍
综合实战14前后端分离项目完整 CI/CD 流水线

完整流水线架构

code
Git Push → Webhook 触发 → Jenkins 构建 → Docker 镜像打包 → 推送至 Nexus → kubectl 更新 K8s Deployment → 滚动升级完成

进阶方向

本系列聚焦于 CI/CD 的核心流程,以下方向值得继续深入:

  • 高可用集群:多 Master 节点、etcd 集群、负载均衡
  • Helm:Kubernetes 包管理器,简化复杂应用的部署和版本管理
  • GitOps:ArgoCD / Flux,以 Git 仓库为唯一事实来源驱动部署
  • 服务网格:Istio / Linkerd,细粒度流量管理和可观测性
  • 监控告警:Prometheus + Grafana + AlertManager 全链路监控
  • 安全加固:RBAC 权限控制、NetworkPolicy 网络隔离、镜像漏洞扫描

总结:结束语

结束语

如果你看到这段文字,说明你已经打卡完毕本系列!

本系列15章节,可实践的章节高达13章。如果你是0基础操作的话,想必需要花起码1个月左右的时间才能学完。

在这1个月左右的时间里,你可能在安装部署时出现了问题;可能在学习知识时遇到了困惑;可能在遇到新概念时无法理解头皮发麻;但,都一路走过来了,不是吗?

当然,写本系列的我和学习本系列的你同样辛苦。本系列在2020年2月份立项,于疫情居家办公时开始编写,历时半年时间才确定初稿。里面很大一部分知识都来自于我和我的团队实践。

为了写好本系列,虚拟机跑了N个,凌晨赶稿的时刻常有。但为了给你一份靠谱的学习资料,我知道我的付出是值得的。

在11月份我受邀参加早早聊演讲时,有位同学在直播后问了我如何学习cicd。当时我找遍全网资料,也没有一份成体系的,好入门的,较全的资料出现。可是,现在有了。

当然,本系列由于时间和其他原因,内容相对有限。对于 kubernetes Jenkins docker 的许多许多知识点都没有写到讲到。例如如何去创建一套高可用架构,如何使用 helm 安装应用等等。但是,这也刚好是你要去学习和探索的部分。我经常对许多朋友说,千万不能浅尝辄止。一定要继续学下去

当然,本系列也许有疏漏和错误的地方,欢迎大家指出,我个人不胜感激。也欢迎大家入群和我一起交流