前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Argo CD 实践教程 01

Argo CD 实践教程 01

作者头像
拿我格子衫来
发布2023-08-24 10:46:40
3300
发布2023-08-24 10:46:40
举报
文章被收录于专栏:TopFE

在本章中,我们将看看什么是GitOps,以及这个想法在 Kubernetes集群中如何有意义。我们将介绍特定的组件,例如应用程序编程接口(API)服务器和控制器管理器,它们可以使集群对状态更改做出反应。我们将从命令式API开始,然后浏览声明式API,并将看到如何应用文件和文件夹来应用Git存储库只是一个步骤——当执行它时,GitOps出现了。 我们将在本章中介绍以下主要主题:

  • 什么是GitOps?
  • Kubernetes和GitOps
  • 命令式和声明式API
  • 构建一个简单的GitOps操作符
  • 基础设施作为代码(IaC)和GitOps

1.1 技术要求

在本章节,你需要访问一个Kubernetes的集群和一个如minikube 这样的本地集群(https://minikube.sigs.k8s. io / docs /))或者kind (https://kind.sigs.k8s.)就可以了。我们将与集群交互并向它发送命令,因此你还需要安装kubectl (https://kubernetes.io/docs/tasks/tools/#kubectl))。 我们要写一些代码,所以需要一个代码编辑器。本人使用的是Visual Studio Code ( VS Code )(https://code.visualstudio.com)),我们要使用需要安装:https://golang.org(Go的当前版本是1.16.7;代码应该能和它一起使用)的Go语言。代码能在:https://golang.orghttps://github.com/PacktPublishing/ ArgoCD - in Practice的ch01的文件夹中找到。

1.2 什么是GitOps

GitOps一词是在2017年由Weaveworks的人创造的,他们也是名为Flux的GitOps工具的作者。从那以后,我看到了GitOps是如何成为一个流行词的,直到被命为development-operations(DevOps)之后的下一个重要事物。如果你搜索定义和解释,你会发很多:它被定义为通过 pull requests(PRs)进行操作(https://www.weave.works/blog/gitops-operations-by-pull-request)),或者采用开发实践(版本控制、协作、合规、持续集成/持续部署(CI/CD)),并将其运用于基础设施自动化。(https://about.gitlab.com/topics/gitops/ ) 不过,我认为有一个定义很突出。我指的是由GitOps工作组(https://github.com/gitops-working-group/gitops-working- group )创建的工作组,该工作组是Cloud Native Computing Foundation(CNCF)的Application Delivery Technical Advisory Group(Application DeliveryTAG)的一部分。Application DeliveryTAG是专门用于构建、部署、管理和操作云原生应用程序 (https://github.com/cncf/tag-app-delivery)。该工作组是由来自不同公司的人组成的,目的是为GitOps构建一个与供应商无关、以原则为主导的定义,所以我认为这些都是仔细研究他们工作的好理由。 该定义侧重于GitOps的原则,目前已经确定了五个原则(这仍是一个草案),如下:

  • 声明式的配置
  • 版本控制的不可变存储
  • 自动化交付
  • 软件代理
  • 闭环 它从声明式配置开始,这意味着我们想要表达我们的意图、结束状态,而不是要执行特定的动作。这不是一种命令式的风格,你说”让我们再启动三个容器“,而是声明想为这个应用程序拥有三个容器,代理将负责达到这个数字,这可能意味着如果现有五个容器,它需要停止两个正在运行的容器。Git在这里被称为版本控制和不可变控制,这是公平的,虽然它是目前最常用的源代码控制系统,但是它不是唯一的一个,我们可以用其他源代码控制系统实现GitOps。 自动交付意味着一旦更改到达版本控制系统 (VCS),我们就不应该有任何手动操作。配置更新后,软件代理将确保采取必要的操作来达到新的声明式配置。因为我们在表达想要的状态,所以需要计算达到它的动作。它们产生于系统的实际状态和版本控制的期望状态之间的差异——这就是闭环部分试图说明的。 虽然GitOps起源于Kubernetes世界,但是这个定义试图将它从图片中拿出来,并将前面的原则引入整个软件世界。在我们的案例中,看看是什么使GitOps成为可能,并深入了解Kubernetes中的软件代理是什么,或者闭环在这里是如何运行的,仍然很有趣。

1.3 Kubernetes 和 GitOps

现在很难不听说Kubernetes—它可能是目前最知名的开源项目之一。它起源于2014年左右,当时谷歌的一群工程师开始根据他们与谷歌自己的名为Borg的内部协调器合作积累的经验来构建一个容器协调器。该项目于2014开源,并在2015年达到1.0.0版本,这是一个里程碑,鼓励许多公司仔细研究它。 另一个导致它被社区迅速且被狂热的采用的原因是CNCF的治理(https://www.cncf.io))。在使该项目开源之后,谷歌与Linux基金会 (https://www.linuxfoundation.org)讨论创建一个新的非营利组织,该组织将领导开源云原生技术的采用。当Kubernetes成为它的种子项目且KubeCon是它的主要开发者大会时,CNCF就是这样被创建的。当我说到CNCF的治理时,我主要指的是这样一个事实,即CNCF内部的每个项目或组织都有一个完善的维护者结构,并详细的说明了他们是如何被提名的,这些团队是如何做决定的,没有一家公司能拥有一个简单的多数。这确保了在没有社区参与的情况下不会做出任何决定,并确保整个社区在项目生命周期中扮演着重要的角色。

1.3.1 体系结构

代码语言:javascript
复制
Kubernetes变得如此的庞大和可扩展以至于如果不用抽象的概念(比如构建平台的平台)就真的很难去定义它。这是因为它仅仅只是一个起点——你会得到很多部分,但你必须以一种适合你的方式去把它们组合在一起(GitOps就是其中一部分)。如果我们说它只是一个容器编排平台,这并不完全正确,因为你也可以用它来运行虚拟机(VMs),而不仅仅是容器(更多详细信息,请查看[https://ubuntu.com/blog/what-is-kata-containers](https://ubuntu.com/blog/what-is-kata-containers)),不过,编排部分仍然是正确的。

它的组件分为两个主要部分——第一个是控制平面,它由一个 REpresentational State Transfer(REST)API服务器和一个用于存储的数据库(通常是etcd)组成、一个用于运行多个控制环路的控制器管理器,一个调度器负责为我们的Pod分配节点(Pod是一个容器的逻辑分组,有助于在同一节点上运行它们。更多信息请访问https://kubernetes.io/docs/concepts/workloads/pods/),一个云控制器管理器,用于处理任何云特定的工作。第二部分是数据平面,控制平面是关于管理集群的,而这一部分是关于运行用户工作负载的节点上发生的事情。作为Kubernetes集群的一部分节点将具有容器运行时(可以是Docke、CRI-O或 containerd ,和其他一些),Kubelet,负责 REST API服务器和节点的容器运行时之间的连接,以及Kube-proxy负责在节点级别抽象网络。有关所有组件如何协同工作以及API服务器所扮演的中心角色的详细信息,请参阅下一个表。 我们不会详细介绍所有这些组成部分;相反,对于我们来说,使声明性部分成为可能的REST API 服务器和使系统收敛到所需状态的控制器管理器很重要,所以我们想对它们进行一点剖析。 下图显示了一个典型的Kubernetes体系结构的概述:

请注意: 当查看架构图时,你需要知道它只能捕捉到整个画面的一部分。例如,在这里,带有API的云提供商似乎时一个外部系统,但实际上,所有节点和控制平面都是在该云提供商中创建的。

1.3.2 HTTP REST API服务器

从超文本传输协议(HTTP) REST API服务器的角度来看Kubernetes,它就像任何具有REST端点和用于存储状态的数据库的经典应用程序一样,在我们的例子中,通常是etcd和web服务器的多个副本以实现高可用性(HA)。需要强调的是,我们想用Kubernetes做的任何事都需要通过API来完成;我们不能直接连接到其他软件,对于内部组织也是一样:它们之间不能直接对话,它们需要通过API。 从我们的客户端机器上,我们不直接查询API(例如使用curl),而是使用这个kubectl客户端应用程序,它隐藏了一些复杂性,例如身份验证标头、准备请求内容、解析响应正文等。 每当我们执行kubectl get pods之类的命令时,都会有对API服务器的HTTP Secure(HTTPS)进行调用。然后,服务器转到数据库以获取有关Pods的详细信息,然后创建一个响应并将其推送回到客户端。kubectl客户端应用程序接收并解析它,然后能够显示适合人类阅读者的输出。为了了解具体发生了什么,我们可以使用kubectl(–v)的全局标志,我们为其设置的值越高,我们获得的详细信息就越多。 对于一个练习,请尝试kubectl get pods–v=6,当它只显示执行了GET请求时,并将–v不断增加到7、8、9等,以便你可以看到HTTP请求标头、响应标头、部分或全部JavaScript对象表示法(JSON)响应以及许多其他详细信息。 API服务器本身并不负责实际更改集群的状态——它使用新值更新数据库,并根据这些更新,还会发生其他事情。实际的状态更改是由控制器和如调度器或kubelet等组件完成的。我们将深入了解控制器,因为它们对我们理解GitOps很重要。

1.3.3 控制器管理器

当阅读到有关Kubernetes的文章(或者听播客)时,你会经常听到controller这个词。它背后的想法来自工业自动化或机器人,它是关于转换控制回路的。 假设我们有一个机械臂,我们给它一个简单的命令,让它在90度的位置上移动。它要做的第一件事就是分析它的当前状态;或许它已经在90度了,没有什么可做的。如果它不在正确的位置上的话,接下来的事情就是计算到达那个位置而采取的行动,然后,它将尝试应用这些行动来到达它的相对位置。 我们从观察阶段开始,在该阶段,我们将所需状态与当前状态进行比较,然后是差异阶段,我们计算要应用的操作,在操作阶段,我们执行这些操作。同样,在我们执行操作之后,它将开始观察阶段,看看它是否位于正确的位置;如果没有(可能有什么东西阻止了它到达那里),就会计算操作,然后我们开始应用操作,以此类推,直到它到达某个位置或可能耗完电池或者其他什么的。这个控制循环一直持续下去,直到在观察阶段,当前状态与所需状态匹配,因此无需任何操作计算和应用。你可以在下图中看到该流程的表示:

图1.2-控制回路 在Kubernetes中,有许多控制器。我们有以下内容:

  • ReplicaSet :https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/
  • HorizontalPodAutoscaler (HPA):https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
  • 其他的一些可以在这里找到,但这不是一个完整的列表:https://kubernetes.io/docs/concepts/workloads/controllers/ ReplicaSet控制器负责运行固定数量的Pods。你通过kubectl创建了它,并要求运行三个实例,这就是所期望的状态。因此,它从检查当前状态开始:当前有多少Pods在运行;它计算要执行的操作:要启动或终止多少Pods才能拥有三个实例;然后执行这些操作。还有HPA控制器,根据一些指标,它能够增加或减少部署的Pods数量(部署是建立在Pods和ReplicaSets 之上的结构,允许我们定义更新Pods的方法https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)),而 Deployment 依赖于它内部构建的ReplicaSet控制器来更新Pods的数量。修改数量后,仍然是ReplicaSet控制器运行控制循环以达到所需Pods的数量。 控制器的工作是确保实际状态与期望状态相匹配,并且它们永远不会停止尝试达到最终状态。而且,不仅如此,它们还专门处理资源类型——每个资源都负责集群中的一小个部分。 在前面的示例中,我们讨论了内部Kubernetes控制器,但我们也可以编写自己的控制器,这就是Argo CD的真正含义——一个控制器,它的控制循环负责确保Git存储库中声明的状态与集群中的状态相匹配。实际上,正确地说,它不是一个控制器,而是一个操作符,区别在于控制器处理内部Kubernetes对象,而操作符处理两个域:Kubernetes和其他域。在我们的案例中,Git存储库是由操作员处理的外部部分,它使用称为自定义资源的东西来完成此操作,这是一种扩展Kubernetes功能的方式(https://kubernetes.io/docs/concepts/ extend-kubernetes/api-extension/custom-resources/)。 到目前为止,我们已经了解了Kubernetes体系结构与API服务器连接所有组件,以及控制器如何始终在控制循环中工作,以使集群达到所需状态。接下来,我们将详细介绍如何定义所需状态:我们将从命令式方式开始,继续使用更重要的声明式方式,并展示所有这些如何使我们离GitOps更进一步。

1.4 命令式和声明式API

我们讨论了一点命令式风格和声明式风格之间的区别,命令式风格明确指定要采取的操作——比如启动三个以上的pods——而声明式风格则指定你的意图——比如部署中应该有三个正在运行的pods——并且需要计算操作(如果已经运行了三个pods,你可能会增加或减少pods,或者什么都不做)。命令式和声明式方法都会在Kubectl客户端中实现。

1.4.1 命令式——直接命令

无论我们何时创建、更新或删除Kubernetes对象时,我们都可以使用命令式的方式来完成。要创建命名空间,请运行以下命令:

代码语言:javascript
复制
kubectl create namespace test-imperative

然后,为了看到创建的命名空间,使用以下命令:

代码语言:javascript
复制
kubectl get namespace test-imperative

在该命名空间中创建一个部署,如下所示:

代码语言:javascript
复制
kubectl create deployment nginx-imperative --image=nginx -n 
test-imperative 

然后,你可以使用以下命令查看创建的部署:

代码语言:javascript
复制
kubectl get deployment -n test-imperative nginx-imperative

要更新我们创建的任何资源,我们可以使用特定的命令,例如kubectl label来修改资源标签,kubectl scale来修改Deployment、ReplicaSet、StatefulSet或kubectl set中的pods数量,或者Kubectl set用于更改环境变量(kubect1 set env)、容器映像(kubectl set image)、容器资源(kubect1 set resources)等。 如果你想给命名空间添加一个标签,你可以运用以下命令:

代码语言:javascript
复制
kubectl label namespace test-imperative namespace=imperative-apps

最后,你可以用以下命令删除之前创建的对象:

代码语言:javascript
复制
bectl delete deployment -n test-imperative nginx-imperative
kubectl delete namespace test-imperative

命令式命令很清楚它们的作用,当你将它们用于命名空间这样的小对象时,它是有意义的。但是对于更复杂的,比如部署,我们最终可以给它传递很多标志,比如指定一个容器镜像、镜像标签、拉取策略,如果一个秘密被链接到一个拉取(对于私有映像注册表),对于init容器和许多其他选项也是如此。接下来,让我们看看是否有更好的方法来处理这么多可能的标志。

1.4.2 命令式——配置文件

命令式命令还可以使用配置文件,这使事情变得更容易,因为它们大大减少了我们需要传递给命令式命令的标志数量。我们可以使用一个文件来说明我们想要创建什么。 这就是命名空间配置文件的样子——尽可能最简单的版本(没有任何标签或注释)。以下文件也可以在https://github.com/PacktPublishing/ArgoCD-in-Practice/tree/main/ch01/imperative-config中找到。 将以下内容复制到一个名为namespace.yaml的文件中。

代码语言:javascript
复制
apiVersion: v1
kind: Namespace
metadata:
 name: imperative-config-test

然后,执行以下命令:

代码语言:javascript
复制
 kubectl create -f namespace.yaml  

复制以下内容并保存到一个名为deployment.yaml的文件中:

代码语言:javascript
复制
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 namespace: imperative-config-test
spec:
 selector:
 matchLabels:
 app: nginx
 template:
 metadata:
 labels:
 app: nginx
 spec:
 containers:
 - name: nginx
 image: nginx

然后,执行以下命令:

代码语言:javascript
复制
kubectl create -f deployment.yaml

通过运行前面的命令,我们创建了一个命名空间和一个Deployment,类似于我们使用命令式直接命令所做的事情。你可以看到,这比将所有标志传递给kubectl create deployment要容易得多。更重要的是,并非所有字段都可作为标志使用,因此在许多情况下,使用配置文件可能会成为强制性的。 我们也可以通过配置文件修改对象。下面是一个如何向命名空间添加标签的示例。用以下内容更新我们之前使用的命名空间(注意以标签开头的额外两行)。更新后的命名空间可以在官方的https://github.com/PacktPublishing/ArgoCD-in-Practice/tree/main/ch01/imperative-config中看到,配置命名空间中的存储库-with-labels.yaml文件:

代码语言:javascript
复制
apiVersion: v1
kind: Namespace
metadata:
 name: imperative-config-test
 labels:
 name: imperative-config-test

然后,我们可以执行以下命令:

代码语言:javascript
复制
kubectl replace -f namespace.yaml

然后,要查看是否添加了标签,请执行以下命令:

代码语言:javascript
复制
kubectl get namespace imperative-config-test -o yaml

与将所有标志传递给命令相比,这是一个很好的改进,并且可以将这些文件存储在版本控制中,以供将来参考。但是,如果资源是新的,你需要指定你的意图,因此你使用kubectl create,而如果它存在,你使用kubectl replace也有一些限制:kubectl replace命令执行一个完整的对象更新,因此如果有人在两者之间修改了其他东西(例如在命名空间中添加注释),这些更改将丢失。

1.4.3 声明式——配置文件

我们刚刚看到了使用配置文件创建内容是多么容易,如果我们可以修改配置文件并在其中调用某个updat e/sync命令,那就太好了。我们可以修改文件中的标签,而不是使用kubectl标签,也可以对其他更改执行同样的操作,例如缩放部署的Pod、设置容器资源、容器镜像等。还有这样一个命令,你可以向它传递任何文件,无论是新的还是修改的,它将能够对API服务器做出正确的调整:kubectl apply。 请创建名为description-files的新文件夹,并将命名空间.yaml文件,内容如下(这些文件也可以在https://github.com/上找到。打包发布/ArgoCD-实践/tree/main/ch01/description-files):

代码语言:javascript
复制
apiVersion: v1 
kind: Namespace  
 metadata: 
    name: declarative-files 

然后,请执行以下命令:

代码语言:javascript
复制
kubectl apply -f declarative-files/namespace.yaml

控制台的输出应该是这样的:

代码语言:javascript
复制
namespace/declarative-files created

接下来,我们可以修改命名空间。yalm文件,并直接在文件中为其添加标签,如下所示:

代码语言:javascript
复制
apiVersion: v1
kind: Namespace
metadata:
 name: declarative-files
 labels:
 namespace: declarative-files

然后,再次执行以下命名:

代码语言:javascript
复制
kubectl apply -f declarative-files/namespace.yaml

控制台输出应该如下所示:

代码语言:javascript
复制
namespace/declarative-files configured

** **在上述两个案例中发生了什么?在运行任何命令之前,我们的客户端(或我们的服务器-本章将进一步说明何时使用客户端应用或服务器端应用)将集群中的现有状态与文件中的所需状态进行比较,从而能够计算为达到所需状态而需要应用的操作。在第一个应用示例中,它发现名称空间不存在,因此需要创建名称空间;而在第二个应用示例中,它发现名称空间存在,但没有标签,因此添加了一个标签。 接下来,让我们在它自己名为部署的文件中添加Deployment.yaml的文件在相同的声明式文件夹中,如下所示:

代码语言:javascript
复制
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
 namespace: declarative-files
spec:
 selector:
 matchLabels:
 app: nginx
 template:
 metadata:
 labels:
 app: nginx
 spec:
 containers:
 - name: nginx
 image: nginx

我们将运行以下命令,在命名空间中创建一个部署:

代码语言:javascript
复制
 kubectl apply -f declarative-files/deployment.yaml  

如果你需要,你可以对部署进行更改。Yaml文件(标签、容器资源、映像、环境变量等),然后运行kubectl apply命令(完整的是前面的那个),你所做的更改将应用到集群上。

1.4.4 声明式—配置文件夹

在本节中,我们将创建一个名为declarative - folder的新文件夹,并在其中创建两个文件。 这是命名空间的内容。Yaml文件(代码也可以在这里找到:https://github.com/PacktPublishing/ArgoCD-in-Practice/tree/main/ch01/declarative-folder):

代码语言:javascript
复制
apiVersion: v1
kind: Namespace
metadata:
 name: declarative-folder

这是 deployment.yaml 文件夹的内容:

代码语言:javascript
复制
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx
 namespace: declarative-folder
spec:
 selector:
 matchLabels:
 app: nginx
 template:
 metadata:
 labels:
 app: nginx
 spec:
 containers:
 - name: nginx
 image: nginx

然后,我们将执行以下命令:

代码语言:javascript
复制
kubectl apply -f declarative-folder

最有可能的是,你会看到以下错误,这是意料之中的,所以不用担心:

代码语言:javascript
复制
namespace/declarative-folder created
Error from server (NotFound): error when creating "declarativefolder/deployment.yaml": namespaces "declarative-folder" not 
found

** **这是因为这两个资源是同时创建的,但是部署依赖于名称空间,所以当需要创建部署时,需要准备好名称空间。我们看到消息说创建了一个名称空间,但是API调用是同时在服务器上完成的,因此当部署启动其创建流时,名称空间不可用。我们可以通过再次运行以下命令来修复这个问题:

代码语言:javascript
复制
 kubectl apply -f declarative-folder  

在控制台中,我们应该看到以下输出:

代码语言:javascript
复制
deployment.apps/nginx created
namespace/declarative-folder unchanged

由于名称空间已经存在,因此可以在不更改名称空间的情况下在其中创建部署。 kubectl apply命令获取声明式文件夹的全部内容,对在这些文件中找到的每个资源进行计算,然后使用API服务器进行更改。我们可以应用整个文件夹,而不仅仅是文件,尽管如果资源相互依赖的话,我们可以修改这些文件并调用文件夹的apply命令,更改将得到应用。现在,如果这就是我们在集群中构建应用程序的方式,那么我们最好将所有这些文件保存在源代码管理中,以备将来参考,这样在一段时间后应用更改就会变得更容易。 但是,如果我们可以直接应用Git存储库,而不仅仅是文件夹和文件呢?毕竟,本地Git存储库就是一个文件夹,而最终,GitOps操作符就是这样的:一个知道如何使用Git存储库。 请注意 apply命令最初完全在客户端上实现。这意味着查找更改的逻辑是在客户端上运行,然后在服务器上调用特定的命令APIs。但最近,应用逻辑转移到了服务器端;所有对象都有一个apply方法(从REST API的角度来看,它是一个PATCH方法,带有一个application/apply-patch+yaml内容类型头),并且从版本1.16开始默认启用该功能(更多相关信息请访问:https://kubernetes.io/docs/reference/using-api/server-side-apply/)。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.1 技术要求
    • 1.2 什么是GitOps
      • 1.3 Kubernetes 和 GitOps
        • 1.3.1 体系结构
        • 1.3.2 HTTP REST API服务器
        • 1.3.3 控制器管理器
      • 1.4 命令式和声明式API
        • 1.4.1 命令式——直接命令
        • 1.4.2 命令式——配置文件
        • 1.4.3 声明式——配置文件
        • 1.4.4 声明式—配置文件夹
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档