この記事の要点
• Kubernetes 1.30「Uwubernetes」はプロジェクト発足10周年の節目バージョン
• 合計45の機能強化(17がGA、18がBeta、10がAlpha)
• スケジューリング・Pod管理・ストレージ・セキュリティ領域で多数の改善
• 本番運用の信頼性と柔軟性が大きく向上
Kubernetes 1.30の概要
Kubernetes 1.30「Uwubernetes」は2024年4月17日にリリースされた、Kubernetesプロジェクト発足から10周年の節目となるバージョンです。本リリースには合計45の機能強化(KEP: Kubernetes Enhancement Proposal)が含まれ、そのうち17がStable(GA)に、18がBetaに、10がAlphaに昇格しました。特にスケジューリング、Pod管理、ストレージ、セキュリティといった領域で多くの改善が行われ、本番運用における信頼性と柔軟性が大きく向上しています。
背景: なぜ1.30が重要か
Kubernetesは年3回のマイナーリリースサイクルで進化しており、各リリースにおいて「Alpha → Beta → Stable」の段階を経て機能が成熟していきます。1.30では、長らくBetaに留まっていた主要機能が一挙にGA昇格を果たしました。これにより、これまで「本番では使いづらい」と見られていた機能が、安心してプロダクション環境で利用できるようになっています。
flowchart LR
subgraph K130["Kubernetes 1.30"]
GA["17 Stable (GA)"]
BETA["18 Beta"]
ALPHA["10 Alpha"]
end
GA --> Production["本番利用推奨"]
BETA --> Staging["検証環境で評価"]
ALPHA --> Lab["実験環境のみ"]
主要な新機能
1. Pod Scheduling Readiness(GA)
Podがスケジューリング可能かどうかを、外部コントローラから制御できる機能がGAになりました。schedulingGatesを設定したPodは、ゲートが外されるまでスケジューラによる配置対象になりません。これにより、クォータコントローラや依存リソース待ちのワークフローを安全に実装できます。
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
schedulingGates:
- name: example.com/wait-for-resources
- name: example.com/wait-for-quota
containers:
- name: app
image: nginx:1.27
resources:
requests:
cpu: "500m"
memory: "512Mi"
ゲートを解除するには、カスタムコントローラから以下のようにパッチを適用します。
kubectl patch pod my-pod --type=json \
-p='[{"op": "remove", "path": "/spec/schedulingGates"}]'
2. PodDisruptionConditions(GA)
Podが中断された理由をstatus.conditionsで詳細に把握できるようになりました。バッチジョブのリトライ判断や、可用性分析に活用できます。
# Podの中断理由を確認
kubectl get pod my-pod -o jsonpath='{.status.conditions}' | jq
# よくあるConditionの種類:
# - DisruptionTarget: Preemption / EvictionByEvictionAPI / TerminationByKubelet
# - PodScheduled / Ready / ContainersReady / Initialized
3. Min Domains in PodTopologySpread(GA)
topologySpreadConstraintsで「最低限のドメイン数」を指定できるようになりました。例えば「必ず3つ以上のゾーンに分散する」といった制約を強制できます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 9
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
minDomains: 3
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels: { app: web }
containers:
- name: web
image: nginx:1.27
4. Container Resource based Pod Autoscaling(Beta)
HorizontalPodAutoscaler(HPA)が、Pod全体ではなく特定コンテナのリソース使用率でスケールできるようになりました。サイドカー構成で本体コンテナの負荷のみに応じてスケールしたい場合に有効です。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app
minReplicas: 2
maxReplicas: 20
metrics:
- type: ContainerResource
containerResource:
name: cpu
container: app
target:
type: Utilization
averageUtilization: 70
5. Recursive Read-only Mounts(Beta)
マウントされたボリューム内のサブマウントも再帰的に読み取り専用にできます。従来のreadOnly: trueではサブマウント(bind mountなど)には効かない問題がありましたが、これが解決されました。
volumeMounts:
- name: data
mountPath: /data
readOnly: true
recursiveReadOnly: Enabled # IfPossible も選択可能
GAとは: General Availabilityの略で、本番環境での使用が推奨される安定版機能を意味します。BetaからGAに昇格することで、APIの後方互換性が保証されます。
Betaに昇格した機能
Job Success/Failure Policy
Jobの成功・失敗条件をより細かく制御できます。特定のインデックスの失敗を許容したり、特定の終了コードでJob全体を失敗と判断したりできます。
apiVersion: batch/v1
kind: Job
metadata:
name: data-processing
spec:
completions: 10
parallelism: 5
completionMode: Indexed
successPolicy:
rules:
- succeededIndexes: "0-2"
succeededCount: 3
podFailurePolicy:
rules:
- action: FailJob
onExitCodes:
operator: In
values: [42]
template:
spec:
restartPolicy: Never
containers:
- name: worker
image: my-batch-worker:1.0
ServiceAccount Tokens for Image Pulls
ServiceAccountトークンを用いてプライベートレジストリから直接イメージをPullできるようになりました。長期有効なimagePullSecretsの管理負担が軽減されます。
Structured Authorization Configuration
kube-apiserverの認可チェーンをファイルで構造化して記述できます。複数のWebhookやRBACを柔軟に組み合わせられます。
非推奨・削除された機能
削除
- SecurityContextDeny admission plugin: Pod Security Admissionへの移行が完了したため削除
- 古いcloud provider関連: in-tree cloud providersの整理が進行
kubectl exec [POD] [COMMAND]の古いフラグ形式:--の使用が必須に
非推奨
status.nodeInfo.kubeProxyVersionフィールドPodHostIPsの古い表現- 一部のAlpha段階で長く滞留していたフィーチャーゲート
アップグレード時の注意点
- 削除された機能やAPIバージョンを使用していないか、事前に
kubectl convertやkube-no-trouble(kubent)で確認する PodSecurityPolicyはすでに削除済み。PodSecurityAdmissionへの移行が必須- クラスタのバックアップ(etcdスナップショット)を必ず取得する
- ステージング環境で事前検証し、ノードは順次drainする
- CRI、CNI、CSIドライバーが1.30に対応済みか確認
# 現在のバージョン確認
kubectl version
# 非推奨APIの使用状況確認
kubectl get --raw /metrics | grep apiserver_requested_deprecated_apis
# kubentで非推奨APIを検出
kubent
# etcdスナップショット
ETCDCTL_API=3 etcdctl snapshot save backup.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
旧バージョンとの比較
| 項目 | 1.28 | 1.29 | 1.30 |
|---|---|---|---|
| Pod Scheduling Readiness | Beta | Beta | GA |
| Min Domains in Topology Spread | Beta | Beta | GA |
| PodDisruptionConditions | Beta | Beta | GA |
| Sidecar containers | Alpha | Beta | Beta |
| Recursive read-only mounts | - | Alpha | Beta |
| ValidatingAdmissionPolicy | Beta | GA | GA |
| in-tree cloud providers | Deprecating | Removing | Mostly removed |
実践: 1.30機能を活用したマニフェスト例
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels: { app: api }
spec:
replicas: 6
selector:
matchLabels: { app: api }
template:
metadata:
labels: { app: api }
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
minDomains: 3
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels: { app: api }
initContainers:
- name: logger
image: fluent/fluent-bit:3.0
restartPolicy: Always # Sidecar mode
containers:
- name: api
image: my-api:1.2.0
resources:
requests: { cpu: 500m, memory: 512Mi }
limits: { cpu: "1", memory: 1Gi }
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
recursiveReadOnly: Enabled
volumes:
- name: config
configMap:
name: api-config
ベストプラクティス
- 段階的ロールアウト: 開発 → ステージング → 本番の順でアップグレード
- Pod Security Admission:
baselineまたはrestrictedプロファイルを全Namespaceに適用 - リソース制限の徹底:
requestsとlimitsを必ず設定 - PodDisruptionBudget: 可用性を守るため、重要WorkloadにはPDBを必ず設定
- ネットワークポリシー: デフォルト拒否ポリシーからホワイトリスト方式で設計
- 監査ログ:
Audit Policyを設定し、APIコールをログに残す
よくある落とし穴とトラブルシューティング
1. CRIソケットの互換性
1.24以降dockershimが削除されたため、引き続きcontainerdまたはCRI-Oを使用する必要があります。
# 使用中のランタイムを確認
kubectl get nodes -o wide
crictl info
2. cgroup v2への移行漏れ
1.25以降、cgroup v2の利用が推奨されています。v1のままだと一部の機能(MemoryQoSなど)が動作しません。
stat -fc %T /sys/fs/cgroup/
# cgroup2fs と出力されればv2
3. Sidecar containers設定漏れ
initContainersでrestartPolicy: Alwaysを指定しないと、従来のinit動作となり正しく常駐しません。
導入手順(kubeadmクラスタの例)
# 1. リリース情報を取得
sudo apt-mark unhold kubeadm
sudo apt-get update && sudo apt-get install -y kubeadm=1.30.0-*
# 2. アップグレードプランを確認
sudo kubeadm upgrade plan
# 3. コントロールプレーンをアップグレード
sudo kubeadm upgrade apply v1.30.0
# 4. kubelet/kubectlをアップグレード
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=1.30.0-* kubectl=1.30.0-*
sudo systemctl daemon-reload && sudo systemctl restart kubelet
# 5. ワーカーノードをひとつずつdrainしてアップグレード
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
# 同様にkubeadm upgrade node / kubelet更新
kubectl uncordon <node>
パフォーマンスとスケーラビリティ
1.30では内部的なスケジューラ最適化により、大規模クラスタでのPod配置レイテンシが改善されています。一般的な指標として、Kubernetesは以下のスケールをサポートしています。
| 項目 | 推奨上限 |
|---|---|
| ノード数 | 5,000 |
| Pod数 / ノード | 110 |
| Pod総数 | 150,000 |
| コンテナ総数 | 300,000 |
| Namespace数 | 10,000 |
上限に近づく場合は、etcdのディスクI/O、API Serverのリクエストレート、Controller Managerのqpsを計測し、必要に応じてチューニングします。
FAQ
Q1. 1.28から1.30へ一気に上げられますか? A. kubeadmはスキップアップグレードをサポートしていません。1.28 → 1.29 → 1.30と順次上げる必要があります。
Q2. PodSecurityPolicyはまだ使えますか? A. 1.25で削除済みです。Pod Security Admission、またはOPA Gatekeeper、Kyvernoなどのポリシーエンジンへ移行してください。
Q3. Sidecar containersはどのAPIで有効になりますか?
A. initContainersでrestartPolicy: Alwaysを指定することで、サイドカーとして常駐します。1.29でBetaに昇格し、1.30でも引き続きBetaです。
Q4. ValidatingAdmissionPolicyはどう使いますか? A. CEL式で宣言的にAdmission制御ルールを書けます。Webhookの代替として、パフォーマンスと運用性が向上します。
Q5. アップグレード後、既存のマニフェストは動きますか?
A. 削除されたAPIを使っていなければ基本的に動作します。事前にkubentで検査しておくことを強く推奨します。
まとめ
Kubernetes 1.30は、スケジューリングとPod管理に関する多くの機能がGAに昇格し、本番環境での利用がより安心になりました。特にschedulingGates、minDomains、PodDisruptionConditionsの正式化は、大規模運用にとって大きな意味を持ちます。アップグレード時は非推奨機能の確認とetcdバックアップを忘れず、段階的に本番展開を進めましょう。
実運用のためのHelm例
1.30向けに典型的なWebアプリケーションをHelmで管理する例を示します。リソースリミット、ヘルスチェック、PDB、HPA、NetworkPolicyまでを一式揃えた構成です。
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "app.fullname" . }}
labels: {{- include "app.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels: {{- include "app.selectorLabels" . | nindent 6 }}
template:
metadata:
labels: {{- include "app.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "app.fullname" . }}
securityContext:
runAsNonRoot: true
runAsUser: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
minDomains: 3
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels: {{- include "app.selectorLabels" . | nindent 14 }}
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
readinessProbe:
httpGet: { path: /healthz, port: http }
periodSeconds: 5
livenessProbe:
httpGet: { path: /livez, port: http }
periodSeconds: 10
resources:
requests: { cpu: 500m, memory: 512Mi }
limits: { cpu: "1", memory: 1Gi }
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
# templates/pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "app.fullname" . }}
spec:
minAvailable: 2
selector:
matchLabels: {{- include "app.selectorLabels" . | nindent 6 }}
# templates/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "app.fullname" . }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "app.fullname" . }}
minReplicas: 3
maxReplicas: 30
metrics:
- type: ContainerResource
containerResource:
name: cpu
container: app
target:
type: Utilization
averageUtilization: 70
# templates/networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "app.fullname" . }}
spec:
podSelector:
matchLabels: {{- include "app.selectorLabels" . | nindent 6 }}
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector:
matchLabels: { kubernetes.io/metadata.name: ingress }
ports:
- protocol: TCP
port: 8080
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 53
- protocol: UDP
port: 53
監視とロギング
Kubernetes 1.30クラスタの監視はPrometheus + Grafanaが定番です。特に1.30で追加されたPodDisruptionConditionsやschedulingGates周りのメトリクスは運用改善のヒントになります。
# ServiceMonitor例(kube-prometheus-stack)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: app-metrics
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app: app
endpoints:
- port: http
path: /metrics
interval: 15s
重要な監視項目:
kube_pod_status_phase/kube_pod_container_status_restarts_totalscheduler_pending_pods/scheduler_scheduling_attempt_duration_secondsapiserver_request_duration_seconds(p99)etcd_disk_wal_fsync_duration_seconds(etcd健全性)kubelet_runtime_operations_errors_total
セキュリティチェックリスト
1.30クラスタで最低限確認しておきたいセキュリティ項目です。
- Pod Security Admission を全Namespaceで有効化
- RBACを最小権限で構成し、ClusterAdminを制限
- Secretsは暗号化保存(
--encryption-provider-config) - 監査ログを外部ストレージへ転送
- ImagePullPolicyとSignature検証(cosign/sigstore)
- NetworkPolicyでデフォルト拒否
- CISベンチマーク準拠(
kube-benchで検査) - 定期的な脆弱性スキャン(
trivy k8s cluster)