この記事の対象読者
- DockerとDocker Composeの基本は理解している方
- 「コンテナが1台のマシンに収まらなくなったらどうするんだ?」と疑問を持ち始めた方
- AI/ML基盤やマイクロサービスの本番運用に向けて、コンテナオーケストレーションの全体像を掴みたい方
この記事で得られること
- Kubernetesの本質: 「なぜDocker Composeでは不十分なのか」が腹落ちする
- コアコンセプトの完全理解: Pod、Deployment、Service、Namespace — 全てが「宣言的管理」で繋がる
- 実践力: minikubeでのローカル構築から、GPUワークロードのスケジューリングまで体験できる
この記事で扱わないこと
- Kubernetesクラスタの本番構築(kubeadm、EKS、GKE等の詳細)
- Helm、Kustomize等のパッケージ管理ツールの詳細
- Istio等のサービスメッシュ
1. Kubernetesとの出会い
docker compose upで全てが動いた時、私は満足していた。8台のコンテナが1台のマシンで快適に動く。しかしその「1台」が死んだら?
事の発端は、RTX 5090環境でOllama + Open WebUI + PostgreSQL + Redisの構成をDocker Composeで組んだ時のことです。ローカルでは完璧に動いている。しかし次の3つの疑問が浮かんできました。
- このマシンが落ちたら、全サービスが停止する(単一障害点)
- リクエストが増えたら、コンテナを別のマシンに分散したい(スケールアウト)
- コンテナが異常終了したら、自動で復旧してほしい(セルフヒーリング)
Docker Composeは「1台のマシン内で複数コンテナを管理する」ツールです。では、複数のマシンにまたがるコンテナ群を管理するにはどうすればいいのか。
Kubernetes(K8s)は、複数マシンをひとつのクラスタとして束ね、コンテナの配置・スケーリング・自己回復を自動で行うオーケストレーションプラットフォームです。
料理に例えるなら、Dockerは「一皿を作る技術」、Docker Composeは「コース料理のレシピ」、そしてKubernetesは「厨房全体のオペレーションシステム」です。シェフ(Node)が何人いても、注文(ワークロード)を最適に振り分け、誰かが倒れたら別の人が代わりに料理を仕上げる。
ここまでで、Kubernetesの存在意義がなんとなくイメージできたでしょうか。次は、この記事で使う用語を整理しておきましょう。
2. 前提知識の確認
本題に入る前に、この記事で頻出する用語を確認します。
2.1 クラスタ(Cluster)とは
Kubernetesにおけるクラスタは、1台以上のマシン(ノード)をまとめた「コンテナを動かすための基盤」です。1台のマシンでも、100台のマシンでも、Kubernetesの視点からは「1つのクラスタ」として統一的に扱えます。
2.2 ノード(Node)とは
クラスタを構成する個々のマシンです。コントロールプレーンノード(司令塔)とワーカーノード(実際にコンテナを動かす現場)の2種類があります。物理マシンでも仮想マシンでもOKです。
2.3 マニフェスト(Manifest)とは
Kubernetesに「こういう状態にしてくれ」と伝えるYAMLファイルです。Docker Composeのcompose.yamlに相当しますが、Kubernetesのマニフェストは「リソースの種類(Kind)」ごとに分かれているのが特徴です。
2.4 宣言的管理(Declarative Management)とは
「3つのWebサーバーが動いている状態にしてくれ」と目標状態を宣言すると、Kubernetesが現在の状態とのギャップを自動的に埋めてくれる管理モデルです。「コンテナを起動しろ」「停止しろ」と命令する命令的(Imperative)管理とは根本的に異なります。1台落ちたら自動で1台追加してくれるセルフヒーリングの土台もこの仕組みです。
2.5 kubectl(キューブシーティーエル)とは
Kubernetesクラスタを操作するためのCLIツールです。Dockerのdockerコマンド、Docker Composeのdocker composeコマンドに相当します。
これらの用語が押さえられたら、Kubernetesの歴史と背景を見ていきましょう。
3. Kubernetesが生まれた背景
3.1 Google社内の「秘密兵器」 — Borg
Kubernetesの物語は、2000年代初頭のGoogleから始まります。
当時のGoogleは、検索エンジン、Gmail、YouTube等のサービスを数十万台のサーバーで運用していました。この規模になると「人間が手動でサーバーを管理する」のは不可能です。そこで開発されたのが、社内コンテナ管理システムBorgでした。
BorgはStar Trekに登場する「集合意識体」が名前の由来。個体が自律的に動きながら全体として1つのシステムとして機能する — まさにクラスタ管理の比喩です。
Borgは10年以上にわたってGoogleの基盤を支え続けましたが、社外には公開されませんでした。Googleにとって「競争優位」だったからです。
3.2 Docker登場とオーケストレーション戦争
2013年、Dockerがコンテナ技術を一般開発者にとって手軽なものに変えました。しかし「コンテナをどう大規模に管理するか」は未解決のまま。そこでGoogleのBorgチームの数名が、Borgの設計思想をオープンソースで再構築するプロジェクトを始めます。
社内コードネームは「Project Seven of Nine」。こちらもStar Trekネタで、Borgから解放されたキャラクターの名前です。Kubernetesのロゴにある7本のスポークは、このコードネームに由来します。
2014年6月 — GoogleがKubernetesをオープンソースとして公開。
その後、コンテナオーケストレーションの覇権を巡って「三国志」が勃発します。
| 年 | 出来事 |
|---|---|
| 2014年6月 | Google、Kubernetesをオープンソース公開 |
| 2015年7月 | Kubernetes v1.0リリース。同時にCNCF(Cloud Native Computing Foundation)設立、初のホストプロジェクトに |
| 2015年秋 | Docker Swarm登場 — Docker社の公式オーケストレーター |
| 2016-2017年 | Kubernetes vs Docker Swarm vs Apache Mesos の「オーケストレーション戦争」 |
| 2017年末 | AWS、Azure、GCPの全主要クラウドがKubernetesをマネージドサービスとして提供開始 |
| 2018年 | Docker社がKubernetesをDocker Desktopに統合 — 事実上の降伏宣言 |
| 2024年 | CNCF年次調査: 92%の組織がコンテナを本番運用、K8sがデファクトスタンダードに |
Docker SwarmとKubernetesは「競合」と説明されることが多いですが、レイヤーが異なります。Dockerはコンテナランタイム、Kubernetesはオーケストレーター。現在もKubernetesの内部でDockerのコンテナ技術(containerd)は使われています。
3.3 2026年のKubernetes — v1.35 "Timbernetes"
2026年3月現在の最新安定版はKubernetes v1.35.1(2026年2月10日リリース)。コードネームは "Timbernetes(The World Tree Release)" です。60の新機能強化が含まれ、特にAI/ML向けの進化が目立ちます。
| 機能 | ステータス | 概要 |
|---|---|---|
| In-Place Pod Resource Resize | GA(安定版) | Podを再起動せずにCPU/メモリを変更可能 |
| Gang Scheduling | Alpha | 分散学習ジョブの「全Pod同時起動 or 起動しない」制御 |
| Dynamic Resource Allocation(DRA) | GA(v1.34〜) | GPU等の特殊ハードウェアを柔軟に割り当て |
| RestartAllContainers | Alpha | AI/MLジョブの障害回復をPod内で完結 |
| maxParallelImagePulls | GA | イメージ並列ダウンロード数を制御、大規模クラスタの起動高速化 |
| Pod証明書(mTLS) | Beta | Pod単位でx.509証明書を自動発行・ローテーション |
v1.35の目玉はIn-Place Pod Resource Resize(GA) です。従来、Podのリソース変更にはPodの再作成が必要でした。AI推論サービスのような「再起動のコストが高い」ワークロードにとって、これは革命的な変化です。
また、次期バージョン v1.36 は2026年4月22日リリース予定で、Gateway APIの大幅強化とWindowsコンテナサポートの改善が予告されています。
背景がわかったところで、基本的な仕組みを見ていきましょう。
4. 基本概念と仕組み
4.1 Kubernetesのアーキテクチャ全体像
Kubernetesクラスタは、大きくコントロールプレーン(司令塔)とワーカーノード(現場)に分かれます。
┌──────────────────────── Kubernetes Cluster ────────────────────────────┐
│ │
│ ┌─── Control Plane(コントロールプレーン)───────────────────────┐ │
│ │ │ │
│ │ ┌──────────┐ ┌───────────┐ ┌──────────┐ ┌─────────────┐ │ │
│ │ │ API │ │ etcd │ │Scheduler │ │ Controller │ │ │
│ │ │ Server │ │(状態保存) │ │(配置決定)│ │ Manager │ │ │
│ │ └──────────┘ └───────────┘ └──────────┘ └─────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │ kubectl / API │
│ ┌─── Worker Node 1 ─────┼───────────┐ ┌─── Worker Node 2 ──────┐ │
│ │ ┌────────┐ │ │ │ ┌────────┐ │ │
│ │ │kubelet │ ┌─────────────────┐ │ │ │kubelet │ ┌──────┐ │ │
│ │ └────────┘ │ Pod A │ │ │ └────────┘ │Pod C │ │ │
│ │ ┌────────┐ │ ┌────┐ ┌────┐ │ │ │ ┌────────┐ └──────┘ │ │
│ │ │kube- │ │ │ C1 │ │ C2 │ │ │ │ │kube- │ ┌──────┐ │ │
│ │ │proxy │ │ └────┘ └────┘ │ │ │ │proxy │ │Pod D │ │ │
│ │ └────────┘ └─────────────────┘ │ │ └────────┘ └──────┘ │ │
│ │ ┌──────┐ │ │ │ │
│ │ │Pod B │ │ │ │ │
│ │ └──────┘ │ │ │ │
│ └────────────────────────────────────┘ └────────────────────────┘ │
└────────────────────────────────────────────────────────────────────────┘
コントロールプレーンの主要コンポーネント:
| コンポーネント | 役割 | 料理の例え |
|---|---|---|
| API Server | 全ての操作の入り口。kubectlもここを通る |
レストランのフロント受付 |
| etcd | クラスタの状態を保存する分散KVS | 全注文を記録する台帳 |
| Scheduler | Podをどのノードに配置するか決定 | 席の案内係 |
| Controller Manager | 「宣言された状態」と「実際の状態」のギャップを埋め続ける | 店長(問題を自動修復) |
ワーカーノードの主要コンポーネント:
| コンポーネント | 役割 |
|---|---|
| kubelet | ノード上のコンテナ管理エージェント。Podの起動・監視・報告を行う |
| kube-proxy | ネットワークルーティング。Serviceへのトラフィックを適切なPodに転送 |
| Container Runtime | 実際にコンテナを動かすエンジン(containerd、CRI-O等) |
4.2 Pod — Kubernetesの最小単位
Pod(ポッド)は、Kubernetesでコンテナを動かす最小デプロイ単位です。
Dockerの世界では「コンテナ」が最小単位ですが、Kubernetesでは「Pod」です。Pod内には1つ以上のコンテナが含まれ、同じネットワーク空間(IPアドレス)とストレージを共有します。
┌─────── Pod ─────────────────────────────────────────┐
│ │
│ ┌────────────────┐ ┌────────────────────────────┐ │
│ │ メインコンテナ │ │ サイドカーコンテナ │ │
│ │ (Webサーバー) │ │ (ログ収集 / プロキシ) │ │
│ └────────────────┘ └────────────────────────────┘ │
│ │
│ 共有: IPアドレス / localhost通信 / ボリューム │
└──────────────────────────────────────────────────────┘
「1 Pod = 1 コンテナ」が基本パターンです。複数コンテナを入れるのは「メイン + サイドカー」構成(ログ収集、プロキシ等)のような明確な理由がある場合に限ります。
Docker Composeとの比較:
| 概念 | Docker Compose | Kubernetes |
|---|---|---|
| コンテナの単位 | サービス(service) | Pod |
| 複数コンテナのグルーピング | compose.yaml全体 | Pod内のコンテナ群 |
| IPアドレス | コンテナごとに異なる | Pod内は同一IP |
| スケーリング | docker compose up --scale web=3 |
Deploymentのreplicas: 3
|
4.3 Deployment — 「望ましい状態」を宣言する
Deploymentは、「このPodを何個動かし続けてほしいか」を宣言するリソースです。
Podを直接作ることもできますが、実運用ではDeploymentを使います。なぜなら、Deploymentは以下を自動で行ってくれるからです。
- レプリカ管理: 指定した数のPodを常に維持
- セルフヒーリング: Podが落ちたら自動で新しいPodを作成
- ローリングアップデート: アプリの更新時にダウンタイムなしでPodを入れ替え
- ロールバック: 更新に失敗したら前のバージョンに戻す
# deployment.yaml — 最もシンプルな例
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3 # 常に3つのPodを維持
selector:
matchLabels:
app: web-app
template: # Podのテンプレート
metadata:
labels:
app: web-app
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests: # 最低限必要なリソース
cpu: "100m"
memory: "128Mi"
limits: # 上限
cpu: "500m"
memory: "256Mi"
この1枚のマニフェストを適用するだけで、Kubernetesは3つのPodを起動し、1つが落ちたら自動で復旧し、リソース使用量を監視してくれます。
4.4 Service — Podへの安定したアクセス経路
Podは短命です。落ちたら新しいPodが別のIPアドレスで生まれます。では、他のサービスやユーザーは「どのIP」にアクセスすればいいのか?
Service(サービス)は、Podの集合に対して安定したDNS名とIPアドレスを提供するリソースです。
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: web-app-service
spec:
selector:
app: web-app # label「app: web-app」を持つPodにトラフィックを転送
ports:
- port: 80 # Service自体のポート
targetPort: 80 # Pod内コンテナのポート
type: ClusterIP # クラスタ内部のみアクセス可能(デフォルト)
Serviceのタイプ:
| タイプ | アクセス範囲 | 用途 |
|---|---|---|
| ClusterIP | クラスタ内部のみ | マイクロサービス間通信 |
| NodePort | クラスタ外部(ノードのIP:ポート) | 開発・テスト用 |
| LoadBalancer | クラウドのロードバランサー経由 | 本番Web公開 |
Docker Composeでは、サービス名がそのままDNS名になりました(例: http://postgres:5432)。KubernetesのServiceも同じ思想ですが、「Podの集合」に対してDNS名を付ける点が異なります。http://web-app-service:80でアクセスすると、背後の3つのPodに自動で負荷分散されます。
4.5 Namespace — クラスタ内の仮想的な仕切り
Namespaceは、1つのクラスタ内にリソースの論理的な境界を作る仕組みです。
┌────────── Kubernetes Cluster ──────────────────────┐
│ │
│ ┌── Namespace: dev ──┐ ┌── Namespace: prod ──┐ │
│ │ web-app (3 Pod) │ │ web-app (10 Pod) │ │
│ │ db (1 Pod) │ │ db-primary (1 Pod) │ │
│ │ redis (1 Pod) │ │ db-replica (2 Pod) │ │
│ └────────────────────┘ │ redis (3 Pod) │ │
│ └──────────────────────┘ │
│ ┌── Namespace: monitoring ─────────────────────┐ │
│ │ prometheus (1 Pod) │ grafana (1 Pod) │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
同じweb-appという名前のDeploymentでも、devとprodで完全に独立して管理できます。チームやプロジェクトごとにNamespaceを分けるのが一般的です。
4.6 ConfigMap / Secret — 設定と機密情報の分離
| リソース | 用途 | 保存形式 |
|---|---|---|
| ConfigMap | 非機密の設定情報(DB接続先、機能フラグ等) | 平文 |
| Secret | 機密情報(パスワード、APIキー、TLS証明書等) | Base64エンコード |
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "postgres-service"
LOG_LEVEL: "INFO"
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
data:
DATABASE_PASSWORD: c2VjcmV0MTIz # echo -n 'secret123' | base64
KubernetesのSecretはデフォルトではBase64エンコードのみです。暗号化ではありません。本番運用ではetcdの暗号化やExternal Secret Operator等の導入を検討してください。
基本概念が理解できたところで、実際にコードを書いて動かしてみましょう。
5. 実践:実際に使ってみよう
5.1 環境構築 — minikubeでローカルKubernetesクラスタを起動
本番ではAWS EKSやGoogle GKE等のマネージドサービスを使いますが、学習にはminikubeが最適です。ローカルPCに1ノードのKubernetesクラスタを構築できます。
ローカルKubernetesツールの比較:
| ツール | 特徴 | 向いている用途 |
|---|---|---|
| minikube | 最もポピュラー。アドオン豊富 | 学習、機能検証 |
| kind | Dockerコンテナ内にクラスタ構築。起動が速い | CI/CDパイプライン |
| k3s | 軽量K8sディストリビューション。バイナリ100MB未満 | エッジ、IoT、リソース制約環境 |
# --- minikube + kubectl のインストール ---
# macOS (Homebrew)
brew install minikube kubectl
# Windows (WSL2環境推奨 — WSLの導入は下記記事参照)
# https://qiita.com/GeneLab_999/items/29ee4567b0254d47bbc8
choco install minikube kubernetes-cli
# Linux (Ubuntu — Ubuntuの導入は下記記事参照)
# https://qiita.com/GeneLab_999/items/be33633ff1b27f813214
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
# --- クラスタの起動 ---
minikube start --driver=docker --cpus=2 --memory=4096
# 確認
kubectl cluster-info
kubectl get nodes
5.2 環境別の設定ファイル
KubernetesでもDocker Compose同様、環境ごとに設定を分けます。Namespaceで環境を分離し、ConfigMapで設定値を切り替えるのが基本パターンです。
開発環境用(configmap-dev.yaml)
# configmap-dev.yaml — 開発環境用(このままコピーして使える)
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: dev
data:
ENVIRONMENT: "development"
DEBUG: "true"
LOG_LEVEL: "DEBUG"
DATABASE_HOST: "postgres-service"
DATABASE_PORT: "5432"
REDIS_HOST: "redis-service"
OLLAMA_HOST: "http://ollama-service:11434"
本番環境用(configmap-prod.yaml)
# configmap-prod.yaml — 本番環境用
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: prod
data:
ENVIRONMENT: "production"
DEBUG: "false"
LOG_LEVEL: "INFO"
DATABASE_HOST: "postgres-primary-service"
DATABASE_PORT: "5432"
REDIS_HOST: "redis-cluster-service"
OLLAMA_HOST: "http://ollama-service:11434"
テスト環境用(configmap-test.yaml)
# configmap-test.yaml — CI/CD用
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: test
data:
ENVIRONMENT: "test"
DEBUG: "true"
LOG_LEVEL: "DEBUG"
DATABASE_HOST: "postgres-test-service"
DATABASE_PORT: "5432"
REDIS_HOST: "redis-test-service"
OLLAMA_HOST: "http://ollama-test-service:11434"
5.3 基本的な使い方 — kubectl操作ガイド
#!/bin/bash
# k8s_guide.sh — Kubernetes基本操作リファレンス
# ============================================
# 1. リソースの適用(マニフェストの適用)
# ============================================
kubectl apply -f deployment.yaml # 単一ファイル適用
kubectl apply -f ./manifests/ # ディレクトリ内の全ファイル適用
kubectl apply -f configmap-dev.yaml # 開発用ConfigMap適用
# ============================================
# 2. リソースの確認
# ============================================
kubectl get pods # Pod一覧
kubectl get pods -o wide # IPやノード情報も表示
kubectl get deployments # Deployment一覧
kubectl get services # Service一覧
kubectl get all # 全リソース一覧
kubectl get all -n prod # prod Namespace内の全リソース
# ============================================
# 3. 詳細確認とデバッグ
# ============================================
kubectl describe pod <pod名> # Podの詳細(イベントログ含む)
kubectl logs <pod名> # コンテナログ表示
kubectl logs <pod名> -f # ログをリアルタイム追跡
kubectl logs <pod名> -c <コンテナ名> # マルチコンテナPodの特定コンテナ
kubectl exec -it <pod名> -- /bin/bash # Pod内にシェル接続
# ============================================
# 4. スケーリング
# ============================================
kubectl scale deployment web-app --replicas=5 # Pod数を5に変更
kubectl autoscale deployment web-app \
--min=2 --max=10 --cpu-percent=50 # オートスケーリング設定
# ============================================
# 5. 更新とロールバック
# ============================================
kubectl set image deployment/web-app \
web=nginx:1.28 # イメージ更新(ローリングアップデート)
kubectl rollout status deployment/web-app # 更新進捗確認
kubectl rollout history deployment/web-app # 更新履歴
kubectl rollout undo deployment/web-app # 前バージョンにロールバック
# ============================================
# 6. リソースの削除
# ============================================
kubectl delete -f deployment.yaml # ファイル指定で削除
kubectl delete pod <pod名> # 個別Pod削除(Deploymentが再作成する)
kubectl delete namespace dev # Namespace丸ごと削除(注意!)
# ============================================
# 7. Namespace操作
# ============================================
kubectl create namespace dev # Namespace作成
kubectl create namespace prod
kubectl create namespace test
kubectl config set-context --current --namespace=dev # デフォルトNamespace変更
# ============================================
# 8. ポートフォワード(ローカルからServiceにアクセス)
# ============================================
kubectl port-forward service/web-app-service 8080:80 # localhost:8080でアクセス可能
5.4 実行結果の例
$ minikube start --driver=docker --cpus=2 --memory=4096
😄 minikube v1.35.0 on Ubuntu 24.04 (amd64)
✨ Using the docker driver based on user configuration
📌 Using Docker driver with root privileges
🔥 Creating docker container (CPUs=2, Memory=4096MB) ...
🐳 Preparing Kubernetes v1.35.1 on Docker 27.x ...
🔎 Verifying Kubernetes components...
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use "minikube" cluster
$ kubectl apply -f deployment.yaml
deployment.apps/web-app created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
web-app-6d8f9b7c4d-2xk9p 1/1 Running 0 30s
web-app-6d8f9b7c4d-7mj3q 1/1 Running 0 30s
web-app-6d8f9b7c4d-bt5nl 1/1 Running 0 30s
$ kubectl apply -f service.yaml
service/web-app-service created
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m
web-app-service ClusterIP 10.96.128.42 <none> 80/TCP 10s
5.5 よくあるエラーと対処法
| エラー | 原因 | 対処法 |
|---|---|---|
ImagePullBackOff |
コンテナイメージの取得に失敗 |
kubectl describe podでイメージ名を確認。レジストリのURLやタグが正しいか検証 |
CrashLoopBackOff |
コンテナが起動直後にクラッシュし続けている |
kubectl logs <pod>でアプリのエラーログを確認。環境変数や設定ファイルの不備が多い |
Pendingのまま |
スケジューラがPodを配置できるノードを見つけられない |
kubectl describe podのEventsを確認。CPUやメモリのrequestsがノードのキャパシティを超えていないか確認 |
OOMKilled |
コンテナがメモリ上限(limits.memory)を超えた |
limits.memoryの値を増やす。またはアプリのメモリ使用量を最適化 |
CreateContainerConfigError |
ConfigMapやSecretが見つからない | 参照しているConfigMap/Secretが同じNamespaceに存在するか確認 |
node(s) had untolerated taint |
ノードにtaintが設定されており、PodにtolerationがないPod配置不可 |
kubectl describe nodeでtaintを確認。必要に応じてtolerationを追加 |
5.6 環境診断スクリプト
問題が発生した場合は、以下のスクリプトで環境を診断できます。
#!/usr/bin/env python3
"""
Kubernetes環境診断スクリプト
実行方法: python check_k8s_env.py
前提: kubectl がインストール済みで、クラスタに接続済み
"""
import subprocess
import shutil
import json
import sys
def run_cmd(cmd):
"""コマンドを実行して結果を返す"""
try:
result = subprocess.run(
cmd, shell=True, capture_output=True, text=True, timeout=15
)
return result.returncode, result.stdout.strip(), result.stderr.strip()
except subprocess.TimeoutExpired:
return -1, "", "タイムアウト"
except Exception as e:
return -1, "", str(e)
def check_k8s_environment():
"""Kubernetes環境を総合チェック"""
issues = []
warnings = []
print("=" * 60)
print("Kubernetes 環境診断レポート")
print("=" * 60)
# --- 1. kubectl の存在確認 ---
print("\n[1/6] kubectl のインストール確認...")
if shutil.which("kubectl"):
rc, out, _ = run_cmd("kubectl version --client -o json")
if rc == 0:
ver = json.loads(out)
git_ver = ver.get("clientVersion", {}).get("gitVersion", "不明")
print(f" ✅ kubectl {git_ver}")
else:
issues.append("kubectl はあるがバージョン取得に失敗")
else:
issues.append("kubectl がインストールされていません")
# --- 2. クラスタ接続確認 ---
print("\n[2/6] クラスタ接続確認...")
rc, out, err = run_cmd("kubectl cluster-info")
if rc == 0:
lines = out.split("\n")
for line in lines[:2]:
print(f" ✅ {line}")
else:
issues.append(f"クラスタに接続できません: {err}")
# --- 3. ノード状態確認 ---
print("\n[3/6] ノード状態確認...")
rc, out, _ = run_cmd("kubectl get nodes -o json")
if rc == 0:
nodes = json.loads(out)
for node in nodes.get("items", []):
name = node["metadata"]["name"]
conditions = node["status"].get("conditions", [])
ready = any(
c["type"] == "Ready" and c["status"] == "True" for c in conditions
)
version = node["status"]["nodeInfo"]["kubeletVersion"]
if ready:
print(f" ✅ {name} — Ready (K8s {version})")
else:
issues.append(f"ノード {name} が NotReady 状態")
print(f" ❌ {name} — NotReady")
else:
issues.append("ノード情報の取得に失敗")
# --- 4. Pod状態チェック ---
print("\n[4/6] Pod状態チェック (全Namespace)...")
rc, out, _ = run_cmd(
"kubectl get pods --all-namespaces -o json"
)
if rc == 0:
pods = json.loads(out)
total = len(pods.get("items", []))
problem_pods = []
for pod in pods.get("items", []):
phase = pod["status"].get("phase", "Unknown")
name = pod["metadata"]["name"]
ns = pod["metadata"]["namespace"]
if phase not in ("Running", "Succeeded"):
problem_pods.append(f"{ns}/{name} ({phase})")
print(f" Pod総数: {total}")
if problem_pods:
warnings.append(
f"正常でないPod: {len(problem_pods)}個"
)
for p in problem_pods[:5]:
print(f" ⚠️ {p}")
else:
print(" ✅ 全Pod正常稼働中")
# --- 5. リソース使用状況 ---
print("\n[5/6] リソース使用状況...")
rc, out, _ = run_cmd("kubectl top nodes")
if rc == 0:
print(f" {out}")
else:
warnings.append("Metrics Serverが未導入のためリソース使用量を取得できません")
print(" ⚠️ kubectl top にはMetrics Serverが必要です")
print(" 💡 minikube: minikube addons enable metrics-server")
# --- 6. GPU リソース確認 ---
print("\n[6/6] GPU リソース確認...")
rc, out, _ = run_cmd(
'kubectl get nodes -o jsonpath=\'{range .items[*]}{.metadata.name}{" GPU: "}{.status.allocatable.nvidia\\.com/gpu}{"\\n"}{end}\''
)
if rc == 0 and "GPU:" in out:
lines = [l for l in out.split("\n") if l.strip()]
for line in lines:
if "GPU: " in line and line.split("GPU: ")[1].strip():
print(f" ✅ {line.strip()}")
else:
print(f" ℹ️ {line.strip()} (GPUリソースなし)")
else:
print(" ℹ️ NVIDIA Device Pluginが未導入、またはGPUノードなし")
# --- 結果サマリー ---
print("\n" + "=" * 60)
if issues:
print("❌ 問題が見つかりました:")
for issue in issues:
print(f" - {issue}")
if warnings:
print("⚠️ 警告:")
for w in warnings:
print(f" - {w}")
if not issues and not warnings:
print("✅ 環境は正常です。Kubernetesクラスタは稼働中です。")
print("=" * 60)
if __name__ == "__main__":
check_k8s_environment()
実装方法がわかったので、次は具体的なユースケースを見ていきます。
6. ユースケース別ガイド
6.1 ユースケース1: GPU対応AI推論サービスのデプロイ
想定読者: GPU環境でOllama等のLLM推論サービスをKubernetes上で動かしたい方
推奨構成: NVIDIA Device Plugin + Deployment + Service
KubernetesでGPUを使うには、NVIDIA Device Pluginをクラスタにインストールし、Podのresourcesでnvidia.com/gpuを要求します。
v1.34でGA(安定版)になったDynamic Resource Allocation(DRA) は、GPUの部分割り当てやトポロジ考慮スケジューリングを可能にする新機能です。従来のDevice Pluginより柔軟ですが、2026年3月時点ではNVIDIA DRA Driverはまだプレビュー段階のため、本記事では従来のDevice Plugin方式を紹介します。
サンプルコード — GPU推論サービス(Ollama):
# gpu-ai-stack.yaml — GPU対応AI推論サービス一式
# --- 1. Namespace ---
apiVersion: v1
kind: Namespace
metadata:
name: ai-inference
---
# --- 2. PersistentVolumeClaim(モデルデータの永続化) ---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ollama-models-pvc
namespace: ai-inference
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi # LLMモデルは大きい
---
# --- 3. Ollama Deployment(GPU付き) ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
namespace: ai-inference
spec:
replicas: 1 # GPU推論は通常1レプリカ
selector:
matchLabels:
app: ollama
template:
metadata:
labels:
app: ollama
spec:
containers:
- name: ollama
image: ollama/ollama:latest
ports:
- containerPort: 11434
resources:
requests:
cpu: "2"
memory: "8Gi"
nvidia.com/gpu: "1" # GPU 1枚を要求
limits:
cpu: "4"
memory: "16Gi"
nvidia.com/gpu: "1"
volumeMounts:
- name: models
mountPath: /root/.ollama
readinessProbe:
httpGet:
path: /api/tags
port: 11434
initialDelaySeconds: 10
periodSeconds: 10
volumes:
- name: models
persistentVolumeClaim:
claimName: ollama-models-pvc
---
# --- 4. Ollama Service ---
apiVersion: v1
kind: Service
metadata:
name: ollama-service
namespace: ai-inference
spec:
selector:
app: ollama
ports:
- port: 11434
targetPort: 11434
type: ClusterIP
---
# --- 5. Open WebUI Deployment ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: open-webui
namespace: ai-inference
spec:
replicas: 2 # WebUIはスケール可能
selector:
matchLabels:
app: open-webui
template:
metadata:
labels:
app: open-webui
spec:
containers:
- name: webui
image: ghcr.io/open-webui/open-webui:main
ports:
- containerPort: 8080
env:
- name: OLLAMA_BASE_URL
value: "http://ollama-service:11434"
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi"
---
# --- 6. Open WebUI Service(外部公開) ---
apiVersion: v1
kind: Service
metadata:
name: open-webui-service
namespace: ai-inference
spec:
selector:
app: open-webui
ports:
- port: 8080
targetPort: 8080
type: NodePort # 開発環境向け。本番ではLoadBalancerかIngress
# デプロイ手順
kubectl apply -f gpu-ai-stack.yaml
# GPU割り当て確認
kubectl describe pod -n ai-inference -l app=ollama | grep -A5 "nvidia.com/gpu"
# ポートフォワードでアクセス
kubectl port-forward -n ai-inference service/open-webui-service 8080:8080
# → ブラウザで http://localhost:8080 にアクセス
6.2 ユースケース2: ステートレスWebアプリの本番デプロイ
想定読者: FastAPI / Express等のWebアプリをKubernetesで本番運用したい方
推奨構成: Deployment + Service + HPA(Horizontal Pod Autoscaler)
サンプルコード — オートスケーリング付きWebアプリ:
# web-production.yaml — 本番Webアプリ構成
# --- 1. Namespace ---
apiVersion: v1
kind: Namespace
metadata:
name: prod
---
# --- 2. Deployment ---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: prod
spec:
replicas: 3
selector:
matchLabels:
app: api-server
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新時に1つ多くPodを作る
maxUnavailable: 0 # 更新中に利用不可のPodをゼロに
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api
image: myregistry/api-server:v1.2.0
ports:
- containerPort: 8000
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secret
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"
readinessProbe: # トラフィックを受ける準備ができたか
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe: # コンテナが生きているか
httpGet:
path: /health
port: 8000
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3
---
# --- 3. Service ---
apiVersion: v1
kind: Service
metadata:
name: api-service
namespace: prod
spec:
selector:
app: api-server
ports:
- port: 80
targetPort: 8000
type: LoadBalancer
---
# --- 4. HPA(Horizontal Pod Autoscaler) ---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
namespace: prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # CPU使用率60%を超えたらスケールアウト
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 5分間安定してからスケールダウン
readinessProbeとlivenessProbeの違いは重要です。readinessProbeが失敗するとServiceからトラフィックが外されます(Pod自体は生き続ける)。livenessProbeが失敗するとPodが再起動されます。どちらも必ず設定しましょう。
6.3 ユースケース3: In-Place Pod Resource Resize(v1.35 GA新機能)
想定読者: AI推論サービスなど、Podの再起動コストが高いワークロードを運用している方
推奨構成: Deployment + In-Place Resize(resizePolicyフィールド)
Kubernetes v1.35でIn-Place Pod Resource ResizeがGA(安定版)になりました。従来、Podのリソース(CPU/メモリ)を変更するにはPodの再作成が必要でした。AI推論サーバーのように「起動に数十秒かかる」「GPUメモリにモデルをロード済み」のワークロードでは、この再起動は致命的です。
v1.35以降は、Podを動かしたままCPUやメモリの割り当てを変更できます。
サンプルコード — リサイズ対応Deployment:
# resize-demo.yaml — In-Place Resize対応のDeployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: inference-server
namespace: ai-inference
spec:
replicas: 1
selector:
matchLabels:
app: inference-server
template:
metadata:
labels:
app: inference-server
spec:
containers:
- name: inference
image: myregistry/inference:v2.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "4"
memory: "8Gi"
resizePolicy: # v1.35 新フィールド
- resourceName: cpu
restartPolicy: NotRequired # CPU変更時に再起動不要
- resourceName: memory
restartPolicy: NotRequired # メモリ変更時も再起動不要
# 適用
kubectl apply -f resize-demo.yaml
# 稼働中にリソースを変更(Podは再起動されない)
kubectl patch pod <pod名> -n ai-inference --subresource resize --patch \
'{"spec":{"containers":[{"name":"inference","resources":{"requests":{"cpu":"4","memory":"8Gi"},"limits":{"cpu":"8","memory":"16Gi"}}}]}}'
# リサイズの進捗確認
kubectl get pod <pod名> -n ai-inference -o jsonpath='{.status.resize}'
# → "InProgress" → "Completed" と遷移
In-Place Resizeには制約があります。QoSクラス(Guaranteed / Burstable / BestEffort)が変わるリサイズはブロックされます。Guaranteedクラスの場合、requestsとlimitsを同時に変更してください。
ユースケースを把握できたところで、この先の学習パスを確認しましょう。
7. 学習ロードマップ
この記事を読んだ後、次のステップとして以下をおすすめします。
初級者向け(まずはここから)
- Kubernetes公式チュートリアル — ブラウザ内でK8sを試せるインタラクティブ環境あり
- minikubeで本記事のサンプルを全て動かす — 「apply → get → describe → logs → delete」のサイクルを体に覚えさせる
- kubectl チートシート — 日常的に使うコマンドを整理
中級者向け(実践に進む)
- Helm — K8sのパッケージマネージャ。マニフェストをテンプレート化してパラメータ管理
- Kustomize — 環境差分管理。本記事のConfigMap分離をさらに体系的に
- Ingress / Gateway API — 外部トラフィックのルーティング。v1.35でIngress NGINXが非推奨に、Gateway APIへの移行が進行中
- StatefulSet — データベース等のステートフルワークロード管理
上級者向け(さらに深く)
- Operator パターン — CRD(Custom Resource Definition)で独自リソースを定義し、アプリ固有の運用ロジックをKubernetes上に実装
- Dynamic Resource Allocation(DRA) — GPU等の特殊リソースの柔軟な割り当て。NVIDIA DRA Driverの導入
- Gang Scheduling / Kueue — AI分散学習ジョブの「全部揃ってから実行」制御とジョブキューイング
- Kubernetes ソースコードリーディング — Go言語で書かれたK8sの内部実装を読む
8. まとめ
この記事では、Kubernetesについて以下を解説しました。
- なぜKubernetesが必要か — Docker Composeの「単一マシン」の限界と、複数マシンにまたがるオーケストレーションの必要性
- 歴史と背景 — Google Borg → オーケストレーション戦争 → K8sの勝利 → v1.35 "Timbernetes"の最新機能
- コアコンセプト — Pod、Deployment、Service、Namespace、ConfigMap/Secret の関係性と「宣言的管理」の思想
- 実践 — minikubeでのローカル環境構築、kubectl操作、環境別ConfigMap分離
- ユースケース — GPU対応AI推論サービス、オートスケーリングWebアプリ、In-Place Pod Resource Resize
私の所感
本シリーズではLinux → Ubuntu → WSL → Docker → Docker Compose → Kubernetesと、コンテナ基盤技術を一気通貫で辿ってきました。
正直に言うと、Kubernetesは「学習コストが高い」技術です。概念の多さ、YAMLの量、トラブルシューティングの難しさ — 最初は私も圧倒されました。しかし、Docker Composeで「複数コンテナの管理」を経験した人なら、Kubernetesの思想は自然に理解できるはずです。なぜならKubernetesは、Composeの「宣言的にサービスを定義する」思想を、複数マシン・自動スケーリング・自己回復にまで拡張したものだからです。
RTX 5090環境でOllamaを動かす程度ならDocker Composeで十分です。しかし「このサービスを他の人にも使ってもらいたい」「24時間365日止めたくない」「トラフィックの増減に自動で対応したい」 — そう思った瞬間、Kubernetesの出番です。
v1.35のIn-Place Pod Resource Resizeは、AI推論サービスの運用を劇的に楽にする可能性を秘めています。そしてGang SchedulingやDRAの進化は、GPUリソースの効率的な共有を現実のものにしつつあります。2026年のKubernetesは、もはやWebサービスだけのプラットフォームではありません。AI/MLの基盤OSへと進化しています。
このシリーズが、皆さんのコンテナ技術への理解の一助になれば幸いです。
シリーズ全記事リンク
本記事は「インフラ技術を一気通貫で学ぶ」シリーズの最終回です。
| No. | トピック | リンク |
|---|---|---|
| 1 | Linux | Linuxってなんだ? |
| 2 | Ubuntu | Ubuntuってなんだ? |
| 3 | WSL | WSLってなんだ? |
| 4 | Docker | Dockerってなんだ? |
| 5 | Docker Compose | Docker Composeってなんだ? |
| 6 | Kubernetes | 本記事 |
参考文献
- Kubernetes 公式ドキュメント
- Kubernetes v1.35 リリースノート
- Kubernetes v1.35: In-Place Pod Restart
- CNCF — Kubernetes as AI's Operating System
- minikube 公式ドキュメント
- NVIDIA GPU Operator ドキュメント
- Dynamic Resource Allocation(DRA)公式
🐦 X (Twitter): @geneLab_999