API Basics怎么实现

74次阅读
没有评论

共计 13808 个字符,预计需要花费 35 分钟才能阅读完成。

本文丸趣 TV 小编为大家详细介绍“API Basics 怎么实现”,内容详细,步骤清晰,细节处理妥当,希望这篇“API Basics 怎么实现”文章能帮助大家解决疑惑,下面跟着丸趣 TV 小编的思路慢慢深入,一起来学习新知识吧。

Introduction

所谓的云原生应用,能够清楚自己运行在 k8s 上,并且使用 k8s api 和资源当作其扩展。

一方面,云原生应用能够轻便地在不同的云上迁移。另一方面,受益于 k8s 提供的简洁可定义声明式 api。

Extension Patterns

cloud providers: in-tree controller manager:

kubelet: network, devices, storage, container runtimes

extend kubectl with plugins

extensions in the API server, dynamic admission control with webhook

Custom resources and custom controllers.

custom apiserver

scheduler extensions.

Controllers and operators

controller: 实现了一个控制循环,从 API server 监听到的集群状态,并且把当前状态调整到预期状态。

operator: 是指 controller+ 自定义资源的方式,去做像应用生命周期管理相关的一些操作。

the controller loop

读取资源的状态,更倾向于事件驱动。

改变集群或外部集群的对象的状态。

通过 apiserver 更新存储在 etcd 中资源的状态。

重复循环。回到步骤 1.

不管 controller 的实现复杂与否,这三步骤都是一致的。读取资源状态 - 改变 the world- 更新资源状态。

informers:

负责 watch 期望的状态,实现 resync 机制加强周期性的调谐,通常用来确保集群状态和存在内存中的期望状态不飘动。

work queues:

在 client-go 的 workqueue 包中实现了,为 event handler 提供 work queues 来“存储”状态变更操作的队列及其各种重试操作。当更新当前资源对象状态出错时,资源需要重新被排序。

Events

k8s 的控制面通过事件驱动 进行解耦,其他分布式系统通过远程过程调用来触发各种行为。controller 通过 watch k8s 对象的变化对 APISERVER 的增、删、改请求。

example:

用户 创建一个 dp 时,dp controller 通过 dp informer 创建一个 rs。

rs controller 通过 rs informer 创建一个新的 rs,并且创建一个 pod 对象。

scheduler 通过 pod informer 观测到新的 pod,且它的 spec.nodeName 为空,就把这个 pod 放入调度队列。

与此同时,kubelet 通过 pod informer 观测到新的 pod,且它的 spec.nodeName 为空。所以并没有 match 到 kubelet 的 nodeName,会忽略这个 pod 继续等待下一次事件。

scheduler 从 workqueue 中拿出 pod,并且通过更新 spec.nodeName 字段,调度到有足够资源的指定 node,并且写入 apiserver。

kubelet 因为 pod update 事件,继续比较 spec.NodeName 和其本身的 nodeName. 当 match 之后,通过启动 pod 的 container,并且把 container 启动的状态信息写入 pod status, 返回存储到 apiserver。

rs controller 注意到 pod 改变。

最后 pod 销毁时,kubelet watch 到 pod 事件并且将 pod 状态设置为“terminated”状态,并更新到 apiserver。

rs controller 观测到结束的 pod,决定这个 pod 一定需要被替换。于是通过 apiserver 删除 terminated 状态的 pod,并且创建一个新的。

一系列独立的控制循环,通过 watch apiserver 中对象的变化,并且这些变更事件通过 informers 来触发变更。

watch events 和 event 对象是两个东西:

前者通过在 apiserver 和 controllers 之间建立 http streaming 的链接用来驱动 informers.

后者是一种资源,像 pod,dp,services 这些,带有特殊的时效属性并且自动从 etcd 里消失。

Edge-driven triggers or Level-driven triggers 

前者是基于某个状态值变更的瞬间去触发 handler,后者是在某个时间段内、状态被验证与期望值相符,则去触发 handler。

后者更像是一种 polling 轮询,能否扩容到指定数量、控制器注意到变更的延迟依赖于 polling 轮询的时间间隔以及 apiserver 的响应时间。与很多异步的控制器相关,最终系统需要花费一段时间才能达到预期状态。

前者效率更高,延迟则依赖在控制器处理事件时,运行的工作线程。因此 k8s 基于事件驱动,即 edge-driven triggers.

reconciliation with resync:每 5 分钟持续调谐。

edge-triggered, level-driven

level-triggering-and-reconciliation

Changing Cluster Objects or the External worldOptimistic Concurrency

单体式:

二阶段:

共享状态:

并行调度架构:

新一代的并发调度架构依赖共享状态,使用乐观并发控制达到实现可扩展和性能可伸缩。

k8s 中乐观锁的场景是:

retry loop 中,获取了 foo 对象最新的状态后,尝试根据 foo spec 更新 real world 和 foo status, 实际修改的操作在 Update 事件之前执行。

通过 client.Get 返回的 foo 对象,包含资源的版本,ObjectMeta 结构体。这个结构体会在 client.Update 调用时,通过写操作更新 etcd 的数据。所谓的资源版本,在 etcd 中存储的实际上是 string,int 键值对。etcd 维护了一个计数器 counter,每次 key 的值被修改时,counter 值加一。

在 API machinery 代码中,资源版本的处理像是任意的 string,实际上还是遵循一些规则。实际的实现细节在 etcd 存储后端。

essence: 控制器中的冲突错误十分常见,处理和期待十分优雅。

Operators

operator 是一种针对特定应用的控制器,扩展了 k8s API 的创建、配置、管理复杂的有状态应用的实例。

一个 operator 包括一个 CRD+ controller。

Kubernetes API Basics

apiserver 作为挡在存储 etcd 前的唯一组件,跟 etcd 进行直接交互。主要职责是作为 k8s api 的 server 和集群组件的 proxy。

伺服 API 意味着 读取状态和 操作状态。

声明式状态管理

通过比较期望状态的 spec 和当前实际状态的值,

例如:当你在 dp 的声明 yaml 里指定 20 个副本数,dp controller 读到 dp spec 并且创建 rs, 实际去接管 rs- 即特定个数的 pods。如果有任何副本跪了,dp controller 会让你在状态中感知到。

APIServer Processes Requests

DefaultBuildHandlerChain()

WithPanicRecovery(): 处理恢复和日志 panic

Basics of client-go

Repos

ClientLibrary

k8s 项目提供 client-go(https://github.com/kubernetes/client-go) 作为用户的开发工具集,提供了多种类型的 API 支持,以及包含许多通用库代码。

K8S API Types

client-go 管理了客户端接口,像 PODS,services,deployments 这些接口,在这个 repo 中维护 (https://github.com/kubernetes/api),pod 等的类型在 k8s.io/api/core/v1/types.go 中定义,而这些文件均由 code gen 自动生成维护。

// Pod is a collection of containers that can run on a host. This resource is created
// by clients and scheduled onto hosts.
type Pod struct {
 metav1.TypeMeta `json: ,inline `
 // Standard object s metadata.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
 // +optional
 metav1.ObjectMeta `json: metadata,omitempty  protobuf: bytes,1,opt,name=metadata `
 // Specification of the desired behavior of the pod.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
 // +optional
 Spec PodSpec `json: spec,omitempty  protobuf: bytes,2,opt,name=spec `
 // Most recently observed status of the pod.
 // This data may not be up to date.
 // Populated by the system.
 // Read-only.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
 // +optional
 Status PodStatus `json: status,omitempty  protobuf: bytes,3,opt,name=status `

}

API machinery

第三个 repo(https://github.com/kubernetes/apimachinery) 包含所有通用的构建模块,不仅限于容器管理,还包括其他构建 APIs。在 pkg/api/meta/v1 下可以查看到许多 API 类型的定义,包括如下:

ObjectMeta: Name + Namespaces + ResourceVersion + Labels + Annotations

TypeMeta : Kind + APIVersion

GetOptions,

ListOptions

构建模块用来创建 k8s 客户端对象,代表在 k8s 集群中能访问的资源。

版本和兼容

k8s 的 api 是按版本区分的,一方面 client 的不同版本去访问 API server,如果版本不匹配,请求会失败。client 与特定的版本绑定,应用开发者需要选择正确的

其版本与 k8s 版本对应关系如图:

| Branch | Canonical source code location | Maintenance status |
| `release-1.4` | Kubernetes main repo, 1.4 branch | = - |
| `release-1.5` | Kubernetes main repo, 1.5 branch | = - |
| `release-2.0` | Kubernetes main repo, 1.5 branch | = - |
| `release-3.0` | Kubernetes main repo, 1.6 branch | = - |
| `release-4.0` | Kubernetes main repo, 1.7 branch | = - |
| `release-5.0` | Kubernetes main repo, 1.8 branch | = - |
| `release-6.0` | Kubernetes main repo, 1.9 branch | = - |
| `release-7.0` | Kubernetes main repo, 1.10 branch | = - |
| `release-8.0` | Kubernetes main repo, 1.11 branch | =- |
| `release-9.0` | Kubernetes main repo, 1.12 branch | =- |
| `release-10.0` | Kubernetes main repo, 1.13 branch | =- |
| `release-11.0` | Kubernetes main repo, 1.14 branch | ✓ |
| `release-12.0` | Kubernetes main repo, 1.15 branch | ✓ |
| `release-13.0` | Kubernetes main repo, 1.16 branch | ✓ |
| `release-14.0` | Kubernetes main repo, 1.17 branch | ✓ |
| client-go HEAD | Kubernetes main repo, master branch | ✓ |

Key:

✓ Changes in main Kubernetes repo are actively published to client-go by a bot

= Maintenance is manual, only severe security bugs will be patched.

– Deprecated; please upgrade.

你看代码有哪些用的较多的公用库呀:  sharedInformer、workqueue、event、leaderelection、rest、scheme、flowcontrol、codec

K8S objects in Go

k8s 资源是一种 kind 的实例,在 API server 作为一种资源结构体提供服务。

k8s 中的 object 实现了 runtime.Object 接口,来自于 k8s.io/apimachinery/pkg/runtime 包。其中,schema.ObjectKind 主要实现的另一个接口,来自于 k8s.io/apimachinery/pkg/runtime/schema.

这些 Go 实现的 object 作为数据结构主要是用来返回和设置 GroupVersionKind, 以及用来深度拷贝。

type Object interface { 
 GetObjectKind () schema.ObjectKind 
 DeepCopyObject () Object } nbsp;
type ObjectKind interface { 
// SetGroupVersionKind sets or clears the intended serialized kind of an 
// object. Passing kind nil should clear the current setting. 
 SetGroupVersionKind ( kind GroupVersionKind ) 
// GroupVersionKind returns the stored group, version, and kind of an // object, or nil if the object does not expose or provide these fields. 
 GroupVersionKind () GroupVersionKind } nbsp;

TypeMeta

通过嵌套 metav1.TypeMeta(k8s.io/apimachinery/meta/v1) 来实现 k8s 对象 schema.ObjectMeta(k8s.io/api) 的 getter 和 setter 类型。

// TypeMeta describes an individual object in an API response or request
// with strings representing the type of the object and its API schema version.
// Structures that are versioned or persisted should inline TypeMeta.
// +k8s:deepcopy-gen=false
type TypeMeta struct {
 // 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/api-conventions.md#types-kinds
 // +optional
 Kind string `json: kind,omitempty  protobuf: bytes,1,opt,name=kind `
 // APIVersion defines the versioned schema of this representation of an object.
 // Servers should convert recognized schemas to the latest internal value, and
 // may reject unrecognized values.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources
 // +optional
 APIVersion string `json: apiVersion,omitempty  protobuf: bytes,2,opt,name=apiVersion `
}
// Pod is a collection of containers that can run on a host. This resource is created
// by clients and scheduled onto hosts.
type Pod struct {
 metav1.TypeMeta `json: ,inline `
 // Standard object s metadata.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
 // +optional
 metav1.ObjectMeta `json: metadata,omitempty  protobuf: bytes,1,opt,name=metadata `
 // Specification of the desired behavior of the pod.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
 // +optional
 Spec PodSpec `json: spec,omitempty  protobuf: bytes,2,opt,name=spec `
 // Most recently observed status of the pod.
 // This data may not be up to date.
 // Populated by the system.
 // Read-only.
 // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
 // +optional
 Status PodStatus `json: status,omitempty  protobuf: bytes,3,opt,name=status `

}

基于 client-go 的应用这些字段在内存中值为空,在实际序列化成 json 或者 pb 时,才会有实际的值。而这些约定在版本序列中自动实现。

ObjectMeta

出了 TypeMeta,大部分 top-level 对象还会包括 metav1.ObjectMeta,来自 k8s.io/apimachinery/pkg/meta/v1。

(staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go)

type ObjectMeta struct { 
Name string `json: name,omitempty ` 
Namespace string `json: namespace,omitempty ` 
UID types.UID `json: uid,omitempty ` 
ResourceVersion string `json: resourceVersion,omitempty ` CreationTimestamp Time `json: creationTimestamp,omitempty ` DeletionTimestamp * Time `json: deletionTimestamp,omitempty ` 
Labels map [ string ] string `json: labels,omitempty ` 
Annotations map [ string ] string `json: annotations,omitempty ` 
... 
}

metav1.ObjectMeta 基本包含所有元数据信息,像 name,namespace,resource version, 一些时间戳,以及 labels 和 annotation. 其中 client-go 并不能对 resource version 进行读写,但却是整个 k8s 代码中核心工作的重要信息.

它作为 ObjectMeta 信息的一部分,来源于 etcd 里所有数据的 key 字段。

Client Sets

kubernetes.NewForConfig(config) 返回一个 client set, 用它可以访问定义在 k8s.io/api 中的大部分 API groups 和 resources,除了 APIServices(aggregated API servers) 和 CRD。

clientSet 主接口如下:

type Interface interface { Discovery() discovery.DiscoveryInterface
 AdmissionregistrationV1alpha1() admissionregistrationv1alpha1.AdmissionregistrationV1alpha1Interface
 AdmissionregistrationV1beta1() admissionregistrationv1beta1.AdmissionregistrationV1beta1Interface
 // Deprecated: please explicitly pick a version if possible.
 Admissionregistration() admissionregistrationv1beta1.AdmissionregistrationV1beta1Interface
 AppsV1beta1() appsv1beta1.AppsV1beta1Interface
 AppsV1beta2() appsv1beta2.AppsV1beta2Interface
 AppsV1() appsv1.AppsV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Apps() appsv1.AppsV1Interface
 AuthenticationV1() authenticationv1.AuthenticationV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Authentication() authenticationv1.AuthenticationV1Interface
 AuthenticationV1beta1() authenticationv1beta1.AuthenticationV1beta1Interface
 AuthorizationV1() authorizationv1.AuthorizationV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Authorization() authorizationv1.AuthorizationV1Interface
 AuthorizationV1beta1() authorizationv1beta1.AuthorizationV1beta1Interface
 AutoscalingV1() autoscalingv1.AutoscalingV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Autoscaling() autoscalingv1.AutoscalingV1Interface
 AutoscalingV2beta1() autoscalingv2beta1.AutoscalingV2beta1Interface
 BatchV1() batchv1.BatchV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Batch() batchv1.BatchV1Interface
 BatchV1beta1() batchv1beta1.BatchV1beta1Interface
 BatchV2alpha1() batchv2alpha1.BatchV2alpha1Interface
 CertificatesV1beta1() certificatesv1beta1.CertificatesV1beta1Interface
 // Deprecated: please explicitly pick a version if possible.
 Certificates() certificatesv1beta1.CertificatesV1beta1Interface
 CoreV1() corev1.CoreV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Core() corev1.CoreV1Interface
 EventsV1beta1() eventsv1beta1.EventsV1beta1Interface
 // Deprecated: please explicitly pick a version if possible.
 Events() eventsv1beta1.EventsV1beta1Interface
 ExtensionsV1beta1() extensionsv1beta1.ExtensionsV1beta1Interface
 // Deprecated: please explicitly pick a version if possible.
 Extensions() extensionsv1beta1.ExtensionsV1beta1Interface
 NetworkingV1() networkingv1.NetworkingV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Networking() networkingv1.NetworkingV1Interface
 PolicyV1beta1() policyv1beta1.PolicyV1beta1Interface
 // Deprecated: please explicitly pick a version if possible.
 Policy() policyv1beta1.PolicyV1beta1Interface
 RbacV1() rbacv1.RbacV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Rbac() rbacv1.RbacV1Interface
 RbacV1beta1() rbacv1beta1.RbacV1beta1Interface
 RbacV1alpha1() rbacv1alpha1.RbacV1alpha1Interface
 SchedulingV1alpha1() schedulingv1alpha1.SchedulingV1alpha1Interface
 SchedulingV1beta1() schedulingv1beta1.SchedulingV1beta1Interface
 // Deprecated: please explicitly pick a version if possible.
 Scheduling() schedulingv1beta1.SchedulingV1beta1Interface
 SettingsV1alpha1() settingsv1alpha1.SettingsV1alpha1Interface
 // Deprecated: please explicitly pick a version if possible.
 Settings() settingsv1alpha1.SettingsV1alpha1Interface
 StorageV1beta1() storagev1beta1.StorageV1beta1Interface
 StorageV1() storagev1.StorageV1Interface
 // Deprecated: please explicitly pick a version if possible.
 Storage() storagev1.StorageV1Interface
 StorageV1alpha1() storagev1alpha1.StorageV1alpha1Interface}

在这个接口中存在一些没有版本的方法:appsv1beta1.AppsV1beta1Interface。

在过去 k8s 有所谓的内部客户端,为了对象在内存中存在的更加通用,并且遵循在需要的时候再定义的规范。这种规范为了从实际使用的 API 版本中抽象 controller 代码,同时方便切换不同版本。事实上,为了遵循这种规范,增加了大量的复杂性,付出了不太值得的代价。此外,在 client 和 APIServer 之间的交互,并没有自动协商的机制。尽管存在内部的版本和客户端,controller 硬编码到指定版本导致并没有很好兼容。在最近的版本,k8s 代码尽量避免用到这些内部版本。

所有 clientset 可以访问 discovery client,被用作 RESTMappers;

type AppsV1beta1Interface interface { RESTClient() rest.Interface
 ControllerRevisionsGetter
 DeploymentsGetter
 ScalesGetter
 StatefulSetsGetter
}

在每个 GroupVersion(AppsV1beta1) 下我们发现 API group 的资源都存在通用的 RESTClient

读到这里,这篇“API Basics 怎么实现”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注丸趣 TV 行业资讯频道。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计13808字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)