Kubernetes HPA / リソース制限を考慮した設計ガイド

はじめに

Kubernetes を使い始めると、
多くの人が HPA(Horizontal Pod Autoscaler) に大きな期待を寄せます。

  • トラフィックが増えたら自動で Pod が増える
  • 負荷が下がったら自動で Pod が減る
  • 人が張り付かなくても安定運用できる

一見すると、HPA を設定するだけで
“スケール問題はすべて解決する” ように思えるかもしれません。

しかし、実務の現場では次のような声をよく聞きます。

  • HPA を設定したのに Pod が増えない
  • スケールはするがレスポンスが改善しない
  • 急に Pod が落ちてサービスが不安定になる
  • CPU 使用率の数字が信用できない

これらの原因は、HPA の設定ミスというよりも
HPA とリソース制限(requests / limits)を前提にした設計ができていないことにあります。

本記事では、
「HPA を使う」ではなく「HPA が正しく機能する設計を作る」
という視点で、考え方からハンズオンまでを丁寧に解説します。


HPA とは何かを改めて整理する

HPA(Horizontal Pod Autoscaler)は、
Pod の負荷状況を監視し、レプリカ数を自動で増減させる仕組みです。

重要なのは、HPA がやっていることは非常にシンプルだという点です。

  • CPU 使用率が高い → Pod を増やす
  • CPU 使用率が下がった → Pod を減らす

👉 HPA は「Pod を増やすだけ」で、アプリを速くはしてくれません

つまり、

  • 1 Pod あたりの性能
  • Pod の起動速度
  • Pod が落ちたときの挙動

これらはすべて 設計者の責任になります。


HPA が正しく動くための前提条件

HPA は万能ではなく、
いくつかの前提条件を満たして初めて意味を持つ仕組みです。

前提①:リソース requests が正しく設定されている

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"

HPA は CPU 使用率 / requests を基準に
「負荷が高いかどうか」を判断します。

requests が設定されていない、または適当だと、

  • 使用率が異常に高く見える
  • 逆に全然スケールしない

といった問題が発生します。


前提②:Metrics Server が動作している

HPA はメトリクスがなければ動きません。

  • CPU 使用率
  • メモリ使用量

これらは Metrics Server が提供しています。

👉 「HPA が動かない」原因の多くは Metrics Server 未導入


前提③:アプリがスケール前提で作られている

HPA は Pod を増やしますが、
アプリがスケールを前提としていなければ意味がありません

  • セッションを Pod 内に持っていないか
  • ファイルをローカルに保存していないか
  • 起動に何十秒もかからないか

Kubernetes の世界では、
Pod は消耗品 という考え方が基本です。


requests / limits の役割を正しく理解する

requests:最低保証ライン

requests は、

  • この Pod が最低限必要なリソース
  • スケジューラが Pod を配置する判断材料
  • HPA の計算基準

という、非常に重要な値です。


limits:絶対に超えてはいけない上限

limits は、

  • CPU:超えるとスロットリング
  • メモリ:超えると即 OOM Kill

という 強制的な制限です。

特にメモリは、
一度超えたら容赦なく Pod が再起動します。


実務で多い誤解

  • 「limits だけ設定すればいい」
  • 「requests は小さい方がお得」

👉 どちらも危険な考え方です。

requests が小さすぎると、
HPA は「常に高負荷」と誤認し、
無意味なスケールを繰り返します。


HPA × Dockerfile × アプリ設計はセットで考える

HPA は Kubernetes の機能ですが、
Dockerfile とアプリ設計が HPA の成否を決定します

なぜ Dockerfile が重要なのか?

  • イメージが大きい → 起動が遅い
  • 起動が遅い → スケールに追従できない
  • SIGTERM 未対応 → スケールダウン時に事故

👉 HPA は「速く増えて、きれいに減る」ことが前提


ハンズオン①:HPA 前提のサンプルアプリ

アプリ構成

hpa-app/
├─ Dockerfile
├─ package.json
└─ index.js

index.js(CPU 負荷を意図的に発生させる)

const http = require("http");

function heavyTask() {
  let sum = 0;
  for (let i = 0; i < 1e7; i++) {
    sum += i;
  }
  return sum;
}

const server = http.createServer((req, res) => {
  heavyTask();
  res.end("Hello HPA");
});

server.listen(3000);

process.on("SIGTERM", () => {
  console.log("SIGTERM received, shutting down...");
  server.close(() => process.exit(0));
});

このように
CPU を使う処理を意図的に入れることで、
HPA の挙動を確認しやすくします。


ハンズオン②:HPA 向け Dockerfile

FROM node:20-alpine

WORKDIR /app
COPY package.json ./
RUN npm install --only=production
COPY index.js .

USER node
EXPOSE 3000
CMD ["node", "index.js"]

ポイント

  • 軽量イメージ
  • 非 root 実行
  • 起動が速い

👉 HPA との相性が良い Dockerfile


ハンズオン③:Deployment(リソース設計)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hpa-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hpa-app
  template:
    metadata:
      labels:
        app: hpa-app
    spec:
      containers:
        - name: app
          image: hpa-app:1.0
          ports:
            - containerPort: 3000
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"

ハンズオン④:HPA 定義

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-app
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50

ハンズオン⑤:スケール挙動を確認する

kubectl apply -f deployment.yaml
kubectl apply -f hpa.yaml
kubectl get hpa

負荷をかけます。

kubectl run -it --rm load \
  --image=busybox \
  --restart=Never -- sh
while true; do wget -q -O- http://hpa-app; done

別ターミナルで:

kubectl get pods -w

👉 Pod が段階的に増えていくことを確認してください。


HPA 設計で頻発する失敗パターン

① requests が適当

  • 小さすぎて常に高負荷判定
  • 大きすぎてスケールしない

② limits が厳しすぎる

  • CPU スロットリング
  • レスポンス劣化
  • スケールしても改善しない

③ アプリがステートフル

  • セッションが Pod 固有
  • スケール=ユーザー切断

実務向け設計の考え方

  • requests:平均的な通常負荷
  • limits:想定ピーク + 余裕
  • HPA:段階的に増える設定
  • Pod:いつでも消える前提

チェックリスト(本番前確認用)

  • requests / limits を設定している
  • HPA の target が妥当
  • Pod 起動が速い
  • SIGTERM に対応
  • ステートレス設計

まとめ

HPA とリソース制限は、

  • Kubernetes の自動化の心臓部
  • 設計ミスが即障害につながる
  • Dockerfile / アプリ設計と不可分

という特徴を持っています。

「HPA を有効にした」ではなく
「HPA が意味を持つ設計をした」

この意識が、
Kubernetes 本番運用を安定させる最大のポイントです。


投稿日

カテゴリー:

投稿者:

タグ:

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です