Kubernetes入門実践ガイド - コンテナオーケストレーションの基礎から運用まで

2025.12.02

公式ドキュメント

Kubernetesは、コンテナ化されたアプリケーションのデプロイ、スケーリング、管理を自動化するオープンソースプラットフォームです。本記事では、Kubernetesの基本から実践的な運用までを解説します。

実践メモ: minikubeやkindを使えばローカルPCでKubernetesクラスタを立ち上げられます。

Kubernetesアーキテクチャ

クラスタ構成

flowchart TB
 subgraph ControlPlane["Control Plane"]
 API["API Server"]
 Sched["Scheduler"]
 CM["Controller Manager"]
 etcd["etcd<br/>(データストア)"]
 end

 subgraph WorkerNodes["Worker Nodes"]
 subgraph Node1["Node 1"]
 kubelet1["kubelet"]
 proxy1["kube-proxy"]
 runtime1["Container Runtime<br/>(containerd)"]
 Pod1["Pod"]
 Pod2["Pod"]
 end
 subgraph Node2["Node 2"]
 kubelet2["kubelet"]
 proxy2["kube-proxy"]
 runtime2["Container Runtime<br/>(containerd)"]
 Pod3["Pod"]
 Pod4["Pod"]
 end
 end

 ControlPlane -->|kubelet通信| WorkerNodes

主要コンポーネント

コンポーネント役割
API ServerクラスタへのすべてのAPIリクエストを処理
etcdクラスタの状態を保存する分散KVストア
SchedulerPodを適切なノードに配置
Controller Manager各種コントローラを実行(ReplicaSet, Deployment等)
kubeletノード上でPodを管理
kube-proxyネットワークプロキシ、サービスの負荷分散

注意: 本番環境ではkubectlコマンドの実行権限を適切に制限してください。RBACでロールベースのアクセス制御を設定しましょう。

ローカル環境のセットアップ

minikubeのインストール

# macOS (Homebrew)
brew install minikube

# Linux
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Windows (winget)
winget install Kubernetes.minikube

# クラスタの起動
minikube start --driver=docker --cpus=4 --memory=8192

# ステータス確認
minikube status

# Kubernetesダッシュボードの起動
minikube dashboard

kindのインストール(代替)

# Kubernetes IN Docker - より軽量な選択肢
# macOS/Linux
brew install kind

# クラスタ作成(マルチノード)
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF

# クラスタ一覧
kind get clusters

# クラスタ削除
kind delete cluster

kubectlの設定

# インストール
brew install kubectl

# コンテキスト確認
kubectl config get-contexts

# コンテキスト切り替え
kubectl config use-context minikube

# クラスタ情報確認
kubectl cluster-info
kubectl get nodes

基本的なリソース

Pod

# pod.yaml - 最小デプロイ単位
apiVersion: v1
kind: Pod
metadata:
 name: nginx-pod
 labels:
 app: nginx
 environment: development
spec:
 containers:
 - name: nginx
 image: nginx:1.25
 ports:
 - containerPort: 80
 resources:
 requests:
 memory: "64Mi"
 cpu: "250m"
 limits:
 memory: "128Mi"
 cpu: "500m"
 livenessProbe:
 httpGet:
 path: /
 port: 80
 initialDelaySeconds: 10
 periodSeconds: 10
 readinessProbe:
 httpGet:
 path: /
 port: 80
 initialDelaySeconds: 5
 periodSeconds: 5
# Podの操作
kubectl apply -f pod.yaml
kubectl get pods
kubectl describe pod nginx-pod
kubectl logs nginx-pod
kubectl exec -it nginx-pod -- /bin/bash
kubectl delete pod nginx-pod

Deployment

# deployment.yaml - アプリケーションのデプロイメント
apiVersion: apps/v1
kind: Deployment
metadata:
 name: web-app
 labels:
 app: web-app
spec:
 replicas: 3
 selector:
 matchLabels:
 app: web-app
 strategy:
 type: RollingUpdate
 rollingUpdate:
 maxSurge: 1
 maxUnavailable: 0
 template:
 metadata:
 labels:
 app: web-app
 spec:
 containers:
 - name: web-app
 image: myapp:1.0.0
 ports:
 - containerPort: 3000
 env:
 - name: NODE_ENV
 value: "production"
 - name: DATABASE_URL
 valueFrom:
 secretKeyRef:
 name: app-secrets
 key: database-url
 resources:
 requests:
 memory: "256Mi"
 cpu: "250m"
 limits:
 memory: "512Mi"
 cpu: "1000m"
 livenessProbe:
 httpGet:
 path: /health
 port: 3000
 initialDelaySeconds: 30
 periodSeconds: 10
 timeoutSeconds: 5
 failureThreshold: 3
 readinessProbe:
 httpGet:
 path: /ready
 port: 3000
 initialDelaySeconds: 10
 periodSeconds: 5
 affinity:
 podAntiAffinity:
 preferredDuringSchedulingIgnoredDuringExecution:
 - weight: 100
 podAffinityTerm:
 labelSelector:
 matchLabels:
 app: web-app
 topologyKey: kubernetes.io/hostname
# Deploymentの操作
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods -l app=web-app

# スケーリング
kubectl scale deployment web-app --replicas=5

# ローリングアップデート
kubectl set image deployment/web-app web-app=myapp:2.0.0

# ロールバック
kubectl rollout undo deployment/web-app
kubectl rollout history deployment/web-app
kubectl rollout status deployment/web-app

Service

# service.yaml - サービス公開
apiVersion: v1
kind: Service
metadata:
 name: web-app-service
spec:
 type: ClusterIP # ClusterIP, NodePort, LoadBalancer
 selector:
 app: web-app
 ports:
 - name: http
 port: 80
 targetPort: 3000
 protocol: TCP
---
# NodePortサービス(外部アクセス用)
apiVersion: v1
kind: Service
metadata:
 name: web-app-nodeport
spec:
 type: NodePort
 selector:
 app: web-app
 ports:
 - port: 80
 targetPort: 3000
 nodePort: 30080
---
# LoadBalancerサービス(クラウド環境用)
apiVersion: v1
kind: Service
metadata:
 name: web-app-lb
 annotations:
 service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
 type: LoadBalancer
 selector:
 app: web-app
 ports:
 - port: 80
 targetPort: 3000

Ingress

# ingress.yaml - HTTPルーティング
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: web-app-ingress
 annotations:
 nginx.ingress.kubernetes.io/rewrite-target: /
 nginx.ingress.kubernetes.io/ssl-redirect: "true"
 cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
 ingressClassName: nginx
 tls:
 - hosts:
 - app.example.com
 secretName: app-tls-secret
 rules:
 - host: app.example.com
 http:
 paths:
 - path: /
 pathType: Prefix
 backend:
 service:
 name: web-app-service
 port:
 number: 80
 - path: /api
 pathType: Prefix
 backend:
 service:
 name: api-service
 port:
 number: 80
# Ingress Controllerのインストール(minikube)
minikube addons enable ingress

# Ingress確認
kubectl get ingress
kubectl describe ingress web-app-ingress

設定管理

ConfigMap

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
 name: app-config
data:
 # 単純なキー・バリュー
 LOG_LEVEL: "info"
 API_TIMEOUT: "30s"

 # ファイルとしてマウント
 nginx.conf: |
 server {
 listen 80;
 server_name localhost;

 location / {
 proxy_pass http://backend:3000;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 }
 }

 # JSON設定
 config.json: |
 {
 "database": {
 "host": "postgres",
 "port": 5432
 },
 "cache": {
 "enabled": true,
 "ttl": 3600
 }
 }
# ConfigMapの使用例
apiVersion: apps/v1
kind: Deployment
metadata:
 name: app-with-config
spec:
 template:
 spec:
 containers:
 - name: app
 image: myapp:1.0
 # 環境変数として注入
 envFrom:
 - configMapRef:
 name: app-config
 # 個別の環境変数
 env:
 - name: LOG_LEVEL
 valueFrom:
 configMapKeyRef:
 name: app-config
 key: LOG_LEVEL
 # ボリュームとしてマウント
 volumeMounts:
 - name: config-volume
 mountPath: /etc/config
 readOnly: true
 volumes:
 - name: config-volume
 configMap:
 name: app-config
 items:
 - key: nginx.conf
 path: nginx.conf
 - key: config.json
 path: config.json

Secret

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
 name: app-secrets
type: Opaque
data:
 # Base64エンコード
 database-url: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0Bsb2NhbGhvc3Q6NTQzMi9teWRi
 api-key: c3VwZXJzZWNyZXRhcGlrZXk=
stringData:
 # プレーンテキスト(自動でBase64エンコード)
 jwt-secret: my-super-secret-jwt-key
---
# Docker Registry認証用Secret
apiVersion: v1
kind: Secret
metadata:
 name: docker-registry-secret
type: kubernetes.io/dockerconfigjson
data:
 .dockerconfigjson: eyJhdXRocyI6ey...
# Secretの作成(コマンドライン)
kubectl create secret generic app-secrets \
 --from-literal=database-url='postgresql://user:YOUR_DB_PASSWORD@localhost:5432/mydb' \
 --from-literal=api-key='supersecretapikey'

# ファイルからSecret作成
kubectl create secret generic tls-secret \
 --from-file=tls.crt=./server.crt \
 --from-file=tls.key=./server.key

# Secret確認(値はマスク)
kubectl get secrets
kubectl describe secret app-secrets

永続化ストレージ

PersistentVolume と PersistentVolumeClaim

# storage.yaml
# PersistentVolume(クラスタ管理者が作成)
apiVersion: v1
kind: PersistentVolume
metadata:
 name: postgres-pv
spec:
 capacity:
 storage: 10Gi
 accessModes:
 - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: standard
 hostPath: # ローカル開発用
 path: /data/postgres
---
# PersistentVolumeClaim(開発者が要求)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: postgres-pvc
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 10Gi
 storageClassName: standard
---
# StorageClass(動的プロビジョニング)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
 name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
 type: gp3
 iops: "3000"
 throughput: "125"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer

StatefulSet(データベース用)

# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: postgres
spec:
 serviceName: postgres
 replicas: 1
 selector:
 matchLabels:
 app: postgres
 template:
 metadata:
 labels:
 app: postgres
 spec:
 containers:
 - name: postgres
 image: postgres:15
 ports:
 - containerPort: 5432
 env:
 - name: POSTGRES_USER
 valueFrom:
 secretKeyRef:
 name: postgres-secrets
 key: username
 - name: POSTGRES_PASSWORD
 valueFrom:
 secretKeyRef:
 name: postgres-secrets
 key: password
 - name: POSTGRES_DB
 value: myapp
 - name: PGDATA
 value: /var/lib/postgresql/data/pgdata
 volumeMounts:
 - name: postgres-storage
 mountPath: /var/lib/postgresql/data
 resources:
 requests:
 memory: "256Mi"
 cpu: "250m"
 limits:
 memory: "1Gi"
 cpu: "1000m"
 volumeClaimTemplates:
 - metadata:
 name: postgres-storage
 spec:
 accessModes: ["ReadWriteOnce"]
 storageClassName: fast-ssd
 resources:
 requests:
 storage: 20Gi
---
# Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
 name: postgres
spec:
 clusterIP: None
 selector:
 app: postgres
 ports:
 - port: 5432

実践的な構成例

完全なWebアプリケーション構成

# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
 name: production
 labels:
 name: production
---
# resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
 name: production-quota
 namespace: production
spec:
 hard:
 requests.cpu: "10"
 requests.memory: 20Gi
 limits.cpu: "20"
 limits.memory: 40Gi
 pods: "50"
---
# limitrange.yaml
apiVersion: v1
kind: LimitRange
metadata:
 name: default-limits
 namespace: production
spec:
 limits:
 - default:
 cpu: "500m"
 memory: "512Mi"
 defaultRequest:
 cpu: "100m"
 memory: "128Mi"
 type: Container
# complete-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: frontend
 namespace: production
spec:
 replicas: 2
 selector:
 matchLabels:
 app: frontend
 template:
 metadata:
 labels:
 app: frontend
 spec:
 containers:
 - name: frontend
 image: myapp/frontend:1.0
 ports:
 - containerPort: 80
 resources:
 requests:
 memory: "128Mi"
 cpu: "100m"
 limits:
 memory: "256Mi"
 cpu: "500m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: backend
 namespace: production
spec:
 replicas: 3
 selector:
 matchLabels:
 app: backend
 template:
 metadata:
 labels:
 app: backend
 spec:
 serviceAccountName: backend-sa
 containers:
 - name: backend
 image: myapp/backend:1.0
 ports:
 - containerPort: 3000
 env:
 - name: DATABASE_URL
 valueFrom:
 secretKeyRef:
 name: app-secrets
 key: database-url
 - name: REDIS_URL
 value: "redis://redis:6379"
 resources:
 requests:
 memory: "256Mi"
 cpu: "250m"
 limits:
 memory: "512Mi"
 cpu: "1000m"
 livenessProbe:
 httpGet:
 path: /health
 port: 3000
 initialDelaySeconds: 30
 periodSeconds: 10
 readinessProbe:
 httpGet:
 path: /ready
 port: 3000
 initialDelaySeconds: 10
 periodSeconds: 5
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: redis
 namespace: production
spec:
 replicas: 1
 selector:
 matchLabels:
 app: redis
 template:
 metadata:
 labels:
 app: redis
 spec:
 containers:
 - name: redis
 image: redis:7-alpine
 ports:
 - containerPort: 6379
 resources:
 requests:
 memory: "64Mi"
 cpu: "50m"
 limits:
 memory: "128Mi"
 cpu: "100m"
---
# Services
apiVersion: v1
kind: Service
metadata:
 name: frontend
 namespace: production
spec:
 selector:
 app: frontend
 ports:
 - port: 80
---
apiVersion: v1
kind: Service
metadata:
 name: backend
 namespace: production
spec:
 selector:
 app: backend
 ports:
 - port: 3000
---
apiVersion: v1
kind: Service
metadata:
 name: redis
 namespace: production
spec:
 selector:
 app: redis
 ports:
 - port: 6379
---
# Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
 name: main-ingress
 namespace: production
 annotations:
 nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
 ingressClassName: nginx
 rules:
 - host: myapp.example.com
 http:
 paths:
 - path: /
 pathType: Prefix
 backend:
 service:
 name: frontend
 port:
 number: 80
 - path: /api
 pathType: Prefix
 backend:
 service:
 name: backend
 port:
 number: 3000

監視とデバッグ

kubectl デバッグコマンド

# Pod一覧(詳細)
kubectl get pods -o wide -n production

# リソース使用状況
kubectl top nodes
kubectl top pods -n production

# Pod詳細
kubectl describe pod <pod-name> -n production

# ログ確認
kubectl logs <pod-name> -n production
kubectl logs <pod-name> -c <container-name> # マルチコンテナ
kubectl logs -f <pod-name> # リアルタイム
kubectl logs --previous <pod-name> # 前回のコンテナ

# Pod内でコマンド実行
kubectl exec -it <pod-name> -- /bin/sh
kubectl exec -it <pod-name> -c <container-name> -- /bin/sh

# ポートフォワード
kubectl port-forward <pod-name> 8080:80
kubectl port-forward svc/<service-name> 8080:80

# イベント確認
kubectl get events -n production --sort-by='.lastTimestamp'

# リソースのYAML出力
kubectl get deployment <name> -o yaml

Horizontal Pod Autoscaler

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
 name: backend-hpa
 namespace: production
spec:
 scaleTargetRef:
 apiVersion: apps/v1
 kind: Deployment
 name: backend
 minReplicas: 2
 maxReplicas: 10
 metrics:
 - type: Resource
 resource:
 name: cpu
 target:
 type: Utilization
 averageUtilization: 70
 - type: Resource
 resource:
 name: memory
 target:
 type: Utilization
 averageUtilization: 80
 behavior:
 scaleDown:
 stabilizationWindowSeconds: 300
 policies:
 - type: Percent
 value: 10
 periodSeconds: 60
 scaleUp:
 stabilizationWindowSeconds: 0
 policies:
 - type: Percent
 value: 100
 periodSeconds: 15
 - type: Pods
 value: 4
 periodSeconds: 15
 selectPolicy: Max
# HPA確認
kubectl get hpa -n production
kubectl describe hpa backend-hpa -n production

Helmによるパッケージ管理

Helmの基本

# Helmインストール
brew install helm

# リポジトリ追加
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update

# チャート検索
helm search repo nginx

# チャートインストール
helm install my-nginx bitnami/nginx

# カスタム値でインストール
helm install my-nginx bitnami/nginx \
 --set service.type=ClusterIP \
 --set replicaCount=3

# values.yamlを使用
helm install my-nginx bitnami/nginx -f values.yaml

# アップグレード
helm upgrade my-nginx bitnami/nginx -f values.yaml

# ロールバック
helm rollback my-nginx 1

# アンインストール
helm uninstall my-nginx

# リリース一覧
helm list

カスタムChartの作成

# チャートの雛形作成
helm create myapp
myapp/
├── Chart.yaml # チャートメタデータ
├── values.yaml # デフォルト値
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ ├── _helpers.tpl # テンプレートヘルパー
│ └── NOTES.txt # インストール後のメッセージ
└── charts/ # 依存チャート
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
 name: {{ include "myapp.fullname" . }}
 labels:
 {{- include "myapp.labels" . | nindent 4 }}
spec:
 replicas: {{ .Values.replicaCount }}
 selector:
 matchLabels:
 {{- include "myapp.selectorLabels" . | nindent 6 }}
 template:
 metadata:
 labels:
 {{- include "myapp.selectorLabels" . | nindent 8 }}
 spec:
 containers:
 - name: {{ .Chart.Name }}
 image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
 ports:
 - containerPort: {{ .Values.service.port }}
 resources:
 {{- toYaml .Values.resources | nindent 12 }}
 {{- if .Values.env }}
 env:
 {{- range $key, $value := .Values.env }}
 - name: {{ $key }}
 value: {{ $value | quote }}
 {{- end }}
 {{- end }}
# values.yaml
replicaCount: 3

image:
 repository: myapp/backend
 tag: "1.0.0"
 pullPolicy: IfNotPresent

service:
 type: ClusterIP
 port: 3000

ingress:
 enabled: true
 className: nginx
 hosts:
 - host: myapp.example.com
 paths:
 - path: /
 pathType: Prefix

resources:
 requests:
 memory: "256Mi"
 cpu: "250m"
 limits:
 memory: "512Mi"
 cpu: "1000m"

env:
 NODE_ENV: production
 LOG_LEVEL: info

autoscaling:
 enabled: true
 minReplicas: 2
 maxReplicas: 10
 targetCPUUtilizationPercentage: 70

まとめ

Kubernetesは、コンテナオーケストレーションの標準となっています。

学習ステップ

ステップ内容
1. 基礎Pod, Deployment, Service
2. 設定ConfigMap, Secret
3. ストレージPV, PVC, StatefulSet
4. ネットワークIngress, NetworkPolicy
5. 運用HPA, Helm, 監視

ベストプラクティス

  1. リソース制限の設定: requests/limitsを必ず設定
  2. ヘルスチェック: liveness/readinessProbeの実装
  3. ラベル活用: 一貫したラベリング戦略
  4. Namespace分離: 環境やチームごとに分離
  5. GitOps: マニフェストのバージョン管理

Kubernetesを習得することで、スケーラブルで信頼性の高いアプリケーション運用が可能になります。

参考リンク

参考リソース

← 一覧に戻る
PR
PR
PR
PR