Slide 1

Slide 1 text

©MIXI 突然のメモリ使用率上昇へ対応! k8sカスタムコントローラー開発事 例 株式会社MIXI 井上 翔太

Slide 2

Slide 2 text

©MIXI 自己紹介 名前:しょっさん
 X(旧: Twitter)/ mixi2:@syossan27
 所属:MIXI
 活動:
 ● SRE Kaigi ● SRE Magazine ● ゆるSRE勉強会

Slide 3

Slide 3 text

©MIXI Fanstaのご紹介 • スポーツ観戦ができる飲食店に特化した検索サービス • スポーツ観戦できる飲食店をエリアやチーム、放映予定から検索し、予約できる • お店にとってはスポーツ観戦ができることを告知し、集客することができる • [New] 事前決済ができるようになりました! ©Fansta

Slide 4

Slide 4 text

©MIXI ある日のこと・・・

Slide 5

Slide 5 text

©MIXI ある日突然・・・ まぁもし、なにかが起こっても爆速で対処しますよ! 最近は昔に比べると、特にエラーも起こらず平和ですなぁ〜

Slide 6

Slide 6 text

©MIXI ある日突然・・・ まぁもし、なにかが起こっても爆速で対処しますよ! ハッハッハッ! 最近は昔に比べると、特にエラーも起こらず平和ですなぁ〜

Slide 7

Slide 7 text

©MIXI ある日突然・・・ まぁもし、なにかが起こっても爆速で対処しますよ! ハッハッハッ! 最近は昔に比べると、特にエラーも起こらず平和ですなぁ〜 やばい!

Slide 8

Slide 8 text

©MIXI 何が起こったのか? 起因は一部のGraphQLクエリの修正 ● 修正したGraphQLクエリでは非常に大きいレスポンスを返すように なってしまっていた ● 結果、メモリ使用量が急速に肥大化 ● メモリがサチることでレイテンシの悪化につながった ● k8sを利用していたため、HPAでメモリ使用率がある一定を超えたら スケールするようにしていたが、スケール数も上限に達していた

Slide 9

Slide 9 text

©MIXI すぐに直したいが・・・

Slide 10

Slide 10 text

©MIXI ある日突然・・・ まぁもし、なにかが起こっても爆速で対処しますよ! 最近は昔に比べると、特にエラーも起こらず平和ですなぁ〜

Slide 11

Slide 11 text

©MIXI なんとかするっきゃねぇ!

Slide 12

Slide 12 text

©MIXI k8sカスタムコントローラー、作りました pre-oom-killer ※ GitHub - syossan27/pre-oom-killer : https://github.com/syossan27/pre-oom-killer

Slide 13

Slide 13 text

©MIXI 前提:k8sカスタムコントローラーとは? ● k8sでは様々なリソースを管理するコントローラーが動いています ○ Node ○ Deployment ○ ReplicaSet ○ CronJob ○ etc… ※ つくって学ぶkubebuilder - カスタムコントローラーの基礎 : https://zoetrope.github.io/kubebuilder-training/introduction/basics.html

Slide 14

Slide 14 text

©MIXI 前提:k8sカスタムコントローラーとは? ● このコントローラーを自作することができる! ● コントローラーを自作することで、独自のロジックをk8s内に持ち込むこと ができます ● また、独自リソース(CR)を定義し、それを利用するためにカスタムコン トローラーを作成することもできます ● 有名なカスタムコントローラーだと ○ cert-manager ○ external-secrets ○ などなど ● とはいえ、きちんとk8sの内部処理を理解していかないといけないので、ち とハードルは高め

Slide 15

Slide 15 text

©MIXI カスタムコントローラーを作ってみよう やりたいこと:指定したメモリ使用率に達したらPodをevictする ● evict(立ち退き)とは、Podに対して終了を要請すること ● 当初調べた時に、 zapier/preoomkiller-controller というカスタムコン トローラーもあったが、こちらはメモリ使用量を指定するタイプで取 り回しが悪かった ● やりたいことはほぼpreoomkiller-controllerが実現していたので、こ ちらを参考にする形で実装する

Slide 16

Slide 16 text

©MIXI カスタムコントローラーを作ってみよう - 処理イメージ

Slide 17

Slide 17 text

©MIXI カスタムコントローラーを作ってみよう - クライアント設定

Slide 18

Slide 18 text

©MIXI カスタムコントローラーを作ってみよう client-goを使い、k8sクライアントを作成 ● kubernetes/client-go を利用して、 k8sクライアントを作成 ● シグナルハンドラー、k8s APIやmetrics API へ接続する設定を渡して作成します ● 最後に、コントローラー構造体にそれらを 入れ、監視ループを走らせます config, err := clientcmd.BuildConfigFromFlags(masterUrl, kubeconfig) if err != nil { log.Fatal(err) } clientset, err := kubernetes.NewForConfig(config) if err != nil { log.Fatal(err) } metricsClientset, err := metrics.NewForConfig(config) if err != nil { log.Fatal(err) } ctx := SetupSignalHandler() controller := NewController(ctx, clientset, metricsClientset, time.Duration(interval)*time.Second) controller.Run()

Slide 19

Slide 19 text

©MIXI カスタムコントローラーを作ってみよう - Pod検出

Slide 20

Slide 20 text

©MIXI カスタムコントローラーを作ってみよう 監視ループを回し、対象Podを検出 ● 対象となるannotationが付与されたPodを 検出します ● annotationはdeploymentに以下のように設 定する ○ pre-oom-killer.v1alpha1.k8s.io/target-container-name: "app" ● ここで検出したPodで更に処理を行う podList, err := c.clientset.CoreV1().Pods("").List( c.context, metav1.ListOptions{ LabelSelector: PodLabelSelector, }, ) for _, pod := range podList.Items { podName, podNamespace := pod.ObjectMeta.Name, pod.ObjectMeta.Namespace podTargetContainer, ok := pod.ObjectMeta.Annotations[TargetContainerName] if !ok { log.WithFields(log.Fields{ "pod": podName, "namespace": podNamespace, }).Errorf("PodTargetContainerNameFetchError: %s", err) continue } // 後続処理は省略 }

Slide 21

Slide 21 text

©MIXI カスタムコントローラーを作ってみよう - Pod検出

Slide 22

Slide 22 text

©MIXI カスタムコントローラーを作ってみよう - メモリ使用率チェック メモリ使用率をチェックし、超過でevict ● 対象となるPodで設定されているevictにす るメモリ使用率を取得する ● メモリの最大値・使用量を取得する podMemoryUsageThreshold, err := resource.ParseQuantity(pod.ObjectMeta.Annotations[MemoryUsa geThresholdAnnotation]) // メモリの最大値を取得 var containerLimitsMemory *resource.Quantity for _, container := range pod.Spec.Containers { containerLimitsMemory = container.Resources.Limits.Memory() } // メモリの使用量を取得 containerMemoryUsage := &resource.Quantity{} podMetrics, err := c.metricsClientset.MetricsV1beta1().PodMetricses(podNamespac e).Get(c.context, podName, metav1.GetOptions{}) for _, containerMetrics := range podMetrics.Containers { containerMemoryUsage = containerMetrics.Usage.Memory() }

Slide 23

Slide 23 text

©MIXI カスタムコントローラーを作ってみよう - メモリ使用率チェック メモリ使用率をチェックし、超過でevict ● 対象となるPodで設定されているevictにす るメモリ使用率を取得する ● メモリの最大値・使用量を取得する ● 最後に、メモリ使用率を取得し、超過した 場合にPodをevictさせる // メモリ使用率を算出 containerMemoryUsagePercentage := (float64(containerMemoryUsage.Value()) / float64(containerLimitsMemory.Value())) * 100 if containerMemoryUsagePercentage > float64(podMemoryUsageThreshold.Value()) { _, err := evictPod(c.context, c.clientset, podName, podNamespace, "v1", false) }

Slide 24

Slide 24 text

©MIXI カスタムコントローラーを作ってみよう - メモリ使用率チェック メモリ使用率をチェックし、超過でevict ● 対象となるPodで設定されているevictにす るメモリ使用率を取得する ● メモリの最大値・使用量を取得する ● メモリ使用率を取得し、超過した場合にPod をevictさせる ● 最後に作成したクライアントを通して evictする eviction := &policy.Eviction{ TypeMeta: metav1.TypeMeta{ APIVersion: policyGroupVersion, Kind: EvictionKind, }, ObjectMeta: metav1.ObjectMeta{ Name: podName, Namespace: podNamespace, }, DeleteOptions: deleteOptions, } err := client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(ctx, eviction)

Slide 25

Slide 25 text

©MIXI カスタムコントローラーを作ってみよう - Pod検出

Slide 26

Slide 26 text

©MIXI カスタムコントローラーを作ってみよう - 終了処理 シグナルをハンドリングし、終了させる ● SIGTERMとSIGINTをハンドリングし、 キャッチしたら終了させる ● シグナルハンドラを多重で呼び出さないよ う、onlyOneSignalHandlerを作成 var onlyOneSignalHandler = make(chan struct{}) var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} func SetupSignalHandler() context.Context { close(onlyOneSignalHandler) c := make(chan os.Signal, 2) ctx, cancel := context.WithCancel(context.Background()) signal.Notify(c, shutdownSignals...) go func() { <-c cancel() <-c os.Exit(1) }() return ctx }

Slide 27

Slide 27 text

©MIXI 実装は以上! 思ったより簡単ですね

Slide 28

Slide 28 text

©MIXI カスタムコントローラーを作ってみると k8sの内部処理も併せて理解できるメリットも!

Slide 29

Slide 29 text

©MIXI 非常にお世話になった文献 ※ 実践入門 Kubernetes カスタムコントローラーへの道 : https://amzn.asia/d/8oIrthm ● カスタムコントローラーについて、日本語 で体系的にまとまった文献が少ない中、内 部構造の説明から非常に分かりやすく書か れていました ● ただ、本書内で使われているコマンド等は 使えなくなっている可能性が大なので、適 宜最新の内容に変えつつ読み進めていくの がよし

Slide 30

Slide 30 text

©MIXI 後日談

Slide 31

Slide 31 text

©MIXI pre-oom-killerを参考にカスタムコントロー ラーを作ってくれた方が! ※ GitHub - phamngocsonls/k8s-oom-killer : https://github.com/phamngocsonls/k8s-oom-killer

Slide 32

Slide 32 text

©MIXI ご清聴ありがとうございました