Kubernetes编程——什么是 Kubernetes 编程?

什么是 Kubernetes 编程?

 

  这里的 Kubernetes编程 指开发原生 Kubernetes 应用,这类应用通过与 API 服务器进行开发,直接查询、更新资源的状态。

  这里不会在 `Controller` 和 `Operator` 中,这里也不会过多关注操作层面的东西,而是会关注开发和测试的阶段。

  因此,我们会聊下关于如何开发纯正的云原生应用。

一、举个例子

  我们把这个例子叫做 `cnat`,其工作逻辑是:假如你想让 cnat 在 2023年5月21日凌晨2点运行命令echo "Hello, Shanghai!",你可以这么做:

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 映射。

 

/
├── /
├── │   ├── //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
posted @ 2023-05-21 20:49  左扬  阅读(150)  评论(0编辑  收藏  举报
levels of contents