Kubernetes基礎:Pod、Service、Deploymentを実例で徹底解説

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

問題:DockerアプリはローカルでOKなのに、本番環境はカオス

Dockerでアプリをコンテナ化し、ローカル環境では完璧に動いている——しかし、スケールして実行しようとした途端、問題が噴出する。コンテナが落ちても自動再起動されない。トラフィックスパイクが単一インスタンスに集中する。ローリングアップデートでサービスがダウンする。コンテナを管理してくれる仕組みが必要だ。

それがKubernetesが解決するために作られた問題だ。ほとんどのチュートリアルは大量のYAMLを投げつけて終わりにする。このガイドは違う。3つのコアな構成要素——PodServiceDeployment——を説明し、それらがどう連携するかを正確に示す。

コアコンセプト

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の再起動や再スケジューリングに対して耐性を持たせる仕組みだ。

実践:実際のアプリをエンドツーエンドでデプロイする

前提条件

ローカルテスト用にMinikubekubectlをインストールする:

# ローカルクラスターを起動する
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が復元するのを見てみよう。ローリングアップデートを実行してライブで監視してみよう。そのハンズオン体験こそが、高度なコンセプトを本当に理解するための鍵だ。

Share: