13

DockOne微信分享(二六八):Kubernetes应用持续交付的探索与实践

 3 years ago
source link: http://dockone.io/article/10671
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【编者的话】越来越多的企业开始采用微服务和容器技术来开发和运行自己的数字化解决方案,而Kubernetes作为容器编排的首选平台正逐渐成为企业必不可少的基础架构。在这种背景下,如何高效地交付Kubernetes上的应用也就成为了一个重要的主题。本文主要介绍我们在持续交付Kubernetes应用上的一些探索与实践。

背景介绍

应用架构

刚开始数字化转型的企业由于没有什么遗留的包袱,基本上都会直接选择微服务架构,仅管服务的数量还不是很多,而技术栈基本上也是以Java和Spring Boot的组合为主。每个应用(服务)都有自己独立的Git仓库,通过持续集成流水线最终生成Docker镜像,再通过部署流水线部署并运行在基于Kubernetes的不同环境中。

应用的部署脚本

我们选择Helm来定义模板化的部署资源文件。Helm的模板机制可以让我们把针对不同环境的配置参数化,同时Helm也提供了类似于编程语言的条件和循环以及类似于Linux的管道等功能,可以用来定义更复杂的部署资源文件产生的逻辑。

运行环境的规划

按照功能的不同,逻辑上把环境分成开发、测试、集成、预生产和生产五套环境。开发环境是面向开发人员的,通过持续集成流水线生成的Docker镜像会被自动部署在这个环境中,并执行一些重要的冒烟测试;开发环境成功部署后会自动触发测试环境的部署并执行更全面的自动化测试;集成环境的部署由测试工程师选择性的触发,用来发布通过测试环境且相对比较稳定的版本,供其它服务调用;预生产环境最接近生产环境,是应用上生产环境的最后一道关卡,通常会做一些性能和动态安全测试。物理上,开发、测试和集成环境运行在一个Kubernetes集群上,通过不同的命名空间隔离,而预生产和生产环境分别运行在不同的Kubernetes集群上,这两个集群的资源和配置基本上要保持一致,以保证通过预生产环境的应用,能够顺利的发布到生产环境。

CI/CD流水线工具

选择Jenkins作为CI/CD流水线的建设工具。Jenkins是目前开源领域比较流行和成熟的一款CI/CD工具。随着Pipeline2.0的引进,可以通过编写代码的方式来创建CI/CD流水线,极大地提高了CI/CD流水线的开发效率,同时CI/CD流水线的代码也可以和应用代码一样在Git仓库中做版本管理。

持续交付和部署模式介绍

我们的持续交付和部署的模式已经经历了三个版本的演进,未来仍然会持续的改进。下面,我将逐一介绍这三个版本。

仅使用Helm命令行的推模型

持续交付架构图:

aENb2in.png!mobile

这一版中,应用代码和部署资源文件(Helm模板文件)被放在同一个Git仓库里(见下图代码仓库的结构)。无论是应用代码的变更还是部署资源文件的变更都会触发CI流水线构建出新的Docker镜像和Helm Chart,接着CI流水线会依次自动触发部署流水线部署最新的应用到开发和测试环境中。CI流水线包含了这么几个阶段:代码构建、单元测试、静态代码质量检测(SonarQube Scan)、编译器警告(Compiler Warning)检测、无用和冗余代码(Dead Code)检测、编码标准(Code Standard)检测、打包和上传。部署流水线可以被多个应用共享,除了自动部署应用外,还会根据不同的环境执行对应的自动化测试。其它高阶环境的部署则需要相关负责人确认后手动触发部署流水线并输入要部署的应用和版本。

代码仓库的结构:

bmMV7fa.png!mobile

在深入使用这一版本的过程中,我们也逐渐发现了一些问题:

  • 安全问题,为了实现推模型,每个Jenkins agent(Docker容器或VM)需要安装Helm命令行工具,并配置kubeconfig设置连接Kubernetes集群的账号,而且这个账号需要相对较高的权限,这样才能对集群中的资源进行增、删、改、查的操作,这就使得Jenkins成为了Kubernetes集群的一个很大的攻击面。一个恶意的Jenkins Job可以任意操作集群,另外,如果Jenkins agent镜像泄露的话,它也会被用作跳板来访问和攻击集群。Jenkins的Kubernetes持续部署 插件 可以将Kubernetes账号信息保存在Jenkins master中,只有在构建时才会通过ssh动态地从Jenkins master拉取账号信息,这样避免了因Jenkins agent镜像泄露而造成的风险,但还是无法避免恶意Job的⻛险。这个插件本身也有局限性,比如它不支持Helm部署,只支持部署Kubernetes的基本资源文件等等。
  • 非不可变更部署(Non Immutable Deployment),这种推模型在部署完后,就不会持续监测Kubernetes集群中实际运行的资源是否与之前部署的版本一致。如果有人手动修改了Kubernetes集群中某个应用的资源,出现了问题,有时除了本人还真需要花点时间来定位。这种情况经常出现在开发环境,因为开发环境是面向开发人员的,所以我们是允许开发人员访问dev命名空间。
  • 重复的部署资源文件,这一版中,我们是把Helm的模板文件和代码放在一个仓库里面,每次构建时都会打包出新版本的Helm Chart,其实也就是将Helm的版本和引用的Docker镜像版本更改成当前的版本。但是为了添加一个新的部署逻辑,比如添加Istio的资源文件以支持Istio服务网格,我们需要往多个代码仓库里添加Helm模板文件,工作量大不说,重复的手工操作也很容易出错。在版本2时,我们改变了策略,提取出了通用的Helm资源模板文件。

基于FluxCD和Helm Operator的拉模型

持续交付架构图:

RZrU7nj.png!mobile

FluxCD 是一款开源的实现GitOps的工具,其核心思想是将所有部署资源文件保存在Git仓库中作为唯一事实来源,FluxCD持续监听这一事实来源并将其同步到Kubernetes集群中,这就解决了我们版本1中遇到的第2个问题,即非不可变更部署。由于我们是利用Helm模板文件作为我们的部署资源文件,所以需要安装FluxCD的另一个工具Helm Operator。从上面的持续交付架构可以看出,FluxCD和Helm Operator是部署在目标Kubernetes集群中的,负责同步Git仓库中的部署资源文件。而CI流水线和部署流水线只需往Git仓库中提交变更来声明部署需求,因此在Jenkins和Agent中不需要存储Kubernetes集群的账号信息,降低了安全⻛险。

为了解决Helm模板文件在不同的应用代码仓库中重复存在的问题,我们抽取出了一个通用的Helm模板并将其打包成一个通用的Helm Chart供每个具体应用的部署资源文件引用。这个通用的Helm Chart也有自己独立的流水线以保证持续的缺陷修复和新功能添加。同时,为了适应FluxCD的工作方式,将产品的部署资源抽取到一个单独的Git仓库中,具体的示例结构如下:

Chart.yaml描述了当前应用的版本信息,以及所使用的通用Helm Chart的版本,示例代码如下:

apiVersion: v2 

name: service2 

version: 8.0.9 

appVersion: 8.0.9 

dependencies: 

- name: common-chart 

version: 1.0.1 

repository: https://xxxx/charts 

values.yaml设置了针对具体环境的参数值,示例代码如下:

common-chart: 

image: 

repository: docker-registry.local/service2 

tag: 8.0.9 

pullPolicy: IfNotPresent 

ingress: 

enabled: true 

annotations: 

kubernetes.io/ingress.class: nginx 

kubernetes.io/tls-acme: "true" 

nginx.ingress.kubernetes.io/rewrite-target: /$2 

hosts: [] 

paths: 

- /service2/dev(/|$)(.*) 

由于FluxCD只能绑定一个Git仓库,因此所有项目的部署资源文件都会放在这个仓库中。如上图所示,dev目录下既包含了project1,也包含了project2的部署资源文件。

随着FluxCD这一GitOps工具的引入,我们的持续交付由面向过程的模式转变为面向资源以Git为中心的声明式的模式。但是在使用过程中也存在着一些问题:

  • FluxCD的部署不是实时的 ,FluxCD是每隔一定的时间(可以设置)扫描一次Git仓库,如果有新的版本,就会将其同步到Kubernetes集群中。为了能够实时地检测部署是否成功,需要每个应用提供一个健康检查接口用来返回当前版本是否运行正常,在部署流水线中持续访问这个接口直到获取部署成功的状态,或者超时的错误状态。FluxCD提供了一个命令行工具fluxctl,通过命令“fluxctl sync”可以实时触发FluxCD去同步,也就是推模型,而不必等待FluxCD的轮询,但是这就需要在客户端配置 kubeconfig存储集群的账号来访问Kubernetes集群。
  • 无法实时方便地检查最新Pod的日志,对于开发人员来说,当收到部署流水线失败的通知后,如果能够通过部署流水线快速看到最新Pod的运行日志,是非常有助于他们快速定位和修复问题的,但是FluxCD并没有提供这样的便捷接口。
  • 各个应用的部署资源文件相互干扰,由于所有应用的部署资源文件放在一个Git仓库中,当某个应用的部署资源文件出现问题,比如语法有错,整个同步过程会失败,其它应用的最新资源就无法同步到Kubernetes集群中。

以上的问题促使我们对另一个GitOps工具ArgoCD进行了研究和尝试,这也是我们第三个版本的持续交付模式。

基于ArgoCD的推拉结合的模型

持续交付架构图:

6NNJZr.png!mobile

ArgoCD 作为GitOps工具,其核心功能和 FluxCD相同,通过间隔扫描Git仓库并同步最新的部署资源到Kubernetes集群中完成部署,但是相比于FluxCD,ArgoCD提供了更多的功能,而这些功能为我们构建基于GitOps的安全的持续交付流水线提供了更多的便利。

以下是ArgoCD的一些功能:

ArgoCD不需要在每个目标集群中部署,它可以部署在一个独立的环境中(VM 或者Kubernetes集群)作为统一的Kubernetes应用部署平台。所有目标集群可以注册进ArgoCD,注册粒度可以是命名空间级别,也可以是整个集群级别。

ArgoCD有项目(Project)和应用(Application)的概念,应用属于某个项目,可以做到应用级别的隔离。一个ArgoCD项目可以代表一个具体的项目,比如电商网站,一个ArgoCD应用(Application)可以代表某个实际的应用在某个集群或命名空间里面的部署,这样可以实现应用之间的部署资源相互隔离避免部署时相互干扰。ArgoCD项目可以设置黑白名单来限制项目中的应用能够访问的Git仓库和集群或者集群的命名空间,这样即使应用被恶意触发,它的部署行为也是可控的;

ArgoCD对外提供了两个接口:Web和基于gRPC的API接口。Web接口可视 化了部署状态,DevOps工程师和开发工程师通过这个Web接口可以方便地查看应用的资源结构,资源同步状态和Pod日志等。下面是通过ArgoCD Web接口可以看到的一些信息:

部署资源在Kubernetes集群中的静态结构:

naummqa.png!mobile

应用的网络流向图:

a2yeAzr.png!mobile

可以看到网络流量从负载均衡器进入,经过Ingress和Service的转发到达实际的工作负载Pod。

查看Pod的日志:

q63mIbm.png!mobile

ArgoCD的gRPC接口提供了编程的方式管理ArgoCD。ArgoCD也提供了一个命令行工具ArgoCD,这个命令通过gRPC接口管理ArgoCD的应用。命令行工具ArgoCD和Fluxctl不同的是它不直接和Kubernetes集群通信,所以不需要设置 Kuberetes账号,它只是通过gRPC接口给ArgoCD发送指令,由ArgoCD完成对 Kubernetes集群的操作。所以在我们的部署流水线里集成ArgoCD,主动地触发 ArgoCD去同步Git仓库来实现推模型;

ArgoCD支持基于RBAC的用户权限管理,我们可以灵活地定义不同的角色和权限。比如,以项目为粒度定义出开发、DevOps、管理员等角色,而开发角色可以是只读权限,这样当开发收到部署流水线失败的通知后,可以登录ArgoCD快速地查看和定位错误信息。

目前基于ArgoCD的Kubernetes应用的持续交付是我们主推的模式,更多的问题有待于我们在深入使用的过程中去发现和解决。

展望

如果从静态的角度来看Kubernetes应用的部署,可以把Kubernetes的部署分为两个层面,数据层面和控制层面。数据层面就是包含应用代码的Docker镜像,而控制层面则是Kubernetes的资源文件,比如Deployment、Service和Ingress等,它们负责把Docker镜像安全、正确地安装在Kubernetes集群中,并打通从集群外到应用的网络流量。Kubernetes应用的持续交付流水线就是持续地将最新部署需求也就是部署资源文件同步到Kubernetes集群中,让应用按照规定的方式运行。 Kubernetes的Controller和Operator等扩展方法使得第三方组件能够提供越来越多的部署资源类型,让我们可以更好地控制应用的部署和运行。比如,如果想让Istio服务网格来托管应用的流量管理,我们可以添加Istio组件提供的部署资源类型,通过持续交付流水线同步到Kubernetes集群中。

如下图所示,通过使用不同类型的部署资源,未来我们可以把应用自动对接到不同的运维监控平台上,使得Kubernetes应用的持续交付流水线真正地成为一个从 Dev到Ops的端到端的完整交付链。

NzM7N3y.png!mobile

Q&A

Q:请问Helm是需要手动来进行更新操作的吗?每次打包生成的镜像版本号不同,如何保证Argo可以检测到并且部署正确的Helm应用呢?这个value.yml是存放在这个Chart仓库里面的吗?

A:Helm最大的优势就是它的参数化。在ArgoCD关联的部署资源的代码库里,我们通过dependency引用通用的Helm Chart,在Vaules.yaml里可以设置Docker镜像版本。通过CI Pipeline自动去提交commit,改这个Values.yaml里的Docker镜像版本,这个提交就会触发Deployment pipeline。Chart里是一个缺省的。你可以在外面指定一个针对你当前部署环境的vaules.yaml,比如values-dev.yaml,values-qa.yaml……

Q:请问使用Helm与使用Terraform相比有什么优势和劣势?

A:我觉得这两个是没有冲突的。Helm是Kubernetes上应用现在比较流行的部署方式。Terraform是一款跨平台的基础架构即代码的工具,早期版本它是支持以代码的方式来创建我们的基础架构,比如网络,虚拟机等等,后来随着Kubernetes逐渐流行,Terraform又提供了Helm的Provider,用来执行Helm的部署。要勉强说优势的话,可能现在基于云平台的基础架构代码既包含创建基础架构,比如Kubernetes,同时也调用Helm去部署Kubernetes上的系统组件。

Q:你们在Terraform和Helm结合的时候,用多个Pipeline么?我的意思是,假如Terraform出问题了,会影响后面的Helm的部署。是不是适合分开成两个Pipeline?不清楚分开或者不分开哪种更好。

A:我个人觉的还是分开好!部署Helm单独的一个,这样便于后面切换成别的工具。因为,我认为用Terraform来产生各种基础机构比如网路,存储,ECS,包括Kubernetes,这是很正常的事,但是Kubernetes的部署(包括基础组件比如Istio,Nginx Controller等)其实我觉得是容器时代应用层面的东西。未来Terraform也不一定是最适合的,你也可以看看ArgoCD的 Cluster Bootstrap ,它提供了另一个思路来初始化你的集群。

Q:对于Pipeline您有什么推荐么?我们在AWS上用了CodePipeline,但还是希望能用跨平台的,比如Jenkins。除此之外,还用更好的么?或者更加推荐的。

A:目前,我们还是在用Jenkins(Jenkinsfile + Extended Library封装通用功能)。如果是我的话,目前还会选择Jenkins,各种云平台的Pipeline可能会更好,但是我觉得就是平台相关了,尤其对我们是使用多云的场景下,比如中国这边我们是Ali云,而国外总部是GCP和Azure。

Q:kubectl apply -f xxx.yaml这种部署方式,每次apply的时候可以重新部署image么?kubectl是如何检查是否unchanged(包括image和yaml配置)?以及deployment的时候有什么建议?比如先delete,再重新apply,需要么?还比如我看到有rolling update。

A:如果image(latest)本身没有任何变化(sha码),且Deployment对于Image的任何配置都没有改动,都不会重新部署Image吧。或者你的pull策略设成Pull if not present,即使image有变化也不会被pull吧。

Q:Deployment与滚动更新的区别?

A:CICD deployment Pipeline是调度层,它会触发ArgoCD去同步和部署,而滚动部署实际上是定义在Deployment资源里,由Kubernetes解释的。

Q:使用到ConfigMaps吗?也会配置在Helm里还是独立出来,更新策略是怎么样的?

A:在Helm里ConfigMap和Deployment,Service,Ingress一样都是可以作为模板资源定义在Helm Chart里面,把可变的部分抽取出参数放到values.yaml文件里。其实我们是想尽量避免使用ConfigMap,因为它的更新有时并不能被Deployment实时更新。我们更多希望把应用的配置放到配置中心里去。

Q:想问下有了解过Tekton吗?对这个有什么看法?

A:我个人觉得Tekton是未来Cloud Native的比较流行的CICD的解决方案,尽管它现在还不是很成熟。你也可以看看ArgoCD还有一款产品叫Workflow,和Tekton比较类似,只不过现在还不支持Git的webhook。对于Jenkins,我觉的它是VM时代的CICD 方案,其实它不适合在Kubernetes上运行的

Q:想问下集群内部调用是直接使用的svc还是其他的方式。如果使用的是svc,是规范好的命名,然后代码写死的,还是通过其他手段?

A:集群内部调用就是通过svc的方式,是按照标准格式写好的:svc.<namespace>.cluster.local。

Q:贵公司没有经历虚拟机部署到Kubernetes部署的过程吗?如果经历了,这个过程可以简单介绍下吗?

A:在我前一家公司,我们是既有VM应用,也有Kubernetes应用。对于VM的部署,基本上都是用Ansible。而我们的虚机环境的管理是通过Terraform+Ansible代码的方式。

Q:请问ArgoCD跟Tekton对比,哪个更适合当下使用?

A:如果按照ThoughtWorks的技术雷达来衡量的话,我觉得ArgoCD是可采用的阶段,而Tekton是可以尝试的阶段。未来,我很看好Tekton。

以上内容根据2020年8月25日晚微信群分享内容整理。 分享人 云斋,现任某著名外资零售企业高级数字化工程师,负责产品上云的技术选型、方案设计以及云平台基础架构的运维。曾任某知名外资医疗企业高级DevOps工程师,主要负责中国数字化创新部门的CI/CD平台的建设,运维和推广 。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesf,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK