1. Kubernetesとは? なぜ必要なのか
以前は、Webアプリケーションをサーバーにデプロイする際、Node.jsやNginx、ライブラリのインストールから始めることも珍しくありませんでした。Dockerを使えば、アプリケーションと実行環境をイメージにまとめられます。しかし、サーバーが複数台に増えると、別の課題が出てきます。
- あるサーバーが故障したら、どこでコンテナを再起動するのか。
- アクセスが増えたら、アプリケーションをどう増やすのか。
- デプロイ中に既存のリクエストをどう処理するのか。
- コンテナのIPアドレスが変わったら、ほかのサービスはどう接続するのか。
Kubernetes(K8s)は、コンテナ化されたアプリケーションを配置・管理するオープンソースのオーケストレーション基盤です。Kとsの間に8文字あるため、K8sと略します。
K8sの特徴は、「このPodを今すぐ1個起動して終わり」ではなく、「常に3個動いていてほしい」と希望状態を宣言することです。Kubernetesのコントローラーは実際の状態を観察し、希望状態に近づける処理を繰り返します。これを Reconciliation(調整・整合) と呼びます。
| 比較 | Docker | Docker Compose | Kubernetes |
|---|---|---|---|
| 主な役割 | イメージ構築・コンテナ実行 | 複数サービスの定義 | クラスター全体の運用管理 |
| 想定規模 | 単一ホスト中心 | 開発・小規模構成 | 複数ノード・大規模構成 |
| サービス発見 | Docker Network | サービス名 | ServiceとDNS |
| デプロイ更新 | 手動設計が必要 | 基本的な再作成 | ローリング更新・ロールバック |
| データ保存 | Volume | Volume | PV/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などと連携 |
| ExternalName | DNS名への別名として扱う |
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管理などを合わせて考える必要があります。
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
| リソース | 利用例 | 注意点 |
|---|---|---|
| StatefulSet | DBなどのステートフルなサービス | 固定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
| 状態 | よくある原因 | 確認方法 |
|---|---|---|
| Pending | リソース不足・PVC未バインド・配置条件 | describe / events |
| ImagePullBackOff | イメージ名ミス・認証情報不足 | describe / imagePullSecrets |
| CrashLoopBackOff | プロセス異常終了・設定ミス | logs –previous |
| OOMKilled | メモリ上限超過 | describe / resources |
| Serviceにつながらない | selector不一致・Probe失敗・ポート違い | Service / EndpointSlice |
| PVC Pending | StorageClassやPV不足 | get pvc,sc / describe pvc |
| Ingressで404/502 | Controller・Host・Path・Service設定 | Controllerログ |
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、ログ、監視、データベースの運用は引き続き設計が必要です。
おすすめの学習順序:
- Dockerfile、イメージ、Volume、Networkを復習する。
- kindでPod・Deployment・Serviceを実際に作る。
- ConfigMap、Secret、PVCを組み合わせる。
- Probe、ロールバック、ログ、Eventsを理解する。
- RBAC、Ingress/Gateway、Helm、HPA、NetworkPolicyに進む。
- 最後にEKS、GitOps、監視、運用設計を学ぶ。
kubectlコマンド早見表
| コマンド | 用途 |
|---|---|
kubectl get pods -A | 全NamespaceのPod |
kubectl get deploy,svc | Deploymentと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 FILE | YAML適用 |
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といった運用寄りの技術に進むのが良いと思います。
参考リンク
- Kubernetes公式概説 — Kubernetesの役割と全体像
- Kubernetesの概念 — リソースと基本構成
- ツールのインストール — kubectlなどのセットアップ
- Workloads — Pod・Deployment・Jobなど
- Networking — Service・DNS・ネットワーク
- Storage — Volume・PV・PVC
- Security — 権限とセキュリティ設計
- kubectl Reference — CLIの使い方
- kind — ローカルKubernetes環境
- Helm — Chartとリリース管理
本番環境での操作前に確認
本番適用前には必ずcontext、Namespace、イメージタグ、RBAC、PVCの削除ポリシー、アクセス制限、バックアップ手順を確認してください。

