本文首发于 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 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 事件是集群中的资源,关注的人也多是集群的维护者。
所以这种事件上报机制,还是比拟适宜一些根底组件来应用,能够让集群维护者理解到以后集群的状态。
如果须要有更加灵便的告警、监控,那么能够应用更加贴近业务的、规定更加丰盛的工夫、告警零碎。