Kubernetes(K8s)Events介绍(上)
Kubernetes Events虽不常被提起,却意义非凡。它存储在Etcd里,记录了 集群运行所遇到的各种大事件。本系列文章将一步一步地揭开Kubernetes Events的神秘面纱。
师出有名
前些天群里有位同学(@二东)提问说怎么通过API得到kubectl describe pod的结果,我立刻找到了Kubernetes相关的API并回复他,但他说这不是他要的东西。经过一番描述,我才了解到他想要的是原来如下图中红框里的信息:
Message属于Kubernetes的一种特殊的资源:Events。老实讲,我以前是没有怎么注意过这个Events是怎么来的,甚至一直觉得它应该是Pod的一部分。那么这个Events到底是什么样的资源?它从何而来?下面随我一起踏上捉妖之路。
真身难觅
熟悉Kubernetes的小伙伴应该对于它的资源比较有体会,作为调度基本单元的Pod是一种资源,控制Pod更新、扩容、副本数量的ReplicaSet是一种资源,作为发布单位的Deployment也是一种资源,哦,还有Service,Endpoints等等。它们有这么一些共同点:
- 都可以通过kubectl get $ResourceName的方式获取
- 都有对应的RESTful API定义的操作
Events亦如此,kubectl get events的结果为下图:
就像kubectl get pods一样,通过kubectl get events获得当前命名空间下所有Events的列表。如果想查看某条Events的详细信息,是否也可以使用kubectl describe events $EventsName进行获取呢?我尝试了下,得到了下面的失败信息:
甚至连kubectl get events $EventsName也变得没那么好使了:
另外,Events的RESTful API为下图:
而通过最朴素的curl 之刃也没能找出Events的真身:
这着实让人“大吃一斤”。
踏破铁鞋
那么这个Events到底是何方神圣?常规手段居然拿它没一丁点办法了?不,还有一招json宝典待我祭出使用。我们知道kubectl get一些资源的时候可以通过它的-o json参数得到该资源的json格式的信息描述,那么对上面的Events进行kubectl get events -o json > /tmp/events.json,得到了一个json数组,里面的每一条都对应着一个Events对象:
Bingo!这就是我们要找的Events对应的实体json。仔细观察,图中红框里的名字正是kubectl get events里得到的Events名字,然而实际上它并不是真正的Events的名字。这是为何?在Kubernetes资源的数据结构定义中,一般都会包含一个metav1.TypeMeta和一个ObjectMeta成员,比如:
TypeMeta里定义了该资源的类别和版本,对应我们平时写json文件或者yaml文件时的kind: Pod和apiVersion: v1。OjbectMeta里定义了该资源的元信息,包括名称、命名空间、UID、创建时间、标签组等。
Events的数据结构定义里同样包含了metav1.TypeMeta和ObjectMeta,那么从前面的json图中可以确定红框里的名字并不是该Events对象的真实名字,它对应的是InvolvedObject的名字,而蓝框里对应的才是Events的真实名字。然后使用kubectl get和curl进行验证:
到此,我们已经探明了Events的真名。仔细观察它的名字,发现它由三部分构成:”发生该Events的Pod名称” + “.” + “神秘数字串”。发生该Events的Pod名称倒是好理解,而后面的神秘数字串却不知从何而来。既然这个Events是来自这个Pod的状态变化,那么想必这个数字串也可以在Pod的详情里找到吧。出乎意料的是在Pod的详情json未曾找到这个神秘数字串,甚至把这个Pod祖上的ReplicaSet和Deployment也翻了个底朝天也是没有一点线索。捉妖行动陷入僵局。
星宿Kubelet
道高一尺,魔高一丈。纵使齐天大圣也得经常搬救兵,而不少时候这个妖怪就是某个星宿的坐骑或童子。拿起这个Events的json仔细观察,发现里面有个不起眼的component: kubelet:
回想一下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.sourcego 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介绍(上)
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
关于K8s集群器日志收集的总结
本文介绍了kubernetes官方提供的日志收集方法并介绍了Fluentd日志收集器并与其他产品做了比较。最后介绍了好雨云帮如何对k8s进行改造并使用ZeroMQ以消息的形式将日志传输到统一的日志处理中心。 容器日志存在形式 目前容器日志有两种输出形式 stdout,stderr标准输出 这种形式的日志输出我们可以直接使用docker logs查看日志k8s集群中同样集群可 以使用kubectl logs类似的形式查看日志。 日志文件记录 这种日志输出我们无法从以上方法查看日志内容只能tail日志文件查看。 在k8s官方文档中对于以上两种形式的日志形式我们如果想收集并分析日志的话官方推荐以下两种对策 对于第一种文档中这样说 When a cluster is created, the standard output and standard error output of each container can be ingested using a Fluentd agent running on each node into either Google Cloud Logging o...
- 下一篇
Kubernetes(k8s)容器运行时(CRI)简介
Kubernetes节点的底层由一个叫做“容器运行时”的软件进行支撑,它 负责比如启停容器 这样的事情。最广为人知的容器运行时当属Docker,但它不是唯一的。事实上,容器运行时这个领域发展迅速。为了使Kubernetes的扩展变得更容易,我们一直在打磨支持容器运行时的K8s插件API:容器运行时接口(Container Runtime Interface, CRI)。 CRI是什么? 每种容器运行时各有所长,许多用户都希望Kubernetes支持更多的运行时。在Kubernetes 1.5发布版里,我们引入了CRI–一个能让kubelet无需编译就可以支持多种容器运行时的插件接口。CRI包含了一组protocol buffers,gRPC API,相关的库,以及在活跃开发下的额外规范和工具。CRI目前是Alpha版本。 支持可替换的容器运行时在Kubernetes中概念中并非首次。在1.3发布版里,我们介绍了rktnetes项目,它可以让rkt容器引擎作为Docker容器运行时的一个备选。然而,不管是Docker还是Rkt都需要通过内部、不太稳定的接口直接集成到kubelet的源码中...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Red5直播服务器,属于Java语言的直播服务器
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS6,CentOS7官方镜像安装Oracle11G
- Windows10,CentOS7,CentOS8安装Nodejs环境