如何编写Kubernetes的YAML(一)
什么是API对象
作为一个集群操作系统,Kubernetes 归纳总结了 Google 多年的经验,在理论层面抽象出了很多个概念,用来描述系统的管理运维工作,这些概念就叫做“API 对象”。
因为 apiserver 是 Kubernetes 系统的唯一入口,外部用户和内部组件都必须和它通信,而它采用了 HTTP 协议的 URL 资源理念,API 风格也用 RESTful 的 GET/POST/DELETE 等等,所以,这些概念很自然地就被称为是“API 对象”了。
你可以使用 kubectl api-resources
来查看当前 Kubernetes 版本支持的所有对象:
$ kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
bindings v1 true Binding
configmaps cm v1 true ConfigMap
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
persistentvolumes pv v1 false PersistentVolume
pods po v1 true Pod
services svc v1 true Service
...
在输出的“NAME”一栏,就是对象的名字,比如 ConfigMap、Pod、Service 等等,第二栏“SHORTNAMES”则是这种资源的简写,在我们使用 kubectl 命令的时候很有用,可以少敲几次键盘,比如 Pod 可以简写成 po,Service 可以简写成 svc。
在使用 kubectl 命令的时候,你还可以加上一个参数 --v=9
,它会显示出详细的命令执行过程,清楚地看到发出的 HTTP 请求,比如:
$ kubectl get pod --v=9
I0305 10:09:13.170728 14747 loader.go:372] Config loaded from file: /Users/niuben/.kube/config
I0305 10:09:13.174187 14747 cert_rotation.go:137] Starting client certificate rotation controller
I0305 10:09:13.185102 14747 round_trippers.go:435] curl -v -XGET -H "Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json" -H "User-Agent: kubectl/v1.22.5 (darwin/amd64) kubernetes/5c99e2a" 'https://127.0.0.1:60754/api/v1/namespaces/default/pods?limit=500'
I0305 10:09:13.202710 14747 round_trippers.go:454] GET https://127.0.0.1:60754/api/v1/namespaces/default/pods?limit=500 200 OK in 17 milliseconds
I0305 10:09:13.202729 14747 round_trippers.go:460] Response Headers:
I0305 10:09:13.202734 14747 round_trippers.go:463] Audit-Id: 44a71844-55c4-44e8-81cf-e2755f408d70
I0305 10:09:13.202738 14747 round_trippers.go:463] Cache-Control: no-cache, private
I0305 10:09:13.202741 14747 round_trippers.go:463] Content-Type: application/json
I0305 10:09:13.202744 14747 round_trippers.go:463] X-Kubernetes-Pf-Flowschema-Uid: c869356b-2a64-4f9b-b761-8648d9bf1512
I0305 10:09:13.202747 14747 round_trippers.go:463] X-Kubernetes-Pf-Prioritylevel-Uid: d3e9c83e-89b3-4f24-8253-c4267546be71
I0305 10:09:13.202751 14747 round_trippers.go:463] Date: Sun, 05 Mar 2023 02:09:13 GMT
...
可以看到,kubectl 客户端等价于调用了 curl,向 8443 端口发送了 HTTP GET 请求,URL 是 /api/v1/namespaces/default/pods。
目前的 Kubernetes 1.23 版本有 50 多种 API 对象,全面地描述了集群的节点、应用、配置、服务、账号等等信息,apiserver 会把它们都存储在数据库 etcd 里,然后 kubelet、scheduler、controller-manager 等组件通过 apiserver 来操作它们,就在 API 对象这个抽象层次实现了对整个集群的管理。
如何描述 API 对象
因为 API 对象采用标准的 HTTP 协议,为了方便理解,我们可以借鉴一下 HTTP 的报文格式,把 API 对象的描述分成“header”和“body”两部分。
header
“header”包含的是 API 对象的基本信息,有三个字段:apiVersion、kind、metadata。
- apiVersion 表示操作这种资源的 API 版本号,由于 Kubernetes 的迭代速度很快,不同的版本创建的对象会有差异,为了区分这些版本就需要使用 apiVersion 这个字段,比如 v1、v1alpha1、v1beta1 等等。
- kind 表示资源对象的类型,这个应该很好理解,比如 Pod、Node、Job、Service 等等。
- metadata 这个字段顾名思义,表示的是资源的一些“元信息”,也就是用来标记对象,方便 Kubernetes 管理的一些信息。
apiVersion: v1
kind: Pod
metadata:
name: ngx-pod
labels:
env: demo
owner: chrono
比如在这个 YAML 示例里 metadata 就有两个“元信息”,一个是 name,给 Pod 起了个名字叫 ngx-pod,另一个是 labels,给 Pod“贴”上了一些便于查找的标签,分别是 env 和 owner。
apiVersion、kind、metadata 都被 kubectl 用于生成 HTTP 请求发给 apiserver,你可以用 --v=9
参数在请求的 URL 里看到它们,比如:
https://192.168.49.2:8443/api/v1/namespaces/default/pods/ngx-pod
body
和 HTTP 协议一样,“header”里的 apiVersion、kind、metadata 这三个字段是任何对象都必须有的,而“body”部分则会与对象特定相关,每种对象会有不同的规格定义,在 YAML 里就表现为 spec 字段(即 specification),表示我们对对象的“期望状态”(desired status)。
举个例子:
apiVersion: v1
kind: Pod
metadata:
name: ngx-pod
labels:
env: demo
owner: chrono
spec:
containers:
- image: nginx:alpine
name: ngx
ports:
- containerPort: 80
这个 Pod,它的 spec 里就是一个 containers 数组,里面的每个元素又是一个对象,指定了名字、镜像、端口等信息。
现在把这些字段综合起来,我们就能够看出,这份 YAML 文档完整地描述了一个类型是 Pod 的 API 对象,要求使用 v1 版本的 API 接口去管理,其他更具体的名称、标签、状态等细节都记录在了 metadata 和 spec 字段等里。
YAML 创建删除对象
使用 kubectl apply
、kubectl delete
,再加上参数 -f
,你就可以使用这个 YAML 文件,创建或者删除对象了:
kubectl apply -f ngx-pod.yml
kubectl delete -f ngx-pod.yml
Kubernetes 收到这份“声明式”的数据,再根据 HTTP 请求里的 POST/DELETE 等方法,就会自动操作这个资源对象,至于对象在哪个节点上、怎么创建、怎么删除完全不用我们操心。
如何编写 YAML
这么多 API 对象,我们怎么知道该用什么 apiVersion、什么 kind?metadata、spec 里又该写哪些字段呢?还有,YAML 看起来简单,写起来却比较麻烦,缩进对齐很容易搞错,有没有什么简单的方法呢?
这些问题最权威的答案无疑是 Kubernetes 的官方参考文档(https://kubernetes.io/docs/reference/kubernetes-api/),API 对象的所有字段都可以在里面找到。不过官方文档内容太多太细,查阅起来有些费劲,所以介绍下面几个简单实用的小技巧。
第一个技巧
使用 kubectl api-resources
命令,它会显示出资源对象相应的 API 版本和类型,比如 Pod 的版本是“v1”,Ingress 的版本是“networking.k8s.io/v1”,照着它写绝对不会错。
第二个技巧
使用命令 kubectl explain
,它相当于是 Kubernetes 自带的 API 文档,会给出对象字段的详细说明,这样我们就不必去网上查找了。比如想要看 Pod 里的字段该怎么写,就可以这样:
$ kubectl explain pod
$ kubectl explain pod.metadata
$ kubectl explain pod.spec
$ kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.
FIELDS:
apiVersion <string>
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/sig-architecture/api-conventions.md#resources
kind <string>
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
metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
status <Object>
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/sig-architecture/api-conventions.md#spec-and-status
...
第三个技巧
使用 kubectl 的两个特殊参数 --dry-run=client
和 -o yaml
,前者是空运行,后者是生成 YAML 格式,结合起来使用就会让 kubectl 不会有实际的创建动作,而只生成 YAML 文件。
例如,想要生成一个 Pod 的 YAML 样板示例,可以在 kubectl run
后面加上这两个参数:
kubectl run ngx --image=nginx:alpine --dry-run=client -o yaml
就会生成一个正确的 YAML 文件:
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: ngx
name: ngx
spec:
containers:
- image: nginx:alpine
name: ngx
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
接下来你要做的,就是查阅对象的说明文档,添加或者删除字段来定制这个 YAML 了。
这个小技巧还可以再进化一下,把这段参数定义成 Shell 变量(名字任意,比如\(do/\)go,这里用的是$out),用起来会更省事,比如:
export out="--dry-run=client -o yaml"
kubectl run ngx --image=nginx:alpine $out