Fargate for EKSのVPC管理をTerraform、それ以外をeksctlでやる

# はじめに
[eksctl](https://eksctl.io/)を使用するとFargate for EKSを構築するのが手軽にできます

eksctlを使うとすごく簡単にFargate for EKS環境を構築できてとてもよいです！よいのですが...

Clusterの再構築、VPC上に別のAWS resourceを建てる...etc
後々のオペレーションを考えるとVPCはeksctl管理下(CloudFormationでVPCとEKSが同じスタックで管理される)にあるのは個人的にあまり好きではなかったのでVPCはTerraformで建てることにしました

# eksctlでVPCも管理
単純にeksctlでFargate for EKSを構築する場合はこんな感じ

```bash
$ export AWS_PROFILE=dev
$ eksctl create cluster -f eksctl/dev/cluster.yaml
$ eksctl create fargateprofile  -f eksctl/dev/fargateprofile.yaml
```
```yaml:eksctl/dev/cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: dev
  region: ap-northeast-1
  version: "1.14"

iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: alb-ingress-controller
        namespace: kube-system
      attachPolicyARNs:
        - "arn:aws:iam::XXXXXXXXXXX:policy/dev-AWSALBIngressController"

vpc:
  extraCIDRs:
    - 10.240.0.0/16

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]
```

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

metadata:
  name: dev
  region: ap-northeast-1
  version: "1.14"

fargateProfiles:
  - name: fp-default
    selectors:
      - namespace: default
      - namespace: kube-system
  - name: fp-development
    selectors:
      - namespace: development
```

# TerraformでVPCを管理
今回の本筋

VPCの構築には[terraform-aws-modules/vpc/aws](https://github.com/terraform-aws-modules/terraform-aws-vpc)モジュールを使用しました
※特殊な構成にしないならモジュール使用でいいと思う

```bash
$ export AWS_PROFILE=dev
$ terraform plan -out tfplan
$ terraform apply -input tfplan
$ vi eksctl/dev/cluster.yaml # Terraformで出力された値で更新が必要な場所を更新
$ vi eksctl/dev/fargateprofile.yaml # Terraformで出力された値で更新が必要な場所を更新
$ eksctl create cluster -f eksctl/dev/cluster.yaml
$ eksctl create fargateprofile  -f eksctl/dev/fargateprofile.yaml
```

```tf:vpc.tf
module "vpc" {
  source         = "terraform-aws-modules/vpc/aws"
  name           = "VPC"
  cidr           = "10.10.0.0/16"
  azs            = ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
  public_subnets = ["10.10.1.0/24", "10.10.2.0/24", "10.10.3.0/24"]
  public_subnet_tags = {
    "kubernetes.io/role/elb" = 1
  }
  private_subnets = ["10.10.101.0/24", "10.10.102.0/24", "10.10.103.0/24"]
  private_subnet_tags = {
    "kubernetes.io/role/internal-elb" = 1
  }
  enable_nat_gateway = true
  single_nat_gateway = false
  one_nat_gateway_per_az = false
  
  enable_vpn_gateway = true

  enable_dns_hostnames = true
  
  tags = {
    "kubernetes.io/cluster/${terraform.workspace}" = "shared"
  }
}
```

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

metadata:
  name: dev
  region: ap-northeast-1
  version: "1.14"

iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: alb-ingress-controller
        namespace: kube-system
      attachPolicyARNs:
        - "arn:aws:iam::XXXXXXXXXXX:policy/dev-AWSALBIngressController"

vpc:
  subnets:
    public:
      ap-northeast-1a: { id: subnet-XXXXXXXXXXXXXXXX0 }
      ap-northeast-1c: { id: subnet-XXXXXXXXXXXXXXXX1 }
      ap-northeast-1d: { id: subnet-XXXXXXXXXXXXXXXX2 }
    private:
      ap-northeast-1a: { id: subnet-XXXXXXXXXXXXXXXX3 }
      ap-northeast-1c: { id: subnet-XXXXXXXXXXXXXXXX4 }
      ap-northeast-1d: { id: subnet-XXXXXXXXXXXXXXXX5 }

cloudWatch:
  clusterLogging:
    enableTypes: ["*"]
```
```yaml:eksctl/dev/fargateprofile.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: dev
  region: ap-northeast-1
  version: "1.14"

fargateProfiles:
  - name: fp-default
    selectors:
      - namespace: default
      - namespace: kube-system
    subnets:
      - subnet-XXXXXXXXXXXXXXXX3
      - subnet-XXXXXXXXXXXXXXXX4
      - subnet-XXXXXXXXXXXXXXXX5
  - name: fp-development
    selectors:
      - namespace: development
    subnets:
      - subnet-XXXXXXXXXXXXXXXX3
      - subnet-XXXXXXXXXXXXXXXX4
      - subnet-XXXXXXXXXXXXXXXX5

```


# 注意点
Fargate for EKSに関連する設定がTerraformに数点入っているので説明に入っていきます

## private subnetとpublic subnetどっちも必要
```
  public_subnets = ["10.10.1.0/24", "10.10.2.0/24", "10.10.3.0/24"]
  private_subnets = ["10.10.101.0/24", "10.10.102.0/24", "10.10.103.0/24"]
```

Fargateではprivate subnetにしかNodeを構築ができないのでpublic subnetいらないのでは？と思うかもしれませんがECRからのpullで必要です

## k8sでVPC、サブネットを検出できるようにするための設定
```
  tags = {
    "kubernetes.io/cluster/${terraform.workspace}" = "shared"
  }
```
これがないとNodeを配置できません

## ALB Ingress ControllerをAmazon EKSクラスターにデプロイするための設定
```
  public_subnet_tags = {
    "kubernetes.io/role/elb" = 1
  }
  private_subnet_tags = {
    "kubernetes.io/role/internal-elb" = 1
  }
```

## VPCのDNSホスト名の有効化
```
enable_dns_hostnames = true
```

```bash
# enable_dns_hostnames = false
$ k get nodes
NAME           STATUS   ROLES    AGE   VERSION
fargate-null   Ready    <none>   17m   v1.14.8-eks
# enable_dns_hostnames = true
$ k get nodes                                                                                                                
NAME                                                      STATUS   ROLES    AGE   VERSION
fargate-ip-10-10-3-37.ap-northeast-1.compute.internal   Ready    <none>   20s   v1.14.8-eks
fargate-ip-10-10-3-8.ap-northeast-1.compute.internal    Ready    <none>   11s   v1.14.8-eks
```
これをしないと起動してくるNodeの名前が上記のように`fargate-null`に固定されてしまい1Nodeしか作成できないので必須です

Fargateでは1Node1Podで作成されるため、corednsが動かない問題が自分の環境ではおきました

# 参考
- https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/network_reqs.html
- https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/alb-ingress.html
- https://qiita.com/bbrfkr/items/efe41e78a8dcfcd35a10