こんにちは、Mashです。
本記事は、近年大注目されているコンテナオーケストレーション技術である Kubernetes について、図解と実機確認例を交えて解説するシリーズとなります。
今回は第4回。プロダクション環境で求められるPodの冗長化や世代管理についてご紹介します。
Podってなに? というかたがいらっしゃれば前回記事をご参照ください。
- Podの冗長化を管理するリソース「ReplicaSet」を理解する
- ReplicaSetを世代管理するリソース「Deployment」を理解する
- プロダクション環境で求められる「Podの冗長化」「ローリングアップデート/ロールバック」が実現できるようになる
※本シリーズはLinuxコマンドやネットワーク、Dockerの基礎知識がある方を前提としています。
それではいきましょう!
環境
私の検証環境はこのようになっています。
- 検証環境はUbuntu Server 20.04 LTSのサーバ1台構成(MasterとNodeを兼任)
- 検証マシンへSSH接続済み
- kubectlコマンドでK8sクラスタを操作
- ノードがインターネットに接続していること
- Kubernetesバージョンは1.19.4
イメージはこのような状態↓です。

ReplicaSet
まずはReplicaSetリソースについて。
ReplicaSetとは

前回、K8sでコンテナを展開するためにはPodというリソースとして扱う必要があることを学びました。
また、Podの仕様をマニフェストで定義して立ち上げる方法も習得しました。
しかし、これだけではPodに障害が発生した場合にはシステム管理者がまた手動でPodを立ち上げる必要があります。
めっちゃ面倒ですよね。
安心してください。K8sにはPodを冗長化する機能があります。
それが ReplicaSet です。
ReplicaSetマニフェストとPodの複製展開
それでは実際にReplicaSetのマニフェストを作成しながらご説明します。
まずはYAMLファイルを作成します。
$ vi nginx-replicaset.yaml
apiVersion: apps/v1 kind: ReplicaSet metadata: name: nginx labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: mash4work/mash-k8s-nginx:1.0 env: - name: TEST_ENV_VALUE1 value: "01" - name: TEST_ENV_VALUE2 value: "02" ports: - containerPort: 80
こちらがReplicaSetのマニフェストです。kind で「ReplicaSet」と宣言しています。
注目していただきたいのが「template」 以降のセクション。前回使用したPodのマニフェストとほぼ同じ内容になっている点です。
ReplicaSetは「Podがいくつ存在するべきか」を管理する概念なので、ReplicaSetとPodは包含関係にある ということをここで抑えてください。

ReplicaSetのマニフェストがあれば、Podのマニフェストは使いません。
それではReplicaSetのマニフェストを使ってPodを展開してみます。
# ReplicaSetマニフェストからPodを展開
$ kubectl apply -f nginx-replicaset.yaml
replicaset.apps/nginx created
# ReplicaSetの状況を確認
$ kubectl get replicasets
NAME DESIRED CURRENT READY AGE
nginx-c9fff459 3 3 3 8h
# Podの状況を確認
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-mr5nf 1/1 Running 0 16s 10.42.32.142 k8s-master01 <none> <none>
nginx-rcgpb 1/1 Running 0 16s 10.42.32.143 k8s-master01 <none> <none>
nginx-z48tn 1/1 Running 0 16s 10.42.32.140 k8s-master01 <none> <none>
Podが3つ作成されていることが確認できます。
そしてここからがReplicaSetの重要ポイント。
Podをわざと削除してPod障害を起こしてみましょう。
# Podの状況確認
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-mr5nf 1/1 Running 0 65s 10.42.32.142 k8s-master01 <none> <none>
nginx-rcgpb 1/1 Running 0 65s 10.42.32.143 k8s-master01 <none> <none>
nginx-z48tn 1/1 Running 0 65s 10.42.32.140 k8s-master01 <none> <none>
# Podをわざと削除
mash@k8s-master01:~$ kubectl delete pods nginx-mr5nf
pod "nginx-mr5nf" deleted
# 再度Podの状況を確認
mash@k8s-master01:~$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-5gn6g 1/1 Running 0 35s 10.42.32.141 k8s-master01 <none> <none>
nginx-rcgpb 1/1 Running 0 2m1s 10.42.32.143 k8s-master01 <none> <none>
nginx-z48tn 1/1 Running 0 2m1s 10.42.32.140 k8s-master01 <none> <none>
-> 新しいPod(nginx-5gn6g)が作成されている!
# いったん環境をお掃除
$ kubectl delete -f nginx-replicaset.yaml
削除したPodのかわりにまた別のPodが起動して、3つの状態を維持してくれています!
これがReplicaSetの特徴、Podの冗長化機能です。
ReplicaSetマニフェスト内で定義したレプリカ数は「Podの個数に関するあるべき姿」で、Podに障害などが発生するとK8sが「あるべき姿」に自動的に戻してくれるわけですね。
これがオートヒーリング機能になります。
Deployment
さて、ReplicaSetでPodのオートヒーリングを実現することができました。これだけでプロダクション環境を運用するには十分でしょうか。
いえ、まだ足りないんです(泣)
つづいてDeploymentリソースについてご説明します。
Deploymentとは

Deploymentは、ReplicaSetより上位の概念でReplicaSetの世代を管理します。
実際のシステム運用現場では、アプリケーションコードの修正などによりコンテナをバージョンアップしたり、また不具合がみつかった場合にはコンテナをロールバックする必要がでてきます。
こういった作業の度にサービスが全停止していてはお客様に迷惑がかかってしまうので、極力システムに影響を出さずにコンテナを差し替えたいです。
そこで登場するのが Deployment です。
第1回目でK8sは「ローリングアップデート機能」や「ロールバック機能」を持っていることをご紹介しましたが、あれはまさにDeploymentで提供される機能になります。
DeploymentマニフェストとPot展開の世代管理
それではDeploymentのマニフェストを作成します。
$ vi nginx-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: nginx labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: mash4work/mash-k8s-nginx:1.0 env: - name: TEST_ENV_VALUE1 value: "01" - name: TEST_ENV_VALUE2 value: "02" ports: - containerPort: 80
実はDeploymentのマニフェストはReplicaSetとほぼかわりません。kind 指定が変わっているだけです。
これは、DeploymentはReplicaSetの世代を管理するリソースだからです。

ここまでもったいぶってきましたが、Podを展開する場合は基本的にこのDeploymentで管理していきます。
PodやReplicaSetのマニフェストを使うことはありません。
それではDeploymentマニフェストからPodを展開します。
$ kubectl apply -f nginx-deployment.yaml --record=true
deployment.apps/nginx created
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE
nginx-c9fff459-bgtdq 1/1 Running 0 31s
nginx-c9fff459-cdtpj 1/1 Running 0 31s
nginx-c9fff459-pj45h 1/1 Running 0 31s
# PodのIPアドレスにcurl
$ curl http://10.42.32.186
<p>Hostname of this container is nginx-c9fff459-bgtdq</p>
<p>version 1.0</p>
# Deploymentの展開履歴を確認
$ kubectl rollout history deploy nginx
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 kubectl apply --filename=nginx-deployment.yaml --record=true
さて、ここからがご注目
nginx-deployment.yamlを以下の通り編集してください。
nginxコンテナイメージを2.0にバージョンアップしています。
apiVersion: apps/v1 kind: Deployment metadata: name: nginx labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: mash4work/mash-k8s-nginx:2.0 env: - name: TEST_ENV_VALUE1 value: "01" - name: TEST_ENV_VALUE2 value: "02" ports: - containerPort: 80
Deploymentをアップデートします。
難しいことはなく、これまで実施してきた kubectl apply コマンドでOKです。
# 更新したdeploymentマニフェストを適用
$ kubectl apply -f nginx-deployment.yaml --record=true
deployment.apps/nginx configured
# 1秒ごとに kubectl get pods の結果を表示
$ watch -n 1 'kubectl get pods'
Podがローリングアップデートされる様子をご覧ください。

おわかりいただけますでしょうか。
新しいPodがRunningになるにつれて古いPodがTerminatingとなり、最終的には新しいPod3つだけが残りました。
Deploymentを使えば、このようにPodを更新したい場合もサービス提供を停止せずにローリングアップデートを実現することが可能になります。
最後にロールバックしてみましょう。
# Podの状況を確認
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-57f7546dc4-8q7ww 1/1 Running 0 24s
nginx-57f7546dc4-hq49d 1/1 Running 0 25s
nginx-57f7546dc4-tvtj5 1/1 Running 0 21s
# PodのIPアドレスにcurl
# バージョンが2.0になっている
$ curl http://10.42.32.191
<p>Hostname of this container is nginx-57f7546dc4-8q7ww</p>
<p>version 2.0</p>
# Deploymentの展開履歴を確認
# 2世代保持していることがわかる
$ kubectl rollout history deployment
deployment.apps/nginx
REVISION CHANGE-CAUSE
1 kubectl apply --filename=nginx-deployment.yaml --record=true
2 kubectl apply --filename=nginx-deployment.yaml --record=true
# 現在REVISON2なので、REVISON1へロールバック
$ kubectl rollout undo deployment nginx --to-revision=1
deployment.apps/nginx rolled back
# PodのIPアドレスにcurl
# バージョンが1.0にロールバックできている
$ curl http://10.42.32.132
<p>Hostname of this container is nginx-c9fff459-2rg89</p>
<p>version 1.0</p>
無事にNginxイメージのバージョンが1.0に巻き戻ってくれています。ロールバック成功ですね。
このようにDeploymentはREVISIONというかたちで過去の展開を世代管理していて、ロールバックも非常にかんたんにおこなうことができるようになっています。
もちろんサービス停止はありません。すごい!
まとめ
今回はReplicaSetによるPodの冗長化、DeploymentによるPodの世代管理をご紹介しました。
さきほど触れましたが、Podの展開については基本的にDeploymentで管理します。Podや
ReplicaSetで管理することはありません。
Deployment > ReplicaSet > Pod という包含関係を覚えておきましょう。
さて、ここまででPodをいいかんじに複数展開できるようになったわけですが、PodのIPアドレスは再起動のたびに毎回変わってしまいます。
プロダクション環境でPodのIPを確認して接続して、、、なんてやってられません。

そこで登場するのが Service です。
次回からK8sのネットワークや通信の負荷分散を司るServiceリソースについて学習していきます。
今回は以上です。
それじゃあまたね。