Kubernetes: Deployment のローリングアップデートの条件と設定

Kubernetes の Deployment のローリングアップデートが起こる条件と設定についてのメモです。Deployment のローリングアップデート自体の仕組みについては [Kubernetes: Deployment の仕組み](https://qiita.com/tkusumi/items/01cd18c59b742eebdc6a) にまとめています。

## Deployment のローリングアップデートが起きる条件

Deployment のローリングアップデートは Deployment の [Pod Template](https://kubernetes.io/docs/api-reference/v1.8/#podtemplatespec-v1-core) (`.spec.template`) という Pod のテンプレート情報に変更があったときにのみ行われます。例えば Deployment のアノテーション(`.metadata.annotations`) を変更してもローリングアップデートは起きませんが、Pod Template のアノテーション(`.spec.template.metadata.annotations`) を更新するとローリングアップデートが実行されます。

- https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment


```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    run: myapp
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      run: myapp
  # この部分が PodTemlate。ここに変更があったときローリングアップデートが起こる
  template:
    metadata:
      labels:
        run: myapp
    spec:
      containers:
      - image: nginx:1.13
        name: myapp
```

## 手動のローリングアップデート

Deployment の PodTemlate の値を書き換えることで Pod のイメージの変更なしにローリングアップデートを手動で行うことができます。Pod を再起動させたいときに便利です。変更は Pod Template のどの値でも良いですが、アノテーション(`.spec.template.metadata.annotations`)が一番影響が少ないと思われます。下記のように `kubectl patch` コマンドで書き換えることができます。

```bash
# dummy-for-update というアノテーションに現在時刻を書き込んでローリングアップデートさせる
$ kubectl patch deployment $DEPLOYMENT_NAME \
  -p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"dummy-for-update\":\"$(date +%s)\"}}}}}"
```

自分の所属している [Z Lab](http://zlab.co.jp/) では[force-update-deployment](https://github.com/zlabjp/kubernetes-scripts/blob/master/force-update-deployment)というスクリプトにして利用しています。

```bash
# kube-dns の kube-dns を再起動する
$ force-update-deployment kube-dns -n kube-system
deployment "kube-dns" patched
```


## ローリングアップデート時の Pod 数の増減の設定

Deployment では下記の 2つのフィールドによってローリングアップデート時の Pod 数の増減を制御できます。manifest で指定した Deployment の apiVersion によってデフォルト値が異なるため注意が必要です。`extensions/v1beta1` のデフォルトの挙動ではレプリカ数 (`.spec.replicas`) が 1 の場合 ready 状態の Pod が 0 になるタイミングがありえます。

**apiVersion に関わらずレプリカ数の値を 2 以上にしておけば多くの場合問題ないでしょう。**

- `.spec.strategy.rollingUpdate.maxUnavailable` ローリングアップデート時に許容できる最大の unavailable になる Pod 数 (削除できる Pod 数)
  - apiVersion が `extensions/v1beta1` の場合デフォルトは `1`
  - apiVersion が `apps/v1beta1`, `apps/v1beta2` の場合デフォルトは `25%`。端数は**切り捨て**
- `.spec.strategy.rollingUpdate.maxSurge` ローリングアップデート時に許容できる超過して作られる Pod の最大数
  - apiVersion が `extensions/v1beta1` の場合デフォルトは `1`
  - apiVersion が `apps/v1beta1`, `apps/v1beta2` の場合デフォルトは `25%`。端数は**切り上げ**

`apps/v1beta2` は kubernetes v1.8 からの apiVersion になります。

### apiVersion が `extensions/v1beta1` の場合

maxUnavailable, maxSurge ともに `1` がデフォルトのため、Pod を新しく作って そのready を待たず、すぐにPod を削除する挙動になります。**レプリカ数が 1 の場合は一時的にどれも ready になっていない状態がありえます。** maxUnavailable を 0 に設定することで最低 1 pod は ready な状態を保つことができます。

```bash
$ kubectl get deploy mydep-ext -w
NAME        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
mydep-ext   1         1         1         1         3m # rolling-update 前
mydep-ext   1         1         0         1         3m # 今の Pod が up-to-date でなくなる
mydep-ext   1         2         1         1         3m # 新しいバージョンの Pod が作られる
mydep-ext   1         1         1         0         3m # ready を待たず古い Pod を削除
mydep-ext   1         1         1         1         3m # 新しいバージョンが ready になる
```

### apiVersion が `apps/v1beta1` の場合

maxUnavailable, maxSurge ともに `25%` がデフォルトになっており、maxUnavailable の方は端数は切り捨てられるので、レプリカ数が 1の場合、unavailable として許容する Pod数は 0 となります。Pod を新しく作り ready になるのを待ってから Pod を削除するため、**レプリカ数が 1 であっても最低 1 Pod は ready な状態になります。**


```bash
$ kubectl get deploy mydep-app -w
NAME        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
mydep-app   1         1         1         1         37s # rolling-update 前
mydep-app   1         1         0         1         37s # 今の Pod が up-to-date でなくなる
mydep-app   1         2         1         1         37s # 新しいバージョンの Pod が作られる
mydep-app   1         2         1         2         41s # ready を待つ
mydep-app   1         1         1         1         41s # 古い Pod を削除
```