第2章 Kubernetes API
在本章中,我们将向您介绍Kubernetes API的基础知识。这包括深入了解API服务器的内部工作机制,API本身以及如何从命令行与API进行交互。我们将向您介绍Kubernetes API概念,例如资源和资源类型,以及分组和版本。
API服务器
Kubernetes由一组具有不同角色的节点(集群中的机器)组成,如图2-1所示:主节点上的控制平面由API服务器,控制管理器和调度程序组成。API服务器是中央管理实体,也是与分布式存储组件etcd
直接对话的唯一组件。
API服务器具有以下核心职责:
提供Kubernetes API服务。此API可以被集群内主要组件,工作节点以及Kubernetes原生应用程序在集群内部使用,也可由客户端程序外部调用,如:
kubectl
。作为集群组件的代理(例如Kubernetes仪表板),或流式传输日志,服务端口或提供
kubectl exec
会话。
提供API意味着:
读取状态:获取单个对象,列出它们以及流式地更改对象
操作状态:创建,更新和删除对象
状态存储在etcd
中。

图2-1。Kubernetes架构预览
Kubernetes的核心是API服务器。但是API服务器如何工作?我们可先将API服务器视为黑盒子并仔细查看其HTTP接口,然后我们再深入其中讨论API服务器的内部工作原理。
API服务器的HTTP接口
从客户的视角来看,API服务器公开了一个RESTful HTTP API,支持JSON或protocol buffer(简称protobuf)协议的payload,protobuf主要用于集群内部通信,用于提升性能。
API服务器HTTP接口使用以下HTTP verbs(或HTTP方法)处理HTTP请求以查询和操作Kubernetes资源:
HTTP
GET
用于检索具有特定资源(例如某个pod)或资源集合或列表(例如,命名空间中的所有pods)的数据。HTTP
POST
用于创建资源,例如services或deployments。HTTP
PUT
用于更新现有资源 - 例如,更改pod的容器镜像。HTTP
PATCH
用于现有资源的部分更新。阅读Kubernetes文档中的 “Use a JSON merge patch to update a Deployment” ,以了解有关可用策略和含义的更多信息。HTTP
DELETE
用于以不可恢复的方式删除资源。
我们看一下Kubernetes api,以 1.14 API为例,你可以看到不同的HTTP方法。例如,要使用等效的CLI命令列出当前命名空间中的pod kubectl -n
THE_NAMESPACE
get pods
,您将执行GET方法 /api/v1/namespaces/*THENAMESPACE*/pods
(参见图2-2)。

图2-2。运行中的API服务器HTTP接口:列出给定命名空间中的pod
有关如何从Go程序调用API服务器HTTP接口的介绍,请参阅“客户端库”。
API术语
在我们深入API业务逻辑前,让我们首先定义Kubernetes API服务器涉及到的术语:
资源类型Kind
一个资源实体的类型。每个对象都有一个字段
Kind
(具体的kind
值在JSON中的小写的,Kind
在Golang中是首字母大写),它告诉如kubectl
之类客户端,这个资源对象代表一个pod。kind可以分为三类:对象可以是系统中的持久化实体 - 例如,
Pod
或Endpoints
。对象具有名称,其中许多都存在于命名空间中。列表是一种或多种实体的集合。列表具有有限的公共metadta。比如
PodList
s或NodeList
s。当你执行命令kubectl get pods,将会得到以上结果。针对对象和非持久化实体执行的特定操作,如
/binding
或/scale
。对于discovery api,Kubernetes使用APIGroup
和APIResource
; 对于错误结果,它使用Status
。
在Kubernetes程序中,一个资源类型直接对应于Golang类型。因此,作为Golang类型,资源类型的表述形式为单数,并且首字母需要大写。
API group
一组逻辑上相关的资源类型组成的集合。例如,所有批量操作的对象,如
Job
或ScheduledJob
都属于批处理API group。version
每API group可以存在多个版本,实际中这也是常见的。例如,一个api group最初是以
v1alpha1
出现,然后升级为v1beta1
版本,最终发布稳定版本v1
。一个对象可以作为一个特定的版本创建(比如:v1beta1
)也可以在查询时指定不同于创建时指定的api group版本进行检索,只要这个版本是被api group 支持的。API服务器将进行无损转换,为返回请求版本中的对象。从集群用户的角度来看,版本只是同一对象的不同表示。
提示
对于“ 一个资源实例在集群中存在一个v1
版本的对象,另外还有一个v1beta1
的对象。”,这种情况是不存在的,每个资源实例只有一个对象存在,可以按照用户的期望,既可以作为v1
版本返回也可以作为v1beta1
版本返回。
资源
资源通常是一个小写的、复数的单词(例如,
pods
)标识出一组HTTP endpoints(路径),其暴露系统中某个对象类型的CRUD(创建,读取,更新,删除)语义。常见路径是:根目录,例如... / pods,列出该类型的所有实例
各个命名资源的路径,例如... / pods / nginx
通常,这些HTTP请求接收和返回的是同一种资源类型(第一个列子为
PodList
,在第二个例子为Pod
)。但在其他情况下(例如,在出现错误的情况下),会返回一个Status
类型的对象。除了具有完整CRUD语义的主要资源之外,资源还可以有其他HTTP路径来执行特定操作(例如,... / pod / nginx / port-forward,... / pod / nginx / exec,或... / pod / nginx / logs)。我们调用这些子资源 sub resource(参见“子资源”)。这些通常实现自定义协议而不是REST - 例如,通过WebSockets或命令式API进行某种流式连接。
提示
资源和资源类型经常混在一起。请注意明确的区别:
资源对应于HTTP路径。
资源类型是这些端点返回和接收的对象,以及持久化在
etcd
中的对象。
资源始终是要和API group和version放在一起说的,统称为GroupVersionResource(或GVR)。GVR唯一地定义HTTP路径。例如,default
命名空间中的具体路径是/ apis / batch / v1 / namespaces / default / jobs。图2-3显示了一个命名空间下的资源示例GVR,a Job
。

图2-3。Kubernetes API - GroupVersionResource(GVR)
与jobs
GVR示例相反,集群范围的资源(如节点或命名空间)本身在路径中没有$ NAMESPACE部分。例如,nodes
GVR示例可能如下所示:/ api / v1 / nodes。请注意,出现在其他资源HTTP路径中的命名空间本身也是资源,可通过路径/ api / v1 / namespaces进行访问。
与GVR类似,每种资源类型都存在于API group中,具有版本,并通过GroupVersionKind(GVK)进行识别。
共栖- 生活在多个API组中的类型
同名的类型,不仅可以在不同的版本中共存,也可以在不同的API group中存在。例如,Deployment
在扩展组extensions group中以alpha类型开始,最终在其自己的组apps.k8s.io
中升级为稳定版本。我们称之为共栖。虽然在Kubernetes中不常见,但有一些:
Ingress
,NetworkPolicy
在extensions
和networking.k8s.io
Deployment
,DaemonSet
,ReplicaSet
在extensions
和apps
Event
在核心core组和events.k8s.io
GVK和GVR是相关的。GVK在GVR标识的HTTP路径下提供。将GVK映射到GVR的过程称之为REST mapping。我们将看到RESTMappers
在Golang中实现“REST Mapping”。
从全局的角度来看,API资源空间在逻辑上形成了一个具有顶级节点的树,包括/ api,/ apis和一些非层次化端点,例如/ healthz或/ metrics。此API空间的示例呈现如图2-4所示。请注意,确切的层次和路径与Kubernetes版本相关,随着时间将日趋稳定。

图2-4。一个Kubernetes API空间示例
Kubernetes API版本控制
对于可扩展性原因,Kubernetes支持不同API路径下的多个API版本,例如/ api / v1或/ apis / extensions / v1beta1。不同的API版本意味着不同级别的稳定性和支持:
Alpha级别(例如
v1alpha1
)通常在默认情况下禁用; 对功能的支持可能随时被删除,恕不另行通知,并且只能在短期测试群集中使用。Beta级别(例如
v2beta3
)默认情况下启用,这意味着代码已经过充分测试; 但是,对象的语义可能会在随后的beta或稳定版本中以不兼容的方式发生变化。稳定(通常可用或GA)级别(例如
v1
)对于许多后续版本,将出现在已发布的软件中。
让我们来看看如何构造HTTP API空间:在顶层我们区分核心组 - 即/ api / v1下面的所有内容- 以及以命名组形式的路径下,如:/ apis / $NAME
/ $VERSION
。
注意
由于历史原因,核心小组位于/api/v1
下,而不是人们所期望的/ apis / core / v1下。因为在引入API组的概念之前,核心组已经存在。
API服务器公开了第三种类型的HTTP路径 - 非资源对齐的路径:集群范围的实体,例如/ metrics,/ logs或/ healthz。此外,API服务器支持watch; 也就是说,您可以添加?watch=true
特定请求,并将API服务器转换为watch模式,代替通常设定时间间隔去轮询资源的模式。
声明性状态管理
大多数API需要区分期望的对象状态和当前对象的状态。规格specification,或简称为spec,就是对一种资源的期望状态的完整描述,并且通常被持久化存储下来,通常会使用etcd
来存储。
注意
为什么我们说“通常会是etcd
”?好吧,有的Kubernetes发行版和产品,如k3s或微软的AKS,已经取代或正在努力替换etcd
其他东西。得益于Kubernetes控制平面的模块化架构,这实现起来很方便。
让我们在API服务器的上下文中多讨论一些规格Spec(期望状态)与状态(当前观察到的状态)。
规格描述了您所需的资源状态,您需要通过命令行工具例如kubectl
,或者通过Go代码以编程方式获取。当前资源的状态(或者说资源当前被观察到的状态),是由控制平面管理的。可以时由核心组件(如控制管理器controller manager)或您自己的自定义控制管理器custom controller(请参阅“控制器和operator”)。例如,在部署deployment时,您可以指定您希望始终运行20个应用程序副本。deployment控制器是控制平面中控制管理器的一部分,它读取您提供的deployment规格并创建一个副本集replica set,然后控制权交由replica set管理:它创建相应数量的pod,最终(通过kubelet
)将容器在工作节点上启动。如果任何副本失败,deployment控制器会在status状态信息中反馈给您。这就是我们所谓的声明式状态管理 - 也就是说,声明所需的状态并让Kubernetes处理剩下的事情。
我们将在下一节中看到更多声明式状态管理的内容,我们会从命令行开始探索API。
在命令行中访问API
在本节我们将使用kubectl
和curl
演示访问Kubernetes API。如果您不熟悉这些CLI工具,现在是安装它们并试用它们的好时机。
首先,让我们看一下资源的期望状态和当前观察到的状态。我们以一个每个集群都会存在的属于控制平面的CoreDNS插件为例进行演示,该插件在kube-system
命名空间中(旧版本的Kubernetes中为Kube-dns插件)(此输出只显示了我们所关注重要部分):
正如您从此kubectl
命令中看到的那样,在spec
的部分中,您将定义一些特征,例如要使用的容器映像以及要并行运行的副本数,并在本status
节中看到了当前运行的有多少个副本。
为了执行与CLI相关的操作,在本章的其余部分中,我们将使用批处理操作作为运行示例。让我们首先在终端中执行以下命令:
此命令将Kubernetes API代理到本地计算机,并且还负责身份验证和授权。它允许我们通过HTTP直接发出请求并接收JSON返回结果。让我们通过启动第二个终端来查询的v1
下的资源:
提示
除了使用curl
与kubectl proxy
命令一起使用来对Kubernetes API进行直接HTTP API访问之外。您可以改为使用kubectl get --raw
命令:例如,替换curl http://127.0.0.1:8080/apis/batch/v1
为kubectl get --raw /apis/batch/v1
。
将以上信息与v1beta1
版本进行比较,注意到在查看http://127.0.0.1:8080/apis/batch 时,您可以获得批处理API组的受支持版本列表。
v1beta1
版本返回信息如下:
如您所见,该v1beta1
版本还包含cronjobs
资源,对应的类型为CronJob
。在撰写本文时,cronjob尚未升级到v1
版本。
如果您想了解集群中支持哪些API资源,包括它们的类型,是否为命名空间,以及它们的短名称(主要用于kubectl
命令行),您可以使用以下命令:
使用以下命令,对于确定集群中所支持的不同资源版本非常有用:
API服务器如何处理请求
现在您对面向外部的HTTP接口有了一定的理解,那么我们将重点关注API服务器的内部工作原理。图2-5显示了一个高层视角观察API服务器对请求的处理。

图2-5。Kubernetes API服务器请求处理概览
所以实际上当HTTP请求到达Kubernetes API时会发生什么呢?在较高的层面上,发生以下交互过程:
HTTP请求由注册的过滤器链处理在
DefaultBuildHandlerChain()
方法中。该链定义在k8s.io/apiserver/pkg/server/config.go中,稍后将详细讨论。它将在之前提到的请求上应用一系列过滤操作。每一个过滤器将传递和携带各自的信息到上下文中,准确地来说ctx.RequestInfo
,这里ctx
就是Go语言中的contextGo(这里场景假设为,通过认证的用户) -如果用户未通过认证,它返回一个适当的HTTP响应代码并说明原因(例如,用户身份验证失败时返回401
响应)。接下来,根据HTTP路径,代码k8s.io/apiserver/pkg/server/handler.go中的多路复用器将HTTP请求路由到相应的处理程序。
每个API group对应注册了一个处理程序 - 有关详细信息,请参阅k8s.io/apiserver/pkg/endpoints/groupversion.go和k8s.io/apiserver/pkg/endpoints/installer.go。它接受HTTP请求以及context上下文(例如,用户和访问权限)以及将所需的对象从
etcd
存储中检索出来。
现在让我们仔细看看server / config.go中设置的过滤器DefaultBuildHandlerChain(),以及其中发生的事情:
所有包都在k8s.io/apiserver/pkg中。更具体地审查:
WithPanicRecovery()
处理recovery并且记录panic。定义在server / filters / wrap.go中。
WithRequestInfo()
给context上下文附一个
RequestInfo
。定义在endpoints/filters/requestinfo.go.。WithWaitGroup()
将所有非长时间运行的请求添加到等待组; 用于优雅关机。定义在server / filters / waitgroup.go中。
WithTimeoutForNonLongRunningRequests()
设置非长期运行的请求的超时时间(如大多数的
GET
,PUT
,POST
,和DELETE
请求),相对的长期运行的请求有watch和proxy请求。方法定义在server / filters / timeout.go中。WithCORS()
提供CORS跨域的实现。CORS是跨域资源共享的缩写,是一种允许嵌入HTML页面的JavaScript将XMLHttpRequests发送到与JavaScript发起的域不同的域的机制。在server / filters / cors.go中定义。
WithAuthentication()
尝试将对请求进行身份验证,用户可以是人员或机器用户,并将用户信息存储在提供的上下文中。成功时,
Authorization
将从请求中删除HTTP header信息。如果身份验证失败,则返回HTTP401
状态代码。在endpoints/filters/authentication.go定义。WithAudit()
使用所有传入请求加入审核日志记录信息功能的装饰器处理程序。审计日志条目包含诸如请求的源IP,用户调用操作和请求的命名空间之类的信息。在admission/audit.go定义。
WithImpersonation()
通过检查尝试更改用户的请求(类似于
sudo
)来处理用户模拟。在endpoints/filters/impersonation.go定义。WithMaxInFlightLimit()
限制已已发起未结束的请求数量。在server / filters / maxinflight.go中定义。
WithAuthorization()
通过调用授权模块检查权限,并将所有授权请求传递给多路复用器,多路复用器将请求分派给正确的处理程序。如果用户没有足够的权限,则返回HTTP
403
状态代码。Kubernetes现在使用基于角色的访问控制(RBAC)。定义在endpoints/filters/authorization.go。
在通用处理程序链处理完后(图2-5中的第一个框),实际的请求处理开始(即执行请求处理程序的语义):
对于请求/,/ version,/ apis,/ healthz和其他nonRESTful API的请求将被直接处理。
对于RESTful资源的请求进入请求管道,包括:
validation
根据通用验证逻辑检查传入对象,该逻辑对系统中的每个对象类型都存在。例如,检查字符串格式以验证服务名称中仅使用有效的DNS兼容字符,或者pod中的所有容器名称是唯一的。
etcd
-端的CRUD逻辑这里实现了我们在“API服务器的HTTP接口”中看到的不同方法; 例如,更新逻辑从
etcd
中读取对象,检查没有其他用户在“乐观并发”意义上修改了对象,如果没有,则将请求对象写入etcd
。
我们将在以下章节中更详细地研究所有这些步骤; 例如:
自定义资源
验证在 “Validating Custom Resources”,admission在 “Admission Webhooks”中,还有常规的CRUD语义在第4章
Golang原生资源
Validation in “Validation”, admission in “Admission”, and the implementation of CRUD semantics in “Registry and Strategy”
摘要
在本章中,我们首先将Kubernetes API服务器作为黑盒子进行了讨论,并查看了其HTTP接口。然后你学习了如何在命令行上与那个黑盒子进行交互,最后我们打开了黑盒子并探索了它的内部工作原理。到目前为止,您应该知道API服务器如何在内部工作,以及如何使用命令行工具kubectl
与资源进行交互以进行资源探索和操作。
现在是时候将手动交互留在我们身后的命令行,并开始使用Go: client-go
(Kubernetes“标准库”的核心)进行编程API服务器访问。
1在一个Kubernetes 1.14集群,这些是(以此顺序): AlwaysAdmit
, NamespaceAutoProvision
, NamespaceLifecycle
, NamespaceExists
, SecurityContextDeny
, LimitPodHardAntiAffinityTopology
, PodPreset
, LimitRanger
, ServiceAccount
, NodeRestriction
, TaintNodesByCondition
, AlwaysPullImages
, ImagePolicyWebhook
, PodSecurityPolicy
, PodNodeSelector
, Priority
, DefaultTolerationSeconds
, PodTolerationRestriction
, DenyEscalatingExec
, DenyExecOnPrivileged
, EventRateLimit
, ExtendedResourceToleration
, PersistentVolumeLabel
, DefaultStorageClass
, StorageObjectInUseProtection
, OwnerReferencesPermissionEnforcement
, PersistentVolumeClaimResize
, MutatingAdmissionWebhook
, ValidatingAdmissionWebhook
, ResourceQuota
, and AlwaysDeny
.
最后更新于
这有帮助吗?