📋
フレームワーク #Kubernetes #マニフェスト #YAML #kubectl #Deployment #Service 📚 TCPIPネットワーク

Kubernetesマニフェスト読解ガイド

apiVersion/kind/metadata/specの構造からDeployment・Service・ラベル・セレクターの仕組みまで。マニフェストを読み書きするための基礎知識。

マニフェストの骨格:4つのトップレベルフィールド

すべてのK8sリソースは同じ骨格を持つ。

apiVersion: apps/v1        # ① どのAPIグループ・バージョンか
kind: Deployment           # ② リソースの種類
metadata:                  # ③ リソースの識別情報
  name: my-app
  namespace: production
  labels:
    app: my-app
spec:                      # ④ リソース固有の仕様(kindによって全く異なる)
  replicas: 3
  ...
apiVersion ─── どの「棚」に入っているリソースか
kind       ─── 何のリソースか(設計図の名前)
metadata   ─── 名札・住所(name, namespace, labels, annotations)
spec       ─── 中身の仕様。ここだけ kind ごとに異なる

apiVersion の読み方

apps/v1
↑     ↑
グループ  バージョン

v1(グループなし)= core group(最も基本的なリソース)

リソースとapiVersionの対応

apiVersion含まれる主なリソース
v1Pod, Service, ConfigMap, Secret, PersistentVolumeClaim, Namespace
apps/v1Deployment, ReplicaSet, StatefulSet, DaemonSet
batch/v1Job, CronJob
networking.k8s.io/v1Ingress, NetworkPolicy
rbac.authorization.k8s.io/v1Role, RoleBinding, ClusterRole
autoscaling/v2HorizontalPodAutoscaler
storage.k8s.io/v1StorageClass
# 使えるAPIグループとバージョン一覧
kubectl api-versions

# リソース一覧(略称・namespaced かどうかも確認)
kubectl api-resources

よく使うリソース早見表

┌──────────────────┬─────────────────────────────────────────────────┐
│  リソース         │  役割・一言説明                                   │
├──────────────────┼─────────────────────────────────────────────────┤
│  Pod             │  コンテナの最小実行単位。直接作ることは少ない       │
│  Deployment      │  Podのローリングアップデート・レプリカ管理          │
│  StatefulSet     │  順序・安定したIDが必要なPod(DB, Kafkaなど)      │
│  DaemonSet       │  全Node(or 特定Node)に1つずつPodを置く          │
│  Job             │  1回限りのバッチ処理                              │
│  CronJob         │  定期実行のバッチ処理(cron式で指定)              │
│  Service         │  Podへの安定したエンドポイント(仮想IP)            │
│  Ingress         │  L7ルーティング(ホスト名・パスで振り分け)         │
│  ConfigMap       │  アプリの設定(平文)を外部化                      │
│  Secret          │  機密情報(Base64エンコード)                      │
│  PVC             │  ストレージの要求。PVと紐付く                      │
│  Namespace       │  クラスター内の論理的な分離空間                    │
│  ServiceAccount  │  PodのIAM的なID。外部サービスへの認証に使う        │
│  HPA             │  CPU/メモリ等の指標でPod数を自動スケール           │
└──────────────────┴─────────────────────────────────────────────────┘

Deployment を完全読解

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app                      # Deployment自体の名前
  namespace: production
  labels:                           # Deployment自身のラベル(セレクターとは別)
    app: my-app
    version: "1.2.0"
spec:
  replicas: 3                       # Pod数の目標値

  selector:                         # 「このDeploymentが管理するPod」の条件
    matchLabels:
      app: my-app                   # ← spec.template.metadata.labels と一致させる

  strategy:
    type: RollingUpdate             # 更新戦略(RollingUpdate or Recreate)
    rollingUpdate:
      maxSurge: 1                   # 目標数を超えて同時に作れるPod数
      maxUnavailable: 0             # 同時に停止できるPod数(0 = ダウンタイムなし)

  template:                         # ここからがPodの設計図
    metadata:
      labels:
        app: my-app                 # ← selector.matchLabels と必ず一致させる
    spec:
      containers:
        - name: app
          image: my-app:1.2.0
          ports:
            - containerPort: 8080

          resources:
            requests:               # スケジューリング時の保証値(Nodeの割り当てに使用)
              cpu: "250m"           # 0.25 vCPU
              memory: "256Mi"
            limits:                 # 超えると OOM Kill / CPU スロットリング
              cpu: "500m"
              memory: "512Mi"

          env:
            - name: DB_HOST
              valueFrom:
                configMapKeyRef:
                  name: my-config   # ConfigMapの名前
                  key: db_host

            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: my-secret   # Secretの名前
                  key: db_password

          livenessProbe:            # 失敗するとコンテナを再起動
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10 # 起動後この秒数待ってからProbe開始
            periodSeconds: 10

          readinessProbe:           # 失敗するとServiceのEndpointから除外(トラフィックを切る)
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5

strategy の違い

RollingUpdate(デフォルト):
  旧Pod を1つずつ停止 → 新Pod を起動 → 繰り返し
  ダウンタイムゼロだがv1とv2が混在する瞬間がある

Recreate:
  旧Pod を全部停止 → 新Pod を全部起動
  ダウンタイムが発生するが、バージョン混在しない
  DBマイグレーション等で使うケース

Service を完全読解

apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
  namespace: production
spec:
  type: ClusterIP                   # ClusterIP / NodePort / LoadBalancer

  selector:                         # このServiceが転送先とするPodの条件
    app: my-app                     # ← DeploymentのPod labelsと一致させる

  ports:
    - name: http
      port: 80                      # Serviceが受け付けるポート
      targetPort: 8080              # 転送先PodのポートPort(containerPort)
      protocol: TCP

port と targetPort の混乱を解消

クライアント
    │ :80 でServiceに接続

Service (port: 80)
    │ targetPort: 8080 に転送

Pod (containerPort: 8080)

port      = Serviceが外(クラスター内)に公開するポート
targetPort = 転送先のPodが実際に待ち受けているポート

ラベルとセレクター:K8sの結合の仕組み

K8sリソース間の「紐付け」はラベルとセレクターだけで行われる。明示的な参照(ID等)は使わない。

【Deployment → Pod の管理関係】
Deployment の spec.selector.matchLabels: {app: my-app}
                        ↓ 一致する
Pod の metadata.labels: {app: my-app}
→ このPodはこのDeploymentが管理する

【Service → Pod の転送関係】
Service の spec.selector: {app: my-app}
                        ↓ 一致する
Pod の metadata.labels: {app: my-app}
→ このPodにトラフィックを転送する

よくある失敗パターン

# NG: selectorとtemplateのラベルが不一致
spec:
  selector:
    matchLabels:
      app: my-app         # ← "my-app"
  template:
    metadata:
      labels:
        app: myapp         # ← "myapp"(ハイフンなし)
# エラー: `selector` does not match template `labels`

# NG: Serviceのselectorがtypo
# Service: selector: {app: my-apps}  ← sがある
# Pod:     labels:   {app: my-app}   ← sがない
# → Endpointsが空になり、トラフィックが届かない
# セレクター不一致のデバッグ
kubectl get endpoints my-app-svc
# ADDRESS が空なら Podが1つもマッチしていない

kubectl get pods -l app=my-app
# Serviceのselectorと同じラベルで手動確認

metadata の重要フィールド

labels と annotations の違い

metadata:
  labels:             # セレクターに使える(検索・フィルタの基準)
    app: my-app       # 短い・識別子として使う
    env: production

  annotations:        # セレクターに使えない(メタデータの保存)
    kubernetes.io/created-by: "terraform"
    deployment.kubernetes.io/revision: "3"
    # 長い文字列・URL・JSONなど自由に入れられる
    # Ingressコントローラーの設定などもここに書く

namespace の影響範囲

同じ namespace 内:
  Service は名前だけで参照できる
  my-app-svc → my-app-svc.production.svc.cluster.local の短縮

別の namespace から:
  FQDN で参照する必要がある
  my-app-svc.production.svc.cluster.local

ClusterScope なリソース:
  Node / PersistentVolume / ClusterRole / Namespace 自体
  → namespace に属さない(kubectl api-resources でNAMESPACED列を確認)

kubectl の読解支援コマンド

# フィールドの説明を確認(一番重要)
kubectl explain deployment
kubectl explain deployment.spec.strategy
kubectl explain pod.spec.containers.resources

# 既存リソースをYAMLで出力(構造の確認に)
kubectl get deployment my-app -o yaml

# 差分確認(適用前に変更内容を確認)
kubectl diff -f manifest.yaml

# ドライラン(実際には適用しない)
kubectl apply -f manifest.yaml --dry-run=client
kubectl apply -f manifest.yaml --dry-run=server  # サーバー側でバリデーション

# リソースなしでPodのYAMLテンプレートを生成
kubectl run my-pod --image=nginx --dry-run=client -o yaml

# JSONPathで特定フィールドだけ取り出す
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.phase}{"\n"}{end}'

# カスタムカラムで表示
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName

よくある誤読パターン

誤読①:spec.template は「Podの設計図」であり「Pod自体」ではない
  → spec.replicas: 3 なら template から3つのPodが作られる

誤読②:containerPort は「開放宣言」であり「穴あけ」ではない
  → containerPort を書かなくても通信は通る(ドキュメント目的)
  → 実際のポート制御は NetworkPolicy で行う

誤読③:resources.requests はネットワーク帯域ではなくCPU/メモリ
  → "250m" は 0.25 vCPU(milli CPU)
  → "256Mi" は 256 メビバイト(1Mi = 1024 * 1024 bytes)

誤読④:Secret の value は暗号化ではなくBase64エンコード
  → echo -n "password" | base64 で作れる(誰でも復元可能)
  → 本当の秘密管理は Vault / AWS Secrets Manager / SOPS + GitOps

誤読⑤:Deployment を削除するとPodも消える
  → Deployment → ReplicaSet → Pod の親子関係
  → --cascade=orphan オプションで Pod を残すことも可能

複数マニフェストのまとめ方

# 単一ファイルに --- で区切って複数リソースを記述
cat <<EOF > app.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-config
data:
  db_host: "postgres:5432"
---
apiVersion: apps/v1
kind: Deployment
...
---
apiVersion: v1
kind: Service
...
EOF

kubectl apply -f app.yaml        # 全リソースを一括適用
kubectl apply -f ./manifests/    # ディレクトリ内の全yamlを適用
kubectl apply -f ./manifests/ --recursive  # サブディレクトリも再帰的に