EKSでMattermostを構築した話：3-2. EKSクラスターでアプリケーションを稼働させる前にセットアップしておくべきツール編

## 今回、作成するもの
- 今回はEKSクラスターにMattermostをデプロイする前に、設定しておくと便利なものの残りを対応していきたいと思います。
    - external dns
    - Metrics Server
    - Amazon EFS CSI ドライバー

### 復習
- `external dns`
    - ロードバランサーのDNS名を、指定したドメイン名に自動で割り振ってくれる機能
- `Metrics Server`
    - 負荷が高まった時にコンテナの数をオートスケールしてくれる機能
- `Amazon EFS CSI ドライバー`
    - Mattermostで利用されるデータを共有ストレージに保管する機能
    
## external dnsの設定
- ではexternal dnsの設定をしていきます。まずRoute53を操作するためのPolicyを作成します。
    - 以下のjsonを用意してCLIで実行していきます。
    - iam-policy-external-dns.json
        - 参考：[Amazon EKSでALB Ingress Controllerを利用する - goodbyegangsterのブログ](https://goodbyegangster.hatenablog.com/entry/2020/02/02/234204)
    
```json:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
```

- 実行

```:shell
$ aws iam create-policy \
--policy-name ExternalDNSIAMPolicy \
--policy-document file://iam-policy-external-dns.json 
```

- 次に上記で作成したIAMポリシーを紐付けるサービスアカウントを作成します。
    - 以下のyamlを用意して実行していきます。
    - sa-external-dns.yaml

```:yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: ver-Mattermost-eks
  region: ap-northeast-1

iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: external-dns
        namespace: kube-system
        labels:
          app.kubernetes.io/name: external-dns
      attachPolicyARNs:
        - "arn:aws:iam::394923402640:policy/ExternalDNSIAMPolicy"
```

- 上記yamlを適用

```:yaml
eksctl create iamserviceaccount -f sa-external-dns.yml --approve 

$ eksctl create iamserviceaccount -f sa-external-dns.yaml --approve
[ℹ]  eksctl version 0.29.1
[ℹ]  using region ap-northeast-1
[ℹ]  2 existing iamserviceaccount(s) (kube-system/alb-ingress-controller,kube-system/aws-node) will be excluded
[ℹ]  1 iamserviceaccount (kube-system/external-dns) was included (based on the include/exclude rules)
[ℹ]  1 iamserviceaccount (kube-system/aws-node) was excluded (based on the include/exclude rules)
[!]  serviceaccounts that exists in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
[ℹ]  1 task: { 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/external-dns", create serviceaccount "kube-system/external-dns" } }
[ℹ]  building iamserviceaccount stack "eksctl-ver-Mattermost-eks-addon-iamserviceaccount-kube-system-external-dns"
[ℹ]  deploying stack "eksctl-ver-Mattermost-eks-addon-iamserviceaccount-kube-system-external-dns"
[ℹ]  created serviceaccount "kube-system/external-dns"

```

- サービスアカウントができたことを確認します

```:shell
$ kubectl get sa -n kube-system | grep external
external-dns                         1         46s
```


- サービスアカウントとクラスターロールを繋げていきます。Rolebindingと呼ぶみたいです。
    - 参考：[KubernetesのRBACについて - Qiita](https://qiita.com/sheepland/items/67a5bb9b19d8686f389d)
        - この辺りもっと勉強しないと、、、違いや役割がまだ理解できていません
    - 以下のyamlを実行していきます。
        - clusterrole-external-dns.yaml

```yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
  labels:
    app.kubernetes.io/name: external-dns
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns
  labels:
    app.kubernetes.io/name: external-dns
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
  - kind: ServiceAccount
    name: external-dns
    namespace: kube-system
```

- 実はここのClusterRoleのrulesではまりました、、、参考にした記事の通りのrulesだと権限が足りないようでエラーになり続けたのです。
    - [external-dns/aws.md at master · kubernetes-sigs/external-dns](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md)の記述内容を参考にしたらうまくいきました！


- 上記のyamlを適用していきます。

```
$ kubectl apply -f clusterrole-external-dns.yaml
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns created
```

- ExternalDNSの処理を実行してくれるpodを作成してデプロイしていきます。
    - deploy-external-dns.yaml

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: kube-system
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.opensource.zalan.do/teapot/external-dns:latest
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=xxxx.xxxxx.co.jp # ここに自動で作成してほしいFQDNを入力します
        - --provider=aws
        - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
        - --registry=txt
        - --txt-owner-id=xxxxxxxxxxxxxxxxxxx # ここに適用先のドメインのowner-idを入力
      securityContext:
        fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files
```

- 適用します。

```
$ kubectl apply -f deploy-external-dns.yaml
deployment.apps/external-dns created
```

- 確認します。

```
$ kubectl get pod -n kube-system
NAME                                     READY   STATUS    RESTARTS   AGE
alb-ingress-controller-869744b58-s6blb   1/1     Running   0          23h
```
- 正常にPodが動いていることが確認できました。


## metrics serverの設定
- 次はmetrics serverです。これを利用する目的はpodの負荷を検知してオートスケール(Horizontal Pod Autoscaler)をするために、podの負荷を確認しないといけないのですが、その`podの負荷を確認`するために必要なのがmetrics serverになります。
- なので、metrics serverをデプロイする前は`kubectl top node`や`kubectl top pod`といったコマンドが実行できません。
    - 参考：[EKSでHorizontal Pod Autoscalerを試したメモ - goodbyegangsterのブログ](https://goodbyegangster.hatenablog.com/entry/2020/02/29/224322)

```:shell
$ kubectl top node
Error from server (NotFound): the server could not find the requested resource (get services http:heapster:)
```
- ということなので、metrics serverをデプロイして上記コマンドを使えるようにしていきましょう。
- インストール
    - `kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml`

```:shell
$ kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.6/components.yaml
clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created
clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created
rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created
apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
serviceaccount/metrics-server created
deployment.apps/metrics-server created
service/metrics-server created
clusterrole.rbac.authorization.k8s.io/system:metrics-server created
clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created
```

- デプロイできていそうなので、コマンドを実行してみましょう。

```:shell
$ kubectl top node
NAME                                             CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
ip-10-0-21-171.ap-northeast-1.compute.internal   59m          3%     635Mi           23%       
ip-10-0-22-50.ap-northeast-1.compute.internal    69m          3%     757Mi           27%   
```

- `kubectl top node`が無事に使えるようになっています！もちろん`kubectl top pod`も使えるようになっています。

```
$ kubectl top pod alb-ingress-controller-869744b58-s6blb -n kube-system
NAME                                     CPU(cores)   MEMORY(bytes)   
alb-ingress-controller-869744b58-s6blb   2m           13Mi            
```

- 次はCPU負荷がXX％になったらpodをXX個増やす、みたいな設定が必要になってくるのですが、それはMattermostをデプロイする時の設定になるので、metrics serverの設定は一旦ここでおしまいです。

## Amazon EFS CSI ドライバーの設定
- これは特に何も考えずにMattermostをデプロイするとMattermostの機能を提供するpodのデータはEC2に繋がっているEBSに保存されます。EBSはEKSクラスターを形作るEC2に繋がっているストレージです。
- これの何が問題かと言うと、例えばEC2を2台でクラスターを組んでいる今の構成では、Mattermostの機能を提供するpodが2つ各EC2で稼働しているとして、こっちのEC2で稼働しているMattermostからAというデータは見れるけど、あっちのEC2で稼働しているMattermostからはAというデータは見れない、というような事態が起こりうるということです。
- そんな時の解決策といえば、`共有ストレージ`ですよね。ここでAWSで共有ストレージのサービスを提供しているEFSを利用するということになります。
- なのでまずEFSを利用できるようにしておかないといけません。
    - 参考：[Amazon EKS で永続的ストレージを使用する](https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-persistent-storage/)

### EFSの作成をしていきます。
- 事前準備：ネットワーク関連の設定

```:shell
#  VPC ID を取得する
$ aws eks describe-cluster --name ver-Mattermost-eks --query "cluster.resourcesVpcConfig.vpcId" --output text
vpc-xxxxxxxxxxxxxxx

#  VPC クラスターの CIDR 範囲を取得する
$ aws ec2 describe-vpcs --vpc-ids vpc-xxxxxxxxxxxxxxx --query "Vpcs[].CidrBlock" --output text
10.0.0.0/16

# Amazon EFS マウントポイントのインバウンドネットワークファイルシステム (NFS) トラフィックを許可するセキュリティグループを作成する
$ aws ec2 create-security-group --description efs-sg --group-name efs-sg --vpc-id vpc-xxxxxxxxxxxxxxx
{
    "GroupId": "sg-xxxxxxxxxxxxxxx"
}

# NFS インバウンドルールを追加して、VPC のリソースが EFS と通信できるようにする
$ aws ec2 authorize-security-group-ingress --group-id sg-xxxxxxxxxxxxxxx--protocol tcp --port 2049 --cidr 10.0.0.0/16
```

- Amazon EFS ファイルシステムを作成

```:shell
$ aws efs create-file-system --creation-token eks-efs
{
    "OwnerId": "xxxxxxxxxxxxxxx",
    "CreationToken": "eks-efs",
    "FileSystemId": "fs-xxxxxxxx",
    "CreationTime": "2020-08-30T07:44:27+00:00",
    "LifeCycleState": "creating",
    "NumberOfMountTargets": 0,
    "SizeInBytes": {
        "Value": 0,
        "ValueInIA": 0,
        "ValueInStandard": 0
    },
    "PerformanceMode": "generalPurpose",
    "Encrypted": false,
    "ThroughputMode": "bursting",
    "Tags": []
}

```

- EFSのマウント (各AZで実行する)

```:shell
aws efs create-mount-target --file-system-id fs-xxxxxxxx--subnet-id subnet-0ad4ab16f800797fa --security-group sg-xxxxxxxxxxxxxxx

aws efs create-mount-target --file-system-id fs-xxxxxxxx--subnet-id subnet-0d0c2821bd833ab40 --security-group sg-xxxxxxxxxxxxxxx

aws efs create-mount-target --file-system-id fs-xxxxxxxx --subnet-id subnet-00a0740f60c9fd5e1 --security-group sg-xxxxxxxxxxxxxxx
```


### Amazon EFS CSI ドライバーをデプロイする

- 以下のようにデプロイします。
    - `kubectl apply -k "github.com/kubernetes-sigs/aws-efs-csi-driver/deploy/kubernetes/overlays/stable/?ref=master"`

```
$ kubectl apply -k "github.com/kubernetes-sigs/aws-efs-csi-driver/deploy/kubernetes/overlays/stable/?ref=master"
daemonset.apps/efs-csi-node created
csidriver.storage.k8s.io/efs.csi.aws.com created
```

- 確認します。

```
$ kubectl get daemonsets efs-csi-node --namespace=kube-system
NAME           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
efs-csi-node   2         2         2       2            2           kubernetes.io/os=linux   53s
```

- `efs-csi-node`が正常に稼働していればOKです。
- この後に、StorageClassやPV、PVCを作っていきますが、それはMattermostのデプロイ時にやっていきたいと思います。

## まとめ
- これでようやくMattermostをデプロイする準備が整ってきました。
- こういった`EKSクラスターでアプリケーションを稼働させる前にセットアップしておくべきツール`をもっと効率よく準備できるような方法も考えないといけないなあと感じました。

## 関連記事
- [EKSでMattermostを構築した話：0.概要編 - Qiita](https://qiita.com/tnagano1981/items/555301cecd3ed309fa43)
- [EKSでMattermostを構築した話：1. VPC作成編 - Qiita](https://qiita.com/tnagano1981/items/75408d509c0660408c97)
- [EKSでMattermostを構築した話：2. EKSクラスター作成編 - Qiita](https://qiita.com/tnagano1981/items/fc5d96ddf9a3b5c4fb03)
- [EKSでMattermostを構築した話：3-1. EKSクラスターでアプリケーションを稼働させる前にセットアップしておくべきツール編 - Qiita](https://qiita.com/tnagano1981/items/c91d27fa2e286a96b6ff)