エムスリー Advent Calendar 2021 4日目!
AI・機械学習チームの横本 @yokomotod です。
今日は弊チームでかれこれ2年くらい使っているk8sのevents監視くんや、最近 今日そこに機能追加されたOOM Podの自動通知などを話したいと思います。
k8s eventsログ
Kubernetesは events
というリソースを持っており、GKEではCloud Loggingsで
logName="projects/MY_PROJECT_NAME/logs/events"
とフィルタすることで全イベントを見ることが出来ます。
Podのschedule, Imageのpull、ContainerのStartといったライフサイクルや、NodeのScaleIn/ScaleOutなどk8s内の動きをいろいろと見ることができます。
GKEではmaster nodeのログなどを直接見ることは出来ませんが、eventsログが代わりの有益なヒントにもなります。
events監視
AI・機械学習チームでは各種バッチ、APIがCronJobやDeploymentとしてGKE上で動いています。
このうちAPIについてはレスポンスのステータスコードや応答レイテンシでの監視、CronJobについては所定の時間内に完了したかどうかのSLOでの監視をしています。
(SLO監視についてはこちらの記事で紹介しています)
これらにより、最終防衛ラインとして「最低限サービスが稼働していること」の監視は出来ていますが
- OutOfMemoryでContainerがrestartされた
- PodがEvictされた
- docker imageの指定が間違っていてDeploymentが新しいバージョンにrolling updateされていなかった
など内部的な異常には気づくことができません。
そのため、eventsログの監視も併せて行っています。
Logs RouterでeventsログをPub/Subに飛ばし、それをトリガにして自作のCloud Functionsで通知するという構成で
logName="projects/MY_PROJECT_NAME/logs/events" (jsonPayload.reason="Evicted" OR jsonPayload.reason="BackoffLimitExceeded" OR (jsonPayload.reason="Failed" AND NOT (jsonPayload.message:"Error: cannot find volume" OR jsonPayload.message:"\" not registered")) OR (jsonPayload.reason="ScaleDown" AND resource.type="k8s_pod" AND NOT jsonPayload.involvedObject.namespace="kube-system") OR (jsonPayload.reason="OOMKilling" AND NOT ("k8s_metadata" OR "google-fluentd" OR "otelsvc")))
というLogs Routerのフィルタ条件でアラートを通知しています。
フィルタの内容は
reason:BackoffLimitExceeded
- = CronJobが
backoffLimit
まで失敗・リトライしきった
- = CronJobが
reason:OOMKilling
- = OutOfMemory
- たまにkube-system系のPodがOOMすることがあり、対応が難しく実影響がないため泣く泣く除外
reason:Evicted
- = schedulerによって別NodeにPodが移動させられた
reason:ScaleDown AND type:k8s_pod
- = NodeのScaleDownによってPodが移動させられた
reason:Failed
- 名前の汎用性のとおり、多岐にわたるタイミングで発生
- 例:イメージのpull失敗
cannot find volume
などは正常であってもPod起動時に発生することがあるようで、泣く泣く除外
フィルタ条件の秘伝のタレ感がとても香ばしいのがネックですが、特に BackoffLimitExceeded
や OOMKilling
はそれなりに発生することがあり通知が役立っています
皆さんも是非オリジナルのアレンジを加えたタレを作ってみてください
フィルタに該当するイベントが発生するとこのように通知が送られます。
OOM Killed
されたPodは誰だ
特に便利と言ったばかりな OOMKilling
イベント通知ですが、画像のとおりPodなどの情報がなく、どのPod(正確にはContainer)がkillされたのかわかりません。
どうやら OOMKilling
イベントはPodではなくNodeのレベルで送られているイベント(kind:node
)で、OOMしたプロセスIDやバイナリ名の情報しか持っていないようです。
このあたりの改善は公式のIssueも建っていますが長らく解決されていない状況です。
そこで上記Issueが改善するまでの対処として、 OOMKilling
を検知した際はCloudFunction上でpod一覧を取得し、
Status.ContainerStatuses[].State.Terminated.Reason == "OOMKilled"
なPodの一覧を通知する追加処理を行っています。
(厳密にeventとpodの紐付けを行っているわけではないので、短期間に複数のpodがOOMすると混乱するケースがあるかもしれません。そんなOOM多発が無いことを祈っています…)
ソース
Cloud Function部分のコードをこちらに公開しました。
GKE events log monitoring (Cloud Function) · GitHub
200行くらいの1ファイルのGoです。
おわり
以上、k8s/GKEのevnetsログと、それを用いた監視、また特にOOM発生時のPod情報の取得についてでした。
エムスリー Advent Calendar 2021 明日もどうぞお楽しみに!
We are hiring!!
spec.resources.requests.nakama: "たくさん"
!!