嘉宾 | 贾恒 整理 | 李会明
出品 | CSDN云原生
2022年5月24日,在线上举行的云原生系列Meetup·北京站上,DaoCloud道客微服务架构师贾恒分享了发布工具Argo CD、Argo Rollouts,并对云原生发布体系如何在微服务中完成闭环等问题进行了解读。
戳👇观看贾恒分享视频
如何实现全链路发布闭环?DaoCloud贾恒告诉你答案
所谓“发布现代化”可以理解为云原生微服务体系下的发布,发布现代化的探索主要分三个部分来阐述:
理论基础:GitOps
发布工具:Argo CD & Rollouts
闭环:发布体系与微服务、数据隔离的配合
GitOps是Weaveworks于2017年推出的一种为云原生应用程序实施持续部署的方法。它通过使用Git和持续部署工具,专注于提供在操作基础架构时以开发人员为中心的体验。
GitOps有4 个组成部分:
Git仓库:用来存储应用程序声明性定义的yaml文件的源代码仓库
Kubernetes集群:用于部署应用程序的底层集群
同步代理(Kubernetes Operator):将Git仓库和应用状态持续同步到集群中
CD Pipeline:持续部署的流水线,用来编排整个流程的持续部署
GitOps具备3个特点:
更快更频繁的部署
简单快速的错误恢复
更容易的凭证管理
Operator的使用可以提醒Git与集群中运行的内容之间的差异,如果存在差异,Kubernetes控制器会根据情况自动更新或回滚集群。将Git置于交付管道的中心,开发人员可以使用熟悉的工具发出拉取请求,以加速和简化Kubernetes的应用程序部署和操作任务。
Push & Pull发布模式
上图为传统的Push模式,涉及应用仓库和环境仓库。应用仓库指的是代码仓库,环境仓库则是部署清单仓库。部署清单包括:Kubernetes的demployment、service、helm、kustomize等。
Push模式下的部署流程是这样的:
当应用程序代码有变更时,触发CI流水线(Build Pipeline)
CI流水线执行完成后,将镜像推送到镜像仓库(Image Registry)
更改资源配置清单(Environment Repository)
触发CD流水线(Deployment Pipeline),完成环境发布
上图为Pull模式,Pull模式下的部署流程是这样的:
当应用程序代码有变更时,触发CI流水线(Build Pipeline)
CI流水线执行完成后,将镜像推送到镜像仓库(Image Registry)
更改资源配置清单(Environment Repository)
Operator控制器监控镜像仓库和资源配置清单的变更,完成环境发布
Pull模式下的部署流程的前三步与Push模式基本一致,不同点在于:Pull模式下,更改资源配置清单后,并不是资源配置清单去触发CD流水线的构建,而是由集群中的Operator控制器watch镜像仓库和资源配置清单的变更,发现变更后进行环境发布。
Pull模式解决了传统Push模式的两个问题:
密钥管理问题:Operator应始终与要部署的应用程序处于相同的环境或集群中。这可以防止在基于推送的方法中看到特权模式。
期望状态与实际状态冲突问题:get配置与基础设施实际运行不符这个问题。
基于这些原因,GitOps选择采用了Pull模式。
GitOps的原则
任何能够被描述的内容都必须存储在Git库中:通过使用Git作为存储声明性基础架构和应用程序代码的存储仓库,可以方便地监控集群,以及检查比较实际环境的状态与代码库上的状态是否一致。
不应直接使用kubectl命令:一般不提倡在命令行中直接使用kubectl命令操作执行部署基础架构或应用程序到集群中。使用CI工具驱动应用程序的部署可能会给生产环境带来潜在不可预测的风险。
调用Kubernetes的API接口或者控制器应该遵循Operator模式:集群的状态和Git库中的配置文件等要保持一致,并且查看分析它们之间的状态差异。
基于GitOps提供的理论基础,可能会用到一些发布的工具,比如Argo CD和Argo Rollouts。
Argo CD
Argo CD架构
API Service
API服务是一个gRPC/REST服务器,它公开了Web UI、CLI和CI/CD系统使用的API。具有以下职责:
应用程序管理和状态报告
调用应用程序操作(例如同步、回滚、用户定义的操作)
存储库和集群凭证管理(存储为K8S Secrets)
对外部身份提供者的身份验证和授权委托
RBAC权限管理
Git webhook事件监听
Repository Service
存储仓库服务是一个内部服务,负责维护保存应用程序清单,Git仓库的本地缓存。当提供以下输入时,它负责生成并返回Kubernetes清单:
存储URL
revision版本(commit、tag、branch)
应用路径
模板配置:参数、ksonnet环境、helm values.yaml等
Application Controller
应用控制器是一个Kubernetes控制器,它持续watch正在运行的应用程序并将当前的实时状态与所期望的目标状态(repo中指定的)进行比较;检测应用程序的OutOfSync状态,并采取一些措施来同步状态;负责调用任何用户定义的生命周期事件的钩子(PreSync、Sync、PostSync)。
Argo CD的功能
Argo CD遵循GitOps模式,使用Git仓库作为定义所需应用程序状态的真实来源,Argo CD 支持多种Kubernetes清单:
kustomize
helm charts
ksonnet applications
jsonnet files
Plain directory of YAML/json manifests
Any custom config management tool configured as a config management plugin
除此之外,Argo CD还支持很多功能:
将应用程序自动部署到指定的目标环境
支持多种配置管理/模板工具(Kustomize、Helm、Ksonnet、Jsonnet、plain-YAML)
能够管理和部署到多个集群
SSO集成(OIDC、OAuth2、LDAP、SAML 2.0、GitHub、GitLab、Microsoft、LinkedIn)
用于授权的多租户和RBAC策略
回滚/任意滚动到Git存储库中提交的任何应用程序配置
应用资源的健康状况分析
自动配置漂移检测和可视化
自动或手动将应用程序同步到所需状态
提供应用程序活动实时视图的Web UI
用于自动化和CI集成的CLI
Webhook集成(GitHub、BitBucket、GitLab)
用于自动化的访问令牌
PreSync、Sync、PostSync挂钩以支持复杂的应用程序推出(例如蓝/绿和金丝雀升级)
应用程序事件和API调用的审计跟踪
普罗米修斯指标
用于在Git中覆盖ksonnet/helm参数的参数覆盖
Argo CD的亮点
多租户:Argo CD的单个实例可以处理不同团队的许多应用程序。它使用Project CRD来做到这一点。Project可以容纳多个应用程序并映射到一个团队。团队成员只能看到分配给他们的项目,并且只能看到这些项目中的应用程序。该模型与Kubernetes命名空间中的资源非常相似。
多集群:Argo CD可以同步运行它的Kubernetes集群上的应用程序,还可以管理外部集群。它可以配置为只能访问一组受限制的命名空间。其他集群API服务器的凭证作为机密存储在Argo CD的命名空间中。
配置漂移检测:当集群的操作员更改资源而不经过GitOps工作流(即提交到Git)时,Kubernetes资源可能会偏离存储在Git中的配置,Argo CD可以检测到这些更改并恢复它们,将状态恢复到Git中定义的状态。
垃圾收集:当从Git中删除一些文件时,kubectl apply将忽略它(除非使用“--prune”标志),否则开发人员无法删除他们创建的资源。
Argo Rollouts
Argo Rollouts是一个Kubernetes控制器和一组 CRD,为Kubernetes提供高级部署功能,例如蓝绿、金丝雀、金丝雀分析和渐进式发布功能。
Argo Rollouts(可选)与入口控制器和服务网格集成,利用它们的流量整形能力在更新期间逐渐将流量转移到新版本。此外,Rollouts可以查询和解析来自不同服务提供者的指标,以验证关键KPI并在更新期间推动自动升级或回滚。
渐进式发布
渐进式交付是以受控和渐进的方式发布产品更新的过程,从而降低发布风险,通常结合自动化和度量分析来驱动更新的自动升级或回滚。
原理
与部署对象类似,Argo Rollouts控制器将管理ReplicaSets的创建、缩放和删除。这些ReplicaSet由spec.template Rollout资源中的字段定义,它使用与部署对象相同的pod模板。当spec.template更改时,这会向Argo Rollouts 控制器发出信号,告知将引入新的 ReplicaSet。控制器将使用该spec.strategy字段中的策略集来确定如何从旧ReplicaSet进展到新ReplicaSet。一旦新的ReplicaSet被放大(并且可以选择通过Analysis),控制器会将其标记为“稳定”。
如果在spec.template从稳定的ReplicaSet过渡到新的ReplicaSet的过程中发生了另一个变化(即您在rollout的中间更改了应用程序版本),那么之前的新ReplicaSet将被缩小,控制器将尝试进行反映更新spec.template字段的副本集。
为什么选择Rollouts
原生Kubernetes部署对象支持RollingUpdate在更新期间提供一组基本安全保证(就绪探测)的策略。然而滚动更新策略面临许多限制:
百分比,请求头等控制
无法控制流量到新版本
就绪探针场景有限,比如需要基于某些指标阈值控制
无法查询外部指标来验证更新
由于这些原因,在大规模大批量生产环境中,滚动更新通常被认为更新过程的风险太大,因为它无法控制爆炸半径。
Rollouts的功能
蓝绿更新策略
金丝雀更新策略
细粒度的加权流量
自动回滚和前进
人工判断
可定制的指标查询和业务KPI分析
入口控制器集成:NGINX、ALB
服务网格集成:Istio、Linkerd、SMI
多个提供者同时使用:SMI + NGINX、Istio + ALB等
指标提供程序集成:Prometheus、Wavefront、Kayenta、Web、Kubernetes Jobs、Datadog、New Relic、Graphite
Rollouts的架构
Argo Rollout controller
这是监控集群事件并在资源类型Rollout发生更改时做出反应的主控制器。控制器将读取部署的所有详细信息,并将集群置于部署定义中描述的相同状态。
Rollout resource
Rollout资源是Argo Rollouts引入和管理的自定义Kubernetes资源。它主要与原生Kubernetes部署资源兼容,但具有控制高级部署方法(如金丝雀和蓝/绿部署)的阶段、阈值和方法的额外字段。
Argo Rollouts控制器只会响应Rollout源中发生的那些更改。如果使用Argo Rollouts管理部署,则需要将deployment迁移到Rollouts。
Replica sets for old and new version
这些是标准Kubernetes ReplicaSet资源的实例。Argo Rollouts在它们上放置了一些额外的元数据,以便跟踪作为应用程序一部分的不同版本。
Ingress/Service
针对Canary部署,Argo Rollouts支持多种服务网格和ingress解决方案,用于以特定百分比拆分流量,而不是基于pod数量的简单平衡。
AnalysisTemplate and AnalysisRun
AnalysisRun是将Rollout连接到指标程序并为某些指标定义特定阈值的能力,这些指标将决定更新是否成功,如果指标查询良好,Rollout将自行进行;如果指标显示失败,则自动回滚;如果指标无法提供成功/失败答案,则暂停。AnalysisTemplate包含有关要查询哪些指标的说明。
Metric Providers
Argo Rollouts为前面提到的Prometheus、Wavefront、Kayenta等几个流行的指标提供程序的集成。
渐进式发布
以上示例展示的是每10分钟逐渐增加Canary权重20%,直到达到100%。AnalysisRun基于AnalysisTemplate命名的success-rate。该success-rate模板查询Prometheus服务器,以5分钟间隔/样本测量HTTP成功率。它没有结束时间,一直持续到停止或失败。如果度量标准被测量为小于95%,并且存在三个这样的测量结果,则认为分析失败。失败的分析导致Rollout中止,将Canary权重设置回零,并且Rollout将被Degraded。否则,如果完成所有金丝雀步骤,则认为发布成功并且控制器停止分析运行。
渐进式交付的风格
使用Prometheus查询执行测量
参数化分析的能力
延迟开始分析运行直到第3步
这种渐进式发布风格是指会有一个指标提供程序来执行测量。比如说这里的Prometheus 10分钟加一次权重,然后停一下,看测量的阈值是否超出;又比如说我们到第二步才开始测量,这种就是延迟分析。这种发布风格就是渐进式发布。
迁移
新的发布
将Deployment转换为Rollout时,涉及更改三个字段:
替换apiVersion从apps/v1到argoproj.io/v1alpha1
替换kind从Deployment到Rollout
将部署策略替换为蓝绿或金丝雀策略
在线迁移
缩小到零并从Rollout资源中引用,而不是删除Deployment。
创建一个Rollout资源。
WorkloadRef使用字段引用现有Deployment。
Replicas通过将现有部署的字段更改为零来缩小现有部署。
要执行更新,应更改Deployment的pod模板字段。
了解了Argo CD和Argo Rollouts的整个发布体系,但还不足以完成发布的闭环。比如:发布体系怎么样和微服务配合?灰度数据怎么样做到隔离?
Argo CD和Argo Rollouts原生并不支持高级的流量路由,但在微服务中可以提供Istio、Ingress的流量控制,或者SpringCloud体系中注册中心的mete-data自定义来实现。
流量管理
流量管理控制数据平面,以便为应用程序提供智能路由规则。这些路由规则可以操纵流量流向支持渐进式交付的应用程序的不同版本。这些控件通过确保一小部分用户在验证时收到新版本来限制新版本的爆炸范围。
有多种技术可以实现流量管理:
原始百分比(即5%的流量应该流向新版本,而其余的流向稳定版本)
基于头部的路由(即向新版本发送带有特定头部的请求)
复制所有流量并并行发送到新版本的镜像流量(但忽略响应)
核心Kubernetes对象没有满足流量管理所有要求所需的细粒度工具。Kubernetes最多通过Service对象提供本机负载平衡功能,方法是提供一个端点,该端点根据该Service的选择器将流量路由到一组pod。默认核心服务对象无法实现流量镜像或按标头路由等功能,控制应用程序不同版本的流量百分比的唯一方法是操纵这些版本的副本计数。服务网格填补了Kubernetes中缺失的功能。
Ingress集成
以下示例是Rollout借助Ingress来完成金丝雀发布的一个场景。
如上图所示,可以看到Rollout这个CRD里有一个strategy字段,字段中定义了两个服务,一个是金丝雀服务,一个是稳定版本的服务。其中,setWeight是指执行的发布策略——设置权重5,也就是说最开始5%的流量路由到金丝雀版本。pause是指暂停。
另外,还需要创建两个Service,一个金丝雀Service和一个稳定版本的Service。如下图所示:
除此之外,还需要准备Ingress资源,一个稳定版本的Ingress资源。
有了这几种资源之后,就可以线上执行发布。比如说blue版本改为yellow,也就是去做更新镜像的操作,就可以用下面的命令进行发布:
kubectl argo rollouts set image rollouts-demo
rollouts-demo=argoproj/rollouts-demo:yellow
在更新完镜像之后,会发现多了一个Ingress的CI。
CI里面的字段信息如下:
annotations下canary-weight为5,这就是Ingress本身做金丝雀发布的配置信息。Argo Rollouts的实现逻辑实际就是通过watch Rollout资源,监测镜像是否发生变更。镜像发生变更后,它会根据配置的策略再次生成Ingress的资源信息。
在上述示例中,Ingress资源要做的事情就是把5%的流量发给金丝雀版本,Argo Rollouts 就借助了Ingress的能力完成了一个金丝雀发布的资源配置清单。
Istio集成
Argo Rollouts如何和Istio集成,实现一个高级的流量发布呢?
首先,看一下Rollouts的配置清单里面的strategy字段。strategy包括两个Service,一个是金丝雀的Service,另一个是稳定版本的Service。有了这两个Service之后,再看一下流量路由VirtualService。
上图左侧的资源rollouts-demo-vsvc1的路由是primary。中间的资源是Istio本身需要创建的资源,需要在Istio中创建一个Gateway的资源,接收入口的流量,然后创建创建两个K8s原生的Service资源,一个是金丝雀资源,另一个是稳定版本的资源。
此外,Istio中除了Gateway之外,还需要配置VirtualService,如上图右侧所示。VirtualServiceDemo中匹配了rollout-demo-vsvc1.local这个host。匹配完成后,就能够路由到稳定的版本,路由稳定版本的权重是100,路由金丝雀的权重是0,这是它初始化的配置。
接下来,就可以进行版本发布。举个例子,还是将blue版本改为yellow,也就是更新镜像的操作,更新发布命令如下:
kubectl argo rollouts set image rollouts-demo
rollouts-demo=argoproj/rollouts-demo:yellow
一旦完成更新后,Argo Rollouts的控制器就会更改资源配置清单,它会把VirtualService 的权重按照Rollout的配置将稳定版本的权重改为95%,金丝雀的权重改为5%。这样,Istio配合Argo Rollouts完成了一个按流量权重百分比的高级发布。
标记透传
除了发布系统对接微服务体系,比如上面提到的Ingress集成、Istio集成,为了完成发布的闭环,还需要考虑灰度标记的问题。灰度标记怎样层层传下来?如果遇到了跨线程会怎样?跨服务会怎样?
跨线程
如果出现跨线程的情况,可以使用ThreadLocal来保存透传交界。
线程可能存在父子线程,这就会出现子线程不能获取父线程里存在的灰度标记的情况,这时就需要使用InheritableThreadLocal。
如果线程是使用线程池的话,灰度标记会在线程池里出现数据混乱的情况,所以跨线程的传递最好还是使用transmittable-thread-local解决跨线程的问题。
ThreadLocal
InheritableThreadLocal
transmittable-thread-local
跨服务
作为接口参数
放入HttpRequest Header
使用字节码增强技术
解决跨服务的标记透传问题一般的做法是借助于字节码增强技术,如SkyWalking技术去做这种跨服务的灰度标记透传。
灰度/压测数据隔离
多种数据类型的灰度隔离:
DB数据:
影子库
影子表
Redis数据:
影子key
影子缓存(多实例/单实例)
MQ数据:
影子队列
日志数据:
MDC
第三方服务:
Mock
完成一个灰度发布的全链路闭环,除了要借助公共基础设施工具,比如Argo CD、Argo Rollouts、Ingress、Istio等,还需要业务应用配合改造,比如:灰度标记线程传递问题、上下文封装问题。
除此之外,还需要考虑数据隔离,这也涉及到业务侧的改造,比如:多数据源的判断路由。还有一些需要修改业务代码,比如:正常流量走A库,灰度流量走B库,或者借助Shareding-JDBC、Shareding-Proxy等工具。
这样就实现了全流程全链路发布的闭环。
参考链接
https://www.gitops.tech/
https://www.weave.works/technologies/gitops/
https://blog.container-solutions.com/fluxcd-argocd-jenkins-x-gitops-tools
https://argo-cd.readthedocs.io/en/stable/
https://argoproj.github.io/argo-rollouts/
聚焦云原生新技术、新实践,帮助开发者群体赢在开发范式转移的新时代。欢迎关注CSDN云原生微信公众号~
限时活动,数量有限🔥
关注【CSDN云原生】公众号,回复【图书】
邀请好友助力即可免费领图书,赶快参与吧!