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

参考:https://segmentfault.com/a/1190000041204565

posted @ 2022-02-21 08:30  人艰不拆_zmc  阅读(5267)  评论(3编辑  收藏  举报