Kubernetes Events介绍(中)

简介: 上一回,历经千辛万苦终于破解了Events的姓名之谜,(Kubernetes(K8s)Events介绍(上))寻得Events真身。那么Events的身世究竟如何?根据Pod怎样才能找到对应的Events?本回将一一揭开谜底。

上一回,历经千辛万苦终于破解了Events的姓名之谜,(Kubernetes(K8s)Events介绍(上))寻得Events真身。那 么Events的身世究竟如何?根据Pod怎样才能找到对应的Events?本回将一一揭开谜底。

顺藤摸瓜

前面说到在DockerManager里定义了EventRecorder的成员,它的方法Event()、Eventf()、PastEventf()都可以用来构造Events实例,略有区别的地方是Eventf()调用了Sprintf()来输出Events message,PastEventf()可创建指定时间发生的Events。

一方面可以推测所有拥有EventsRecorder成员的Kubernetes资源定义都可以产生Events。经过暴力搜索发现,EventsRecorder主要被K8s的重要组件ControllerManager和Kubelet使用。比如,负责管理注册、注销等的NodeController,会将Node的状态变化信息记录为Events。DeploymentController会记录回滚、扩容等的Events。他们都在ControllerManager启动时被初始化并运行。与此同时Kubelet除了会记录它本身运行时的Events,比如:无法为Pod挂载卷、无法带宽整型等,还包含了一系列像docker_manager这样的小单元,它们各司其职,并记录相关Events。

另一方面在调查的时候发现,Events分为两类,并定义在kubernetes/pkg/api/types.go里,分别是EventTypeNormal和EventTypeWarning,它们分别表示该Events“仅表示信息,不会造成影响”和“可能有些地方不太对”。

在types.go里,还找到了Event数据结构的定义:

type Event struct {
 unversioned.TypeMeta `json:",inline"` ObjectMeta `json:"metadata,omitempty"` // Required. The object that this event is about. InvolvedObject ObjectReference `json:"involvedObject,omitempty"` Reason string `json:"reason,omitempty"` Message string `json:"message,omitempty"` Source EventSource `json:"source,omitempty"` FirstTimestamp unversioned.Time `json:"firstTimestamp,omitempty"` LastTimestamp unversioned.Time `json:"lastTimestamp,omitempty"` Count int32 `json:"count,omitempty"` Type string `json:"type,omitempty"` }

除了标准的Kubernetes资源必备的unversioned.TypeMeta和ObjectMeta成员外,Event结构体还包含了Events相关的对象、原因、内容、消息源、首次记录时间、最近记录时间、记录统计和类型。

另外还定义了EventsList的结构类型,这就是我们使用kubectl get events和GET /api/v1/namespaces/{namespace}/events获取Events列表时K8s使用的数据结构。

在Events的定义里,比较重要的有两个成员,一个是InvolvedObject, 另一个是Source。

首先,InvolvedObject表示的是这个Events所属的资源。它的类型是ObjectReference,定义如下:

type ObjectReference struct { Kind string `json:"kind,omitempty"` Namespace string `json:"namespace,omitempty"` Name string `json:"name,omitempty"` UID types.UID `json:"uid,omitempty"` APIVersion string `json:"apiVersion,omitempty"` ResourceVersion string `json:"resourceVersion,omitempty"` FieldPath string `json:"fieldPath,omitempty"` }

ObjectReference里包含的信息足够我们唯一确定该资源实例。

然后,Source表示的是该Events的来源,它的类型是EventSource,定义如下:

type EventSource struct { // Component from which the event is generated. Component string `json:"component,omitempty"` // Host name on which the event is generated. Host string `json:"host,omitempty"` }

来龙无去脉

前面的研究已经为我们大致画出了Events的内部轮廓。回到开始时的问题: 既然Events的名字跟发生它的Pod的名字不同,那么kubectl describe pod时如何找到对应的Events的?我们可以大胆推测,正是通过Events定义里的InvolvedObject成员来锁定了它们之间的关系。

在前面分析kubeadm原理的文章中已经介绍过Kubernetes的命令都是利用第三方包Cobra生成的,kubectl describe也不例外,它定义在kubernetes/pkg/kubectl/cmd/describe.go里。

Kubernetes中只有部分资源可以被describe,可以称为“DescribableResources”,通过一个资源类型(unversioned.GroupKind)和对应描述器(Describer)的Map映射相关联。这些资源有我们常见的Pod、ReplicationController、Service、Node、Deloyment、ReplicaSet等等。注意这些资源里并不包含Events。

显而易见,我们需要去仔细看看Pod的Describer做了什么。PodDescriber只有一个方法,那就是Describe(),实现在kubernetes/pkg/kubectl/describe.go里。

Describe()方法首先通过namespace和name唯一确定所请求的Pod。如果出错并且ShowEvents标识为true的情况下,会根据FieldSelector找到Events,并说明“获取Pod出错,但发现了Events”。如果请求Pod未出错且ShowEvents标识为true,则通过GetReference()方法找到相关的Events。

不管哪种方式,只要找到的Events不为空,总是会通过DescribeEvents()方法将Events列表按特定格式输出。即下图:

20161226093445

这么一说就明白了,原来我们在kubectl describe pod时得到的返回的结果不仅包含了Pod的信息,还有Events的信息,它们来自的是不同的处理过程。

到此我们已经摸清了Events的来龙。具体来说对于describe pod时看到的Events,它是由Kubelet的DockerManager生成,在执行kubectl命令时通过PodDescriber进行采集。显然如果我们不执行kubectl命令的时候这些Events仍然是存在的,那么这个时候这些Events会流向何处?也就说,我们还没捋顺Events的去脉。

Kubelet在启动的时候会初始化一个EventRecorder,这个EventRecorder又被交于Kubelet上每个小manager使用,比如DockerManager。它将产生的Events的Source成员进行初始化:Componets为“kubelet”,Host为该节点的名字。

狡兔三窟

在追寻Events的去脉前,我们先来看看PodDescriber是如何采集这些Events的。

前面简单描述了PodDescriber的Describe()方法的作用,如果不够明朗,下面贴出它的源码:

func (d *PodDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {  pod, err := d.Pods(namespace).Get(name) if err != nil { // 获取Pod失败时 if describerSettings.ShowEvents { eventsInterface := d.Events(namespace)  selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil)  options := api.ListOptions{FieldSelector: selector} events, err2 := eventsInterface.List(options)  if describerSettings.ShowEvents && err2 == nil && len(events.Items) > 0 {  return tabbedString(func(out io.Writer) error {
 fmt.Fprintf(out, "Pod '%v': error '%v', but found events.\n", name, err) DescribeEvents(events, out) return nil }) } } return "", err
 } var events *api.EventList // 获取Pod成功 if describerSettings.ShowEvents { if ref, err := api.GetReference(pod); err != nil {
 glog.Errorf("Unable to construct reference to '%#v': %v", pod, err) } else { ref.Kind = "" // 通过Events().Search()获取
  1. 获取Pod失败时
    Events的GetFieldSelector()方法同时根据InvolvedObject的名称、命名空间、资源类型和UID生成一个FieldSelector。使用它作为ListOptions,可以选中满足这个Selector对应的资源。如果选中的Events不为空,说明“获取Pod出错,但发现了Events”的情况,并将其按照特定的格式打印。
  2. 获取Pod成功,GetReference()失败
    GetReference()根据传入的K8s资源实例,构造它的引用说明。如果执行失败,记录失败日志,并直接执行describePod(),将目前获取的结果输出到屏幕上。
  3. 获取Pod成功,GetReference()成功
    GetReference()成功后,调用Events的Search()方法,寻找关于该Pod的所有Events。最终执行describePod(),并将目前获取的结果输出到屏幕上。

当然,即使是Events的Search()方法,内部执行的仍是先GetFieldSelector()再Events List()的过程。这是因为DockerManager在生成Event的时候会调用它的makeEvent()方法(代码在上篇引用过,这里不再赘述),将Pod关联到该Events的InvolvedObject上。GetFieldSelector()返回的是一个field.Selector接口实例,它定义在kubernetes/pkg/fields/selector.go里,通过它的Matches()方法可以选中含有该Field且对应值相同的Events。

Kubernetes里,FieldSelector和LabelSelector的设计异曲同工,不同的是Field匹配的是该资源的域,比如Name、Namespace,而Label匹配的是Labels域里的键值对。

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

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
7月前
|
Kubernetes 监控 物联网
IoT 边缘集群基于 Kubernetes Events 的告警通知实现(二):进一步配置
IoT 边缘集群基于 Kubernetes Events 的告警通知实现(二):进一步配置
|
7月前
|
存储 Kubernetes 监控
IoT 边缘集群基于 Kubernetes Events 的告警通知实现
IoT 边缘集群基于 Kubernetes Events 的告警通知实现
|
JSON Kubernetes 数据格式
Kubernetes(K8s)Events介绍(上)
Kubernetes Events虽不常被提起,却意义非凡。它存储在Etcd里,记录了集群运行所遇到的各种大事件。本系列文章将一步一步地揭开Kubernetes Events的神秘面纱。 师出有名 前些天群里有位同学(@二东)提问说怎么通过API得到kubectl describe pod的结果,我立刻找到了Kubernetes相关的API并回复他,但他说这不是他要的东西。
2719 0
|
Kubernetes 容器 Perl
Kubernetes Events介绍(下)
经过前两回的“踏血寻妖”,一个完整的Events原形逐渐浮出水面。我们已经摸清了它的由来和身世,本回将一起探索Events的去向,这是一个终点却也是另一个起点。 蜜汁去向 前面已经了解到,Event是由一个叫EventRecorder的东西幻化而生。
1562 0
|
12天前
|
Kubernetes 监控 Cloud Native
Kubernetes集群的高可用性与伸缩性实践
Kubernetes集群的高可用性与伸缩性实践
44 1
|
2月前
|
JSON Kubernetes 容灾
ACK One应用分发上线:高效管理多集群应用
ACK One应用分发上线,主要介绍了新能力的使用场景
|
2月前
|
Kubernetes 持续交付 开发工具
ACK One GitOps:ApplicationSet UI简化多集群GitOps应用管理
ACK One GitOps新发布了多集群应用控制台,支持管理Argo CD ApplicationSet,提升大规模应用和集群的多集群GitOps应用分发管理体验。
|
2月前
|
Kubernetes Cloud Native 云计算
云原生之旅:Kubernetes 集群的搭建与实践
【8月更文挑战第67天】在云原生技术日益成为IT行业焦点的今天,掌握Kubernetes已成为每个软件工程师必备的技能。本文将通过浅显易懂的语言和实际代码示例,引导你从零开始搭建一个Kubernetes集群,并探索其核心概念。无论你是初学者还是希望巩固知识的开发者,这篇文章都将为你打开一扇通往云原生世界的大门。
127 17
|
2月前
|
Kubernetes 应用服务中间件 nginx
搭建Kubernetes v1.31.1服务器集群,采用Calico网络技术
在阿里云服务器上部署k8s集群,一、3台k8s服务器,1个Master节点,2个工作节点,采用Calico网络技术。二、部署nginx服务到k8s集群,并验证nginx服务运行状态。
634 1
|
2月前
|
Kubernetes Cloud Native 流计算
Flink-12 Flink Java 3分钟上手 Kubernetes云原生下的Flink集群 Rancher Stateful Set yaml详细 扩容缩容部署 Docker容器编排
Flink-12 Flink Java 3分钟上手 Kubernetes云原生下的Flink集群 Rancher Stateful Set yaml详细 扩容缩容部署 Docker容器编排
76 3