terraform-provider-eksctlで既存のVPC上にEKSクラスターを作成してみた

2021.02.28

eksctlでのEKSクラスター作成は、デフォルトではクラスターが稼働するVPCも併せて作成してくれます。が、既存のVPC上にクラスターを作成することもできます。今回はこの構成をterraform-provider-eksctlでやってみます。

※ terraform-provider-eksctlについてご存じない方は、まずは以下ブログをどうぞ。

VPCの作成

VPC関連のリソースは色々あって一つずつプロビジョニングするのが煩雑なので、以下公式モジュールを利用してラクします。

main.tf

terraform {
  required_version = "= 0.14.7"

  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "3.30.0"
    }
  }
}

provider "aws" {
  region = "ap-northeast-1"
}

vpc.tf

data "aws_availability_zones" "available" {
  state = "available"
}

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "2.77.0"

  name = "canary-test-vpc"
  cidr = "10.0.0.0/16"

  azs             = data.aws_availability_zones.available.names
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
}

terraform initしてterraform applyすると、以下リソースが作成されました。楽ちんですね。

$ terraform state list
data.aws_availability_zones.available
module.vpc.aws_eip.nat[0]
module.vpc.aws_eip.nat[1]
module.vpc.aws_eip.nat[2]
module.vpc.aws_internet_gateway.this[0]
module.vpc.aws_nat_gateway.this[0]
module.vpc.aws_nat_gateway.this[1]
module.vpc.aws_nat_gateway.this[2]
module.vpc.aws_route.private_nat_gateway[0]
module.vpc.aws_route.private_nat_gateway[1]
module.vpc.aws_route.private_nat_gateway[2]
module.vpc.aws_route.public_internet_gateway[0]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.private[1]
module.vpc.aws_route_table.private[2]
module.vpc.aws_route_table.public[0]
module.vpc.aws_route_table_association.private[0]
module.vpc.aws_route_table_association.private[1]
module.vpc.aws_route_table_association.private[2]
module.vpc.aws_route_table_association.public[0]
module.vpc.aws_route_table_association.public[1]
module.vpc.aws_route_table_association.public[2]
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.private[2]
module.vpc.aws_subnet.public[0]
module.vpc.aws_subnet.public[1]
module.vpc.aws_subnet.public[2]
module.vpc.aws_vpc.this[0]

上記VPCを使ってEKSクラスターを作成

eksctlを使う部分は、今後(=次回以降のブログで)複数回使いたいのでmoduleにします。

blue.tf

data "aws_region" "current" {}

module "blue" {
  source = "./modules/k8s_cluster"

  cluster_name       = "blue"
  region             = data.aws_region.current.name
  k8s_version        = "1.19"
  vpc_id             = module.vpc.vpc_id
  private_subnet_ids = module.vpc.private_subnets
  public_subnet_ids  = module.vpc.public_subnets
}

モジュール用のファイル群を用意します。

$ tree ./
./
├── blue.tf
├── main.tf
├── modules
│   └── k8s_cluster
│       ├── eksctl.tf
│       ├── providers.tf
│       ├── target_group.tf
│       ├── variables.tf
│       └── yaml
│           └── eksctl-config.yaml
└── vpc.tf

provider.tfには、terraform-provider-eksctlを使うことだけ定義しています。

./modules/k8s_cluster/provider.tf

terraform {
  required_providers {
    eksctl = {
      source  = "mumoshu/eksctl"
      version = "0.15.1"
    }
  }
}
provider "eksctl" {}

variables.tfでは変数を定義します。vpc_id以降の3変数は先程のVPCモジュールから参照する想定です。この公式VPCモジュール、output値がものすごくたくさんあるので、こういった感じで他で参照するような要件もほとんど対応できるのではないかと思います。

./modules/k8s_cluster/variables.tf

variable "cluster_name" {
  type = string
}
variable "region" {
  type = string
}
variable "k8s_version" {
  type = string
}
variable "vpc_id" {
  type = string
}
variable "private_subnet_ids" {
  type = list(string)
}
variable "public_subnet_ids" {
  type = list(string)
}

eksctl.tfspecに対しtemplatefile関数を使って動的に値を渡します。

./modules/k8s_cluster/eksctl.tf

data "aws_availability_zones" "available" {
  state = "available"
}

resource "eksctl_cluster" "main" {
  eksctl_version = "0.38.0"
  name           = var.cluster_name
  region         = var.region
  version        = var.k8s_version
  spec = templatefile(
    "./modules/k8s_cluster/yaml/eksctl-config.yaml"
    , {
      target_group_arn = aws_alb_target_group.tg.arn
      az               = data.aws_availability_zones.available.names
      private          = var.private_subnet_ids
      public           = var.public_subnet_ids
    }
  )
  vpc_id = var.vpc_id
}

VPCのIDについては、素のeksctl(=terraform-provider-eksctlを介さず直接eksctlを使う、という意味です)ではYAMLファイル内(上記spec引数部分に相当)に指定することができます。が、terraform-provider-eksctlでspec内に記載したところ、

Error: validating eksctl_cluster's "spec": vpc.id must not be set within the spec yaml. use "vpc_id" attribute instead, becaues the provider uses it for generating the final eksctl cluster config yaml

と怒られましたので、 vpc_id引数として specから外出しにする必要があるようです。


以下は前述のeksctl.tfから呼ばれているYAMLファイルの部分です。

./modules/k8s_cluster/yaml/eksctl-config.yaml

nodeGroups:
  - name: ng2
    instanceType: m5.large
    desiredCapacity: 1
    targetGroupARNs:
      - ${target_group_arn}
vpc:
  subnets:
    private:
      ${element(az,0)}:
        id: ${element(private,0)}
      ${element(az,1)}:
        id: ${element(private,1)}
      ${element(az,2)}:
        id: ${element(private,2)}
    public:
      ${element(az,0)}:
        id: ${element(public,0)}
      ${element(az,1)}:
        id: ${element(public,1)}
      ${element(az,2)}:
        id: ${element(public,2)}
  • vpc.subnets以降の実装はちょっとイマイチだなと自分でも思うのですが、ちょっと他の方法を思いつきませんでした。。
  • nodeGroups.targetGroupARNsはNodeGroupのAuto Scaling Groupと紐付けるTarget GroupのARNを指定します。今回の要件では不要ですが、次回以降のブログで必要になる予定なので指定しておきます。以下がそのTarget Groupの定義ファイルです。

./modules/k8s_cluster/target_group.tf

resource "aws_alb_target_group" "tg" {
  name     = "${var.cluster_name}-tg"
  port     = 30080
  protocol = "HTTP"
  vpc_id   = var.vpc_id
}

再びterraform initしてterraform applyします。

Target Groupとクラスターがプロビジョニングできました。

$ terraform state list
data.aws_availability_zones.available
data.aws_region.current
module.blue.data.aws_availability_zones.available
module.blue.aws_alb_target_group.tg
module.blue.eksctl_cluster.main
module.vpc.aws_eip.nat[0]
module.vpc.aws_eip.nat[1]
module.vpc.aws_eip.nat[2]
module.vpc.aws_internet_gateway.this[0]
module.vpc.aws_nat_gateway.this[0]
module.vpc.aws_nat_gateway.this[1]
module.vpc.aws_nat_gateway.this[2]
module.vpc.aws_route.private_nat_gateway[0]
module.vpc.aws_route.private_nat_gateway[1]
module.vpc.aws_route.private_nat_gateway[2]
module.vpc.aws_route.public_internet_gateway[0]
module.vpc.aws_route_table.private[0]
module.vpc.aws_route_table.private[1]
module.vpc.aws_route_table.private[2]
module.vpc.aws_route_table.public[0]
module.vpc.aws_route_table_association.private[0]
module.vpc.aws_route_table_association.private[1]
module.vpc.aws_route_table_association.private[2]
module.vpc.aws_route_table_association.public[0]
module.vpc.aws_route_table_association.public[1]
module.vpc.aws_route_table_association.public[2]
module.vpc.aws_subnet.private[0]
module.vpc.aws_subnet.private[1]
module.vpc.aws_subnet.private[2]
module.vpc.aws_subnet.public[0]
module.vpc.aws_subnet.public[1]
module.vpc.aws_subnet.public[2]
module.vpc.aws_vpc.this[0]

クラスター内のリソースも確認できました。

$ kubectl get all -A
NAMESPACE     NAME                           READY   STATUS    RESTARTS   AGE
kube-system   pod/aws-node-cj9bp             1/1     Running   0          94m
kube-system   pod/coredns-59847d77c8-s47js   1/1     Running   0          113m
kube-system   pod/coredns-59847d77c8-z2flq   1/1     Running   0          113m
kube-system   pod/kube-proxy-rv9vs           1/1     Running   0          94m

NAMESPACE     NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
default       service/kubernetes   ClusterIP   172.20.0.1    <none>        443/TCP         114m
kube-system   service/kube-dns     ClusterIP   172.20.0.10   <none>        53/UDP,53/TCP   113m

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
kube-system   daemonset.apps/aws-node     1         1         1       1            1           <none>          113m
kube-system   daemonset.apps/kube-proxy   1         1         1       1            1           <none>          113m

NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/coredns   2/2     2            2           113m

NAMESPACE     NAME                                 DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/coredns-59847d77c8   2         2         2       113m