<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Kubernetes on AI2CORE - AI技術ブログ</title>
    <link>https://www.ai2core.com/tags/kubernetes/</link>
    <description>Recent content in Kubernetes on AI2CORE - AI技術ブログ</description>
    <generator>Hugo -- 0.146.4</generator>
    <language>ja</language>
    <lastBuildDate>Sun, 08 Mar 2026 10:00:00 +0900</lastBuildDate>
    <atom:link href="https://www.ai2core.com/tags/kubernetes/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Kubernetes HPA/VPA完全ガイド：本番環境で失敗しないオートスケーリング設計と実装</title>
      <link>https://www.ai2core.com/posts/2026-03-08-kubernetes-hpa-vpa-autoscaling-masterguide/</link>
      <pubDate>Sun, 08 Mar 2026 10:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-03-08-kubernetes-hpa-vpa-autoscaling-masterguide/</guid>
      <description>KubernetesのHorizontal Pod Autoscaler（HPA）とVertical Pod Autoscaler（VPA）を使った自動スケーリングの設計・実装・運用ノウハウを解説。メトリクス選定からトラブルシューティングまで完全網羅。</description>
      <content:encoded><![CDATA[<h2 id="はじめに">はじめに</h2>
<p>Kubernetesを本番環境で運用する上で、適切なリソース管理とスケーリングは避けて通れない課題です。過剰なリソース割り当てはコストの無駄になり、不足していればパフォーマンス低下やサービス障害につながります。</p>
<p>本記事では、Kubernetesの自動スケーリング機能であるHorizontal Pod Autoscaler（HPA）とVertical Pod Autoscaler（VPA）について、基礎概念から本番環境での実践的な運用ノウハウまでを詳しく解説します。</p>
<h2 id="hpaとvpaの違い">HPAとVPAの違い</h2>
<h3 id="horizontal-pod-autoscalerhpa">Horizontal Pod Autoscaler（HPA）</h3>
<p>HPAは、Pod数を水平方向にスケールさせる機能です。負荷が増加するとPod数を増やし、負荷が減少するとPod数を減らします。</p>
<p><strong>適用シーン：</strong></p>
<ul>
<li>ステートレスなWebアプリケーション</li>
<li>APIサーバー</li>
<li>ワーカープロセス</li>
</ul>
<h3 id="vertical-pod-autoscalervpa">Vertical Pod Autoscaler（VPA）</h3>
<p>VPAは、個々のPodのリソース要求（CPU/メモリ）を垂直方向に調整する機能です。実際の使用状況に基づいて、より適切なリソース割り当てを推奨・適用します。</p>
<p><strong>適用シーン：</strong></p>
<ul>
<li>ステートフルなアプリケーション</li>
<li>データベースやキャッシュサーバー</li>
<li>バッチ処理ジョブ</li>
</ul>
<h2 id="hpa実践ガイド">HPA実践ガイド</h2>
<h3 id="基本的なhpa設定">基本的なHPA設定</h3>
<p>まずはシンプルなCPUベースのHPAから始めましょう。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">44
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">45
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">46
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">47
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">48
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">49
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">50
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">51
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">52
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">53
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">54
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">55
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">56
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">57
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">58
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">59
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">60
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">61
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">62
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">63
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">64
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">65
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">66
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">67
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">68
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">69
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">70
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">71
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">72
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">73
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">74
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">75
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">76
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">77
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">78
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># deployment.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">production</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">replicas</span>: <span style="color:#ae81ff">3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">app</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">template</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">app</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">containers</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">image</span>: <span style="color:#ae81ff">myregistry/web-api:v1.2.3</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">200m</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">256Mi</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">limits</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">1000m</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">512Mi</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">containerPort</span>: <span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">readinessProbe</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">httpGet</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">path</span>: <span style="color:#ae81ff">/health</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">port</span>: <span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">initialDelaySeconds</span>: <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span><span style="color:#75715e"># hpa.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling/v2</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">HorizontalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-hpa</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">production</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleTargetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minReplicas</span>: <span style="color:#ae81ff">3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">maxReplicas</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">metrics</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Resource</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">resource</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#ae81ff">cpu</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Utilization</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">averageUtilization</span>: <span style="color:#ae81ff">70</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Resource</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">resource</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#ae81ff">memory</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Utilization</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">averageUtilization</span>: <span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">behavior</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">scaleDown</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">300</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">value</span>: <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">scaleUp</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">value</span>: <span style="color:#ae81ff">100</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">15</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Pods</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">value</span>: <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">15</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">selectPolicy</span>: <span style="color:#ae81ff">Max</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="カスタムメトリクスを使ったhpa">カスタムメトリクスを使ったHPA</h3>
<p>CPUやメモリだけでなく、アプリケーション固有のメトリクスでスケーリングすることも可能です。</p>
<h4 id="prometheus-adapterの設定">Prometheus Adapterの設定</h4>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># prometheus-adapter-config.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">ConfigMap</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">prometheus-adapter-config</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">monitoring</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">data</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">config.yaml</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    rules:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    - seriesQuery: &#39;http_requests_total{namespace!=&#34;&#34;,pod!=&#34;&#34;}&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      resources:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        overrides:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          namespace: {resource: &#34;namespace&#34;}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          pod: {resource: &#34;pod&#34;}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      name:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        matches: &#34;^(.*)_total$&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        as: &#34;${1}_per_second&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      metricsQuery: &#39;sum(rate(&lt;&lt;.Series&gt;&gt;{&lt;&lt;.LabelMatchers&gt;&gt;}[2m])) by (&lt;&lt;.GroupBy&gt;&gt;)&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    - seriesQuery: &#39;request_queue_length{namespace!=&#34;&#34;,pod!=&#34;&#34;}&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      resources:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        overrides:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          namespace: {resource: &#34;namespace&#34;}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">          pod: {resource: &#34;pod&#34;}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      name:
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        matches: &#34;^(.*)$&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        as: &#34;${1}&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">      metricsQuery: &#39;avg(&lt;&lt;.Series&gt;&gt;{&lt;&lt;.LabelMatchers&gt;&gt;}) by (&lt;&lt;.GroupBy&gt;&gt;)&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="カスタムメトリクスhpaの定義">カスタムメトリクスHPAの定義</h4>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling/v2</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">HorizontalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-custom-hpa</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">production</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleTargetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minReplicas</span>: <span style="color:#ae81ff">3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">maxReplicas</span>: <span style="color:#ae81ff">100</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">metrics</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># リクエスト数ベースのスケーリング</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Pods</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pods</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">metric</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">http_requests_per_second</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">AverageValue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">averageValue</span>: <span style="color:#ae81ff">1000</span>  <span style="color:#75715e"># Pod当たり1000 RPS</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># キュー長ベースのスケーリング</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Pods</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">pods</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">metric</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">request_queue_length</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">AverageValue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">averageValue</span>: <span style="color:#ae81ff">10</span>  <span style="color:#75715e"># Pod当たりキュー長10以下</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="外部メトリクスを使ったスケーリング">外部メトリクスを使ったスケーリング</h3>
<p>SQSキューやPub/Subサブスクリプションなど、外部システムのメトリクスに基づくスケーリングも可能です。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling/v2</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">HorizontalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">queue-worker-hpa</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">production</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleTargetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">queue-worker</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minReplicas</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">maxReplicas</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">metrics</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">External</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">external</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">metric</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">sqs_queue_messages_visible</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">queue_name</span>: <span style="color:#e6db74">&#34;production-tasks&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">AverageValue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">averageValue</span>: <span style="color:#ae81ff">20</span>  <span style="color:#75715e"># ワーカー当たり20メッセージ</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="hpaのスケーリング動作を制御するbehavior設定">HPAのスケーリング動作を制御するbehavior設定</h3>
<p>Kubernetes 1.18以降では、<code>behavior</code>フィールドでスケーリングの動作を細かく制御できます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">behavior</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleDown</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># スケールダウン前の安定化期間（急な縮小を防ぐ）</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">300</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 60秒ごとに最大10%減少</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">value</span>: <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># または60秒ごとに最大2 Pod減少</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Pods</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">value</span>: <span style="color:#ae81ff">2</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 複数ポリシーがある場合、最小の変更量を採用</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">selectPolicy</span>: <span style="color:#ae81ff">Min</span>
</span></span><span style="display:flex;"><span>  
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleUp</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># スケールアップは即座に反応</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 15秒ごとに最大100%増加</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">value</span>: <span style="color:#ae81ff">100</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">15</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># または15秒ごとに最大4 Pod増加</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Pods</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">value</span>: <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">15</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 複数ポリシーがある場合、最大の変更量を採用</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">selectPolicy</span>: <span style="color:#ae81ff">Max</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="vpa実践ガイド">VPA実践ガイド</h2>
<h3 id="vpaのインストール">VPAのインストール</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># VPAコンポーネントのインストール</span>
</span></span><span style="display:flex;"><span>git clone https://github.com/kubernetes/autoscaler.git
</span></span><span style="display:flex;"><span>cd autoscaler/vertical-pod-autoscaler
</span></span><span style="display:flex;"><span>./hack/vpa-up.sh
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># インストール確認</span>
</span></span><span style="display:flex;"><span>kubectl get pods -n kube-system | grep vpa
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="vpaの設定">VPAの設定</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling.k8s.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">VerticalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-vpa</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">production</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">targetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">updatePolicy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">updateMode</span>: <span style="color:#e6db74">&#34;Auto&#34;</span>  <span style="color:#75715e"># Off, Initial, Recreate, Auto</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">resourcePolicy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">containerPolicies</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">containerName</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">minAllowed</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">100m</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">128Mi</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">maxAllowed</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">4</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">8Gi</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">controlledResources</span>: [<span style="color:#e6db74">&#34;cpu&#34;</span>, <span style="color:#e6db74">&#34;memory&#34;</span>]
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">controlledValues</span>: <span style="color:#ae81ff">RequestsAndLimits</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="vpaの動作モード">VPAの動作モード</h3>
<table>
  <thead>
      <tr>
          <th>モード</th>
          <th>説明</th>
          <th>ユースケース</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Off</td>
          <td>推奨値の計算のみ。適用しない</td>
          <td>初期検証、リソース分析</td>
      </tr>
      <tr>
          <td>Initial</td>
          <td>Pod作成時のみ適用</td>
          <td>安全に導入したい場合</td>
      </tr>
      <tr>
          <td>Recreate</td>
          <td>Podを再作成して適用</td>
          <td>ダウンタイム許容可能な場合</td>
      </tr>
      <tr>
          <td>Auto</td>
          <td>最適な方法で自動適用</td>
          <td>本番環境での運用</td>
      </tr>
  </tbody>
</table>
<h3 id="vpaの推奨値を確認">VPAの推奨値を確認</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># VPAの状態確認</span>
</span></span><span style="display:flex;"><span>kubectl describe vpa web-api-vpa -n production
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 出力例</span>
</span></span><span style="display:flex;"><span>Status:
</span></span><span style="display:flex;"><span>  Recommendation:
</span></span><span style="display:flex;"><span>    Container Recommendations:
</span></span><span style="display:flex;"><span>    - Container Name: web-api
</span></span><span style="display:flex;"><span>      Lower Bound:
</span></span><span style="display:flex;"><span>        Cpu:     150m
</span></span><span style="display:flex;"><span>        Memory:  180Mi
</span></span><span style="display:flex;"><span>      Target:
</span></span><span style="display:flex;"><span>        Cpu:     250m
</span></span><span style="display:flex;"><span>        Memory:  320Mi
</span></span><span style="display:flex;"><span>      Uncapped Target:
</span></span><span style="display:flex;"><span>        Cpu:     250m
</span></span><span style="display:flex;"><span>        Memory:  320Mi
</span></span><span style="display:flex;"><span>      Upper Bound:
</span></span><span style="display:flex;"><span>        Cpu:     500m
</span></span><span style="display:flex;"><span>        Memory:  640Mi
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="hpaとvpaの併用">HPAとVPAの併用</h2>
<p>HPAとVPAを同じDeploymentに適用する場合、注意が必要です。両者がリソース設定を競合して変更すると、予期しない動作が発生する可能性があります。</p>
<h3 id="推奨アプローチ1vpaをoffinitialモードで使用">推奨アプローチ1：VPAをOff/Initialモードで使用</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># VPAは推奨値の計算のみ</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling.k8s.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">VerticalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-vpa</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">targetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">updatePolicy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">updateMode</span>: <span style="color:#e6db74">&#34;Off&#34;</span>  <span style="color:#75715e"># 自動適用しない</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">resourcePolicy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">containerPolicies</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">containerName</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">controlledResources</span>: [<span style="color:#e6db74">&#34;memory&#34;</span>]  <span style="color:#75715e"># メモリのみVPAで管理</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="推奨アプローチ2リソース種別で分担">推奨アプローチ2：リソース種別で分担</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># VPAはメモリのみ管理</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling.k8s.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">VerticalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-vpa</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">targetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">updatePolicy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">updateMode</span>: <span style="color:#e6db74">&#34;Auto&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">resourcePolicy</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">containerPolicies</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">containerName</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">controlledResources</span>: [<span style="color:#e6db74">&#34;memory&#34;</span>]  <span style="color:#75715e"># CPUは管理しない</span>
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span><span style="color:#75715e"># HPAはCPUベースでスケーリング</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling/v2</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">HorizontalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-hpa</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleTargetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minReplicas</span>: <span style="color:#ae81ff">3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">maxReplicas</span>: <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">metrics</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Resource</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">resource</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">name</span>: <span style="color:#ae81ff">cpu</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Utilization</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">averageUtilization</span>: <span style="color:#ae81ff">70</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="本番環境での設計ベストプラクティス">本番環境での設計ベストプラクティス</h2>
<h3 id="1-適切なリソースrequestsの設定">1. 適切なリソースrequestsの設定</h3>
<p>HPAはrequestsに対する使用率を計算します。requestsが不適切だと、スケーリングも不適切になります。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># 実際の平均使用量に近い値を設定</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cpu: 200m     # 観測された平均</span>: <span style="color:#ae81ff">180m</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">memory: 256Mi # 観測された平均</span>: <span style="color:#ae81ff">220Mi</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">limits</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># バースト時の上限</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cpu</span>: <span style="color:#ae81ff">1000m</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">memory</span>: <span style="color:#ae81ff">512Mi</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="2-pod-disruption-budgetの設定">2. Pod Disruption Budgetの設定</h3>
<p>スケールダウン時にサービス断が発生しないよう、PDBを設定します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">policy/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">PodDisruptionBudget</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">web-api-pdb</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">production</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minAvailable: 2  # または maxUnavailable</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">app</span>: <span style="color:#ae81ff">web-api</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="3-readiness-probeの最適化">3. Readiness Probeの最適化</h3>
<p>新しいPodがトラフィックを受け入れる前に、十分なウォームアップ時間を確保します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">readinessProbe</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">httpGet</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">path</span>: <span style="color:#ae81ff">/health/ready</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">port</span>: <span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">initialDelaySeconds</span>: <span style="color:#ae81ff">30</span>  <span style="color:#75715e"># アプリ起動時間を考慮</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">10</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">successThreshold</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">failureThreshold</span>: <span style="color:#ae81ff">3</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="4-スケーリングのテスト">4. スケーリングのテスト</h3>
<p>本番投入前に、負荷テストでスケーリング動作を検証します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 負荷テストツール（k6）でのテスト例</span>
</span></span><span style="display:flex;"><span>k6 run --vus <span style="color:#ae81ff">100</span> --duration 10m load-test.js
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># HPAの状態を監視</span>
</span></span><span style="display:flex;"><span>watch kubectl get hpa web-api-hpa -n production
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="トラブルシューティング">トラブルシューティング</h2>
<h3 id="hpaが動作しない場合">HPAが動作しない場合</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># HPAの状態確認</span>
</span></span><span style="display:flex;"><span>kubectl describe hpa web-api-hpa -n production
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># よくあるエラー</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 1. &#34;unable to get metrics for resource cpu&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#    → metrics-serverが動作していない</span>
</span></span><span style="display:flex;"><span>kubectl get pods -n kube-system | grep metrics-server
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 2. &#34;missing request for cpu&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#    → Podにresources.requestsが設定されていない</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 3. ScalingActive: False</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#    → メトリクスが取得できていない</span>
</span></span><span style="display:flex;"><span>kubectl get --raw <span style="color:#e6db74">&#34;/apis/metrics.k8s.io/v1beta1/pods&#34;</span> | jq
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="vpaの推奨が反映されない場合">VPAの推奨が反映されない場合</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># VPAコンポーネントの状態確認</span>
</span></span><span style="display:flex;"><span>kubectl logs -n kube-system -l app<span style="color:#f92672">=</span>vpa-recommender
</span></span><span style="display:flex;"><span>kubectl logs -n kube-system -l app<span style="color:#f92672">=</span>vpa-updater
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Podのアノテーション確認</span>
</span></span><span style="display:flex;"><span>kubectl get pod &lt;pod-name&gt; -o jsonpath<span style="color:#f92672">=</span><span style="color:#e6db74">&#39;{.metadata.annotations}&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="スケーリングが遅い場合">スケーリングが遅い場合</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># HPAのscaleUp設定を調整</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">behavior</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleUp</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">0</span>  <span style="color:#75715e"># 即座にスケールアップ</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">value</span>: <span style="color:#ae81ff">200</span>  <span style="color:#75715e"># より積極的にスケール</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">10</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="メトリクス収集とモニタリング">メトリクス収集とモニタリング</h2>
<h3 id="prometheus--grafanaでのダッシュボード">Prometheus + Grafanaでのダッシュボード</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># PrometheusRuleでアラート設定</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">monitoring.coreos.com/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">PrometheusRule</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">hpa-alerts</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">monitoring</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">groups</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">hpa.rules</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">rules</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">alert</span>: <span style="color:#ae81ff">HPAAtMaxReplicas</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">expr</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        kube_horizontalpodautoscaler_status_current_replicas
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        == kube_horizontalpodautoscaler_spec_max_replicas</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">for</span>: <span style="color:#ae81ff">15m</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">severity</span>: <span style="color:#ae81ff">warning</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">annotations</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">summary</span>: <span style="color:#e6db74">&#34;HPA {{ $labels.horizontalpodautoscaler }} is at max replicas&#34;</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">alert</span>: <span style="color:#ae81ff">HPAScalingFailed</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">expr</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        kube_horizontalpodautoscaler_status_condition{condition=&#34;ScalingActive&#34;,status=&#34;false&#34;} == 1</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">for</span>: <span style="color:#ae81ff">5m</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">severity</span>: <span style="color:#ae81ff">critical</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">annotations</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">summary</span>: <span style="color:#e6db74">&#34;HPA {{ $labels.horizontalpodautoscaler }} scaling is inactive&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="まとめ">まとめ</h2>
<p>Kubernetesのオートスケーリングを効果的に活用するためのポイントをまとめます：</p>
<ol>
<li><strong>HPAとVPAの使い分け</strong>：ステートレスなアプリにはHPA、ステートフルなアプリにはVPAが適している</li>
<li><strong>適切なメトリクス選定</strong>：CPU/メモリだけでなく、アプリケーション固有のメトリクスも検討</li>
<li><strong>behaviorによる動作制御</strong>：急激なスケーリングを防ぎ、安定した運用を実現</li>
<li><strong>PDBとの組み合わせ</strong>：スケールダウン時のサービス断を防止</li>
<li><strong>事前の負荷テスト</strong>：本番投入前にスケーリング動作を検証</li>
</ol>
<p>オートスケーリングは設定して終わりではありません。定期的にメトリクスを確認し、実際の負荷パターンに合わせて調整を続けることが、効率的なKubernetes運用の鍵となります。</p>
]]></content:encoded>
      <category>Kubernetes</category>
      <category>インフラ</category>
      <category>Kubernetes</category>
      <category>HPA</category>
      <category>VPA</category>
      <category>オートスケーリング</category>
      <category>インフラ</category>
      <category>クラウドネイティブ</category>
    </item>
    <item>
      <title>Kyvernoで始めるKubernetes Admission Policy実践: 事故を減らすポリシー設計プレイブック</title>
      <link>https://www.ai2core.com/posts/2026-03-06-kubernetes-admission-policy-kyverno-playbook/</link>
      <pubDate>Fri, 06 Mar 2026 09:05:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-03-06-kubernetes-admission-policy-kyverno-playbook/</guid>
      <description>KubernetesでKyvernoを使い、現場で運用可能なAdmission Policyを段階導入するための実装手順とトラブル対応を具体例付きで解説。</description>
      <content:encoded><![CDATA[<h1 id="kyvernoで始めるkubernetes-admission-policy実践-事故を減らすポリシー設計プレイブック">Kyvernoで始めるKubernetes Admission Policy実践: 事故を減らすポリシー設計プレイブック</h1>
<p>Kubernetes運用で一番つらい事故は、クラスタが壊れるよりも「本来防げたはずのミスがそのまま本番へ入る」ことです。たとえば、<code>latest</code> タグのイメージが本番に入り再現不能になる、<code>resources</code> 未設定でノードが詰まる、<code>privileged</code> コンテナが混入する。これらは人の注意力だけに依存すると必ず再発します。</p>
<p>そこで有効なのが Admission Policy（入場制御）です。本記事では <strong>Kyverno</strong> を使って、現場で本当に運用できるポリシー群を段階導入する手順をまとめます。単なる「denyの例」ではなく、監査→警告→強制の移行、例外管理、CI連携まで含めて解説します。</p>
<h2 id="1-なぜkyvernoなのか">1. なぜKyvernoなのか</h2>
<p>OPA Gatekeeper も強力ですが、Kyvernoは以下の特徴があり、初期導入が比較的スムーズです。</p>
<ul>
<li>YAML中心で書ける（Rego学習コストを後回しにしやすい）</li>
<li>validate / mutate / generate / verifyImages を一貫して扱える</li>
<li>PolicyReportにより違反可視化がしやすい</li>
<li>Pod SecurityやSupply Chain対策との相性が良い</li>
</ul>
<p>「まずルールを回し始める」目的なら、Kyvernoは現実的な選択肢です。</p>
<h2 id="2-先に決めるべき設計原則">2. 先に決めるべき設計原則</h2>
<p>導入前に、以下だけは先に決めておきます。</p>
<ol>
<li><strong>導入フェーズ</strong>: <code>Audit</code> → <code>Enforce</code> を基本にする</li>
<li><strong>責任分界</strong>: プラットフォームチームが共通ポリシー、各チームがアプリ固有例外</li>
<li><strong>例外の期限</strong>: 永久例外は禁止。期限付きで必ず棚卸し</li>
<li><strong>観測性</strong>: 違反数・対象Namespace・上位違反ルールをダッシュボード化</li>
</ol>
<p>この原則なしにルールだけ増やすと、運用が破綻します。</p>
<h2 id="3-最小導入手順3060分">3. 最小導入手順（30〜60分）</h2>
<h3 id="31-kyvernoのインストール">3.1 Kyvernoのインストール</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">9
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>helm repo add kyverno https://kyverno.github.io/kyverno/
</span></span><span style="display:flex;"><span>helm repo update
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>helm upgrade --install kyverno kyverno/kyverno <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  -n kyverno --create-namespace <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --set admissionController.replicas<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --set backgroundController.replicas<span style="color:#f92672">=</span><span style="color:#ae81ff">2</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --set cleanupController.replicas<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  --set reportsController.replicas<span style="color:#f92672">=</span><span style="color:#ae81ff">1</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>本番では可用性のため、admission/backgroundは最低2レプリカ推奨です。</p>
<h3 id="32-まずはauditモードで3ルール">3.2 まずはAuditモードで3ルール</h3>
<p>最初に効くルールは、次の3つです。</p>
<ul>
<li>イメージタグに <code>latest</code> を禁止</li>
<li>CPU/Memory requests/limits必須</li>
<li><code>privileged: true</code> を禁止</li>
</ul>
<p>例: <code>latest</code> タグ禁止</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">kyverno.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">ClusterPolicy</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">disallow-latest-tag</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">validationFailureAction</span>: <span style="color:#ae81ff">Audit</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">background</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rules</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">validate-image-tag</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">match</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">any</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">kinds</span>:
</span></span><span style="display:flex;"><span>                - <span style="color:#ae81ff">Pod</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">validate</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">message</span>: <span style="color:#e6db74">&#34;latestタグは禁止です。固定タグまたはdigestを使用してください。&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">foreach</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#f92672">list</span>: <span style="color:#e6db74">&#34;request.object.spec.containers&#34;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">deny</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">conditions</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">any</span>:
</span></span><span style="display:flex;"><span>                  - <span style="color:#f92672">key</span>: <span style="color:#e6db74">&#34;{{ element.image }}&#34;</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">operator</span>: <span style="color:#ae81ff">Matches</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;.*:latest$&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="33-レポートで現状把握">3.3 レポートで現状把握</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl get policyreport -A
</span></span><span style="display:flex;"><span>kubectl get clusterpolicy
</span></span><span style="display:flex;"><span>kubectl describe clusterpolicy disallow-latest-tag
</span></span></code></pre></td></tr></table>
</div>
</div><p>導入直後は違反が大量に出るのが普通です。ここで「Kyvernoが厳しすぎる」と判断しないでください。違反は“見えていなかった負債”です。</p>
<h2 id="4-実務で効くルールセット具体例">4. 実務で効くルールセット（具体例）</h2>
<h3 id="41-リソース未設定防止">4.1 リソース未設定防止</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">kyverno.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">ClusterPolicy</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">require-resources</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">validationFailureAction</span>: <span style="color:#ae81ff">Audit</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rules</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">check-resources</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">match</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">any</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">kinds</span>: [<span style="color:#e6db74">&#34;Pod&#34;</span>]
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">validate</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">message</span>: <span style="color:#e6db74">&#34;全コンテナにrequests/limitsを設定してください。&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">pattern</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">containers</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">cpu</span>: <span style="color:#e6db74">&#34;?*&#34;</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">memory</span>: <span style="color:#e6db74">&#34;?*&#34;</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">limits</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">cpu</span>: <span style="color:#e6db74">&#34;?*&#34;</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#f92672">memory</span>: <span style="color:#e6db74">&#34;?*&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="42-特権設定防止">4.2 特権設定防止</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">kyverno.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">ClusterPolicy</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">disallow-privileged</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">validationFailureAction</span>: <span style="color:#ae81ff">Enforce</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">rules</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#66d9ef">no</span>-<span style="color:#ae81ff">privileged</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">match</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">any</span>:
</span></span><span style="display:flex;"><span>          - <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">kinds</span>: [<span style="color:#e6db74">&#34;Pod&#34;</span>]
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">validate</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">message</span>: <span style="color:#e6db74">&#34;privilegedコンテナは禁止です。&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">pattern</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">containers</span>:
</span></span><span style="display:flex;"><span>              - <span style="color:#f92672">securityContext</span>:
</span></span><span style="display:flex;"><span>                  <span style="color:#f92672">=(privileged)</span>: <span style="color:#e6db74">&#34;false&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="43-digest固定の推奨supply-chain">4.3 Digest固定の推奨（Supply Chain）</h3>
<p>本来は digest 固定が理想です。移行期は <code>Audit</code> で始め、違反率が下がってから <code>Enforce</code> に切り替えます。</p>
<h2 id="5-auditからenforceへ移行する基準">5. AuditからEnforceへ移行する基準</h2>
<p>「何となく」で切り替えると炎上します。次の客観指標を使うと安全です。</p>
<ul>
<li>直近14日で対象ポリシー違反率が5%未満</li>
<li>主要Namespace（prod/stg/shared）で違反ゼロ</li>
<li>例外申請フロー（Issue/PRテンプレート）が整備済み</li>
<li>当番がトラブル時に切り戻し手順を理解している</li>
</ul>
<p><code>validationFailureAction</code> を一括で上げるのではなく、ルール単位・Namespace単位で段階化するのがポイントです。</p>
<h2 id="6-例外運用のテンプレート">6. 例外運用のテンプレート</h2>
<p>ポリシー導入で最も壊れるのは「例外管理」です。おすすめは以下。</p>
<ul>
<li>例外は <code>PolicyException</code> で明示</li>
<li>期限（例: 14日）を必須にする</li>
<li>チケット番号をannotationで必須化</li>
<li>期限切れを毎日バッチで通知</li>
</ul>
<p>例外YAMLに <code>expires</code> と <code>owner</code> を必須化すると、放置率が一気に下がります。</p>
<h2 id="7-ciに組み込んで本番前に落とす">7. CIに組み込んで“本番前に落とす”</h2>
<p>クラスタ投入時に拒否されるより、PR段階で検知される方が開発体験は良いです。<code>kyverno-cli</code> をCIで実行します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kyverno apply policies/ -r manifests/ --audit-warn
</span></span></code></pre></td></tr></table>
</div>
</div><p>GitHub Actions例:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Validate manifests with Kyverno</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">run</span>: |<span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    kyverno version
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">    kyverno apply ./policies -r ./k8s --audit-warn</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>これで「マージ後に初めて失敗する」パターンを減らせます。</p>
<h2 id="8-よくある障害と対処">8. よくある障害と対処</h2>
<h3 id="症状1-正常なpodまで拒否される">症状1: 正常なPodまで拒否される</h3>
<ul>
<li>match条件が広すぎる可能性</li>
<li><code>exclude</code> で <code>kube-system</code> や監視系を一時除外</li>
<li>まずAuditで影響範囲を確認してからEnforceへ</li>
</ul>
<h3 id="症状2-admission-timeoutでデプロイ遅延">症状2: Admission timeoutでデプロイ遅延</h3>
<ul>
<li>Kyvernoコンポーネントのリソース不足を確認</li>
<li>policy数が多い場合、ルール統合とmatch最適化</li>
<li>API Serverとのネットワーク遅延確認</li>
</ul>
<h3 id="症状3-レポートは出るが改善が進まない">症状3: レポートは出るが改善が進まない</h3>
<ul>
<li>違反上位3ルールに絞ってKPI化</li>
<li>チーム別に違反件数を見える化</li>
<li>例外を期限付きに強制</li>
</ul>
<h2 id="9-運用を続けるためのダッシュボード指標">9. 運用を続けるためのダッシュボード指標</h2>
<p>最低限、以下を可視化してください。</p>
<ul>
<li>ルール別違反件数（7日移動平均）</li>
<li>Namespace別違反件数</li>
<li><code>Audit</code> と <code>Enforce</code> の比率</li>
<li>期限切れ例外の件数</li>
<li>deploy失敗要因のうちpolicy起因の割合</li>
</ul>
<p>これを見ないと、ポリシーは「導入しただけ」で止まります。</p>
<h2 id="10-実践ロードマップ最初の4週間">10. 実践ロードマップ（最初の4週間）</h2>
<ul>
<li><strong>Week 1</strong>: Kyverno導入、3ルールをAuditで開始</li>
<li><strong>Week 2</strong>: 違反上位を改善、例外テンプレート導入</li>
<li><strong>Week 3</strong>: 一部NamespaceでEnforce化</li>
<li><strong>Week 4</strong>: CI連携完了、SLOに違反率を組み込み</li>
</ul>
<p>4週間で「人頼みのレビュー文化」から「仕組みで防ぐ文化」へ移行できます。</p>
<h2 id="まとめ">まとめ</h2>
<p>Kyverno導入の本質は、Kubernetesを縛ることではなく、<strong>再発するミスを設計で減らすこと</strong>です。</p>
<ul>
<li>最初はAuditで可視化</li>
<li>指標を持って段階的にEnforce</li>
<li>例外は期限付きで管理</li>
<li>CIに前倒し検知を組み込む</li>
</ul>
<p>この4点を守れば、ポリシーは“開発を止める壁”ではなく“事故を減らすガードレール”になります。まずは <code>latest</code> 禁止とリソース必須の2ルールから始めて、違反データを見ながら育てていきましょう。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>Kubernetes</category>
      <category>Kyverno</category>
      <category>Security</category>
      <category>Platform Engineering</category>
      <category>Policy as Code</category>
    </item>
    <item>
      <title>Kubernetes環境でDBスキーマ変更を止めずに進める：ゼロダウンタイム移行の実践戦略</title>
      <link>https://www.ai2core.com/posts/2026-03-05-kubernetes-zero-downtime-db-migration-strategy/</link>
      <pubDate>Thu, 05 Mar 2026 09:16:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-03-05-kubernetes-zero-downtime-db-migration-strategy/</guid>
      <description>本番KubernetesでDBマイグレーションを安全に実施するためのExpand-Contract戦略、手順、ロールバック設計を具体例つきで解説。</description>
      <content:encoded><![CDATA[<h1 id="kubernetes環境でdbスキーマ変更を止めずに進めるゼロダウンタイム移行の実践戦略">Kubernetes環境でDBスキーマ変更を止めずに進める：ゼロダウンタイム移行の実践戦略</h1>
<p>「カラムを追加するだけだから大丈夫」──この油断が、本番障害の入口になります。Kubernetes のように複数バージョンの Pod が同時に存在する環境では、DB スキーマ変更はアプリ変更よりも慎重に扱う必要があります。</p>
<p>本記事では、<strong>Expand-Contract パターン</strong>を中心に、ゼロダウンタイムを目指すための具体手順を解説します。実際の運用では、DDLの速さより「互換性のある期間をどう作るか」が勝負です。</p>
<h2 id="1-なぜkubernetesでdb移行が難しいのか">1. なぜKubernetesでDB移行が難しいのか</h2>
<p>Kubernetesでは、ローリングアップデート中に新旧Podが混在します。つまり次の状態が同時に発生します。</p>
<ul>
<li>新アプリは新スキーマを期待</li>
<li>旧アプリは旧スキーマしか知らない</li>
<li>DBは1つしかない</li>
</ul>
<p>このとき破綻するのが「破壊的変更を先に適用する」ケースです。たとえば旧カラムを即削除すると、旧Podがエラーを連発します。</p>
<h2 id="2-基本戦略expand--migrate--contract">2. 基本戦略：Expand → Migrate → Contract</h2>
<p>ゼロダウンタイム移行の原則はこの3段階です。</p>
<ol>
<li><strong>Expand</strong>: 互換性を壊さない変更を先に入れる（新カラム追加など）</li>
<li><strong>Migrate</strong>: アプリを段階的に切替え、データを移行する</li>
<li><strong>Contract</strong>: 旧仕様を最終削除する（十分な監視後）</li>
</ol>
<p>この順序なら、どの時点でも旧新どちらのアプリも動作可能にできます。</p>
<h2 id="3-具体例usersfull_name-を-first_name--last_name-へ分割">3. 具体例：<code>users.full_name</code> を <code>first_name</code> / <code>last_name</code> へ分割</h2>
<h3 id="31-expand-フェーズ">3.1 Expand フェーズ</h3>
<p>まず破壊的でないDDLを適用します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> users <span style="color:#66d9ef">ADD</span> <span style="color:#66d9ef">COLUMN</span> first_name text;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> users <span style="color:#66d9ef">ADD</span> <span style="color:#66d9ef">COLUMN</span> last_name text;
</span></span></code></pre></td></tr></table>
</div>
</div><p>この時点で旧アプリは <code>full_name</code> を使い続けられます。新アプリは新カラムに対応した実装を持っていても、まだ必須にしません。</p>
<h3 id="32-アプリを両対応にする">3.2 アプリを「両対応」にする</h3>
<p>書き込み時は両方へ保存（dual write）し、読み込み時は新カラム優先 + 旧カラムフォールバックにします。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">save_user_name</span>(user_id: str, full_name: str):
</span></span><span style="display:flex;"><span>    first, last <span style="color:#f92672">=</span> split_name(full_name)
</span></span><span style="display:flex;"><span>    db<span style="color:#f92672">.</span>execute(
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        UPDATE users
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        SET full_name = </span><span style="color:#e6db74">%s</span><span style="color:#e6db74">,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            first_name = </span><span style="color:#e6db74">%s</span><span style="color:#e6db74">,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">            last_name = </span><span style="color:#e6db74">%s</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        WHERE id = </span><span style="color:#e6db74">%s</span><span style="color:#e6db74">
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">        &#34;&#34;&#34;</span>,
</span></span><span style="display:flex;"><span>        (full_name, first, last, user_id),
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">read_user_display_name</span>(row):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> row<span style="color:#f92672">.</span>first_name <span style="color:#f92672">and</span> row<span style="color:#f92672">.</span>last_name:
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;</span><span style="color:#e6db74">{</span>row<span style="color:#f92672">.</span>first_name<span style="color:#e6db74">}</span><span style="color:#e6db74"> </span><span style="color:#e6db74">{</span>row<span style="color:#f92672">.</span>last_name<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> row<span style="color:#f92672">.</span>full_name
</span></span></code></pre></td></tr></table>
</div>
</div><p>この両対応期間を作るのが、ゼロダウンタイムの本質です。</p>
<h3 id="33-バックフィル既存データ移行">3.3 バックフィル（既存データ移行）</h3>
<p>大規模テーブルでは一括更新を避け、チャンク更新します。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">7
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#75715e">-- 疑似コードイメージ
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">UPDATE</span> users
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> first_name <span style="color:#f92672">=</span> split_part(full_name, <span style="color:#e6db74">&#39; &#39;</span>, <span style="color:#ae81ff">1</span>),
</span></span><span style="display:flex;"><span>    last_name <span style="color:#f92672">=</span> split_part(full_name, <span style="color:#e6db74">&#39; &#39;</span>, <span style="color:#ae81ff">2</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> id <span style="color:#f92672">&gt;</span> :last_id
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">AND</span> id <span style="color:#f92672">&lt;=</span> :last_id <span style="color:#f92672">+</span> <span style="color:#ae81ff">10000</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">AND</span> (first_name <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">OR</span> last_name <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span>);
</span></span></code></pre></td></tr></table>
</div>
</div><p>ジョブ実装時のポイント:</p>
<ul>
<li>1チャンクごとに commit</li>
<li>再開可能なチェックポイント（last_id）を保存</li>
<li>実行時間帯を制御（ピーク時間回避）</li>
</ul>
<h3 id="34-contract-フェーズ">3.4 Contract フェーズ</h3>
<p>全Podが新実装になり、フォールバックが不要と判断できたら旧カラム削除へ進みます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> users <span style="color:#66d9ef">DROP</span> <span style="color:#66d9ef">COLUMN</span> full_name;
</span></span></code></pre></td></tr></table>
</div>
</div><p>削除は必ず最後です。ここを急ぐとロールバック不能になります。</p>
<h2 id="4-マイグレーション実行方式の選び方">4. マイグレーション実行方式の選び方</h2>
<p>Kubernetesでは主に3パターンがあります。</p>
<h3 id="a-cicdで先行実行推奨">A. CI/CDで先行実行（推奨）</h3>
<p>デプロイ前に migration Job を走らせ、成功後にアプリを更新。</p>
<ul>
<li>メリット: 実行順序を固定しやすい</li>
<li>デメリット: 長時間 migration の扱いが難しい</li>
</ul>
<h3 id="b-initcontainer-実行">B. initContainer 実行</h3>
<p>Pod起動時に migration を走らせる方式。</p>
<ul>
<li>メリット: 実装が単純</li>
<li>デメリット: 複数Pod同時起動で競合しやすい</li>
</ul>
<h3 id="c-専用-migration-controller--job">C. 専用 Migration Controller / Job</h3>
<p>Argo Workflows などで明示的に管理。</p>
<ul>
<li>メリット: 大規模運用で監査しやすい</li>
<li>デメリット: 初期構築コストが高い</li>
</ul>
<p>中規模までなら A が最も事故が少ないです。</p>
<h2 id="5-alembicflyway運用の実務ポイント">5. Alembic/Flyway運用の実務ポイント</h2>
<h3 id="51-1-migration--1責務">5.1 1 migration = 1責務</h3>
<p>「追加 + データ移行 + 削除」を1ファイルに詰め込むと失敗時に戻しづらくなります。Expand/Migrate/Contract を別 migration に分けて、各段階を検証可能にしてください。</p>
<h3 id="52-lock-timeout-を設定する">5.2 lock timeout を設定する</h3>
<p>DDL はロック待ちでアプリを止めることがあります。PostgreSQL なら <code>lock_timeout</code> を設定して、危険な待機を避けます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SET</span> lock_timeout <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;5s&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> ...;
</span></span></code></pre></td></tr></table>
</div>
</div><p>失敗時は即中断し、メンテナンス時間帯に再実行する判断が可能です。</p>
<h3 id="53-破壊的変更前に計測窓を設ける">5.3 破壊的変更前に計測窓を設ける</h3>
<p>旧カラム参照がゼロになったことを、メトリクスやログで一定期間確認してから削除します。目安として 7〜14日程度の観測期間を取ると安全です。</p>
<h2 id="6-ロールバック戦略">6. ロールバック戦略</h2>
<p>ゼロダウンタイム設計では、ロールバック可能性を最初に決めます。</p>
<ul>
<li>Expand段階: ほぼ即ロールバック可能</li>
<li>Migrate段階: dual write を維持していればロールバック可能</li>
<li>Contract段階: 削除後は復元コスト高（バックアップ依存）</li>
</ul>
<p>したがって、Contract 実施前に以下を満たす必要があります。</p>
<ul>
<li>直近スナップショット取得済み</li>
<li>復元手順を演習済み</li>
<li>影響範囲（API・バッチ・BI）を棚卸し済み</li>
</ul>
<h2 id="7-監視項目と成功判定">7. 監視項目と成功判定</h2>
<p>移行中は「成功したか」より「安全に進んでいるか」を見ます。</p>
<ul>
<li>APIエラー率（4xx/5xx）</li>
<li>DBロック待ち時間</li>
<li>スロークエリ件数</li>
<li>migration ジョブ進捗（処理済み件数、残件数）</li>
<li>旧カラム参照回数</li>
</ul>
<p>成功判定の例:</p>
<ol>
<li>新旧Pod混在中のエラー率に有意な悪化なし</li>
<li>バックフィル完了率100%</li>
<li>旧カラム参照0が連続7日</li>
<li>Contract後24時間で異常なし</li>
</ol>
<h2 id="8-よくある失敗と回避策">8. よくある失敗と回避策</h2>
<h3 id="失敗1-非null制約を早く付けすぎる">失敗1: 非NULL制約を早く付けすぎる</h3>
<p>バックフィル完了前に <code>NOT NULL</code> を付けると、旧データで失敗します。まずは nullable で追加し、データ移行後に制約追加が正解です。</p>
<h3 id="失敗2-インデックス作成で書き込み停止">失敗2: インデックス作成で書き込み停止</h3>
<p>大きいテーブルで通常インデックス作成を行うとロックが重くなります。PostgreSQL では <code>CREATE INDEX CONCURRENTLY</code> を使って影響を下げます。</p>
<h3 id="失敗3-migration-の実行主体が複数">失敗3: migration の実行主体が複数</h3>
<p>同時に2つのJobが走ると競合します。Kubernetes Job は排他制御（Lease/Lock）を持たせるか、CI側で単一実行を保証してください。</p>
<h2 id="9-実運用テンプレートmigration-job-と段階リリース">9. 実運用テンプレート：Migration Job と段階リリース</h2>
<p>最後に、現場でそのまま使える最小テンプレートを示します。ポイントは「DDLを一気にやらない」「アプリ側フラグで読取切替を制御する」の2点です。</p>
<h3 id="91-migration-jobexpand専用">9.1 Migration Job（Expand専用）</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">batch/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Job</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">users-expand-20260305</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">backoffLimit</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">template</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">restartPolicy</span>: <span style="color:#ae81ff">Never</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">containers</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">migrate</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">image</span>: <span style="color:#ae81ff">ghcr.io/example/api:1.42.0</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">command</span>: [<span style="color:#e6db74">&#34;bash&#34;</span>, <span style="color:#e6db74">&#34;-lc&#34;</span>, <span style="color:#e6db74">&#34;alembic upgrade 20260305_expand_users_name&#34;</span>]
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">envFrom</span>:
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">secretRef</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#f92672">name</span>: <span style="color:#ae81ff">api-db-secret</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Expand 用 Job を分離しておけば、失敗時にアプリデプロイを止める判断が明確になります。<code>backoffLimit: 0</code> にして「失敗を隠さない」運用にするのも実務で有効です。</p>
<h3 id="92-段階リリース手順例">9.2 段階リリース手順（例）</h3>
<ol>
<li>Expand Job 実行（DDLのみ）</li>
<li>アプリ v1（dual write + fallback read）を10%配信</li>
<li>エラー率・ロック待ちを30分監視</li>
<li>50% → 100%へ段階拡大</li>
<li>バックフィル Job 実行</li>
<li>旧参照ゼロ確認後に Contract 実施</li>
</ol>
<p>Kubernetes では <code>kubectl rollout status deployment/api -n prod</code> を必ず監視に組み込み、配信完了判定を人間が明示的に確認する運用が安全です。</p>
<h2 id="まとめ">まとめ</h2>
<p>KubernetesでのDBマイグレーションは、DDLテクニックだけでは成功しません。重要なのは、</p>
<ul>
<li>互換性期間を意図的に作る</li>
<li>段階を分けて進める</li>
<li>ロールバック可能性を先に設計する</li>
<li>監視で「削除してよい」根拠を取る</li>
</ul>
<p>この4点です。Expand-Contract を守るだけで、移行の失敗率は目に見えて下がります。スキーマ変更は怖い作業ですが、手順化すれば再現可能な運用にできます。次回の変更から、ぜひこの流れで試してみてください。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>Kubernetes</category>
      <category>Database</category>
      <category>Migration</category>
      <category>SRE</category>
      <category>DevOps</category>
    </item>
    <item>
      <title>Kubernetesキャパシティ設計実践：HPA/VPA/Cluster Autoscalerを衝突させない運用術</title>
      <link>https://www.ai2core.com/posts/2026-03-03-kubernetes-hpa-vpa-capacity-tuning/</link>
      <pubDate>Tue, 03 Mar 2026 09:10:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-03-03-kubernetes-hpa-vpa-capacity-tuning/</guid>
      <description>Kubernetesのスケーリング機構を本番で安定運用するための設計、メトリクス選定、調整手順、障害時の確認ポイントを解説。</description>
      <content:encoded><![CDATA[<h1 id="kubernetesキャパシティ設計実践hpavpacluster-autoscalerを衝突させない運用術">Kubernetesキャパシティ設計実践：HPA/VPA/Cluster Autoscalerを衝突させない運用術</h1>
<p>Kubernetes は「自動でスケールするから安心」と思われがちですが、実運用では逆です。HPA、VPA、Cluster Autoscaler（CA）の設定が噛み合わないと、スケールアウトと再スケジューリングが衝突し、レイテンシ悪化やコスト増大を引き起こします。</p>
<p>本記事では、3つのオートスケーリング機構を同時運用する際の設計ポイントを、障害対応目線で整理します。</p>
<h2 id="1-役割分担を明確にする">1. 役割分担を明確にする</h2>
<p>まず前提として、各コンポーネントの責務を固定します。</p>
<ul>
<li>HPA: Pod 数を短期的に増減</li>
<li>VPA: Pod あたりの requests/limits を中長期で最適化</li>
<li>CA: ノード数を増減</li>
</ul>
<p>この役割分担が曖昧だと、同じ問題を複数レイヤーで同時に解こうとして不安定化します。特に Web/API ワークロードでは、<strong>HPA を主軸、VPA は recommendation 中心</strong>で始めるのが安全です。</p>
<h2 id="2-requestslimits-が崩れていると全て失敗する">2. requests/limits が崩れていると全て失敗する</h2>
<p>HPA の CPU 指標は requests 基準で計算されます。requests が不正確だと、HPA の判断もズレます。最初にやるべきは次です。</p>
<ol>
<li>過去 2 週間の実使用量を可視化</li>
<li>p95 使用量を requests の初期値に設定</li>
<li>limits は requests の 1.5〜2 倍で開始</li>
</ol>
<p>極端に低い requests は「見かけの高負荷」を作り、不要スケールを誘発します。逆に高すぎる requests は CA の過剰増設を招きます。</p>
<h2 id="3-hpa-指標選定の実践">3. HPA 指標選定の実践</h2>
<p>CPU だけで運用すると、I/O 待ちや外部 API 待ちのボトルネックを見逃します。推奨は複合指標です。</p>
<ul>
<li>CPU Utilization（基本）</li>
<li>メモリ使用率（リーク監視）</li>
<li>RPS あたりレイテンシ（SLO 接続）</li>
<li>Queue 長（非同期処理）</li>
</ul>
<p><code>autoscaling/v2</code> では複数メトリクスを扱えるため、最初から設計しておくと後で楽です。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">autoscaling/v2</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">HorizontalPodAutoscaler</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">api-hpa</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleTargetRef</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">api</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minReplicas</span>: <span style="color:#ae81ff">3</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">maxReplicas</span>: <span style="color:#ae81ff">30</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">metrics</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Resource</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">resource</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">cpu</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Utilization</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">averageUtilization</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Pods</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">pods</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">metric</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">http_requests_per_second</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">target</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">type</span>: <span style="color:#ae81ff">AverageValue</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">averageValue</span>: <span style="color:#e6db74">&#34;80&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="4-スケール挙動を安定化する">4. スケール挙動を安定化する</h2>
<p>HPA は設定次第で「増えすぎ・減りすぎ」を起こします。<code>behavior</code> を入れて振動を抑えます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">behavior</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleUp</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">value</span>: <span style="color:#ae81ff">100</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">60</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">scaleDown</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">stabilizationWindowSeconds</span>: <span style="color:#ae81ff">300</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">policies</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">type</span>: <span style="color:#ae81ff">Percent</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">value</span>: <span style="color:#ae81ff">20</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">periodSeconds</span>: <span style="color:#ae81ff">60</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>スケールアップは素早く、スケールダウンは慎重に、が基本です。障害時の復旧速度と平常時コストのバランスを取りやすくなります。</p>
<h2 id="5-vpa-の安全な導入順序">5. VPA の安全な導入順序</h2>
<p>VPA をいきなり <code>Auto</code> で入れると、Pod 再作成が頻発し、ピーク時間帯に影響することがあります。次の順序を推奨します。</p>
<ol>
<li><code>Off</code> で recommendation だけ収集</li>
<li>2〜4週間データ蓄積</li>
<li>非クリティカルなバッチ系から <code>Auto</code> 開始</li>
<li>API 系は <code>Initial</code> または手動反映を継続</li>
</ol>
<p>この段階導入にすると、VPA が既存 SLO を壊すリスクを抑えられます。</p>
<h2 id="6-hpa-と-vpa-の衝突回避">6. HPA と VPA の衝突回避</h2>
<p>同一 Deployment で CPU/Memory の両方を HPA と VPA が同時に強く制御すると、調整ループが競合します。現場では次の運用が現実的です。</p>
<ul>
<li>HPA: レプリカ数を制御（主）</li>
<li>VPA: requests 最適化（従）</li>
<li>クリティカルサービスは VPA recommendation を人間レビューして反映</li>
</ul>
<p>また、メモリで OOM が起こるサービスは、HPA より先にアプリ側リーク調査を優先します。スケールで隠すと後で必ず再発します。</p>
<h2 id="7-cluster-autoscaler-の盲点">7. Cluster Autoscaler の盲点</h2>
<p>CA は Pending Pod を見てノードを増やしますが、以下の条件で期待通り動きません。</p>
<ul>
<li>PDB が厳しすぎて eviction できない</li>
<li>NodeSelector/taint 制約で配置先がない</li>
<li>requests が過大で BinPacking 不可能</li>
</ul>
<p>スケールしない時の確認手順を runbook 化しておくと、夜間障害で迷いません。</p>
<ol>
<li>Pending Pod の events を確認</li>
<li>CA logs で scale-up decision を確認</li>
<li>node group の上限値と quota を確認</li>
<li>Pod 制約（affinity/taint/tolerations）を確認</li>
</ol>
<h2 id="8-典型障害シナリオと対処">8. 典型障害シナリオと対処</h2>
<h3 id="シナリオa-突発トラフィックで-5xx-増加">シナリオA: 突発トラフィックで 5xx 増加</h3>
<ul>
<li>兆候: CPU 90%超、pod 起動待ち</li>
<li>対処: HPA minReplicas を一時引き上げ、イメージ pull 最適化、readiness 調整</li>
<li>恒久策: 予測ピーク前の scheduled scaling を導入</li>
</ul>
<h3 id="シナリオb-コスト急増">シナリオB: コスト急増</h3>
<ul>
<li>兆候: ノード数だけ増えて利用率低い</li>
<li>対処: requests 見直し、scaleDown stabilization 調整、CA consolidate 有効化</li>
<li>恒久策: ワークロード別の node pool 分離</li>
</ul>
<h3 id="シナリオc-断続的なタイムアウト">シナリオC: 断続的なタイムアウト</h3>
<ul>
<li>兆候: CPU 余裕あり、レイテンシ悪化</li>
<li>対処: 外部依存（DB/Redis/API）をトレースで確認</li>
<li>恒久策: HPA 指標に queue 長・レイテンシを追加</li>
</ul>
<h2 id="9-実運用のメトリクスセット">9. 実運用のメトリクスセット</h2>
<p>運用ダッシュボードには、最低限次を置きます。</p>
<ul>
<li>HPA current/desired replicas</li>
<li>Pod 起動時間（image pull + readiness）</li>
<li>Pod Pending 数と理由</li>
<li>Node 利用率（CPU/Memory）</li>
<li>CA scale up/down イベント数</li>
<li>5xx 率と p95 latency</li>
</ul>
<p>このセットがあると「スケーラが原因か、アプリが原因か」を 5 分以内で切り分けられます。</p>
<h2 id="10-段階的チューニング手順4週間">10. 段階的チューニング手順（4週間）</h2>
<h3 id="week-1-ベースライン計測">Week 1: ベースライン計測</h3>
<ul>
<li>現行 requests/limits と実使用量を比較</li>
<li>HPA 閾値を仮設定（CPU 60%）</li>
</ul>
<h3 id="week-2-振動抑制">Week 2: 振動抑制</h3>
<ul>
<li>behavior 追加</li>
<li>scaleDown window を 300s 前後で調整</li>
</ul>
<h3 id="week-3-指標追加">Week 3: 指標追加</h3>
<ul>
<li>RPS/queue 指標を導入</li>
<li>アラートを SLO 連動へ変更</li>
</ul>
<h3 id="week-4-コスト最適化">Week 4: コスト最適化</h3>
<ul>
<li>idle 時の minReplicas 見直し</li>
<li>node pool の統廃合</li>
</ul>
<p>この 4 週間を回すだけでも、無駄スケールと障害時復旧の両方が改善します。</p>
<h2 id="11-導入チェックリスト">11. 導入チェックリスト</h2>
<ul>
<li><input disabled="" type="checkbox"> requests/limits が実使用量に基づいている</li>
<li><input disabled="" type="checkbox"> HPA に <code>behavior</code> を設定済み</li>
<li><input disabled="" type="checkbox"> VPA は recommendation 期間を経て適用している</li>
<li><input disabled="" type="checkbox"> CA の上限値・quota・制約を定期点検している</li>
<li><input disabled="" type="checkbox"> SLO 指標とスケーリング指標を接続している</li>
<li><input disabled="" type="checkbox"> 典型障害シナリオの runbook がある</li>
</ul>
<p>Kubernetes のスケーリングは、機能を有効にするだけでは安定しません。役割分担、指標設計、調整ループの制御、そして runbook をセットで整えることで、初めて「自動化が味方になる」状態を作れます。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>Kubernetes</category>
      <category>HPA</category>
      <category>VPA</category>
      <category>SRE</category>
      <category>Platform Engineering</category>
    </item>
    <item>
      <title>Kubernetes障害対応ランブック実践編：15分で切り分け、60分で復旧するための現場手順</title>
      <link>https://www.ai2core.com/posts/2026-03-01-kubernetes-incident-response-runbook/</link>
      <pubDate>Sun, 01 Mar 2026 09:05:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-03-01-kubernetes-incident-response-runbook/</guid>
      <description>Kubernetes本番障害を短時間で切り分け・復旧するための実践ランブック。初動、調査コマンド、復旧判断、再発防止まで具体例付きで解説。</description>
      <content:encoded><![CDATA[<h1 id="kubernetes障害対応ランブック実践編15分で切り分け60分で復旧するための現場手順">Kubernetes障害対応ランブック実践編：15分で切り分け、60分で復旧するための現場手順</h1>
<p>Kubernetes で障害が起きたとき、技術力より先に問われるのは「順序」です。順序が崩れると、正しいコマンドを打っていても復旧が遅れます。逆に、判断フレームが決まっていれば、難しい障害でも被害を最小化できます。</p>
<p>本記事では、実運用で使える障害対応ランブックを、<strong>初動 15 分・復旧 60 分</strong>を目安に具体化します。対象は EKS/GKE/AKS を含む一般的な Kubernetes 本番環境です。</p>
<h2 id="1-最初の5分人と情報の交通整理">1. 最初の5分：人と情報の交通整理</h2>
<p>まずやるべきは技術調査ではなく、交通整理です。</p>
<ul>
<li>インシデントの指揮者（IC）を1人決める</li>
<li>ログ担当、メトリクス担当、アプリ担当を分ける</li>
<li>Slack/Discord の専用チャンネルを作る</li>
<li>5分ごとの時系列ログ（タイムライン）を残す</li>
</ul>
<p>この段階で「誰が何を見ているか」が曖昧だと、同じ調査を3人で繰り返し、誰も復旧判断をしない状態になります。ICは手を動かさず、意思決定に集中するのが基本です。</p>
<h2 id="2-515分影響範囲の特定">2. 5〜15分：影響範囲の特定</h2>
<p>次に「どのユーザーに、どの機能で、何%の影響があるか」を確定します。</p>
<h3 id="2-1-まず全体の健康状態">2-1. まず全体の健康状態</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl get nodes
</span></span><span style="display:flex;"><span>kubectl get pods -A --field-selector<span style="color:#f92672">=</span>status.phase!<span style="color:#f92672">=</span>Running
</span></span><span style="display:flex;"><span>kubectl get events -A --sort-by<span style="color:#f92672">=</span>.lastTimestamp | tail -n <span style="color:#ae81ff">50</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>見るポイント:</p>
<ul>
<li>Node が <code>NotReady</code> になっていないか</li>
<li>特定 namespace だけで <code>CrashLoopBackOff</code> が増えていないか</li>
<li>直近イベントに <code>FailedScheduling</code> や <code>Back-off restarting failed container</code> がないか</li>
</ul>
<h3 id="2-2-slosliから影響を数値化">2-2. SLO/SLIから影響を数値化</h3>
<p>「遅い気がする」ではなく、以下を数値で押さえます。</p>
<ul>
<li>エラー率（5xx / total）</li>
<li>p95 / p99 レイテンシ</li>
<li>リクエスト成功率</li>
<li>影響時間（何時何分から）</li>
</ul>
<p>復旧優先順位は「売上影響」「決済影響」「ログイン影響」で並べると迷いにくいです。</p>
<h2 id="3-典型障害ごとの切り分けテンプレート">3. 典型障害ごとの切り分けテンプレート</h2>
<h3 id="3-1-podが再起動ループする">3-1. Podが再起動ループする</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl describe pod &lt;pod-name&gt; -n &lt;ns&gt;
</span></span><span style="display:flex;"><span>kubectl logs &lt;pod-name&gt; -n &lt;ns&gt; --previous
</span></span></code></pre></td></tr></table>
</div>
</div><p>判断軸:</p>
<ol>
<li><strong>アプリ起因</strong>（例: マイグレーション失敗、環境変数不足）</li>
<li><strong>リソース起因</strong>（OOMKill, CPU スロットリング）</li>
<li><strong>依存先起因</strong>（DB接続失敗、外部APIタイムアウト）</li>
</ol>
<p>OOMKill が見えたら、即座に requests/limits の見直し候補です。緊急回避として replicas を増やすより、まずメモリリークや一時的負荷スパイクの切り分けを優先します。</p>
<h3 id="3-2-podは生きているのに503が増える">3-2. Podは生きているのに503が増える</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl get endpoints -n &lt;ns&gt;
</span></span><span style="display:flex;"><span>kubectl describe svc &lt;service&gt; -n &lt;ns&gt;
</span></span><span style="display:flex;"><span>kubectl get deploy &lt;deploy&gt; -n &lt;ns&gt; -o yaml | grep -n <span style="color:#e6db74">&#34;readinessProbe\|livenessProbe&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>よくある原因:</p>
<ul>
<li>readinessProbe が厳しすぎて endpoint から外れ続ける</li>
<li>依存先劣化で app は起動しているが実処理が詰まる</li>
<li>HPA は増えているが、DB 接続プールが上限で飽和</li>
</ul>
<p>このケースでは「Pod数が多いのにエラーが減らない」ので、アプリ外（DB、キュー、外部API）を疑うのが早いです。</p>
<h3 id="3-3-特定ノードでのみ障害">3-3. 特定ノードでのみ障害</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl describe node &lt;node&gt;
</span></span><span style="display:flex;"><span>kubectl top node
</span></span><span style="display:flex;"><span>kubectl get pods -A -o wide | grep &lt;node&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p>ノード隔離が必要なら、以下で新規スケジュールを止めます。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl cordon &lt;node&gt;
</span></span><span style="display:flex;"><span>kubectl drain &lt;node&gt; --ignore-daemonsets --delete-emptydir-data
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>drain</code> は影響が大きい操作なので、IC の明示承認を挟むのが安全です。</p>
<h2 id="4-復旧アクションの優先順位実務向け">4. 復旧アクションの優先順位（実務向け）</h2>
<p>障害中は「恒久対策」を始めないこと。順番は固定します。</p>
<ol>
<li><strong>被害抑制</strong>: feature flag で重い機能を止める</li>
<li><strong>暫定復旧</strong>: 直前の安定リビジョンへロールバック</li>
<li><strong>性能確保</strong>: replicas/HPA 一時調整、キュー消化速度調整</li>
<li><strong>恒久対策</strong>: 根本修正は障害収束後にPRで実施</li>
</ol>
<h3 id="4-1-ロールバックの型を決めておく">4-1. ロールバックの型を決めておく</h3>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>kubectl rollout history deployment/&lt;deploy&gt; -n &lt;ns&gt;
</span></span><span style="display:flex;"><span>kubectl rollout undo deployment/&lt;deploy&gt; -n &lt;ns&gt; --to-revision<span style="color:#f92672">=</span>&lt;rev&gt;
</span></span><span style="display:flex;"><span>kubectl rollout status deployment/&lt;deploy&gt; -n &lt;ns&gt;
</span></span></code></pre></td></tr></table>
</div>
</div><p>ポイントは「戻す判断に迷わない基準」を事前に定義しておくことです。</p>
<ul>
<li>5分間で 5xx &gt; 2% 継続</li>
<li>p95 が通常比 2倍超を 10分継続</li>
<li>ログイン/決済失敗が監視閾値超え</li>
</ul>
<p>基準がないと、誰も責任を取りたくなくて引き延ばしになります。</p>
<h2 id="5-事後対応postmortemで必ず残すべきもの">5. 事後対応（Postmortem）で必ず残すべきもの</h2>
<p>障害対応の価値は、復旧速度だけではありません。再発率を下げて初めて完了です。</p>
<p>最低限残す項目:</p>
<ul>
<li>発生時刻、検知時刻、復旧時刻</li>
<li>ユーザー影響（件数、売上影響）</li>
<li>技術的原因（直接原因 / 背景要因）</li>
<li>うまくいった対応、失敗した判断</li>
<li>再発防止のアクション（担当者・期限付き）</li>
</ul>
<h3 id="5-1-アクションを監視に落とす">5-1. アクションを「監視」に落とす</h3>
<p>「次は気をつける」は効果ゼロです。必ず機械化します。</p>
<p>例:</p>
<ul>
<li><code>CrashLoopBackOff &gt; N件</code> を5分継続で即アラート</li>
<li>DB接続待ち時間の p95 をダッシュボード化</li>
<li>リリース直後30分は自動で高感度監視モード</li>
</ul>
<h2 id="6-そのまま使える障害対応チェックリスト">6. そのまま使える障害対応チェックリスト</h2>
<h3 id="初動チェック015分">初動チェック（0〜15分）</h3>
<ul>
<li><input disabled="" type="checkbox"> IC決定、連絡チャンネル作成</li>
<li><input disabled="" type="checkbox"> 影響範囲（機能・ユーザー・売上）を数値化</li>
<li><input disabled="" type="checkbox"> Node / Pod / Event の全体確認</li>
<li><input disabled="" type="checkbox"> 直近デプロイ有無の確認</li>
</ul>
<h3 id="復旧チェック1560分">復旧チェック（15〜60分）</h3>
<ul>
<li><input disabled="" type="checkbox"> 被害抑制（feature flag / トラフィック制御）</li>
<li><input disabled="" type="checkbox"> ロールバック判断基準を満たすか確認</li>
<li><input disabled="" type="checkbox"> 復旧後 15 分の再監視（再発確認）</li>
<li><input disabled="" type="checkbox"> ユーザー向けステータス更新</li>
</ul>
<h3 id="事後チェック24時間以内">事後チェック（24時間以内）</h3>
<ul>
<li><input disabled="" type="checkbox"> Postmortem 作成</li>
<li><input disabled="" type="checkbox"> 再発防止チケット発行</li>
<li><input disabled="" type="checkbox"> 監視ルール・Runbook 更新</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>Kubernetes 障害対応は、個人技で勝つゲームではありません。<strong>誰でも同じ順序で動ける運用設計</strong>が最も効きます。特に「初動の交通整理」「ロールバック基準の明文化」「事後の監視自動化」の3点は、復旧時間と再発率を同時に改善します。</p>
<p>次の障害が来たときに迷わないよう、今日のうちにこのランブックを自チーム用に1ページ化しておくことを強くおすすめします。</p>
<h2 id="7-障害を早く終わらせるための役割分担テンプレート">7. 障害を早く終わらせるための役割分担テンプレート</h2>
<p>実際の現場では、技術調査よりコミュニケーションで詰まることが多いです。そこで、あらかじめ役割を固定しておくと復旧が速くなります。</p>
<ul>
<li><strong>IC（Incident Commander）</strong>: 優先順位決定、Go/No-Go判断、対外連絡承認</li>
<li><strong>Ops Lead</strong>: クラスタ状態確認、ロールバック実行、インフラ変更の安全確認</li>
<li><strong>App Lead</strong>: アプリログ解析、機能フラグ切り替え、暫定パッチ判断</li>
<li><strong>Comms</strong>: 社内・顧客向けステータス更新、問い合わせ窓口統一</li>
</ul>
<p>この分担があると、「調査担当が勝手に危険操作を実施する」「連絡が多重化して混乱する」といった二次被害を防げます。</p>
<h2 id="8-よくあるアンチパターンと回避策">8. よくあるアンチパターンと回避策</h2>
<h3 id="アンチパターン1-まず再デプロイしてしまう">アンチパターン1: まず再デプロイしてしまう</h3>
<p>原因が不明なまま再デプロイすると、証拠ログを失い再現不能になります。最低限 <code>events</code> と <code>previous logs</code> を保存してから操作します。</p>
<h3 id="アンチパターン2-たぶん直ったでクローズする">アンチパターン2: 「たぶん直った」でクローズする</h3>
<p>復旧後 15 分の監視を省くと、同じ障害が波状的に再発します。復旧宣言は「指標が閾値内で安定した」ことを条件にします。</p>
<h3 id="アンチパターン3-障害後の改善が担当者依存">アンチパターン3: 障害後の改善が担当者依存</h3>
<p>改善をドキュメント化せず担当者の記憶に任せると、同じ夜間障害を繰り返します。Runbook への反映までをインシデント完了条件に含めるべきです。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>Kubernetes</category>
      <category>SRE</category>
      <category>Incident Response</category>
      <category>Runbook</category>
    </item>
    <item>
      <title>Kubernetesコスト最適化の実務：FinOps視点で無駄を可視化し、性能を落とさず削減する方法</title>
      <link>https://www.ai2core.com/posts/2026-02-27-kubernetes-finops-cost-optimization/</link>
      <pubDate>Fri, 27 Feb 2026 18:30:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-27-kubernetes-finops-cost-optimization/</guid>
      <description>Kubernetes環境で発生しがちな無駄コストを、計測・改善・運用ルールの3段階で削減する具体的な実践ガイド。</description>
      <content:encoded><![CDATA[<h1 id="kubernetesコスト最適化の実務finops視点で無駄を可視化し性能を落とさず削減する方法">Kubernetesコスト最適化の実務：FinOps視点で無駄を可視化し、性能を落とさず削減する方法</h1>
<p>Kubernetes を導入した直後は「自動で効率化される」と期待されがちですが、実際には逆です。適切な運用ルールがないと、クラウド請求は静かに膨らみ続けます。</p>
<p>典型例は次の通りです。</p>
<ul>
<li>リクエスト/リミットが過大でノードが常時スカスカ</li>
<li>夜間トラフィック減少時も同じ台数を維持</li>
<li>開発環境が週末ずっと起動</li>
<li>HPA が CPU しか見ておらず、キュー滞留を無視</li>
<li>過剰なストレージクラス（IOPS課金）を全環境で使用</li>
</ul>
<p>本記事では、FinOps の考え方を用いて、Kubernetesコストを「見える化→改善→定着」の順に進める方法を解説します。</p>
<h2 id="コスト削減の前に定義すべき指標">コスト削減の前に定義すべき指標</h2>
<p>削減施策が失敗する理由は、目標が「安くする」しかないからです。最低でも次の 4 指標を定義します。</p>
<ol>
<li><strong>Unit Cost</strong>: 1リクエストあたりコスト / 1ジョブあたりコスト</li>
<li><strong>Utilization</strong>: CPU・Memoryの実効使用率</li>
<li><strong>Reliability</strong>: SLO 達成率（削減で品質を落とさない）</li>
<li><strong>Waste Ratio</strong>: 予約/実使用の差分率</li>
</ol>
<p>この4つを同時に追うと、単なるコストカットでなく「健全な最適化」になります。</p>
<h2 id="ステップ1可視化基盤を整える">ステップ1：可視化基盤を整える</h2>
<h3 id="1-1-kubecost-または-opencost-の導入">1-1. Kubecost または OpenCost の導入</h3>
<p>EKS/GKE/AKS いずれでも、namespace / deployment / label 単位で費用を把握できる状態にします。重要なのは <code>team</code>, <code>service</code>, <code>env</code> ラベルを強制することです。</p>
<p>推奨ラベル:</p>
<ul>
<li><code>cost-center</code></li>
<li><code>owner</code></li>
<li><code>environment</code>（prod/stg/dev）</li>
<li><code>criticality</code></li>
</ul>
<p>ラベルがないと請求分析ができず、改善責任が曖昧になります。</p>
<h3 id="1-2-観測ダッシュボード">1-2. 観測ダッシュボード</h3>
<p>最初のダッシュボードは次を必須にします。</p>
<ul>
<li>Namespace別コスト（当日/前日比）</li>
<li>Pod別 request/usage ギャップ</li>
<li>ノード空き率（CPU、Memory）</li>
<li>時間帯別トラフィックとレプリカ数</li>
</ul>
<p>ここまでで「どこが高いか」は見えます。次は「なぜ高いか」を潰します。</p>
<h2 id="ステップ2最も効果が高い改善施策">ステップ2：最も効果が高い改善施策</h2>
<h3 id="2-1-right-sizing最優先">2-1. Right Sizing（最優先）</h3>
<p>多くの環境で最大効果が出るのは request/limit の見直しです。VPA recommendation を参考に、まずは read-only で 2 週間観測します。</p>
<p>例:</p>
<ul>
<li>現在: <code>requests.cpu=1000m</code>, 実使用 P95 が 220m</li>
<li>改善後: <code>requests.cpu=300m</code></li>
</ul>
<p>この1変更だけでノード数が 20〜35% 下がるケースがあります。</p>
<h3 id="2-2-cluster-autoscaler--karpenter-最適化">2-2. Cluster Autoscaler / Karpenter 最適化</h3>
<p>オートスケーラーを入れていても、設定不備で縮退しないことが多いです。次をチェックします。</p>
<ul>
<li>scale down unneeded time が長すぎないか</li>
<li>pod disruption budget が硬すぎないか</li>
<li>daemonset が過剰にリソースを固定していないか</li>
</ul>
<p>Karpenter なら Spot + On-Demand の比率制御を導入し、重要ワークロードのみ On-Demand 固定にします。</p>
<h3 id="2-3-ワークロードの時間制御">2-3. ワークロードの時間制御</h3>
<p>開発・検証環境はスケジュール停止が有効です。平日 9:00-20:00 だけ稼働し、夜間/休日は scale to zero。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># 例: CronJobで夜間停止</span>
</span></span><span style="display:flex;"><span>kubectl scale deployment api-staging --replicas<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> -n staging
</span></span></code></pre></td></tr></table>
</div>
</div><p>単純ですが、月額で大きく効きます。</p>
<h3 id="2-4-hpa指標の改善">2-4. HPA指標の改善</h3>
<p>CPU のみでスケールすると、I/O 待ちやキュー詰まりを見逃します。Prometheus Adapter を使い、次の指標を追加します。</p>
<ul>
<li>queue length</li>
<li>request latency P95</li>
<li>in-flight request</li>
</ul>
<p>結果として「必要な時だけ増やす」が実現し、過剰常時稼働を減らせます。</p>
<h2 id="ステップ3ストレージネットワークの見直し">ステップ3：ストレージ/ネットワークの見直し</h2>
<h3 id="ストレージ">ストレージ</h3>
<p>高IOPSディスクを全環境に使うのは典型的な浪費です。ワークロード特性でクラス分離します。</p>
<ul>
<li>DB本番: 高IOPS</li>
<li>バッチ/開発: 標準クラス</li>
</ul>
<p>スナップショット保持期間も要確認です。無制限保持は請求を圧迫します。</p>
<h3 id="ネットワーク">ネットワーク</h3>
<p>クロスAZ通信量は見落とされがちです。チャットAPIや内部gRPCが高頻度だと、トラフィック課金が無視できません。依存サービスを同一AZに寄せる設計を検討します。</p>
<h2 id="ガードレールを仕組み化する">ガードレールを仕組み化する</h2>
<p>最適化は一度で終わりません。再び膨らむので、CI/CD に制約を組み込みます。</p>
<ul>
<li>OPA/Gatekeeper で「requests未設定」を拒否</li>
<li>ラベル欠落 (<code>owner</code>, <code>cost-center</code>) の manifest を reject</li>
<li>予算超過 namespace の新規デプロイを警告</li>
</ul>
<p>さらに、週次で「Top Waste 10」を自動配信し、チームごとに改善オーナーを固定します。</p>
<h2 id="実例3か月で-32-削減した手順">実例：3か月で 32% 削減した手順</h2>
<p>ある SaaS 環境（5クラスタ）で実施した順序は次の通りです。</p>
<ol>
<li>2週間の使用実績を収集</li>
<li>上位20 deployment の rightsizing</li>
<li>staging/dev の時間停止</li>
<li>Spot 比率を 15% → 45% へ引き上げ</li>
<li>高コストPVの棚卸し</li>
</ol>
<p>結果:</p>
<ul>
<li>月次コスト: -32%</li>
<li>P95 レイテンシ: ほぼ維持</li>
<li>インシデント増加: なし</li>
</ul>
<p>ポイントは、SLO を維持しながら段階実施したことです。</p>
<h2 id="よくある反論への回答">よくある反論への回答</h2>
<p><strong>Q. 削減すると障害が増えるのでは？</strong>
A. いきなり下げると危険です。P95実績を基準に 10〜15% ずつ下げ、SLO と同時監視すれば安全に進められます。</p>
<p><strong>Q. チームが協力しない</strong>
A. コストを「共通責任」にすると進みません。namespaceごとに owner を明示し、改善期限を決めると動きます。</p>
<p><strong>Q. どこから始めるべき？</strong>
A. Right Sizing と夜間停止です。最短で効果が出ます。</p>
<h2 id="まとめ">まとめ</h2>
<p>Kubernetes コスト最適化は、節約テクニックの寄せ集めではありません。計測、改善、運用ガードレールをセットで回す FinOps プロセスです。</p>
<p>まずは「誰の、どのワークロードが、なぜ高いか」を可視化し、Right Sizing から着手してください。性能を落とさずにコストを下げる道筋は、思っているより現実的です。</p>
<h2 id="実装テンプレート無駄を自動検出する">実装テンプレート：無駄を自動検出する</h2>
<p>可視化だけだと改善は進みません。週次で自動的に「無駄候補」を抽出してチームへ配信する仕組みを入れると、改善が継続します。</p>
<p>抽出ルール例:</p>
<ul>
<li>7日平均で CPU 使用率 &lt; 15% かつ request &gt; 500m</li>
<li>夜間（0:00-6:00）にトラフィックほぼゼロなのに replicas &gt; 1</li>
<li>メモリ使用率 P95 &lt; 40% が 14 日継続</li>
</ul>
<p>このルールで候補を Slack/Discord に自動投稿し、オーナーと期限を付けるだけで改善率が上がります。</p>
<h2 id="具体的なyaml見直し例">具体的なYAML見直し例</h2>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">requests</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cpu</span>: <span style="color:#e6db74">&#34;250m&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">memory</span>: <span style="color:#e6db74">&#34;512Mi&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">limits</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">cpu</span>: <span style="color:#e6db74">&#34;500m&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">memory</span>: <span style="color:#e6db74">&#34;1Gi&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">autoscaling</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">minReplicas</span>: <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">maxReplicas</span>: <span style="color:#ae81ff">8</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">targetCPUUtilizationPercentage</span>: <span style="color:#ae81ff">65</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>ポイントは「requests を現実に寄せる」「limits を安全マージンに置く」「HPA で不足分だけ増やす」の3点です。</p>
<h2 id="ガバナンスコストを技術負債化させない">ガバナンス：コストを技術負債化させない</h2>
<p>削減施策は放置すると必ず戻ります。そこで、リリースゲートに FinOps 観点を追加します。</p>
<ul>
<li>新規サービスは cost label 必須</li>
<li>request 未設定の Pod はデプロイ拒否</li>
<li>予算超過時は自動で注意喚起</li>
</ul>
<p>さらに月次レビューで「削減額」だけでなく「SLO維持率」も一緒に報告することで、コスト最適化が品質改善と対立しない文化を作れます。</p>
<h2 id="まとめ実行順の再確認">まとめ（実行順の再確認）</h2>
<ol>
<li>まず可視化（誰が何にいくら使っているか）</li>
<li>次に Right Sizing と時間停止</li>
<li>最後にガードレールをCI/CDに組み込む</li>
</ol>
<p>この順番を守れば、短期の削減成果と中長期の運用安定を両立できます。</p>
]]></content:encoded>
      <category>Tech</category>
      <category>Kubernetes</category>
      <category>FinOps</category>
      <category>Cloud</category>
      <category>SRE</category>
    </item>
    <item>
      <title>Kubernetesの最新トレンド：Wasm統合とサイドカーレス・メッシュ</title>
      <link>https://www.ai2core.com/posts/2026-02-22-k8s-trends/</link>
      <pubDate>Sun, 22 Feb 2026 08:00:00 +0900</pubDate>
      <guid>https://www.ai2core.com/posts/2026-02-22-k8s-trends/</guid>
      <description>KubeCon 2026で話題になったWebAssemblyワークロードの統合について。</description>
      <content:encoded><![CDATA[<h1 id="kubernetesの最新トレンドwasm統合とサイドカーレスメッシュ">Kubernetesの最新トレンド：Wasm統合とサイドカーレス・メッシュ</h1>
<h2 id="はじめに">はじめに</h2>
<p>先日閉幕した「KubeCon + CloudNativeCon Europe 2026」の熱気も冷めやらぬ中、この記事を執筆しています。今年のカンファレンスで最も注目を集め、キーノートから各セッションまで、あらゆる場所で議論の中心となっていたテーマは、間違いなく<strong>WebAssembly (Wasm) のKubernetesへのネイティブ統合</strong>と、それを支える<strong>サイドカーレス・サービスメッシュ</strong>の進化でした。</p>
<p>Kubernetesを運用する多くのエンジニアが、日々の業務で次のような課題に直面しているのではないでしょうか？</p>
<ul>
<li><strong>肥大化するコンテナイメージ</strong>: アプリケーションの依存関係が増えるにつれ、コンテナイメージは数百MBから数GBに達し、CI/CDパイプラインの実行時間、ストレージコスト、そしてコンテナの起動時間に悪影響を与えている。</li>
<li><strong>「サイドカー税」の深刻化</strong>: マイクロサービスの数が増加するにつれ、各Podにインジェクトされるサービスメッシュのサイドカープロキシ（Envoyなど）が消費するCPUやメモリが無視できないレベルになっている。この「サイドカー税」は、クラスター全体のコストを押し上げる大きな要因です。</li>
<li><strong>複雑化するセキュリティ管理</strong>: コンテナはOSの一部を同梱するため、OSレイヤーの脆弱性（CVE）スキャンとパッチ適用に追われる日々。攻撃対象領域（Attack Surface）も広くなりがちです。</li>
</ul>
<p>もし、これらの課題に一つでも心当たりがあるなら、本記事はあなたのためのものです。KubeCon 2026で示された未来は、これらの長年の課題を根本から解決する可能性を秘めています。この記事では、Wasmとサイドカーレス・メッシュがどのように連携し、次世代のKubernetesエコシステムを形作っていくのか、具体的なコード例やアーキテクチャ図を交えながら、徹底的に解説していきます。</p>
<h2 id="なぜこの技術話題が重要なのか背景と2つの大きな課題">なぜこの技術・話題が重要なのか：背景と2つの大きな課題</h2>
<p>この新しいトレンドを理解するためには、まず私たちが現在立っている場所、つまりコンテナとサイドカーモデルがなぜ成功し、そして今どのような壁にぶつかっているのかを振り返る必要があります。</p>
<h3 id="コンテナ技術の成熟と新たなオーバーヘッド">コンテナ技術の成熟と新たなオーバーヘッド</h3>
<p>DockerとKubernetesがクラウドネイティブの世界を切り拓いてから10年以上が経ち、コンテナはアプリケーションをパッケージングし、デプロイするためのデファクトスタンダードとなりました。しかし、その成功の裏で、いくつかの根深い課題が顕在化しています。</p>
<ol>
<li><strong>リソースオーバーヘッド</strong>: コンテナは軽量な仮想化技術ですが、ゼロオーバーヘッドではありません。各コンテナは、アプリケーションの実行に必要なライブラリやOSのユーザーランドの一部をイメージ内に含んでいます。これにより、同じライブラリがノード上の複数のコンテナで重複してメモリを消費し、イメージサイズも不必要に大きくなります。</li>
<li><strong>遅いコールドスタート</strong>: コンテナイメージのダウンロード、展開、そしてコンテナプロセスの起動には、数百ミリ秒から数秒の時間がかかります。これは、常時稼働するサービスでは問題になりにくいですが、FaaS（Function as a Service）やサーバーレス環境のように、リクエストに応じてスケールアウト・スケールインを頻繁に行うユースケースでは致命的なボトルネックとなります。</li>
<li><strong>広い攻撃対象領域</strong>: コンテナイメージに含まれるOSパッケージやライブラリは、それ自体が脆弱性の温床となり得ます。<code>glibc</code>や<code>openssl</code>といった基本的なライブラリにCVEが発見されれば、影響範囲の特定と修正に多大な労力を要します。</li>
</ol>
<h3 id="サービスメッシュのサイドカー税問題">サービスメッシュの「サイドカー税」問題</h3>
<p>マイクロサービスアーキテクチャの普及に伴い、サービス間の通信を制御・可視化するためのサービスメッシュ（IstioやLinkerdなど）が広く採用されるようになりました。その中心的なアーキテクチャが「サイドカーモデル」です。</p>
<p>サイドカーモデルは、アプリケーションコンテナの隣にプロキシコンテナ（Envoyなど）を配置し、全てのネットワーク通信をこのプロキシ経由で行わせることで、アプリケーションコードを変更することなく、mTLS、リトライ、サーキットブレーキング、詳細なテレメトリといった高度な機能を提供します。</p>
<p>このモデルは非常に強力ですが、大規模な環境では「<strong>サイドカー税 (Sidecar Tax)</strong>」と呼ばれる深刻な問題を引き起こします。</p>
<ul>
<li><strong>リソース消費</strong>: クラスター内に数千のPodがあれば、数千のサイドカープロキシが起動します。これらが消費するCPUとメモリの合計は、アプリケーション本体が消費するリソースに匹敵、あるいはそれを上回ることも珍しくありません。</li>
<li><strong>レイテンシ増加</strong>: Pod内のアプリケーションとサイドカー間の通信は、<code>localhost</code>のネットワークスタックを経由します。これにより、マイクロ秒単位のわずかな遅延が積み重なり、サービス間通信全体のレイテンシを悪化させる可能性があります。</li>
<li><strong>運用の複雑化</strong>: サイドカーのインジェクション、バージョンアップ、Podの起動・終了シーケンスの制御など、運用は複雑になりがちです。特に、サイドカーがアプリケーションより先に終了してしまうと、正常なトラフィックロスを引き起こすこともあります。</li>
</ul>
<p>これらの課題に対する解決策として、クラウドネイティブコミュニティがたどり着いた答えが、<strong>WebAssembly</strong>と<strong>サイドカーレス・アーキテクチャ</strong>の融合なのです。</p>
<h2 id="具体的な解決策wasm-on-kubernetesとサイドカーレスメッシュ">具体的な解決策：Wasm on Kubernetesとサイドカーレス・メッシュ</h2>
<p>それでは、KubeCon 2026で示された次世代アーキテクチャの具体的な中身を見ていきましょう。これは2つの主要な技術要素から構成されています。</p>
<h3 id="1-kubernetesにおけるwasmワークロードの実行">1. KubernetesにおけるWasmワークロードの実行</h3>
<p>WebAssembly（Wasm）は、もともとWebブラウザでネイティブコードに近いパフォーマンスを実現するための技術でしたが、その「軽量・高速・セキュア・ポータブル」という特性がサーバーサイドでも高く評価されるようになりました。</p>
<h4 id="wasmwasiとは簡単なおさらい">Wasm/WASIとは？（簡単なおさらい）</h4>
<ul>
<li><strong>Wasm</strong>: 特定のCPUアーキテクチャに依存しない、ポータブルなバイナリフォーマットです。Go, Rust, C++, Python, Javaなど、多くの言語からコンパイルできます。</li>
<li><strong>WASI (WebAssembly System Interface)</strong>: Wasmがサンドボックスの外側、つまりファイルシステムやソケット、環境変数といったシステムリソースと安全に対話するための標準インターフェースです。これにより、Wasmはブラウザの外でも安全に動作する汎用的な実行環境となりました。</li>
</ul>
<h4 id="kubernetesでのwasm実行の進化">KubernetesでのWasm実行の進化</h4>
<p>数年前（2023-2024年頃）は、KubernetesでWasmを実行するにはKrustletのようなカスタムプロジェクトが必要で、実験的な取り組みでした。しかし2026年の現在、状況は大きく変わりました。<code>containerd</code>の<code>runwasi</code> shimや、<code>crun</code>のようなOCIランタイムがWasmをネイティブサポートしたことで、<strong>Wasmはコンテナと並ぶ第一級のワークロードとして扱えるようになった</strong>のです。</p>
<p>これにより、私たちは使い慣れた<code>Pod</code>リソースの<code>runtimeClassName</code>フィールドを指定するだけで、Wasmワークロードをシームレスにデプロイできるようになりました。</p>
<h4 id="コード例wasmワークロードをデプロイするpodマニフェスト">コード例：WasmワークロードをデプロイするPodマニフェスト</h4>
<p>以下は、Rustで書かれたシンプルなHTTPサーバーをWasmにコンパイルし、OCI準拠のコンテナレジストリにプッシュした後、Kubernetesにデプロイするためのマニフェストです。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># pod-wasm.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Pod</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">wasm-hello-world-pod</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">app</span>: <span style="color:#ae81ff">hello-wasm</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># runtimeClassNameでWasmランタイムを指定するだけ。</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># このクラスは管理者が事前にRuntimeClassリソースとして定義しておく。</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">runtimeClassName</span>: <span style="color:#ae81ff">wasmtime-crun</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">containers</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">hello-wasm-server</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># コンテナイメージと同様に、OCIアーティファクトとして格納されたWasmモジュールを指定</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">image</span>: <span style="color:#ae81ff">myregistry.io/wasm-apps/hello-server:1.0.2</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">containerPort</span>: <span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># commandやenvもコンテナと同じように設定可能</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">command</span>: [<span style="color:#e6db74">&#34;/app.wasm&#34;</span>]
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">env</span>:
</span></span><span style="display:flex;"><span>        - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">GREETING</span>
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">value</span>: <span style="color:#e6db74">&#34;Hello from Wasm on Kubernetes!&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>このマニフェストを<code>kubectl apply -f pod-wasm.yaml</code>でデプロイするだけで、Kubernetesは指定されたWasmランタイムを使ってPodを起動します。Podの管理、スケーリング、ネットワーキングは、全て既存のKubernetesの仕組みの上で行われます。</p>
<h4 id="図解コンテナ-vs-wasm-on-kubernetes">図解：コンテナ vs Wasm on Kubernetes</h4>
<p>このアーキテクチャの違いを視覚的に理解しましょう。</p>
<pre tabindex="0"><code>【従来のコンテナワークロード】

+-------------------------------------------------------------+
|    Pod                                                      |
|  +-------------------------------------------------------+  |
|  |   Container (App + Libs + OS Userland)                |  |
|  +-------------------------------------------------------+  |
+-------------------^-----------------------------------------+
                    | (CRI)
+-------------------v-----------------------------------------+
|    Containerd     |   runc (OCI Runtime)                    |
+-------------------v-----------------------------------------+
|    Linux Kernel (Namespaces, Cgroups, Syscalls)             |
+-------------------------------------------------------------+

【新しいWasmワークロード】

+-------------------------------------------------------------+
|    Pod                                                      |
|  +-------------------------------------------------------+  |
|  |   Wasm Module (App Code Only)                         |  |
|  +-------------------------------------------------------+  |
+-------------------^-----------------------------------------+
                    | (CRI)
+-------------------v-----------------------------------------+
|    Containerd     |   crun / runwasi (Wasm OCI Runtime)     |
|                   +---------------------------------------+ |
|                   |           Wasm VM (Sandbox)           | |
+-------------------v--------------------^--------------------+
|    Linux Kernel (Minimal Syscalls via WASI)                 |
+-------------------------------------------------------------+
</code></pre><p>この図が示すように、WasmワークロードはOSのユーザーランドをほとんど含まず、Wasm VMという非常に薄いサンドボックス層の上で動作します。これにより、起動時間の短縮、リソース消費の削減、そしてセキュリティの向上が実現されます。</p>
<h3 id="2-サイドカーレスサービスメッシュとebpf">2. サイドカーレス・サービスメッシュとeBPF</h3>
<p>Wasmがコンピューティング層の革新だとすれば、ネットワーキング層の革新を担うのが<strong>サイドカーレス・サービスメッシュ</strong>です。このアーキテクチャは、IstioのAmbient MeshやLinkerd、Cilium Service Meshといったプロジェクトによって牽引されています。</p>
<h4 id="サイドカーレスアーキテクチャとは">サイドカーレス・アーキテクチャとは？</h4>
<p>サイドカーレスは、その名の通り、各Podにサイドカープロキシを配置するモデルを廃止します。代わりに、<strong>ノード単位でプロキシ機能を共有する</strong>アプローチを取ります。</p>
<p>代表的なIstio Ambient Meshの実装では、以下の2つのコンポーネントが登場します。</p>
<ol>
<li><strong>ztunnel (Secure Tunnel)</strong>: 各ノードにDaemonSetとしてデプロイされる軽量なL4プロキシ。mTLSの確立、L4のテレメトリ収集、L4のアクセス制御など、基本的な機能のみを担当します。</li>
<li><strong>Waypoint Proxy</strong>: サービスアカウント単位（あるいはService単位）でデプロイされる、EnvoyベースのL7プロキシ。HTTPルーティング、リトライ、フォールトインジェクションといった高度なL7機能が必要なサービスに対してのみ、選択的に導入されます。</li>
</ol>
<p>このアーキテクチャの鍵を握るのが<strong>eBPF</strong>です。</p>
<h4 id="ebpfによる革命">eBPFによる革命</h4>
<p>eBPF (extended Berkeley Packet Filter) は、カーネルのコードを書き換えることなく、カーネル空間でカスタムプログラムを安全に実行できる革新的な技術です。サービスメッシュの文脈では、eBPFは以下のような役割を果たします。</p>
<ul>
<li>Podから発信される全てのTCPパケットをカーネルレベルで傍受します。</li>
<li>パケットの宛先情報を元に、ztunnelにトラフィックをリダイレクトし、mTLSトンネルを確立させます。</li>
<li>L7ポリシーが適用されるトラフィックの場合、さらにWaypoint Proxyにトラフィックをリダイレクトします。</li>
</ul>
<p>これにより、アプリケーションやPodのネットワーク名前空間に一切手を加えることなく、透過的にサービスメッシュの機能を提供できるのです。</p>
<h4 id="図解サイドカーモデル-vs-サイドカーレスモデル">図解：サイドカーモデル vs サイドカーレスモデル</h4>
<pre tabindex="0"><code>【サイドカーモデル】

  Node
+-----------------------------------------------------------------+
| Pod A                                 | Pod B                   |
| +-----------+   +-----------------+   | +-----------+   +-----+ |
| | App A     |&lt;-&gt;| Envoy (Sidecar) |&lt;-----&gt;| App B     |&lt;-&gt;| ... | |
| +-----------+   +-----------------+   | +-----------+   +-----+ |
+-----------------------------------------------------------------+
  (リソース消費大、Pod内レイテンシ増)

【サイドカーレスモデル (Ambient Mesh)】

  Node
+-----------------------------------------------------------------+
|                                                                 |
|  Pod A                  Pod B                  Pod C            |
| +---------+            +---------+            +---------+       |
| |  App A  |            |  App B  |            |  App C  |       |
| +---------+            +---------+            +---------+       |
|      |                      |                      |             |
| &lt;----|----------------------|----------------------|--------&gt;    |
|      | (eBPFがトラフィックをリダイレクト)           |             |
| +----v------------------------------------------------v----+    |
| |             ztunnel (Node-level L4 Proxy)              |    |
| +--------------------------^-----------------------------+    |
|                            | (L7処理が必要な場合)               |
|                            +-----------------------------&gt;    |
|                                                     +--------------------+
|                                                     | Waypoint Proxy     |
|                                                     | (for ServiceAccount) |
|                                                     +--------------------+
+-----------------------------------------------------------------+
  (リソース効率◎、Pod内レイテンシなし)
</code></pre><p>このアーキテクチャにより、「サイドカー税」は劇的に削減され、運用も大幅に簡素化されます。</p>
<h3 id="3-究極の組み合わせwasmフィルター--サイドカーレスメッシュ">3. 究極の組み合わせ：Wasmフィルター + サイドカーレス・メッシュ</h3>
<p>ここまで、コンピューティングのWasmとネットワーキングのサイドカーレスを個別に解説してきましたが、この2つが組み合わさることで、真の力が発揮されます。</p>
<p>サイドカーレス・メッシュのWaypoint Proxyは共有リソースであるため、ここにカスタムロジックを追加するのは慎重に行う必要があります。プロキシ自体を再起動すると、多くのサービスに影響が及ぶからです。</p>
<p>ここで登場するのが<strong>Wasmフィルター</strong>です。Waypoint Proxyとして利用されるEnvoyは、Wasm VMを内蔵しており、カスタムロジックをWasmモジュールとして動的にロードし、リクエスト/レスポンス処理パイプラインに組み込むことができます。</p>
<ul>
<li><strong>ユースケース</strong>:
<ul>
<li>カスタム認証・認可ロジック</li>
<li>APIキーの検証</li>
<li>リクエスト/レスポンスの変換</li>
<li>高度なレートリミット</li>
<li>独自のテレメトリデータの生成</li>
</ul>
</li>
</ul>
<p>これにより、<strong>プロキシを再起動することなく、安全かつ動的にサービスメッシュの機能を拡張できる</strong>ようになります。開発者は、GoやRustといった好みの言語でビジネスロジックを書き、Wasmにコンパイルしてレジストリにプッシュするだけです。プラットフォームチームは、Istioの<code>WasmPlugin</code>リソースを適用するだけで、そのカスタムロジックをプロダクション環境に展開できます。</p>
<h4 id="コード例カスタムヘッダーを追加するwasmフィルターの適用">コード例：カスタムヘッダーを追加するWasmフィルターの適用</h4>
<p>以下は、特定のWaypoint Proxyに、リクエストヘッダーを追加する簡単なWasmフィルターを適用するための<code>WasmPlugin</code>マニフェストです。</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># wasm-plugin.yaml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">extensions.istio.io/v1alpha1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">WasmPlugin</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">add-custom-header</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">namespace</span>: <span style="color:#ae81ff">default</span> <span style="color:#75715e"># ワークロードと同じNamespace</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># このWasmPluginを適用するワークロードを選択</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">matchLabels</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#75715e"># &#39;product-reviews&#39;サービスアカウントに紐づくWaypoint Proxyに適用</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">istio.io/waypoint-for</span>: <span style="color:#ae81ff">service-account</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">service.istio.io/canonical-name</span>: <span style="color:#ae81ff">product-reviews</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># OCIレジストリ内のWasmモジュールを指定</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">url</span>: <span style="color:#ae81ff">oci://myregistry.io/wasm-filters/add-header:2.1.0</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># Wasmモジュールに渡す設定</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">pluginConfig</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">headerName</span>: <span style="color:#e6db74">&#34;X-Request-Source&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">headerValue</span>: <span style="color:#e6db74">&#34;istio-mesh&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>このYAMLを適用するだけで、<code>product-reviews</code>サービスへのトラフィックを処理するWaypoint Proxyは、指定されたWasmモジュールをダウンロードし、即座にリクエストパイプラインに組み込みます。ダウンタイムは一切発生しません。</p>
<h2 id="メリットとデメリット">メリットとデメリット</h2>
<p>この新しいアーキテクチャがもたらす変化は計り知れませんが、もちろん銀の弾丸ではありません。メリットとデメリットを冷静に評価することが重要です。</p>
<h3 id="メリット">メリット</h3>
<ul>
<li><strong>劇的なリソース効率の向上</strong>: WasmによるPodの軽量化と、サイドカーレスによるメッシュの効率化は、クラスター全体のリソース消費を50%以上削減する可能性も秘めています。これは、クラウドコストの削減に直結します。</li>
<li><strong>超高速な起動</strong>: Wasmのミリ秒単位のコールドスタート性能は、サーバーレス、バッチ処理、CI/CDジョブといった断続的なワークロードの実行効率を飛躍的に向上させます。</li>
<li><strong>堅牢なセキュリティ</strong>: WasmのCapability-basedセキュリティモデルは、デフォルトで何も許可しない（Deny-by-default）サンドボックスを提供します。WASIを通じてファイルアクセスやネットワークなどの権限を明示的に付与する必要があり、攻撃対象領域を最小限に抑えられます。</li>
<li><strong>真のポータビリティ</strong>: WasmはCPUアーキテクチャ（<code>amd64</code>, <code>arm64</code>）やOSに依存しません。一度Wasmにコンパイルすれば、ローカルのMacBookからクラウド上のLinuxサーバーまで、どこでも全く同じように動作します。</li>
<li><strong>運用の簡素化</strong>: サイドカーのライフサイクル管理という複雑なタスクから解放されます。Wasmフィルターによる動的な機能拡張は、カナリアリリースやA/Bテストも容易にし、DevOpsの生産性を向上させます。</li>
</ul>
<h3 id="デメリットと考慮事項">デメリットと考慮事項</h3>
<ul>
<li><strong>エコシステムの成熟度</strong>: 2026年時点でも、Wasmを取り巻く開発ツール、デバッグ、モニタリングのエコシステムは、コンテナに比べるとまだ発展途上です。特に、本番環境でのトラブルシューティングには新たな知識と経験が求められます。</li>
<li><strong>WASIの標準化と機能</strong>: WASIは急速に進化していますが、GPUへの直接アクセスや特定のカーネル機能の利用など、まだ全てのユースケースをカバーしているわけではありません。高度なハードウェア連携が必要なワークロードでは、依然としてコンテナが最適な選択肢となる場合があります。</li>
<li><strong>学習コスト</strong>: Wasm特有のプログラミングモデル、eBPFの仕組み、サイドカーレス・メッシュのアーキテクチャなど、エンジニアチームが習得すべき新しい概念は少なくありません。</li>
<li><strong>パフォーマンスのトレードオフ</strong>: Wasm VMを介することによるオーバーヘッドは非常に小さいですが、ゼロではありません。レイテンシが極めて重要な金融取引システムや、純粋な計算性能が求められるHPC（High-Performance Computing）のような領域では、ネイティブバイナリと比較して性能を慎重に評価する必要があります。</li>
</ul>
<h2 id="現場で使える実践的なtips">現場で使える実践的なTips</h2>
<p>この新しい技術を現場に導入する際に役立つ、いくつかの実践的なヒントを紹介します。</p>
<ol>
<li><strong>段階的な導入から始める</strong>: 全てのワークロードをいきなりWasmに置き換えるのは現実的ではありません。まずは、新規に開発するステートレスなマイクロサービスや、起動速度がビジネス価値に直結するAPIエンドポイント、あるいはCI/CDパイプライン内のジョブなど、影響範囲の小さいところから試してみましょう。</li>
<li><strong>ユースケースに応じたWasmランタイムの選定</strong>: Kubernetesでは<code>RuntimeClass</code>を通じて複数のWasmランタイムを併用できます。パフォーマンスを最優先するなら<code>wasmtime</code>ベースのランタイム、AI/ML推論のような高度な機能が必要なら<code>WasmEdge</code>といったように、ワークロードの特性に合わせて最適なランタイムを選択しましょう。</li>
<li><strong>ローカルでのデバッグを徹底する</strong>: Wasmモジュールは、<code>wasmtime run</code>や<code>wasmer run</code>といったコマンドラインツールを使ってローカルで簡単に実行・デバッグできます。Kubernetesにデプロイする前に、ローカル環境で単体テストと基本的な動作確認を徹底することが、開発効率を上げる鍵です。</li>
<li><strong>セキュリティを意識したWasmモジュール設計</strong>: Wasmモジュールをビルドする際は、WASIで要求する権限を最小限に絞り込みましょう。例えば、外部ネットワークへのアクセスが不要なモジュールには、ソケットの権限を与えないようにします。また、TrivyのようなセキュリティスキャナもWasmアーティファクトに対応し始めているため、CIプロセスにスキャンを組み込むことを推奨します。</li>
</ol>
<h2 id="まとめ">まとめ</h2>
<p>KubeCon 2026が示した未来は、もはや単なるコンセプトではありません。Wasmとサイドカーレス・メッシュの統合は、「コンテナのオーバーヘッド」と「サイドカー税」という、我々が長年抱えてきた課題に対する、現実的かつ強力なソリューションです。</p>
<p>このパラダイムシフトは、単なる技術の置き換えに留まりません。</p>
<ul>
<li><strong>コンピューティング層</strong>では、Wasmがコンテナの不得意な領域（超高速起動、高密度集積、堅牢なセキュリティ）を補完し、ワークロードの選択肢を広げます。</li>
<li><strong>ネットワーキング層</strong>では、サイドカーレス・メッシュがリソース効率と運用性を劇的に改善します。</li>
<li>そして、<strong>アプリケーション拡張層</strong>では、Wasmフィルターが安全で動的な機能追加を可能にし、プラットフォームとアプリケーションの境界をより柔軟にします。</li>
</ul>
<p>もちろん、この移行には学習と試行錯誤が伴います。しかし、この変化の波を早期に捉え、自社のシステムやプロダクトにどう活かせるかを検討し始めることが、数年後の技術的な優位性を築く上で不可欠となるでしょう。</p>
<p>クラウドネイティブの旅は、新たな章に突入しました。さあ、一緒にこのエキサイティングな未来を探求していきましょう。</p>
]]></content:encoded>
      <category>Infrastructure</category>
      <category>Kubernetes</category>
      <category>Wasm</category>
      <category>Istio</category>
    </item>
  </channel>
</rss>
