Amazon EKS on AWS Fargateに触れてみた備忘録

# 概要
[AWS re:Invent 2019](http://reinvent.awseventsjapan.com/)にて **Amazon EKS on AWS Fargate** がGAになったと発表された模様なので、遅ればせながら触れてみた備忘録を残してみたいと思います。

ちなみに[この記事](Amazon EKS on AWS Fargate Now Generally Available)を参考にしながら動かしてみました。

# 準備
事前に必要なものとしては **`eksctl`** なるものがあると良いとのこと。
`AWS CLI` をいれておくのと、 `AWS CLI Credentials` を作成しておけばOK。

## eksctl
`eksctl` とはAmazon EKS上にkubernates clusterを作ったり管理したりできるコマンドラインツールとのこと。
[Getting Started with eksctl](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html)を参考に準備してみる。

### インストール
自分は `macOS` なので `brew` を使ってインストールしてみます。

* `Weaveworks Homebrew tap` のインストール

```shell
$ brew tap weaveworks/tap
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 1 tap (homebrew/cask).
No changes to formulae.

==> Tapping weaveworks/tap
Cloning into '/usr/local/Homebrew/Library/Taps/weaveworks/homebrew-tap'...
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 10 (delta 1), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (10/10), done.
Tapped 4 formulae (30 files, 10.5KB).
```

* `eksctl` のインストール

```
$ brew install weaveworks/tap/eksctl
==> Installing eksctl from weaveworks/tap
==> Installing dependencies for weaveworks/tap/eksctl: kubernetes-cli and aws-iam-authenticator
==> Installing weaveworks/tap/eksctl dependency: kubernetes-cli
・・・・・
🍺  /usr/local/Cellar/kubernetes-cli/1.17.1: 235 files, 49MB
==> Installing weaveworks/tap/eksctl dependency: aws-iam-authenticator
・・・・・
🍺  /usr/local/Cellar/aws-iam-authenticator/0.4.0: 5 files, 24.9MB
==> Installing weaveworks/tap/eksctl
・・・・・
🍺  /usr/local/Cellar/eksctl/0.12.0: 3 files, 87.0MB, built in 14 minutes 43 seconds

$ eksctl version
[ℹ]  version.Info{BuiltAt:"", GitCommit:"", GitTag:"0.12.0"}
```

# クラスタの作成
インストールしたeksctlでクラスタを作成してみます。

```
$ eksctl create cluster --name demo-newsblog --region ap-northeast-1 --fargate
[ℹ]  eksctl version 0.12.0
[ℹ]  using region ap-northeast-1
[ℹ]  setting availability zones to [ap-northeast-1d ap-northeast-1a ap-northeast-1c]
[ℹ]  subnets for ap-northeast-1d - public:〜 private:〜
[ℹ]  subnets for ap-northeast-1a - public:〜 private:〜
[ℹ]  subnets for ap-northeast-1c - public:〜 private:〜
[ℹ]  using Kubernetes version 1.14
[ℹ]  creating EKS cluster "demo-newsblog" in "ap-northeast-1" region with Fargate profile
[ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-1 --cluster=demo-newsblog'
[ℹ]  CloudWatch logging will not be enabled for cluster "demo-newsblog" in "ap-northeast-1"
[ℹ]  you can enable it with 'eksctl utils update-cluster-logging --region=ap-northeast-1 --cluster=demo-newsblog'
[ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "demo-newsblog" in "ap-northeast-1"
[ℹ]  1 task: { create cluster control plane "demo-newsblog" }
[ℹ]  building cluster stack "eksctl-demo-newsblog-cluster"
[ℹ]  deploying stack "eksctl-demo-newsblog-cluster"
[✔]  all EKS cluster resources for "demo-newsblog" have been created
[✔]  saved kubeconfig as "/Users/yumatsud/.kube/config"
[ℹ]  creating Fargate profile "fp-default" on EKS cluster "demo-newsblog"
[ℹ]  created Fargate profile "fp-default" on EKS cluster "demo-newsblog"
[ℹ]  "coredns" is now schedulable onto Fargate
[ℹ]  "coredns" is now scheduled onto Fargate
[ℹ]  "coredns" pods are now scheduled onto Fargate
[✖]  parsing kubectl version string  (upstream error: error: EOF
) / "0.0.0": Version string empty
[ℹ]  cluster should be functional despite missing (or misconfigured) client binaries
[✔]  EKS cluster "demo-newsblog" in "ap-northeast-1" region is ready
```

ちょっとだけ中身を見てみると、

* 各AZ毎にpublic subnetとprivate subnetが作られる
* SGもコントロールパネルとノードのやりとり用やノード間のやりとり用などが自動で作られる
* IAMロールやCloudWatch Logsなども作られる

とかで自動的に色々と作ってくれるのば便利ですね。上記コマンドだと、
`parsing kubectl version string  (upstream error: error: EOF) / "0.0.0": Version string empty`
ですが、クラスタはちゃんと作られてたっぽいです。念の為下記で確認したところ、

```
$ kubectl version 
Please enter Username: 
```

のように入力を求められてしまうからで、これ自体は [こちらの記事](https://qiita.com/ponkio-o/items/16d8e363ff65ec16f391) にある通り、kubectlのバージョンが低いと出てしまうとのこと。`eksctl` の該当箇所は[こちら](https://github.com/weaveworks/eksctl/blob/master/pkg/utils/kubectl.go#L43)でした。

# Fargate Profile
**`Fargate Profile`** というのが最初はなんのこっちゃ？と思いましたが、Profileの追加画面を見てみると、
<img width="700" alt="スクリーンショット 2020-01-18 21.40.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/71637/83fb06d1-8014-7ec9-ee6a-bd15b63c1f93.png">
とのことで、podをどこでどのように動かすかなどを定義するものらしいです。
ここからroleやsubnetを追加していくのですが、参考にしてた記事によると、
`only private subnets are supported for Fargate pods`
とあったので、public subnetは除いてみました。

そのあとにpodを動かす `namespace` を設定し、確認後に作成する。

これだけで、namespaceに一致するpodがあれば、そこでpodが動くようです。
ちなみに、jsonを定義してコマンドラインから作成することもできるとのこと。

# deploy
`nginx` のコンテナを動かしてみます。

```
$ kubectl create deployment demo-app --image=nginx
deployment.apps/demo-app created

$ kubectl get pods                                
NAME                        READY   STATUS              RESTARTS   AGE
demo-app-6dbfc49497-frmdq   0/1     ContainerCreating   0          45s

$ kubectl get nodes 
NAME                                                         STATUS   ROLES    AGE    VERSION
fargate-ip-<ip>.ap-northeast-1.compute.internal   Ready    <none>   160m   v1.14.8-eks
fargate-ip-<ip>.ap-northeast-1.compute.internal   Ready    <none>   160m   v1.14.8-eks
fargate-ip-<ip>.ap-northeast-1.compute.internal    Ready    <none>   5s     v1.14.8-eks

$ kubectl get pods 
NAME                        READY   STATUS    RESTARTS   AGE
demo-app-6dbfc49497-frmdq   1/1     Running   0          3m9s
```

ここまでで [Getting Started with eksctl](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html) の実行は終了。。。これだけだと物足りないので、アクセスできるところまでやってみる。

# AWS ALB Ingress Controller
[Kubernetes Ingress with AWS ALB Ingress Controller](https://aws.amazon.com/jp/blogs/opensource/kubernetes-ingress-aws-alb-ingress-controller/)を参考にインターネットからアクセスできるようにしてみようと思います。

ちなみに、実際にALBを作成してアクセスできるようにするには「[Amazon EKS on Fargate で ALB Ingress Controller を使用する](https://aws.amazon.com/jp/blogs/news/using-alb-ingress-controller-with-amazon-eks-on-fargate/)」が参考になりそうでした。


* クラスターの OIDC プロバイダーをセットアップ

```
$ eksctl utils associate-iam-oidc-provider --cluster demo-newsblog --approve
[ℹ]  eksctl version 0.12.0
[ℹ]  using region ap-northeast-1
[ℹ]  will create IAM Open ID Connect provider for cluster "demo-newsblog" in "ap-northeast-1"
[✔]  created IAM Open ID Connect provider for cluster "demo-newsblog" in "ap-northeast-1"
```

* `AWS ALB Ingress controller` に必要なRBACの作成

```
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/rbac-role.yaml     
clusterrole.rbac.authorization.k8s.io/alb-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/alb-ingress-controller created
serviceaccount/alb-ingress-controller created
```

* `ALB Ingress controller` がAWS APIにアクセスするためのIAMポリシーの作成

```
$ aws iam create-policy \
    --policy-name ALBIngressControllerIAMPolicy \
    --policy-document https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/iam-policy.json
```

* Kubernetes用のサービスアカウント作成し、上記で作成したIAMロールを付与

```
$ eksctl create iamserviceaccount \
> --cluster=demo-newsblog \
> --namespace=kube-system \
> --name=alb-ingress-controller \
> --attach-policy-arn=<上記のarn> \
> --override-existing-serviceaccounts \
> --approve
[ℹ]  eksctl version 0.12.0
[ℹ]  using region ap-northeast-1
[ℹ]  1 iamserviceaccount (kube-system/alb-ingress-controller) was included (based on the include/exclude rules)
[!]  metadata of serviceaccounts that exist in Kubernetes will be updated, as --override-existing-serviceaccounts was set
[ℹ]  1 task: { 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/alb-ingress-controller", create serviceaccount "kube-system/alb-ingress-controller" } }
[ℹ]  building iamserviceaccount stack "eksctl-demo-newsblog-addon-iamserviceaccount-kube-system-alb-ingress-controller"
[ℹ]  deploying stack "eksctl-demo-newsblog-addon-iamserviceaccount-kube-system-alb-ingress-controller"
[ℹ]  serviceaccount "kube-system/alb-ingress-controller" already exists
[ℹ]  updated serviceaccount "kube-system/alb-ingress-controller"
```

* `AWS ALB Ingress controller` をdeploy

```
$ curl -sS "https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/alb-ingress-controller.yaml" \
 | sed "s/# - --cluster-name=devCluster/- --cluster-name=demo-newsblog/g" 
 | kubectl apply -f -
deployment.apps/alb-ingress-controller created

$ kubectl logs -n kube-system $(kubectl get po -n kube-system | grep alb-ingress | awk '{print $1}')
W0118 14:31:53.198889       1 client_config.go:549] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
-------------------------------------------------------------------------------
AWS ALB Ingress controller
  Release:    v1.1.4
  Build:      git-0db46039
  Repository: https://github.com/kubernetes-sigs/aws-alb-ingress-controller.git
-------------------------------------------------------------------------------
```

* サンプルアプリケーションの [2048 game](https://gabrielecirulli.github.io/2048/) をデプロイ

```
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/2048/2048-namespace.yaml
namespace/2048-game created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/2048/2048-deployment.yaml
deployment.apps/2048-deployment created

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/2048/2048-service.yaml   
service/service-2048 created
```

* Ingressリソースのデプロイ

```
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/2048/2048-ingress.yaml
ingress.extensions/2048-ingress created
```

* 下記の `DNS-Name-Of-Your-ALB` で動作確認

```
$ kubectl get ingress/2048-ingress -n 2048-game
NAME           HOSTS   ADDRESS                                                                  PORTS   AGE
2048-ingress   *       <<アクセス先>>   80      3m
```

# 感想
eksctlとkubectlを使い分けていく必要があるのがやや面倒ですが、
AWSのリソースとの組み合わせをこのくらいの手順でやれるのは良いかもですね。
あとはproductionに耐えうるものにするにはどうするかを検討していければ実サービスでも使えそうだなと思いました。

# 参考
* [Getting Started with eksctl](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-eksctl.html)
* [Amazon EKS on Fargate で ALB Ingress Controller を使用する](https://aws.amazon.com/jp/blogs/news/using-alb-ingress-controller-with-amazon-eks-on-fargate/)
* [Kubernetes Ingress with AWS ALB Ingress Controller](https://aws.amazon.com/jp/blogs/opensource/kubernetes-ingress-aws-alb-ingress-controller/)