Kubernetes(K8s)Events介绍(上)

简介: Kubernetes Events虽不常被提起,却意义非凡。它存储在Etcd里,记录了集群运行所遇到的各种大事件。本系列文章将一步一步地揭开Kubernetes Events的神秘面纱。 师出有名 前些天群里有位同学(@二东)提问说怎么通过API得到kubectl describe pod的结果,我立刻找到了Kubernetes相关的API并回复他,但他说这不是他要的东西。

Kubernetes Events虽不常被提起,却意义非凡。它存储在Etcd里,记录了 集群运行所遇到的各种大事件。本系列文章将一步一步地揭开Kubernetes Events的神秘面纱。

师出有名

前些天群里有位同学(@二东)提问说怎么通过API得到kubectl describe pod的结果,我立刻找到了Kubernetes相关的API并回复他,但他说这不是他要的东西。经过一番描述,我才了解到他想要的是原来如下图中红框里的信息:

20161219150015

Message属于Kubernetes的一种特殊的资源:Events。老实讲,我以前是没有怎么注意过这个Events是怎么来的,甚至一直觉得它应该是Pod的一部分。那么这个Events到底是什么样的资源?它从何而来?下面随我一起踏上捉妖之路。

真身难觅

熟悉Kubernetes的小伙伴应该对于它的资源比较有体会,作为调度基本单元的Pod是一种资源,控制Pod更新、扩容、副本数量的ReplicaSet是一种资源,作为发布单位的Deployment也是一种资源,哦,还有Service,Endpoints等等。它们有这么一些共同点:

  • 都可以通过kubectl get $ResourceName的方式获取
  • 都有对应的RESTful API定义的操作

Events亦如此,kubectl get events的结果为下图:

20161219150024

就像kubectl get pods一样,通过kubectl get events获得当前命名空间下所有Events的列表。如果想查看某条Events的详细信息,是否也可以使用kubectl describe events $EventsName进行获取呢?我尝试了下,得到了下面的失败信息:

20161219150032

甚至连kubectl get events $EventsName也变得没那么好使了:

20161219150039

另外,Events的RESTful API为下图:

20161219150048

而通过最朴素的curl 之刃也没能找出Events的真身:

20161219150055

这着实让人“大吃一斤”。

踏破铁鞋

那么这个Events到底是何方神圣?常规手段居然拿它没一丁点办法了?不,还有一招json宝典待我祭出使用。我们知道kubectl get一些资源的时候可以通过它的-o json参数得到该资源的json格式的信息描述,那么对上面的Events进行kubectl get events -o json > /tmp/events.json,得到了一个json数组,里面的每一条都对应着一个Events对象:

20161219150104

Bingo!这就是我们要找的Events对应的实体json。仔细观察,图中红框里的名字正是kubectl get events里得到的Events名字,然而实际上它并不是真正的Events的名字。这是为何?在Kubernetes资源的数据结构定义中,一般都会包含一个metav1.TypeMeta和一个ObjectMeta成员,比如:

20161219150115

TypeMeta里定义了该资源的类别和版本,对应我们平时写json文件或者yaml文件时的kind: Pod和apiVersion: v1。OjbectMeta里定义了该资源的元信息,包括名称、命名空间、UID、创建时间、标签组等。

Events的数据结构定义里同样包含了metav1.TypeMeta和ObjectMeta,那么从前面的json图中可以确定红框里的名字并不是该Events对象的真实名字,它对应的是InvolvedObject的名字,而蓝框里对应的才是Events的真实名字。然后使用kubectl get和curl进行验证:

20161219150124

到此,我们已经探明了Events的真名。仔细观察它的名字,发现它由三部分构成:”发生该Events的Pod名称” + “.” + “神秘数字串”。发生该Events的Pod名称倒是好理解,而后面的神秘数字串却不知从何而来。既然这个Events是来自这个Pod的状态变化,那么想必这个数字串也可以在Pod的详情里找到吧。出乎意料的是在Pod的详情json未曾找到这个神秘数字串,甚至把这个Pod祖上的ReplicaSet和Deployment也翻了个底朝天也是没有一点线索。捉妖行动陷入僵局。

星宿Kubelet

道高一尺,魔高一丈。纵使齐天大圣也得经常搬救兵,而不少时候这个妖怪就是某个星宿的坐骑或童子。拿起这个Events的json仔细观察,发现里面有个不起眼的component: kubelet:

20161219150133

回想一下kubelet的作用是:负责管理和维护在这台主机上运行着的所有容器,维持pod的运行状态(status)和它的期望值(spec)一致。kubelet启动时,会加载它本身的配置信息、接入容器运行时(Docker或Rkt)、以及定义kubelet与整个集群进行交互所需的信息。在启动后,会进行一系列的初始化工作,创建ContainerManager、设置OOMScoreAdj值、创建DiskSpaceManager、PodManager等等。那么会不会是kubelet为这个Pod创建了该Events呢?

为了解答这个问题,不得不拿出尘封已久的究极“源码大法”。

果然在Kubernetes源码的kubernetes/pkg/kubelet/events/event.go里有了一些收获。event.go里定义了是Events资源常用的一些常量,挑选列出如下:

const (
FailedToStartContainer = “Failed”
BackOffStartContainer = “BackOff”
ErrImageNeverPullPolicy = “ErrImageNeverPull”
BackOffPullImage = “BackOff”
FailedSync = “FailedSync”

)

这里面的不少我们平时都会经常遇到。它们是各种事件的原因列表,包含容器事件(创建、启动、失败等)、镜像事件(镜像拉取失败等)、kubelet事件(节点失效、节点不可调度等)、Pod Worker事件(同步失败)等。随便拿出一条进行暴力搜寻,比如”FailedToStartContainer”,发现除了在此处的定义进行了引用外,还在kubernetes/pkg/kubelet/dockertools/docker_manager.go里711行进行了引用:

dm.recorder.Eventf(ref, api.EventTypeWarning, events.FailedToStartContainer, “Failed to start container with docker id %v with error: %v”, utilstrings.ShortenString(createResp.ID, 12), err)

由此更加确认的是Events跟Kubelet密不可分。循着这条线索一直往下,还会有什么惊奇的发现?

火眼金睛

上面引用了docker_manager.go里的一行,除开事件本身外,里面还有一些关键词值得琢磨:dm, recorder, Eventf。

dm是结构体DockerManager的实例,该结构体定义也在这个文件中。定义比较长,这里就不贴出来了,它包含了DockerInterface的成员、EventRecorder的成员、OSInterface的成员以及cadvisorapi.MachineInfo的成员等等。这个EventRecorder的定义在k8s.io/kubernetes/pkg/client/record/event.go里,是一个接口,可以按照EventSource的表现记录相应的事件。
type EventRecorder interface {
Event(object runtime.Object, eventtype, reason, message string)
Eventf(object runtime.Object, eventtype, reason, messageFmt string, args …interface{})
PastEventf(object runtime.Object, timestamp unversioned.Time, eventtype, reason, messageFmt string, args …interface{})
}

在这个文件的285~295行有对这个三个接口的实现:

func (recorder *recorderImpl) Event(object runtime.Object, eventtype, reason, message string) {
recorder.generateEvent(object, unversioned.Now(), eventtype, reason, message)
}
func (recorder *recorderImpl) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args …interface{}) {
recorder.Event(object, eventtype, reason, fmt.Sprintf(messageFmt, args…))
}
func (recorder *recorderImpl) PastEventf(object runtime.Object, timestamp unversioned.Time, eventtype, reason, messageFmt string, args …interface{}) {
recorder.generateEvent(object, timestamp, eventtype, reason, fmt.Sprintf(messageFmt, args…))
}

不管是PastEventf()、Eventf()还是Event()最终都指向了辅助函数generateEvent(),它的实现在255行:

func (recorder *recorderImpl) generateEvent(object runtime.Object, timestamp unversioned.Time, eventtype, reason, message string) { // 获取发生事件的对象,比如前面说到的Pod
ref, err := api.GetReference(object) if err != nil {
glog.Errorf(“Could not construct reference to: ‘%#v’ due to: ‘%v’. Will not report event: ‘%v’ ‘%v’ ‘%v'”, object, err, eventtype, reason, message) return
}
// 验证事件类型
if !validateEventType(eventtype) {
glog.Errorf(“Unsupported event type: ‘%v'”, eventtype) return
}
// 生成Event
event := recorder.makeEvent(ref, eventtype, reason, message)
event.Source = recorder.source

go func() {
// NOTE: events should be a non-blocking operation
defer utilruntime.HandleCrash()
// 将Event进行广播
recorder.Action(watch.Added, event)
}()
}

recorder.makeEvent()就是这个Events的真正来源:

func (recorder *recorderImpl) makeEvent(ref *api.ObjectReference, eventtype, reason, message string) *api.Event { t := unversioned.Time{Time: recorder.clock.Now()} namespace := ref.Namespace
if namespace == “” {
namespace = api.NamespaceDefault
}
return &api.Event{
ObjectMeta: api.ObjectMeta{
// 事件的命名规则
Name: fmt.Sprintf(“%v.%x”, ref.Name, t.UnixNano()),
Namespace: namespace,
},
InvolvedObject: *ref,
Reason: reason,
Message: message,
FirstTimestamp: t,
LastTimestamp: t,
Count: 1,
Type: eventtype,
}
}

到此恍然大悟,原来前面所说的Events的命名规则:”发生该Events的Pod名称” + “.” + “神秘数字串”实际上是”ref.Name” + “.” + “t.UnixNano())”。

终于找到了Events的真身,原来它是Kubelet负责用来记录多个容器运行过程中的事件,命名由被记录的对象和时间戳构成。前面看起来难以捉摸的表现最终还是逃不过火眼金睛。到此上篇解决了Events从哪儿来的问题,暂告一段落。在下篇中将对Events关于到哪儿去、怎么根据Pod找到对应的Events等进行深度拷问。欢迎继续关注!

本文中出现的curl 之刃、json宝典、源码大法均属于为了轻松阅读体验而凭空捏造的词汇,分别对应用命令行工具curl进行访问和实验、查看该对象的json信息、阅读源码寻找答案释疑。

本文转自中文社区-Kubernetes(K8s)Events介绍(上)

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
2月前
|
Kubernetes 监控 物联网
IoT 边缘集群基于 Kubernetes Events 的告警通知实现(二):进一步配置
IoT 边缘集群基于 Kubernetes Events 的告警通知实现(二):进一步配置
|
2月前
|
存储 Kubernetes 监控
IoT 边缘集群基于 Kubernetes Events 的告警通知实现
IoT 边缘集群基于 Kubernetes Events 的告警通知实现
|
JSON Kubernetes 数据格式
Kubernetes Events介绍(中)
上一回,历经千辛万苦终于破解了Events的姓名之谜,(Kubernetes(K8s)Events介绍(上))寻得Events真身。那么Events的身世究竟如何?根据Pod怎样才能找到对应的Events?本回将一一揭开谜底。
1622 0
|
Kubernetes 容器 Perl
Kubernetes Events介绍(下)
经过前两回的“踏血寻妖”,一个完整的Events原形逐渐浮出水面。我们已经摸清了它的由来和身世,本回将一起探索Events的去向,这是一个终点却也是另一个起点。 蜜汁去向 前面已经了解到,Event是由一个叫EventRecorder的东西幻化而生。
1505 0
|
2月前
|
Kubernetes Ubuntu 应用服务中间件
在Ubuntu22.04 LTS上搭建Kubernetes集群
在Ubuntu22.04.4上安装Kubernetes v1.28.7,步骤超详细
311 1
在Ubuntu22.04 LTS上搭建Kubernetes集群
|
30天前
|
Prometheus 监控 Kubernetes
Kubernetes 集群监控与日志管理实践
【2月更文挑战第29天】 在微服务架构日益普及的当下,Kubernetes 已成为容器编排的事实标准。然而,随着集群规模的扩大和业务复杂度的提升,有效的监控和日志管理变得至关重要。本文将探讨构建高效 Kubernetes 集群监控系统的策略,以及实施日志聚合和分析的最佳实践。通过引入如 Prometheus 和 Fluentd 等开源工具,我们旨在为运维专家提供一套完整的解决方案,以保障系统的稳定性和可靠性。
|
16天前
|
数据库 存储 监控
什么是 SAP HANA 内存数据库 的 Delta Storage
什么是 SAP HANA 内存数据库 的 Delta Storage
16 0
什么是 SAP HANA 内存数据库 的 Delta Storage
|
5天前
|
Kubernetes 搜索推荐 Docker
使用 kubeadm 部署 Kubernetes 集群(二)k8s环境安装
使用 kubeadm 部署 Kubernetes 集群(二)k8s环境安装
38 17
|
18天前
|
消息中间件 Kubernetes Kafka
Terraform阿里云创建资源1分钟创建集群一键发布应用Terraform 创建 Kubernetes 集群
Terraform阿里云创建资源1分钟创建集群一键发布应用Terraform 创建 Kubernetes 集群
14 0
|
18天前
|
Kubernetes 安全 网络安全
搭建k8s集群kubeadm搭建Kubernetes二进制搭建Kubernetes集群
搭建k8s集群kubeadm搭建Kubernetes二进制搭建Kubernetes集群
101 0