Skip to Content
ArchiveProvisioning EKS with Terraform

테라폼으로 EKS 배포하기

EKS 클러스터 배포

Terraform을 사용하기 위한 선수조건이 만족되어있는 기준으로 프로비저닝을 진행합니다. 코드는 gasida님 github에 있는 코드를 기준으로 프로비저닝을 진행합니다.

프로비저닝을 위해 TL;DR 개념으로 코드를 apply 하는 과정을 먼저 보여드리고 프로비저닝 하는 옵션에 대해서는 글 하단부에 별도로 설명을 진행했습니다.

# 코드 가져오기 git clone https://github.com/gasida/aews-cicd.git cd aews-cicd/4 # terraform 환경 변수 저장 export TF_VAR_KeyName=[각자 ssh keypair] echo $TF_VAR_KeyName # terraform init terraform plan # 10분 후 배포 완료 terraform apply -auto-approve

테라폼 정보 확인

# terraform state list ~~~ # terraform console ----------------- data.aws_caller_identity.current data.aws_caller_identity.current.arn data.aws_eks_cluster.cluster data.aws_eks_cluster.cluster.name data.aws_eks_cluster.cluster.version data.aws_eks_cluster.cluster.access_config data.aws_eks_cluster_auth.cluster kubernetes_service_account.aws_lb_controller ----------------- terraform state show ... # cat terraform.tfstate | jq more terraform.tfstate

배포 정보 확인

# kubectl get node -v=6 # EKS 클러스터 인증 정보 업데이트 CLUSTER_NAME=myeks aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME kubectl config rename-context "arn:aws:eks:ap-northeast-2:$(aws sts get-caller-identity --query 'Account' --output text):cluster/$CLUSTER_NAME" "Aews-Labs" # kubectl cluster-info kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone kubectl get pod -A

이 상태에서 코드 재활용해서 두번째 클러스터 배포하기

# cd .. mkdir 5 cd 5 cp ../4/*.tf . ls # terraform init terraform apply -auto-approve -var=ClusterBaseName=myeks2 -var=KubernetesVersion="1.28" # EKS 클러스터 인증 정보 가져오기 CLUSTER_NAME2=myeks2 aws eks update-kubeconfig --region ap-northeast-2 --name $CLUSTER_NAME2 --kubeconfig ./myeks2config # EKS 클러스터 정보 확인 kubectl --kubeconfig ./myeks2config get node kubectl --kubeconfig ./myeks2config get pod -A

Untitled

같은 코드로 클러스터 두개의 배포에 성공했습니다.

이제 다시 삭제해주도록 하겠습니다

# 첫번째 폴더에서 클러스터 삭제 terraform destroy -auto-approve # 두번째 폴더에서 클러스터 삭제 terraform destroy -auto-approve -var=ClusterBaseName=myeks2 -var=KubernetesVersion="1.28"

개인적으로 테라폼을 적극적으로 사용해보질 않아

aews cicd git에 있는 terraform 코드를 한번씩 살펴보겠습니다.

Untitled

구조는 굉장히 간단하네요.

어디에 어떻게 사용될지 모르는 변수 항목을 먼저 보겠습니다.

뒤에서 참조하려면 꼭 선언되어있어야 할테니깐요

variable "KeyName" { description = "Name of an existing EC2 KeyPair to enable SSH access to the instances." type = string } variable "ClusterBaseName" { description = "Base name of the cluster." type = string default = "myeks" } variable "KubernetesVersion" { description = "Kubernetes version for the EKS cluster." type = string default = "1.29" } variable "WorkerNodeInstanceType" { description = "EC2 instance type for the worker nodes." type = string default = "t3.medium" } variable "WorkerNodeCount" { description = "Number of worker nodes." type = number default = 3 } variable "WorkerNodeVolumesize" { description = "Volume size for worker nodes (in GiB)." type = number default = 30 } variable "TargetRegion" { description = "AWS region where the resources will be created." type = string default = "ap-northeast-2" } variable "availability_zones" { description = "List of availability zones." type = list(string) default = ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"] } variable "VpcBlock" { description = "CIDR block for the VPC." type = string default = "192.168.0.0/16" } variable "public_subnet_blocks" { description = "List of CIDR blocks for the public subnets." type = list(string) default = ["192.168.1.0/24", "192.168.2.0/24", "192.168.3.0/24"] } variable "private_subnet_blocks" { description = "List of CIDR blocks for the private subnets." type = list(string) default = ["192.168.11.0/24", "192.168.12.0/24", "192.168.13.0/24"] }

VPC CIDR 블록과 서브넷들의 변수가 선언되어있고

인스턴스의 정보들을 선언하고

접근할 자격증명, 그리고 쿠버네티스의 버전 명시가 되어있네요

우선 모든 클라우드 리소스의 표준이 되는 네트워크 VPC부터 보겠습니다.

vpc.tf

provider "aws" { region = var.TargetRegion } module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "~>5.7" name = "${var.ClusterBaseName}-VPC" cidr = var.VpcBlock azs = var.availability_zones enable_dns_support = true enable_dns_hostnames = true public_subnets = var.public_subnet_blocks private_subnets = var.private_subnet_blocks enable_nat_gateway = true single_nat_gateway = true one_nat_gateway_per_az = false map_public_ip_on_launch = true igw_tags = { "Name" = "${var.ClusterBaseName}-IGW" } nat_gateway_tags = { "Name" = "${var.ClusterBaseName}-NAT" } public_subnet_tags = { "Name" = "${var.ClusterBaseName}-PublicSubnet" "kubernetes.io/role/elb" = "1" } private_subnet_tags = { "Name" = "${var.ClusterBaseName}-PrivateSubnet" "kubernetes.io/role/internal-elb" = "1" } tags = { "Environment" = "${var.ClusterBaseName}-lab" } }

vpc 모듈을 사용하는데 있어 최소 버전명시를 진행하고

var.tf에서 사용했던 서브넷 그리고 NAT Gateway 설정을 진행합니다.

NAT GW가 NAT Instance에 비해 세배정도 비싸긴하지만,

multi AZ에서 NAT GW자체를 한개만 생성하기위해 single nat gw를 선언했습니다.

기초를 다시 다질겸 적어보자면

IGW는 Public Subnet 을 위한 리소스로 인/아웃바운드가 모두 가능하며

NAT GW는 Private Subnet을 위한 리소스로 아웃바운드만 가능합니다.

다음은 eks.tf 파일입니다

data "aws_caller_identity" "current" {} resource "aws_iam_policy" "external_dns_policy" { name = "${var.ClusterBaseName}ExternalDNSPolicy" description = "Policy for allowing ExternalDNS to modify Route 53 records" policy = jsonencode({ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "route53:ChangeResourceRecordSets" ], "Resource": [ "arn:aws:route53:::hostedzone/*" ] }, { "Effect": "Allow", "Action": [ "route53:ListHostedZones", "route53:ListResourceRecordSets" ], "Resource": [ "*" ] } ] }) } resource "aws_iam_role_policy_attachment" "external_dns_policy_attach" { role = "${var.ClusterBaseName}-node-group-eks-node-group" policy_arn = aws_iam_policy.external_dns_policy.arn depends_on = [module.eks] } resource "aws_security_group" "node_group_sg" { name = "${var.ClusterBaseName}-node-group-sg" description = "Security group for EKS Node Group" vpc_id = module.vpc.vpc_id tags = { Name = "${var.ClusterBaseName}-node-group-sg" } } module "eks" { source = "terraform-aws-modules/eks/aws" version = "~>20.0" cluster_name = var.ClusterBaseName cluster_version = var.KubernetesVersion cluster_endpoint_private_access = false cluster_endpoint_public_access = true cluster_addons = { coredns = { most_recent = true } kube-proxy = { most_recent = true } vpc-cni = { most_recent = true } } vpc_id = module.vpc.vpc_id enable_irsa = true subnet_ids = module.vpc.public_subnets eks_managed_node_groups = { default = { name = "${var.ClusterBaseName}-node-group" use_name_prefix = false instance_type = var.WorkerNodeInstanceType desired_size = var.WorkerNodeCount max_size = var.WorkerNodeCount + 2 min_size = var.WorkerNodeCount - 1 disk_size = var.WorkerNodeVolumesize subnets = module.vpc.public_subnets key_name = var.KeyName vpc_security_group_ids = [aws_security_group.node_group_sg.id] iam_role_name = "${var.ClusterBaseName}-node-group-eks-node-group" iam_role_use_name_prefix = false iam_role_additional_policies = { "${var.ClusterBaseName}ExternalDNSPolicy" = aws_iam_policy.external_dns_policy.arn } } } access_entries = { admin = { kubernetes_groups = [] principal_arn = "${data.aws_caller_identity.current.arn}" policy_associations = { myeks = { policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy" access_scope = { namespaces = [] type = "cluster" } } } } } tags = { Environment = "${var.ClusterBaseName}-lab" Terraform = "true" } }

external dns 를 사용하기위해 선언한 iam policy, polciy attachment

그리고 노드그룹을 선언했습니다.

그리고 EKS를 선언하는데

eks module 사용을 위한 최소버전명시

EKS Provisioning 시간에 배웠던 cluster endpoint 설정과

애드온으로 coredns, kube-proxy, vpc-cni의 최신버전 설치를 진행합니다.

예전에 찾아봤었을때는 EBS, EFS CSI DRIVER는 most recent 파라미터가 없었던걸로 기억이되는데 맞는지 확인을 다시 한번 해봐야겠네요.

vpc 설정과 서브넷 설정을 진행하고

irsa 활성화를하는데, 이 값들은 다음 파일에서 다룹니다.

마지막으로 access_entries 코드가 있는데

뭐지 모르겠어서 검색을해보니 eksctl 사이트에서 정보 제공을 해주네요

https://eksctl.io/usage/access-entries/

Untitled

A deep dive into simplified Amazon EKS access management controls | Amazon Web Services

aws doc의 정보도 같이 첨부해봤습니다.

이제 다시보니 인증/인가 다룰때 공부했던 신기능이네요

configmap에서 기본 설정을 잘못지우면 클러스터에 접근권한을 완벽하게 상실되는 케이스를 보면서 이를 방지하기 위한 기능이였습니다.

Untitled

이런데서 실수로 건들다간…

마지막으론 IRSA입니다

irsa.tf

locals { cluster_oidc_issuer_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(module.eks.cluster_oidc_issuer_url, "https://", "")}" } module "eks-external-dns" { source = "DNXLabs/eks-external-dns/aws" version = "0.2.0" cluster_name = var.ClusterBaseName cluster_identity_oidc_issuer = module.eks.cluster_oidc_issuer_url cluster_identity_oidc_issuer_arn = local.cluster_oidc_issuer_arn enabled = false } module "aws_load_balancer_controller_irsa_role" { source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" version = "5.3.1" role_name = "${var.ClusterBaseName}-aws-load-balancer-controller" attach_load_balancer_controller_policy = true oidc_providers = { ex = { provider_arn = module.eks.oidc_provider_arn namespace_service_accounts = ["kube-system:aws-load-balancer-controller"] } } } provider "kubernetes" { host = module.eks.cluster_endpoint token = data.aws_eks_cluster_auth.cluster.token cluster_ca_certificate = base64decode(module.eks.cluster_certificate_authority_data) } data "aws_eks_cluster" "cluster" { name = module.eks.cluster_name depends_on = [module.eks] } data "aws_eks_cluster_auth" "cluster" { name = module.eks.cluster_name depends_on = [module.eks] } resource "kubernetes_service_account" "aws_lb_controller" { metadata { name = "aws-load-balancer-controller" namespace = "kube-system" annotations = { "eks.amazonaws.com/role-arn" = module.aws_load_balancer_controller_irsa_role.iam_role_arn } } }