本文首发于 https://robberphex.com/error-reporting-with-kubernetes-events/

组内有保护一个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 mainimport (    "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事件是集群中的资源,关注的人也多是集群的维护者。

所以这种事件上报机制,还是比拟适宜一些根底组件来应用,能够让集群维护者理解到以后集群的状态。

如果须要有更加灵便的告警、监控,那么能够应用更加贴近业务的、规定更加丰盛的工夫、告警零碎。