Kubernetesアドオンのインストールを自動化しよう

# AWS EKS + Kubernetesアドオンのインストールを自動化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/b5a03d14-56a4-93c9-fb91-a7c31807de78.png)

## はじめに
今回はKubernetesアドオンのインストールの自動化の取り組みを共有したいと思います。

AWS EKSでしか試してはいないのですが、この基本的な仕組みは他のクラウド（Azure AKSやGCP GKEなど）でも応用はできるかなと思います。

## なぜKubernetesアドオンのインストールを自動化するの？

AWS EKSの場合、EKS構築後に必要なアドオンを追加で導入する必要があります。要件にあわせたアドオンを選べるメリットがありますが、別途導入や設定作業を行う必要になります。
また、EKSのIAM Roles for Service Accounts（IRSA)を利用し、Roleとサービスアカウントと連携する設定は、手順が複雑で設定ミスや設定漏れも起こりやすいかと思います。

私の場合、何度も繰返しEKSの構築することがあり、また誰でも同じ環境をいつでも構築できるようにしたいという思いがあって、アドオンの導入も含めたEKSのインストールの自動化に挑戦してきました。

## 何のツールを使って自動化するか？
自動で構築できるツールはたくさんありますが、私は以下の条件でツールを選びました。

#### ツールの選定条件
なるべく誰でも使いやすい、また変更しやすいなどのメンテナンス性を重視しました。

1. 利用するツールの種類をなるべく少なくして、学習コストを低くしたい
2. どのクラウドプラットフォームでも同じツールで構築できるように統一したい

#### 選定したツール
TerraformとHelmを連携させ、EKS構築だけでなくアドオンの導入までの全てを自動構築します。
helmやkubectlをshellから実行させる方法もありますが、変更時の冪等性を考慮し、この組み合わせだけで完結するように構成しました。

- Terraform
    - EKSのようなKubernetesのマネージドサービスの部分だけでなく、RDSなどの構築もできる
    - どのクラウドプラットフォームでも共通で使える
- Helm
    - 多くのアドオンのインストール定義「Chart」が用意されていて、網羅性が高い（全部あるわけではない）
    - Terraformと連携させるとEKS構築も含めて一度に全部自動構築できる

## TerraformとHelmを連携されるには
TerraformからHelmを連携させるには、TerraformのHelm Providerを利用します。

- Terraform Helm Provider  
https://registry.terraform.io/providers/hashicorp/Helm/latest/docs

#### Terraform Helm Providerの基本的な利用方法
Kubernetesアドオンを導入するTerraformの定義は以下のような感じです。
これだけ覚えればOKなので理解しやすいと思います。

```terraform
resource "Helm_release" "（Terraformの定義で管理される名前）" {
  name       = "（Helmで管理される名前）"
  repository = "（Helm リポジトリのURL）"
  chart      = "（Helm Chart名）"
  namespace  = "（導入先のNamespace）"
  version    = "（Helm Chartのバージョン）"

  # 導入パラメーター
  set {
    name  = "（設定項目名）"
    value = "（設定値）"
  }

  set {
    name  = "（設定項目名）"
    value = "（設定値）"
  }
}
```

#### Helm Providerを利用するために事前にインストールしておくモジュール
- Terraform
- Helmコマンド
- kubectlコマンド


## EKS構築＆k8sアドオン導入のサンプル定義
GitHubにサンプル一式を公開しましたので、必要であればこちらも参照してください。

- [Sample] AWS EKS & Kubernetes Addon install (using Terraform and Helm)  
https://github.com/takaf04/auto-install_aws-eks_addon_sample

このサンプルでは、VPCとEKSを構築し、その後のKubernetesアドオンの導入までを一気に実行できます。

#### Helm Providerの設定
Helm ProviderからKubernetesクラスターへ接続する設定方法はいくつかあります。Terraformで構築されたEKS構築とスムーズに連携させるには、構築されたEKSのTerraformの値を直接連携させたほうが楽です。

```terraform:providers.tf
provider "Helm" {
  kubernetes {
    host                   = data.aws_eks_cluster.cluster.endpoint
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data)
    token                  = data.aws_eks_cluster_auth.cluster.token
  }
  version = "~> 1.3.2"
}
```

### k8sアドオンの導入設定をしてみよう
ここでは、Cluster Autoscalerのアドオンを例にして説明を行います。
Cluster Autoscalerはクラウドと連携しNode数の設定値を増減させるためにIAMの権限と連携する必要があります。

#### (1) Helm Chartを探してみよう
1. Helm ChartのHubページへいく
Artifact Hub 
https://artifacthub.io/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/889b25bb-8a88-c2e4-aaad-6a537c105312.png)

2. HubでChartを検索してみる
「cluster autoscaler」で検索してみると、該当するChartが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/8fee5042-9062-b087-4d3b-e099adce2fc0.png)

3. 利用するChartを選ぶ
今回はORGがKubernetesのChartを選びました。

- cluster-autoscaler  
https://artifacthub.io/packages/helm/cluster-autoscaler/cluster-autoscaler

導入方法や設定パタメータは詳細情報のページに記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/6e9af854-9f3e-89d4-250f-aad94fd574b6.png)

#### (2) IAM Roles for Service Accounts（IRSA)の設定
Cluster Autoscalerのアドオンを実行するには、IAM Roleとの連携が必要です。
（IAM Roleとの連携が不要なアドオンの場合は、この手順は不要です。）

この後の説明するためにIRSAの仕組みをおさらいします。

EKS（Kubernetes）からクラウドのリソースを操作するには、クラウドのリソースを操作する権限を与える必要があります。EC2（Node）に権限を与えても動作できますが、その場合EC2（Node）上で稼働するすべてのPodにクラウドリソースを操作する権限を与えることになり、セキュリティ上好ましくありません。
そこで、IAM RoleとKubernetesのサービスアカウントを連携させて、そのサービスアカウントが指定されたPodのみにその権限を与えるようにします。

ざっくりとイメージにすると以下のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/eed2a6bb-db85-956f-e43a-ed872ec50ad5.png)

IAM Roleとサービスアカウントを連携するには、IAM Roleとアドオンが利用するサービスアカウントの設定値を合わせる必要があります。

#### IRSA構築の設定
IRSA用のIAM Role構築の定義を簡単に記述する為に、Terraform Moduleとして提供されているものを使用しました。 

- Terraform iam-assumable-role-with-oidc  
https://registry.terraform.io/modules/terraform-aws-modules/iam/aws/latest/submodules/iam-assumable-role-with-oidc

oidc_fully_qualified_subjectsには、アドオンが使用するサービスアカウントの名前を記述します。
この名称とアドオンが使用するサービスアカウント名は一致させなくてはいけません。

```terraform:k8saddon_aws_iam_sa_role
module "iam_role_sa_cluster_autoscaler" {
  source                        = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"
  version                       = "~> v2.14.0"
  create_role                   = true
  role_name                     = "${module.eks.cluster_id}_cluster-autoscaler"
  provider_url                  = replace(module.eks.cluster_oidc_issuer_url, "https://", "")
  role_policy_arns              = [aws_iam_policy.cluster_autoscaler.arn]
  oidc_fully_qualified_subjects = ["system:serviceaccount:kube-system:cluster-autoscaler-aws-cluster-autoscaler"]
}

resource "aws_iam_policy" "cluster_autoscaler" {
  name_prefix = "${module.eks.cluster_id}_cluster-autoscaler"
  description = "EKS cluster-autoscaler policy for cluster ${module.eks.cluster_id}"
  policy      = file("./policy_json/cluster_autoscaler.json")
}
```

IAM Roleのポリシーは、Terraform内のjsonファイルに記述して、aws_iam_policyリソースの定義内で読み込ませてに構築を行います。アドオンのバージョンを変更すると、ポリシーの内容も変更になる可能性があるので合わせて変更するよう気をつけておきます。

```json:cluster_autoscaler.json
{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "autoscaling:DescribeAutoScalingGroups",
          "autoscaling:DescribeAutoScalingInstances",
          "autoscaling:DescribeLaunchConfigurations",
          "autoscaling:DescribeTags",
          "autoscaling:SetDesiredCapacity",
          "autoscaling:TerminateInstanceInAutoScalingGroup"
        ],
        "Resource": "*"
      }
    ]
}
```


### (３) Helmの記述方法

#### Helm Chartのインストール

まず最初にHelm Chartを管理しているリポジトリとChartの情報を取得します。
右上の「INSTALL」のボタンを押すと情報が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/531d7722-d840-90ce-b1b3-c522f058a25d.png)

表示されたインストールコマンドに含まれるリポジトリのURLとChartの名称を取得します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/92bccdbb-50a4-bcd1-674b-e1bebe76a4fc.png)

Terraform側の定義に反映した完成イメージは以下のようになります。

```terraform:k8saddon_Helm.tf
resource "helm_release" "cluster-autoscaler" {
  name       = "cluster-autoscaler"
  repository = "https://kubernetes.github.io/autoscaler"
  chart      = "cluster-autoscaler"
  namespace  = "kube-system"
  version    = var.param.k8saddon.cluster_autoscaler.version
  set {
    name  = "autoDiscovery.clusterName"
    value = data.aws_eks_cluster.cluster.id
  }
  set {
    name  = "awsRegion"
    value = var.param.region
  }
  set {
    name  = "rbac.serviceAccount.annotations.eks\\.amazonaws\\.com/role-arn"
    value = module.iam_role_sa_cluster_autoscaler.this_iam_role_arn
  }
}
```

setには、アドオンの導入に必要な設定パラメータをセットします。アドオンの説明をみて項目名と値をセットします。

#### IRSA用の設定追加
また、IRSAと連携させるためには、サービスアカウントのAnnotationへIAM RoleのARNを登録するようにsetの設定を追加する必要があります。

IRSAと連携するサービスアカウントのAnnotationには以下のようにセットします。

| Key | Value |
|:-----------|:------------|
| eks.amazonaws.com/role-arn | arn:aws:iam::(アカウントID):role/(Role名) |

アドオンの詳細説明にどのようにセットすればよいかが明記されています。
setに記述するnameの表記はアドオンによって異なっていますが、name名や説明をみればおおよそ判断できるとおもいます。

- Cluster-AutoscalerのValues一覧より抜粋  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/38f1ba31-d9f8-d556-586a-74564c1b97a4.png)

Terraformへの記述方法は以下になります。
valueがObject型になっていますが、以下のようにnameにAnnotationのKeyを連結させた文字列にすればOKです。（Object型の正式な記述方法があると思うのですが、わからなかったのでこの方法で対応しました。）

```terraform:記述例
  set {
    name  = "rbac.serviceAccountAnnotations.eks\\.amazonaws\\.com/role-arn"
    value = module.iam_role_sa_cluster_autoscaler.this_iam_role_arn
  }
```


## 実行してみよう
アドオンの定義をふくめて記述ができたら、実行してみます。
実行終了するまで約１５分ほど時間がかかりますが、無事AWS EKSからアドオンの導入までを自動で構築できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/f3302f5b-3e62-1d61-1dd1-d6bf53a993c5.png)

#### IRSAの設定ができているかの確認方法
IAM RoleとサービスアカウントのIRSA連携部分では、それぞれが同じ値になっているかを確認してみましょう。

- IAM Role側
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/c9027ae4-0b8d-6e5c-823e-ef917b0c08a8.png)

- サービスアカウント側
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/8fbbf22b-5232-369b-2393-f384cbfb4d13.png)


## まとめ
このような方法で、Kubernetesのアドオンのインストールまでを自動化することができ、いつでもすぐに同じEKSの環境を構築できるようになりました。


Helmをつかったアドオンの自動化でいろいろハマったことや注意事項があったので、最後に共有します。

### Helmを使った自動化の課題点
- すべてのアドオンのChartがあるわけではないので、Chartがないアドオンは今まで通り手動での導入が必要になる。
- 2020年11月にHelmのStableリポジトリが廃止になったため、どのChartが安心して使える”鉄板”なのかがわかりにくくなってしまった。
- Terraform実行中に「Helmリポジトリを更新しなさい」というエラーが発生することがあり、発生した場合はhelm repo updateを実行してTerraformの再実行が必要になる。

以上になります。
説明不足の点があるかもしれませんが、もし同じようなことを考えている方の参考になれば幸いです。
ありがとうございました。