资讯详情

DaoCloud贾恒:一文搞懂发布现代化

嘉宾 | 贾恒 整理 | 李会明

出品 | CSDN云原生

2022年5月24日,云原生系列在线举行Meetup·北京站上,DaoCloud道客微服务架构师贾恒分享了发布工具Argo CD、Argo Rollouts,并解释了云原生发布系统如何在微服务中完成闭环。

如何实现全链路发布闭环?DaoCloud贾恒告诉你答案

所谓发布现代化,可以理解为云原生微服务系统下的发布。发布现代化的探索主要分为三个部分:

  • 理论基础:GitOps

  • 发布工具:Argo CD & Rollouts

  • 闭环:发布系统与微服务和数据隔离的合作

GitOps是Weaveworks2017年推出的一种持续部署云原生应用程序的方法。通过使用Git并不断部署工具,专注于在操作基础设施时提供以开发人员为中心的体验。

GitOps有4 个组成部分:

  • Git仓库:用于存储应用程序声明定义yaml文件源代码仓库

  • Kubernetes集群:部署应用程序的底层集群

  • 同步代理(Kubernetes Operator):将Git持续同步到集群中的仓库和应用状态

  • CD Pipeline:连续部署的装配线用于整个过程的连续部署

GitOps有三个特点:

  • 部署更快更频繁

  • 恢复错误简单快捷

  • 凭证管理更容易

Operator使用可提醒Git如果集群中运行的内容有差异,Kubernetes控制器将根据情况自动更新或回滚集群。Git在交付管道的中心,开发人员可以使用熟悉的工具来加速和简化要求Kubernetes应用程序部署和操作任务。

上图是传统的Push该模式涉及应用仓库和环境仓库。应用仓库是指代码仓库,环境仓库是部署清单仓库。部署清单包括:Kubernetes的demployment、service、helm、kustomize等。

Push模式下的部署过程如下:

  1. 当应用程序代码发生变化时,触发CI流水线(Build Pipeline)

  2. CI流水线完成后,将镜像推到镜像仓库(Image Registry)

  3. 更改资源配置清单(Environment Repository)

  4. 触发CD流水线(Deployment Pipeline),完成环境发布

上图为Pull模式,Pull模式下的部署流程是这样的:

  1. 当应用程序代码发生变化时,触发CI流水线(Build Pipeline)

  2. CI流水线完成后,将镜像推到镜像仓库(Image Registry)

  3. 更改资源配置清单(Environment Repository)

  4. Operator变更控制器监控镜像仓库和资源配置清单,完成环境发布

Pull部署过程在模式下的前三步Push模式基本相同,区别在于:Pull在模式下,更改资源配置清单后,不会触发资源配置清单CD流水线的建设是由集群组成的Operator控制器watch变更镜像仓库和资源配置清单,发现变更后发布环境。

Pull模式解决了传统问题Push模型的两个问题:

  • 密钥管理:Operator应始终与要部署的应用程序处于相同的环境或集群中。这可以防止在基于推送的方法中看到特权模式。

  • 预期状态与实际状态冲突:get配置与基础设施的实际运行不一致。

基于这些原因,GitOps选择采用了Pull模式。

  • 任何能够被描述的内容都必须存储在Git库中:通过使用Git存储仓库作为存储声明基础设施和应用程序代码,可以轻松监控集群,检查实际环境是否与代码库上的状态一致。

  • 不得直接使用kubectl命令:一般不建议在命令行中直接使用kubectl将基础设施或应用程序部署到集群中。CI部署工具驱动应用程序可能会给生产环境带来潜在的不可预测的风险。

  • 调用Kubernetes的API应遵循接口或控制器Operator模式:集群状态和Git库中的配置文件应保持一致,并检查和分析它们之间的状态差异。

基于GitOps一些发布工具可能用于提供的理论基础,如Argo CD和Argo Rollouts。

API服务是一个gRPC/REST它公开了服务器Web UI、CLI和CI/CD系统使用的API。有下列职责:

  • 应用程序管理和状态报告

  • 调用应用程序操作(例如同步、回滚、用户定义的操作)

  • 存储和集群凭证管理(存储为K8S Secrets)

  • 外部身份提供者的身份验证和授权

  • RBAC权限管理

  • Git webhook事件监听

仓库服务是负责维护和保存应用程序清单的内部服务,Git本地缓存仓库。提供以下输入时,负责生成并返回Kubernetes清单:

  • 存储URL

  • revision版本(commit、tag、branch)

  • 应用路径

  • 模板配置:参数,ksonnet环境、helm values.yaml等

应用应用控制器Kubernetes控制器,它是连续的watch正在运行的应用程序将当前的实时状态和预期的目标状态(repo比较中指定的);检测应用程序OutOfSync状态,并采取一些措施同步状态;调用任何用户定义的生命周期事件的钩子(PreSync、Sync、PostSync)。

Argo CD遵循GitOps模式,使用Git仓库作为定义所需应用程序状态的真实来源,Argo CD 支持多种Kubernetes清单:

  • kustomize

  • helm charts

  • ksonnet applications

  • jsonnet files

  • Plain directory of YAML/json manifests

  • Any custom config maagement 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的单个实例可以处理不同团队的许多应用程序。它使用Project CRD来做到这一点。Project可以容纳多个应用程序并映射到一个团队。团队成员只能看到分配给他们的项目,并且只能看到这些项目中的应用程序。该模型与Kubernetes命名空间中的资源非常相似。

多集群:Argo CD可以同步运行它的Kubernetes集群上的应用程序,还可以管理外部集群。它可以配置为只能访问一组受限制的命名空间。其他集群API服务器的凭证作为机密存储在Argo CD的命名空间中。

配置漂移检测:当集群的操作员更改资源而不经过GitOps工作流(即提交到Git)时,Kubernetes资源可能会偏离存储在Git中的配置,Argo CD可以检测到这些更改并恢复它们,将状态恢复到Git中定义的状态。

垃圾收集:当从Git中删除一些文件时,kubectl apply将忽略它(除非使用“--prune”标志),否则开发人员无法删除他们创建的资源。

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字段的副本集。

原生Kubernetes部署对象支持RollingUpdate在更新期间提供一组基本安全保证(就绪探测)的策略。然而滚动更新策略面临许多限制:

  • 百分比,请求头等控制

  • 无法控制流量到新版本

  • 就绪探针场景有限,比如需要基于某些指标阈值控制

  • 无法查询外部指标来验证更新

由于这些原因,在大规模大批量生产环境中,滚动更新通常被认为更新过程的风险太大,因为它无法控制爆炸半径。

  • 蓝绿更新策略

  • 金丝雀更新策略

  • 细粒度的加权流量

  • 自动回滚和前进

  • 人工判断

  • 可定制的指标查询和业务KPI分析

  • 入口控制器集成:NGINX、ALB

  • 服务网格集成:Istio、Linkerd、SMI

  • 多个提供者同时使用:SMI + NGINX、Istio + ALB等

  • 指标提供程序集成:Prometheus、Wavefront、Kayenta、Web、Kubernetes    Jobs、Datadog、New Relic、Graphite

这是监控集群事件并在资源类型Rollout发生更改时做出反应的主控制器。控制器将读取部署的所有详细信息,并将集群置于部署定义中描述的相同状态。

Rollout资源是Argo Rollouts引入和管理的自定义Kubernetes资源。它主要与原生Kubernetes部署资源兼容,但具有控制高级部署方法(如金丝雀和蓝/绿部署)的阶段、阈值和方法的额外字段。

Argo Rollouts控制器只会响应Rollout源中发生的那些更改。如果使用Argo Rollouts管理部署,则需要将deployment迁移到Rollouts。

这些是标准Kubernetes ReplicaSet资源的实例。Argo Rollouts在它们上放置了一些额外的元数据,以便跟踪作为应用程序一部分的不同版本。

针对Canary部署,Argo Rollouts支持多种服务网格和ingress解决方案,用于以特定百分比拆分流量,而不是基于pod数量的简单平衡。

AnalysisRun是将Rollout连接到指标程序并为某些指标定义特定阈值的能力,这些指标将决定更新是否成功,如果指标查询良好,Rollout将自行进行;如果指标显示失败,则自动回滚;如果指标无法提供成功/失败答案,则暂停。AnalysisTemplate包含有关要查询哪些指标的说明。

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时,涉及更改三个字段:

  1. 替换apiVersion从apps/v1到argoproj.io/v1alpha1

  2. 替换kind从Deployment到Rollout

  3. 将部署策略替换为蓝绿或金丝雀策略

缩小到零并从Rollout资源中引用,而不是删除Deployment。

  1. 创建一个Rollout资源。

  2. WorkloadRef使用字段引用现有Deployment。

  3. Replicas通过将现有部署的字段更改为零来缩小现有部署。

  4. 要执行更新,应更改Deployment的pod模板字段。

了解了Argo CD和Argo Rollouts的整个发布体系,但还不足以完成发布的闭环。比如:发布体系怎么样和微服务配合?灰度数据怎么样做到隔离?

Argo CD和Argo Rollouts原生并不支持高级的流量路由,但在微服务中可以提供Istio、Ingress的流量控制,或者SpringCloud体系中注册中心的mete-data自定义来实现。

流量管理控制数据平面,以便为应用程序提供智能路由规则。这些路由规则可以操纵流量流向支持渐进式交付的应用程序的不同版本。这些控件通过确保一小部分用户在验证时收到新版本来限制新版本的爆炸范围。

有多种技术可以实现流量管理:

  • 原始百分比(即5%的流量应该流向新版本,而其余的流向稳定版本)

  • 基于头部的路由(即向新版本发送带有特定头部的请求)

  • 复制所有流量并并行发送到新版本的镜像流量(但忽略响应)

核心Kubernetes对象没有满足流量管理所有要求所需的细粒度工具。Kubernetes最多通过Service对象提供本机负载平衡功能,方法是提供一个端点,该端点根据该Service的选择器将流量路由到一组pod。默认核心服务对象无法实现流量镜像或按标头路由等功能,控制应用程序不同版本的流量百分比的唯一方法是操纵这些版本的副本计数。服务网格填补了Kubernetes中缺失的功能。

以下示例是Rollout借助Ingress来完成金丝雀发布的一个场景。

如上图所示,可以看到Rollout这个CRD里有一个strategy字段,字段中定义了两个服务,一个是金丝雀服务,一个是稳定版本的服务。其中,setWeight是指执行的发布策略——设置权重5,也就是说最开始5%的流量路由到金丝雀版本。pause是指暂停。

另外,还需要创建两个Service,一个金丝雀Service和一个稳定版本的Service。如下图所示:

除此之外,还需要准备Ingress资源,一个稳定版本的Ingress资源。

有了这几种资源之后,就可以线上执行发布。比如说blue版本改为yellow,也就是去做更新镜像的操作,就可以用下面的命令进行发布:

kubectl argo rollouts set image rollouts-demorollouts-demo=argoproj/rollouts-demo:yellow

在更新完镜像之后,会发现多了一个Ingress的CI。

CI里面的字段信息如下:

annotations下canary-weight为5,这就是Ingress本身做金丝雀发布的配置信息。Argo Rollouts的实现逻辑实际就是通过watch Rollout资源,监测镜像是否发生变更。镜像发生变更后,它会根据配置的策略再次生成Ingress的资源信息。

在上述示例中,Ingress资源要做的事情就是把5%的流量发给金丝雀版本,Argo Rollouts 就借助了Ingress的能力完成了一个金丝雀发布的资源配置清单。

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-demorollouts-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等工具。

这样就实现了全流程全链路发布的闭环。

参考链接

标签: 连接器挂钩

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台