大家好,我是張晉濤。
之前我寫了一篇《更優雅的 Kubernetes 叢集事件度量方案》,利用 Jaeger 利用 tracing 的方式來採集 Kubernetes 叢集中的 events 並進行展示。最終效果如下:
寫那篇文章的時候,立了個 flag 要詳細介紹下其中的原理,鴿了很久,現在年底了,也該發出來了。
Eents 概覽
我們先來做個簡單的示例,來看看 Kubernetes 叢集中的 events 是什麼。
建立一個新的名叫 moelove 的 namespace ,然後在其中建立一個叫做 redis 的 deployment。接下來檢視這個 namespace 中的所有 events。
(MoeLove) ➜ kubectl create ns moelove
namespace/moelove created
(MoeLove) ➜ kubectl -n moelove create deployment redis --image=ghcr.io/moelove/redis:alpine
deployment.apps/redis created
(MoeLove) ➜ kubectl -n moelove get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
redis 1/1 1 1 11s
(MoeLove) ➜ kubectl -n moelove get events
LAST SEEN TYPE REASON OBJECT MESSAGE
21s Normal Scheduled pod/redis-687967dbc5-27vmr Successfully assigned moelove/redis-687967dbc5-27vmr to kind-worker3
21s Normal Pulling pod/redis-687967dbc5-27vmr Pulling image "ghcr.io/moelove/redis:alpine"
15s Normal Pulled pod/redis-687967dbc5-27vmr Successfully pulled image "ghcr.io/moelove/redis:alpine" in 6.814310968s
14s Normal Created pod/redis-687967dbc5-27vmr Created container redis
14s Normal Started pod/redis-687967dbc5-27vmr Started container redis
22s Normal SuccessfulCreate replicaset/redis-687967dbc5 Created pod: redis-687967dbc5-27vmr
22s Normal ScalingReplicaSet deployment/redis Scaled up replica set redis-687967dbc5 to 1
但是我們會發現預設情況下 kubectl get events
並沒有按照 events 發生的順序進行排列,所以我們往往需要為其增加 --sort-by='{.metadata.creationTimestamp}'
引數來讓其輸出可以按時間進行排列。
這也是為何 Kubernetes v1.23 版本中會新增 kubectl alpha events
命令的原因。我在之前的文章《K8S 生態週報| Kubernetes v1.23.0 正式釋出,新特性一覽》中已進行了詳細的介紹,這裡就不展開了。
按時間排序後可以看到如下結果:
(MoeLove) ➜ kubectl -n moelove get events --sort-by='{.metadata.creationTimestamp}'
LAST SEEN TYPE REASON OBJECT MESSAGE
2m12s Normal Scheduled pod/redis-687967dbc5-27vmr Successfully assigned moelove/redis-687967dbc5-27vmr to kind-worker3
2m13s Normal SuccessfulCreate replicaset/redis-687967dbc5 Created pod: redis-687967dbc5-27vmr
2m13s Normal ScalingReplicaSet deployment/redis Scaled up replica set redis-687967dbc5 to 1
2m12s Normal Pulling pod/redis-687967dbc5-27vmr Pulling image "ghcr.io/moelove/redis:alpine"
2m6s Normal Pulled pod/redis-687967dbc5-27vmr Successfully pulled image "ghcr.io/moelove/redis:alpine" in 6.814310968s
2m5s Normal Created pod/redis-687967dbc5-27vmr Created container redis
2m5s Normal Started pod/redis-687967dbc5-27vmr Started container redis
通過以上的操作,我們可以發現 events 實際上是 Kubernetes 叢集中的一種資源。當 Kubernetes 叢集中資源狀態發生變化時,可以產生新的 events。
深入 Events
單個 Event 物件
既然 events 是 Kubernetes 叢集中的一種資源,正常情況下它的 metadata.name 中應該包含其名稱,用於進行單獨操作。所以我們可以使用如下命令輸出其 name :
(MoeLove) ➜ kubectl -n moelove get events --sort-by='{.metadata.creationTimestamp}' -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
redis-687967dbc5-27vmr.16c4fb7bde8c69d2
redis-687967dbc5.16c4fb7bde6b54c4
redis.16c4fb7bde1bf769
redis-687967dbc5-27vmr.16c4fb7bf8a0ab35
redis-687967dbc5-27vmr.16c4fb7d8ecaeff8
redis-687967dbc5-27vmr.16c4fb7d99709da9
redis-687967dbc5-27vmr.16c4fb7d9be30c06
選擇其中的任意一條 event 記錄,將其輸出為 YAML 格式進行檢視:
(MoeLove) ➜ kubectl -n moelove get events redis-687967dbc5-27vmr.16c4fb7bde8c69d2 -o yaml
action: Binding
apiVersion: v1
eventTime: "2021-12-28T19:31:13.702987Z"
firstTimestamp: null
involvedObject:
apiVersion: v1
kind: Pod
name: redis-687967dbc5-27vmr
namespace: moelove
resourceVersion: "330230"
uid: 71b97182-5593-47b2-88cc-b3f59618c7aa
kind: Event
lastTimestamp: null
message: Successfully assigned moelove/redis-687967dbc5-27vmr to kind-worker3
metadata:
creationTimestamp: "2021-12-28T19:31:13Z"
name: redis-687967dbc5-27vmr.16c4fb7bde8c69d2
namespace: moelove
resourceVersion: "330235"
uid: e5c03126-33b9-4559-9585-5e82adcd96b0
reason: Scheduled
reportingComponent: default-scheduler
reportingInstance: default-scheduler-kind-control-plane
source: {}
type: Normal
可以看到其中包含了很多資訊, 這裡我們先不展開。我們看另一個例子。
kubectl describe
中的 Events
我們可以分別對 Deployment 物件和 Pod 物件執行 describe
的操作,可以得到如下結果(省略掉了中間輸出):
- 對 Deployment 操作
(MoeLove) ➜ kubectl -n moelove describe deploy/redis
Name: redis
Namespace: moelove
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 15m deployment-controller Scaled up replica set redis-687967dbc5 to 1
- 對 Pod 操作
(MoeLove) ➜ kubectl -n moelove describe pods redis-687967dbc5-27vmr
Name: redis-687967dbc5-27vmr
Namespace: moelove
Priority: 0
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 18m default-scheduler Successfully assigned moelove/redis-687967dbc5-27vmr to kind-worker3
Normal Pulling 18m kubelet Pulling image "ghcr.io/moelove/redis:alpine"
Normal Pulled 17m kubelet Successfully pulled image "ghcr.io/moelove/redis:alpine" in 6.814310968s
Normal Created 17m kubelet Created container redis
Normal Started 17m kubelet Started container redis
我們可以發現 對不同的資源物件進行 describe 的時候,能看到的 events 內容都是與自己有直接關聯的。在 describe Deployment 的時候,看不到 Pod 相關的 Events 。
這說明, Event 物件中是包含它所描述的資源物件的資訊的,它們是有直接聯絡的。
結合前面我們看到的單個 Event 物件,我們發現 involvedObject
欄位中內容就是與該 Event 相關聯的資源物件的資訊。
更進一步瞭解 Events
我們來看看如下的示例,建立一個 Deployment ,但是使用一個不存在的映象:
(MoeLove) ➜ kubectl -n moelove create deployment non-exist --image=ghcr.io/moelove/non-exist
deployment.apps/non-exist created
(MoeLove) ➜ kubectl -n moelove get pods
NAME READY STATUS RESTARTS AGE
non-exist-d9ddbdd84-tnrhd 0/1 ErrImagePull 0 11s
redis-687967dbc5-27vmr 1/1 Running 0 26m
我們可以看到當前的 Pod 處於一個 ErrImagePull
的狀態。檢視當前 namespace 中的 events (我省略掉了之前 deploy/redis 的記錄)
(MoeLove) ➜ kubectl -n moelove get events --sort-by='{.metadata.creationTimestamp}'
LAST SEEN TYPE REASON OBJECT MESSAGE
35s Normal SuccessfulCreate replicaset/non-exist-d9ddbdd84 Created pod: non-exist-d9ddbdd84-tnrhd
35s Normal ScalingReplicaSet deployment/non-exist Scaled up replica set non-exist-d9ddbdd84 to 1
35s Normal Scheduled pod/non-exist-d9ddbdd84-tnrhd Successfully assigned moelove/non-exist-d9ddbdd84-tnrhd to kind-worker3
17s Warning Failed pod/non-exist-d9ddbdd84-tnrhd Error: ErrImagePull
17s Warning Failed pod/non-exist-d9ddbdd84-tnrhd Failed to pull image "ghcr.io/moelove/non-exist": rpc error: code = Unknown desc = failed to pull and unpack image "ghcr.io/moelove/non-exist:latest": failed to resolve reference "ghcr.io/moelove/non-exist:latest": failed to authorize: failed to fetch anonymous token: unexpected status: 403 Forbidden
18s Normal Pulling pod/non-exist-d9ddbdd84-tnrhd Pulling image "ghcr.io/moelove/non-exist"
4s Warning Failed pod/non-exist-d9ddbdd84-tnrhd Error: ImagePullBackOff
4s Normal BackOff pod/non-exist-d9ddbdd84-tnrhd Back-off pulling image "ghcr.io/moelove/non-exist"
對這個 Pod 執行 describe
操作:
(MoeLove) ➜ kubectl -n moelove describe pods non-exist-d9ddbdd84-tnrhd
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 4m default-scheduler Successfully assigned moelove/non-exist-d9ddbdd84-tnrhd to kind-worker3
Normal Pulling 2m22s (x4 over 3m59s) kubelet Pulling image "ghcr.io/moelove/non-exist"
Warning Failed 2m21s (x4 over 3m59s) kubelet Failed to pull image "ghcr.io/moelove/non-exist": rpc error: code = Unknown desc = failed to pull and unpack image "ghcr.io/moelove/non-exist:latest": failed to resolve reference "ghcr.io/moelove/non-exist:latest": failed to authorize: failed to fetch anonymous token: unexpected status: 403 Forbidden
Warning Failed 2m21s (x4 over 3m59s) kubelet Error: ErrImagePull
Warning Failed 2m9s (x6 over 3m58s) kubelet Error: ImagePullBackOff
Normal BackOff 115s (x7 over 3m58s) kubelet Back-off pulling image "ghcr.io/moelove/non-exist"
我們可以發現,這裡的輸出和之前正確執行 Pod 的不一樣。最主要的區別在與 Age
列。這裡我們看到了類似 115s (x7 over 3m58s)
這樣的輸出。
它的含義表示: 該型別的 event 在 3m58s 中已經發生了 7 次,最近的一次發生在 115s 之前
但是當我們去直接 kubectl get events
的時候,我們並沒有看到有 7 次重複的 event 。這說明 Kubernetes 會自動將重複的 events 進行合併。
選擇最後一條 Events (方法前面內容已經講了) 並將其內容使用 YAML 格式進行輸出:
(MoeLove) ➜ kubectl -n moelove get events non-exist-d9ddbdd84-tnrhd.16c4fce570cfba46 -o yaml
apiVersion: v1
count: 43
eventTime: null
firstTimestamp: "2021-12-28T19:57:06Z"
involvedObject:
apiVersion: v1
fieldPath: spec.containers{non-exist}
kind: Pod
name: non-exist-d9ddbdd84-tnrhd
namespace: moelove
resourceVersion: "333366"
uid: 33045163-146e-4282-b559-fec19a189a10
kind: Event
lastTimestamp: "2021-12-28T18:07:14Z"
message: Back-off pulling image "ghcr.io/moelove/non-exist"
metadata:
creationTimestamp: "2021-12-28T19:57:06Z"
name: non-exist-d9ddbdd84-tnrhd.16c4fce570cfba46
namespace: moelove
resourceVersion: "334638"
uid: 60708be0-23b9-481b-a290-dd208fed6d47
reason: BackOff
reportingComponent: ""
reportingInstance: ""
source:
component: kubelet
host: kind-worker3
type: Normal
這裡我們可以看到其欄位中包括一個 count
欄位,表示同類 event 發生了多少次。以及 firstTimestamp
和 lastTimestamp
分別表示了這個 event 首次出現了最近一次出現的時間。這樣也就解釋了前面的輸出中 events 持續的週期了。
徹底搞懂 Events
以下內容是從 Events 中隨便選擇的一條,我們可以看到它包含的一些欄位資訊:
apiVersion: v1
count: 1
eventTime: null
firstTimestamp: "2021-12-28T19:31:13Z"
involvedObject:
apiVersion: apps/v1
kind: ReplicaSet
name: redis-687967dbc5
namespace: moelove
resourceVersion: "330227"
uid: 11e98a9d-9062-4ccb-92cb-f51cc74d4c1d
kind: Event
lastTimestamp: "2021-12-28T19:31:13Z"
message: 'Created pod: redis-687967dbc5-27vmr'
metadata:
creationTimestamp: "2021-12-28T19:31:13Z"
name: redis-687967dbc5.16c4fb7bde6b54c4
namespace: moelove
resourceVersion: "330231"
uid: 8e37ec1e-b3a1-420c-96d4-3b3b2995c300
reason: SuccessfulCreate
reportingComponent: ""
reportingInstance: ""
source:
component: replicaset-controller
type: Normal
其中主要欄位的含義如下:
- count: 表示當前同類的事件發生了多少次 (前面已經介紹)
- involvedObject: 與此 event 有直接關聯的資源物件 (前面已經介紹) , 結構如下:
type ObjectReference struct {
Kind string
Namespace string
Name string
UID types.UID
APIVersion string
ResourceVersion string
FieldPath string
}
- source: 直接關聯的元件, 結構如下:
type EventSource struct {
Component string
Host string
}
- reason: 簡單的總結(或者一個固定的程式碼),比較適合用於做篩選條件,主要是為了讓機器可讀,當前有超過 50 種這樣的程式碼;
- message: 給一個更易讓人讀懂的詳細說明
- type: 當前只有
Normal
和Warning
兩種型別, 原始碼中也分別寫了其含義:
// staging/src/k8s.io/api/core/v1/types.go
const (
// Information only and will not cause any problems
EventTypeNormal string = "Normal"
// These events are to warn that something might go wrong
EventTypeWarning string = "Warning"
)
所以,當我們將這些 Events 都作為 tracing 的 span 採集回來後,就可以按照其 source
進行分類,按 involvedObject
進行關聯,按時間進行排序了。
總結
在這篇文章中,我主要通過兩個示例,一個正確部署的 Deploy,以及一個使用不存在映象部署的 Deploy,深入的介紹了 Events 物件的實際的作用及其各個欄位的含義。
對於 Kubernetes 而言,Events 中包含了很多有用的資訊,但是這些資訊卻並不會對 Kubernetes 造成什麼影響,它們也並不是實際的 Kubernetes 的日誌。預設情況下 Kubernetes 中的日誌在 1 小時後就會被清理掉,以便釋放對 etcd 的資源佔用。
所以為了能更好的讓叢集管理員知道發生了什麼,在生產環境中,我們通常會把 Kubernetes 叢集的 events 也給採集回來。我個人比較推薦的工具是: https://github.com/opsgenie/k...
當然你也可以按照我之前的文章 《更優雅的 Kubernetes 叢集事件度量方案》,利用 Jaeger 利用 tracing 的方式來採集 Kubernetes 叢集中的 events 並進行展示。
歡迎訂閱我的文章公眾號【MoeLove】