GKEクラスターのバージョン管理を便利にする自動アップグレードの戦略

# はじめに

GKE(Google Kubernetes Engine)には、クラスタの自動アップグレード機能があります。Kubernetesにおけるマスターのバージョン管理をGoogleに任せることができる機能で、これを使うことで自分たちでクラスターの中身を入れ替える必要がなくなるため、運用コストの削減につながります。NodeのmaxSurge/maxUnavailableと組み合わせるとほぼ全自動でクラスタのバージョン入れ替えが終わりますね！なお、この機能はデフォルトで有効化されており、無効にすることはできません。

Ref: https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-upgrades

今回は、この自動アップグレードにおいて注意すべきポイントや、どのような設定ができるかを見ていきます。

公式ドキュメントが散らばっているのとわかりにくいので、Terraformでどう設定するかも含めてまとめてみました。

## GKEのReleaase channelについて

GKEには[リリースチャンネル](https://cloud.google.com/kubernetes-engine/docs/concepts/release-channels)という概念があり、それぞれのチャンネルに応じて、別々のバージョンライフサイクルがクラスターに提供されます。

| チャンネル | おおよそのアップグレード サイクル | 対象ユーザー | 見込み |
|:--|:--|:--|:--|
| Rapid | 毎週 | 新しい機能を必要とする早期のテスターとデベロッパー。 | 認定された最新のコンポーネントで、他すべてのチャンネルより早期に提供されます。他のチャンネルよりも検証が不十分で、既知の解決策のない問題などの、未解決の問題が含まれます。本番環境のワークロードはサポートされません。GKE SLA の対象外です。 |
| Regular | 1 か月に複数回 | Stable チャンネルで未提供の機能を必要とする本番環境のユーザー | これらのバージョンは内部検証に合格しており、本番環境に対応した品質であると見なされていますが、履歴データが不十分なため安定性は保証されません。既知の問題には、通常は既知の解決策があります。 |
| Stable | 数か月に 1 回 | 安定性を何よりも優先し、頻繁なアップグレードによるリスクの大きい本番環境のユーザー | これらのバージョンは、標準チャンネルの要件すべてを満たしており、実行中のクラスタのパフォーマンスに基づいて、本番環境レベルの安定性、信頼性があるものであることが判明しています。 |

※公式ページから引用

例えば、2020年1月頭におけるバージョンはそれぞれRapidが1.16、Regularが1.14、Stableが1.13となっています。

GKEにおけるKubernetesはDigital OceanやAmazon EKSなどと違い、多くの機能がデフォルトで統合化されたものです。より新しいバージョンベースのほうが有効な機能が多い一方で、Stableチャンネルのほうが変更頻度も少なく自動アップグレードなどに悩まされる心配も少ないという側面もあります。

例えば、GKE上でGCLBのIngressを使うためのIngress Controllerには[ingress-gce](https://github.com/kubernetes/ingress-gce)がありますが、こちらはALB Ingressと異なりデフォルトでGKEのクラスターに組み込まれています。[ingress-gceのリリースノート](https://github.com/kubernetes/ingress-gce/releases)をご覧になれば分かる通り、新しいバージョンのIngress Controlelrは新しいバージョンのGKEでないと使えません。

まずは、お使いのクラスターの特性やメンテナンスコストの観点から、どのリリースチャンネルを使うべきかを見定めてみるとよさそうです。

## 自動アップグレードの適用とメンテナンス時間枠の設定

自動アップグレードは基本的にGoogleが自動的にスケジュールしたタイミングで発生します。そのため、ユーザーが手動でクラスターのバージョンを上げない限り、手元で具体的な時間を制御することはできません。

しかし、[メンテナンスの時間枠と除外](https://cloud.google.com/kubernetes-engine/docs/how-to/maintenance-windows-and-exclusions)を設定することで、一定の制限の範囲において「このタイミングでのみアップグレードを行う」という条件を設定できます。

<img width="478" alt="スクリーンショット 2020-01-06 11.33.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100275/80d08ac2-7433-19c4-52b1-9ca2f575f049.png">

クラスター作成時に実際に設定するときはこのような形で行います。

### メンテナンス時間枠における制限事項

GCPのドキュメントには以下のような記述があります。

>14日間のローリング ウィンドウ内で少なくとも24時間はメンテナンスが可能な状態にする必要があります。メンテナンスに4時間以上連続する時間を用意してください。

日本語がわかりにくいのですが、これは、要するに

- 14日間(2週間)の間に少なくとも合計で24時間のメンテナンス時間を作る
- 1回のメンテナンス時間は最低でも4時間以上確保する

という２つの条件を満たす必要があるという意味になります。

逆算すると、14日間に24時間以上なので、**仮に毎日メンテしていいがとにかく最低限の時間に抑えたい**場合は24/14で平均して1.71時間以上を設定すればよいが、1回のメンテナンス時間は4時間以上確保する必要があるのでだめ。

このバランスを取ろうと思うと、4(時間)×6(日)=24なので、**1週間のうち少なくとも3日、4時間のメンテナンス時間を設ければよい**、という計算になります。

### 結論

以上を踏まえて、うちのチームでは以下のような設定をTerraformに入れました。

```cluster.tf
resource "google_container_cluster" "cluster-name" {
  provider                 = google-beta

...

  maintenance_policy {
    recurring_window {
      start_time = "2019-12-04T05:00:00+09:00"
      end_time   = "2019-12-04T09:00:00+09:00"
      recurrence = "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH"
    }
  }

}
```

ドキュメントにもある通り、時刻と日付のフォーマットは`RFC-5545`準拠なので、このような表記になります。

月曜から木曜の朝5時から9時までメンテ時間として確保しています。

マスターのバージョンアップはワークロード(上に乗ってるアプリ)自体には原則として影響はありません。

ただ、Kuberntes APIを使うようなアプリ(例えばバッチで内部的にKubernetesのPodをプロビジョンするとか)の場合はAPI Serverの応答がなくなるので注意が必要です。