目次
はじめに
こんにちは。京都大学 修士1年の安済です。
2025年10月の1ヶ月間、『スタディサプリENGLISH』のSREチームでインターンシップに参加しました。今回のインターンシップでは、Kubernetesで動いているマイクロサービスのPodオートスケール機能の検証に取り組みました。
本記事では、携わった案件と取り組んだタスク、そしてインターンシップを通じて学んだことについてお伝えします。
担当した案件の説明
背景
現在、私たちのサービスでは、Kubernetesで動いている各マイクロサービスのReplica数が固定値になっています。
しかし、実際のリクエスト数を見てみると、ピーク時以外はそれほどリクエスト数が多くないことが分かります。
上の図は、平日1日のリクエスト数の推移を示しています。朝(6:30〜9:30)と夜(18:00〜24:00)にピークがありますが、それ以外の時間帯はリクエスト数が大幅に少なくなっています。
現状では、ピーク時に合わせてReplica数を設定しているため、オフピーク時にはリソースが余っている状態でした。
オフピーク時のReplica数を減らし、本番環境のコスト最適化ことを目的として、Podのオートスケール機能の検証を行うことになりました。
要件整理
オートスケールを導入するにあたり、まず満たすべき要件を整理しました。
満たすべき要件は大きく分けて2つです。
-
ピークより前にコンテナ数がMax値になる
-
ピークタイム以外はコンテナ数が増えすぎない
特に、ピーク時はリクエスト数が「5分で2倍」になるため、このピーク時のリクエスト増に耐えられるかが重要でした。
インターンシップでは、これらの要件を満たすことができるかを重点的に検証しました。
実際に取り組んだこと
1. 検証環境の準備
まず、検証を行うための環境を整備しました。
メトリクスの可視化
検証環境で以下のメトリクスをDatadogで可視化しました:
- Replica数
- PodのCPU使用率
- Node数
これにより、オートスケールの挙動を詳細に観察できるようになりました。
負荷試験ツールの改良
既存の負荷試験コードがあったのですが、今回とは異なる目的のために作られたものだったため、今回とは関係がないサーバーにも負荷がかかってしまっていました。それがボトルネックとなって、今回負荷をかけたかった「学習関連」のサーバーに負荷が十分にかけられないという問題がありました。
そこで、関係ない処理を省いて意図通りに負荷がかけられる環境を整備しました。
ボトルネックを特定するのに苦労しましたが、関連するメトリクスを精査して問題解決にたどり着きました。
2. HPA(CPU使用率ベース)の検証
KubernetesにはHorizontal Pod Autoscaler(HPA)という標準のオートスケール機能があります。まずはこのHPAを使い、CPU使用率をもとにReplica数を変化させる方法で実験をしました。
実験の目的は大きく分けて2つです。
- パラメータ値の調整
- 要件を実現可能か検証
HPAでは以下のような設定項目があります:
-
何の指標をもとにスケールするか(CPU、メモリ、External Metricsなど)
-
どれくらいのリソース使用率を目指すか(例:CPU 30%を維持)
-
どれくらいの期間、条件が続いたらスケールするか(例:20秒間CPU 30%超過でスケールアウト)
-
どれくらいの速度でスケールするか(例:30秒ごとに最大4個増やす)
これらの値をどのように設定するのがいいか、検証する必要がありました。
また、これらの値を調整したとして、CPU使用率ベースの方法で要件を実現可能なのかも検証する必要がありました。
今回は大きく分けて2つの実験を行いました。
実験1:平日 日中の時間帯のリクエスト数でCPU使用率、Replica数を調べる
まずはピーク時以外にReplica数が増えすぎないかを検証しました。リクエスト数はオフピーク時(平日の日中の時間帯)相当の数を送りました。
パラメータの設定は、Kubernetesのドキュメントに書かれているデフォルトの設定値を参考に、以下の2点を意識して設定しました:
-
スケールアウトは素早く行える
-
Replica数が頻繁に変わらないようにする(振動を防ぐ)
実際に適用したマニフェストがこちらです:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: study-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: study-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 30
behavior:
scaleUp:
stabilizationWindowSeconds: 20
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 4
periodSeconds: 30
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Min
この設定で実験をしたところ、Podを起動した直後の負荷がスケールアウトをトリガーしてしまい、過剰にスケールアウトが発生してしまいました。
そのため、スケールアウトが若干発生しにくくなるように設定を調整しました。
改善後の設定:
-
目標CPU使用率:30%
-
スケールアウト条件:20秒間平均CPU使用率が30%を超えていたらスケールアウトさせる
-
スケールイン条件:300秒間平均CPU使用率が30%を下回っていたらスケールインさせる
その結果、Replica数は現実的な値に落ち着くようになりました。
実験2:ピーク時のスパイクに対して、スケールアウトが間に合うのかを調べる
2つ目の実験では、ピーク時のスパイクに対応できるかを確認しました。
リクエストを2分間で3倍に引き上げて、CPU使用率・Replica数の変化を観察しました。
その結果、Replica数が最大値になったのはリクエストを増やし始めて7分後で、最終的に全てのPodがReadyになったのは24分後でした。
考察
ひとつ目の実験では、スケールアウトしやすくしすぎると、通常時でもPodが増えすぎてしまうということが分かりました。スケールアウトのしやすさを少し下げて調整したところ、Podが増えすぎるということは無くなりました。
一方で、ふたつ目の実験から、ピーク時のスパイクには間に合わないということが分かりました。
これらの結果から、CPU使用率だけでPodの数を調整するのは難しいと判断しました。
3. CPU使用率+時間ベースの検証
HPA検証の結果、CPU使用率だけでピーク時のリクエストに対応するのは難しいことが分かりました。
次に考えられる方法は、「時間に応じてReplica数を変える」という方法です。この方法なら、ピーク時の少し前にあらかじめReplica数を最大数にしておくことができます。
一方、時間に応じてReplica数を固定すると、不測のリクエスト増加に対応しづらいという問題があります。
そこで、「通常時はCPU使用率に応じてReplica数が変化するようにしておき、ピーク時のみReplica数を固定する」という方法を検証することにしました。
「時間によってReplica数を変える」という方法はKubernetesの標準機能だけでは実現できないため、KEDAを使うことにしました。
KEDAとは
KEDAは、Kubernetes上でより高度なオートスケールを実現するためのOSSです。HPAを拡張する形で動作します。
KEDAはTriggerを複数設定することで、複数のメトリクスをもとにScaleさせることができます。Triggerを複数指定した場合、「Replica数を多くしようとしている方」が優先されます。
設定例:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: study-api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: study-api
minReplicaCount: 2
maxReplicaCount: 10
cooldownPeriod: 30
pollingInterval: 60
advanced:
horizontalPodAutoscalerConfig:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
selectPolicy: Min
scaleUp:
stabilizationWindowSeconds: 20
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 4
periodSeconds: 30
selectPolicy: Max
triggers:
- type: cpu
metricType: Utilization
metadata:
value: "30"
- type: cron
metadata:
timezone: Asia/Tokyo
start: 0 16 * * *
end: 0 17 * * *
desiredReplicas: "10"
この設定により、以下のような動作が実現できます:
-
通常時:CPU使用率30%を目標にReplica数が変化
-
16:00〜17:00:Replica数を10に固定
実験
作成したKEDAのScaledObjectが正常に動作するかを実験しました。
実験の結果、Podの数を指定して3分ほどで最大数に到達して、実運用でも使えそうということが分かりました。
学んだこと
今回のインターンシップでは、実際の本番環境を想定した負荷試験の行われ方を学ぶことができました。メトリクスをもとに課題の仮説を立て、実験をすることでボトルネックを特定するプロセスはかなり学びが多かったと感じています。
また、技術力が高い人が多く、検証で行き詰まった際も手厚くサポートしてくれたり、OSSコミッターがいるなどのリクルートの風土を知ることができました。
お世話になった皆様、本当にありがとうございました!