Kubernetes实战(第二版)--第四章 介绍kubernetes API对象
本章涵盖了
- 通过API管理Kubernetes集群和它托管的应用程序
- 理解Kubernetes API对象的结构
- 检索和理解对象的YAML或JSON清单
- 通过Node对象检查集群节点的状态
- 通过Event对象检查集群事件
前一章介绍了组成一个部署应用程序的三个基本对象。您创建了一个Deployment对象,该对象派生了多个Pod对象,这些Pod对象表示应用程序的各个实例,并通过创建一个Service对象将它们公开给外界(在它们前面部署了一个负载平衡器)。
本书第二部分的章节详细解释了这些和其他对象类型。本章以Node对象和Event对象为例介绍了Kubernetes对象的共同特征。
4.1 熟悉Kubernetes API
在Kubernetes集群中,用户和Kubernetes组件都通过Kubernetes API操作对象与集群交互,如图4.1所示。
这些对象表示整个集群的配置。它们包括在集群中运行的应用程序、它们的配置、在集群中或在外部公开它们的负载均衡器、底层服务器和这些应用程序使用的存储、用户和应用程序的安全权限,以及基础设施的许多其他细节。
图4.1 Kubernetes集群是通过操作Kubernetes API中的对象来配置的4.1.1 API简介
Kubernetes API是与集群交互的中心点,因此这本书的大部分内容都致力于解释这个API。最重要的API对象将在后面的章节中描述,但这里提供了API的基本介绍。
理解API的架构风格
Kubernetes API是一个基于HTTP的RESTful API,其中状态由使用标准HTTP方法(如POST、GET、PUT/PATCH或Delete)执行CRUD操作(创建、读取、更新、删除)的资源表示。
定义
REST是具象状态传输(Representational State Transfer),是一种通过使用无状态操作的web服务在计算机系统之间实现互操作性的架构风格,Roy Thomas Fielding在他的博士论文中对此进行了描述。要了解更多信息,请登录https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm阅读论文。
这些资源(或对象)表示集群的配置。因此,将应用程序部署到集群中的集群管理员和工程师通过操作这些对象来操作配置。
在Kubernetes社区中,术语“资源(resource)”和“对象(object)”是可互换使用的,但是有一些细微的差异需要进行解释。
理解资源和对象之间的区别
RESTful api中的基本概念是资源,每个资源都被分配一个惟一标识它的URI或统一资源标识符。例如,在Kubernetes API中,应用程序部署由部署资源表示。
集群中所有部署的集合是在/api/v1/deployments处公开的REST资源。当您使用GET方法向这个URI发送HTTP请求时,您会收到一个响应,其中列出了集群中的所有部署实例。
每个单独的部署实例也有自己的惟一URI,可以通过它进行操作。因此,单独的部署被公开为另一个REST资源。您可以通过向资源URI发送GET请求来检索有关部署的信息,还可以使用PUT请求修改它。
图4.2 单个对象可以公开为两个或多个资源
因此,一个对象可以通过多个资源公开。如图4.2所示,在查询部署资源时,名为mydeploy的Deployment对象实例作为集合的元素返回,在直接查询单个资源URI时作为单个对象返回。
此外,如果一个对象类型存在多个API版本,那么单个对象实例也可以通过多个资源公开。在Kubernetes 1.15版本之前,API公开了两种不同的Deployment对象表示。除了在/apis/apps/v1/deployments处公开的apps/v1/部署版本外,API中还提供了一个较老的版本extensions/v1beta1,公开在/apis/extensions/v1beta1/deployments。这两种资源并没有代表两组不同的部署对象,而是以两种不同的方式表示的一组对象——在对象模式上略有不同。您可以通过第一个URI创建部署对象的实例,然后使用第二个URI读取它。
在某些情况下,资源根本不表示任何对象。例如,Kubernetes API允许客户端验证主题(人或服务)是否被授权执行API操作。这是通过向/apis/authorization.k8s.io/v1/subjectaccessreviews资源提交一个POST请求来完成的。响应显示主体是否被授权,执行请求主体中指定的操作。这里的关键是POST请求不会创建任何对象。
上面描述的示例表明,资源与对象不同。如果您熟悉关系数据库系统,则可以将资源和对象类型与视图和表进行比较。资源是与对象交互的视图。
请注意
因为术语“资源”也可以指计算资源,比如CPU和内存,为了减少混淆,本书中使用术语“对象”来指API资源。
理解对象是如何表示的
当您对资源发出GET请求时,Kubernetes API服务器以结构化文本形式返回对象。默认的数据模型是JSON,但是您也可以告诉服务器返回YAML。当使用POST或PUT请求更新对象时,还可以使用JSON或YAML指定新状态。
对象清单中的单个字段取决于对象类型,但是一般结构和许多字段由所有Kubernetes API对象共享。接下来您将了解它们。
4.1.2 理解对象清单的结构
在你面对Kubernetes对象的完整清单之前,让我先解释它的主要部分,因为这将帮助你找到适合自己理解的方式,完整清单有时由数百行组成。
介绍对象的主要部分大多数Kubernetes API对象的清单由以下四个部分组成:
- Type Metadata包含关于此清单所描述的对象类型的信息。它指定对象类型、类型所属的组和API版本。
- Object Metadata包含关于对象实例的基本信息,包括它的名称、创建时间、对象的所有者和其他标识信息。对象元数据中的字段对于所有对象类型都是相同的。
- Spec(规范)是指定对象所需状态的部分。它的字段因不同的对象类型而不同。对于pods,这部分指定了pod的容器、存储卷和与其操作相关的其他信息。
- Status包含对象的当前实际状态。对于pod,它告诉您pod的情况、每个容器的状态、IP地址、运行的节点,以及其他显示pod所发生的情况的信息。
对象清单及其四个部分的可视化表示如下图所示。
图4.3 Kubernetes API对象的主要部分。请注意尽管图中显示了用户写入对象的Spec部分并读取其状态,但当您执行GET请求时,API服务器总是返回整个对象;要更新对象,还需要在PUT请求中发送整个对象。稍后您将看到一个示例,以了解这些部分中存在哪些字段,但首先让我解释Spec和Status部分,因为它们表示对象的实体。
理解规范(Spec)和状态(Status)部分
在前面的图中,您可能已经注意到,对象的两个最重要部分是Spec和Status部分。您可以使用Spec来指定对象所需的状态,并从Status部分读取对象的实际状态。所以,您是编写规范并读取状态的人,但是谁或什么东西读取规范并写入状态呢?
Kubernetes控制平面运行几个称为控制器的组件,这些组件管理您创建的对象。每个控制器通常只负责一种对象类型。例如,Deployment控制器管理Deployment对象。
如图4.4所示,控制器的任务是从对象的Spec部分读取所需的对象状态,执行实现此状态所需的操作,并通过写入Status部分来报告对象的实际状态。
图4.4 控制器如何管理对象
实际上,您通过创建和更新API对象来告诉Kubernetes它必须做什么。Kubernetes控制器使用相同的API对象来告诉您它们已经做了什么以及它们的工作状态是什么。
您将在第13章中了解更多关于单个控制器及其职责的内容。现在,请记住,控制器与大多数对象类型相关联,并且控制器是读取规范和写入对象状态的东西。
并不是所有对象都有spec和status部分。
所有Kubernetes API对象都包含两个元数据(metadata)部分,但并不是所有对象都包含Spec和Status部分。那些不包含数据的对象,通常只包含静态数据,并且没有相应的控制器,因此没有必要区分对象的期望状态和实际状态。
此类对象的一个例子是Event对象,它由各种控制器创建,以提供关于控制器管理的对象正在发生什么的附加信息。Event对象在4.3节中解释。
现在您已经了解了对象的大致轮廓,因此本章的下一节将最终探究对象的各个字段。
4.2 检查对象的单个属性
要近距离地研究Kubernetes API对象,我们需要一个具体的示例。让我们以Node对象为例,它应该很容易理解,因为它表示您可能比较熟悉的东西——集群中的计算机。
这个kind工具提供的Kubernetes集群有三个节点——一个主节点和两个工作节点。它们由API中的三个Node对象表示。我可以查询API并使用 kubectl get nodes 列出这些对象,如下一个清单所示。
Listing 4.1 Listing Node objects
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready master 1h v1.18.2
kind-worker Ready <none> 1h v1.18.2
kind-worker2 Ready <none> 1h v1.18.2
下图显示了组成集群的三个Node对象和实际的集群机器。每个Node对象实例代表一个主机。在每个实例中,Spec部分包含主机的(部分)配置,Status部分包含主机的状态。
图4.5 集群节点由Node对象表示
Node对象与其他对象略有不同,因为它们通常是由Kubelet(运行在集群节点上的节点代理)创建的,而不是由用户创建的。在集群中添加机器时,Kubelet通过创建表示主机的Node对象来注册节点。然后,用户可以编辑Spec部分中的一些字段。
4.2.1 探索Node对象的完整清单
让我们仔细看看其中一个Node对象。通过运行kubectl get nodes命令列出集群中的所有Node对象,然后选择一个要检查的节点。然后,执行kubectl get node <node-name> -o yaml命令,其中使用节点的名称替换<node-name>,如下面的清单所示。
Listing 4.2 Displaying the complete YAML manifest of an object
$ kubectl get node kind-control-plane -o yaml
apiVersion: v1 //类型元数据指定对象的类型和此对象清单的API版本。
kind: Node
metadata://对象元数据部分
annotations: ...
creationTimestamp: "2020-05-03T15:09:17Z"
labels: ...
managedFields: ...
name: kind-control-plane #C
resourceVersion: "3220054"
selfLink: /api/v1/nodes/kind-control-plane
uid: 16dc1e0b-8d34-4cfb-8ade-3b0e91ec838b
spec://这里指定了节点所需的状态
podCIDR: 10.244.0.0/24 #E
podCIDRs: #E
- 10.244.0.0/24 #E
taints:
- effect: NoSchedule
key: node-role.kubernetes.io/master
status: //节点的实际状态显示在这里
addresses: #G
- address: 172.18.0.2 #G
type: InternalIP #G
- address: kind-control-plane #G
type: Hostname #G
allocatable: ...
capacity: #H
cpu: "8" #H
ephemeral-storage: 401520944Ki #H
hugepages-1Gi: "0" #H
hugepages-2Mi: "0" #H
memory: 32720824Ki #H
pods: "110" #H
conditions:
- lastHeartbeatTime: "2020-05-17T12:28:41Z"
lastTransitionTime: "2020-05-03T15:09:17Z"
message: kubelet has sufficient memory available
reason: KubeletHasSufficientMemory
status: "False"
type: MemoryPressure
...
daemonEndpoints:
kubeletEndpoint:
Port: 10250
images: #I
- names: #I
- k8s.gcr.io/etcd:3.4.3-0 #I
sizeBytes: 289997247 #I
... #I
nodeInfo: #J
architecture: amd64 #J
bootID: 233a359f-5897-4860-863d-06546130e1ff #J
containerRuntimeVersion: containerd://1.3.3-14-g449e9269 #J
kernelVersion: 5.5.10-200.fc31.x86_64 #J
kubeProxyVersion: v1.18.2 #J
kubeletVersion: v1.18.2 #J
machineID: 74b74e389bb246e99abdf731d145142d #J
operatingSystem: linux #J
osImage: Ubuntu 19.10 #J
systemUUID: 8749f818-8269-4a02-bdc2-84bf5fa21700 #J
请注意
使用-o json选项以json而不是YAML显示对象。
在清单中,对对象定义的四个主要部分和节点的较重要属性进行了注释,以帮助您区分较重要和较不重要的字段。为了减少清单的长度,省略了一些行。
直接访问API
您可能会对直接访问API感兴趣,而不是通过kubectl的方式。如前所述,Kubernetes API是基于web的,因此您可以使用web浏览器或curl命令来执行API操作,但是API服务器使用TLS,您需要一个客户端证书或令牌来进行身份验证。幸运的是,kubectl提供了一个特殊的代理来处理这个问题,允许您使用普通HTTP通过代理与API对话。
要运行代理,请执行以下命令:
$ kubectl proxy
Starting to serve on 127.0.0.1:8001
现在可以使用HTTP访问127.0.0.1:8001。例如,要检索节点对象,请访问URL http://127.0.0.1:8001/api/v1/nodes/kind-control-plane(用一个节点的名称替换kind-control-plane)。
现在让我们仔细看看四个主要部分中的各个字段。
Type Metadata(类型元数据)字段
如您所见,清单从apiVersion和kind字段开始,它们指定此对象清单所指定的对象的API版本和类型。API版本是用于描述该对象的模式。如前所述,对象类型可以与多个模式相关联,每个模式中使用不同的字段来描述对象。但是,通常每种类型只有一个模式。
前面清单中的apiVersion就是简单的v1,但是您将在接下来的章节中看到,其他对象类型中的apiVersion不仅仅包含版本号。例如,对于Deployment对象,apiVersion是apps/v1。虽然字段最初仅用于指定API版本,但现在它还用于指定资源所属的API组。Node对象属于核心API组,按照惯例,apiVersion字段会省略这个组。
清单中定义的对象类型由字段kind指定。前面清单中的对象类型是Node,到目前为止,在本书中您还处理了以下类型:Deployment、Service和Pod。
Object Metadata(对象元数据)部分中的Fields(字段)
元数据部分包含此对象实例的元数据。它包含实例的名称,以及附加属性(如标签和注释,将在第9章中进行解释)和字段(如resourceVersion、managedFields和其他低级字段,将在第12章中进行深入解释)。
Spec(规范)部分中的Fields(字段)
接下来是spec部分,它是针对每种对象类型的。相对于其他类型的对象,它对于Node对象来说是相对简短的。podCIDR字段指定分配给节点的pod IP范围。在这个节点上运行的Pods将从这个范围分配ip。taints字段在这一点上并不重要,但是你将在第18章中了解它。
通常,对象的spec部分包含更多用于配置对象的字段。
Status(状态)部分中的Fields(字段)
状态部分在不同类型的对象之间也是不同的,但是它的目的总是相同的——它包含对象所表示的事物的最后观察到的状态。Node对象的状态显示节点的IP地址(s)、主机名、提供计算资源的能力,当前节点的条件,已经下载并在本地缓存的容器镜像,和操作系统信息和运行的Kubernetes组件的版本。
4.2.2 了解单个对象字段
要了解清单中各个字段的更多信息,可以参考文档http://kubernetes.io/docs/reference/或使用kubectl explain命令,如下所述。
使用kubectl explain来研究API对象字段
kubectl工具有一个很好的特性,它允许您从命令行中查找每种对象类型(种类)的每个字段的解释。通常,首先运行kubectl explain <kind>,要求它提供对象类型的基本描述,如下所示:
Listing 4.3 Using kubectl explain to learn about an object kind
$ kubectl explain nodes
KIND: Node
VERSION: v1
DESCRIPTION:
Node is a worker node in Kubernetes. Each node will have a unique
identifier in the cache (i.e. in etcd).
FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest...
kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client...
metadata <Object>
Standard object's metadata. More info: ...
spec <Object>
Spec defines the behavior of a node...
status <Object>
Most recently observed status of the node. Populated by the system.
Read-only. More info: ...
该命令打印对象的解释,并列出该对象可以包含的顶级字段。
深入API对象的结构
然后可以深入挖掘,在每个特定字段下查找子字段。例如,您可以使用以下命令来解释节点的spec字段:
Listing 4.4 Using kubectl explain to learn about a specific object field and sub-fields
$ kubectl explain node.spec
KIND: Node
VERSION: v1
RESOURCE: spec <Object>
DESCRIPTION:
Spec defines the behavior of a node.
NodeSpec describes the attributes that a node is created with.
FIELDS:
configSource <Object>
If specified, the source to get node configuration from The
DynamicKubeletConfig feature gate must be enabled for the Kubelet...
externalID <string>
Deprecated. Not all kubelets will set this field...
podCIDR <string>
PodCIDR represents the pod IP range assigned to the node.
...
请注意顶部给出的API版本。如前所述,同一类型的多个版本可以存在。不同的版本可以有不同的字段或默认值。如果希望显示不同的版本,请使用--api-version选项指定它。
请注意
如果您想查看对象的完整结构(字段的完整层次列表,但没有描述),请尝试kubectl explain pods --recursive。
4.2.3 了解对象的状态条件
spec和status部分的字段集对于每种对象类型都是不同的,但是条件字段在许多对象中都可以找到。它给出了对象当前所处的条件列表。当需要对对象进行故障排除时,它们非常有用,所以让我们更仔细地研究一下它们。由于使用Node对象作为示例,本节还将介绍如何轻松地识别集群节点的问题。
介绍节点的状态条件
让我们再次打印出其中一个节点对象的YAML清单,但这一次我们将只关注对象状态中的条件字段,如下面的清单所示。
Listing 4.5 The current status conditions in a Node object
$ kubectl get node kind-control-plane -o yaml
...
status:
...
conditions:
- lastHeartbeatTime: "2020-05-17T13:03:42Z"
lastTransitionTime: "2020-05-03T15:09:17Z"
message: kubelet has sufficient memory available
reason: KubeletHasSufficientMemory
status: "False"
type: MemoryPressure
- lastHeartbeatTime: "2020-05-17T13:03:42Z"
lastTransitionTime: "2020-05-03T15:09:17Z"
message: kubelet has no disk pressure
reason: KubeletHasNoDiskPressure
status: "False"
type: DiskPressure
- lastHeartbeatTime: "2020-05-17T13:03:42Z"
lastTransitionTime: "2020-05-03T15:09:17Z"
message: kubelet has sufficient PID available
reason: KubeletHasSufficientPID
status: "False"
type: PIDPressure
- lastHeartbeatTime: "2020-05-17T13:03:42Z"
lastTransitionTime: "2020-05-03T15:10:15Z"
message: kubelet is posting ready status
reason: KubeletReady
status: "True"
type: Ready
提示
如果您只想查看对象结构的一部分,那么jq工具非常方便。例如,要显示节点的状态条件,可以运行kubectl get node <name> -o json | jq .status.conditions。与YAML等价的工具是yq。
有四个条件可以显示节点的状态。每个条件都有一个type和一个status字段,可以是True、False或Unknown,如图4.6所示。条件还可以为条件的最后一次转换指定面向机器的reason,以及包含转换细节的面向人的message。lastTransitionTime字段指示条件何时从一种状态转移到另一种状态,而lastHeartbeatTime字段显示控制器最后一次收到给定条件的更新的时间。
图4.6 指示Node对象状态的状态条件
尽管Ready 条件是列表中的最后一个条件,但它可能是最重要的条件,因为它表示节点是否准备好接受新工作负载(pods)。其他条件(MemoryPressure、DiskPressure和PIDPressure)表示节点是否正在耗尽资源。记住,如果节点的行为开始异常,请检查这些条件——例如,如果在节点上运行的应用程序开始耗尽资源和/或崩溃。
理解其他对象类型的条件
在许多其他对象类型中,也使用类似于Node对象中的条件列表。前面解释的条件很好地说明了为什么大多数对象的状态是由多个条件而不是单个字段表示的。
请注意:条件通常是正交的,这意味着它们表示对象不相关方面的信息。
如果对象的状态表示为单个字段,那么随后就很难用新值扩展它,因为这将需要更新监视对象状态并基于它执行操作的所有客户端。有些对象类型最初使用这样的单个字段,有些仍然使用,但现在大多数使用条件列表代替。
由于本章的重点是介绍Kubernetes API对象的常见特性,所以我们只关注conditions 字段,但它远不是Node对象状态的唯一字段。要研究其他方法,请使用kubectl explain命令,如前面的侧栏中所述。那些对你来说不是很容易理解的领域,在阅读了本书的这一部分的其余章节之后,你会变得很清楚。
请注意
作为练习,使用命令 kubectl get <kind> <name> -o yaml 来研究到目前为止已经创建的其他对象(deployments, services和pods)。
4.2.4 使用kubectl describe 命令检查对象
为了让您对Kubernetes API对象的整个结构有一个正确的印象,有必要向您展示对象的完整YAML清单。虽然我个人经常使用这种方法来检查对象,但检查对象的更用户友好的方法是kubectl describe命令,它通常显示相同的信息,有时甚至更多。
理解kubectl描述Node对象的输出
让我们尝试在Node对象上运行kubectl describe命令。为了保持有趣,我们现在取一个工作节点而不是主节点。下面的清单显示了kubectl describe命令为我的两个工作节点之一显示的内容。
#清单4.6 使用kubectl describe检查Node对象
$ kubectl describe node kind-worker-2
Name: kind-worker2
Roles: <none>
Labels: beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/arch=amd64
kubernetes.io/hostname=kind-worker2
kubernetes.io/os=linux
Annotations: kubeadm.alpha.kubernetes.io/cri-socket: /run/contain...
node.alpha.kubernetes.io/ttl: 0
volumes.kubernetes.io/controller-managed-attach-deta...
CreationTimestamp: Sun, 03 May 2020 17:09:48 +0200
Taints: <none>
Unschedulable: false
Lease:
HolderIdentity: kind-worker2
AcquireTime: <unset>
RenewTime: Sun, 17 May 2020 16:15:03 +0200
Conditions:
Type Status ... Reason Message
---- ------ --- ------ -------
MemoryPressure False ... KubeletHasSufficientMemory ...
DiskPressure False ... KubeletHasNoDiskPressure ...
PIDPressure False ... KubeletHasSufficientPID ...
Ready True ... KubeletReady ...
Addresses:
InternalIP: 172.18.0.4
Hostname: kind-worker2
Capacity:
cpu: 8
ephemeral-storage: 401520944Ki
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 32720824Ki
pods: 110
Allocatable:
...
System Info:
...
PodCIDR: 10.244.1.0/24
PodCIDRs: 10.244.1.0/24
Non-terminated Pods: (2 in total)
Namespace Name CPU Requests CPU Limits ... AGE
--------- ---- ------------ ---------- ... ---
kube-system kindnet-4xmjh 100m (1%) 100m (1%) ... 13d
kube-system kube-proxy-dgkfm 0 (0%) 0 (0%) ... 13d
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.)
Resource Requests Limits
-------- -------- ------
cpu 100m (1%) 100m (1%)
memory 50Mi (0%) 50Mi (0%)
ephemeral-storage 0 (0%) 0 (0%)
hugepages-1Gi 0 (0%) 0 (0%)
hugepages-2Mi 0 (0%) 0 (0%)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Starting 3m50s kubelet, kind-worker2 ...
Normal NodeAllocatableEnforced 3m50s kubelet, kind-worker2 ...
Normal NodeHasSufficientMemory 3m50s kubelet, kind-worker2 ...
Normal NodeHasNoDiskPressure 3m50s kubelet, kind-worker2 ...
Normal NodeHasSufficientPID 3m50s kubelet, kind-worker2 ...
Normal Starting 3m49s kube-proxy, kind-worker2 ...
如您所见,kubectl describe命令显示了您以前在Node对象的YAML清单中找到的所有信息,但是以一种更可读的形式。您可以看到名称、IP地址和主机名,以及节点的条件和可用容量。
检查与Node相关的其他对象
除了存储在Node对象本身中的信息外,kubectl describe命令还显示在节点上运行的pods以及分配给它们的计算资源总量。下面还列出了与该节点相关的事件。
这些附加信息不在Node对象本身中找到,而是由kubectl工具从其他API对象中收集的。例如,运行在节点上的pods列表是通过pods资源检索Pod对象获得的。
如果您自己运行describe命令,则不会显示任何事件。这是因为只显示最近发生的事件。对于Node对象,除非节点有资源容量问题,否则只有在最近(重新)启动节点时才会看到事件。
实际上,每种API对象类型都有与之关联的事件。由于它们对于调试集群至关重要,因此在开始研究其他对象之前,需要对它们进行仔细研究。
4.3 通过Event对象观察集群事件
当控制器执行将对象的实际状态与所需状态协调起来的任务时,就像对象的spec字段中指定的那样,它们会生成事件来揭示它们所做的事情。事件有两种类型:普通事件和警告事件。后一种类型的事件通常是由控制器在某些事情阻止它们协调对象时生成的。通过监视这种类型的事件,您可以迅速获知集群遇到的任何问题。
4.3.1 介绍Event对象
与Kubernetes中的其他内容一样,事件由通过Kubernetes API创建和读取的Event对象表示。如下图所示,它们包含关于对象发生了什么以及事件源是什么的信息。与其他对象不同,每个事件对象在创建一小时后被删除,以减轻etcd (Kubernetes API对象的数据存储)的负担。
图4.7 Event 对象、控制器和其他API对象之间的关系。
请注意
保留事件的时间可以通过API服务器的命令行选项进行配置。
使用kubectl get events列出事件
kubectl describe显示的事件引用指定的对象作为命令的参数。由于它们的性质以及可以在短时间内为一个对象创建许多事件的事实,它们不是对象本身的一部分。您不会在对象的YAML清单中找到它们,因为它们是独立存在的,就像到目前为止您看到的Nodes和其他对象一样。
请注意
如果希望在自己的集群中遵循本节中的练习,可能需要重新启动其中一个节点,以确保事件是最近的,以便在etcd中仍然存在。如果您不能这样做,不要担心,您可以跳过这些练习,因为您还将在下一章的练习中生成和检查事件。
因为Events是独立的对象,所以可以使用kubectl get events列出它们,如下面的清单所示。
Listing 4.7 Listing events using kubectl get events
$ kubectl get ev
LAST
SEEN TYPE REASON OBJECT MESSAGE
48s Normal Starting node/kind-worker2 Starting kubelet.
48s Normal NodeAllocatableEnforced node/kind-worker2 Updated Node A...
48s Normal NodeHasSufficientMemory node/kind-worker2 Node kind-work...
48s Normal NodeHasNoDiskPressure node/kind-worker2 Node kind-work...
48s Normal NodeHasSufficientPID node/kind-worker2 Node kind-work...
47s Normal Starting node/kind-worker2 Starting kube-...
请注意
前面的清单使用短名称ev代替events。
您将注意到清单中显示的一些事件与节点的状态条件匹配。这是常见的情况,但您还会发现其他事件。Reason为Starting 的两个事件就是这样的两个例子。在本例中,它们表示Kubelet和Kube代理组件已经在节点上启动。您还不需要担心这些组件。书的第三部分对它们进行了解释。
理解Event对象中的内容
与其他对象一样,kubectl get命令只输出最重要的对象数据。要显示附加信息,您可以通过执行命令的-o wide选项启用附加列:
$ kubectl get ev -o wide
这个命令的输出非常宽泛,本书中没有列出。相反,显示的信息在下表中解释。
表4.1 Event对象的属性
属性 |
解释 |
Name | 此事件对象实例的名称。只有当您想要从API检索给定的对象时才有用。 |
Type | 事件的类型。正常Normal 或警告Warning。 |
Reason | 面向机器的描述事件发生的原因。 |
Source | 报告此事件的组件。这通常是一个控制器。 |
Object | 事件引用的对象实例。例如,node/xyz。 |
Sub-object | 事件引用的子对象。例如,什么容器的pod。 |
Message | 对事件的人性化描述。 |
First seen | 事件第一次发生时。记住,每个事件对象在一段时间后都会被删除,因此这可能不是事件实际发生的第一次。 |
Last seen | 事件经常重复发生。此字段指示此事件最近发生的时间。 |
Count | 此事件发生的次数。 |
提示
当您完成本书中的练习时,您可能会发现在每次对某个对象进行更改时运行kubectl get events命令非常有用。这将帮助您了解表面之下发生了什么。
只显示警告事件
kubectl describe命令只显示与所描述对象相关的事件,与此不同,kubectl get events命令显示所有事件。如果您想检查是否有需要关注的事件,这将非常有用。您可能想忽略Normal 类型的事件,而只关注Warning类型的事件。
API提供了一种通过称为字段选择器的机制来筛选对象的方法。只返回指定字段与指定选择器值匹配的对象。您可以使用它来仅显示警告事件。kubectl get命令允许您使用--field selector选项指定字段选择器。若要仅列出表示警告的事件,请执行以下命令:
$ kubectl get ev --field-selector type=Warning
No resources found in default namespace.
如果该命令没有打印任何事件(如上述情况),则集群中最近没有记录任何警告。
您可能想知道我是如何知道要在字段选择器中使用的字段的确切名称以及它的确切值应该是什么(例如,可能应该是小写的)。如果您认为这些信息是由kubectl explain events命令提供的,那么请向您致敬。因为事件是常规的API对象,所以可以使用它来查找关于事件对象结构的文档。在这里,您将了解到type字段可以有两个值:Normal或Warning。
4.3.2 检查事件对象的YAML
要检查集群中的事件,kubectl describe 和kubectl get events应该足够了。与其他对象不同,您可能永远都不需要显示事件对象的完整YAML。但我想借此机会向您展示API返回的Kubernetes对象清单的一个恼人之处。
事件对象没有规范和状态部分
如果您使用kubectl explain来研究Event对象的结构,您会注意到它没有spec或status部分。不幸的是,这意味着它的字段组织得不像Node对象中的那样好。
检查下面的清单,看看是否可以轻松地找到对象类型、元数据和其他字段。
Listing 4.8 The YAML manifest of an Event object
apiVersion: v1
count: 1
eventTime: null
firstTimestamp: "2020-05-17T18:16:40Z"
involvedObject:
kind: Node
name: kind-worker2
uid: kind-worker2
kind: Event
lastTimestamp: "2020-05-17T18:16:40Z"
message: Starting kubelet.
metadata:
creationTimestamp: "2020-05-17T18:16:40Z"
managedFields:
- ...
name: kind-worker2.160fe38fc0bc3703 #D
namespace: default
resourceVersion: "3528471"
selfLink: /api/v1/namespaces/default/events/kind-worker2.160f...
uid: da97e812-d89e-4890-9663-091fd1ec5e2d
reason: Starting
reportingComponent: ""
reportingInstance: ""
source:
component: kubelet
host: kind-worker2
type: Normal
您肯定会同意清单中的YAML清单是无序的。字段按字母顺序列出,而不是按一致的组组织。这使得我们人类很难阅读。它看起来如此混乱,难怪许多人讨厌处理Kubernetes YAML或JSON清单,因为它们都有这个问题。
相反,节点对象的较早的YAML清单文件比较容易阅读,因为顶级字段的顺序是我们所期望的:apiVersion、kind、metadata、spec和status。您会注意到,这只是因为这五个字段的字母顺序恰好有意义。但是这些字段下面的字段也有同样的问题,因为它们也是按字母顺序排序的。
YAML应该便于人们阅读,但是Kubernetes YAML中的字段的字母顺序打破了这一点。幸运的是,大多数对象都包含spec和status部分,因此至少这些对象中的顶级字段组织得很好。至于其他方面,您将不得不接受处理Kubernetes清单的这个不幸方面。
4.4 总结
在本章中,你会学到:
-
Kubernetes提供了用于与集群交互的RESTful API。API对象映射到组成集群的实际组件,包括应用程序、负载平衡器、节点、存储卷和许多其他组件。
-
一个对象实例可以由许多资源表示。单一对象类型可以通过几个资源公开,这些资源只是同一事物的不同表示。
-
Kubernetes API对象在YAML或JSON清单中描述。对象是通过向API发布清单来创建的。对象的状态存储在对象本身中,可以通过使用GET请求从API请求对象来获取。
-
所有Kubernetes API对象都包含类型和对象元数据,并且大多数都有spec 和status部分。一些对象类型没有这两个部分,因为它们只包含静态数据。
-
控制器通过不断观察对象spec 中的变化,更新集群状态并通过对象的status字段报告当前状态,从而使对象具有生命力。
-
当控制器管理Kubernetes API对象时,它们发出事件来显示它们执行了哪些操作。与其他事情一样,事件由事件对象表示,并且可以通过API进行检索。事件表明节点或其他对象发生了什么。它们显示了这个物体最近发生了什么,并能提供它为什么会破碎的线索。
-
kubectl explain命令提供了一种从命令行快速查找特定对象类型及其字段的文档的方法。
-
节点对象中的状态包含关于节点的IP地址和主机名、其资源容量、条件、缓存的容器映像和关于节点的其他信息。在节点上运行哪些pods并不影响节点的状态,但是kubectl describe node命令从pods资源中获取这些信息。
-
许多对象类型使用状态条件来表示对象所表示的组件的状态。对nodes来说,这些条件是MemoryPressure, DiskPressure 和PIDPressure。每个条件要么是True, False, 或者Unknown的,并且都有一个相关的reason 和message来解释为什么该条件处于指定状态。
现在您应该熟悉了Kubernetes API对象的一般结构。在下一章中,您将了解Pod对象,它是表示应用程序的一个运行实例的基本构建块。