EKSのKubernetesバージョンを1.15>1.19へアップデート

## 目的
サービス基盤として使用しているAmazon EKS の「kubernetesバージョン1.15」のサポート終了が2021 年5月3日と近いため作業時の最新リリースバージョン1.19にアップデートする
[Amazon EKS Kubernetes バージョン](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/kubernetes-versions.html)

## 前提条件
* 既存のkubernetesバージョンは1.15
    * EKSのリリースのタイミング上、検証時はKubernetes1.18,切替時はKubernetes1.19を使用
* 環境はterraform（基本最新版 執筆時v0.14.6）で管理している
    * 旧環境はeksctlで作成している（この辺の置き換えは[eksctlで作成していたEKSリソースをterraformに置き換える](https://qiita.com/myu_mx/items/1b37b5302e39ffb80fdb)で紹介）

## 大体の流れ
Kubernetesクラスタのバージョンアップは基本的に1つ後のマイナーバージョンにしか更新できないため、1.15>1.19へのアップデートするには4回の実行が必要になる。
>Amazon EKS は可用性の高いコントロールプレーンを実行しているため、一度に更新できるのは 1 マイナーバージョンのみです。
[クラスターの更新](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/update-cluster.html)

そのため今回は新環境を別途作成しALBの向け先を切り替えることでアップデート対応を行う。

1. 検証環境にKubernetes1.18の環境を立てて各サービスが動くことを確認する
    * 外部アクセスの部分はいくつか妥協
* Kubernetes1.19で新環境を作成する
    * ようやくこの時点でEKSでKubernetes1.19が使えるようになった
    * cluster名やterraformのリソース名などは[既存環境名]+"-v2"とした
* terraformでALBの向け先変更
    * stagingはバツっと切り替える、本番は徐々に切り替える
    * e2eテストの実施
    * マネコン>ロードバランサ>ターゲットグループ>メトリクスからアクセスが変わっていることを見る
* 旧環境を削除する
* 廃止予定のAPI置き換え対応
    * 今回は対応が後手になってしまったが、1. の時点で対応できると良い

### 検証環境にKubernetes 1.18の環境を立てて各サービスが動くことを確認する
#### 環境構築
* terraformで新規ディレクトリを切りstaging環境のsubnet上に新規EKSクラスタを作成
    * ALBも新規に作成し、Route53でサブドメインを切り紐付ける（証明書問題が手っ取り早く回避できる）
    * terraform deleteで検証環境を安全に削除できる
* 動作確認のためRDBとかSQSの連携システムはそのままstaging環境のものを流用。



#### kubernetesを運用する上で使っているツール系のコンテナ
マニフェストで修正したヶ所
##### metrics-server 
metrics-serverは[v0.3.1](https://github.com/kubernetes-sigs/metrics-server/releases/tag/v0.3.1)を使用している。
メトリクスの差異などが出ると運用上影響があるので、今回アップデートせず別途対応する。
[クラスターの更新](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/update-cluster.html)より以下の廃止APIを置き換え


```metrics-server-deployment.yaml
apiVersion: extensions/v1beta1 
↓
apiVersion: apps/v1
```

##### cluster-autoscaler
[Amazon EKS で Cluster Autoscaler を設定するには、どうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-cluster-autoscaler-setup/)を再確認
commandの部分以外は以下のマニフェストに置き換え
https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
最後に[release](https://github.com/kubernetes/autoscaler/releases)から最新のイメージのバージョンを確認しを修正

```yaml
image: k8s.gcr.io/autoscaling/cluster-autoscaler:v1.19.3
```

##### dashboard
[v1.10.1](https://github.com/kubernetes/dashboard/releases/tag/v1.10.1)を使用していたが、metrics-serverとの通信がうまく行ってなかったのもあり、[チュートリアル: Kubernetes ダッシュボード (ウェブ UI) のデプロイ
](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/dashboard-tutorial.html)より [v2.0.5](https://github.com/kubernetes/dashboard/releases/tag/v2.0.5)へ更新。（最新版でも良かった気がする）
v1→v2に伴い機能が増えたらしい

#### サービス基盤系の動作確認
* サービス基盤系のコンテナはcodebuild経由でデプロイする
    * 新環境のEKSクラスタ名を指定したブランチを公開
        * 以前はkubeconfigをS3から取得していたが、[update-kubeconfig-cli-command](https://aws.amazon.com/jp/about-aws/whats-new/2018/09/amazon-eks-simplifies-cluster-setup-with-update-kubeconfig-cli-command/)対応に伴い置き換え
    * デプロイ時にそのブランチを指定することで新環境にデプロイ
* e2eテストを実施
    * ALBを新規で立てている関係でログアウト後のリダイレクト先が異なり失敗するので、ここだけは手動対応した


###  Kubernetes1.19で新環境を作成する
terraformの既存staging環境のディレクトリに新環境を作成。後続作業のために、ターゲットグループまで作成しておく。
マニフェストのデプロイは当日に行うが、向け先変更するための仕込みは行っておく。

### ALBの向け先変更
#### staging
一気に置き換えるが重み付けの機能を使い、問題があったら100:0に置き換える。完全移行後forwardは削除するので、元のコードはコメントアウト。
弊社の場合ALBは複数立てているので同時に置き換えている。

  * e2eテストの実施
  * マネコン>ロードバランサ>ターゲットグループ>メトリクスからアクセスが変わっていることを見る

```elb.yaml
resource "aws_lb_listener" "service" {
  load_balancer_arn = aws_lb.service.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn   = var.acm["arn"]
  default_action {
    target_group_arn = aws_lb_target_group.service.arn
    #target_group_arn = aws_lb_target_group.service.arn
    type             = "forward"
    forward {
      target_group {
        arn = aws_lb_target_group.service.arn
        weight = 0
      }
      target_group {
        arn = aws_lb_target_group.service_v2.arn
        weight = 100
      }
    }
  }
}
```

#### production
ALBの重み付けを使い、新環境に90:10とかで徐々に切替。
本番切替時にterraformのバグかstickinessを明示的に記述しないとapplyできなかったので入れている。

  * e2eテストの実施
  * マネコン>ロードバランサ>ターゲットグループ>メトリクスからアクセスが変わっていることを見る

```elb.yaml
resource "aws_lb_listener" "service" {
  load_balancer_arn = aws_lb.service.arn
  port              = 443
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-TLS-1-2-2017-01"
  certificate_arn   = var.acm["arn"]
  default_action {
    target_group_arn = aws_lb_target_group.service.arn
    #target_group_arn = aws_lb_target_group.service.arn
    type             = "forward"
    forward {
      target_group {
        arn = aws_lb_target_group.service.arn
        weight = 90
      }
      target_group {
        arn = aws_lb_target_group.service_v2.arn
        weight = 10
      }
      stickiness {
        enabled  = false
        duration = 1
      }
    }
  }
}
```

### 旧環境を削除する
* ALBの重み付けのルール削除&ターゲットグループを削除
* eksctlで作成していたEKS Kubernetes1.15の環境を削除


### 廃止予定のAPI置き換え対応
本件は作業スケジュール上Kubernetes1.18で検証を行い、1.19はぶっつけ本番で対応した。
Kubernetes1.19で機能追加された廃止予定のAPI警告[REST resources (aka API objects) ](https://kubernetes.io/docs/reference/using-api/deprecation-policy/#rest-resources-aka-api-objects)によって、廃止予定のAPIが表面化した。スケジュールに余裕があったため後作業ではあるが警告に沿って対応を実施した。

apply時に出たエラー抜粋

```sh
Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
Warning: rbac.authorization.k8s.io/v1beta1 Role is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 Role
Warning: rbac.authorization.k8s.io/v1beta1 RoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 RoleBinding
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding
Warning: apiregistration.k8s.io/v1beta1 APIService is deprecated in v1.19+, unavailable in v1.22+; use apiregistration.k8s.io/v1 APIService
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
```

Ingressのextensions/v1beta1→networking.k8s.io/v1は記述方法が変わるので、[こちら](https://kubernetes.io/ja/docs/concepts/services-networking/ingress/#ingress%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9)から確認して置き換える。
さらにIngressに関しては置き換えても警告が出続けるバグがある模様... [Expecting apiVersion - networking.k8s.io/v1 instead of extensions/v1beta1 #94761](https://github.com/kubernetes/kubernetes/issues/94761)
Kubernetes1.20で解決しているので、1.19を使う上では諦めるしかなさそうな雰囲気。

argo-rolloutsに関しては単純にAPIの置き換え、[Issues](Release manifests with CRD's using apiextensions.k8s.io/v1 #779)が未対応だったため保留。

## 余談
GMSに入社してから初めてのタスクだったのですが、ゼロからのKubernetes / terraformの入門然り、サービス基盤の入門しかり丁度いいチュートリアルだったかなと思っています。
知識がなかったのもあり3ヶ月かけて行いましたが、次回は1ヶ月も要らなさそう。Kubernetesのバージョンアップは今後も付き合っていく必要があるので効率よく、ミスなくやっていければと思います。

[Global Mobility Serviceでは、インフラエンジニア含めITエンジニアを募集しています。](https://recruit.global-mobility-service.com/)