理解k8s中的事件及其原理

1,602 阅读3分钟

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

pexels-gratisography-2007

概述

在k8s中事件就像信鸽一样,将各个组件发生的事情发送给apiserver,然后apiserver又会将这些信心存储到etcd中,以供有需要的人来查询集群所发生的事情。

事件在k8s中也是一种资源,用于表示集群内发生的情况。由于事件的数量可能会非常庞大,为了防止打爆存储系统,k8s会强制清理一小时之前的事件。

另外一点需要说明的是事件是单纯的用于系统诊断用的,k8s控制器不会基于事件来触发任何行为,也因此k8s清理一小时之前的事件并不会有任何副作用,可以放心清理。

事件实例

Kubernetes的每一个组件都会发出事件,这些事件就是一个要告诉你组件内部发生了什么的小信息。大部分用过Kubernetes的人都已经看到一些“kubectl describe"输出尾部的事件信息。例如:

$ kubectl describe pod/podinfo-9f56d4b58-2jj8z
Name:         podinfo-9f56d4b58-2jj8z
Namespace:    default
Node:         ...
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  42s   default-scheduler  Successfully assigned default/podinfo-9f56d4b58-2jj8z to node1
  Normal  Pulled     41s   kubelet            Container image "ghcr.io/stefanprodan/podinfo:5.1.2" already present on machine
  Normal  Created    41s   kubelet            Created container podinfod
  Normal  Started    41s   kubelet            Started container podinfod

这些事件告诉了你一个小故事:首先,一个pod被分配到了一个节点,接着那个节点的kubelet开始了一系列的操作:拉取镜像、创建容器并且启动它。"kubectl describe"按照从旧到新的方式输出事件,这是因为最近发生的事情一般最让人感兴趣。

这是另外一个例子,我们将镜像地址改成”example“,一个不存在的地址。

Type     Reason     Age            From               Message
----     ------     ----           ----               -------
Normal   Scheduled  22s            default-scheduler  Successfully assigned default/podinfo-5487f6dc6c-gvr69 to node1
Normal   BackOff    20s            kubelet            Back-off pulling image "example"
Warning  Failed     20s            kubelet            Error: ImagePullBackOff
Normal   Pulling    8s (x2 over 22s)  kubelet         Pulling image "example"
Warning  Failed     6s (x2 over 20s)  kubelet         Failed to pull image "example": rpc error: code = Unknown desc = failed to pull and unpack image "docker.io/library/example": failed to resolve reference "docker.io/library/example§": pull access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
Warning  Failed     6s (x2 over 20s)  kubelet         Error: ErrImagePull

你将会看到最后三行都有"x2",这是因为Kubernetes会把重复发生的事件合并到一起。例如“Age: 6s (x2 over 20s)”,意思是最近发生的事件发生在6s之前,而第二次发生在20s之前。

第二天再来看这个pod,我得到这样的信息

Type     Reason   Age                     From     Message
----     ------   ----                    ----     -------
Normal   BackOff  19m (x4524 over 17h)    kubelet  Back-off pulling image "example"
Warning  Failed   4m18s (x4591 over 17h)  kubelet  Error: ImagePullBackOff

这表明,一方面kubelet的执行策略,17个小时之后依然在尝试执行,另一方面,Kubernetes也会关注资源的使用情况,一个小时之后的事件会被自动删除。也就是说我们不能再看的那些错误的详细信息了。

事件的用处

如果你不知道哪里去找一个具体的问题,你可能需要查看所有的事件。

$ kubectl get events -A
NAMESPACE   LAST SEEN   TYPE      REASON              OBJECT                                MESSAGE
...
default     46s         Normal    Scheduled           pod/podinfo-78bbb69b79-wfzrk    Successfully assigned default/podinfo-78bbb69b79-wfzrk to kind-control-plane
default     46s         Normal    Pulled              pod/podinfo-78bbb69b79-wfzrk    Container image "ghcr.io/stefanprodan/podinfo:5.1.1" already present on machine
default     46s         Normal    Created             pod/podinfo-78bbb69b79-wfzrk    Created container podinfod
default     46s         Normal    Started             pod/podinfo-78bbb69b79-wfzrk    Started container podinfod
default     47s         Normal    SuccessfulCreate    replicaset/podinfo-78bbb69b79   Created pod: podinfo-78bbb69b79-wfzrk
default     47s         Normal    ScalingReplicaSet   deployment/podinfo              Scaled up replica set podinfo-78bbb69b79 to 1

现在你可以看到关于Pod、ReplicaSet和Deployment的不同的事件都在滚动列出。

注意:”kubectl get events" 可能会输出大量信息,特别是集群非常繁忙时。令人头疼的是事件并不是按照时间戳排列的,因此你需要有办法来寻找或者将这些信息输出到文件中进行分析。

时间的命名方式为: “InvoledObject对象名称"+"."+"事件创建纳秒时间戳"

fmt.Sprintf("%v.%x", ref.Name, t.UnixNano())

一般事件本身的命名意义不是很大,而其产生对象的名称更有意义,通过这个名称来查询它所发生的事件,当然也可能查不到,因为事件超过一个小时就会被删除。

事件原理

事件结构

Kubernetes使用对象模型存储事件,正如Deployment和Pod那样。我们通过kubectl来看一下Event的内部信息。

$ kubectl get event/podinfo-84b5bccbfd-rgb42.166185b1cd1dc668 -o yaml
apiVersion: v1
kind: Event
metadata:
  name: podinfo-84b5bccbfd-rgb42.166185b1cd1dc668
  namespace: default
type: Normal
count: 4636
firstTimestamp: "2021-02-07T16:59:00Z"
lastTimestamp: "2021-02-08T10:39:04Z"
message: Back-off pulling image "example"
reason: BackOff
source:
  component: kubelet
  host: node1
involvedObject:
  apiVersion: v1
  kind: Pod
  name: podinfo-84b5bccbfd-rgb42
  namespace: default

正如你所看到的,每一个事件都是一个对象,属于一个命名空间,拥有一个唯一的名字,其他主要字段详述如下:

  • Count,firstTimestamp和lasteTimestamp 表示事件重复了多少次
  • Message 人类可读的文本信息
  • Reason 简短的编码,可用于过滤器
  • Type Normal或者Warning
  • Source 事件发出的来源
  • InvolvedObject 引用的另一个Kubernetes对象,例如Pod或者Deployment

现在我们清楚了,通过kubectl describe展示的事件列表,正是InvoledObject匹配的事件。

事件管理机制

Event事件管理主要包含三部分:

  • EventRecorder:事件生成器,k8s组件通过调用它的方法来生成事件;
  • EventBroadcaster:事件广播器,负责消费EventRecorder产生的事件,然后分发给broadcasterWatcher;
  • broadcasterWatcher:用于定义事件的处理方式,如上报apiserver;

事件管理的整体流程如下:

image-20201011221745830

扩展阅读

www.kubernetes.org.cn/1031.html这是一个系列文章,分为上中下三篇,从浅入深以通俗的方式讲解了k8s的事件机制,值得一看。

kubernetes.io/docs/tasks/… 官方文档,介绍了如何通过事件调试应用程序,推荐查阅,并最好实际操作一下

www.bluematador.com/blog/kubern… 详细解释了事件使用