Kubernetes デプロイメント についての自習メモ (その１)


Kubernetesのデプロイメントについて自習したメモで、YAML先頭部分で、良く見かける `kind: Deployment` について自習ノートです。

~~~
apiVersion: apps/v1beta2 # for versions before 1.8.0 use apps/v1beta1
kind: Deployment
~~~

## デプロイメントとは

デプロイメントは、ポッドとレプリカ・セット（次世代レプリケーションコントローラ）の宣言的な更新を提供します。 デプロイメント・オブジェクトの目的の状態を記述するだけで、デプロイメント・コントローラは、実際の状態を目的の状態へ、制御下の速度で、変更します。 デプロイメントを定義して新しいリソースを作成したり、既存のリソースを新しいものに置き換えることができます。

典型的な使用例は次のとおりです。

* デプロイメントを作成し、レプリカセットとポッドが起動します。
* デプロイメントの状態をチェックして、成功か否かを確認できます。
* 実行中のデプロイメントを更新して、ポッドを再作成できます。（たとえば、新しいイメージを使用するなど）
* 更新後のデプロイメントが安定しないケースには、以前のレビジョンにロールバックできます。 
* デプロイメントの一時停止と再開


## デプロイメントの作成
デプロイメント・コントローラーは、次のYAMLを読み込んで、nginx の ポッドを３個、レプリカ・セットを作成します。

~~~file:nginx-deployment.yaml
 1: apiVersion: apps/v1beta2 # for versions before 1.8.0 use apps/v1beta1
 2: kind: Deployment
 3: metadata:
 4:   name: nginx-deployment
 5:   labels:
 6:     app: nginx
 7: spec:
 8:   replicas: 3    <--- レプリカ・セット 3個を指定
 9:   selector:
10:     matchLabels:
11:       app: nginx
12:   template:
13:     metadata:
14:       labels:
15:         app: nginx
16:     spec:
17:       containers:
18:       - name: nginx
19:         image: nginx:1.7.9   <--- コンテナのイメージを指定
20:         ports:
21:         - containerPort: 80
~~~


#### 主要な項目解説
* 一行目にAPIのバージョン指定 (注１)
* コントローラの種別指定は、2行目 `kind: Deployment`
* このデプロイメントの名前は、3行目の`metadata:`の下 `name:`の nginx-deployment
* デプロイメントのポッド数は、8行目`replicas:`で示される3つのレプリカのポッドを作成
* 管理対象のポッドは、9行目からの`app: nginx`に一致するポッド
* ポッド・テンプレートであるメタ情報と仕様が、12行目から記述、メタ情報(metadata)には管理対象として識別するラベル、仕様(spec)には、レジストリに登録されたコンテナ・イメージの名前とタグ、外部とのポート番号の指定

(注1) IBM Cloud k8s では ver 1.8.2.1501 を利用に際しても apps/v1beta1 で提供されています。apiVersion マイナー文字列の扱いは、共通では無い様です。


### デプロイメント作成の実行例

次の様にGitHubに保存された上記のYAMLファイルを指定することもできます。

~~~
$ kubectl create -f https://raw.githubusercontent.com/takara9/k8s_study/master/nginx_deploy.yaml
~~~

動作を確認してするには、`get deployment` のオプションで実行します。

~~~
$ kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           12s
~~~


* NAME  クラスター内のデプロイメントの名前をリストします。
* DESIRED  アプリケーションのレプリカの必要な数を表示します。これは、デプロイメントの作成時に定義します。 これが望ましい状態です。
* CURRENT  現在実行中のレプリカの数を表示します。
* UP-TO-DATE  希望の状態になるように更新されたレプリカの数を表示します。
* AVAILABLE  アプリケーションに使用可能なレプリカの数を表示します。
* AGE  アプリケーションが実行されている時間が表示されます。


デプロイメントの実行(ロールアウト）を確認するには、`kubectl rollout status` を実行します。

~~~
$ kubectl rollout status deployment nginx-deployment
deployment "nginx-deployment" successfully rolled out
~~~


### デプロイメントの階層構造

デプロイメントの構成要素を`kubectl`コマンドで確認していきます。 NAMEフィールドに注目して追ってみてください。 デプロイメントの名前の次に、レプリカセットのID、そして次に、ポッドのIDが繋がっていることがわかります。 最下層にDockerコンテナが動作している事がわかります。


#### デプロイメント

~~~
$ kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           6m
~~~

#### レプリカセット

~~~
$ kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-569477d6d8   3         3         3         6m
~~~

#### ポッド

~~~
$ kubectl get pod
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-569477d6d8-4fcgg   1/1       Running   0          6m
nginx-deployment-569477d6d8-vk69d   1/1       Running   0          6m
nginx-deployment-569477d6d8-vnz8b   1/1       Running   0          6m
~~~

#### コンテナ（ボッド内）
Containeters を見れば、dockerコンテナを利用している事がわかります。

~~~
$ kubectl describe pod
Name:         nginx-deployment-569477d6d8-4fcgg
Namespace:    default
Node:         10.132.253.17/10.132.253.17
Start Time:   Thu, 14 Dec 2017 04:52:57 +0000
Labels:	       app=nginx
              pod-template-hash=1250338284
Status:	       Running
IP:           172.30.170.222
Controllers:  ReplicaSet/nginx-deployment-569477d6d8
Containers:
  nginx:
    Container ID:	 docker://2709b784b9fd160b658c1c7f5ec5ff79d3fb6aa739cd1922f658b77662aa2d0b
    Image:         nginx:1.7.9
    Image ID:      docker-pullable://nginx@sha256:e3456c851a152494c3e4ff5fcc26f240206abac0c9d794affb40e0714846c451
    Port:          80/TCP
    State:         Running
    Started:       Thu, 14 Dec 2017 04:52:59 +0000
    Ready:         True
    Restart Count: 0
~~~



#### ポッド・テンプレートのラベル
`--show-labels` を `kubectl get pods`に付与する事で、付与されているラベルを確認できます。

~~~
$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-569477d6d8-4fcgg   1/1       Running   0          32m       app=nginx,pod-template-hash=1250338284
nginx-deployment-569477d6d8-vk69d   1/1       Running   0          32m       app=nginx,pod-template-hash=1250338284
nginx-deployment-569477d6d8-vnz8b   1/1       Running   0          32m       app=nginx,pod-template-hash=1250338284
~~~


#### 参考情報
[The Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/)
[Labels and Selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
[Pod Templates](https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates)



## デプロイメントの更新

「k8sは、どの様にアプリの更新するのか？」と言った動作を理解する目的で、コンテナの`nginx 1.7.9`のイメージを`nginx 1.9.2'に更新して動作を確認します。

### 初期状態

初期の状態は`nginx 1.7.9`のコンテナで、レプリカ・セットのハッシュ 569477d6d8 でサービスが提供されています。 

~~~
$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-569477d6d8-4fcgg   1/1       Running   0          32m       app=nginx,pod-template-hash=1250338284
nginx-deployment-569477d6d8-vk69d   1/1       Running   0          32m       app=nginx,pod-template-hash=1250338284
nginx-deployment-569477d6d8-vnz8b   1/1       Running   0          32m       app=nginx,pod-template-hash=1250338284
~~~

### `nginx 1.7.9`から`nginx 1.9.2`への更新
コンテナ・イメージを更新する方法は 以下の三通りあります。

#### 1.YAMLファイルを更新後に適用する

一番目の方法は、デプロイした時のYAMLを編集して、`kubectl apply -f nginx-deployment.yml` を実行します。
vimで編集して、1.7.9 を1.9.1にします。

~~~
16:     spec:
17:       containers:
18:       - name: nginx
19:         image: nginx:1.7.9  -> 1.9.1 へ変更
~~~
kubectlで更新したYAMLファイルを適用します。

~~~
$ kubectl apply -f nginx-deployment.yml
~~~

#### 2.実行中デプロイメントのYAMLを編集

二番目の方法は、`kubectl edit...`とする事で、稼働中のデプロイメントの定義をエディタで編集できます。エディタを保存終了すると、ただちにロールアウトが開始されます。

~~~
$ kubectl edit deployment nginx-deployment
deployment "nginx-deployment" edited
~~~
下記のはデフォルトのエディタvim で編集中の画面例です。

~~~
 31   template:
 32     metadata:
 33       creationTimestamp: null
 34       labels:
 35         app: nginx
 36     spec:
 37       containers:
 38       - image: nginx:1.9.1
 39         imagePullPolicy: IfNotPresent
 40         name: nginx
 41         ports:
 42         - containerPort: 80
 43           protocol: TCP
~~~


#### 3.コマンドラインで目的のコンテナを指定

三番目の方法は、コマンドラインから、目的のバージョンに変更します。

~~~
$ kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated
~~~

### 実行結果の確認
上記のいずれの方法でも、次のコマンドで全体の動作について確認できます。

~~~
$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
~~~
ロールアウトが完了した後に、ポッドのレベルで確認すると、レプリカ・セット、ポッドのハッシュ値が全て変更されており、デプロイメントの中身が全て入れ替わった事が判ります。

~~~
$ kubectl get pods --show-labels
NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-6bd4859cdb-2kpnl   1/1       Running   0          27s       app=nginx,pod-template-hash=2680415786
nginx-deployment-6bd4859cdb-mdx4q   1/1       Running   0          32s       app=nginx,pod-template-hash=2680415786
nginx-deployment-6bd4859cdb-mg5f5   1/1       Running   0          45s       app=nginx,pod-template-hash=2680415786
~~~



### ロールアウト中の動作

現行でサービスしているサービスの新バージョンをリリースする際のk8sの動きを確認してみます。
具体的には、以下の様にイメージのweb:v1からweb:v2へバージョンをアップします。

~~~file:nginx-web1.yml
    14	    spec:
    15	      containers:
    16	      - name: nginx
    17	        image: registry.ng.bluemix.net/takara/web:v1
~~~    

~~~file:nginx-web2.yml
    14	    spec:
    15	      containers:
    16	      - name: nginx
    17	        image: registry.ng.bluemix.net/takara/web:v2
~~~    
    
    
#### 初期状態
３個のポッドを持つweb:v1のデプロイメントが稼働しています。 この状態からweb:v2へロールオーバーします。

~~~
$ kubectl get deploy
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-web-deployment   3         3         3            3           10m

$ kubectl get pods
NAME                                    READY     STATUS    RESTARTS   AGE
nginx-web-deployment-765656d945-7fkk7   1/1       Running   0          10m
nginx-web-deployment-765656d945-fjt7w   1/1       Running   0          10m
nginx-web-deployment-765656d945-g6zbd   1/1       Running   0          10m
~~~

#### Web:v2へ更新の開始
web:v2のイメージを記述したnginx-web2.ymlを適用して、ロールオーバーを開始します。

~~~
$ kubectl apply -f nginx-web2.yml
deployment "nginx-web-deployment" configured
~~~


#### Web:v1からWeb:v2へ切替り進行中
web:v1のレプリカ・セットは 765656d945 で、二つが稼働を続けています。
web:v2のレプリカ・セットが b6fd9b64b として作られています。

~~~
$ kubectl get pods
NAME                                    READY     STATUS              RESTARTS   AGE
nginx-web-deployment-765656d945-7fkk7   1/1       Terminating         0          11m
nginx-web-deployment-765656d945-fjt7w   1/1       Running             0          11m
nginx-web-deployment-765656d945-g6zbd   1/1       Running             0          11m
nginx-web-deployment-b6fd9b64b-kf2hp    0/1       ContainerCreating   0          3s
nginx-web-deployment-b6fd9b64b-x7ckc    0/1       ContainerCreating   0          3s
~~~
web:v2のレプリカ・セットの２ポッドが稼働した処で、アプリのバージョンアップは完了です。そして、web:v1が全数終了となります。

~~~
$ kubectl get pods
NAME                                    READY     STATUS              RESTARTS   AGE
nginx-web-deployment-765656d945-7fkk7   0/1       Terminating         0          11m
nginx-web-deployment-765656d945-fjt7w   0/1       Terminating         0          11m
nginx-web-deployment-765656d945-g6zbd   0/1       Terminating         0          11m
nginx-web-deployment-b6fd9b64b-kf2hp    1/1       Running             0          5s
nginx-web-deployment-b6fd9b64b-wp995    0/1       ContainerCreating   0          2s
nginx-web-deployment-b6fd9b64b-x7ckc    1/1       Running             0          5s
~~~
web:v2の３個目が起動しました。

~~~
$ kubectl get pods
NAME                                    READY     STATUS        RESTARTS   AGE
nginx-web-deployment-765656d945-7fkk7   0/1       Terminating   0          11m
nginx-web-deployment-765656d945-fjt7w   0/1       Terminating   0          11m
nginx-web-deployment-765656d945-g6zbd   0/1       Terminating   0          11m
nginx-web-deployment-b6fd9b64b-kf2hp    1/1       Running       0          9s
nginx-web-deployment-b6fd9b64b-wp995    1/1       Running       0          6s
nginx-web-deployment-b6fd9b64b-x7ckc    1/1       Running       0          9s
~~~
web:v1のクリーンナップ進行中

~~~
$ kubectl get pods
NAME                                    READY     STATUS        RESTARTS   AGE
nginx-web-deployment-765656d945-fjt7w   0/1       Terminating   0          11m
nginx-web-deployment-b6fd9b64b-kf2hp    1/1       Running       0          15s
nginx-web-deployment-b6fd9b64b-wp995    1/1       Running       0          12s
nginx-web-deployment-b6fd9b64b-x7ckc    1/1       Running       0          15s
~~~
切り替えのプロセスは完了です。

~~~
$ kubectl get pods
NAME                                   READY     STATUS    RESTARTS   AGE
nginx-web-deployment-b6fd9b64b-kf2hp   1/1       Running   0          18s
nginx-web-deployment-b6fd9b64b-wp995   1/1       Running   0          15s
nginx-web-deployment-b6fd9b64b-x7ckc   1/1       Running   0          18s
~~~


参考資料
[Perform Rolling Update Using a Replication Controller](https://kubernetes.io/docs/tasks/run-application/rolling-update-replication-controller/)



#### ストラテジー

前述の過渡期状態での web:v1 のポッドの減少とweb:v2のポッドの増加は、RollingUpdateStrategy で定められた値にもとずいて制御されています。

~~~
$ kubectl describe deploy
Name:			nginx-web-deployment
Namespace:		default
CreationTimestamp:	Thu, 14 Dec 2017 11:00:49 +0000
Labels:			app=nginx
Selector:		app=nginx
Replicas:		3 updated | 3 total | 3 available | 0 unavailable
StrategyType:		RollingUpdate
MinReadySeconds:	0
RollingUpdateStrategy:	1 max unavailable, 1 max surge
Conditions:
  Type		Status	Reason
  ----		------	------
  Available 	True	MinimumReplicasAvailable
OldReplicaSets:	<none>
NewReplicaSet:	nginx-web-deployment-b6fd9b64b (3/3 replicas created)
<以下省略>
~~~

移行中のポッドの数の制御は、`RollingUpdateStrategy` に指定してあり、`1 max unavailable`によって、レプリカのポッドの内で停止状態のポッドは１個に制限されています。そして、最大１個のサージ`1 max surge`が指定されており、上記の例では判りませんが、ポッドの同時稼働数の超過数は、最大１個に制限されています。

上記のデプロイメントを詳しく見ると、最初に新しいポッドを作成した後、古いポッドをいくつか削除して新しいポッドを作成したことがわかります。十分な数の新しいポッドが登場するまで古いポッドを止めることはありません。十分な数の古いポッドが止められるまで、新しいポッドは作成されません。利用可能なポッドの数が2以上で、ポッドの総数が最大で4であることを確認します。

以下は、`kubectl describe deploy`のEventsパートを理解に必要な部分だけに編集したものです。このイベントの記録では、最初に新しいポッド１個を開始しています。ここで`Web:v1`の3個の稼働中ポッドと、最初に起動した`Web:v2`ポッドの合わせて４個が稼働しており、サージ数の最大１個を守っている事が判ります。

~~~file:つづき
Events:
From	                  Reason			    Message
-----	                  ------            -------
{deployment-controller } ScalingReplicaSet	Scaled up replica set nginx-web-deployment-b6fd9b64b to 1
{deployment-controller }	ScalingReplicaSet	Scaled down replica set nginx-web-deployment-765656d945 to 2
{deployment-controller }	ScalingReplicaSet	Scaled up replica set nginx-web-deployment-b6fd9b64b to 2
{deployment-controller }	ScalingReplicaSet	Scaled down replica set nginx-web-deployment-765656d945 to 1
{deployment-controller } ScalingReplicaSet	Scaled up replica set nginx-web-deployment-b6fd9b64b to 3
{deployment-controller } ScalingReplicaSet	Scaled down replica set nginx-web-deployment-765656d945 to 0
~~~  

## `kubectl rolling update` とデプロイメントの比較

`kubectl rolling update`は、Podと `ReplicationControllers`で、同様の更新を実行します。 しかし、デプロイメントは、サーバーサイドで宣言的であり、ローリング・アップデートが完了した後でも以前のリビジョンにロールバックするなどの優れた機能がありますので、デプロイメントを利用するのが良いと思います。


## ロールオーバー

ロールオーバーとは、サービス提供中のバージョンを次のバージョンへの切り替えるロールアウト推進中に、さらに被せる様に、その次のリリース要求する、複数の更新を要求するケースを指しています。　スマートフォンのアプリでは同時接続のクライアント数が多いため、アプリサーバー数が100台くらい存在するケースがあります。 そうすると、新バージョンへのリリースで、稼働中のセッションを維持しながら移行を想定すると、それなりの時間を要します。新バージョンのリリースを進めている最中に、問題修正版のリリースが必要となり、実行に移した場合に、Kubernetesは、素晴らしい働きをしてくれます。  その動作を実際に実験した結果で見て行きます。

実験では、Web:v1、Web:v2、Web:v3 のバージョンがあります。 Web:v1を本番提供中に、Web:v2へロールアウトを進めている最中に、Web:v3をロールオーバーします。

内部の動作では、Web:v2のレプリカ・セットをポッドを増やし、Web:v1のレプリカ・セットのポッドを減らす作業の進行中に、Web:v3のレプリカ・セットを増やす動作を要求するものとなります。 この場合、デプロイメント・コントローラーは、Web:v2の移行を待たずに、Web:v1を終了させてWeb:v3へ移行を進め始めます。 既に起動を開始した Web:v2のポッドを終了させて行きます。 それでは、実際の動作を確認します。

#### 初期状態 Web:v1
初期の状態では、デプロイメントのレプリカセットの全てのポッドがWeb:v1の状態です。 ここで、Web:v1のレプリカセットのハッシュ値 765656d945 として識別する事ができます。

~~~
NAME                                    READY     STATUS    RESTARTS   AGE
nginx-web-deployment-765656d945-9g8ks   1/1       Running   0          14s
nginx-web-deployment-765656d945-b44mt   1/1       Running   0          14s
nginx-web-deployment-765656d945-j9bft   1/1       Running   0          14s
nginx-web-deployment-765656d945-jvccm   1/1       Running   0          14s
nginx-web-deployment-765656d945-p69qx   1/1       Running   0          14s
nginx-web-deployment-765656d945-p6v6d   1/1       Running   0          14s
nginx-web-deployment-765656d945-p8l7l   1/1       Running   0          14s
nginx-web-deployment-765656d945-qm4s7   1/1       Running   0          14s
nginx-web-deployment-765656d945-s2ljf   1/1       Running   0          14s
nginx-web-deployment-765656d945-t742l   1/1       Running   0          14s
~~~


#### ロールアウト中 Web:v1 -> Web:v2
以下は、Web:v2のロールアウトを要求した直後の状態です。 Web:v2のハッシュ値  b6fd9b64b のポッドが増加して、Web:v1のハッシュ値 765656d945のポッドが減っているのが見て判ります。何もなければ、全数がWeb:v2へ切り替わるはずです。

~~~
NAME                                    READY     STATUS        RESTARTS   AGE
nginx-web-deployment-765656d945-9g8ks   1/1       Terminating   0          19s
nginx-web-deployment-765656d945-b44mt   1/1       Running       0          19s
nginx-web-deployment-765656d945-j9bft   0/1       Terminating   0          19s
nginx-web-deployment-765656d945-jvccm   1/1       Running       0          19s
nginx-web-deployment-765656d945-p69qx   1/1       Terminating   0          19s
nginx-web-deployment-765656d945-p6v6d   1/1       Running       0          19s
nginx-web-deployment-765656d945-p8l7l   1/1       Terminating   0          19s
nginx-web-deployment-765656d945-qm4s7   0/1       Terminating   0          19s
nginx-web-deployment-765656d945-s2ljf   1/1       Running       0          19s
nginx-web-deployment-765656d945-t742l   1/1       Running       0          19s
nginx-web-deployment-b6fd9b64b-7qqg7    0/1       Pending       0          0s
nginx-web-deployment-b6fd9b64b-pgj4s    0/1       Pending       0          0s
nginx-web-deployment-b6fd9b64b-rccl5    1/1       Running       0          3s
nginx-web-deployment-b6fd9b64b-rmfwk    1/1       Running       0          3s
nginx-web-deployment-b6fd9b64b-tz2tv    1/1       Running       0          5s
nginx-web-deployment-b6fd9b64b-wht4l    1/1       Running       0          5s
~~~


#### ロールオーバー要求 (Web:v3のロールアウトを投入)
ここでv2に問題が発見され、一瞬のうちに対策版コンテナのv3のビルドが仕上がって、Web:v3のロールアウトを要求した。というちょっと有り得ないかもしれない様なストーリが展開したとして、Web:v3 のレプリカ・セット(758c4bbc7f)のロールオーバーの開始直後の状況が以下になります。
Web:v1、Web:v2、Web:v3の三世代が同時に存在しています。Running中のポッドが９個で、停止数が１個、RunningとContainerCreatingのポッドの総数は11個で、サージ数は１個となっており、RollingUpdateStrategyの規定数を維持しながら、ロールオーバーが進んでいます。Web:v2 b6fd9b64b も既に終了が始まっています。

~~~
NAME                                    READY     STATUS              RESTARTS   AGE
nginx-web-deployment-758c4bbc7f-m9vqs   0/1       ContainerCreating   0          0s
nginx-web-deployment-758c4bbc7f-zg6qh   0/1       ContainerCreating   0          0s
nginx-web-deployment-765656d945-9g8ks   1/1       Terminating         0          23s
nginx-web-deployment-765656d945-b44mt   1/1       Running             0          23s
nginx-web-deployment-765656d945-j9bft   0/1       Terminating         0          23s
nginx-web-deployment-765656d945-jvccm   1/1       Running             0          23s
nginx-web-deployment-765656d945-p69qx   1/1       Terminating         0          23s
nginx-web-deployment-765656d945-p6v6d   1/1       Running             0          23s
nginx-web-deployment-765656d945-p8l7l   1/1       Terminating         0          23s
nginx-web-deployment-765656d945-qm4s7   0/1       Terminating         0          23s
nginx-web-deployment-765656d945-s2ljf   1/1       Running             0          23s
nginx-web-deployment-765656d945-t742l   1/1       Running             0          23s
nginx-web-deployment-b6fd9b64b-7qqg7    0/1       Terminating         0          4s
nginx-web-deployment-b6fd9b64b-pgj4s    0/1       Terminating         0          4s
nginx-web-deployment-b6fd9b64b-rccl5    1/1       Running             0          7s
nginx-web-deployment-b6fd9b64b-rmfwk    1/1       Running             0          7s
nginx-web-deployment-b6fd9b64b-tz2tv    1/1       Running             0          9s
nginx-web-deployment-b6fd9b64b-wht4l    1/1       Running             0          9s
~~~

#### ロールオーバー中 Web:v3 
Web:v2は全て終了して、クリーンナップが進んでいます。 一番多く稼働しているボッドは、Web:v3のものとなっています。

~~~
NAME                                    READY     STATUS              RESTARTS   AGE
nginx-web-deployment-758c4bbc7f-8zps4   1/1       Running             0          10s
nginx-web-deployment-758c4bbc7f-d4ln5   1/1       Running             0          21s
nginx-web-deployment-758c4bbc7f-m9vqs   1/1       Running             0          23s
nginx-web-deployment-758c4bbc7f-nspbm   1/1       Running             0          9s
nginx-web-deployment-758c4bbc7f-rc2v6   1/1       Running             0          8s
nginx-web-deployment-758c4bbc7f-rzwv5   1/1       Running             0          21s
nginx-web-deployment-758c4bbc7f-sff26   0/1       ContainerCreating   0          6s
nginx-web-deployment-758c4bbc7f-snb6w   1/1       Running             0          17s
nginx-web-deployment-758c4bbc7f-vw5nb   1/1       Running             0          11s
nginx-web-deployment-758c4bbc7f-zg6qh   1/1       Running             0          23s
nginx-web-deployment-765656d945-s2ljf   0/1       Terminating         0          46s
nginx-web-deployment-b6fd9b64b-rccl5    0/1       Terminating         0          30s
nginx-web-deployment-b6fd9b64b-tz2tv    0/1       Terminating         0          32s
nginx-web-deployment-b6fd9b64b-wht4l    1/1       Terminating         0          32s
~~~

#### ロールオーバー完了 Web:v3 
Web:v3のロールアウトが完了した状態です。 

~~~
NAME                                    READY     STATUS    RESTARTS   AGE
nginx-web-deployment-758c4bbc7f-8zps4   1/1       Running   0          21s
nginx-web-deployment-758c4bbc7f-d4ln5   1/1       Running   0          32s
nginx-web-deployment-758c4bbc7f-m9vqs   1/1       Running   0          34s
nginx-web-deployment-758c4bbc7f-nspbm   1/1       Running   0          20s
nginx-web-deployment-758c4bbc7f-rc2v6   1/1       Running   0          19s
nginx-web-deployment-758c4bbc7f-rzwv5   1/1       Running   0          32s
nginx-web-deployment-758c4bbc7f-sff26   1/1       Running   0          17s
nginx-web-deployment-758c4bbc7f-snb6w   1/1       Running   0          28s
nginx-web-deployment-758c4bbc7f-vw5nb   1/1       Running   0          22s
nginx-web-deployment-758c4bbc7f-zg6qh   1/1       Running   0          34s
~~~

レプリカ・セットを表示すると、クリーンナップされたもの含めて、全体を把握する事ができます。

~~~
$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-758c4bbc7f   10        10        10        1h
nginx-web-deployment-765656d945   0         0         0         1h
nginx-web-deployment-b6fd9b64b    0         0         0         1h
~~~

この機能はk8sの有用性を最も顕著に示すものと思います。大規模なシステムでアプリのリリースで苦労した経験を持った者であれば、この機能の素晴らしさが理解できると思います。 しかし、ロードバランサーとの関係やコンテナ内のアプリ設計に注意を払う必要がありますので、後述したいと思います。


#### ローリング・アップデートの調整機能

RollingUpdate Deploymentsは、複数のバージョンのアプリケーションを同時に実行することをサポートします。 あなたまたはオートスケーラがロールアウト中（進行中または一時停止中）にあるRollingUpdate Deploymentの規模を調整すると、Deployment Controllerは既存のアクティブなReplicaSets（ReplicaSets with Pods）の追加のバランスを取ってリスクを軽減します。 
たとえば、replicas = 10、maxSurge = 3、および maxUnavailable = 2 を使用してデプロイメントを実行しているとします。 

~~~file:nginx-web6.yml
  1 apiVersion: extensions/v1beta1
  2 kind: Deployment
  3 metadata:
  4   name: nginx-web-deployment
  5 spec:
  6   replicas: 10
  7   selector:
  8     matchLabels:
  9       app: nginx
 10   strategy:
 11     rollingUpdate:          <--- ここからの条件に注意 
 12       maxSurge: 3
 13       maxUnavailable: 2
 14     type: RollingUpdate
 15   template:
 16     metadata:
 17       labels:
 18         app: nginx
 19     spec:
 20       containers:
 21       - name: nginx
 22         image: registry.ng.bluemix.net/takara/web:v1
 23         ports:
 24         - containerPort: 80
~~~
下記 `kubectl create -f nginx-web6.yml` を実行して、アプリが稼働している状態です。
 
~~~
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-6798cb747c   10        10        10        19s
~~~
ここで、Web:v2を適用します。 

~~~
$ kubectl create -f nginx-web7.yml 
deployment "nginx-web-deployment" created
~~~
maxUnavailable = 2 の条件により、 6798cb747c のレプリカ・セットは、2個減らす事ができます。 maxSurge = 3 の条件により、全体で13個までのポッドが許されているので、67bcffb8b6 の要求数(DESIRED) を 5 にします。 

~~~
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-6798cb747c   8         8         8         22s
nginx-web-deployment-67bcffb8b6   5         5         0         0s
~~~
次に、67bcffb8b6 のレプリカ・セットの数を増やし、maxUnavailable = 2 の条件をキープしながらローリング・アップデートを推進していきます。

~~~
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-6798cb747c   5         5         5         25s
nginx-web-deployment-67bcffb8b6   8         8         3         3s
~~~
6798cb747cの DESIREDを１づつ減らし、対応して 67bcffb8b6 のDESIREDを増やし、最終的にREADYが10となったら、ローリング・アップデートを完了します。 

~~~
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-6798cb747c   0         0         0         42s
nginx-web-deployment-67bcffb8b6   10        10        10        20s
~~~


## ロールバック
何らかの理由があって、デプロイしたアプリを前のバージョンに戻す（ロールバック）ケースの動作です。

### 一つ前のバージョンに戻す
前述からの続きで、以下は、Web:v3にロールアウトが完了した状態です。 これから、一つ前のWeb:v2にロールバックしてみます。

~~~
$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-758c4bbc7f   10        10        10        1h
nginx-web-deployment-765656d945   0         0         0         1h
nginx-web-deployment-b6fd9b64b    0         0         0         1h
~~~

前のバージョン Web:v2にロールバックを開始するコマンド `kubectl rollout undo deployment`を投入します。

~~~
$ kubectl rollout undo deployment nginx-web-deployment
deployment "nginx-web-deployment" rolled back
~~~
ロールバックコマンド投入直後の状態です。 Web:v3(758c4bbc7f)のポッドは減らされて、Web:v2(b6fd9b64b)のポッドが増えているのが解ります。

~~~
$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-758c4bbc7f   7         7         7         1h
nginx-web-deployment-765656d945   0         0         0         1h
nginx-web-deployment-b6fd9b64b    4         4         2         1h
~~~
少し時間が経過すると、すべてのポッドがWeb:v2(b6fd9b64b)にロールバックされました。 

~~~
$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-758c4bbc7f   0         0         0         1h
nginx-web-deployment-765656d945   0         0         0         1h
nginx-web-deployment-b6fd9b64b    10        10        10        1h
~~~

こんなに簡単にリリースしたアプリをロールバックできるk8sのデプロイメント・コントローラに感動してしまいました。


### リリース失敗のロールバック

リリースを緊急で戻したいケースがあります。 例えば、コンテナが安定せず、リスタートを繰り返す、コンテナイメージの名前のタイプミスや、レジストリへの登録ミスなどにより、ロールアウトが成功しないケースです。 この様な問題に対して、デプロイメント・コントローラの振る舞いと対処方法について確かめていきます。

次のコマンドで、Web:v4 のリリースをロールアウトします。 しかし、Web:v4 のコンテナ・イメージは、レジストリに別の名前で登録されており、コンテナをプルできないという状態に陥ります。

~~~
$ kubectl apply -f nginx-web4.yml
deployment "nginx-web-deployment" configured
~~~
次のコマンドで、ロールアウトの状態を確認します。 2個の失敗、完了待ちとのメッセージです。

~~~
$ kubectl rollout status deployment nginx-web-deployment
Waiting for rollout to finish: 2 out of 10 new replicas have been updated...
~~~
コマンドをコントロール Cで止めて、次のコマンドで、レプリカ・セットの状態を確認します。 そうすると、Web:v4(7598f5987b)のレプリカ・セットが、2個起動しており、READYになっていない状態である事が判ります。 そして、Web:v2(b6fd9b64b)は、1個減らされています。 このポッド数は、RollingUpdateStrategyのデフォルトの値によって制御されています。

~~~
$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-758c4bbc7f   0         0         0         9h
nginx-web-deployment-7598f5987b   2         2         0         3m
nginx-web-deployment-765656d945   0         0         0         9h
nginx-web-deployment-b6fd9b64b    9         9         9         9h
~~~
次にポッドの状態を確認します。  Web:v4のレプリカ・セット 7598f5987b のポッドが起動せず、`ImagePullBackOff`の状態、すなわち、レジストリからイメージをプル（ダウンロード）に失敗して、Back off つまり 中断している状態である事が判ります。

~~~
$ kubectl get pods
NAME                                    READY     STATUS             RESTARTS   AGE
nginx-web-deployment-7598f5987b-pk87t   0/1       ImagePullBackOff   0          4m
nginx-web-deployment-7598f5987b-z5t8h   0/1       ImagePullBackOff   0          4m
nginx-web-deployment-b6fd9b64b-4s2v9    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-ffmdk    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-h544r    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-hf44d    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-kp8x5    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-rzmst    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-s9swt    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-wxjf7    1/1       Running            0          7h
nginx-web-deployment-b6fd9b64b-x696f    1/1       Running            0          7h
~~~
ここで、ロールバックを実行します。 

~~~
$ kubectl rollout undo deployment nginx-web-deployment
deployment "nginx-web-deployment" rolled back
~~~
レプリカセットを確認すると、Web:v2 へロールバックされた事が読み取れます。

~~~
$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-758c4bbc7f   0         0         0         9h
nginx-web-deployment-7598f5987b   0         0         0         5m
nginx-web-deployment-765656d945   0         0         0         9h
nginx-web-deployment-b6fd9b64b    10        10        10        9h
~~~

デプロイメント・コントローラには、起動できないデプロイメントが、誤ってリリースされても、サービスの全滅を防止する機能があり、コマンド一つでロールバックする事がきます。

#### クラッシュ・ループのケース

コンテナ・イメージのプル（ダウンロード）で失敗するケースを見ましたが、コンテナが起動直後にクラッシュするケースを以下に示します。 フェール・セーフの機能が働いている事が解りますね。

~~~~
$ kubectl apply -f nginx-web5.yml 
deployment "nginx-web-deployment" configured

$ kubectl get rs
NAME                              DESIRED   CURRENT   READY     AGE
nginx-web-deployment-69c86977bf   2         2         0         5s
nginx-web-deployment-758c4bbc7f   0         0         0         11h
nginx-web-deployment-7598f5987b   0         0         0         1h
nginx-web-deployment-765656d945   0         0         0         11h
nginx-web-deployment-b6fd9b64b    9         9         9         11h

$ kubectl get pods
NAME                                    READY     STATUS             RESTARTS   AGE
nginx-web-deployment-69c86977bf-fsklh   0/1       CrashLoopBackOff   1          15s
nginx-web-deployment-69c86977bf-xhjdr   0/1       CrashLoopBackOff   1          15s
nginx-web-deployment-b6fd9b64b-4s2v9    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-ffmdk    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-h544r    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-hf44d    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-kp8x5    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-rzmst    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-s9swt    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-wxjf7    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-x696f    1/1       Running            0          9h

$ kubectl get pods
NAME                                    READY     STATUS             RESTARTS   AGE
nginx-web-deployment-69c86977bf-fsklh   0/1       CrashLoopBackOff   6          8m
nginx-web-deployment-69c86977bf-xhjdr   0/1       CrashLoopBackOff   6          8m
nginx-web-deployment-b6fd9b64b-4s2v9    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-ffmdk    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-h544r    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-hf44d    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-kp8x5    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-rzmst    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-s9swt    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-wxjf7    1/1       Running            0          9h
nginx-web-deployment-b6fd9b64b-x696f    1/1       Running            0          9h
~~~~

この状態から、ロールバックすることで、失敗したリリースを取り消す事ができます。

#### レビジョンを指定してのロールバック

`kubectl apply -f nginx-web2.yml --record`の様にコマンドのフラグとして`--record`を追加することで、過去にリリースしたレビジョン（改定）の履歴を記録してくれます。 このレビジョンを指定してロールバックもできます。

~~~
$ kubectl rollout history deployment nginx-web-deployment
deployments "nginx-web-deployment"
REVISION	CHANGE-CAUSE
1		kubectl create -f nginx-web1.yml --record
2		kubectl apply -f nginx-web2.yml --record
4		kubectl apply -f nginx-web4.yml --record
5		kubectl apply -f nginx-web3.yml --record
~~~
例として、２番目のレビジョンに戻すには、`--to-revision=2`を付与します。 

~~~
$ kubectl rollout undo deployment nginx-web-deployment --to-revision=2
~~~

その２へ続きます。



# 参考資料
* Kubernetes Reference Documentation Deployment https://kubernetes-v1-4.github.io/docs/user-guide/deployments/
* Kubernetes Documentation CONCEPTS Deployment https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
* Kubernetes: Deployment の仕組み https://qiita.com/tkusumi/items/01cd18c59b742eebdc6a