Kubernetes Event详述及持久化方案
1、什么是Kubernetes Event
Kubernetes的事件(Event)是一种资源对象(Resource Object),用于展示集群内发生的情况,Kubernetes系统中的各个组件会将运行时发生的各种事件上报给Kubernetes API Server。例如,调度器做了什么决定,某些Pod为什么被从节点中驱逐。可以通过kubectl get event或kubectl describe pod <podname>命令显示事件,查看Kubernetes集群中发生了哪些事件。执行这些命令后,默认情况下只会显示最近(1小时内)发生的事件。
由于Kubernetes的事件是一种资源对象,因此它们存储在Kubernetes API Server的Etcd集群中。为避免磁盘空间被填满,故强制执行保留策略:在最后一次的事件发生后,删除1小时之前发生的事件。
2、为什么需要Kubernetes Event
Kubernetes 是分布式架构,apiserver 是整个集群的交互中心,客户端主要和它打交道,kubelet 是各个节点上的 worker,负责执行具体的任务。对于用户来说,每次创建资源的时候,除了看到它的最终状态(一般是运行态),希望看到资源执行的过程,中间经过了哪些步骤。这些反馈信息对于调试来说非常重要,有些任务会失败或者卡在某个步骤,有了这些信息,我们就能够准确地定位问题。
3、Kubernetes Event示例
我们先来做个简单的示例,来看看 Kubernetes 集群中的 events 是什么。
创建一个新的名叫 zmc 的 namespace ,然后在其中创建一个叫做 redis 的 deployment。接下来查看这个 namespace 中的所有 events。
[root@master1 ~]# kubectl create ns zmc namespace/zmc created [root@master1 ~]# kubectl -n zmc create deployment redis --image=redis deployment.apps/redis created [root@master1 ~]# kubectl -n zmc get deploy NAME READY UP-TO-DATE AVAILABLE AGE redis 1/1 1 1 35s [root@master1 ~]# kubectl -n zmc get events LAST SEEN TYPE REASON OBJECT MESSAGE 67s Normal Scheduled pod/redis-6749d7bd65-m6cmj Successfully assigned zmc/redis-6749d7bd65-m6cmj to node2 66s Normal Pulling pod/redis-6749d7bd65-m6cmj Pulling image "redis" 55s Normal Pulled pod/redis-6749d7bd65-m6cmj Successfully pulled image "redis" in 11.720629892s 48s Normal Created pod/redis-6749d7bd65-m6cmj Created container redis 48s Normal Started pod/redis-6749d7bd65-m6cmj Started container redis 67s Normal SuccessfulCreate replicaset/redis-6749d7bd65 Created pod: redis-6749d7bd65-m6cmj 67s Normal ScalingReplicaSet deployment/redis Scaled up replica set redis-6749d7bd65 to 1 [root@master1 ~]#
但是我们会发现默认情况下 kubectl get events 并没有按照 events 发生的顺序进行排列,所以我们往往需要为其增加 --sort-by='{.metadata.creationTimestamp}' 参数来让其输出可以按时间进行排列。
按时间排序后可以看到如下结果:
[root@master1 ~]# kubectl -n zmc get events --sort-by='{.metadata.creationTimestamp}' LAST SEEN TYPE REASON OBJECT MESSAGE 3m12s Normal Scheduled pod/redis-6749d7bd65-m6cmj Successfully assigned zmc/redis-6749d7bd65-m6cmj to node2 3m12s Normal SuccessfulCreate replicaset/redis-6749d7bd65 Created pod: redis-6749d7bd65-m6cmj 3m12s Normal ScalingReplicaSet deployment/redis Scaled up replica set redis-6749d7bd65 to 1 3m11s Normal Pulling pod/redis-6749d7bd65-m6cmj Pulling image "redis" 3m Normal Pulled pod/redis-6749d7bd65-m6cmj Successfully pulled image "redis" in 11.720629892s 2m53s Normal Created pod/redis-6749d7bd65-m6cmj Created container redis 2m53s Normal Started pod/redis-6749d7bd65-m6cmj Started container redis [root@master1 ~]#
通过以上的操作,我们可以发现 events 实际上是 Kubernetes 集群中的一种资源。当 Kubernetes 集群中资源状态发生变化时,可以产生新的 events。
4、深入了解Kubernetes Event
4.1、获取当前namespace下所有的event名称
既然 events 是 Kubernetes 集群中的一种资源,正常情况下它的 metadata.name 中应该包含其名称,用于进行单独操作。所以我们可以使用如下命令输出其 name :
[root@master1 ~]# kubectl -n zmc get events --sort-by='{.metadata.creationTimestamp}' -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' redis-6749d7bd65-m6cmj.16d5a1b59bce1473 redis-6749d7bd65.16d5a1b59b9ad7c4 redis.16d5a1b5990ac00c redis-6749d7bd65-m6cmj.16d5a1b5cc73736d redis-6749d7bd65-m6cmj.16d5a1b8870e4081 redis-6749d7bd65-m6cmj.16d5a1ba20df9afd redis-6749d7bd65-m6cmj.16d5a1ba2c4418d8 [root@master1 ~]#
4.2、kubectl describe 中的 Events
我们可以分别对 Deployment 对象和 Pod 对象执行 describe 的操作,可以得到如下结果(省略掉了中间输出):
- 对 Deployment 操作
[root@master1 ~]# kubectl -n zmc describe deploy/redis ...... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 11m deployment-controller Scaled up replica set redis-6749d7bd65 to 1
- 对 Pod 操作
[root@master1 ~]# kubectl -n zmc describe pods redis-6749d7bd65-m6cmj Name: redis-6749d7bd65-m6cmj Namespace: zmc ....... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 13m default-scheduler Successfully assigned zmc/redis-6749d7bd65-m6cmj to node2 Normal Pulling 13m kubelet Pulling image "redis" Normal Pulled 13m kubelet Successfully pulled image "redis" in 11.720629892s Normal Created 13m kubelet Created container redis Normal Started 13m kubelet Started container redis
我们可以发现 对不同的资源对象进行 describe 的时候,能看到的 events 内容都是与自己有直接关联的。在 describe Deployment 的时候,看不到 Pod 相关的 Events 。
这说明, Event 对象中是包含它所描述的资源对象的信息的,它们是有直接联系的。
4.3、更进一步了解Kubernetes Events
我们来看看如下的示例,创建一个 Deployment ,但是使用一个不存在的镜像:
[root@master1 ~]# kubectl -n zmc create deployment non-exist --image=zmcno deployment.apps/non-exist created [root@master1 ~]# kubectl -n zmc get pods NAME READY STATUS RESTARTS AGE non-exist-54d7bcffbc-7kq4b 0/1 ErrImagePull 0 12s redis-6749d7bd65-m6cmj 1/1 Running 0 16m [root@master1 ~]#
我们可以看到当前的 Pod 处于一个 ErrImagePull
的状态。查看当前 namespace 中的 events (我省略掉了之前 deploy/redis 的记录)
[root@master1 ~]# kubectl -n zmc get events --sort-by='{.metadata.creationTimestamp}' LAST SEEN TYPE REASON OBJECT MESSAGE ...... 69s Normal SuccessfulCreate replicaset/non-exist-54d7bcffbc Created pod: non-exist-54d7bcffbc-7kq4b 69s Normal ScalingReplicaSet deployment/non-exist Scaled up replica set non-exist-54d7bcffbc to 1 69s Normal Scheduled pod/non-exist-54d7bcffbc-7kq4b Successfully assigned zmc/non-exist-54d7bcffbc-7kq4b to node2 21s Normal Pulling pod/non-exist-54d7bcffbc-7kq4b Pulling image "zmcno" 18s Warning Failed pod/non-exist-54d7bcffbc-7kq4b Error: ErrImagePull 18s Warning Failed pod/non-exist-54d7bcffbc-7kq4b Failed to pull image "zmcno": rpc error: code = Unknown desc = Error response from daemon: pull access denied for zmcno, repository does not exist or may require 'docker login': denied: requested access to the resource is denied 3s Warning Failed pod/non-exist-54d7bcffbc-7kq4b Error: ImagePullBackOff 3s Normal BackOff pod/non-exist-54d7bcffbc-7kq4b Back-off pulling image "zmcno" [root@master1 ~]#
对这个 Pod 执行 describe
操作:
[root@master1 ~]# kubectl -n zmc describe pod non-exist-54d7bcffbc-7kq4b Name: non-exist-54d7bcffbc-7kq4b Namespace: zmc ....... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 2m26s default-scheduler Successfully assigned zmc/non-exist-54d7bcffbc-7kq4b to node2 Normal Pulling 54s (x4 over 2m25s) kubelet Pulling image "zmcno" Warning Failed 50s (x4 over 2m22s) kubelet Failed to pull image "zmcno": rpc error: code = Unknown desc = Error response from daemon: pull access denied for zmcno, repository does not exist or may require 'docker login': denied: requested access to the resource is denied Warning Failed 50s (x4 over 2m22s) kubelet Error: ErrImagePull Warning Failed 23s (x6 over 2m21s) kubelet Error: ImagePullBackOff Normal BackOff 11s (x7 over 2m21s) kubelet Back-off pulling image "zmcno"
我们可以发现,这里的输出和之前正确运行 Pod 的不一样。最主要的区别在于 Age 列。这里我们看到了类似 11s (x7 over 2m21s) 这样的输出。
它的含义表示:该类型的 event 在 2m21s 中已经发生了 7 次,最近的一次发生在 11s 之前
但是当我们去直接 kubectl get events 的时候,我们并没有看到有 7 次重复的 event 。这说明 Kubernetes 会自动将重复的 events 进行合并。
4.4、Kubernetes Event资源对象字段详细解释
以下内容是从 Events 中随便选择的一条,(方法前面内容已经讲了) 并将其内容使用 YAML 格式进行输出:
[root@master1 ~]# kubectl -n zmc get events non-exist-54d7bcffbc-7kq4b.16d5a293c26b7082 -o yaml apiVersion: v1 count: 19 eventTime: null firstTimestamp: "2022-02-20T23:33:14Z" involvedObject: apiVersion: v1 fieldPath: spec.containers{zmcno} kind: Pod name: non-exist-54d7bcffbc-7kq4b namespace: zmc resourceVersion: "1540948" uid: a68c9ffe-4025-4254-9dda-f6eab5d4edb9 kind: Event lastTimestamp: "2022-02-20T23:38:16Z" message: Back-off pulling image "zmcno" metadata: creationTimestamp: "2022-02-20T23:33:14Z" name: non-exist-54d7bcffbc-7kq4b.16d5a293c26b7082 namespace: zmc resourceVersion: "1541908" selfLink: /api/v1/namespaces/zmc/events/non-exist-54d7bcffbc-7kq4b.16d5a293c26b7082 uid: c8505943-4740-4a15-bb30-c4727508be40 reason: BackOff reportingComponent: "" reportingInstance: "" source: component: kubelet host: node2 type: Normal [root@master1 ~]#
其中主要字段的含义如下:
-
count: 表示当前同类的事件发生了多少次
-
firstTimestamp
和lastTimestamp:
分别表示了这个 event 首次出现了最近一次出现的时间 -
involvedObject: 与此 event 有直接关联的资源对象(触发event的资源对象), 结构如下:
type ObjectReference struct { Kind string Namespace string Name string UID types.UID APIVersion string ResourceVersion string FieldPath string }
-
source: 直接关联的组件, 结构如下:
type EventSource struct { Component string Host string }
-
reason: 简单的总结(或者一个固定的代码),比较适合用于做筛选条件,主要是为了让机器可读,当前有超过 50 种这样的代码;
-
message: 给一个更易让人读懂的详细说明
-
type: 当前只有
Normal(正常事件)
和Warning(警告事件)
两种类型, 源码中也分别写了其含义:
// staging/src/k8s.io/api/core/v1/types.go const ( // Information only and will not cause any problems EventTypeNormal string = "Normal" // These events are to warn that something might go wrong EventTypeWarning string = "Warning" )
5、Kubernetes Event持久化方案
Kubernetes中的事件最终还是存储在etcd中,默认情况下只保存1个小时,由于etcd并不支持一些复杂的分析操作,默认Kubernetes只提供了非常简单的过滤方式,比如通过Reason、时间、类型等。同时这些事件只是被动的存在etcd中,并不支持主动推送到其他系统,通常只能手动的去查看。
而实际上我们对事件的使用需求非常高,例如:
- 对系统中的异常事件做实时告警,例如Failed、Evicted、FailedMount、FailedScheduling等。
- 通常问题排查可能要去查找历史数据,因此需要去查询更长时间范围的事件(几天甚至几个月)。
- 事件支持归类统计,例如能够计算事件发生的趋势以及与上一时间段(昨天/上周/发布前)对比,以便基于统计指标进行判断和决策。
- 支持不同的人员按照各种维度去做过滤、筛选。
- 支持自定义的订阅这些事件去做自定义的监控,以便和公司内部的部署运维平台集成。
为了让大家更便捷的使用Kubernetes事件功能,所需需要进行Kubernetes Event持久化。
5.1、使用开源项目eventrouter进行收集
项目地址: https://github.com/heptiolabs/eventrouter
参考:https://blog.csdn.net/weixin_43855694/article/details/103264930
5.2、kspan配合 Jaeger 利用 tracing 的方式来采集
参考:https://blog.csdn.net/tao12345666333/article/details/118214871
5.3、使用开源项目KubeWatch进行收集
参考:https://segmentfault.com/a/1190000041204565
5.4、使用开源项目kubernetes-event-exporter进行收集(推荐)
项目地址:https://github.com/opsgenie/kubernetes-event-exporter
参考:https://qiankunli.github.io/2020/04/27/kubernetes_event.html
6、总结
在这篇文章中,我主要通过两个示例,一个正确部署的 Deploy,以及一个使用不存在镜像部署的 Deploy,深入的介绍了 Events 对象的实际的作用及其各个字段的含义。
对于 Kubernetes 而言,Events 中包含了很多有用的信息,但是这些信息却并不会对 Kubernetes 造成什么影响,它们也并不是实际的 Kubernetes 的日志。默认情况下 Kubernetes 中的日志在 1 小时后就会被清理掉,以便释放对 etcd 的资源占用。
而在日常的kubernetes环境问题排查过程中有需要借助到event所反映的问题提供思路,所以为了能更好的让集群管理员知道发生了什么,在生产环境中,我们通常会把 Kubernetes 集群的 events 也给采集回来。
参考:https://blog.csdn.net/tao12345666333/article/details/122227883