Kubernetes Horizontal Pod Autoscaler による信頼性向上

こんにちは。SRE の @chaspy です。

みなさん Kubernetes Horizontal Pod Autoscaler は使っていますか?Quipper では先月導入したところ、便利すぎて仕事を奪われてしまいました。

本記事では、Horizontal Pod Autoscaler(以下 HPA)の基本原理を簡単に説明したのち、実運用で導入する際の注意点について話そうと思います。

HPA とは何か

名前のままではありますが、Pod を水平方向(Horizontal)に自動的にスケールしてくれる君です。

対になる存在として、垂直方向にスケールする Virtical Pod Autoscaler も存在します。Resource request を動的に変更してくれる君ですね。こちらは未導入です。*1

なぜ HPA を導入したのか

教育サービスということで、基本的にトラフィックの傾向は一定です。トラフィック量を示すグラフは1日の中で似たような山を描きますが、1年のうち特定の時期にトラフィックが増えることがあります。夏休みの最終日や、ゴールデンウィークの最終日なんかは、みんな宿題を頑張って解くので増えがちです。そういうときは前年度の傾向をざっくり見て、Pod を事前にスケールアウトしておくのがこれまでの負荷対策でした。

最近は特にインドネシアで、模試を Quipper Platform 上で行うことが増えました。日本と異なり、フィリピンやインドネシアでは1年の計画が事前に完璧に決まるということはまったくなく、余裕を持ったスケーリングスケジュールが立てづらいという問題がありました。*2

サービスを落としてしまってからでは遅いので、これまでは特にコミュニケーションを密に行うことで対策を行ってきましたが*3、前述したようなイベントの正確な情報が掴みづらい状況であったり、Business Developer が知りうる「ユーザ数」という数値のみではシステムで起こりうるシステムの見積もりが難しいという問題がありました。具体的には、そのユーザが「一斉にアクセスするのか、該当時間内の任意の時間にアクセスするのか」「使うドメイン・サービスはどれなのか」といったことが分かりづらく、さらに言うと「ユーザ数」という情報すらも正確な数を Business Developer ですら掴むことが難しい状況にありました。

さらにグローバルに特化した状況を説明すると、インドネシアは BtoC、フィリピンは BtoB といった違いはあるものの、いずれも利用者は拡大しており、特にフィリピンでは学校の定期試験でも使われはじめており、Quipper Platform が「当たり前」になりつつあります。そういった状況ではサービスダウンのインパクトも非常に大きくなってきています。人間の情報連携による不正確な見積もりでのスケールアウトではなく、機械的に解決する仕組みが必要であるという背景があり、今回 HPA を導入しました。

また、HPA 導入後も、昨今の COVID-19 の影響により、インドネシアでは政府が休校中に使用する学習プラットフォームとして Quipper を名前にあげたり、日本でも外出自粛や公立校の休校も相まってトラフィックの状況はますます読むことは難しくなってきています。今回、非常に良いタイミングで導入することができました。

HPA の基本原理

詳細は公式ドキュメント を参照して欲しいのですが、おさえるべきポイントは以下の3つです。

  1. 基本的には Pod の平均 CPU 使用率を計算に用いる(targetCPUUtilizationPercentage*4
  2. この使用率は cpu request に対する割合である
  3. HPA はこの割合を保つように Deployment の replicas を変更する

実際に雰囲気を掴むために Manifest を見てみましょう。

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 10
  maxReplicas: 100
  targetCPUUtilizationPercentage: 70 # want 700 mcore of cpu usage. 700 / 1000(requests) = 0.7

HPA の対象の Deployment と、閾値となる targetCPUUtilizationPercentage と、replica 数の range を表す min と max を指定するだけです。簡単ですね!

コメントに書いてありますが、この場合 cpu request が 1000mcore だったとすると、平均 CPU 使用率が 700mcore を保つように HPA は replica 数を変更します。なお計算間隔は5分です。*5

導入の流れ

  1. HPA で扱う対象の metrics を決定する
  2. 安全のため、minreplicas を現在の(desired) replicas と同じ値に設定する
  3. Production で導入する
  4. 動作を観察して、min replicas を下げたり、threshold を調整する

ざっくりこのような流れになります。基本は出してみて、観察してみて調整するしかないと思っています。

導入するときの注意点

Cluster Autoscaler を導入しておく

Pod が自動的に増減するということは、その Pod が動作する Node も増減できる必要があります。Cluster Autoscaler を導入しておきましょう。

このことから、Pod が増えるときに支配的になるのは Node が増える時間になることが分かります。だいたい Pod が1分で Running になるのに対して、Node は5分ほどかかります。つまり、キャパシティオーバーとなる5分前にはスケールを開始できるような閾値を設定する必要があります。

リクエスト数に比例する metrics を選ぶ

リクエスト数に比例して動く metrics であることが望ましいです。

例えばキューに積まれていることを示す unicorn の backlog(request_backlog_total)のような metrics の場合、確かにこれらが増えたときには replicas を増やしたくなりますが、平常時は常に 0 なので replicas の決定に役立ちません。

Quipper では 多くのアプリケーションが Rails/Unicorn で動いており、リクエスト数と CPU 使用率は連動しているように見えます。

  • Rails application のリクエスト数 Screenshot 2020-04-07 11 40 37

  • 平均 CPU 使用率 Screenshot 2020-04-07 11 40 51

Deployment の replicas を指定しない

HPA を導入する前は Deployment で replicas を指定していたと思いますが、この指定を外しましょう。もしこれが指定されたままの場合、apply が行われるたびに replicas がその固定値に1度変更されたあと、HPA によって再計算される、といった挙動が発生してしまいます。

HPA を導入したあとは、replicas の調整は HPA に任せましょう。

Deployment の replicas を取り除く場合は kubectl.kubernetes.io/last-applied-configuration を削除する

replicas の default は 1 です。何もしないままだと、replicas を 1 に変更するリクエストが発生してしまいます。サービスによっては危険でしょう。

Kubernetes は Deployment の kubectl.kubernetes.io/last-applied-configuration という Annotation に前回適用したときの manifest の情報を持っており、差分検出を行っています。そのため、事前にこの annotation を削除しておけば replicas には差分が検知されません。

導入後の効果

なんということでしょう。きれいに replicas は変更され、CPU 使用率は指定した threshold を保っています。うまく動くと気持ちいいですね。

  • Rails application のリクエスト数 Screenshot 2020-04-07 11 54 09
  • レプリカ数 Screenshot 2020-04-07 11 54 14
  • 平均 CPU 使用率 Screenshot 2020-04-07 11 54 32

開発者と一緒に信頼性を向上していくこと

今回、代表的なアプリケーションに HPA を導入したのち、ドキュメントを作成し、日本 / Global ともに HPA の説明会(勉強会)を開催しました。そのおかげもあって、他のサービスに関しても Developer 自身で HPA が導入されており、信頼性向上に役立っています。

信頼性向上のための取り組みは SRE だけが行うものではありません。SRE は検証や先行導入は行うものの、事例を作ったあとはドキュメントや勉強会を通じて誰でもそれが扱えるようにしていけることを心がけています。

謝辞

これまで、pod あたりの Unicorn worker 数を少なくしたり、downtime を回避するために Pod が Delete する際の preStop で sleep をいれたり、それ以前に Cluster Autoscaler をいれたりと、事前にやってきたことの積み重ねがあったから実現できました。アドバイスをくれた @yuya-takeyama ほか SRE チームのみなさんに心から感謝します。

Deployment の Annotation の挙動について調査、アドバイスをくれて、導入を一緒にやってくれた @d-kuro に心から感謝します。

おわりに

HPA を導入したおかげで、以前は SRE がアラートを受けたり、トラフィックが増えそうなときに見積もって事前に replicas を増やしていた仕事がなくなり、サービス・SREの生活ともに信頼性が向上しました。本記事が HPA 導入のヒントになれば幸いです。

Quipper では世界の果てまで学びを届けたい仲間 を募集しています。

*1:今後導入する予定も今のところありません

*2:こういう話も現地に出張にいってステークホルダーと話したからこそ、温度感を持って知ることができたことでもあります。合わせて読みたい:Business Trip for Global Team

*3:連携には Issue Template を利用しています。合わせて読みたい:障害対応とポストモーテム

*4:基本ということは応用があり、Memory だったり、外部の Metrics を使用することもできます

*5:このあたりの挙動は v1.18 から manifest で制御できるようになるようです。