Kubernetes GitOps Tools
本文很好地介绍了GitOps,并给出了当下比较热门的GitOps工具。
简介
在本文中,将回顾一下kubernetes上我比较喜欢的GitOps工具。
在我看来,Kubernetes的优势主要在于它的声明式性质与控制循环相结合,并通过这些控制循环持续监控集群的活动状态,确保它与etcd中存储的期望状态保持一致。这种方式非常强大,但同时其数据库也被限制为etcd(etcd仅提供了有限的可观察性),并影响到了集群变更时的责任性和审计性。另外一个缺点是将etcd和代码仓库作为两个SOT(sources of truth),有可能会导致配置漂移,造成管理上的困难。
开发者使用代码仓库这种安全且可追溯的方式来保存代码,并开发出了Workflows这种方式来高效地管理*仓库,不同的团队可以并行工作,且不会产生过多的冲突,同时保证对所有变更进行审核,并支持追溯和回滚。
如果我们能够从围绕Git仓库创建的流程中获得这些优势,并将它们扩展到基础设施或是kubernetes中,那不是很好吗?…欢迎来到GitOps的世界!
首先聊一下什么是GitOps,以及如何将其应用到Kubernetes,然后再看一下在kubernetes中实现的GitOps声明式工具,最后回顾一些GitOps友好的工具,即它们是以代码的形式实现和声明的工具。
什么是GitOps?
GitOps的目的是将etcd的这种声明式特性扩展到(保存代码的)Git 仓库,并作为SSOT(single source of truth)。通过这种方式,我们可以利用Git带来的优势,如代码监视、历史变更、快速回滚、可追溯性等等。
GitOps的核心观点是使用包含当前期望的(生产环境基础设施的)声明式描述的GIt仓库,并通过自动化流程来确保生产环境与仓库中的期望状态保持一致。如果你想要部署一个新的应用或更新一个现有的应用,只需要更新相应的仓库即可(自动化流程会处理后续的事情)。这就像在生产中使用巡航控制来管理应用程序一样。
GitOps 不仅限于Kubernetes,实际上它还可以通过将基础设施作为代码保存到GIt仓库中来将应用代码延伸到基础设施中,这通常是通过Terraform这样的工具进行普及的。声明式基础设施既代码在实现GitOps中扮演着一个重要的作用,但不仅限于此。GitOps围绕Git构建了整个生态系统和工具,并将其应用到基础设施,仅仅在Git中使用Terraform并不能保证基础设施的状态能够与生产环境保持一致,还需要持续运行Terraform命令(trrraform apply)来监控实时版本的变化,以及在流水线中实现手动审批等功能。
GitOps的理念是持续监控集群的状态并与Git仓库进行对比,在配置不一致时立即采取相应的动作,防止发生配置漂移。除此之外,你还可以获得Git带来的好处,如通过代码监视进行手动审批。
对于我来说,这些理念是革命性的,如正确使用,可以让组织更多关注功能特性,并减少自动化脚本的编写工作。这种观念可以延申到软件开发的其他领域,如可以将文档存储在代码中,以此来跟踪历史变更,并保证文档的及时更新;或使用 ADRs来跟踪架构决策。
Kubernetes中的Gitops
Kubernetes从一开始就有控制循环的想法,这意味着Kunernetes总是会监控集群的状态来保证达到期望状态。例如,使运行的副本数与期望的副本数匹配。将GitOps的理念延申到应用,这样就可以将服务定义为代码,例如,通过定义 Helm Charts,并使用一个通过K8s提供的功能来监控App状态的工具来调整对应的集群状态,即,更新了代码仓库,或更新了生产集群的helm chart。这才是真正的持续部署。其中的核心原则是,应用部署和生命周期管理应该是自动化的、可审计并易于理解的。
每个环境都应该有一个代码仓库,用于定义给定集群的期望状态。然后Kubernetes Operators 会持续监控特定分支(通常是master分支),并在探测到Git发生变更后,将此次变更传递到集群,并更新etcd中的状态。在这种方式中,etcd只作为一个数据库,且不是唯一的SOT。可以在包含声明式Kubernetes基础设施的Git仓库中定义应用的Helm chart。此外,还可以链接存储库,这样一个存储库可以监视另一个存储库,以此类推。Kubernetes GitOps工具可以监控其他仓库(如Helm Chart仓库)的状态,这样你的集群环境仓库中无需包含Helm Chart,只需要一条到Helm 仓库的连接,并使用该链接监控其变更即可,这样当你发布的一个新的chart时,后续会将该chart自动部署到集群。通过这种方式自动执行端到端的声明式CI/CD流水线。
声明式GitOps工具
如果考虑Kubernetes上的GitOps,就需要讨论那些在Kubernetes上实现了GitOps原则的工具(负责监控Git的状态,并将其同步到集群)。
ArgoCD
在我看来,Kubernetes上最好的GitOps工具就是ArgoCD。ArgoCD 是Argo生态系统的一部分,该生态系统包含了很多很好的工具,后续会进行讨论。
使用ArgoCD,可以在代码库中定义每个环境的所有配置。Argo CD会在特定的目标环境中自动部署所需的应用状态。
Argo CD 作为一个kubernetes controller,持续监控运行的应用,并将当前的活动状态与所需的目标状态(如Git repo中描述的状态)进行比较。Argo CD会报告并呈现这种差异,并通过自动或手动的方式将实时状态同步到期望的目标状态。
ArgoCD有一个非常棒的UI,支持SSO,它是安全、可扩展且易于使用的。
Flux
Flux 是除Argo CD外另一个非常有名项目。最近的版本包含很多改进,功能上非常接近Argo CD,Flux是CNCF孵化项目。
GitOps工具
在本节中,回顾一下我比较喜欢的GitOps友好的(即基于CRD的)工具。
helm
Helm 无需多言,它是Kubernetes上最著名的包管理器,当然,你应该像在编程语言中使用包一样,在K8s中使用包管理器。Helm允许使用Charts 的方式打包应用,将复杂的应用抽象为可重用的简单组件,便于定义、安装和升级。
它还提供了一个强大的模板引擎,Helm 已经很成熟,它包含很多预定义的charts,支持性强,使用方便。
Helm可以很好地集成到ArgoCD或Flux,后者可以监控Helm仓库的变化,并在发布新的charts时进行部署。实现思路是将ArgoCD或Flux 指向Helm仓库,并指定一个特定的版本或通配版本。如果使用通配版本,一旦发布了新的版本,就会进行自动部署。你可以使用主版本或次版本的方式进行命名。我通常倾向于将应用打包到charts中,将其作为CI/CD的一部分进行构建,并让ArgoCD监控特定的仓库。这种关注点分离允许开发者在独立于环境的仓库中管理应用,并让ArgoCD选择在哪个环境中部署哪个charts。你可以使用多个Helm仓库并根据不同的环境推送变更。例如,在合并一个PR后,可以执行一次"bronce"构建,该构建会将Helm chart发布到一个名为"bronce"的仓库中。dev环境的ArgoCD仓库指向"bronce"仓库,并在可用时部署新版本。staging 环境的ArgoCD仓库会指向名为"silver"仓库,而production 环境则指向名为"gold"的仓库。当需要向staging或production环境推送某些内容时,CI/CD只需将该chart发布到下一个仓库即可。
ArgoCD可以覆盖任何环境的特定Helm值。
Kustomize 是一个新的helm的替代品,它不需要模板引擎,而使用了一个overlay引擎,之上有基本定义和overlays 。
Argo Workflows 和 Argo Events
在Kubernetes中,你可能需要运行批量任务或复杂的工作流,作为数据处理流程、异步处理或CI/CD的一部分。除此之外,你可能需要运行驱动微服务来响应特定的事件,如文件上传或发送消息到某个队列。为了实现这些功能,可以使用 Argo Workflows 和Argo Events。
虽然它们是独立的项目,但趋向于部署到一起。
Argo Workflows是一个类似Apache Airflow 的编排引擎,但天然适用于Kubernetes。它使用CRDs来定义复杂的workflows(使用YAML表示的steps或DAGs 来表达工作流)。它还有个不错的UI,支持重试机制,定时任务,输入输出跟踪等。你可以使用它来编排数据处理流水线和批量任务等。
有时你可能希望将流水线与异步服务(如流引擎Kafka、队列、webhook或底层存储服务)集成到一起。例如,你可能希望对上传文件到S3这样的事件做出响应,此时你可以使用Argo Events。
上述两种工具为需要CI/CD流水线提供了简单且强大的解决方案,可以在Kubernetes上运行CI/CD流水线。
由于所有workflows 定义都可以打包到Helm charts中,因此Argo Workflows 可以很好地集成到ArgoCD。
对于ML流水线,可以使用Kubeflow实现相同的目的。
对于CI/CD流水线,可以使用Tekton。
Istio
Istio 是市面上最著名的服务网格,它是开源的,且非常流行。由于服务网格内容庞大,因此这里不会讨论细节,但如果你在构建微服务,有可能你会需要一个服务网格来管理通信、可观测性、错误处理、安全以及(作为微服务架构一部分的)其他交叉方面。使用服务网格可以避免因为重复的逻辑而污染微服务的代码。
简而言之,一个服务网格就是一个特定的基础设施层,你可以在上面添加应用,它允许透明地添加可观测性、流量管理和安全,而无需自己实现这些功能。
Istio使用 CRDs 来实现其所有功能,因此可以将virtual services、gateways、policies等作为代码打包在Helm charts中,并使用ArgoCD或Flux来让Istio变得GitOps友好(虽然不是那么强大)。
Argo Rollouts
上面已经提到过,你可以使用Kubernetes来运行使用Argo Workflows或类似工具的CI/CD流水线。下一步逻辑上是执行持续部署,但在现实场景中,由于其风险较高,因此大部分公司只做了持续交付,这意味着他们可以实现自动化,但仍然采用手动方式进行审批和校验,这类手动步骤的根因是这些团队无法完全信任其自动化。
那么该如何构建这种信任度来避免使用脚本,进而实现从代码到生产的完全自动化。答案是:可观测性。你需要关注资源的指标,并通过采集所有的数据来精确地传达应用的状态,即使用一组指标来构建信任度。如果Prometheus中包含所有的数据,那么就可以实现自动化部署,因为此时你可以根据指标来实现滚动更新应用。
简单地说,你可以使用K8s提供的开箱即用的高级部署技术--滚动升级。我们需要使用金丝雀部署来实现渐进式发布,目的是将流量逐步路由到新版本的应用,等待指标采集,然后进行分析并于预先定义的规则进行匹配。如果检查正常,则增加流量;如果发现问题,则回滚部署。
在Kubernetes上可以使用Argo Rollouts来实现金丝雀发布。
Argo Rollouts是一个Kubernetes controller,它使用一组CRDs来提供高级部署能力,如蓝绿、金丝雀、金丝雀分析、实验等,并向Kubernetes提供渐进式交付功能。
虽然像 Istio 这样的服务网格可以提供金丝雀发布,但Argo Rollouts简化了处理流程,且以开发者为中心。除此之外,Argo Rollouts还可以集成到任何服务网格中。
Argo Rollouts 是GitOps友好的,且能很好地与Argo Workflows和ArgoCD进行集成。使用这三种工具可以为部署创建一个非常强大的声明式工具集。
Flagger 和Argo Rollouts非常类似,可以很好地与Flux进行集成,因此如果你正在使用Flux,可以考虑使用Flagger。
Crossplane
Crossplane是我最近喜欢的K8s工具,它填补了Kubernetes的一个关键空白:像管理K8s资源一样管理第三方服务。这意味着,你可以使用YAML中定义的K8s资源,像在K8s中配置数据库一样配置AWS RDS或GCP cloud SQL等云供应商的数据库。
有了Crossplane,就不需要使用不同的工具和方法来分离基础设施和代码。你可以使用K8s资源定义所有内容。通过这种方式,你无需去学习并分开保存像Terraform 这样的工具。
Crossplane 是一个开源的Kubernetes扩展,可以让平台团队组合来自多个供应商的基础设施,并为应用团队提供更高级别的自服务APIs(而无需编码任何代码)。
Crossplane 扩展了Kubernetes集群,使用CRDs来提供基础设施或管理云服务。再者,相比于Terraform这样的工具,它可以完全实现自动部署。Crossplane 使用现成的K8s能力,如使用控制循环来持续监控集群,并自动检测配置漂移。例如,如果定义了一个可管理的数据库实例,后续有人手动进行了变更,Crossplane 会自动检测该问题,并将其设置回先前的值。它将基础设施作为代码并按照GitOps原则来执行。
Crossplane 可以与ArgoCD配合起来,监控源代码,并保证代码库是唯一的信任源(SOT),代码中的任何变更都会传递到集群以及外部云服务。
如果没有Crossplane,你可以在K8s服务中实现GitOps,但无法在没有额外处理的前提下实现云服务的GitOps。
Kyverno
Kubernetes为敏捷自治团队提供了极大的灵活性,但能力越大责任越大。必须有一套最佳实践和规则来保证部署的一致性和连贯性,并使工作负载遵循公司要求的策略和安全。
Kyverno 是一种为Kubernetes设计的策略引擎,它可以像管理Kubernetes资源一样管理策略,不需要使用新的语言来编写策略。Kyverno 策略可以校验、修改和生成Kubernetes资源。
Kyverno 策略是一组规则,每条规则包含一个match子句,一条exclude子句和
validate
,mutate
或generate
中的某条子句。一条规则定义中只可以包含一个validate
,mutate
或generate
子节点。
你可以配置任何有关最佳实践、网络或安全的策略。例如,可以强制所有包含标签的服务或所有容器运行在非root权限下。 这里提供了一些Kyverno 策略的例子。策略可以应用到整个集群或特定的命名空间中。你可以选择是否期望对策略进行审计或强制它们阻止用户部署资源。
Kubevela
Kubernetes 的一个问题是开发者需要知道并对平台和集群配置有所了解。很多开发者抱怨K8s的抽象级别太低,因此在那些只关心编写和交付应用的开发者中间出现了争议。
Open Application Model (OAM) 可以解决这个问题,它的理念是围绕应用创建一个更高级别的抽象,其独立于底层运行时。更多参见这里.。
通过关注应用而非容器或编排器。OAM带来了模块化、可扩展和可移植的设计,通过更高级别且一致的API对应用程序部署进行建模。
Kubevela是OMA模型的一种实现。KubeVela是运行时无关,且可扩展的,但最重要的是,它以应用程序为中心。在Kubevela 中,应用程序是以Kubernetes资源实现的一等公民。需要区分集群运维人员(平台团队)和开发者(应用团队)的区别。集群运维人员通过定义components(构成应用程序的可部署/可提供的实体,如helm charts)和traits来管理集群和不同的环境。开发者通过组装components 和traits来定义应用。
平台团队:将平台能力作为components或traits以及目标环境规范进行建模和管理。
应用团队:选择一个环境,组装需要的components和traits,并将其部署到目标环境中。
KubeVela 是CNCF的沙盒项目,目前正处于初始阶段,在不久的将来,它可以改变开发者使用Kubernetes的方式,使他们能够更加关注应用本身。但我对OAM在现实世界中的适用性有一些担忧,由于一些像系统程序、ML或大数据处理之类的服务,它们在很大程度上取决于底层细节,这种情况就很难融入OAM模型。
Schema Hero
软件开发中另一种常见的的处理流程是在使用关系型数据库时,需要管理schema的演进。
SchemaHero是一个开源数据库的schema迁移工具,可以将schema定义转化为可以在任何环境运行的迁移脚本。它使用了Kubernetes的声明特性来管理数据库schema的迁移。你可以指定期望的状态,并让SchemaHero管理剩下的工作。
Bitnami Sealed Secrets
至此,我们已经涵盖了很多GitOps工具,我们的目标是将所有内容保存到Git,并使用Kubernetes的声明特性来让环境保持同步。我们可以(也应该)在Git中保证SOT,并让自动化流程处理配置更改。
有一种资源通常难以保存到Git中,即secret,如DB密码或API密钥。一种常见的解决方案是使用外部"保险库",如 AWS Secret Manager 或HashiCorp Vault 来保存这些secret,但这样也产生了冲突,你需要一个单独的流程来处理secrets。理想情况下,我们希望通过某种方式将secrets像其他资源一样安全地保存到Git中。
Sealed Secrets 就是用来解决这种问题的,它允许你将敏感数据通过强加密保存到Git中,Bitnami Sealed Secrets天然集成进了Kubernetes中,可以使用Kubernetes中运行的Kubernetes controller来解密secret,而无需依赖其他组件。controller可以解密数据并创建原生的K8s secrets资源。通过这种方式可以将所有内容作为代码保存到仓库中,进而可以安全地执行持续部署,不需要依赖外部资源。
Sealed Secrets包含两部分:
- 集群侧的控制器
- 客户端侧程序:
kubeseal
kubeseal 程序使用非对称加密来对secrets进行加密,且只有controller可以解密。这些加密的secrets被编码到了K8s的SealedSecret
资源中(可以保存到Git中)。
Capsule
很多公司使用多租户来管理不同的客户,这在软件开发中很常见,但很难在Kubernetes中实现。一种方式是通过命名空间
将集群划分为独立的逻辑分区,但无法完全隔离用户,还需要引入网络策略、配额等等。你可以在每个命名空间中创建网络策略和规则,但这是一个让人乏味的过程,且无法扩展,而且租户无法使用多个命名空间,这是一个很大的限制。
分层命名空间 可以用来解决这些问题。方法是为每个租户分配分配一个父命名空间,并为该租户配置通用的网络策略和配额,并允许创建子命名空间。这是一个很大的改进,但在租户层面,缺少安全性和治理方面的原生支持。此外,它还没有达到生产状态,但预计将在未来几个月发布1.0版本。
一种常见的方式是为每个客户创建一个集群,这样做相对比较安全,并给租户提供了所需的一切内容,但这种方式很难管理,费用也很高。
Capsule 是一种在单个集群中为Kubernetes提供多租户功能的工具。使用Capsule,你可以将所有租户放到一个集群中。Capsule会为租户提供一个"近乎"原生的体验(虽然有一些小小的限制),租户可以在其集群中创建多个命名空间。这种方式隐藏了多租户共享底层集群的事实。
在一个集群中,Capsule控制器将多个命名空间聚合到一起,抽象为一个轻量级的Kubernetes,称为租户,它是一组Kubernetes命名空间。在每个租户中,用户可以创建其命名空间并共享分配到的资源,Policy Engine 保证租户间的隔离性。
租户级别定义的 Network and Security Policies, Resource Quota, Limit Ranges, RBAC以及其他策略会自动继承到租户的所有命名空间中(类似分层命名空间)。这样,用户可以自主地操作他们的租户,而不需要集群管理员的干预。
由于Capsule 的所有配置都可以保存到Git中,因此它是GitOps的。