写点什么

如何通过 Kubernetes 事件来报告错误

作者:Robert Lu
  • 2021 年 12 月 05 日
  • 本文字数:2967 字

    阅读完需:约 10 分钟

组内有维护一个 Kubernetes Webhook,可以拦截 pod 的创建请求,并做一些修改(比如添加环境变量、添加 init-container 等)。


业务逻辑本身很简单,但是如果过程中产生错误,就很难处理。要不直接阻止 pod 创建,那么就有可能导致应用无法启动。要么忽略业务逻辑,那么就会导致静默失败,谁也不知道这儿出现了一个错误。


于是,朴素的想法就是接入告警系统,但这会导致当前组件和具体的告警系统耦合起来。


在 Kubernetes 中,有 Event 机制,可以做到把一些事件,比如警告、错误等信息记录下来,就比较适合这个场景。

什么是 Kubernetes 中的事件/Event?

事件(Event)是 Kubernetes 中众多资源对象中的一员,通常用来记录集群内发生的状态变更,大到集群节点异常,小到 Pod 启动、调度成功等等。


比如我们 Describe 一个 pod,就能看到这个 pod 对应的事件:


kubectl describe pod sc-b-68867c5dcb-sf9hn



可以看到,从调度、到启动、再到这个 pod 最终拉取镜像失败,都会通过 event 的方式记录下来。


我们来看下一个 Event 的结构:


$ k get events -o json | jq .items[10]


{  "apiVersion": "v1",  "count": 1,  "eventTime": null,  "firstTimestamp": "2021-12-04T17:02:14Z",  "involvedObject": {    "apiVersion": "v1",    "fieldPath": "spec.containers{sc-b}",    "kind": "Pod",    "name": "sc-b-68867c5dcb-sf9hn",    "namespace": "default",    "resourceVersion": "322554830",    "uid": "24df4a07-f41e-42c2-ba26-d90940303b00"  },  "kind": "Event",  "lastTimestamp": "2021-12-04T17:02:14Z",  "message": "Error: ErrImagePull",  "metadata": {    "creationTimestamp": "2021-12-04T17:02:14Z",    "name": "sc-b-68867c5dcb-sf9hn.16bd9bf933d60437",    "namespace": "default",    "resourceVersion": "1197082",    "selfLink": "/api/v1/namespaces/default/events/sc-b-68867c5dcb-sf9hn.16bd9bf933d60437",    "uid": "f928ff2d-c618-44a6-bf5a-5b0d3d20e95e"  },  "reason": "Failed",  "reportingComponent": "",  "reportingInstance": "",  "source": {    "component": "kubelet",    "host": "eci"  },  "type": "Warning"}
复制代码


可以看到,一个 event,比较重要的几个字端:


  • type - 事件类型,可以是 Warning、Normal、Error 等

  • reason - 事件的原因,可以是 Failed、Scheduled、Started、Completed 等

  • message - 事件的描述信息

  • involvedObject - 这个事件对应的资源对象,可以是 Pod、Node 等

  • source - 这个事件的来源,可以是 kubelet、kube-apiserver 等

  • firstTimestamp,lastTimestamp - 这个事件的第一次和最后一次发生的时间


基于这些信息,我们就可以做一些集群级别的监控、告警了,比如阿里云的 ACK,就会将Event发送到SLS中,然后根据对应的规则来做告警。

如何上报事件

前面说了什么是 Kubernetes 中的 Event,但是我们必须要上报事件,才能让 Kubernetes 集群知道这个事件发生了,从而做出后续的监控和告警。

如何访问 Kubernetes API

上报事件的第一步是访问 Kubernetes API,这个 API 是基于 Restful API 的,Kubernetes 也基于这个 API,包装了 SDK,直接可以用。


通过 SDK 连接到 Kubernetes API,有两种方式


第一种是通过 kubeconfg 文件来访问(从外部访问),第二种是通过 serviceaccount 访问(从 Pod 访问)。


为了简单起见,我们使用第一种方式作为例子:


package main
import ( "flag" "fmt" "path/filepath"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir")
func main() { var kubeconfig *string if home := homedir.HomeDir(); home != "" { kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") } else { kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") } flag.Parse()
config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig) if err != nil { panic(err) } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic(err) } versionInfo, err := clientset.ServerVersion() if err != nil { panic(err) } fmt.Printf("Version: %#v\n", versionInfo)}
复制代码


运行这段代码,就可以连接到集群中,可以获取到 Kubernetes Server 版本了:


Version: &version.Info{Major:"1", Minor:"18+", GitVersion:"v1.18.8-aliyun.1", GitCommit:"27f24d2", GitTreeState:"", BuildDate:"2021-08-19T10:00:16Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}

如何创建、上报事件

在上面的例子中,有了 clientset 对象,我们现在就要依赖这个对象,在 Kuberentes 集群中创建一个事件:


now := time.Now()message := "test message at " + now.Format(time.RFC3339)// 命名空间为default_, err = clientset.CoreV1().Events("default").Create(&apiv1.Event{    ObjectMeta: metav1.ObjectMeta{        GenerateName: "test-",    },    Type:           "Warning",    Message:        message,    Reason:         "OnePilotFail",    FirstTimestamp: metav1.NewTime(now),    LastTimestamp:  metav1.NewTime(now),    InvolvedObject: apiv1.ObjectReference{        Namespace: "default",        Kind:      "Deployment",        Name:      "sc-b",    },})fmt.Printf("create event with err: %v\n", err)
复制代码


在上面的例子中,我们在命名空间 default 下创建了一个名为test-开头的 Event,这个 Event 的类型是 Warning。


我们也可以看下最终产生出来的 Event:


kubectl get events -o json | jq .items[353]


{  "apiVersion": "v1",  "eventTime": null,  "firstTimestamp": "2021-12-04T17:27:06Z",  "involvedObject": {    "kind": "Deployment",    "name": "sc-b",    "namespace": "default"  },  "kind": "Event",  "lastTimestamp": "2021-12-04T17:27:06Z",  "message": "test message at 2021-12-05T01:27:06+08:00",  "metadata": {    "creationTimestamp": "2021-12-04T17:27:06Z",    "generateName": "test-",    "name": "test-vvjzp",    "namespace": "default",    "resourceVersion": "1198057",    "selfLink": "/api/v1/namespaces/default/events/test-vvjzp",    "uid": "f2bcdd1c-442f-4f61-921a-e18637ee5871"  },  "reason": "OnePilotFail",  "reportingComponent": "",  "reportingInstance": "",  "source": {},  "type": "Warning"}
复制代码


这样,关心对应事件的人,比如运维人员的人,就可以依据这些信息做监控、告警了。

使用场景

和业务事件不同,Kubernetes 事件是集群中的资源,关注的人也多是集群的维护者。


所以这种事件上报机制,还是比较适合一些基础组件来使用,可以让集群维护者了解到当前集群的状态。


如果需要有更加灵活的告警、监控,那么可以使用更加贴近业务的、规则更加丰富的时间、告警系统。


---


本文首发于 https://robberphex.com/error-reporting-with-kubernetes-events/?utm_source=infoq&utm_medium=tail

发布于: 2021 年 12 月 05 日阅读数: 10
用户头像

Robert Lu

关注

还未添加个人签名 2015.04.06 加入

阿里云高级开发工程师

评论

发布
暂无评论
如何通过Kubernetes事件来报告错误