Kubernetes Patterns
本番環境でワークロードを確実にデプロイ、管理、デバッグするためのKubernetesパターン。
アクティブ化するタイミング
- Kubernetesマニフェストの作成(Deployment、Service、Ingress、Job)
- リソース要求/制限、liveness/readinessプローブの設定
- RBAC、Namespace、ServiceAccountの設定
- K8sにおける設定とシークレットの管理
- CrashLoopBackOff、OOMKilled、Pending Pod、イメージプルエラーのデバッグ
- HPA(Horizontal Pod Autoscaler)またはPodDisruptionBudgetの設定
- セキュリティや正当性のためのK8s YAMLのレビュー
使用するタイミング
上記のアクティブ化するタイミングと同じです。このエイリアスはリポジトリのスキル形式の慣習に従います。Kubernetes YAMLとワークロードの作成、レビュー、デバッグを行うときはいつでもこのスキルを使用してください。
仕組み
このスキルは、タスクごとに整理されたコピー&ペースト可能で本番環境グレードのYAMLパターンとkubectlデバッグコマンドを提供します。
- Deploymentテンプレート — セキュリティコンテキスト、ローリングアップデート戦略、3種類すべてのプローブ、リソース制限、ConfigMap/Secretからの環境変数注入を備えた、完全に構成された本番用Deployment。
- プローブ — startup vs liveness vs readinessの判断表と、正しい
failureThreshold × periodSecondsの計算。 - ServiceとIngress — cert-managerアノテーション付きのClusterIP、LoadBalancer、TLS Ingressパターン。
- ConfigMapとSecret —
envFrom、ファイルマウント、外部シークレットのガイダンス。 - リソース管理 — ワークロードタイプ(Web API、JVM、ワーカー、サイドカー)別の要求と制限の経験則。
- RBAC — 最小権限のServiceAccount→Role→RoleBindingチェーン。
- HPAとPDB — オートスケーリングとノードドレイン安全構成。
- JobとCronJob — 正しい
restartPolicyを用いた一時的およびスケジュールされたワークロードパターン。 - kubectlチートシート — ログ、exec、ロールバック、ポートフォワード、dry-run、一般的なエラー診断コマンド。
- アンチパターンとチェックリスト — やってはいけないことと、セキュリティ/信頼性/観測可能性のチェックリスト。
例
以下のセクションで、完全で実行可能な例を参照してください。クイックリファレンス:
| タスク | ジャンプ先 |
|---|---|
| 完全な本番用Deployment YAML | コアワークロードパターン |
| プローブ構成 | プローブ |
| RBAC最小権限設定 | RBAC |
| CrashLoopBackOffのデバッグ | kubectlデバッグチートシート |
| オートスケーリング | HPA |
コアワークロードパターン
Deployment — 本番テンプレート
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: my-namespace
labels:
app: my-app
version: "1.0.0"
spec:
replicas: 3
selector:
matchLabels:
app: my-app
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # アップデート中に1つの追加Podを許可
maxUnavailable: 0 # 希望数を下回らない
template:
metadata:
labels:
app: my-app
version: "1.0.0"
spec:
# Podレベルのセキュリティコンテキスト
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
# グレースフルシャットダウン
terminationGracePeriodSeconds: 30
containers:
- name: my-app
image: ghcr.io/org/my-app:1.0.0 # :latestは絶対に使用しない
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
protocol: TCP
# リソース要求と制限は両方とも必須
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
# コンテナセキュリティコンテキスト
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# プローブ(以下のプローブセクションを参照)
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 0
periodSeconds: 30
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
failureThreshold: 2
# ConfigMapとSecretからの環境変数
envFrom:
- configMapRef:
name: my-app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-app-secrets
key: db-password
# readOnlyRootFilesystem: true の場合の書き込み可能なtmpディレクトリ
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
プローブ — Liveness、Readiness、Startup
各プローブをいつ使用するかを理解することは重要です:
| プローブ | 失敗時のアクション | 使用目的 |
|---|---|---|
startupProbe | 起動が遅い場合にコンテナを強制終了 | 起動の遅いアプリ(JVM、Python) |
livenessProbe | コンテナを再起動 | デッドロック / ハングしたプロセスの検出 |
readinessProbe | Serviceのエンドポイントから削除 | 一時的な利用不可(DB再接続など) |
# 正しいパターン:startupProbeが起動の遅さをカバーし、
# その後liveness/readinessが引き継ぐ
startupProbe:
httpGet:
path: /health
port: 8080
failureThreshold: 30 # 30 * 5s = 最大150秒の起動時間
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
periodSeconds: 30
failureThreshold: 3 # 3 * 30s = 再起動まで90秒
readinessProbe:
httpGet:
path: /ready # 別のエンドポイント:DB、キャッシュなどをチェック
port: 8080
periodSeconds: 10
failureThreshold: 2
# 間違い:startupProbeなしでinitialDelaySecondsを使用
# アプリの起動に60秒かかる場合は、代わりにstartupProbeを設定する
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60 # 悪い例:任意の待機、競合状態
ServiceとIngress
Serviceタイプ
# ClusterIP(デフォルト)— 内部のみ
apiVersion: v1
kind: Service
metadata:
name: my-app
namespace: my-namespace
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
# LoadBalancer — 外部トラフィック(クラウドプロバイダー)
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8080
TLS付きIngress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
namespace: my-namespace
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: my-app-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app
port:
number: 80
ConfigMapとSecret
ConfigMap — 機密性の低い設定
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
namespace: my-namespace
data:
LOG_LEVEL: "info"
APP_ENV: "production"
MAX_CONNECTIONS: "100"
# 複雑な設定はファイルとしてマウント
app.yaml: |
server:
port: 8080
timeout: 30s
# ConfigMapをファイルとしてマウント
volumes:
- name: config
configMap:
name: my-app-config
items:
- key: app.yaml
path: app.yaml
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
Secret — 機密データ
# リテラルからSecretを作成(CLI、その後Vault/SOPSに保存)
kubectl create secret generic my-app-secrets \
--from-literal=db-password='s3cr3t' \
--namespace=my-namespace \
--dry-run=client -o yaml | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: my-app-secrets
namespace: my-namespace
type: Opaque
# 値はbase64エンコードされています(暗号化されてはいません — 実際の暗号化にはSealed SecretsまたはESOを使用)
data:
db-password: czNjcjN0 # 's3cr3t'のbase64
重要: 生のKubernetes Secretはbase64エンコードされているだけで、クラスターで暗号化が設定されていない限り保存時に暗号化されません。本番環境では、Sealed SecretsまたはExternal Secrets Operatorを使用してください。
リソース要求と制限
resources:
requests: # スケジューラーがPodの配置に使用
cpu: "100m" # 100ミリコア = 0.1 CPU
memory: "128Mi"
limits: # これを超えるとコンテナは強制終了/スロットリングされる
cpu: "500m"
memory: "256Mi"
経験則:
| ワークロードタイプ | CPU要求 | メモリ要求 | 備考 |
|---|---|---|---|
| Web API | 100–250m | 128–256Mi | 制限は要求の2〜4倍に設定 |
| ワーカー/コンシューマー | 250–500m | 256–512Mi | 予測可能性のためメモリ制限 = 要求 |
| JVMアプリ | 500m–1 | 512Mi–2Gi | JVMオーバーヘッドのために-Xmx以上の余裕を確保 |
| サイドカー | 10–50m | 32–64Mi | 最小限に抑える |
# 間違い:要求または制限がない — スケジューリングが予測不能、OOMによる強制終了
containers:
- name: app
image: myapp:latest
# resources: {} が欠落 — 本番環境では危険
# 間違い:制限のみで要求がない — 要求は制限にデフォルト設定され、容量を過剰に予約する
resources:
limits:
cpu: "2"
memory: "1Gi"
# requestsが欠落 — 制限値にデフォルト設定される
RBAC — RoleとServiceAccount
最小権限の原則
アプリがKubernetes APIを呼び出すかどうかによって2つのパターン:
パターンA — アプリがKubernetes APIを必要としない場合(ほとんどのアプリ)
ServiceAccountでトークンの自動マウントを無効にします。Role/RoleBindingは不要です。
# トークンが無効化されたServiceAccount — 最も安全なデフォルト
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: false # PodにK8s APIトークンが注入されない
# Deploymentでの参照 — トークンなし、APIアクセスなし
spec:
template:
spec:
serviceAccountName: my-app-sa
automountServiceAccountToken: false # 二重の安全策:Podレベルでも設定
パターンB — アプリがKubernetes APIを必要とする場合(Operator、コントローラー、設定監視)
トークンを有効にし、実際に必要な権限のみを付与します。
# 1. ServiceAccount — このSAのトークンを有効化
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: my-namespace
automountServiceAccountToken: true # トークンが必要:アプリがK8s APIを呼び出す
# 2. Role — アプリが必要とするものだけを付与(名前空間スコープ)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: my-app-role
namespace: my-namespace
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch"] # 読み取り専用、特定のリソース
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["my-app-secrets"] # 名前で特定のシークレットに制限
verbs: ["get"]
# 3. RoleをServiceAccountにバインド
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-rolebinding
namespace: my-namespace
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: my-namespace
roleRef:
kind: Role
apiGroup: rbac.authorization.k8s.io
name: my-app-role
# 4. DeploymentでSAを参照
spec:
template:
spec:
serviceAccountName: my-app-sa
# automountServiceAccountTokenはSAからデフォルトでtrue — トークンが注入される
Horizontal Pod Autoscaler (HPA)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-app-hpa
namespace: my-namespace
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 2 # HAのために常に最低2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 平均CPU使用率が70%を超えたらスケールアップ
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
HPAを使用するには、すべてのコンテナに
resources.requestsが設定されている必要があります。使用率は現在値 / 要求値として計算されます。
PodDisruptionBudget (PDB)
ノードドレインやローリングアップデート中にダウンするPodが多くなりすぎるのを防ぎます:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: my-namespace
spec:
minAvailable: 2 # または maxUnavailable: 1 を使用
selector:
matchLabels:
app: my-app
Namespaceとマルチテナンシー
# リソースクォータ付きでNamespaceを作成
kubectl create namespace my-namespace
# ResourceQuotaを適用してNamespaceの消費量を制限
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-namespace-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "4"
requests.memory: 4Gi
limits.cpu: "8"
limits.memory: 8Gi
pods: "20"
EOF
JobとCronJob
# 一時的なJob(DBマイグレーション、データ処理)
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: my-namespace
spec:
backoffLimit: 3 # 失敗時に最大3回再試行
ttlSecondsAfterFinished: 3600 # 1時間後に自動削除
template:
spec:
restartPolicy: OnFailure # Jobの場合はNeverではなくOnFailure
containers:
- name: migrate
image: ghcr.io/org/my-app:1.0.0
command: ["python", "manage.py", "migrate"]
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# スケジュールされたJob(CronJob)
apiVersion: batch/v1
kind: CronJob
metadata:
name: cleanup
namespace: my-namespace
spec:
schedule: "0 2 * * *" # 毎日午前2時に実行
concurrencyPolicy: Forbid # 実行中のジョブがあれば新しいジョブを開始しない
startingDeadlineSeconds: 300 # スケジュールから最大5分の遅延まで開始
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
restartPolicy: OnFailure
serviceAccountName: cleanup-sa
containers:
- name: cleanup
image: bitnami/kubectl:1.28
command: ["kubectl", "delete", "pod", "--field-selector=status.phase=Succeeded"]
kubectlデバッグチートシート
ログとexec
# リアルタイムログの追跡
kubectl logs -f deployment/my-app -n my-namespace
# 前のインスタンスのログ(クラッシュループの場合)
kubectl logs deployment/my-app --previous -n my-namespace
# ラベル選択による複数Podのログ
kubectl logs -l app=my-app -n my-namespace --tail=50
# シェルへのアクセス(デバッグ用)
kubectl exec -it deployment/my-app -n my-namespace -- /bin/sh
Podとイベント
# Podの詳細表示(イベントを含む)
kubectl describe pod my-app-7d8f9c4b5c-x9h2k -n my-namespace
# 名前空間内のすべてのイベント
eventskubectl get events -n my-namespace --sort-by='.lastTimestamp'
# YAML出力でリソースを確認(dry-runの代わりに-o yamlも使用可能)
kubectl get deployment my-app -n my-namespace -o yaml
ロールバックとデプロイ
# デプロイ履歴の表示
kubectl rollout history deployment/my-app -n my-namespace
# 前のリビジョンにロールバック
kubectl rollout undo deployment/my-app -n my-namespace --to-revision=2
# ロールアウトステータスの確認
kubectl rollout status deployment/my-app -n my-namespace
ポートフォワードとデバッグ
# ローカルポート8080をPodの8080に転送
kubectl port-forward deployment/my-app 8080:8080 -n my-namespace
# dnsutils Podを作成してDNSトラブルシューティング
kubectl run dnsutils --image=gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 --rm -it --restart=Never -- /bin/sh
# ネットワーク接続のテスト
kubectl run netshoot --image=nicolaka/netshoot --rm -it --restart=Never -- /bin/bash
トラブルシューティングコマンド
| 問題 | コマンド |
|---|---|
| CrashLoopBackOff | kubectl logs -f pod/<pod-name> --previous kubectl describe pod <pod-name>(終了コードと理由を確認) |
| OOMKilled | kubectl describe pod <pod-name> で Last State: Terminated / Reason: OOMKilled を確認 メモリ制限を引き上げるか、メモリリークを調査 |
| ImagePullBackOff | kubectl describe pod <pod-name> でイベントを確認 kubectl get pod <pod-name> -o yaml でイメージ名とpullPolicyを確認 crictl pull <image> でノード上で手動pullをテスト |
| Pending Pod | kubectl describe pod <pod-name> でイベントを確認 リソース不足の場合は kubectl describe node <node-name> で容量を確認 PVCがPendingの場合は kubectl get pvc |
リソース使用量の確認
# PodのCPU/メモリ使用量を表示
kubectl top pod -n my-namespace
# ノードのリソースを表示
kubectl top node
YAMLの検証とdry-run
# マニフェストを検証してクラスターに送信せずに適用
kubectl apply -f manifest.yaml --dry-run=client -o yaml
# 既存のデプロイに対して設定を検証
kubectl diff -f manifest.yaml
アンチパターンとチェックリスト
やってはいけないこと(アンチパターン)
:latestタグの使用 — 常に特定のセマンティックバージョンまたはSHAを使用するimagePullPolicy: Always(:latest以外) — デフォルトのIfNotPresentで十分、不必要なプルを減らす- startupProbeなしの
initialDelaySeconds— 競合状態と不安定な再起動を引き起こす resources: {}(要求または制限なし) — 予測不能なスケジューリングとOOMによる強制終了automountServiceAccountToken: true(APIアクセスが必要ない場合) — 不要なセキュリティリスク- 特権コンテナ — 絶対に必要な場合以外は避ける
- 環境変数に直接のシークレット値 — Secretまたは外部シークレットストアを使用する
- readinessProbeと同じエンドポイントのlivenessProbe — livenessProbeは別のエンドポイントをチェックしてデッドロックを検出する必要がある
restartPolicy: AlwaysのJob — 終了後に再起動ループを引き起こす- 過剰に寛容なRBACルール — ワイルドカードを使用したり、必要以上に多くのリソースへのアクセスを許可しない
本番環境チェックリスト
セキュリティ
- すべてのイメージに既知の脆弱性がないかスキャンされている
- コンテナは非rootユーザーとして実行されている(
runAsNonRoot: true) -
readOnlyRootFilesystem: true(書き込み可能なtmpディレクトリのみ) - デフォルトのcapabilityはすべて削除されている(
drop: ["ALL"]) -
allowPrivilegeEscalation: false - ServiceAccountトークンは必要なPodにのみマウントされている
- Secretは保存時に暗号化されている(etcd暗号化、Sealed Secrets、または外部シークレット)
- IngressはTLSを使用している(cert-manager)
- ネットワークポリシーが適用されている
信頼性
-
resources.requestsとresources.limitsがすべてのコンテナに設定されている - 適切なプローブが構成されている(startup + liveness + readiness)
- HPAが設定されている(CPUベース、できればカスタムメトリクスも)
- PDB(minAvailableまたはmaxUnavailable)が設定されている
-
replicas >= 2(HAのため) - ローリングアップデート戦略(maxSurge + maxUnavailable)
- ヘルスチェックエンドポイント(
/health、/ready、/metrics) - Startup/Shutdownの適切な猶予期間
観測可能性
- 構造化ログ出力(JSON形式)
- トレース相関のためのリクエストID
- Prometheusメトリクスエンドポイント
- livenessProbeがreadinessProbeと同じエンドポイントを使用していない
- readinessProbeがデータベースやキャッシュなどの依存関係の準備状態をチェックしている
運用
- スケジュールされたバックアップ(etcd、永続ボリューム)
- リソース使用量アラート(CPU、メモリ、ディスク)
- コンテナイメージの定期的な更新/パッチ
- デプロイメントのロールバック計画
- プレイブック/手順書の文書化