跳到主要内容
稻草人
プロフィール

EP.02

💻Technology

Kubernetes(K8s)入門から実践まで

Webエンジニアのための完全ガイド

稻, 草人

本記事では、概念を覚えるだけでなく、Node.js、Vue/React、Nginx、MySQLを例に、ローカルで試せる構成まで扱います。サンプルは学習用です。本番環境ではセキュリティ、バックアップ、監視、可用性などの設計が別途必要です。

目次

1. Kubernetesとは? なぜ必要なのか

以前は、Webアプリケーションをサーバーにデプロイする際、Node.jsやNginx、ライブラリのインストールから始めることも珍しくありませんでした。Dockerを使えば、アプリケーションと実行環境をイメージにまとめられます。しかし、サーバーが複数台に増えると、別の課題が出てきます。

  • あるサーバーが故障したら、どこでコンテナを再起動するのか。
  • アクセスが増えたら、アプリケーションをどう増やすのか。
  • デプロイ中に既存のリクエストをどう処理するのか。
  • コンテナのIPアドレスが変わったら、ほかのサービスはどう接続するのか。

Kubernetes(K8s)は、コンテナ化されたアプリケーションを配置・管理するオープンソースのオーケストレーション基盤です。Kとsの間に8文字あるため、K8sと略します。

K8sの特徴は、「このPodを今すぐ1個起動して終わり」ではなく、「常に3個動いていてほしい」と希望状態を宣言することです。Kubernetesのコントローラーは実際の状態を観察し、希望状態に近づける処理を繰り返します。これを Reconciliation(調整・整合) と呼びます。

スクロールできます
比較DockerDocker ComposeKubernetes
主な役割イメージ構築・コンテナ実行複数サービスの定義クラスター全体の運用管理
想定規模単一ホスト中心開発・小規模構成複数ノード・大規模構成
サービス発見Docker Networkサービス名ServiceとDNS
デプロイ更新手動設計が必要基本的な再作成ローリング更新・ロールバック
データ保存VolumeVolumePV/PVC/CSI
自動スケーリング基本的に外部実装限定的HPAなど

KubernetesはDocker Engineを必須としません。現代のK8sノードでは、containerdやCRI-OなどのCRI対応ランタイムを使います。Dockerで構築したOCI互換イメージはK8sでも利用できます。

個人サイトや小規模サービスなら、まずComposeで十分な場合もあります。 K8s導入そのものが目的にならないよう、運用コストと要件を比較しましょう。

2. クラスターの仕組みを理解する

開発者 / CI
     |
  kubectl
     |
  kube-apiserver ── etcd
     |        (Control Plane)
     +── scheduler
     +── controller-manager
     |
     +-----------------------------+
     |                             |
 Worker Node A                Worker Node B
 kubelet / containerd         kubelet / containerd
    Pod A / Pod B                Pod C / Pod D
  • Control Plane:クラスター全体の状態管理と制御を担当。
  • kube-apiserver:Kubernetes APIの入口。kubectlもAPIを呼び出す。
  • etcd:クラスターの状態を保持するキーバリューストア。バックアップが重要。
  • kube-scheduler:Podを配置するノードを選択。
  • kube-controller-manager:Deploymentなどの希望状態と現在状態を調整するコントローラー群。
  • kubelet:ノード上でPodの実行状態を管理するエージェント。
  • Container Runtime:containerdなど、実際にコンテナを起動する仕組み。
  • CNI:Podネットワークを実現する仕組み。Calico、Ciliumなどがある。
  • kube-proxy:一般的なService転送を実装するコンポーネント。別のデータプレーンが代替する場合もある。

例えば、replicas: 3 を指定したDeploymentでPodが1個消えた場合、コントローラーが不足を検出して新しいPodの作成を進めます。これは「同じPodが復活する」のではなく、「新しいPodで希望状態に戻す」という動きです。

3. KubernetesのYAMLを読む

Kubernetesを学ぶとYAMLを頻繁に書きます。最初に押さえたいのは4つのフィールドです。

apiVersion: v1
kind: Pod
metadata:
  name: hello-pod
  labels:
    app: hello
spec:
  containers:
    - name: nginx
      image: nginx:alpine
      ports:
        - containerPort: 80
  • apiVersion:対象APIのグループ・バージョン。
  • kind:Pod、Deployment、Serviceなどのリソース種別。
  • metadata:名前、ラベル、Namespace、アノテーションなど。
  • spec:期待する動作や構成。
  • status:Kubernetes側が反映する現在状態。

よく使う操作:

kubectl apply -f pod.yaml
kubectl get pods -o wide
kubectl describe pod hello-pod
kubectl delete -f pod.yaml
kubectl explain pod.spec

ラベル(Label)は選択や分類に使われます。一方、Annotationは補助的なメタデータに適しています。ServiceのselectorとPodのラベルが一致していないと、Serviceが接続先を見つけられません。

4. kindとkubectlでローカル環境を作る

初心者はまず kind(Kubernetes in Docker) を使うと理解しやすいです。Dockerなどのコンテナランタイムを利用して、ローカルにKubernetesクラスターを作成できます。

macOSでHomebrewを使っている場合:

brew install kind kubectl
kind version
kubectl version --client
kind create cluster --name k8s-study
kubectl cluster-info
kubectl get nodes

kubectl はクラスター自体ではなく、Kubernetes APIを操作するCLIツールです。~/.kube/config に接続先が管理されます。本番環境への誤操作を防ぐため、コマンド実行前にcontextを確認する習慣をつけましょう。

kubectl config get-contexts
kubectl config current-context
kubectl config use-context kind-k8s-study

自分で作成したDockerイメージは、kindノードに取り込む必要がある場合があります。

docker build -t demo-api:v1 .
kind load docker-image demo-api:v1 --name k8s-study

imagePullPolicy: IfNotPresent を指定し、ローカル学習では latest ではなく v1 など明示的なタグを使うと扱いやすくなります。

終了後にクラスターごと削除する場合:

kind delete cluster --name k8s-study

5. Podとは何か

PodはKubernetesが管理する最小のデプロイ単位です。普通はアプリケーションコンテナを1個配置しますが、ログ収集などのsidecarコンテナを同じPodに含めることもできます。

重要なのはネットワークです。

Pod A: [Node.js:3000] [sidecar:9000]  → localhostを共有
Pod B: [MySQL:3306]                  → 別のlocalhost

同じPod内のコンテナはネットワーク名前空間を共有します。一方、異なるPod間では localhost は相手Podを指しません。PodのIPは作り直すと変わる可能性があるため、サービス間通信にはServiceを使います。

kubectl get pods
kubectl logs hello-pod
kubectl exec -it hello-pod -- sh
kubectl port-forward pod/hello-pod 8080:80

PodのPhaseにはPending、Running、Succeeded、Failed、Unknownなどがあります。ただし Running は「HTTPリクエストを正常に受けられる」という意味ではありません。後述するreadinessProbeが重要です。

6. Deploymentとローリングアップデート

Podだけを直接作成しても、削除時に継続的な復元を保証する上位コントローラーがありません。Web APIのようなステートレスなアプリケーションでは、通常Deploymentを利用します。DeploymentはReplicaSetを介してPodの数や更新を管理します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          ports:
            - containerPort: 80
kubectl apply -f deployment.yaml
kubectl get deploy,rs,pods
kubectl scale deployment/web --replicas=5
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web

新しいバージョンに更新する例:

kubectl set image deployment/web nginx=nginx:1.29-alpine
kubectl rollout status deployment/web

例のタグは参考用です。実際には使用するイメージの存在や互換性を確認してください。

ローリング更新時は maxSurge(追加で作れるPod数)と maxUnavailable(一時的に利用不能になってよいPod数)を設定できます。

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

ただし、この設定だけで無停止を保証できるわけではありません。readinessProbe、アプリケーションの終了処理、残りのキャパシティー、負荷分散なども関係します。

7. ServiceとKubernetes DNS

PodのIPが変わるたびにAPIの接続先を書き換えるのは現実的ではありません。そこでServiceが登場します。ServiceはラベルでPodを選択し、安定した接続先を提供します。

apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  selector:
    app: web
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
HTTPクライアント → web-svc:80 → app=web のPod:80
  • port:Serviceが公開するポート。
  • targetPort:接続先Podのポート。
  • containerPort:コンテナ側のポート情報。これだけではホストに公開されない。

同じNamespaceなら http://web-svc のように接続できます。別Namespaceでは web-svc.team-a.svc.cluster.local といった完全修飾名を使えます。ただしクラスターのDNSドメインや設定によって異なる場合があります。

Serviceの主なタイプ:

タイプ用途
ClusterIPクラスター内部から接続
NodePortノードのポートを利用
LoadBalancerクラウドLBなどと連携
ExternalNameDNS名への別名として扱う

Serviceだけでは安全境界になりません。通信制限にはNetworkPolicyを検討し、CNIがそれを実装しているか確認します。

8. ConfigMapとSecretの使い分け

ConfigMapは一般設定をイメージから分離するためのリソースです。

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  NODE_ENV: "production"
  API_MESSAGE: "Hello from Kubernetes"

Secretはパスワードなどの機密情報を扱います。

kubectl create secret generic api-secret \
  --from-literal=API_KEY='local-demo-only'

Deploymentからの参照例:

env:
  - name: API_MESSAGE
    valueFrom:
      configMapKeyRef:
        name: api-config
        key: API_MESSAGE
  - name: API_KEY
    valueFrom:
      secretKeyRef:
        name: api-secret
        key: API_KEY

注意:SecretのBase64表現は暗号化ではありません。 本番環境ではetcdの保存時暗号化、RBAC、監査、外部Secrets管理などを合わせて考える必要があります。

環境変数として渡した値は、ConfigMap/Secretを更新しても既存プロセスに自動反映されません。必要に応じてDeploymentを再起動します。

9. 永続化:Volume・PV・PVC・StorageClass

DockerのVolumeを学んだ後は、K8sの永続ストレージを理解しましょう。

  • Volume:Podにマウントするストレージ。emptyDir は一般的にPod削除時にデータが失われる。
  • PV(PersistentVolume):クラスターに提供される永続ストレージ資源。
  • PVC(PersistentVolumeClaim):アプリケーション側が容量やアクセスモードを要求するリソース。
  • StorageClass:動的プロビジョニングを行う際のストレージクラス。CSIドライバーなどを使う。

PVCの例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: demo-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

Podの spec.volumes からPVCを参照します。

volumes:
  - name: data
    persistentVolumeClaim:
      claimName: demo-data

コンテナ側は volumeMounts で /data などにマウントします。

volumeMounts:
  - name: data
    mountPath: /data

ReadWriteOnce は「1Podしか使えない」と完全に同義ではなく、基本的にはノード単位のアクセス制約を表します。具体的な動作はストレージ実装を確認してください。

PVCはバックアップではありません。 ディスク障害・誤削除・データ破損に備えるには、別途バックアップと復元テストが必要です。PVCの削除時にはPVのReclaimPolicyにも注意します。

10. Deployment以外のWorkload

スクロールできます
リソース利用例注意点
StatefulSetDBなどのステートフルなサービス固定ID・個別PVC。DBの冗長化は別途必要
DaemonSetログ収集、ノード監視対象ノードごとにPodを配置
Jobバッチ処理、DBマイグレーション完了する処理向け
CronJob定期バックアップ、定期クリーンアップ厳密な一度だけの実行保証ではない

CronJobの例:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: heartbeat
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: job
              image: busybox:1.36
              command: ["sh", "-c", "date; echo heartbeat"]

MySQLをStatefulSetで起動しただけでは、本番用の高可用性DBにはなりません。レプリケーション、フェイルオーバー、バックアップ、運用手順などが必要です。

11. Namespaceとリソース管理

Namespaceはクラスター内のリソースを論理的に分ける仕組みです。チーム・環境別に分けることはできますが、Namespaceだけでは強固なセキュリティ分離になりません。

kubectl create namespace study
kubectl get pods -n study
kubectl get pods -A

requestsとlimitsの例:

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "256Mi"

requestsはスケジューラーが配置判断を行う際の基準になります。limitsは実行時の上限です。CPUの500mは0.5 CPU相当、メモリの256Miは256 MiBです。メモリ上限を超えるとOOMKilled、CPU上限に達するとスロットリングが起こる可能性があります。

配置制御にはnodeSelector、Affinity、Taint/Tolerationなどがあります。Tolerationだけでノードを固定できるわけではない点に注意してください。

12. ProbeとHPA

Kubernetesには代表的な3種類のProbeがあります。

  • livenessProbe:コンテナを再起動すべきかを判断。
  • readinessProbe:Service経由のリクエストを受けられるかを判断。
  • startupProbe:起動に時間がかかるアプリケーション向け。
startupProbe:
  httpGet:
    path: /healthz
    port: 3000
  failureThreshold: 30
  periodSeconds: 2
readinessProbe:
  httpGet:
    path: /readyz
    port: 3000
  periodSeconds: 5
livenessProbe:
  httpGet:
    path: /healthz
    port: 3000
  periodSeconds: 10

DBが一時的に切断されたというだけでlivenessを必ず失敗させる設計にすると、障害時に全APIが再起動し続ける場合があります。どのProbeが何を保証するか分けて設計しましょう。

HPA(HorizontalPodAutoscaler)はCPU使用率などを利用してPod数を調整します。

kubectl autoscale deployment web --min=2 --max=6 --cpu=70%
kubectl get hpa

CPUベースのHPAには適切なrequests設定とMetrics Serverなどのメトリクス基盤が必要です。また、HPAでPod数を増やしてもノードのリソースが不足していればPendingになります。ノード側のオートスケーリングは別の仕組みです。

13. Ingress・Gateway API・HTTPS

IngressはHTTP/HTTPSのホスト名やパスに基づいてServiceへルーティングするAPIリソースです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: demo.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-svc
                port:
                  number: 80

Ingressリソースだけ作成しても外部アクセスできません。 対応するIngress Controllerの導入が必要です。Controllerのサポート状況と保守状況も確認してください。HTTPSにはDNS設定、証明書、TLS設定が必要になります。

より柔軟な設計にはGateway API(GatewayClass / Gateway / HTTPRouteなど)もあります。ただし、こちらも対応Controllerを必要とします。

14. RBAC・ServiceAccount・セキュリティ

RBACではRole / ClusterRoleに権限を定義し、RoleBinding / ClusterRoleBindingで主体に付与します。最小権限の原則を徹底することが重要です。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: readonly-bot
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
subjects:
  - kind: ServiceAccount
    name: readonly-bot
    namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

権限を確認:

kubectl auth can-i list pods \
  --as=system:serviceaccount:default:readonly-bot

本番環境では、非rootユーザー、不要な権限の削除、Pod Security Admission、NetworkPolicy、ServiceAccount Tokenの取り扱い、イメージ脆弱性のスキャン、監査ログなども考慮します。

15. Helm・Kustomize・GitOps

YAMLの数が増えてくると、環境ごとにファイルを複製する運用は変更漏れが発生しやすくなります。

  • Kustomize:共通のYAMLとoverlayで環境差分を管理。kubectl apply -k を使用。
  • Helm:Chartとvaluesによるテンプレート管理、リリース履歴やロールバックを提供。
  • GitOps:Gitで希望状態を管理し、Argo CDやFluxが継続的に反映。
kubectl apply -k overlays/dev
helm list -A
helm upgrade --install demo ./chart -n study --create-namespace
helm history demo -n study

CI/CDのよくある流れは、テスト → イメージビルド → セキュリティスキャン → Registryへpush → デプロイ設定更新 → ロールアウト確認 → 必要ならロールバック、です。

16. 障害調査で最初に使うコマンド

kubectl get nodes
kubectl get pods -A -o wide
kubectl describe pod <pod-name>
kubectl logs <pod-name> --all-containers=true --tail=100
kubectl logs <pod-name> --previous
kubectl get events --sort-by=.lastTimestamp
kubectl get svc,endpointslices
kubectl rollout status deployment/<name>
kubectl top pods

kubectl topにはMetrics Serverなどが必要です。

スクロールできます
状態よくある原因確認方法
Pendingリソース不足・PVC未バインド・配置条件describe / events
ImagePullBackOffイメージ名ミス・認証情報不足describe / imagePullSecrets
CrashLoopBackOffプロセス異常終了・設定ミスlogs –previous
OOMKilledメモリ上限超過describe / resources
Serviceにつながらないselector不一致・Probe失敗・ポート違いService / EndpointSlice
PVC PendingStorageClassやPV不足get pvc,sc / describe pvc
Ingressで404/502Controller・Host・Path・Service設定Controllerログ

障害時にいきなりPodを消すと情報が失われる可能性があります。まずログ、イベント、詳細状態を記録しましょう。


17. 実践① Nginxを3Podで起動する

まずはNginxを3つのPodで動かし、Service経由でアクセスしてみます。

01-nginx.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-nginx
  template:
    metadata:
      labels:
        app: demo-nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
            periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: demo-nginx
spec:
  selector:
    app: demo-nginx
  ports:
    - port: 80
      targetPort: 80

実行:

kubectl apply -f 01-nginx.yaml
kubectl rollout status deployment/demo-nginx
kubectl get pods -l app=demo-nginx
kubectl get svc demo-nginx
kubectl port-forward svc/demo-nginx 8080:80

別ターミナルから:

curl -I http://localhost:8080

スケールアウトも試します。

kubectl scale deployment/demo-nginx --replicas=5
kubectl get pods -l app=demo-nginx
kubectl delete pod <実在するPod名>
kubectl get pods -w

ReplicaSetによってPod数が補充されます。確認後は kubectl delete -f 01-nginx.yaml で削除できます。

18. 実践② Node.js APIをKubernetesにデプロイ

Node.jsで簡単なAPIを作り、ConfigMapとSecretを注入し、更新・ロールバックまで試します。

k8s-api/
├── package.json
├── server.js
├── Dockerfile
└── 02-api.yaml

package.json:

{
  "name": "k8s-api-demo",
  "version": "1.0.0",
  "private": true,
  "scripts": { "start": "node server.js" }
}

server.js:

const http = require('node:http');
const server = http.createServer((req, res) => {
  const healthy = ['/healthz', '/readyz'].includes(req.url);
  res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });
  res.end(JSON.stringify(healthy
    ? { status: 'ok' }
    : { message: process.env.API_MESSAGE || 'Hello K8s', version: process.env.APP_VERSION || 'v1' }));
});
server.listen(3000, '0.0.0.0', () => console.log('API started'));

Dockerfile(外部依存なし):

FROM node:22-alpine
WORKDIR /app
COPY package.json server.js ./
USER node
EXPOSE 3000
CMD ["node", "server.js"]

02-api.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  API_MESSAGE: "Hello from Kubernetes"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-api
  template:
    metadata:
      labels:
        app: demo-api
    spec:
      containers:
        - name: api
          image: demo-api:v1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000
          env:
            - name: API_MESSAGE
              valueFrom:
                configMapKeyRef:
                  name: api-config
                  key: API_MESSAGE
            - name: API_KEY
              valueFrom:
                secretKeyRef:
                  name: api-secret
                  key: API_KEY
          readinessProbe:
            httpGet:
              path: /readyz
              port: 3000
            initialDelaySeconds: 2
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 5
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 250m
              memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
  name: demo-api
spec:
  selector:
    app: demo-api
  ports:
    - port: 3000
      targetPort: 3000

イメージ作成から動作確認まで:

docker build -t demo-api:v1 .
kind load docker-image demo-api:v1 --name k8s-study
kubectl create secret generic api-secret --from-literal=API_KEY=dev-secret
kubectl apply -f 02-api.yaml
kubectl rollout status deployment/demo-api
kubectl port-forward svc/demo-api 3000:3000

別ターミナルで:

curl http://localhost:3000
# {"message":"Hello from Kubernetes","version":"v1"}

ローリング更新の練習:server.js の v1 を v2 に変更後、

docker build -t demo-api:v2 .
kind load docker-image demo-api:v2 --name k8s-study
kubectl set image deployment/demo-api api=demo-api:v2
kubectl rollout status deployment/demo-api
kubectl rollout history deployment/demo-api
kubectl rollout undo deployment/demo-api

ConfigMapの値を変更しても、環境変数として読み込んだ実行中プロセスには自動反映されません。必要なら kubectl rollout restart deployment/demo-api を実行します。Secretの内容をAPIレスポンスに表示しないようにしましょう。

19. 実践③ Vue/React + NginxとAPIのリバースプロキシ

Viteでビルドした静的ファイルをNginxから配信し、/api/ はKubernetesのServiceである demo-api:3000 に転送します。実践②のAPIを動かしておいてください。

プロジェクトに nginx.conf を作成:

server {
  listen 80;
  server_name _;
  root /usr/share/nginx/html;
  index index.html;

  location /api/ {
    proxy_pass http://demo-api:3000/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
  location /assets/ {
    try_files $uri =404;
    expires 1y;
    add_header Cache-Control "public, immutable";
  }
  location / {
    try_files $uri $uri/ /index.html;
  }
}

proxy_pass の末尾の / に注意してください。/api/healthz のリクエストを上流の /healthz に変換します。本番環境では認証、Cookie、タイムアウト、ヘッダー処理などの検討が必要です。

Dockerfile:

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80

03-front.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-front
spec:
  replicas: 2
  selector:
    matchLabels:
      app: demo-front
  template:
    metadata:
      labels:
        app: demo-front
    spec:
      containers:
        - name: nginx
          image: demo-front:v1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
---
apiVersion: v1
kind: Service
metadata:
  name: demo-front
spec:
  selector:
    app: demo-front
  ports:
    - port: 80
      targetPort: 80

実行:

docker build -t demo-front:v1 .
kind load docker-image demo-front:v1 --name k8s-study
kubectl apply -f 03-front.yaml
kubectl rollout status deployment/demo-front
kubectl port-forward svc/demo-front 8080:80

別ターミナルで:

curl http://localhost:8080/api/healthz
curl http://localhost:8080/

NginxはAPI PodのIPを直接知る必要がありません。Service名 demo-api をDNSで解決するため、API Podが作り直されても接続先の考え方は変わりません。もしNginx起動時にDNSエラーが出る場合は、CoreDNS・Service作成順とNginxの名前解決動作を確認してください。

20. 実践④ MySQLとPVCでデータを永続化する

DBのデータをPodとは別のストレージに置く練習です。本番用MySQLの構成ではありません。 バックアップ、冗長化、アクセス制御などは含みません。

まずStorageClassを確認:

kubectl get storageclass

動的プロビジョニングに対応するStorageClassがない場合、以下のPVCはPendingのままです。必要なストレージ環境を事前に用意してください。

04-db.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 2Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-lab
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: mysql-lab
  template:
    metadata:
      labels:
        app: mysql-lab
    spec:
      containers:
        - name: mysql
          image: mysql:8.4
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-lab-secret
                  key: root-password
          ports:
            - containerPort: 3306
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
          resources:
            requests:
              memory: 512Mi
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: mysql-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: mysql-lab
spec:
  selector:
    app: mysql-lab
  ports:
    - port: 3306
      targetPort: 3306

実行:

kubectl create secret generic mysql-lab-secret --from-literal=root-password=local-only
kubectl apply -f 04-db.yaml
kubectl get pvc,pods
kubectl logs deployment/mysql-lab
kubectl exec -it deployment/mysql-lab -- mysql -uroot -p

MySQLでテストデータを作成:

CREATE DATABASE demo;
USE demo;
CREATE TABLE notes (id INT PRIMARY KEY, body VARCHAR(100));
INSERT INTO notes VALUES (1, 'data survives pod replacement');
SELECT * FROM notes;

MySQLを終了してPodを再作成:

kubectl rollout restart deployment/mysql-lab
kubectl rollout status deployment/mysql-lab
kubectl exec -it deployment/mysql-lab -- mysql -uroot -p

SELECT * FROM demo.notes; で同じ行が取得できれば、データがPodのライフサイクルと分離されていることを確認できます。PVCの削除はストレージの回収ポリシーによって実データ削除につながるため、安易に実行しないでください。

21. AWS EKSと今後の学習順序

AWS EKSはマネージドKubernetesサービスで、Control Planeの運用の一部をAWSに任せられます。ただし、アプリケーション、IAM、VPC、ストレージCSI、ECR、ログ、監視、データベースの運用は引き続き設計が必要です。

おすすめの学習順序:

  1. Dockerfile、イメージ、Volume、Networkを復習する。
  2. kindでPod・Deployment・Serviceを実際に作る。
  3. ConfigMap、Secret、PVCを組み合わせる。
  4. Probe、ロールバック、ログ、Eventsを理解する。
  5. RBAC、Ingress/Gateway、Helm、HPA、NetworkPolicyに進む。
  6. 最後にEKS、GitOps、監視、運用設計を学ぶ。

kubectlコマンド早見表

コマンド用途
kubectl get pods -A全NamespaceのPod
kubectl get deploy,svcDeploymentとService
kubectl describe pod NAME状態・イベントの詳細
kubectl logs NAME --previous直前のコンテナログ
kubectl exec -it NAME -- shコンテナ内でコマンド実行(Shellが存在する場合)
kubectl port-forward svc/NAME 8080:80ローカルへのポート転送
kubectl apply -f FILEYAML適用
kubectl diff -f FILE変更差分の確認
kubectl rollout status deployment/NAMEリリース状況
kubectl rollout undo deployment/NAMEロールバック
kubectl get events --sort-by=.lastTimestampイベント確認
kubectl get pvc,sc永続ストレージ確認
kubectl config current-context操作対象クラスターの確認

まとめ

Kubernetesの難しさは、YAMLの文法よりも「どのリソースが、何を保証しているのか」を理解するところにあると感じます。

DeploymentはPod数の管理、Serviceは安定した接続先、Probeはアプリケーションの状態判定、PVCはデータの永続化、RBACは権限の制御を担っています。各機能を個別に暗記するより、実際のWebアプリケーションを動かしながらつながりを確認する方が身につきます。

最初はNginxと簡単なNode.js APIで十分です。それらを安定して動かせるようになってから、HelmやEKSといった運用寄りの技術に進むのが良いと思います。

参考リンク

仕様や詳しい手順は、以下の公式ドキュメントを参照してください。

本番環境での操作前に確認
本番適用前には必ずcontext、Namespace、イメージタグ、RBAC、PVCの削除ポリシー、アクセス制限、バックアップ手順を確認してください。