⑯１日１０分で理解するコンテナ技術入門 - Deploymentとは -

[前回](https://qiita.com/goroneko/items/ab451647b7fa5acd1b80)はReplicaSetの簡単な紹介を行いました。今回はそのReplicaSetの上位リソースである、Deploymentについて説明していきたいと思います。

# Deploymentとは
Deploymentは、アプリケーションのデプロイや管理を行うためのリソースです。Deploymentを使用すると、
- デプロイ
- アップデート
- スケール
- ロールバック

などが簡単に実行できます。また、DeploymentはReplicaSetの上位リソースなので、ReplicaSetの機能（Podのセルフヒーリングなど）も利用できます。

イメージとしては以下のような感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532202/db506c53-096e-46b4-b873-54cea296651a.png)

ですので、ReplicaSetを作成するとPodが作成されたのと同様、Deploymentを作成すると、ReplicaSetとPodが作成されます。

実際にDeploymentを作成する手順は後述するとして、Deploymentを作成するとReplicaSetとPodも作成されていることを確認します。

以下のようなDeploymentがある場合、
```bash
$ kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           13s
```
ReplicaSetの一覧を取得してみると、
```bash
$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-6d6565499c   3         3         3       5m24s
```
このようにReplicaSetも作成されています。また、Podの一覧を確認してみると、
```bash
$ kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-6d6565499c-jwfjw   1/1     Running   0          5m52s
nginx-deployment-6d6565499c-pnz6g   1/1     Running   0          5m52s
nginx-deployment-6d6565499c-vl2m2   1/1     Running   0          5m52s
```
このようにPodも作成されています。

# Deploymentを作成してみる
ここからは実際にDeploymentを作成してみます。Deploymentは`kubectl create deploy`コマンドで作成するか、マニフェストファイルから作成できます。
今回は、`kubectl create deploy`コマンドを使ってマニフェストファイルのひな型を作成し、そのマニフェストファイルをクラスターに適用する方法で作成します。

`kubectl create`コマンドは、`-o yaml`オプションをつけることでyamlファイルとして構成情報を出力できます。`--dry-run=client`オプションをつけると、リソースが作成されなくなるので、単純にマニフェストファイルを出力することができます。
```bash
$ kubectl create deploy nginx-deployment --image=nginx:1.14.2 --replicas=3 --dry-run=client -o yaml >> nginx-deployment.yaml
```
上記のコマンドを実行すると、カレントディレクトリに下記のような`nginx-deployment.yaml`が作成されます。
```yaml:nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: nginx-deployment
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-deployment
    spec:
      containers:
      - image: nginx:1.14.2
        name: nginx
        resources: {}
status: {}

```
この中で今回不要な部分を削除します（削除しなくても問題はありません）。
```yaml:修正後のnginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deployment
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
        - image: nginx:1.14.2
          name: nginx

```
このマニフェストファイルをクラスターに適用します。
```bash
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created
```
これでDeploymentの作成は完了です。

作成されたかどうかは、`kubectl get`コマンドで確認できます。
```bash
$ kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           37s
```

# Deploymentの機能を試してみる
Deploymentの機能として、
- デプロイ
- アップデート
- スケールアップ
- ロールバック

を挙げましたので、これらの機能を試してみたいと思います。

## デプロイ
これは、Deploymentを作成した段階で行われています。`--replicas`オプションに指定した数のPodがデプロイされていることが分かります。
```bash
$ kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-75c7fcd9ff-8wzxv   1/1     Running   0          24s
nginx-deployment-75c7fcd9ff-8zqnf   1/1     Running   0          24s
nginx-deployment-75c7fcd9ff-m9cqs   1/1     Running   0          24s
```

## アップデート
Deploymentは、新しいバージョンのアプリケーションをデプロイする際に、古いバージョンのPodを段階的に置き換えるローリングアップデートをサポートしています。これによって、ダウンタイムを最小限に抑えながらアップデートを行うことができます。

アップデートを行う場合、以下のような方法があります。
- `kubectl set`コマンドを使う
- マニフェストファイルを変更して、`kubectl apply`を行う

`kubectl set`コマンドでの更新は迅速に行えますが、マニフェストファイルとの差分が生じてしまい管理の手間が出てしまうため、基本的にはマニフェストファイルを用いた方法でアップデートを行います。ここでは、マニフェストファイルを編集してアップデートを行いたいと思います。

nginxのイメージを、`nginx:1.14.2`から`nginx:1.16.1`に更新します。
```yaml:アップデート検証用に変更したnginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deployment
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
        - image: nginx:1.16.1
          name: nginx
```
`kubectl apply`コマンドでマニフェストファイルを適用します。
```bash
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment configured
```
`kubectl rollout status`コマンドで、アップデートの状況を確認できます。
```bash
$ kubectl rollout status deployment/nginx-deployment
kubectl rollout status deployment/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
```
このように、順番にPodがアップデートされていることが分かります。

Deploymentは、Podが更新されている間に特定の数のPodのみが停止状態になることを保証しています（ローリングアップデート）。デフォルトでは、目標とするPod数（今回は3）のうち、少なくとも75%が稼働状況であることを保証します。

## スケールアップ
Deploymentのスケールは、以下の方法で行えます。
- `kubectl scale`コマンドを使う
- マニフェストファイルを編集する

アップデートの時と同様の理由で、マニフェストファイルを編集してスケールアップしてみます。
以下のようにマニフェストファイルを変更します。
```yaml:スケール検証用に変更したnginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deployment
  name: nginx-deployment
spec:
  replicas: 10 # replicasを3 => 10に
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
        - image: nginx:1.16.1
          name: nginx
```
クラスタに適用します。
```bash
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment configured
```
スケールされたかを確認します。
```bash
$ kubectl get deploy
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   10/10   10           10          18m
```
`READY`が`10/10`になっていることがわかります。一応、Podの方も確認してみます。
```bash
$ kubectl get po
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-577ccfb786-9ffxg   1/1     Running   0          7m30s
nginx-deployment-577ccfb786-9fkf6   1/1     Running   0          77s
nginx-deployment-577ccfb786-cbbn9   1/1     Running   0          7m24s
nginx-deployment-577ccfb786-h69jh   1/1     Running   0          77s
nginx-deployment-577ccfb786-k7tmw   1/1     Running   0          7m35s
nginx-deployment-577ccfb786-lcfk7   1/1     Running   0          77s
nginx-deployment-577ccfb786-qrjhq   1/1     Running   0          77s
nginx-deployment-577ccfb786-sm4db   1/1     Running   0          77s
nginx-deployment-577ccfb786-vt4p8   1/1     Running   0          77s
nginx-deployment-577ccfb786-wnxls   1/1     Running   0          77s
```
元々作成されていたPodの`AGE`が7分ほどであるのに対し、新たにスケールしたPodが`77s`になっていることが分かります。

## ロールバック
ロールバックは、マニフェストファイルからではなく、`kubectl`コマンドを使用して行います。これは、`kubectl rollout undo`コマンドを使って以前のリビジョンに戻すことができるためです。マニフェストファイルは、Gitなどで管理されていればそちらでリビジョンに合わせてバージョンを戻します。

以下のような手順でロールバックします。
1. 現在のリビジョンを確認
1. リビジョンを指定してロールバックを実行
1. ロールバックの状態を確認

まずは、現在のリビジョンを`kubectl rollout history`コマンドで確認します。
```bash
$ kubectl rollout history deployment.v1.apps/nginx-deployment
deployment.apps/nginx-deployment 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
```
イメージを`nginx:1.14.2`を使った時のリビジョンと、現在の`nginx:1.16.1`を使った時のリビジョンがあります。
`CHANGE-CAUSE`には、アップデート時のコメントが入ります。このコメントはマニフェストファイルの`kubernetes.io/change-cause`アノテーションからリビジョンにコピーされますが、今回は指定していないので`<none>`になっています。

リビジョンは数字が大きいほうが後の更新のものです。ですので、戻す対象のリビジョンは1になります。では、`kubectl rollout undo`コマンドを使ってリビジョン１にロールバックしてみます。
```bash
$ kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=1
deployment.apps/nginx-deployment rolled back
```
ロールバックが成功したかどうか、`kubectl get`で確認します。
```bash
$ kubectl get deploy nginx-deployment
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   10/10   10           10          32m
```
すべてのPodがREADYになっていることが確認できます。
また、イメージが`nginx:1.14.2`になっているかどうか、`kubectl describe`コマンドで確認してみます。
```bash
$ kubectl describe deploy nginx-deployment
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Fri, 28 Jun 2024 04:57:23 +0000
Labels:                 app=nginx-deployment
Annotations:            deployment.kubernetes.io/revision: 3
Selector:               app=nginx-deployment
Replicas:               10 desired | 10 updated | 10 total | 10 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=nginx-deployment
  Containers:
   nginx:
    Image:        nginx:1.14.2　<= 1.14.2になっている
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  nginx-deployment-577ccfb786 (0/0 replicas created)
NewReplicaSet:   nginx-deployment-75c7fcd9ff (10/10 replicas created)
（以降省略）
```
`nginx:1.14.2`になっていることが確認できます。

# まとめ
今回はDeploymentの概要と、作成方法、Deploymentの基本機能の確認を行いました。次回は、これらの機能（Pod, ReplicaSet, Deployment, Service）を使ってウェブサイトをインターネットに公開してみたいと思います。