問題:DockerアプリはローカルでOKなのに、本番環境はカオス
Dockerでアプリをコンテナ化し、ローカル環境では完璧に動いている——しかし、スケールして実行しようとした途端、問題が噴出する。コンテナが落ちても自動再起動されない。トラフィックスパイクが単一インスタンスに集中する。ローリングアップデートでサービスがダウンする。コンテナを管理してくれる仕組みが必要だ。
それがKubernetesが解決するために作られた問題だ。ほとんどのチュートリアルは大量のYAMLを投げつけて終わりにする。このガイドは違う。3つのコアな構成要素——Pod、Service、Deployment——を説明し、それらがどう連携するかを正確に示す。
コアコンセプト
Pod:最小単位
PodはKubernetesにおける最小のデプロイ単位だ。同じネットワーク名前空間とストレージボリュームを共有する1つ以上のコンテナを包む軽量なラッパーと考えると良い。実際のプロジェクトでは、ほとんどのPodは単一のコンテナを実行する。
最初にほとんどの人が見落とすポイントがある:Podは一時的なものだ。KubernetesはPodが壊れても修復しようとしない——破棄して新しいものを作る。永続的なものにPodのIPアドレスやローカルストレージを頼ってはいけない。
最小限のPodマニフェストはこのようになる:
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:1.25
ports:
- containerPort: 80
以下のコマンドで適用する:
kubectl apply -f pod.yaml
kubectl get pods
kubectl describe pod my-app-pod
Podが起動しているのが確認できる——しかしこれだけでは本番環境で役に立たない。自動再起動なし、ロードバランシングなし、ローリングアップデートなし。
Deployment:スケールでPodを管理する
Deploymentは実際に本番環境で使用するものだ。Podを3つのことを処理するコントローラーでラップする:
- 常に必要な数のレプリカを維持する
- ゼロダウンタイムのローリングアップデート
- 問題発生時のロールバック
内部では、DeploymentはReplicaSetを作成し、それがPodの実際の作成と監視を行う。ReplicaSetと直接やり取りする必要はない——Deploymentが代わりに管理してくれる。
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
selector.matchLabelsフィールドが重要な部分だ——DeploymentがどのPodを管理するかを判断する仕組みだ。Podテンプレートのラベルが完全に一致しなければ、何も管理されない。
適用して確認する:
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods # 自動生成された名前の3つのPodが確認できる
Service:Podへの安定したネットワークアクセス
PodのIPは一時的なものだ。Podが再起動するたびに新しいアドレスが割り当てられる。Serviceはラベルセレクターにマッチする健全なPodにトラフィックを自動的にルーティングする安定した仮想IPでこの問題を解決する。
ほとんどのシナリオをカバーする3つのServiceタイプがある:
- ClusterIP — 内部アクセスのみ(デフォルト、サービス間通信に使用)
- NodePort — 各ノードの静的ポートでサービスを公開する(テストに便利)
- LoadBalancer — クラウドのロードバランサーをプロビジョニングする(AWS ELB、GCP LBなど)
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: ClusterIP
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
selectorはDeploymentのPodテンプレートで使用されているのと同じラベル(app: my-app)にマッチする。トラフィックルーティングはPod名やIPではなく、ラベルに基づいている。それがシステムをPodの再起動や再スケジューリングに対して耐性を持たせる仕組みだ。
実践:実際のアプリをエンドツーエンドでデプロイする
前提条件
ローカルテスト用にMinikubeとkubectlをインストールする:
# ローカルクラスターを起動する
minikube start
# 起動確認
kubectl cluster-info
kubectl get nodes
フルスタックをデプロイする
3つのリソースすべてを---で区切って1つのファイルにまとめることができる:
# app-stack.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
nodePort: 30080
kubectl apply -f app-stack.yaml
# すべて起動しているか確認
kubectl get pods,svc
# MinikubeでアクセスするためのURLを取得する
minikube service web-service --url
自己修復をテストする
実行中のPodを1つ削除して、Deploymentがすぐに代替Podを起動するのを確認する:
# Pod名を取得する
kubectl get pods
# 削除する
kubectl delete pod web-deployment-xxxxxxxxx-xxxxx
# KubernetesがPodを再作成するのを監視する
kubectl get pods -w
ゼロダウンタイムのローリングアップデート
コンテナイメージを更新する——あとはKubernetesが処理してくれる:
kubectl set image deployment/web-deployment web=nginx:1.26-alpine
# ロールアウトを監視する
kubectl rollout status deployment/web-deployment
# 問題があればロールバックする
kubectl rollout undo deployment/web-deployment
# ロールアウト履歴を確認する
kubectl rollout history deployment/web-deployment
本番環境では、このセットアップは非常に堅牢だ。2つ以上のレプリカとロールアウト戦略のmaxUnavailable: 0を設定すれば、新しいPodがReadinessチェックを通過するまでKubernetesは古いPodを終了しない。ライブトラフィック下でもゼロダウンタイムを実現できる。
ラベルがすべてをつなぐ仕組み
ラベルはKubernetesの接着剤だ。接続を確認する方法を見ていこう:
# ServiceがどのPodをターゲットにしているか確認する
kubectl describe service web-service
# 確認する:Selector: app=web
# そして: Endpoints: (PodのIPリスト)
# ラベルで直接Podを検索する
kubectl get pods -l app=web
トラフィックが正しくルーティングされない場合、十中八九はServiceセレクターとPodラベルのミスマッチが原因だ。他の何かを確認する前に、まずここを確認しよう。
次のステップ
この3つのリソースが基盤となる。次に学ぶべき内容はこちら:
- ConfigMapとSecret — イメージを再ビルドせずに設定と認証情報を注入する
- Ingress — 1つのロードバランサーで複数のServiceに外部HTTP/HTTPSトラフィックをルーティングする
- PersistentVolume — Podの再起動後も残る耐久性のあるストレージをステートフルなアプリに提供する
- Horizontal Pod Autoscaler (HPA) — CPUやカスタムメトリクスに基づいてレプリカを自動スケールする
すべての高度なKubernetesオブジェクトは同じパターンに従う。StatefulSet、DaemonSet、Job——どれもラベルでタグ付けされたPod、ライフサイクルを管理するコントローラー、セレクターでトラフィックをルーティングするServiceを使用する。語彙は増えるが、ロジックは同じだ。
ローカルでDeploymentを実行してみよう。Podを削除してKubernetesが復元するのを見てみよう。ローリングアップデートを実行してライブで監視してみよう。そのハンズオン体験こそが、高度なコンセプトを本当に理解するための鍵だ。

