Kubernetes编程——什么是 Kubernetes 编程?
什么是 Kubernetes 编程?
这里的 Kubernetes编程 指开发原生 Kubernetes 应用,这类应用通过与 API 服务器进行开发,直接查询、更新资源的状态。
这里不会在 `Controller` 和 `Operator` 中,这里也不会过多关注操作层面的东西,而是会关注开发和测试的阶段。
因此,我们会聊下关于如何开发纯正的云原生应用。
一、举个例子
我们把这个例子叫做 `cnat`,其工作逻辑是:假如你想让 cnat 在 2023年5月21日凌晨2点运行命令echo "Hello, Shanghai!",你可以这么做:
1 2 3 4 5 6 7 8 9 10 11 12 13 | apiVersion: cnat.programming-kubernetes.info/v1alpha1 kind: At metadata: name: cnrex spec: schedule: "2023-05-21T02:00:00Z" containers: - name: shell image: centos:7 command: - "bin/bash" - "-c" - echo "Hello, Shanghai!" |
二、扩展模式
Kubernetes 是一个强大且天然可扩展的系统。有很多种方法来定制、扩展 Kubernetes,比如:修改控制平面组件的配置文件和参数,或者通过各种预定义的扩展点。
三、控制器和Operator
在 kubernetes 的术语中,控制器实现了一个控制循环,它通过 API 服务器观测集群中的共享状态,进行必要的变更,尝试把当前状态转换到期望的目标状态。
- 控制器可以对 Kubernetes 的核心资源(比如 Deployment 或 Service)进行操作,通常是控制平面中 Kubernetes 控制器管理的一部分,它们也可以观察并操作用户自定义的资源。
- Operator 也是一种控制器,但它会包含一些运维逻辑,比如应用程序的生命周期管理。
控制器进程通常会在集群中的一个 Pod 里运行。
3.1、控制循环
控制循环逻辑如下:
1、读取资源的状态,通常采用事件驱动模式(使用 watch 来实现)。
2、改变集群中的对象状态或集群外部系统的状态。
3、通过 API服务更新到第1步的资源状态,这个状态是放在 etcd 中。
4、循环执行这些逻辑,返回第1步骤。
一个控制器通常包含以下的数据结构:
Informer:负责观察资源的目标状态,这个过程需要是可伸缩和可持续的。
工作队列:事件处理器把状态变化情况放入工作队列,用于保证在必要时可以进行重试。
3.2、事件
Kubernetes 的控制器会通过 API 服务器来观察 Kubernetes 对象的变化情况:添加、更新或删除。如果发生了这样的事件,控制器就开始执行对应的业务逻辑。
比如,想要通过一个 Deployment 来启动一个 Pod,一系列的控制器和控制平面中的组件需要协同工作:
1、Deployment 的控制器(在 kube-controller-manager 中)会发现(通过 Deployment Informer)用户创建了一个 Deployment,并执行其相关的业务逻辑;创建一个 ReplicaSet 对象。
2、ReplicaSet 的控制器(同样在 kube-controller-manager 中)会发现(通过 ReplicaSet Informer)这个新创建的 ReplicaSet,并执行其相关的业务逻辑:创建一个 Pod 对象。
3、调度器(在 kube-scheduler 中)本身是一个控制器,它发现了新创建的 Pod(通过 Pod Informer),并且这个 Pod 的 spec.nodeName 字段是空值。调度器执行的业务逻辑就是把这个 Pod 放入它的调度队列。
4、与此同时,kubelet(它是一个控制器)发现了这个新创建的 Pod(通过它的 Pod Informer)。
5、由于这个新的 Pod 的 spec.nodeName 字段是空值,与本 kubelet 所在节点的名字不同,所以它忽略了这个 Pod,并继续等待下一个时间到来。
6、调度器把 Pod 从工作队列中取出来,把它调度到一个具备足够资源的节点的名字不同,并把节点的名字更新到 Pod 的 spec.nodeName 字段中,然后把它写入 API 服务器中。
7、由于上一步操作产生了新的 Pod 更新事件,kubelet 再次被唤醒。它再次比较 Pod 的 spec.nodeName 字段值与自身所在节点的名字。如果两个名字一致,这个 kubelet 就会启动 Pod 所需要的容器,并把容器启动成功的信息写入 Pod 的 Status 中,回报到 API 服务器上。
8、ReplicaSet 控制器收到 Pod 状态变化的事件中,不过它没有社么工作要做。
9、最终,Pod 运行结束。kubelet 发现了这个事件,从 API 服务器获取 Pod 的对象,并把 Pod 的状态设置为 “已终止”,写回 API 服务器。
10、ReplicaSet 控制器发现 Pod 运行结束了,它认为这个 Pod 需要被替换掉。它把已终止的 Pod 在 API 服务器上删除,然后重新创建一个新的 Pod。
多个相互独立的控制器循环完全通过 API 服务器上的对象状态变化相互通信,对象状态的变化由 Informer 以事件的方式通知到控制器。这些事件都是通过 watch 机制在控制器内部由从 API 服务器发向 Informer,也就是流式发送的 watch 事件。这一切对用户来说都是不可见的。甚至在 API 服务器的审计机制中也看不到这些事件,只能看到对象更新的事件。控制器通常在处理事件时,以输出日志的方式来记录它做的动作。
- watch 事件在 API 服务器与控制器之间通过 HTTP 流的方式发送,用于驱动 Informer。
- 顶层 event 对象是与 Pod、Deployment 或 Service 等相似的资源,它具有一个特殊属性,即长度为一小时的生命周期。如果这个对象的存在事件超过了这个阈值,就会从 etcd 中自动清除。
四、API 服务器
kubernetes 集群是由一系列节点组成(kubernetes master nodes 和 kubernetes worker nodes),这些节点属于不同的角色。kubernetes master nodes 包含 API 服务器、控制器管理器和调度器组成。
API 服务器包含以下的职责:
1、提供 kubernetes API。
2、为其他集群组件提供代理。
API 服务器提供的 API 包含以下功能:
1、读取状态:获取单个对象、列出对象、获取变更信息。
2、操作状态:创建、更新或删除对象。
所有的状态都会被持久化在 etcd 中。
4.1、 API 服务器的 HTTP 接口
从客户端的角度来看,API 服务器提供了一个 RESTful HTTP API,提供 JSON 或 Protocol Buffers(缩写为 Protobuf)格式的数据内容。出于性能考虑,集群内的通信主要使用 Protobuf。
API 服务器的 HTTP 接口负责处理 HTTP 请求,这些请求通过指定不同的 HTTP 动词(HTTP Verb,也就 HTTP 方法)来对 kubernetes 的资源进行不同的操作:
- HTTP GET 方法用于获取特定资源(比如一个 Pod)的相关数据,或者列出一组资源(比如,列出一个命名空间中的所有 Pod)。
- HTTP POST 方法用于创建资源,比如创建一个 Service 或者一个 Deployment。
- HTTP PUT 方法用于创建已有的资源,比如:修改一个 Pod 的容器所使用的镜像。
- HTTP PATCH 方法用于部分更新已有的资源。
- HTTP DELETE 方法用于销毁一个资源,这个操作是不可逆的。
kubernetes API 参考文档:https://kubernetes.io/docs/reference/#api-overview
4.2、API 术语
Kind
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
Kind 一共有 3 种类型的类别:
Object 用于表示系统中的持久化的实体对象,比如,Pod 或 Endpoint。这类对象需要名字,并且它们中的大部分都从术语某个命名空间。
List 是一种或多种实体的列表集。这些列表对象包含少量的公共信息,比如 PodList 或 NodeList。当你执行 kubectl get pods 时就会得到这样一个对象。
其他特殊用途的 kind 会用于某些对象上的特定动作或用于一些非持久化的实体,比如 /binding 或者 /scale。 kubernetes 使用 apigroup 和 apiresource 来提供服务发现结果,使用 Status 来返回出错误结果。
在 Kubernetes 中,每一种 kind 都对应一种 Golang 的类型。所以,与 Golang 其他的类型一样,kind 名字都是以单数形式出现,并以大写字母开头。
API 组
一组逻辑上相关的 Kind。比如所有的批对象,Job 或 ScheduledJob 都属于 batch 这个 API 组。
版本
每个 API 组都允许多个版本共存,并且它们大部分也确实如此。比如有个组在 v1alpha1 版本中出现,持续到 v1beta1 版本并最终在 v1 中正式发布。
在某个特定版本下(比如 v1beat1)创建的对象,可以在其他支持的版本中获取并使用。API 服务器会负责把返回的对象无损地转换成需要的版本。
从集群用户的角度来看,不同的版本只是同一个对象的不同表示方式。
资源
通常来说,小写复数形式的单词(比如 pods)会出现在 HTTP 端点(路径)中,用于暴露对于系统中某种对象的 CURD(创建、读取、更新、删除)操作。
-
- 根路径,比如 .../pods,用于列出对应类型的所有势力。
- 提供资源的某个特定实例的路径,比如 .../pods/nginx。
通常来说,这些端点会返回或接受一种类型(比如第一种情况下就是一个 PodList,第二种情况下是一个 Pod)。但在其他情况下(比如出错),也可能返回一个 Status 类型的对象。
资源和型别经常会被混淆,它们的区别在于:
-
- 资源都会对应一个 HTTP 路径。
- 型别是通过这些 HTTP 端点返回或接收的对象类型,也是持久化到 etcd 中的实体。
资源总是从属于某个 API 组的某个版本(经常表示为 GroupVersionResource 或 GVR),一个 GVR 唯一确定了一个 HTTP 路径。
例如:在 default 命名空间中,有一个 /apis/batch/v1/namespaces/default/jobs 的 GVR。与 jobs GVR 的例子不同,还有一些集群范围的资源,比如节点或命名空间自身,它们的路径中就不包含这一段。比如一个 nodes GVR 的路径可能是 /api/v1/nodes。注意,$NAMESPACE 在其他 HTTP 路径中看到的命名空间,它们自身也是一种资源,可以通过 /api/v1/namespaces 来访问。
与 GVR 相似,各种型别也是属于某个 API 组的某个版本的,它们被称为 GroupVersionKind(GVK)。
GVK 和 GVR 是相关的,比如 某个 GVR 定义的 HTTP 路径下会提供对相关 GVK 的服务。对 GVK 和 GVR 建立映射关系的过程叫作 REST 映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | / ├── / ├── │ ├── //batchbindings ├── │ ├── //bindings ├── │ ├── //componentstatuses ├── │ ├── //configmaps ├── │ ├── //endpoints ├── │ ├── //events ├── │ ├── //limitranges ├── │ ├── //namespaces ├── │ ├── //namespaces/finalize ├── │ ├── //namespaces/status ├── │ ├── //nodes ├── │ ├── //nodes/proxy ├── │ ├── //nodes/status ├── │ ├── //persistentvolumeclaims ├── │ ├── //persistentvolumeclaims/status ├── │ ├── //persistentvolumes ├── │ ├── //persistentvolumes/status ├── │ ├── //pods ├── │ ├── //pods/attach ├── │ ├── //pods/binding ├── │ ├── //pods/eviction ├── │ ├── //pods/exec ├── │ ├── //pods/log ├── │ ├── //pods/portforward ├── │ ├── //pods/proxy ├── │ ├── //pods/status ├── │ ├── //podtemplates ├── │ ├── //replicationcontrollers ├── │ ├── //replicationcontrollers/scale ├── │ ├── //replicationcontrollers/status ├── │ ├── //resourcequotas ├── │ ├── //resourcequotas/status ├── │ ├── //secrets ├── │ ├── //serviceaccounts ├── │ ├── //serviceaccounts/token ├── │ ├── //services ├── │ ├── //services/proxy ├──└── │ ├── //services/status ├── /apiregistration .k8s.io ├── │ ├── /apiregistration .k8s.io /apiservices ├──└── │ ├── /apiregistration .k8s.io /apiservices/status ├── /extensions ├── │ ├── /extensions/ingresses ├──└── │ ├── /extensions/ingresses/status ├── /apps ├── │ ├── /apps/controllerrevisions ├── │ ├── /apps/daemonsets ├── │ ├── /apps/daemonsets/status ├── │ ├── /apps/deployments ├── │ ├── /apps/deployments/scale ├── │ ├── /apps/deployments/status ├── │ ├── /apps/replicasets ├── │ ├── /apps/replicasets/scale ├── │ ├── /apps/replicasets/status ├── │ ├── /apps/statefulsets ├── │ ├── /apps/statefulsets/scale ├──└── │ ├── /apps/statefulsets/status ├── /events .k8s.io ├──└── │ ├── /events .k8s.io /events ├── /authentication .k8s.io ├──└── │ ├── /authentication .k8s.io /tokenreviews ├── /authorization .k8s.io ├── │ ├── /authorization .k8s.io /localsubjectaccessreviews ├── │ ├── /authorization .k8s.io /selfsubjectaccessreviews ├── │ ├── /authorization .k8s.io /selfsubjectrulesreviews ├──└── │ ├── /authorization .k8s.io /subjectaccessreviews ├── /autoscaling ├── │ ├── /autoscaling/horizontalpodautoscalers ├──└── │ ├── /autoscaling/horizontalpodautoscalers/status ├── /batch ├── │ ├── /batch/jobs ├──└── │ ├── /batch/jobs/status ├── /certificates .k8s.io ├── │ ├── /certificates .k8s.io /certificatesigningrequests ├── │ ├── /certificates .k8s.io /certificatesigningrequests/approval ├──└── │ ├── /certificates .k8s.io /certificatesigningrequests/status ├── /networking .k8s.io ├── │ ├── /networking .k8s.io /ingressclasses ├── │ ├── /networking .k8s.io /ingresses ├── │ ├── /networking .k8s.io /ingresses/status ├──└── │ ├── /networking .k8s.io /networkpolicies ├── /policy ├── │ ├── /policy/poddisruptionbudgets ├── │ ├── /policy/poddisruptionbudgets/status ├──└── │ ├── /policy/podsecuritypolicies ├── /rbac .authorization.k8s.io ├── │ ├── /rbac .authorization.k8s.io /clusterrolebindings ├── │ ├── /rbac .authorization.k8s.io /clusterroles ├── │ ├── /rbac .authorization.k8s.io /rolebindings ├──└── │ ├── /rbac .authorization.k8s.io /roles ├── /storage .k8s.io ├── │ ├── /storage .k8s.io /csidrivers ├── │ ├── /storage .k8s.io /csinodes ├── │ ├── /storage .k8s.io /storageclasses ├── │ ├── /storage .k8s.io /volumeattachments ├──└── │ ├── /storage .k8s.io /volumeattachments/status ├── /admissionregistration .k8s.io ├── │ ├── /admissionregistration .k8s.io /mutatingwebhookconfigurations ├──└── │ ├── /admissionregistration .k8s.io /validatingwebhookconfigurations ├── /apiextensions .k8s.io ├── │ ├── /apiextensions .k8s.io /customresourcedefinitions ├──└── │ ├── /apiextensions .k8s.io /customresourcedefinitions/status ├── /scheduling .k8s.io ├──└── │ ├── /scheduling .k8s.io /priorityclasses ├── /coordination .k8s.io ├──└── │ ├── /coordination .k8s.io /leases ├── /node .k8s.io ├──└── │ ├── /node .k8s.io /runtimeclasses ├── /discovery .k8s.io ├──└── │ ├── /discovery .k8s.io /endpointslices ├── /crd .yangtse.cni ├── │ ├── /crd .yangtse.cni /podnetworkinterfacepools ├── │ ├── /crd .yangtse.cni /podnetworkinterfacepools/status ├── │ ├── /crd .yangtse.cni /podnetworkinterfaces ├── │ ├── /crd .yangtse.cni /podnetworkinterfaces/status ├── │ ├── /crd .yangtse.cni /podnetworkinterfaceclaims ├── │ ├── /crd .yangtse.cni /podnetworkinterfaceclaims/status ├──└── │ ├── /crd .yangtse.cni /podnetworkinterfaceqosconfigs ├── /k8s .cni.cncf.io ├──└── │ ├── /k8s .cni.cncf.io /network-attachment-definitions ├── /localvolume .everest.io ├──└── │ ├── /localvolume .everest.io /nodelocalvolumes ├── /config .caicloud.io ├── │ ├── /config .caicloud.io /configclaims ├──└── │ ├── /config .caicloud.io /configreferences ├── /geip .cce.io ├── │ ├── /geip .cce.io /geips ├──└── │ ├── /geip .cce.io /geips/status ├── /microservice .caicloud.io ├──└── │ ├── /microservice .caicloud.io /springclouds ├── /networking .caicloud.io ├──└── │ ├── /networking .caicloud.io /networkpolicies ├── /orchestration .caicloud.io ├── │ ├── /orchestration .caicloud.io /applicationdrafts ├──└── │ ├── /orchestration .caicloud.io /applications ├── /release .caicloud.io ├── │ ├── /release .caicloud.io /releases ├──└── │ ├── /release .caicloud.io /releasehistories ├── /tenant .caicloud.io ├── │ ├── /tenant .caicloud.io /partitions ├── │ ├── /tenant .caicloud.io /tenants ├──└── │ ├── /tenant .caicloud.io /clusterquotas ├── /loadbalance .caicloud.io ├──└── │ ├── /loadbalance .caicloud.io /loadbalancers ├── /config .k8s.io ├──└── │ ├── /config .k8s.io /nodeconfigs ├── /rbac .cce.io ├──└── │ ├── /rbac .cce.io /permissions ├── /resource .caicloud.io ├── │ ├── /resource .caicloud.io /configs ├── │ ├── /resource .caicloud.io /nodelocalstorages ├── │ ├── /resource .caicloud.io /machineautoscalinggroups ├── │ ├── /resource .caicloud.io /requirementgaps ├── │ ├── /resource .caicloud.io /snapshots ├── │ ├── /resource .caicloud.io /clusters ├── │ ├── /resource .caicloud.io /extendedresources ├── │ ├── /resource .caicloud.io /nodeclaims ├── │ ├── /resource .caicloud.io /networks ├── │ ├── /resource .caicloud.io /networks/status ├── │ ├── /resource .caicloud.io /machines ├── │ ├── /resource .caicloud.io /tags ├── │ ├── /resource .caicloud.io /infranetworks ├──└── │ ├── /resource .caicloud.io /resourceclasses ├── /snapshot .storage.k8s.io ├── │ ├── /snapshot .storage.k8s.io /volumesnapshots ├── │ ├── /snapshot .storage.k8s.io /volumesnapshots/status ├── │ ├── /snapshot .storage.k8s.io /volumesnapshotclasses ├── │ ├── /snapshot .storage.k8s.io /volumesnapshotclasses/status ├── │ ├── /snapshot .storage.k8s.io /volumesnapshotcontents ├──└── │ ├── /snapshot .storage.k8s.io /volumesnapshotcontents/status ├── /version .cce.io ├── │ ├── /version .cce.io /packageversions ├──└── │ ├── /version .cce.io /packageversions/status ├── /workload .caicloud.io ├──└── │ ├── /workload .caicloud.io /workloads ├── /custom .metrics.k8s.io ├── /metrics .k8s.io ├── │ ├── /metrics .k8s.io /nodes ├──└── │ ├── /metrics .k8s.io /pods └── ├── /proxy .exporter.k8s.io └── ├──└── │ ├── /proxy .exporter.k8s.io /exporter |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具