8. 访问 API Server
有多种方式可以访问 Kubernetes 提供的 REST API:
- kubectl 命令行工具
- SDK,支持多种语言
- Go
- Python
- Javascript
- Java
- CSharp
其他 OpenAPI 支持的语言,可以通过 gen 工具生成相应的 client
8.1 kubectl
kubectl get --raw /api/v1/namespaces kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes kubectl get --raw /apis/metrics.k8s.io/v1beta1/pods
8.2 kubectl proxy
当然,为了能够向 API 发出请求,有必要了解客户端可以使用哪些 API 对象。此过程通过客户端的 API 发现进行。要查看此过程的实际效果并以更实际的方式探索 API Server,我们可以自己执行此 API 发现。
首先,为了简化事情,我们使用kubectl命令行工具的内置proxy来为我们的集群提供身份验证
$ kubectl proxy --port=8080 & $ curl http://localhost:8080/api/ { "versions": [ "v1" ] }
或者
kubectl proxy
这将创建一个在本地计算机上的端口 8001
上运行的简单服务器。
我们可以使用这个服务器来启动 API 发现的过程。我们首先检查/api
前缀:
$ curl localhost:8001/api { "kind": "APIVersions", "versions": [ "v1" ], "serverAddressByClientCIDRs": [ { "clientCIDR": "0.0.0.0/0", "serverAddress": "10.0.0.1:6443" } ] }
您可以看到服务器返回了一个类型为 的 API 对象APIVersions。这个对象为我们提供了一个versions字段,列出了可用的版本。
在这种情况下,只有一个,但对于/apis
前缀,有很多。我们可以使用这个版本继续我们的调查
$ curl localhost:8001/api/v1 { "kind": "APIResourceList", "groupVersion": "v1", "resources": [ { …. { "name": "namespaces", "singularName": "", "namespaced": false, "kind": "Namespace", "verbs": [ "create", "delete", "get", "list", "patch", "update", "watch" ], "shortNames": [ "ns" ] }, … { "name": "pods", "singularName": "", "namespaced": true, "kind": "Pod", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "proxy", "update", "watch" ], "shortNames": [ "po" ], "categories": [ "all" ] }, { "name": "pods/attach", "singularName": "", "namespaced": true, "kind": "Pod", "verbs": [] }, { "name": "pods/binding", "singularName": "", "namespaced": true, "kind": "Binding", "verbs": [ "create" ] }, …. ] }
返回的对象包含/api/v1/
路径下暴露的资源列表。
描述 API(元 API 对象)的 OpenAPI/Swagger JSON 规范除了资源类型之外还包含各种有趣的信息。考虑Pod对象的 OpenAPI 规范:
{ "name": "pods", "singularName": "", "namespaced": true, "kind": "Pod", "verbs": [ "create", "delete", "deletecollection", "get", "list", "patch", "proxy", "update", "watch" ], "shortNames": [ "po" ], "categories": [ "all" ] }, { "name": "pods/attach", "singularName": "", "namespaced": true, "kind": "Pod", "verbs": [] }
查看此对象,该name字段提供此资源的名称。它还指示这些资源的子路径。由于推断英语单词的复数形式具有挑战性,因此 API 资源还包含一个singularName字段,该字段指示应用于该资源的单个实例的名称。我们之前讨论过命名空间。对象描述中的namespaced字段指示对象是否被命名空间。该kind字段提供了存在于 API 对象的 JSON 表示中的字符串,以指示它是什么类型的对象。该verbs字段是 API 对象中最重要的字段之一,因为它指示可以对该对象执行哪些类型的操作。这pods宾语包含所有可能的动词。动词的大部分效果从它们的名字就很明显了。需要更多解释的两个是watch和proxy。watch表示可以为资源建立监视。监视是一个长时间运行的操作,它提供有关对象更改的通知。proxy是一种专门的操作,它通过 API Server与网络端口建立代理网络连接。目前只有两种资源(Pod 和Services)支持proxy.
除了可以对对象执行的操作(描述为动词)之外,还有其他操作被建模为资源类型的子资源。例如,attach命令被建模为子资源:
{ "name": "pods/attach", "singularName": "", "namespaced": true, "kind": "Pod", "verbs": [] }
8.3 curl
$ TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token) $ CACERT=/run/secrets/kubernetes.io/serviceaccount/ca.crt $ curl --cacert $CACERT --header "Authorization: Bearer $TOKEN" https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/api { "kind": "APIVersions", "versions": [ "v1" ], "serverAddressByClientCIDRs": [ { "clientCIDR": "0.0.0.0/0", "serverAddress": "10.0.1.149:443" } ] }
$ APISERVER=$(kubectl config view | grep server | cut -f 2- -d ":" | tr -d " ") $ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default | cut -f1 -d ' ') | grep -E '^token'| cut -f2 -d':'| tr -d '\t') $ curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure { "kind": "APIVersions", "versions": [ "v1" ], "serverAddressByClientCIDRs": [ { "clientCIDR": "0.0.0.0/0", "serverAddress": "10.0.1.149:443" } ] }
9. 调试 API Server
当然,了解 API Server的实现是很好的,但通常情况下,您真正需要的是能够调试 API Server(以及调用 API 的客户端)的实际情况服务器)。实现这一点的主要方式是通过 API Server写入的日志。API Server导出两个日志流——the standard or basic logs,以及更有针对性的审计日志,它们试图捕获发出请求的原因和方式以及更改的 API Server状态。此外,可以打开更详细的日志记录来调试特定问题。
9.1 Basic Logs
默认情况下,API Server记录发送到 API Server的每个请求。此日志包括客户端的 IP 地址、请求的路径以及服务器返回的代码。如果意外错误导致服务器崩溃,服务器也会捕捉到这种恐慌,返回 500,并记录该错误。
I0803 19:59:19.929302 1 trace.go:76] Trace[1449222206]: "Create /api/v1/namespaces/default/events" (started: 2018-08-03 19:59:19.001777279 +0000 UTC m=+25.386403121) (total time: 927.484579ms): Trace[1449222206]: [927.401927ms] [927.279642ms] Object stored in database I0803 19:59:20.402215 1 controller.go:537] quota admission added evaluator for: { namespaces}
在此日志中,您可以看到它以I0803 19:59:…发出日志行时的时间戳开始,然后是发出它的行号,trace.go:76
最后是日志消息本身。
9.2 Audit Logs
审计日志(Audit Logs)旨在使服务器管理员能够取证恢复服务器的状态以及导致 Kubernetes API 中数据当前状态的一系列客户端交互。例如,它使用户能够回答诸如“为什么要ReplicaSet扩大到 100 个?”、“谁删除了那个 Pod?”等问题。
审计日志有一个可插入的后端,用于记录它们的写入位置。通常,审计日志会写入文件,但也可以将它们写入 webhook。在任何一种情况下,记录的数据都是API 组中类型event的结构化 JSON 对象audit.k8s.io。
审计本身可以通过同一 API 组中的策略对象进行配置。此策略允许您指定将审计事件发送到审计日志的规则。
9.3 激活附加日志
Kubernetes 使用 leveled logging github.com/golang/glog 包进行日志记录。使用 API Server上的标志--v,您可以调整日志记录的详细程度。一般来说,Kubernetes 项目已将日志详细级别 2 ( --v=2) 设置为记录相关但不太垃圾邮件的合理默认值。如果您正在调查特定问题,您可以提高日志记录级别以查看更多(可能是垃圾邮件)消息。由于过多的日志记录会影响性能,我们建议不要在生产中使用详细的日志级别运行。如果您正在寻找更有针对性的日志记录,该--vmodule标志可以提高单个源文件的日志级别。这对于仅限于一小部分文件的非常有针对性的详细日志记录非常有用。
9.4 调试 kubectl 请求
除了通过日志调试 API Server外,还可以通过kubectl命令行工具调试与 API Server的交互。与 API Server一样,kubectl命令行工具通过github.com/golang/glog包记录并支持–v详细度标志。将详细程度设置为级别 10 ( --v=10) 会启用最大程度详细记录。在此模式下,kubectl记录它向服务器发出的所有请求,以及尝试打印curl可用于复制这些请求的命令。请注意,这些curl命令有时不完整。
此外,如果您想直接戳 API Server,我们之前用于探索 API 发现的方法效果很好。Running kubectl proxy在 localhost 上创建一个代理服务器,它会根据本地$HOME/.kube/config文件自动提供您的身份验证和授权凭据。运行代理后,使用该curl命令查看各种 API 请求相当简单。
10. API 版本
在 Kubernetes 中,API 最初是一个 alpha API(例如,v1alpha1)。alpha 名称表示 API 不稳定且不适合生产用例。采用 alpha API 的用户应该预料到 API 表面区域可能会在 Kubernetes 版本之间发生变化,并且 API 本身的实现可能会不稳定,甚至可能会破坏整个 Kubernetes 集群的稳定性。因此,在生产 Kubernetes 集群中禁用了 Alpha API。
一旦 API 成熟,它就会成为 beta API(例如,v1beta1)。Beta 指定表明 API 通常是稳定的,但可能存在错误或最终的 API 表面改进。通常,假设 Kubernetes 版本之间的 beta API 是稳定的,并且向后兼容是一个目标。但是,在特殊情况下,Beta API 可能仍然在 Kubernetes 版本之间不兼容。同样,beta API 旨在保持稳定,但可能仍然存在错误。Beta API 通常在生产 Kubernetes 集群中启用,但应谨慎使用。
最后,API 变得普遍可用(例如,v1)。通用可用性 (GA) 表明 API 是稳定的。这些 API 带有向后兼容性保证和弃用保证。在 API 被标记为计划删除后,Kubernetes 会将该 API 保留至少三个版本或一年,以先到者为准。弃用也不太可能。只有在开发出更好的替代方案后,API 才会被弃用。同样,GA API 是稳定的,适用于所有生产用途。
Kubernetes 的特定版本可以支持多个版本(alpha、beta 和 GA)。为了实现这一点,API Server始终具有三种不同的 API 表示:外部表示,即通过 API 请求进入的表示;内部表示,它是在 API Server中用于处理的对象的内存表示;和存储表示,它被记录到存储层以持久化 API 对象。API Server中的代码知道如何在所有这些表示之间执行各种转换。API 对象可以作为v1alpha1版本提交,作为v1对象存储,然后作为v1beta1对象或任何其他任意支持的版本检索。
11. 替代编码
除了支持请求对象的 JSON 编码外,API Server还支持另外两种请求格式。请求的编码由请求上的 Content-Type HTTP 标头指示。如果缺少此标头,则假定内容为application/json,表示 JSON 编码。第一个替代编码是 YAML,由application/yamlContent Type 指示。YAML 是一种基于文本的格式,通常被认为比 JSON 更易于阅读。几乎没有理由使用 YAML 进行编码以与服务器通信,但在某些情况下它可能很方便(例如,通过 手动将文件发送到服务器curl)。
请求和响应的另一种替代编码是协议缓冲区编码格式。Protocol Buffers 是一个相当有效的二进制对象协议。使用协议缓冲区可以为 API Server带来更高效和更高吞吐量的请求。事实上,许多 Kubernetes 内部工具都使用协议缓冲区作为它们的传输。Protocol Buffers 的主要问题是,由于它们的二进制性质,它们在有线格式中更难可视化/调试。此外,目前并非所有客户端库都支持 Protocol Buffers 请求或响应。协议缓冲区格式由application/vnd.kubernetes.protobufContent-Type 标头指示。
12. 常见响应代码
因为 API Server是作为 RESTful 服务器实现的,所以来自服务器的所有响应都与 HTTP 响应代码保持一致。除了典型的 200 表示 OK 响应和 500 表示内部服务器错误之外,以下是一些常见的响应代码及其含义:
202
公认。已收到创建或删除对象的异步请求。结果以状态对象响应,直到异步请求完成,此时将返回实际对象。
400
错误的请求。服务器无法解析或理解该请求。
401
未经授权。在没有已知身份验证方案的情况下接收到请求。
403
禁止。已收到并理解请求,但禁止访问。
409
冲突。已收到请求,但它是更新旧版本对象的请求。
422
无法处理的实体。请求已正确解析,但未通过某种验证.
13. API Server Internals
除了操作 HTTP RESTful 服务的基础知识之外,API Server还有一些内部服务来实现部分 Kubernetes API。通常,这些类型的控制循环在称为控制器管理器的单独二进制文件中运行。但是有一些控制循环必须在 API Server内运行。在每种情况下,我们都会描述其功能以及它存在于 API Server中的原因。
14. CRD Control Loop
自定义资源定义 (CRD) 是可以添加到正在运行的 API Server的动态 API 对象。因为创建 CRD 的行为本质上会创建 API Server必须知道如何提供服务的新 HTTP 路径,所以负责添加这些路径的控制器位于 API Server内部。随着委托 API Server的增加(在后面的章节中描述),这个控制器实际上已经大部分从 API Server中抽象出来了。它目前仍默认在进程中运行,但也可以在进程外运行。
CRD 控制回路的操作如下:
for crd in AllCustomResourceDefinitions: if !RegisteredPath(crd): registerPath for path in AllRegisteredPaths: if !CustomResourceExists(path): markPathInvalid(path) delete custom resource data delete path
自定义资源路径的创建相当简单,但自定义资源的删除稍微复杂一些。这是因为删除自定义资源意味着删除与该类型资源关联的所有数据。这样一来,如果 CRD 被删除,然后在稍后的某个日期被读取,旧数据就不会以某种方式复活。
因此,在可以删除 HTTP 服务路径之前,首先将该路径标记为无效,以便无法创建新资源。然后,与 CRD 关联的所有数据都被删除,最后,路径被删除。
15. OpenAPI 规范服务
当然,了解可用于访问 API Server的资源和路径只是访问 Kubernetes API 所需信息的一部分。除了 HTTP 路径之外,您还需要知道要发送和接收的 JSON 有效负载。API Server还提供路径,为您提供有关 Kubernetes 资源模式的信息。这些模式使用 OpenAPI(以前的 Swagger)语法表示。您可以在以下路径下拉 OpenAPI 规范:
/swaggerapi
在 Kubernetes 1.10 之前,服务于 Swagger 1.2
/openapi/v2
Kubernetes 1.10 及更高版本,服务于 OpenAPI (Swagger 2.0)
OpenAPI 规范本身就是一个完整的主题,超出了本书的范围。无论如何,您不太可能需要在 Kubernetes 的日常操作中访问它。然而,各种客户端编程语言库是使用这些 OpenAPI 规范生成的(值得注意的例外是 Go 客户端库,它目前是手动编码的)。因此,如果您或用户在通过客户端库访问 Kubernetes API 的某些部分时遇到问题,那么第一站应该是 OpenAPI 规范,以了解 API 对象是如何建模的。
参考:
Oreilly The Kubernetes API Server
官方 The Kubernetes API
什么是 Kubernetes API?