Managed Node Groupsを用いてTerraformでEKSクラスタをシュッと構築する

jagaNikumanPosted by

こんにちは、SREの岩立です。
re:Invent2019の少し前にEKSのManaged Node Groupsが発表されました。
https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html

従来のEKSでは、Nodeを追加するためには、EC2インスタンスの起動時にUserDataなどを用いてVMの設定、VMをEKSのクラスタにNodeとして登録させたりしなければいけませんでした。しかし、このManaged Node Groupsが登場したことにより、それらの作業を行わずにEKSクラスタにNodeを追加することが出来るのでとても楽になれます。

今回はそのManaged Node Groupsを用いてEKSのクラスタをTerraformでシュッと構築してみます。

構築の作業内容

今回の記事では、以下の作業を順番に行っていきます。

  • EKSクラスタの作成
    • VPC・Subnetの作成
    • EKSクラスタ用のIAM Roleの作成
    • EKSクラスタ自体の作成
    • kubeconfigの設定・EKSクラスタへの疎通確認
  • Managed Node Groupsの作成
    • Managed Node Groups用のIAM Roleの作成
    • Managed Node Groupsを作成
    • Nodeが追加されたことの確認

EKSクラスタの作成

クラスタの作成にはVPCとSubnetとIAM Roleが必要なので、先にそちらを作成します。以下のコードではap-northeast-1内のavailableなzoneにそれぞれsubnetを作成します。

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

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

resource "aws_vpc" "eks-test-clueter-vpc" {
    cidr_block = "10.25.0.0/16"
}

resource "aws_subnet" "eks-test-cluster-subnet" {
    count             = "${length(data.aws_availability_zones.available-zone.zone_ids)}"

    availability_zone = "${data.aws_availability_zones.available-zone.names[count.index]}"
    cidr_block        = "${cidrsubnet(aws_vpc.eks-test-clueter-vpc.cidr_block, 8, count.index)}"
    vpc_id            = "${aws_vpc.eks-test-clueter-vpc.id}"
}

VPCとSubnetを作成したら、EKSクラスタ用のIAM Roleの作成を行います。

resource "aws_iam_role" "eks-test-cluster-role" {
    name = "eks-test-cluster-role"

    assume_role_policy = <<POLICY
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-role-AmazonEKSClusterPolicy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
    role       = "${aws_iam_role.eks-test-cluster-role.name}"
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-role-AmazonEKSServicePolicy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
    role       = "${aws_iam_role.eks-test-cluster-role.name}"
}

VPC、Subnet、IAM Roleを作成したら、aws_eks_clusterリソースを用いてEKSクラスタを作成します。
https://www.terraform.io/docs/providers/aws/r/eks_cluster.html

resource "aws_eks_cluster" "test-cluster" {
    name     = "test-cluster"
    role_arn = "${aws_iam_role.eks-test-cluster-role.arn}"

    version  = "1.14"

    vpc_config {
        subnet_ids = ["${aws_subnet.eks-test-cluster-subnet.*.id}"]
        endpoint_public_access = true
    }
}

output "api endpoint" {
    value = "${aws_eks_cluster.test-cluster.endpoint}"
}

output "kubeconfig-certificate-authority-data" {
    value = "${aws_eks_cluster.test-cluster.certificate_authority.0.data}"
}

10分程度でクラスタ自体の作成は完了します。outputで吐き出されたAPIエンドポイントと認証情報を用いてkubeconfigを設定します。kubeconfig上のクラスタ名は任意なので、今回はEKSのクラスタ名と同じtest-clusterとします。

apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority-data: outputで出力された認証情報(base64)
    server: outputで出力されたAPIエンドポイント
  name: 適当なクラスタ名
users:
- name: test-cluster
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      args:
      - --region
      - ap-northeast-1
      - eks
      - get-token
      - --cluster-name
      - test-cluster
      command: aws
      env: null
contexts:
- context:
    cluster: test-cluster
    user: test-cluster
  name: test-cluster
current-context: test-cluster

上記の内容を~/.kube/configに記述します。その後kubectl get svcコマンドでクラスタへ接続できることを確認します。

$ kubectl get svc
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.100.0.1   <none>        443/TCP   14m

$ kubectl get nodes
No resources found.

現段階ではクラスタ自体しか作成していないためkubectl get nodesを叩いてもNo resources foundとなってしまいます。
そのため、これから本題のManaged Worker Groupsを使ってNodeを追加したいと思います。

Managed Node Groupsの作成

Managed Node Groupsを作成する前に、EKSクラスタからAWSのAPIを触るためにIAM Roleを作成する必要があります。そのため、以下のコードでロールの作成、作成したロールに対してポリシーのアタッチを行います。

resource "aws_iam_role" "eks-test-cluster-node-role" {
    name = "eks-test-cluster-node-role"

    assume_role_policy = &lt;&lt;POLICY
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-node-role-AmazonEKSWorkerNodePolicy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
    role       = "${aws_iam_role.eks-test-cluster-node-role.name}"
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-node-role-AmazonEKS_CNI_Policy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
    role       = "${aws_iam_role.eks-test-cluster-node-role.name}"
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-node-role-AmazonEC2ContainerRegistryReadOnly" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
    role       = "${aws_iam_role.eks-test-cluster-node-role.name}"
}

そして、本題のManaged Node Groupsを作成するにはaws_eks_node_groupリソースを使用します。
https://www.terraform.io/docs/providers/aws/r/eks_node_group.html
下記のコードでManaged Node Groupsを作成します。

resource "aws_eks_node_group" "test-cluster-node-group" {
    cluster_name    = "${aws_eks_cluster.test-cluster.name}"
    node_group_name = "test-cluster-node-group"
    node_role_arn   = "${aws_iam_role.eks-test-cluster-node-role.arn}"
    subnet_ids      = ["${aws_subnet.eks-test-cluster-subnet.*.id}"]

    scaling_config {
        desired_size = 3
        max_size     = 3
        min_size     = 3
    }
}

terraform applyした後、大体2分弱でNode GroupとNodeが作成され、kubectl get nodeから追加されたNodeを確認できるようになります。

$ kubectl get node
NAME                                                 STATUS   ROLES    AGE   VERSION
ip-***-***-***-***.ap-northeast-1.compute.internal   Ready    <none>   45s   v1.14.7-eks-*****
ip-***-***-***-***.ap-northeast-1.compute.internal   Ready    <none>   43s   v1.14.7-eks-*****
ip-***-***-***-***.ap-northeast-1.compute.internal   Ready    <none>   51s   v1.14.7-eks-*****

今回は3台のノードを作成しましたが、台数の調整はscaling_configのパラメータを変更することで行えます。以上でManaged Node Groupsを用いたEKSクラスタの構築は完了です。

最終的なコードの全体

https://github.com/jagaNikuman/eks-managed-node-groups

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

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


resource "aws_vpc" "eks-test-clueter-vpc" {
    cidr_block = "10.25.0.0/16"
}

resource "aws_subnet" "eks-test-cluster-subnet" {
    count = "${length(data.aws_availability_zones.available-zone.zone_ids)}"

    availability_zone = "${data.aws_availability_zones.available-zone.names[count.index]}"
    cidr_block        = "${cidrsubnet(aws_vpc.eks-test-clueter-vpc.cidr_block, 8, count.index)}"
    vpc_id            = "${aws_vpc.eks-test-clueter-vpc.id}"
}

resource "aws_iam_role" "eks-test-cluster-role" {
    name = "eks-test-cluster-role"

    assume_role_policy = &lt;&lt;POLICY
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "eks.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-role-AmazonEKSClusterPolicy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
    role       = "${aws_iam_role.eks-test-cluster-role.name}"
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-role-AmazonEKSServicePolicy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKSServicePolicy"
    role       = "${aws_iam_role.eks-test-cluster-role.name}"
}

resource "aws_eks_cluster" "test-cluster" {
    name     = "test-cluster"
    role_arn = "${aws_iam_role.eks-test-cluster-role.arn}"

    version  = "1.14"

    vpc_config {
        subnet_ids = ["${aws_subnet.eks-test-cluster-subnet.*.id}"]
        endpoint_public_access = true
    }
}

output "api endpoint" {
    value = "${aws_eks_cluster.test-cluster.endpoint}"
}

output "kubeconfig-certificate-authority-data" {
    value = "${aws_eks_cluster.test-cluster.certificate_authority.0.data}"
}


resource "aws_iam_role" "eks-test-cluster-node-role" {
    name = "eks-test-cluster-node-role"

    assume_role_policy = &lt;&lt;POLICY
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-node-role-AmazonEKSWorkerNodePolicy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
    role       = "${aws_iam_role.eks-test-cluster-node-role.name}"
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-node-role-AmazonEKS_CNI_Policy" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
    role       = "${aws_iam_role.eks-test-cluster-node-role.name}"
}

resource "aws_iam_role_policy_attachment" "eks-test-cluster-node-role-AmazonEC2ContainerRegistryReadOnly" {
    policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
    role       = "${aws_iam_role.eks-test-cluster-node-role.name}"
}


resource "aws_eks_node_group" "test-cluster-node-group" {
    cluster_name    = "${aws_eks_cluster.test-cluster.name}"
    node_group_name = "test-cluster-node-group"
    node_role_arn   = "${aws_iam_role.eks-test-cluster-node-role.arn}"
    subnet_ids      = ["${aws_subnet.eks-test-cluster-subnet.*.id}"]

    scaling_config {
        desired_size = 3
        max_size     = 3
        min_size     = 3
    }
}

まとめ

今回はManaged Node Groupsを使ってEKSのクラスタ構築をTerraformでシュッとやってみました。Managed Node Groupsを用いることで従来の手法より圧倒的に楽に構築できるようになったかと思います。これからも更なるEKSの新機能に注目したいと思います。