運用と管理
対応バージョン: EKS 1.29+, EKS Auto Mode GA 最終更新: February 19, 2026
このガイドでは、Disruption Budget(中断予算)、Rolling Replacement、モニタリング、トラブルシューティング、セキュリティのベストプラクティスなど、EKS Auto Mode の運用面について説明します。
Disruption Budget 設定
安全な Node 置き換えのために Budget を設定します。
yaml
# disruption-budget.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: production-pool
spec:
template:
spec:
requirements:
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["m", "c", "r"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
nodeClassRef:
group: eks.amazonaws.com
kind: NodeClass
name: default
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 5m
budgets:
# Default: only 10% of total nodes disrupted simultaneously
- nodes: "10%"
# Business hours: minimize disruptions
- nodes: "1"
schedule: "0 9-18 * * mon-fri" # Mon-Fri 9-18
duration: 9h
# Weekends: allow more aggressive consolidation
- nodes: "30%"
schedule: "0 0 * * sat-sun"
duration: 48h
# Emergency maintenance window: no disruptions
- nodes: "0"
schedule: "0 0 1 * *" # 1st of each month
duration: 24hRolling Replacement 戦略
yaml
# rolling-replacement-strategy.yaml
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: rolling-replacement
spec:
template:
spec:
requirements:
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["m"]
nodeClassRef:
group: eks.amazonaws.com
kind: NodeClass
name: default
# Node expiration time
expireAfter: 168h # 7 days
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 2m
budgets:
# Limit concurrent disrupted nodes for sequential replacement
- nodes: "1"Node Replacement 戦略の比較
| 戦略 | ユースケース | 設定 | トレードオフ |
|---|---|---|---|
| Rolling (Conservative) | 本番環境の重要用途 | nodes: "1" | 遅いがより安全 |
| Rolling (Moderate) | 標準的な本番環境 | nodes: "10%" | バランスの取れたアプローチ |
| Aggressive | Dev/Test、バッチ | nodes: "30%" | 高速だがリスクが高い |
| Scheduled | メンテナンスウィンドウ | 時間ベースの Budget | 予測可能な中断 |
各戦略を使用するタイミング
- Rolling (Conservative): Stateful workload、データベース、重要な API
- Rolling (Moderate): 標準的な Web Service、microservice
- Aggressive: CI/CD runner、バッチ処理、開発環境
- Scheduled: コンプライアンス要件、計画メンテナンス
PodDisruptionBudget との統合
yaml
# pdb-example.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-app-pdb
spec:
# Minimum available Pods
minAvailable: 3
# Or maximum unavailable Pods
# maxUnavailable: 1
selector:
matchLabels:
app: web-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 5
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web
image: nginx:latest
resources:
requests:
cpu: 500m
memory: 256Mi
# Graceful shutdown during node replacement
terminationGracePeriodSeconds: 60PDB のベストプラクティス
| Workload タイプ | Replica 数 | PDB 設定 |
|---|---|---|
| Stateless (3+ replicas) | 3-10 | minAvailable: N-1 または maxUnavailable: 1 |
| Stateless (10+ replicas) | 10+ | maxUnavailable: 10% |
| Stateful | 3+ | minAvailable: 2(quorum を維持) |
| Singleton | 1 | PDB なし(または中断を許容) |
Zone 障害への対応
Multi-AZ Deployment 設定
yaml
# multi-az-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: high-availability-app
spec:
replicas: 6
selector:
matchLabels:
app: ha-app
template:
metadata:
labels:
app: ha-app
spec:
# Availability zone distribution
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: ha-app
# Node distribution
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: ha-app
containers:
- name: app
image: my-app:latest
resources:
requests:
cpu: 500m
memory: 512Mi
---
# NodePool for guaranteed minimum nodes per zone
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: multi-az-pool
spec:
template:
spec:
requirements:
- key: topology.kubernetes.io/zone
operator: In
values: ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"]
- key: karpenter.k8s.aws/instance-category
operator: In
values: ["m", "c"]
nodeClassRef:
group: eks.amazonaws.com
kind: NodeClass
name: default
# Guarantee minimum capacity per zone
limits:
cpu: 1000Availability Zone 障害対応パターン
| パターン | 説明 | 実装 |
|---|---|---|
| Active-Active | すべての AZ がトラフィックを処理 | DoNotSchedule を使用した topologySpreadConstraints |
| Active-Standby | セカンダリ AZ へフェイルオーバー | Pod anti-affinity + health check |
| Capacity Reservation | 事前にプロビジョニングされた Capacity | On-Demand Capacity Reservations |
| Overflow | 他の AZ へバースト | ScheduleAnyway 制約 |
CloudWatch モニタリング
EKS Auto Mode はメトリクスを CloudWatch に自動的に送信します。
bash
# CloudWatch metric namespaces
# - AWS/EKS
# - Karpenter
# Key metrics
# - karpenter_nodes_total: Total node count
# - karpenter_pods_pending: Pending Pod count
# - karpenter_nodeclaims_created: Created NodeClaim count
# - karpenter_nodeclaims_terminated: Terminated NodeClaim countCloudWatch Dashboard セットアップ
json
{
"widgets": [
{
"type": "metric",
"properties": {
"title": "Auto Mode Node Count",
"metrics": [
["Karpenter", "karpenter_nodes_total", "cluster", "my-cluster"]
],
"period": 60
}
},
{
"type": "metric",
"properties": {
"title": "Pending Pods",
"metrics": [
["Karpenter", "karpenter_pods_pending", "cluster", "my-cluster"]
],
"period": 60
}
},
{
"type": "metric",
"properties": {
"title": "Node Provisioning Time",
"metrics": [
["Karpenter", "karpenter_nodeclaims_startup_duration_seconds", "cluster", "my-cluster"]
],
"stat": "p99",
"period": 300
}
}
]
}kubectl ベースの診断
bash
# Check NodePool status
kubectl get nodepools
kubectl describe nodepool general-purpose
# Check NodeClaim status (nodes being provisioned)
kubectl get nodeclaims
kubectl describe nodeclaim <name>
# Check node status and labels
kubectl get nodes -o wide -L karpenter.sh/nodepool,karpenter.sh/capacity-type
# Check Pending Pods
kubectl get pods -A --field-selector=status.phase=Pending
# Check events
kubectl get events --sort-by='.lastTimestamp' | grep -E "karpenter|nodepool|nodeclaim"
# Node resource usage
kubectl top nodes
# Pod distribution by node
kubectl get pods -A -o wide | awk '{print $8}' | sort | uniq -c | sort -rnよくある問題と解決策
問題 1: Pod が Pending 状態のままになる
bash
# Analyze cause
kubectl describe pod <pending-pod>
# Common causes:
# 1. Resource requests too large
# 2. NodePool limits exceeded
# 3. nodeSelector/affinity condition mismatch
# 4. taint/toleration mismatch
# Solution: Check NodePool limits
kubectl get nodepool -o yaml | grep -A5 limits
# Solution: Check nodeSelector conditions
kubectl get pod <pending-pod> -o yaml | grep -A10 nodeSelector問題 2: Node Provisioning 失敗
bash
# Check NodeClaim status
kubectl describe nodeclaim <name>
# Check error messages in events
kubectl get events --field-selector reason=FailedProvisioning
# Common causes:
# 1. Instance capacity shortage
# 2. Subnet IP exhaustion
# 3. IAM permission issues
# 4. Security group configuration errors
# Solution: Allow more diverse instance types
# Expand NodePool requirements問題 3: Node Consolidation が機能しない
bash
# Check Consolidation status
kubectl get nodeclaims -o custom-columns=\
NAME:.metadata.name,\
NODEPOOL:.metadata.labels.karpenter\\.sh/nodepool,\
PHASE:.status.phase,\
AGE:.metadata.creationTimestamp
# Check PodDisruptionBudget
kubectl get pdb -A
# Solution: Adjust PDB or check budgets settings問題 4: Spot Interrupt 後の Pod Rescheduling 遅延
bash
# Check Spot interrupt events
kubectl get events --sort-by='.lastTimestamp' | grep -i "spot\|interrupt"
# Solutions for fast re-provisioning:
# 1. Allow diverse instance types
# 2. Reduce consolidateAfter time
# 3. Use mixed Spot and On-Demandセキュリティのベストプラクティス
yaml
# security-best-practices.yaml
apiVersion: eks.amazonaws.com/v1
kind: NodeClass
metadata:
name: secure-nodeclass
spec:
amiFamily: Bottlerocket # Security-hardened OS
# IMDSv2 required
metadataOptions:
httpEndpoint: enabled
httpProtocolIPv6: disabled
httpPutResponseHopLimit: 1 # Block Pod IMDS access
httpTokens: required
# EBS encryption
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 100Gi
volumeType: gp3
encrypted: true
kmsKeyId: arn:aws:kms:ap-northeast-2:123456789:key/xxx
# Use only private subnets
subnetSelectorTerms:
- tags:
kubernetes.io/role/internal-elb: "1"
# Restrictive security groups
securityGroupSelectorTerms:
- tags:
Type: worker-restricted
---
# Apply Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: secure-namespace
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restrictedDay-2 Operations チェックリスト
日次タスク
| タスク | コマンド/アクション | 目的 |
|---|---|---|
| Pending Pod の確認 | kubectl get pods -A --field-selector=status.phase=Pending | スケジューリングの問題を特定 |
| Node ヘルスの確認 | kubectl get nodes | NotReady Node を検出 |
| Node Capacity の確認 | kubectl top nodes | リソース圧迫を監視 |
| Event の確認 | kubectl get events --sort-by='.lastTimestamp' | 警告/エラーを検出 |
週次タスク
| タスク | コマンド/アクション | 目的 |
|---|---|---|
| Node age の確認 | kubectl get nodes --sort-by='.metadata.creationTimestamp' | Node の鮮度を追跡 |
| NodePool limits の監査 | kubectl get nodepools -o yaml | 適切な limits を確認 |
| Consolidation の確認 | CloudWatch メトリクスを確認 | コスト最適化を検証 |
| Spot 使用状況の確認 | Capacity type の分布を確認 | コスト/可用性を最適化 |
月次タスク
| タスク | コマンド/アクション | 目的 |
|---|---|---|
| AMI バージョンの確認 | Node AMI ID を確認 | セキュリティパッチ適用 |
| Security group の監査 | NodeClass 設定を確認 | セキュリティコンプライアンス |
| コスト分析 | AWS Cost Explorer | Budget 追跡 |
| Capacity Planning | 使用傾向を確認 | 適切にスケール |
運用自動化
自動ローテーションアラート
yaml
# CloudWatch Alarm for old nodes
# Create alarm when nodes exceed age thresholdbash
# Script to check node ages
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.creationTimestamp}{"\n"}{end}' | \
while read name created; do
age_days=$(( ($(date +%s) - $(date -d "$created" +%s)) / 86400 ))
if [ $age_days -gt 7 ]; then
echo "WARNING: Node $name is $age_days days old"
fi
donePDB コンプライアンスモニタリング
bash
# Check PDB status across all namespaces
kubectl get pdb -A -o custom-columns=\
NAMESPACE:.metadata.namespace,\
NAME:.metadata.name,\
MIN-AVAILABLE:.spec.minAvailable,\
MAX-UNAVAILABLE:.spec.maxUnavailable,\
CURRENT:.status.currentHealthy,\
DESIRED:.status.desiredHealthy,\
DISRUPTIONS-ALLOWED:.status.disruptionsAllowedCapacity Dashboard クエリ
Capacity monitoring のための主要な Prometheus クエリ:
promql
# Total nodes by NodePool
count(kube_node_labels) by (label_karpenter_sh_nodepool)
# Node CPU utilization by pool
avg(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) by (node) * on(node) group_left(label_karpenter_sh_nodepool) kube_node_labels
# Pending pods over time
sum(kube_pod_status_phase{phase="Pending"})
# Node age distribution
(time() - kube_node_created) / 86400運用サマリーチェックリスト
| 領域 | チェックリスト項目 | ステータス |
|---|---|---|
| 設定 | NodePool limits 設定済み | |
| Disruption Budget 設定済み | ||
| NodeClass セキュリティ設定レビュー済み | ||
| モニタリング | CloudWatch dashboard 作成済み | |
| Alarm 設定済み(Pending Pod、provisioning failure) | ||
| コストモニタリング設定済み | ||
| 可用性 | PodDisruptionBudget 設定済み | |
| Multi-AZ 分散検証済み | ||
| Spot/On-Demand 混在比率レビュー済み | ||
| コスト | Spot instance 比率最適化済み | |
| Consolidation policy レビュー済み | ||
| Resource requests/limits の適切性レビュー済み |
< 前へ: Spot 戦略 | 目次 | 次へ: コスト管理 >