K8s 実戦 #6 運用チェックリスト — アップグレード / バックアップ・リカバリ / コスト / セキュリティ

読了 14分

K8s 実戦シリーズの最後の記事です。#1~#5 まで myshop-api を最初から立て、デプロイ・DB・CI/CD・モニタリングまで 1 つの流れを自動化しました。この時点でクラスタはよく回っている状態ですが、1 年単位で安全に運用すること は異なる性質の作業です。K8s は 1 年にマイナーバージョンが 3 つ出て、AWS は四半期ごとに新しいインスタンスタイプを出し、RDS は定期的に maintenance window が組まれます。この記事ではその定期運用サイクルを整理します — EKS アップグレード、バックアップ・リカバリ、コスト、セキュリティの 4 次元です。シリーズの最後の記事なので実戦 6 編と K8s トラック全体 26 編の振り返りも一緒に入れます。

このシリーズは K8s 実戦 6 編です。

EKS アップグレード — 1 年に 1 度はマイナーバージョン #

K8s 自体のバージョンポリシーは明確です。

  • マイナーバージョン (1.30 → 1.31 → 1.32) — 約 4 か月周期でリリース
  • 各マイナーバージョンのサポート期間 — リリース後約 14 か月
  • EKS のサポート期間 — 標準サポート 14 か月 + 拡張サポート追加 12 か月(有料)

運用クラスタが標準サポート内にあるよう維持するには 1 年に最低 1 度のマイナーアップグレード が必要です。四半期別のアップデートカレンダーで管理することが標準です。

アップグレードの標準流れ #

EKS マイナーアップグレードの 1 サイクル
1. リリースノート検討 — 新バージョンの deprecation、removed API
2. dev クラスタで先にアップグレード
3. 1 週間~2 週間 dev で回してみる
4. マニフェストの deprecated API を整理
5. アップグレード点検ツール実行 (pluto、kubent)
6. prod コントロールプレーンアップグレード
7. prod ノードグループアップグレード (rolling)
8. アドオンアップグレード (vpc-cni、coredns、kube-proxy、ebs-csi)

各ステップで時間がもっとも多くかかるところは 4 ステップ — deprecated API 整理 です。K8s がマイナーバージョンごとに一部の API を削除するので、マニフェストに古い API が入っていれば新しいクラスタで拒否されます。

点検ツール — pluto と kubent #

pluto — マニフェストの deprecated API 点検
pluto detect-files -d charts/ --target-versions k8s=v1.31
kubent — クラスタに生きている deprecated API 点検
kubent --target-version 1.31

2 つのツールが合わさって「私のマニフェストと私のクラスタの両方で」deprecated API を見つけてくれます。運用クラスタのアップグレード標準手順の一部です。

コントロールプレーンアップグレード — Terraform 1 行 #

terraform — cluster_version だけ変えて apply
module "eks" {
  # ...
  cluster_version = "1.31"   # 1.30 → 1.31
}

terraform apply が EKS コントロールプレーンのマイナーアップグレードをトリガします。EKS はコントロールプレーンを無中断でアップグレードします — ユーザーワークロードは影響を受けず、kubectl も継続して動作します。約 30 分~1 時間程度かかります。

ノードグループアップグレード — Rolling vs Blue-green #

ノードグループのアップグレードは 2 つのパターンがあります。

パターンモデル
In-place rolling同じ Managed Node Group 内のノードを 1 台ずつ新しい AMI に交換
Blue-green新しい Managed Node Group を作ってワークロード移行後に古いグループ削除

EKS Managed Node Group のデフォルトは in-place rolling です。EKS が自動で Pod を cordon → drain → 新しいノード立て → Pod を新しいノードへ → 古いノード削除のサイクルを回します。#2 で作った PodDisruptionBudget がこの時点で決定的です — PDB がなければ同じワークロードの Pod が同時に全部下がる可能性があり、ユーザーにダウンタイムとして見えます。

ノードグループアップグレードトリガ (Terraform またはコンソール)
# コントロールプレーンが 1.31 に上がった後
aws eks update-nodegroup-version \
  --cluster-name myshop-prod \
  --nodegroup-name general \
  --region ap-northeast-2

大規模クラスタでアップグレードの影響が懸念されれば blue-green パターンが安全です — 新しいノードグループを作って 上級 #4 Karpenter の disruption 制御でワークロードを段階的に移行した後に古いグループを空ける流れです。

アドオンアップグレード #

コントロールプレーンとノードが新しいバージョンになった後にアドオンも一緒に上げます。

terraform — アドオンバージョン更新
cluster_addons = {
  vpc-cni = {
    most_recent = true   # または明示的バージョン
  }
  coredns = {
    most_recent = true
  }
  # ...
}

most_recent = true にしておくと Terraform が K8s バージョンと互換性のある最新アドオンバージョンに自動アップグレードします。明示的バージョン pin が必要なケースは addon_version フィールドを使います。

バックアップとリカバリ — RDS の PITR が 1 次防衛線 #

myshop のデータはほぼ全部 RDS にあります。K8s クラスタ自体はマニフェストが git にあるので崩れても再構成できますが、RDS データは一度失えばリカバリが難しいです。

自動スナップショット + PITR #

#3 の Terraform で backup_retention_period = 30 と書いた 1 行が次を作り出しました。

  • 毎日の自動スナップショットbackup_window(03:00-04:00 UTC)時間に自動バックアップ
  • PITR (Point-in-Time Recovery) — 過去 30 日内の任意の時刻に 1 秒単位でリカバリ可能

PITR は RDS のもっとも強力なバックアップ機能です。ユーザーが誤って DELETE FROM orders; を prod に回したときに、その直前の時刻に 5 分で新しい RDS インスタンスをリカバリしてデータを比較できます。

PITR で 30 分前の時点リカバリ — 新しいインスタンスとして
aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier myshop-prod \
  --target-db-instance-identifier myshop-prod-recovery \
  --restore-time 2026-05-11T08:30:00Z \
  --db-subnet-group-name myshop-prod \
  --vpc-security-group-ids sg-xxxxx

このコマンドが 30 分前の時点のデータを持つ新しいインスタンスを作ります。そのインスタンスから損傷していないデータを SELECT で取ってきて prod に復元する流れが標準事故対応です。

リカバリ訓練 — 四半期点検 #

自動バックアップが回っていることを見て安心することと実際にリカバリが可能なことは別です。四半期に 1 度ずつリカバリをシミュレーションしてみるのが標準 です。

四半期リカバリ訓練手順
1. PITR で新しいインスタンスをリカバリ (上のコマンド)
2. 新しいインスタンスのデータをサンプル確認 (row count、最近のトランザクション)
3. dev 環境の myshop-api を新しいインスタンスを指すように設定変更
4. 機能テスト (仮想注文生成、照会)
5. 新しいインスタンス削除 + 結果ドキュメント化

この訓練で 1 度でも失敗が出てくれば運用優先順位 1 番に取るべきシグナルです。「バックアップがある」ではなく「リカバリが検証された」が運用の標準です。

クラスタリカバリ — Velero #

K8s クラスタ自体のリカバリは GitOps repo が 1 次防衛線ですが、etcd 自体にある動的状態(例: ユーザーが作った PVC、ConfigMap の自動更新)まで保存するには Velero が標準ツールです。

Velero — S3 にクラスタバックアップ
velero install \
  --provider aws \
  --bucket myshop-velero-backups \
  --region ap-northeast-2 \
  --plugins velero/velero-plugin-for-aws:v1.10.0 \
  --backup-location-config region=ap-northeast-2

# 毎日自動バックアップ
velero schedule create daily --schedule="0 2 * * *" --ttl 720h

Velero が etcd から K8s オブジェクト + EBS ボリュームスナップショットを S3 に定期バックアップします。クラスタを丸ごと失ったときに新しいクラスタで velero restore でリカバリできます。

コスト — もっとも早く漏れる領域 #

K8s クラスタのコストは意図しなければ早く膨らみます。運用クラスタのコスト点検標準項目を押さえておきます。

1. Karpenter + Spot の結合 #

#1 で短く言及した Karpenter がコスト面で最大の節減効果をもたらします。

Karpenter NodePool — Spot 優先
spec:
  template:
    spec:
      requirements:
        - key: karpenter.sh/capacity-type
          operator: In
          values: ["spot", "on-demand"]
        - key: node.kubernetes.io/instance-type
          operator: NotIn
          values: ["m5.metal"]   # 大きすぎるインスタンス除外
  disruption:
    consolidationPolicy: WhenEmptyOrUnderutilized
    consolidateAfter: 30s

Karpenter が ワークロードのリソース要求に正確に合うインスタンスを Spot 価格で 立てます。ON_DEMAND 比約 50~70% 節減が一般的で、consolidation で使用量が低いノードを自動統合します。

2. RDS インスタンス right-sizing #

RDS コストはインスタンスクラス + ストレージ + IOPS の合計です。インスタンスクラスがもっとも大きな比重を占めるので四半期別 right-sizing 検討が標準です。

RDS Performance Insights で見るシグナル
- CPU 使用率平均が 30% 未満 → 1 段階小さいクラス検討
- max_connections 使用率 50% 未満 → プールサイズ / インスタンス両方検討
- ストレージ IOPS の max が baseline の 50% 未満 → gp3 にダウンサイジング検討

管理型サービスなのでインスタンスクラス変更が 1 度の reboot で終わります。四半期単位の right-sizing がもっとも大きなコスト節減項目の 1 つです。

3. NAT Gateway データ転送 #

#1 で短く触れた NAT Gateway コストは時間あたり + GB あたりデータ転送料金です。private サブネットのワークロードが外部と通信するたびにコストが積まれ、意外と大きな比重を占めるケースが多いです。

NAT データ転送節減パターン
- VPC Endpoint 導入 — S3 / ECR / DynamoDB のような AWS サービスは VPC Endpoint で NAT 回避
- 同じ region の RDS は VPC 内部ルーティングなので NAT 回避 (VPC peering / Transit Gateway)
- 外部 API をよく呼ぶならキャッシュレイヤー導入

月 $100~$500 NAT コストが VPC Endpoint 導入でその半分以下に下がることがよくあります。

4. EBS スナップショットと unused リソース #

定期点検する unused リソース
- 古い EBS スナップショット (RDS 自動スナップショット以外に手動で作ったもの)
- 古い AMI
- 分離された EBS ボリューム (古いノードグループの残留物)
- 使用されない ALB / NLB (古い Ingress の残留物)
- ECR の古いイメージ (lifecycle policy で自動削除推奨)

ECR lifecycle policy は Terraform 1 枚で整理されます。

terraform — ECR lifecycle
resource "aws_ecr_lifecycle_policy" "myshop_api" {
  repository = aws_ecr_repository.myshop_api.name

  policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Keep last 30 production tags"
        selection = {
          tagStatus     = "tagged"
          tagPrefixList = ["v"]
          countType     = "imageCountMoreThan"
          countNumber   = 30
        }
        action = { type = "expire" }
      },
      {
        rulePriority = 2
        description  = "Expire untagged after 7 days"
        selection = {
          tagStatus   = "untagged"
          countType   = "sinceImagePushed"
          countUnit   = "days"
          countNumber = 7
        }
        action = { type = "expire" }
      }
    ]
  })
}

5. コスト可視化 — Kubecost / OpenCost #

OpenCost (オープンソース) インストール
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm install opencost opencost/opencost \
  -n opencost --create-namespace

OpenCost が Prometheus メトリクスとクラウドコスト API を結合して namespace・ワークロード単位のコスト を見せます。「myshop namespace が 1 か月にいくら使うか、どのワークロードが大部分か」が一目で見えます。コスト責任の分配がチーム単位に落ちる環境ではほぼ標準です。

セキュリティ — 定期点検の標準項目 #

運用クラスタのセキュリティは 1 度のセットアップではなく定期点検の累積です。標準項目 3 つを押さえておきます。

1. CIS Benchmark — kube-bench #

kube-bench — CIS Benchmark 点検
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job-eks.yaml
kubectl logs -l job-name=kube-bench

kube-bench が CIS Kubernetes Benchmark の項目を自動点検します。EKS はコントロールプレーンが管理型なので点検項目がノード + ワークロードマニフェストに限定されます。四半期別に実行して FAIL 項目を整理することが標準です。

2. コンテナイメージスキャン — Trivy #

.github/workflows/build.yml — イメージスキャンステップ追加
- name: Trivy イメージスキャン
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/myshop-api:${{ steps.meta.outputs.tag }}
    format: sarif
    severity: CRITICAL,HIGH
    exit-code: 1   # CRITICAL 発見時にビルド失敗
    output: trivy-results.sarif

- name: SARIF を GitHub Security タブにアップロード
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif

Trivy がベースイメージの OS パッケージ + アプリケーション依存関係の既知の脆弱性(CVE)をスキャンします。CRITICAL が発見されればビルド失敗で防いで新しいイメージが ECR に入れないようにします。ECR Enhanced Scanning(有料)も同じ領域を埋める管理型オプションです。

3. RBAC 権限 audit #

権限使用現況点検
# すべての ClusterRoleBinding を見る
kubectl get clusterrolebindings -o wide

# 特定権限が付与された主体を探す
kubectl auth can-i --list --as=system:serviceaccount:myshop:myshop-api

# 外部ツール — krane (Salesforce)
# または rbac-tool by InsightCloudSec

四半期別に RBAC を audit して「使用されない権限」「過度な権限」「古い ServiceAccount の残留物」を整理する流れが標準です。上級 #2 で扱った kubectl auth can-i が日常点検のツールです。

4. ポリシーエンジン — Kyverno で admission 強制 #

上級 #3 で扱った Kyverno を導入すると次のようなポリシーが admission 段階で強制されます。

kyverno-policies.yaml — 運用標準ポリシーセット
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-from-ecr
spec:
  validationFailureAction: Enforce
  rules:
    - name: ecr-only
      match:
        resources:
          kinds: [Pod]
      validate:
        message: "Images must come from our ECR registry."
        pattern:
          spec:
            containers:
              - image: "123456789012.dkr.ecr.*"

このようなポリシー 5~10 個が運用クラスタの標準ガードレールです。他の人が外部イメージや limits なしのコンテナを誤ってデプロイしても admission 段階で拒否されます。

定期運用カレンダー #

上の項目をカレンダーで整理すると次のとおりです。

周期作業
毎日アラームレビュー、Grafana ダッシュボード点検
毎週新しいセキュリティパッチ検討、ECR Trivy スキャン結果レビュー
毎月コストレビュー (OpenCost)、unused リソース整理、SLI/SLO レポート
四半期EKS マイナーアップグレード、RDS right-sizing、RBAC audit、リカバリ訓練、kube-bench
半年セキュリティ点検総合 (外部監査 / pentest)、DR シミュレーション
クラスタアーキテクチャレビュー、マニフェスト現代化

このカレンダーが 1 ページに整理されていて担当者が決まっていることが運用チームの到達点です。1 度セットアップした後に「うまく回っているから忘れて生きる」がもっとも危険なパターンです。

シリーズ振り返り — K8s 実戦 6 編で手に入ったもの #

最後の記事なので 6 編を一度押さえておきます。

  • #1EKS クラスタセットアップ。Terraform で VPC・EKS・ノードグループ・IRSA・標準アドオンまでを 1 つのコードベースで。eksctl 比較、Karpenter 予告。
  • #2アプリデプロイ骨格。Deployment + Service + Ingress + ConfigMap + Secret + HPA + PDB の標準 9 セット。AWS Load Balancer Controller で ALB 自動プロビジョニング。Helm chart で dev / prod 環境別 values。
  • #3DB 連動。RDS Terraform、Secrets Manager、External Secrets Operator で秘密同期。PgBouncer コネクションプール。Helm hook ベースの移行 Job。RDS IAM 認証のさらに進んだ内容。
  • #4CI/CD パイプライン。GitHub Actions OIDC で静的キーなしに ECR push。マニフェスト repo 自動 commit。ArgoCD App of Apps。Argo Rollouts カナリー。
  • #5モニタリング・アラーム。kube-prometheus-stack 一度にインストール。ServiceMonitor + PrometheusRule。4 golden signals。Alertmanager severity 分岐。Loki + CloudWatch。
  • #6 — 運用チェックリスト。EKS アップグレード、PITR バックアップ・リカバリ、Karpenter Spot コスト節減、kube-bench / Trivy / Kyverno セキュリティ。

シリーズ最初の記事で押さえた仮想シナリオ(myshop-api)が 6 編を経て実際の運用クラスタの 1 サイクルとして完成しました。抽象ではなく具体で K8s を導入・運用する視野が手に入る段階です。

K8s トラック全体振り返り — 26 編 #

基礎 7 編 + 中級 7 編 + 上級 6 編 + 実戦 6 編を足すと 26 編で、Docker 基礎 6 編まで含めれば 32 編です。この数字が K8s トラックと周辺 Docker トラックの規模を一度に見せます。

K8s トラックの大きな絵
[基礎 7 編]    マニフェスト 1 枚のモデル — kubectl apply 1 度の流れ
[中級 7 編]    そのマニフェストが運用クラスタで動く深さ
[上級 6 編]    その上に乗るポリシー・拡張・観測・同期の深さ
[実戦 6 編]    EKS の上の本物のサービス 1 サイクル — myshop-api

各トラックが次のトラックの入力になる構造で、26 編すべて追ってきた時点なら次の段階が明確になります。

  • マニフェスト 1 枚の意図を 1 行で読める
  • 新しいクラスタのセットアップ・拡張・運用の決定が手に入る
  • 事故発生時にどこから見るべきかすぐに押さえられる
  • コスト・セキュリティ・アップグレードの定期サイクルが頭の中にカレンダーとして押さえられる

その次 — K8s トラックを越えて #

このトラックが到達点ではありません。K8s の上に載せるさらに深いテーマが残っています。

  • Service Mesh — Istio / Linkerd。mTLS・きめ細かなトラフィックルーティング・observability mesh。
  • MLOps on K8s — Kubeflow、KServe、Argo Workflows。ML モデル学習・デプロイ・サービングの専用スタック。
  • マルチクラスタ — 単一クラスタの限界を越えるパターン。クラスタ federation、マルチ region、ArgoCD ApplicationSet。
  • EBPF 深さ — Cilium 越えの領域。セキュリティ / オブザーバビリティ / ネットワーキングの次の世代。
  • eks-anywhere / on-prem K8s — 管理型から外れたクラスタ運用の実際。

これらのテーマは別のシリーズで扱う価値があり、30 編の K8s トラックがその出発点に立つための視野を与えてくれます。

締めくくり #

K8s 実戦シリーズ 6 編を締めくくり、K8s トラック全体 26 編を締めくくります。この記事ではクラスタを 1 年単位で安全に回す定期サイクルを追いました — EKS マイナーアップグレード、RDS PITR と四半期リカバリ訓練、Karpenter + Spot でコスト節減、kube-bench / Trivy / Kyverno でセキュリティ定期点検までが運用サイクルの標準骨格です。このトラックの到達点は単純な 1 文に縮められます — 「クラスタがうまく回っている」と言える視野が手に入ったか。 マニフェスト 1 枚のモデルから四半期カレンダーの運用項目までが 1 人の頭の中に自然に組み合わさる時点が K8s トラックの最後の段階です。

X