本文根据华相在「Kubernetes & Cloud Native Meetup- 广州站」现场演讲内容整理,已授权 InfoQ 转载。
大家好, 我是来自阿里云容器做事团队的华相。首先大略阐明一下作甚 Kubernetes 来帮助大家理解。Kuberentes 是一个生产可用的容器编排系统。Kuberentes 一方面在集群中把所有 Node 资源做一个资源池,然后它调度的单元是 Pod,当然 Pod 里面可以有多个容器。 就像一个人左手抓着 ECS 资源或打算资源,右手抓容器,然后把它们两个匹配起来,这样它就可以作为一个容器的编排系统。
而 Cloudnative 这个观点现在会常常被大家提起,很多人迷惑 Cloudnative 又与 Kuberentes 有什么关联?我们又该如何判断一个运用是 Cloudnative 呢?我认为有以下三个判断标准:

第一,它能够给资源做池化;
第二,运用可以快速接入池的网络。在 Kuberentes 里面有一层自己的独立网络,然后只须要知道我要去访问哪个做事名就可以,便是各种做事创造的一些功能,它可以通过 service mesh 去做一个快速地访问;
第三是有故障转移功能,如果一个池子里面有一台主机,或者某一个节点 down 掉了,然后全体运用就不可用了,这肯定不算是 Cloudnative 的运用。
比较这三点就可以创造 Kuberentes 做的非常好。首先我们看一个资源池的观点,Kuberentes 一个大的集群便是一个资源池,我们再也不用去关心说我这个运用要跑在哪台主机上了,我只要把我们支配的 yaml 文件往 Kuberentes 上发布就可以了,它会自动做这些调度,并且它可以快速地接入我们全体运用的网络,然后故障转移也是自动。接下来我就来分享如何基于 Kuberentes 实现一个弹性的 CI/CD 系统。
CI/CD 的现状首先理解一下 CI/CD 的现状。CI/CD 这个观点实际上已经提出很多年了,但是随着技能地演进和新工具地不断推出,它在全体流程和实现办法上逐渐丰富。而我们常日最早打仗 CI/CD 便是代码提交,随之触发一个事宜,然后在某个 CI/CD 系统上做自动构建。
下图可以反响目前 CI/CD 的现状:
它中间包含了很多流程,从代码提交开始触发一个事宜,然后它可以中间做一层 Build ,首先通 maven 做一个 build,然后做一个单元测试,然后做代码规范扫描,然后支配。 接下来,做一个端到真个 UI 方面的一层测试,这是一个 UI 的自动测试工具。
然后做了一个压测(性能上的压测),这只是在开拓测试环境做了一层处理。然后它可以在 QA 环境,终极延伸到 UAT 环境,全体 pipeline 是一个非常长的过程。CI/CD 的利用范围很广,它可以把全体 IT 从代码编写提交到代码仓库为出发点,从这儿开始今后做,各个步骤都可以纳入 CI/CD 的范围。但是随着它的管控范围越来越多,全体流程越来越繁芜,它对资源的占用也是越来越高。
大家如果理解 C++ 的话,就会知道 C++ 以前是一门著名的、构建韶光特殊长的措辞。Go 措辞的作者之一,也是 C 措辞的作者, 谈及当时写 Go 完备是由于不想写一次代码,编译那么久。以是,Go 措辞刚涌现时的一大上风便是编译韶光非常短。Go 的编译韶光确实非常短。
我曾用一个 I5 的条记本编译 Kubernetes 源码做一次完全地构建,大概编译了 45 分钟旁边,这是一个非常漫长的过程。以是,即便已经对编译过程做了很大优化和改进,只要项目大了之后,只是构建阶段就已经很长了,更不要说包括后面的一些自动化测试了。包括所有东西之后,CI/CD 流程对资源地花费和占用的韶光是非常大的。
CI/CD 工具的选择接下来,我们看一下 CI/CD 这些工具的选择和他们的发展。首先最老牌切实其实定是 Jenkins。实际上在容器技能兴起之前,CI/CD 切实其实约即是 Jenkins。但是在涌现容器技能之后,很多新生 CI/CD 的工具也应运而生,比如 Drone 工具,它是一个完备基于容器来实现的 CI/CD 工具。它与容器地结合地非常好,并且它的构建过程是完备在容器中实现的。
其余还有 Gitlab CI,它紧张特点是与 Gitlab 代码管理工具结合地比较好。Jenkins 2.0 开始引入 pipeline as code 特性,pipeline as code 可以帮我们自动天生一个 jenkins file。
在 Jenkins 1.0 时,如果我们想要配置一条流水线,须要先登录 Jenkins 建一个项目,然后在里面写一些 shell。这样虽然能达到同样的效果,但是它有一个最大的弊端,便是可复制性和迁移性不好。而且它与 Devops 有天然地割裂,比如一样平常是由运维职员来管理 Jenkins 系统,开拓职员编写代码,但是这个代码怎么去构建,发布到哪里,开拓职员完备不知道。这就造成了开拓和运维地割裂。但 pipeline as code 办法涌现了之后,jenkins file 与代码源码可以放在同样的仓库里面。
首先它有一个非常大的好处是发布的流程也可以纳入版本管理,这样对一个缺点就可追溯。这是一个非常大地改动,但是实际上我们在与客户沟通中创造,虽然很多人在 Jenkins 上都已经升到 2.0 系列了,但是它们的用法还是完备在 1.0 系列,很多用户都没有把 jenkins file 这种办法用起来。其余一个便是对容器地支持,大概 2016 年旁边,那时对容器的支持是非常弱的,在容器里面运行 Jenkins,同时构建的产物也是 Docker 会非常麻烦。
但是, Drone 对容器的支持力度就非常好。首先它完备用 Docker 办法来运行,便是说你的构建环境也在一个容器里,你须要构建一个 Docker build 镜像,然后在推送出去的时候,它也在容器里面运行,然后它须要一个 privilege 权限。它这种办法有几个特殊好的地方,首先它不会对宿主机产生任何残留,比如说你这个容器一销毁,构建中产生的一些中间文件完备都随着销毁了,但是你如果用 Jenkins 的话,随着用的韶光越来越久会沉淀出很多临时文件,它占的空间会越来越大。你须要定期做一些清理,而且清理过程中你又不能直接一键清空,以是这是很麻烦的过程。
然后插件的管理 Jenkins 还有一个特殊让人头疼的地方,便是它的插件升级。首先你在 Jenkins 登录进去,然后就做插件升级。如果说我想临时在一个新的环境里面,起一个 Jenkins 先测试一下或做一些调试可能每新建一个环境,都须要把这些插件升级一次。而且刚才我们说的在 Jenkins 里面做的所有配置,也都须要重新配置一遍,这是一个非常繁琐的一个过程。
但是 Drone 这个工具它有一个特殊好的地方,便是所有的插件都是 Docker 容器,比如说你在 pipeline 里用这个插件,你只要声明用这个插件就可以了,你不用去自己管理把插件下载到哪里,然后怎么安装,它这个统统都是全自动,只要说你网络能把插件容器镜像访问到就可以了,这非常便利。
然后关于生态的构建,Jenkins 的最大的上风便是它的插件非常多,便是你想用的各种东西都有,而且它根本的底座非常好,你的插件可以实现的能力非常的强。比如说 pipeline 便是这种办法,它从 1.0 到 2.0 虽然有了,但是它完备是通过插件来实现的。但是现在 Jenkins 的发展又开始有点第二春的觉得。他开始对 Kuberentes 的支持力度明显的加大了很多,首先从 JenkinsX 开始,它领悟了一些 Kuberentes 生态干系的一些工具,比如 Harbor、Helm 它可以非常便利地在 Kuberentes 集群上来做一些构建,并且把一些做做事的固化的一些编排文件放到 Helm 里面。
其余,现在它有一个新的子项目叫 config as code,也便是说他把所有 Jenkin 里面做了一些配置,都可以输出成一个 code 的形式,便是对全体 Jenkins 的迁移,或者说复制都是一个很便利的改进。
讲了那么多,实际上末了我们选择的东西还是 Jenkins,由于最主要的是生态的构建,他们已经很好了。本日我们要讲的在做一个弹性在 CI/CD 这个 Jenkins 上已经有这个插件了,但是在 Drone 的社区里面,有人提这个事,但是现在还没有看到。
CI/CD 的系统业务场景然后我们看一下 CI/CD 它的系统的业务场景,它有一个比较范例的场景与特点,首先它面向开拓职员,这是比较少见的,由于开拓职员一样平常都比较挑剔一点。以是你假如这个别系做的不足稳健了,或者说相应韶光比较长一点的话,会被常常吐槽。
然后便是有时效性哀求,由于我们代码写完之后,往上提交,我们都不肯望在这个代码的构建中一贯排队,我们希望立时就开始进行构建,并且资源足够丰富。其余一个便是它的资源占用的波峰波谷是非常明显的。就由于开拓职员不可能时时刻刻都在提交代码,有的人可能一天提交几次,有的人会提交很多次。
由于我之前看过有一个分享,有一个人画了一条反响自家公司构建任务的曲线。他们公司大概是每天下午三、四点的时候代码提交量最高,其他韶光都比较平缓。这解释他们公司三、四点的时候,程序员提交代码开始划水了。然后随着 CI/CD 资源地需求越来越高,构建集群是一个必须要做的一件事情。便是提高负载能力,缩短任务的排队韶光。当然真正的集群有一个让人以为很不太好的地方,便是它的 Master 实际上只有一个,当然这个也可以通过插件来做改进。
但这不是本日我们要讲的重点,由于很多时候 Master 短暂的不可用,然后能快速规复也是可以接管的。然后其余它须要来知足各种构建场景。一个公司里面可能用到很多的开拓环境,就像虽然我们都用 Java,但可能会有 1.5、1.6、1.7 的差别。 如果不用 Java,很可能用很多其他措辞如 Python、Go、NodeJS 等须要很多的构建环境。而且如果我们不引入容器的话,这些构建环境不能重用,可能一台主机只能给 PHP 在用。
容器可以给我们 CI/CD 系统来注入新的能力,便是环境隔离的能力。我们可以通过 Kubernetes 来为 CI/CD 系统注入更多的能力,然后抵牾点就涌现了。开拓职员总是希望 CI/CD 系统能够快速地相应代码提交的一个事宜,但是每个公司资源都不可能是无限的。由于就像上面提到的,如果每天下午三、四点的时候是一个代码提交的高峰,这个时候可能须要 30 或 40 台机器才能知足构建的任务。但是我不可能每天就开着 30 或 40 台机器在这里,就为了每天下午三、四点,可能会构建一、两个小时。
Kubernetes 可以为 jenkins 注入新的能力,让 CI/CD 系统实现弹性的能力。我们期望的目标是什么呢?有构建任务的时候,可以自动为我们资源增加新的机器也好,或增加新的运打算能力也好,反正便是当我须要的时候,可以帮我自动地做一个资源扩展,但是同时也在我不须要的时候,可以自动把这些资源开释掉。
我们期望的目标便是这样,Kuberentes 就可以为 Jenkins 来做这样的能力。Kuberentes 作为一个容器编排的系统,它所能供应的能力,它可以快速地弹出一些新的实例,并且把它们自动调度到空闲的机器上,做一个资源池,在资源池里面做一个调度,并且他实行完任务之后,它可以做一个回收。而且如果把这 Jenkins Master 也支配在 Kuberentes 之上,它可以对 Master 做一个故障转移,便是说如果我们系统可以容忍的话,Master 就算挂了,我可以快速把它调到其余一台机器上,这个相应韶光不会很长。
Kubernetes-piugin这里面也是用一个插件来实现,这个插件名字比较直接叫 Kuberentes-plugin,它这个插件所能供应的能力便是说,他直接管理一个 Kuberentes 集群,它在 Jenkins 里面安装之后,它可以监听 Jenkins 的构建任务。有构建任务,在等待资源的时候,它就可以向 Kuberenetes 去申请一个新的资源,申请一个新的 Pod 去做自动地构建完之后,它就会自动的清理。
先大略先容一下它的能力,由于这个插件安装完之后,它对 pipeline 的语法也有一个改造, 一会我们来看一下实例。但是就算到了这一步,还是弗成的。首先,Kuberentes 的集群方案还是一个问题。比说我有个集群有 30 个节点,真正的 master 支配在这上面,然后装了那些插件,做了一个管理之后,我们可以创造来了一个新的任务,它就起一个新的 Pod,把这个构建任务给实行制订完。
实行完之后,这 Pod 自动销毁不占集群的资源。 平时我们可以在这集群上做一些别的任务,但这个究竟还是有一点不好,便是我们这个集群到底方案多大,并且这个集群我们平时不做构建任务的时候,可以在上面做一些别的任务。但是如果正在做任务,溘然来了一些构建任务,它可能会涌现资源的冲突问题。
Kubernetes Autoscaler总的来说,还是有一些不完美的地方,那么我们可以利用 Kuberentes 一些比较没那么常见的特性,来办理我们刚才说的这个问题。这两个东西一个是叫 Autoscaler,一个叫 Virtual node。我们先看一下 Autoscaler,Autoscaler 是一个 Kubernetes 官方的一个组件。在 Kuberentes 的 group 下面支持三种能力:
Cluster Autoscaler,可以对集群节点做自动伸缩;Vertical Pod Autoscaler,对集群的 Pod 竖直方向的资源伸缩。由于 Kuberentes 本身里面就带着 HPA 可以做水平方向的 Pod 伸缩、节点数量的伸缩;这个特性还不是生产可用的特性;Addone Resizer,是 Kuberentes 上那些 addone 比如说 Ingress Controler、 DNS 可以根据 Node 的数量来对资源的分配做调度。Cluster autoscaler
我要讲的是 Cluster autoscaler,是对集群 node 节点数量做一个扩缩容。 首先我们看一下,这个是在阿里云我们容器做事上所实现的 Autoscaler 的一个办法。我们看一下这个图,这个是 HPA 和 Autoscler 做结合利用的一个场景。
HPA 监听监控的事宜时创造资源利用率上升到一定程度了之后,HPA 会自动关照 workload,来弹出一个新的 Pod,弹出一个新的 Pod,可能这时候集群资源就已经不足了,以是这个 Pod 可能就会 pending 在这里。它就会触发 Autoscaler 的事宜,Autoscaler 就会根据我们之前配置好的 ESS 这个模板来去弹出来一台新的 Node,然后自动地把 Node 加入到我们集群里面。 它是利用了 ESS 定制的模板功能,并且它可以支持多种的 Node 实例的类型,可以支持普通实例,支持 gpu,还有抢占式实例。
Virtual node
然后第二种是 Virtual node,Virtual node 实现办法是基于微软开源的 Virtual Kubelet 这个项目。它做了一个虚拟的 Kubelet,然后向 Kubernetes 集群里面去注册上面。但如果不太好理解的话,可以想象一下 MySQL proxy ,然后他把自己伪装成一个 MySQL server,然后后端可能管理着非常多的 MySQL server,并且它可以帮你自动的做一些 SQL 查询的路由或者拼接。
Virtual kubelet 做的也是类似的事情,便是说它本身向着 Kubernetes 注册说我是一个节点,但实际上它后端管理的可能是全体公有云上的非常多的资源,他可能对接公有云的一些 ECI 或者说对接的这些 VPC,这是一个它的大体的示意图。
在阿里云上他们对接的是阿里云的 ECI 做一个弹性的容器实例,它相应韶光就非常快,由于它不须要把 Node 去加入到集群里面,它是大概能够到一分钟一百个 Pod 旁边这种性能。而且我们可以在 Pod 上声明这种资源的利用情形,这是一个非常快的相应速率韶光。
然后刚才说我们利用这两种办法,就可以对我们 CI/CD 弹性的系统做出新的改造,我们不用非常早方案好我们集群的规模,我们可以让集群规模在须要的时候自动的做一些伸缩的动作。但是你做了这些动作之后,我们做了这些把真正的放在容器里面的这些动作之后,引入了一些新的门槛:docker-outside-of-docker 和 docker in docker。
我们在 Docker 中运行 Jenkins 时,常日有两种办法,一个是把宿主机的 docker.sock 挂载到容器里面,让 Jenkins 通过这个文件来和本机的 docker daemon 做一个通信,然后它在这里面做 docker build 构建镜像,或者把这些镜像 push 到远程的仓库里面,以是它中间产生的所有镜像都会堆积在本机上,这是一个问题。
在一些 serverless 的那些场景上利用,它就会有一些限定,由于 serverless 本身就不许可把 socket 文件给挂在进去。其余一个便是 docker in docker 这种办法,它的优点就在于在容器里面它启动一个新的 Docker daemon,它所有的中间产物、构建产物随着容器都一起销毁,但是它有问题,它便是须要 privilege 的权限。
很多时候我们是只管即便不要用它。其余一个便是说你做 docker build 的时候能在宿主机上做的时候,它如果有已经有镜像了,它会直接就利用这个镜像,但是你用 docker in docker 这种办法来利用的,它每次都会重新拉进项,拉镜像也是须要一定韶光,这个取决于我们各个利用场景来判断。
新的构建工具——Kaniko这时又引入了一个谷歌开源的新工具——Kaniko。它做的东西是 docker in docker 的办法。它有一个非常大的好处便是不依赖 Docker,而且以是它不须要 privilege 权限就可以在容器里面用用户态的模式,来完备构建 docker image。用户态实行 Dockerfile 的命令,它把这个镜像完备构建出来。
这算是一个比较期望的弹性的 CI/CD 系统。然后这个时候便是说从真正的节点到底层的打算资源全部是弹性扩缩的,而且知足交付的需求,可以非常风雅化地管理我们的资源。
Demo 演示然后我们可以看一下 Demo 演示:
https://github.com/hymian/webdemo 这里是我准备好的一个例子,重点在这个 Jenkinsfile 文件,里面定义了 agent 的 pod template,包含两个容器,一个用来做 golang 的 build,一个用来做 image 的 build。
然后我们现在构建它。开始构建了,刚开始的,由于是现在我们在这环境里面只有一个,只有一个 master,以是他便是没有不会有构建节点。大家可以看到,它现在新启动了一个 Pod,这个 Pod 是作为节点加进来的,但是由于我在这个 Pod 模板里面定义了一个 label,以是它没有这个节点,以是它 Pod 状态是 pending 的。以是我们在构建日志里面显示的这个是 agent 节点是离线的。
但是我们在这个集群里面定义了一个弹性伸缩的一个东西,当没有节点的时候,它会自动做一个新节点分配加入,可以看到有一个节点正在加入,这个我就可以稍等一下。便是说这段韶光可能会有个一分钟两分钟的韶光。
节点不足的时候,它可以自动扩容然后加入集群,这个要轻微等一下。由于这个韶光会轻微久一点,是由于首先它触发到我这个扩容,它有一个轮询的韶光差。全体过程下来可能有三分钟旁边。我看一下我这个做事器,这个做事器刚才是三个,现在这个做事器是刚刚加入了,这是抢占,这是我刚刚加入的一个东西。
这个是非常,是由于这个节点正在向集群加入,以是它显示是非常,这是我们从命令行看一下,好,已经是四个节点了,加了一个节点,这时候我们看 Pod,这时候在 agent 正在创建,这时候大家可能有一个小的细节,大家可以看一下,便是 0/3 是显示 Pod,它有三个容器,但是我刚才在这个里面定义的,它实际上是 Pod 里面只有两个容器,这便是我们刚才 PPT 上写的一个地方。
JNLP 那个容器,是 plugin 自动注入的一个容器,它通过这个容器实时的向 master 申报请示构建的一个中间的状态,我把它的日志给发送出去。这个是 agent 的节点在初始化的一个过程一个事情这时候 slave 节点已经在运行了。我这边已经输出完了,构建完成。我的分享内容就这些,感激大家。
作者简介
华相 阿里巴巴办理方案架构师 关注业务容器化,Kubernetes 管理,DevOps 实践等领域。