Kubernetes をゼロからプロレベルまで: kubectl、マニフェスト、Helm、GitOps の 100 の例
Kubernetes は、複数のマシンからなるクラスター全体でコンテナーを動かします。失敗したコンテナーを再起動し、ノードに分散させ、ダウンタイムなしで新しいバージョンを展開し、負荷に応じてスケールします。この記事では、最初の Pod から本番運用を意識した構成まで、100 の例で進みます。kubectl とコンテキスト、Pod、Deployment とローリングアップデート、Service と Ingress、ConfigMap と Secret、永続ストレージ、Job と CronJob、StatefulSet と DaemonSet、オートスケーリング、リソース要求とプローブ、スケジューリング、RBAC と Pod のセキュリティ、Helm と Kustomize、可観測性とデバッグ、GitOps とバックアップを学びます。正直に伝えると、この記事を準備したコンピューターには Kubernetes クラスターも kubectl もインストールされていなかったため、100 の例はここでは一つも実行していません。マニフェストは現在の Kubernetes API に従って書いていますが、信頼する前にテスト用クラスターでご自身が適用してください。
必要なもの
組み込みの Kubernetes を備えた Docker Desktop、またはローカルの練習用クラスター用の kind か minikube、さらに kubectl と Helm。コンテナーの基礎知識があると役立ちます。このシリーズの Docker の記事が良い出発点です。一部の例では Azure Kubernetes Service などのクラウドプロバイダーと ingress controller を使います。shop.example.com のようなプレースホルダーのホスト名と、<strong-password> のようなプレースホルダーのパスワードを使い、本物の認証情報をマニフェストやコマンドに貼り付けないでください。
例の使い方
bash の例は kubectl、Helm、Kustomize を使います。YAML の例は .yaml ファイルに保存し、本文で指定されたファイル名を使って kubectl apply -f で適用します。Python の例は公式の Kubernetes Python クライアントを使います。
トピック 1: Kubernetes の基本
Kubernetes は制御ループです。目的の状態を YAML で記述すると、現実が一致するまでクラスターが働き続けます。クラスターの構成要素、kubectl クライアント、コンテキスト、名前空間を学びます。
コントロールプレーンとノード
コントロールプレーンは目的の状態を保存し、判断を行います。ワーカーノードはコンテナーを実行します。API サーバーを通じてクラスターと通信し、kubectl はリクエストを送る標準クライアントです。
コンテキストと名前空間
コンテキストは、コマンドが使うクラスター、ユーザー、名前空間を保存します。何かを変更する前に kubectl config current-context で確認し、誤ったクラスターのリソースを削除しないようにします。
マニフェストと宣言的な変更
マニフェストには apiVersion、kind、metadata、spec があります。kubectl apply はファイルとクラスターを比べ、違う部分だけを変更します。これにより変更を繰り返し行え、Git でレビューしやすくなります。
図解
例 1: クライアントとクラスターを確認
kubectl がクラスターと通信できるかを確かめます。
kubectl version --client
kubectl cluster-info
kubectl get nodes -o wide
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 2: kind で練習用クラスターを作成
学習用に自分のコンピューターで動くクラスターです。
kind create cluster --name learn
kubectl config current-context
kubectl get nodes
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 3: コンテキストを切り替える
コマンドがどのクラスターに向くかを選びます。
kubectl config get-contexts
kubectl config use-context kind-learn
kubectl config view --minify --output "jsonpath={.current-context}"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 4: Namespace を作成
1 つのクラスター内でチームやアプリを分けます。
kubectl create namespace shop
kubectl config set-context --current --namespace=shop
kubectl get pods
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 5: マニフェストを読む
すべてのオブジェクトにある 4 つのトップレベルの項目です。
apiVersion: v1
kind: Namespace
metadata:
name: shop
labels:
team: platform
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 6: マニフェストを適用
kubectl apply で宣言的に変更します。
kubectl apply -f namespace.yaml
kubectl get namespace shop --show-labels
kubectl describe namespace shop
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 2: Pod: 最小の単位
Pod は、ネットワークアドレスとストレージを共有する 1 つ以上のコンテナーです。ほとんどの Pod は 1 つのコンテナーで動きますが、メインのアプリと並んで動く必要のあるヘルパーを同じ Pod に入れることもできます。
Pod とは
Pod 内のすべてのコンテナーは同じ IP アドレスを共有し、localhost で互いに通信できます。Pod は一時的なものです。1 つが停止すると、クラスターは新しい名前とアドレスで作り直すため、Pod のアドレスに依存してはいけません。
init コンテナーとサイドカー
init コンテナーはメインのコンテナーが始まる前に完了まで実行されます。データベースの起動を待つ用途に向いています。サイドカーはログ記録、プロキシ、ファイル同期のためにメインのコンテナーと並んで動き、ボリュームを共有します。
ログ、イベント、exec
kubectl logs は出力を表示し、kubectl describe はイメージの取得失敗やスケジューリングの失敗などのイベントを表示し、kubectl exec はシェルを開きます。起動の失敗の多くは describe で説明されるので、まず describe を読んでください。
例 7: Pod をすばやく実行
命令型のコマンドで、簡単なテストだけに使います。
kubectl run hello --image=nginx:1.27 --port=80
kubectl get pod hello -o wide
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 8: 宣言的な Pod
デプロイする最小の単位です。
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 9: ログとイベントを読む
Pod に何が起きたかを調べます。
kubectl logs web
kubectl logs web --previous
kubectl get events --sort-by=.lastTimestamp
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 10: Pod の中でコマンドを実行
内側からデバッグします。
kubectl exec -it web -- sh
kubectl exec web -- nginx -v
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 11: Pod へのポートフォワード
テストのためにご自身のコンピューターから Pod に接続します。
kubectl port-forward pod/web 8080:80
curl http://localhost:8080
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 12: 2 つのコンテナーがボリュームを共有
サイドカーのパターンです。
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
volumes:
- name: logs
emptyDir: {}
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do date >> /logs/app.log; sleep 5; done"]
volumeMounts:
- name: logs
mountPath: /logs
- name: log-reader
image: busybox:1.36
command: ["sh", "-c", "tail -F /logs/app.log"]
volumeMounts:
- name: logs
mountPath: /logs
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 13: init コンテナーが待機
アプリが起動する前に準備の手順を実行します。
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ["sh", "-c", "until nslookup db; do sleep 2; done"]
containers:
- name: app
image: nginx:1.27
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 14: テスト用の Pod を削除
不要になったオブジェクトを片付けます。
kubectl delete pod hello web sidecar-demo init-demo --ignore-not-found
kubectl get pods
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 3: Deployment とローリングアップデート
Pod を直接作ることはほとんどありません。Deployment は ReplicaSet を管理します。ReplicaSet は指定数の Pod を稼働させ続け、Deployment は新しいバージョンが古いものを少しずつ置き換えるようにローリングアップデートを行います。
レプリカ数とセレクター
replicas フィールドは必要な Pod の数を設定します。セレクターは Pod のラベルと一致しなければならず、Deployment がどの Pod を所有するかを判断します。一致しない場合は作成時に拒否されます。
ローリングアップデートの戦略
maxSurge は更新中に存在してよい追加の Pod 数、maxUnavailable は欠けてもよい Pod 数を設定します。maxUnavailable を 0 にすると、リリース中も容量を完全に保てます。
ロールアウト履歴とロールバック
変更のたびに新しい ReplicaSet とリビジョン番号が作られます。kubectl rollout undo で前のバージョンに戻り、kubectl rollout history でリビジョンを一覧できるので、特定のものを選べます。
例 15: Deployment がレプリカを維持
ローリングアップデートを自動で管理します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 16: Deployment を作成して監視
ロールアウトの状態を確認します。
kubectl apply -f deployment.yaml
kubectl rollout status deployment/web
kubectl get deploy,rs,pods -l app=web
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 17: 手動でスケール
レプリカ数を変えます。
kubectl scale deployment web --replicas=5
kubectl get pods -l app=web -o wide
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 18: 新しいイメージをロールアウト
アプリケーションのバージョンを更新します。
kubectl set image deployment/web web=nginx:1.27.2
kubectl rollout status deployment/web
kubectl rollout history deployment/web
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 19: 問題のあるリリースをロールバック
失敗したロールアウトを元に戻します。
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=1
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 20: ローリングアップデートを制御
maxSurge と maxUnavailable。
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 21: ロールアウトを一時停止と再開
複数の変更をまとめて適用します。
kubectl rollout pause deployment/web
kubectl set resources deployment/web -c web --limits=memory=256Mi
kubectl rollout resume deployment/web
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 22: readiness probe
アプリの準備ができるまでトラフィックを待たせます。
containers:
- name: web
image: nginx:1.27
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 4: Service とネットワーク
Pod は現れたり消えたりするため、クライアントには安定した名前とアドレスが必要です。Service はラベルで Pod を選び、変わらない仮想 IP と DNS 名を与えます。
Service の種類
ClusterIP はクラスター内からのみ到達できます。NodePort はすべてのノードでポートを開きます。LoadBalancer はクラウドプロバイダーに外部のロードバランサーを要求します。必要を満たす最小の種類を選んでください。
DNS とエンドポイント
すべての Service には web-svc.shop.svc.cluster.local のような DNS 名が付き、同じ名前空間内では短い名前でも解決できます。kubectl get endpoints は Service の背後にある Pod のアドレスを示します。Service が応答しないときに最初に確認すべき点です。
ヘッドレス Service
clusterIP を None に設定すると、DNS は 1 つの仮想 IP ではなく個々の Pod のアドレスを返します。データベースやクラスター化されたシステムは、クライアントが特定のメンバーと話せるようにこれを使います。
例 23: ClusterIP の Service
クラスター内の安定したアドレスです。
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 24: kubectl expose で公開
Service を作る手早い方法です。
kubectl expose deployment web --port=80 --target-port=80 --name=web-svc
kubectl get svc web-svc
kubectl get endpoints web-svc
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 25: 一時的な Pod から Service を呼び出す
クラスター内の DNS を確かめます。
kubectl run tmp-client --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://web-svc
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 26: NodePort の Service
すべてのノードでポートを開きます。
apiVersion: v1
kind: Service
metadata:
name: web-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
nodePort: 30080
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 27: LoadBalancer の Service
公開アクセス用のクラウドのロードバランサーです。
apiVersion: v1
kind: Service
metadata:
name: web-public
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "false"
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 28: 外部アドレスを確認
クラウドが割り当てたアドレスを読みます。
kubectl get svc web-public -o jsonpath="{.status.loadBalancer.ingress[0].ip}"
kubectl get svc web-public -w
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 29: 名前付きポート
ポート名を使えば番号を安全に変えられます。
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- name: http
port: 8000
targetPort: http
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 30: ヘッドレス Service
個々の Pod のアドレスを取得します。
apiVersion: v1
kind: Service
metadata:
name: db-headless
spec:
clusterIP: None
selector:
app: db
ports:
- port: 5432
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 5: ConfigMap と Secret による設定
設定はイメージの外に置きましょう。ConfigMap は通常の設定を、Secret は機密の値を保持します。どちらも Pod の中で環境変数やファイルになれるので、イメージを作り直さずに設定を変えられます。
ConfigMap
ConfigMap はキーと値のペアや、ファイル全体を保存します。envFrom ですべてのキーを環境変数として読み込むか、アプリケーションがファイルを読む場合は ConfigMap をディレクトリとしてマウントします。
Secret
Secret は base64 でエンコードされた値を保存します。base64 は暗号化ではなくエンコーディングです。そのため、クラスターの保存時暗号化を有効にし、RBAC で Secret を読める人を制限し、可能な限りファイルとしてマウントしてください。
変更の再読み込み
環境変数は起動時にだけ読まれるため、変更された ConfigMap には新しいロールアウトが必要です。マウントされたファイルは遅れて更新されますが、アプリケーションがそれを読み直す必要があります。設定を変えるときは再読み込みも計画してください。
例 31: リテラル値から ConfigMap
秘密ではない設定を保存します。
kubectl create configmap app-config --from-literal=LOG_LEVEL=info --from-literal=FEATURE_X=on
kubectl get configmap app-config -o yaml
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 32: ConfigMap のマニフェスト
設定を YAML で書きます。
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
FEATURE_X: "on"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 33: ConfigMap を環境変数として使う
envFrom ですべてのキーを読み込みます。
containers:
- name: api
image: sample-api:1.4.2
envFrom:
- configMapRef:
name: app-config
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 34: ConfigMap をファイルとしてマウント
コンテナー内の設定ファイルです。
containers:
- name: web
image: nginx:1.27
volumeMounts:
- name: site-config
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: site-config
configMap:
name: nginx-conf
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 35: Secret を作成
パスワードをファイルに書かずに保存します。
kubectl create secret generic db-secret --from-literal=DB_PASSWORD='<strong-password>'
kubectl get secret db-secret -o jsonpath="{.type}"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 36: Secret のマニフェスト
stringData でプレーンテキストを書けます。
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
DB_PASSWORD: <strong-password>
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 37: Secret の値を読む
復号された値を確認し、非公開に保ちます。
kubectl get secret db-secret -o jsonpath="{.data.DB_PASSWORD}" | base64 --decode; echo
# Do not print secrets in shared logs or screenshots.
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 38: Secret をファイルとしてマウント
読み取り専用の認証情報ファイルです。
containers:
- name: api
image: sample-api:1.4.2
volumeMounts:
- name: db-creds
mountPath: /var/run/secrets/db
readOnly: true
volumes:
- name: db-creds
secret:
secretName: db-secret
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 6: 永続ストレージ
コンテナーは再起動するとファイルを失います。PersistentVolumeClaim は Pod の寿命を超えて残るストレージを要求し、StorageClass はそのストレージをどう作るかをクラスターに伝えます。
PV、PVC、StorageClass
PersistentVolume は実際のストレージ、PersistentVolumeClaim はそれへの要求、StorageClass は必要に応じてボリュームを作る方法を定義します。多くのクラスターは既定の StorageClass を備えているため、claim にはサイズだけを指定すれば足りることが多いです。
アクセスモード
ReadWriteOnce は一度に 1 つのノードでボリュームをマウントします。ほとんどのデータベースに適しています。ReadWriteMany は複数のノードから書き込めますが、対応しているストレージは限られます。ストレージが対応しているモードを選んでください。
回収ポリシー
Delete は claim を削除したときにストレージも削除します。Retain はデータを残し、手動での復旧を可能にします。誤った削除から守りたいデータには Retain を使い、いずれの場合も重要なデータはバックアップしてください。
例 39: PersistentVolumeClaim
クラスターにストレージを要求します。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 40: claim を使う Pod
Pod を再起動してもデータが残ります。
apiVersion: v1
kind: Pod
metadata:
name: writer
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "echo hello >> /data/out.txt; sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 41: バインドされたストレージを確認
claim とボリュームの状態を読みます。
kubectl get pvc data-pvc
kubectl get pv
kubectl describe pvc data-pvc
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 42: StorageClass を選ぶ
ディスクの種類を選びます。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fast-pvc
spec:
storageClassName: managed-premium
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 43: StorageClass を一覧表示
既定のクラスを見つけます。
kubectl get storageclass
kubectl get storageclass -o custom-columns=NAME:.metadata.name,DEFAULT:.metadata.annotations."storageclass\.kubernetes\.io/is-default-class"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 44: claim を拡張
対応しているクラスではストレージを増やします。
kubectl patch pvc data-pvc -p '{"spec":{"resources":{"requests":{"storage":"2Gi"}}}}'
kubectl get pvc data-pvc
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 45: 回収ポリシー
claim を削除した後もデータを残します。
apiVersion: v1
kind: PersistentVolume
metadata:
name: archive-pv
spec:
capacity:
storage: 5Gi
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /srv/archive
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 46: ephemeral storage の上限
暴走する一時ファイルを止めます。
containers:
- name: worker
image: busybox:1.36
resources:
limits:
ephemeral-storage: 1Gi
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 7: Ingress とトラフィックのルーティング
Ingress は、クラスター外からの HTTP と HTTPS のルートを Service に公開します。ホスト名とパスでルーティングし、TLS を終端するため、1 つの公開アドレスで多くのアプリケーションを提供できます。
Ingress とコントローラー
Ingress リソースだけでは何もしません。ingress-nginx などの ingress controller が Ingress オブジェクトを読み、プロキシを設定します。クラスターごとに 1 つのコントローラーを入れ、各 Ingress に ingressClassName を設定します。
TLS 証明書
tls セクションは、証明書と鍵を保持する Secret を指します。cert-manager は証明書を自動で要求し更新できるため、有効期限を手作業で管理する必要はありません。
パスとホストによるルーティング
ルールはホスト名とパスの接頭辞に一致します。最も具体的なパスが優先されるので、/api は API に、/ はウェブサイトに送られます。ルーティングの表が確認しやすいように、ルートはシンプルに保ちましょう。
例 47: ホスト名による Ingress
Service への HTTP ルーティングです。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 48: TLS 付きの Ingress
Secret に保存された証明書による HTTPS です。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-tls
spec:
ingressClassName: nginx
tls:
- hosts: [shop.example.com]
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 49: ingress controller をインストール
Helm でコントローラーを入れます。
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress ingress-nginx/ingress-nginx --namespace ingress --create-namespace
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 50: パスによるルーティング
/api と / を別々の Service に送ります。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-routes
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 8000
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 51: アップロード用の Ingress アノテーション
1 つのサイトについて、アップロードの上限とタイムアウトを上げます。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: upload-ingress
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "20m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
spec:
ingressClassName: nginx
rules:
- host: upload.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: uploader
port:
number: 80
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 52: cert-manager の ClusterIssuer
Let's Encrypt に証明書を自動で依頼します。
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
ingressClassName: nginx
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 53: Ingress から証明書を要求
アノテーションで TLS を自動管理します。
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts: [shop.example.com]
secretName: shop-tls
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 54: API 用の NetworkPolicy
Web 層だけが API に接続できるようにします。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-web
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- port: 8000
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 8: Job と CronJob
すべてのワークロードが永久に動くわけではありません。Job は成功するまでタスクを実行し、CronJob はクラスターの中で動く cron テーブルのように、スケジュールに従って Job を開始します。
Job と完了数
Job は、要求された数の完了が成功するまで Pod を作ります。backoffLimit は再試行の上限です。Job では Always は許されないため、restartPolicy には Never か OnFailure を使ってください。
並列の作業
parallelism は同時に動く Pod の数、completions は必要な成功回数を設定します。このパターンは、複数のワーカーで項目のバッチを処理します。
CronJob と履歴
CronJob はスケジュールに cron 形式を使います。concurrencyPolicy を Forbid にすると実行の重複を防ぎ、履歴の上限で古い Job を削除してクラスターを整理します。
例 55: Job は完了まで実行
データベースの移行などの一回限りの作業です。
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: sample-api:1.4.2
command: ["python", "manage.py", "migrate"]
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 56: Job を実行して待つ
作業が終わるまで待ち、ログを読みます。
kubectl apply -f job.yaml
kubectl wait --for=condition=complete job/db-migrate --timeout=120s
kubectl logs job/db-migrate
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 57: 並列の Job
複数のワーカーで多くの項目を処理します。
apiVersion: batch/v1
kind: Job
metadata:
name: thumbnails
spec:
completions: 10
parallelism: 3
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: thumbnailer:2.0
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 58: スケジュールされた CronJob
毎晩 02:00 にレポートを実行します。
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: reporter:1.1
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 59: CronJob の確認と手動実行
過去の実行を確認し、手動で 1 回起動します。
kubectl get cronjob nightly-report
kubectl get jobs --sort-by=.metadata.creationTimestamp
kubectl create job --from=cronjob/nightly-report manual-report-1
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 60: Job の履歴の上限
完了した Job が溜まりすぎないようにします。
spec:
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
startingDeadlineSeconds: 300
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 9: StatefulSet と DaemonSet
ワークロードによっては、安定したアイデンティティや、ノードごとに 1 つの Pod が必要です。StatefulSet は Pod に順序付きの名前と専用のストレージを与え、DaemonSet は条件に合うすべてのノードで Pod のコピーを 1 つずつ動かします。
StatefulSet のアイデンティティ
Pod は name-0、name-1 などと名付けられ、それぞれ volumeClaimTemplates から自分の claim を保持します。Pod が再起動しても名前は変わりません。データベースやメッセージブローカーに適しています。
順序付きの操作
既定では StatefulSet は Pod を順番に起動・停止します。パーティション付きの更新では、番号の大きい Pod から先に変更できるので、ステートフルなシステムを制御されたカナリアとして更新できます。
DaemonSet
DaemonSet は各ノードに 1 つの Pod を置きます。ログ収集、監視エージェント、ネットワークプラグインに適しています。nodeSelector や tolerations で特定のノードに限定できます。
例 61: StatefulSet
データベース向けの安定した名前とストレージです。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 62: StatefulSet を確認
序数付きの名前と、Pod ごとの claim です。
kubectl get statefulset redis
kubectl get pods -l app=redis
kubectl get pvc
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 63: DaemonSet
エージェント用に、すべてのノードに 1 つの Pod を置きます。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-agent
spec:
selector:
matchLabels:
app: node-agent
template:
metadata:
labels:
app: node-agent
spec:
containers:
- name: agent
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 64: ラベル付きノードの DaemonSet
nodeSelector でノードを選びます。
spec:
template:
spec:
nodeSelector:
workload: edge
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 65: StatefulSet の partition
まず一部の Pod だけを更新します。
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 66: StatefulSet を慎重にスケール
縮小した後に claim を確認します。
kubectl get statefulset redis -o jsonpath="{.spec.replicas}"; echo
kubectl scale statefulset redis --replicas=2
kubectl get pvc -l app=redis
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 10: オートスケーリング
負荷は一日の中で変化します。オートスケーリングは需要が高いときに Pod を増やし、低いときに減らします。使った分だけ払い、ユーザーは速い応答を得られます。
Horizontal Pod Autoscaler
HPA は、平均 CPU 使用率などの指標を目標と比べ、レプリカ数を変えます。使用率はリクエストに対して測られるため、metrics server と Pod の CPU リクエストが必要です。
スケーリングの挙動
behavior フィールドで、HPA がどれくらい速くスケールアップ・ダウンするかを制御します。縮小時の安定化ウィンドウは、短い急増の後に急激に上下するのを防ぎます。
イベント駆動のスケーリング
KEDA はキューの長さやメッセージの遅延といった外部のシグナルでスケールし、作業がないときはゼロまで減らせます。キューを読むワーカーに使います。
例 67: リソース使用量をリアルタイムで確認
kubectl top には metrics server が必要です。
kubectl top nodes
kubectl top pods -l app=web
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 68: CPU で自動スケール
平均負荷が高いときに Pod を増やします。
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 69: コマンドラインで HPA を作成
自動スケールの反応を監視します。
kubectl autoscale deployment web --min=2 --max=10 --cpu-percent=70
kubectl get hpa web -w
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 70: メモリで自動スケール
複数の指標を使います。
spec:
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 71: HPA の縮小速度を制限
安定化ウィンドウで振動を防ぎます。
spec:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 72: イベント駆動のスケーリング
KEDA でキューの長さに応じてワーカーを増減します。
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: worker-scaler
spec:
scaleTargetRef:
name: worker
minReplicaCount: 0
maxReplicaCount: 20
triggers:
- type: rabbitmq
metadata:
queueName: orders
mode: QueueLength
value: "50"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 11: リソース、プローブ、スケジューリング
各コンテナーが必要とする CPU とメモリの量、そして正常かどうかの判断方法を Kubernetes に伝えます。スケジューラーは requests を使って Pod を配置し、プローブは再起動やトラフィック送信の判断をします。
requests と limits
request は、スケジューラーがコンテナーのために確保する量です。limit は使ってよい最大量です。limit を超えたメモリはコンテナーを強制終了させ、超えた CPU は速度を制限されます。実測の使用量に基づいて両方を設定してください。
プローブ
liveness probe は止まったコンテナーを再起動します。readiness probe は準備中のコンテナーを Service のエンドポイントから外します。startup probe は起動の遅いアプリケーションのために他のプローブを待たせます。
分散と中断
トポロジーの分散制約は、レプリカを異なるノードやゾーンに配置します。PodDisruptionBudget は、drain などの計画的な操作で同時に削除できる Pod の数を制限します。
例 73: requests と limits
スケジューラーとカーネルがどう使うか。
containers:
- name: api
image: sample-api:1.4.2
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 74: liveness probe
止まってしまったコンテナーを再起動します。
livenessProbe:
httpGet:
path: /healthz
port: 8000
initialDelaySeconds: 10
periodSeconds: 15
failureThreshold: 3
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 75: startup probe
起動の遅いアプリに起動時間を与えます。
startupProbe:
httpGet:
path: /healthz
port: 8000
failureThreshold: 30
periodSeconds: 10
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 76: PodDisruptionBudget
メンテナンス中も十分なレプリカを残します。
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 77: レプリカを複数のノードに分散
すべてのレプリカを 1 台に載せないようにします。
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 78: PriorityClass
クラスターが満杯でも重要な Pod をスケジュールします。
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: business-critical
value: 100000
globalDefault: false
description: "Keep these Pods running when the cluster is full."
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 12: セキュリティ: RBAC と Pod のセキュリティ
最小権限の原則は、人にもアプリケーションにも適用されます。RBAC は誰が何をできるかを決め、Pod のセキュリティ設定はコンテナーがノード上で何をできるかを制限します。
ServiceAccount、Role、バインディング
ServiceAccount は Pod のアイデンティティです。Role は 1 つの名前空間内のリソースに対して許可される動詞を列挙し、RoleBinding がそれを結び付けます。アプリケーションに必要な動詞だけを与え、アプリケーションに cluster-admin を与えないでください。
アクセスの確認
kubectl auth can-i は権限をテストします。別のユーザーや ServiceAccount として確認することもできます。レビューの手順で実行し、事故の後ではなく、デプロイの前にミスが分かるようにしてください。
Pod のセキュリティ
名前空間のラベルは、restricted などの Pod Security Standards を強制します。これは root のコンテナーや権限昇格をブロックします。capability を削除し、ルートファイルシステムを読み取り専用にする securityContext と組み合わせてください。
例 79: ServiceAccount
Pod の ID で、トークンは無効にします。
apiVersion: v1
kind: ServiceAccount
metadata:
name: api-sa
automountServiceAccountToken: false
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 80: 最小権限の Role
1 つの namespace の ConfigMap だけを読めます。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-config
namespace: shop
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 81: RoleBinding
Role を ServiceAccount に結び付けます。
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: api-read-config
namespace: shop
subjects:
- kind: ServiceAccount
name: api-sa
namespace: shop
roleRef:
kind: Role
name: read-config
apiGroup: rbac.authorization.k8s.io
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 82: auth can-i で権限を確認
デプロイ前に RBAC を検証します。
kubectl auth can-i list configmaps --as=system:serviceaccount:shop:api-sa -n shop
kubectl auth can-i delete pods --as=system:serviceaccount:shop:api-sa -n shop
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 83: Pod Security Standards
namespace に restricted プロファイルを適用します。
kubectl label namespace shop pod-security.kubernetes.io/enforce=restricted
kubectl label namespace shop pod-security.kubernetes.io/warn=restricted
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 84: 強化された security context
root 以外、読み取り専用、余分な capability なし。
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 13: Helm と Kustomize
実際のアプリケーションには多数のマニフェストと複数の環境があります。Helm はそれらを values 付きの chart としてパッケージ化し、Kustomize は 1 つの base を保ち、環境ごとに小さなオーバーレイを適用します。
Helm の chart と release
chart は既定の値を持つテンプレートのパッケージです。release はインストールされた 1 つのコピーです。helm でインストール、アップグレード、ロールバックを行い、--atomic を使うと失敗したアップグレードが自動で元に戻ります。
環境ごとの values
環境ごとに 1 つの values ファイルを保ち、-f で渡します。インストール前に helm template で描画結果を確認すると、クラスターに何が届くかが正確に分かります。
Kustomize のオーバーレイ
Kustomize は base と、レプリカ数やイメージ、ラベルを変えるオーバーレイを、テンプレートなしで組み合わせます。kubectl apply -k で結果を適用し、kubectl diff で先に変更を確認できます。
例 85: Helm でアプリをインストール
リポジトリの chart を使います。
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install shop-cache bitnami/redis --namespace shop --set architecture=standalone
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 86: chart を展開して確認
YAML をプレビューし、サーバーで検証します。
helm template shop-web ./chart --values values-prod.yaml > rendered.yaml
kubectl apply --dry-run=server -f rendered.yaml
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 87: Helm の values ファイル
1 つの環境のための設定です。
replicaCount: 3
image:
repository: registry.example.com/team/web
tag: "1.4.2"
resources:
limits:
memory: 512Mi
ingress:
enabled: true
host: shop.example.com
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 88: Helm でアップグレードとロールバック
失敗したら自動で元に戻る atomic アップグレードです。
helm upgrade shop-web ./chart -n shop -f values-prod.yaml --atomic --wait
helm history shop-web -n shop
helm rollback shop-web 1 -n shop
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 89: Kustomize のオーバーレイ
テンプレートなしで環境ごとの変更を行います。
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
replicas:
- name: web
count: 5
images:
- name: web
newTag: "1.4.2"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 90: Kustomize のオーバーレイを適用
まず diff を確認してから適用します。
kubectl kustomize overlays/prod | kubectl diff -f -
kubectl apply -k overlays/prod
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 14: 可観測性とデバッグ
Pod に問題が起きたとき、答えは状態、イベント、ログ、メトリクスにあります。落ち着いた順序立てた手順で、ほとんどの障害をすばやく見つけられます。同じシグナルがダッシュボードやアラートにも使われます。
状態、イベント、describe
kubectl get pod はフェーズと再起動回数を表示します。kubectl describe は ImagePullBackOff、CrashLoopBackOff、FailedScheduling などのイベントを一覧にし、Pod が止まっている理由を説明します。
複数の Pod にまたがるログ
kubectl logs -l でラベルによって Pod を選び、--prefix を使うと、どの Pod が各行を書いたかが分かります。クラッシュして再起動したコンテナーのログは --previous で読めます。
一時的なデバッグコンテナー
kubectl debug は実行中の Pod に一時的なコンテナーを接続します。アプリケーションのイメージにないツールを、アプリを再起動せずに使えます。
例 91: 複数の Pod のログ
ラベルで Pod を選びます。
kubectl logs -l app=web --all-containers --prefix --tail=20
kubectl logs -l app=web -f --max-log-requests=10
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 92: Pod が起動しない理由
describe の出力とコンテナーの状態を読みます。
kubectl describe pod web-6d4cf56db6-abcde
kubectl get pod web-6d4cf56db6-abcde -o jsonpath="{.status.containerStatuses[0].state}"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 93: 一時的なデバッグコンテナー
実行中の Pod を再起動せずにデバッグします。
kubectl debug -it web-6d4cf56db6-abcde --image=busybox:1.36 --target=web -- sh
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 94: イベントとノードの負荷
容量やエビクションの問題を見つけます。
kubectl get events -A --sort-by=.lastTimestamp | tail -20
kubectl describe node worker-1 | grep -A5 "Conditions"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
トピック 15: 本番運用と GitOps
本番運用とは、繰り返し可能なリリース、安全なメンテナンス、復旧を意味します。GitOps はクラスターを Git リポジトリと一致させ続け、バックアップは再構築できないオブジェクトやデータを守ります。
GitOps
Argo CD のような GitOps ツールはリポジトリを監視し、そのマニフェストをクラスターに適用します。すべての変更はプルリクエストを通り、selfHeal は Git からずれた手動の変更を元に戻します。
ダウンタイムなしのメンテナンス
kubectl drain は、アップグレードの前にノードから Pod を移動させます。PodDisruptionBudget は十分なレプリカを稼働させ続けます。ノードの準備ができたら kubectl uncordon を実行します。
バックアップと復旧
Velero はクラスターのオブジェクトと永続ボリュームをオブジェクトストレージにバックアップします。一度も復元していないバックアップは証明されていないので、復元は別のクラスターでテストしてください。
例 95: Argo CD による GitOps
クラスターを Git リポジトリと同期させ続けます。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: shop
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/shop-deploy
targetRevision: main
path: overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: shop
syncPolicy:
automated:
prune: true
selfHeal: true
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 96: 本番向けの Deployment
probe、制限、安全なロールアウトを一緒に設定します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: api
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.example.com/team/api:1.4.2
ports:
- containerPort: 8000
resources:
requests: {cpu: 250m, memory: 256Mi}
limits: {memory: 512Mi}
readinessProbe:
httpGet: {path: /ready, port: 8000}
livenessProbe:
httpGet: {path: /healthz, port: 8000}
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 97: Velero でバックアップ
クラスターのオブジェクトとボリュームをストレージに保存します。
velero install --provider azure --plugins velero/velero-plugin-for-microsoft-azure:v1.10.0 --bucket k8s-backups
velero backup create shop-daily --include-namespaces shop
velero backup describe shop-daily
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 98: ノードの drain と uncordon
Pod を止めずにメンテナンスします。
kubectl cordon worker-1
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon worker-1
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 99: Python からレプリカを確認
公式の Kubernetes クライアントを使います。
from kubernetes import client, config
config.load_kube_config()
apps = client.AppsV1Api()
for d in apps.list_namespaced_deployment("shop").items:
ready = d.status.ready_replicas or 0
print(d.metadata.name, f"{ready}/{d.spec.replicas}")
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
例 100: リリース前チェックのスクリプト
チェックが失敗したらリリースを止めます。
#!/usr/bin/env bash
set -euo pipefail
NS="$1"
kubectl -n "$NS" rollout status deployment --timeout=180s
kubectl -n "$NS" get pdb
kubectl -n "$NS" auth can-i --list --as=system:serviceaccount:"$NS":default | grep -q "\*" && { echo "default account has wildcard rights"; exit 1; }
kubectl -n "$NS" get pods -o jsonpath="{range .items[*]}{.metadata.name}{' runAsRoot='}{.spec.securityContext.runAsNonRoot}{'\n'}{end}"
echo "release checks passed for namespace $NS"
*この例は、この記事を準備したコンピューターに Kubernetes クラスターも kubectl もインストールされていなかったため、ここでは実行していません。*
ラボ: 小さな Web アプリをデプロイ、公開、リリースする
記事の内容を 1 つの練習用クラスターにまとめます。プローブと制限を持つ Deployment、Service、Ingress、ConfigMap と Secret、ロールバック付きのローリングアップデート、そしてリリースチェックです。
1. kind で練習用クラスターを作り、shop という名前空間を作成して既定に設定します。
2. 3 つのレプリカ、リソースの requests と limits、readiness probe、liveness probe を持つ Deployment を適用します。
3. ClusterIP の Service を追加してエンドポイントを確認し、一時的な Pod から呼び出します。
4. 設定用の ConfigMap と、プレースホルダーのパスワードを入れた Secret を作り、両方を Pod にマウントします。
5. 新しいイメージのバージョンをロールアウトし、ロールアウトを見守ってから kubectl rollout undo で元に戻します。
6. shop.example.com 用の Ingress を追加し、NetworkPolicy で API を制限します。
7. 例 100 のリリースチェックリストを名前空間に対して実行し、どれか失敗したら停止します。
次のステップ
ラボの後は、サンドボックスのサブスクリプションでマネージドクラスターを使って練習し、自分のアプリケーション用の Helm chart を学び、すべての変更が Git でレビューされるように GitOps を設定してください。例はリポジトリに保存しておき、クラスターをアップグレードするたびに再実行してください。Kubernetes の API はバージョンによって変わるからです。
まとめ
Kubernetes はコンテナーを稼働させ続け、ノードに分散させ、ダウンタイムなしで更新し、需要に応じてスケールします。kubectl とコンテキスト、Pod、Deployment とロールバック、Service と Ingress、設定とシークレット、ストレージ、Job と CronJob、StatefulSet と DaemonSet、オートスケーリング、リソース要求とプローブ、RBAC と Pod のセキュリティ、Helm と Kustomize、デバッグ、そして本番向けの GitOps とバックアップを学びました。