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介绍(上)

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
3月前
|
存储 Kubernetes 网络安全
关于阿里云 Kubernetes 容器服务(ACK)添加镜像仓库的快速说明
本文介绍了在中国大陆地区因网络限制无法正常拉取 Docker 镜像的解决方案。作者所在的阿里云 Kubernetes 集群使用的是较旧版本的 containerd(1.2x),且无法直接通过 SSH 修改节点配置,因此采用了一种无需更改 Kubernetes 配置文件的方法。通过为 `docker.io` 添加 containerd 的镜像源,并使用脚本自动修改 containerd 配置文件中的路径错误(将错误的 `cert.d` 改为 `certs.d`),最终实现了通过多个镜像站点拉取镜像。作者还提供了一个可重复运行的脚本,用于动态配置镜像源。虽然该方案能缓解镜像拉取问题,
424 2
|
9月前
|
存储 Kubernetes 监控
K8s集群实战:使用kubeadm和kuboard部署Kubernetes集群
总之,使用kubeadm和kuboard部署K8s集群就像回归童年一样,简单又有趣。不要忘记,技术是为人服务的,用K8s集群操控云端资源,我们不过是想在复杂的世界找寻简单。尽管部署过程可能遇到困难,但朝着简化复杂的目标,我们就能找到意义和乐趣。希望你也能利用这些工具,找到你的乐趣,满足你的需求。
896 33
|
9月前
|
存储 人工智能 Kubernetes
ACK Gateway with AI Extension:面向Kubernetes大模型推理的智能路由实践
本文介绍了如何利用阿里云容器服务ACK推出的ACK Gateway with AI Extension组件,在Kubernetes环境中为大语言模型(LLM)推理服务提供智能路由和负载均衡能力。文章以部署和优化QwQ-32B模型为例,详细展示了从环境准备到性能测试的完整实践过程。
|
11月前
|
存储 运维 Kubernetes
正式开源,Doris Operator 支持高效 Kubernetes 容器化部署方案
飞轮科技推出了 Doris 的 Kubernetes Operator 开源项目(简称:Doris Operator),并捐赠给 Apache 基金会。该工具集成了原生 Kubernetes 资源的复杂管理能力,并融合了 Doris 组件间的分布式协同、用户集群形态的按需定制等经验,为用户提供了一个更简洁、高效、易用的容器化部署方案。
527 16
正式开源,Doris Operator 支持高效 Kubernetes 容器化部署方案
|
10月前
|
监控 Kubernetes Cloud Native
基于阿里云容器服务Kubernetes版(ACK)的微服务架构设计与实践
本文介绍了如何基于阿里云容器服务Kubernetes版(ACK)设计和实现微服务架构。首先概述了微服务架构的优势与挑战,如模块化、可扩展性及技术多样性。接着详细描述了ACK的核心功能,包括集群管理、应用管理、网络与安全、监控与日志等。在设计基于ACK的微服务架构时,需考虑服务拆分、通信、发现与负载均衡、配置管理、监控与日志以及CI/CD等方面。通过一个电商应用案例,展示了用户服务、商品服务、订单服务和支付服务的具体部署步骤。最后总结了ACK为微服务架构提供的强大支持,帮助应对各种挑战,构建高效可靠的云原生应用。
|
10月前
|
弹性计算 人工智能 资源调度
DeepSeek大解读系列公开课上新!阿里云专家主讲云上智能算力、Kubernetes容器服务、DeepSeek私有化部署
智猩猩「DeepSeek大解读」系列公开课第三期即将开讲,聚焦阿里云弹性计算助力大模型训练与部署。三位专家将分别讲解智能算力支撑、Kubernetes容器服务在AI场景的应用实践、以及DeepSeek一键部署和多渠道应用集成,分享云计算如何赋能大模型发展。欲观看直播,可关注【智猩猩GenAI视频号】预约。 (239字符)
|
9月前
|
存储 运维 Kubernetes
容器数据保护:基于容器服务 Kubernetes 版(ACK)备份中心实现K8s存储卷一键备份与恢复
阿里云ACK备份中心提供一站式容器化业务灾备及迁移方案,减少数据丢失风险,确保业务稳定运行。
|
12月前
|
存储 Kubernetes Docker
Kubernetes(k8s)和Docker Compose本质区别
理解它们的区别和各自的优势,有助于选择合适的工具来满足特定的项目需求。
1355 19
|
11月前
|
人工智能 运维 监控
容器服务Kubernetes场景下可观测体系生产级最佳实践
阿里云容器服务团队在2024年继续蝉联Gartner亚洲唯一全球领导者象限,其可观测体系是运维的核心能力之一。该体系涵盖重保运维、大规模集群稳定性、业务异常诊断等场景,特别是在AI和GPU场景下提供了全面的观测解决方案。通过Tracing、Metric和Log等技术,阿里云增强了对容器网络、存储及多集群架构的监控能力,帮助客户实现高效运维和成本优化。未来,结合AI助手,将进一步提升问题定位和解决效率,缩短MTTR,助力构建智能运维体系。
|
Kubernetes 应用服务中间件 nginx
二进制安装Kubernetes(k8s)v1.32.0
本指南提供了一个详细的步骤,用于在Linux系统上通过二进制文件安装Kubernetes(k8s)v1.32.0,支持IPv4+IPv6双栈。具体步骤包括环境准备、系统配置、组件安装和配置等。
4381 11

热门文章

最新文章

推荐镜像

更多