JUST WRITE

[Terraform] AWS EKS 한 번에 올리기(1) - VPC 구성 본문

Cloud

[Terraform] AWS EKS 한 번에 올리기(1) - VPC 구성

천재보단범재 2024. 11. 3. 20:48

Terraform - VPC 구성

AWS EKS 한 번에 올리기(1)

이전에 Terraform을 통해서 Kubernetes 클러스터를 한 번에 구성한 적이 있습니다.

AWS EC2 인스턴스를 여러 개 생성해서 클러스터를 구성하였습니다.

 

명령어 한 번에 Kubernetes 설치하기 - Terraform으로 Kubernetes 설치

명령어 한 번에 Kubernetes 설치하기 개발 환경을 자주 구성하다 보니 Kubernetes Cluster를 구성하는 일이 많았습니다. Kubernetes Cluster 구성은 단계도 많고 쉽지 않아 할 때마다 새로웠습니다. 구성을 간

developnote-blog.tistory.com

EC2 인스턴스에 kubeadm를 이용해서 클러스터를 구성하였는데, AWS EKS라는 서비스가 존재합니다.

이번 포스팅에서는 Terraform을 통해서 AWS EKS를 손쉽게 구성해 보도록 하겠습니다.

이번 포스팅은 테라폼을 사용할 줄 아시는 분이 독자라 생각하고 작성하였습니다.

What is EKS?

EKS는 Elastic Kubernetes Service입니다.

명칭대로 AWS에서 제공하는 쿠버네티스 서비스입니다.

AWS 콘솔에서 손쉽게 구성할 수 있으며 다른 AWS 서비스와 쉽게 통합이 가능합니다.

쿠버네티스 클러스터에서 컨트롤 플레인 컴포넌트들은 Worker 노트와 Pod를 관리하는 중요한 역할을 합니다.

Kubernetse Control Plane

  • kube-apiserver
  • etcd
  • kube-scheduler
  • kube-controller-manager
  • cloud-controller-manager

EKS는 위 컨트롤 플레인 컴포넌트들을 직접 구성하지 않고 쿠버네티스 환경을 경험할 수 있게 해 줍니다.

사용자는 쿠버네티스 클러스터 관리 리소스를 최소화할 수 있습니다.

클러스터 규모도 자동으로 조정해 주기 때문에 리소스 관리 비용도 최소화할 수 있습니다.

AWS EKS

Terraform으로 VPC 구성

이제 테라폼을 활용하여 VPC를 구성해 보겠습니다.

VPC는 Virtual Private Cloud로 논리적으로 격리된 가상 네트워크입니다.

EKS용 VPC를 구성하여 EKS에 실행되는 서비스를 보안적으로 안전하게 구성할 수 있습니다.

퍼블릭 서브넷, 프라이빗 서브넷을 VPC에 구성해서 안전하게 구성합니다.

내부 통신만 필요한 것은 프라이빗 서브넷에 두어 외부에서 접근하지 못하도록 합니다.

AWS 권한 설정

테라폼으로 구성하기 전에 AWS 권한 설정이 필요합니다.

VPC를 구성할 수 있는 권한이 필요합니다.

AWS 콘솔 IAM 메뉴에서 AmazonEC2FullAcess 권한을 가진 유저를 생성합니다.

유저 생성 시 만들어지는 accesskey, secretkey를 따로 저장합니다.

AWS IAM - 유저 생성

테라폼을 실행할 서버에 AWS credentials 파일을 생성합니다.

credentials 파일에 명시된 유저의 권한으로 테라폼이 실행됩니다.

[eks]
aws_access_key_id = ************
aws_secret_access_key = ************************************

Terraform code

VPC 구성을 위한 테라폼 코드(tf 파일)는 아래와 같습니다.

VPC만 생성하는 것이 아니라 VPC 내에 필요한 구성요소들이 명시되어 있습니다.

아래 요소들이 아래 테라폼으로 구성됩니다.

  • VPC
  • public subnet, private subnet
  • elastic IP
  • internet gateway
  • nat gateway
  • route table
provider "aws" {
  region = "ap-northeast-2"
  shared_credentials_files = [".aws/credentials"]
  profile = "eks"
}

locals {
  azs             = ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"]
  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"]
}

resource "aws_vpc" "eks_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_hostnames = true

  tags = {
    Name = "eks-vpc"
  }
}

resource "aws_subnet" "eks_private_subnets" {
  count      = length(local.private_subnets)
  vpc_id     = aws_vpc.eks_vpc.id
  cidr_block = local.private_subnets[count.index]
  availability_zone = local.azs[count.index]

  tags = {
    Name = "eks-vpc-private-subnet-${count.index}"
  }
}

resource "aws_subnet" "eks_public_subnets" {
  count      = length(local.public_subnets)
  vpc_id     = aws_vpc.eks_vpc.id
  cidr_block = local.public_subnets[count.index]
  availability_zone = local.azs[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "eks-vpc-public-subnet-${count.index}"
  }
}

resource "aws_eip" "lb" {
  domain = "vpc"

  tags = {
    Name = "eks-eip-lb"
  }
}

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.eks_vpc.id

  tags = {
    Name = "eks-igw"
  }
}

resource "aws_nat_gateway" "ng" {
  allocation_id = aws_eip.lb.id
  subnet_id = aws_subnet.eks_public_subnets[0].id

  tags = {
    Name = "eks-nat-gateway"
  }
}

resource "aws_route_table" "public_route_table" {
  vpc_id = aws_vpc.eks_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "eks-public-route-table"
  }
}

resource "aws_route_table" "private_route_table" {
  vpc_id = aws_vpc.eks_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.ng.id
  }

  tags = {
    Name = "eks-private-route-table"
  }
}

resource "aws_route_table_association" "a" {
  count = length(aws_subnet.eks_public_subnets)

  subnet_id      = aws_subnet.eks_public_subnets[count.index].id
  route_table_id = aws_route_table.public_route_table.id
}

resource "aws_route_table_association" "b" {
  count = length(aws_subnet.eks_private_subnets)

  subnet_id      = aws_subnet.eks_private_subnets[count.index].id
  route_table_id = aws_route_table.private_route_table.id
}

테라폼 코드 부분별로 간단하게 살펴보겠습니다.

AWS Provider를 통해 테라폼에서 AWS 리소스들을 생성할 수 있습니다.

terraform aws provider

리소스를 생성할 리전과 credentials 파일 위치를 설정하였습니다.

변수 설정에서는 생성할 VPC내 가용영역과 IP대역대를 지정하였습니다.

해당 VPC에는 3개의 가용영역에 각각 퍼블릭 서브넷과 프라이빗 서브넷을 구성할 예정입니다.

provider "aws" {
  region = "ap-northeast-2"
  shared_credentials_files = [".aws/credentials"]
  profile = "eks"
}

locals {
  azs             = ["ap-northeast-2a", "ap-northeast-2b", "ap-northeast-2c"]
  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"]
}

다음은 VPC와 서브넷 구성입니다.

VPC 대역대를 10.0.0.0/16으로 지정하여 IP를 여유롭게 사용할 수 있도록 합니다.

DNS 호스트 네임을 활성해서 퍼블릭 IP주소가 할당될 때 DNS 주소를 제공하도록 설정합니다.

서브넷 대역대는 VPC 대역대 내에서 잘 분할하여 설정합니다.

3개의 가용영역에 적절하게 분배해서 설정합니다.

resource "aws_vpc" "eks_vpc" {
  cidr_block = "10.0.0.0/16"
  enable_dns_hostnames = true

  tags = {
    Name = "eks-vpc"
  }
}

resource "aws_subnet" "eks_private_subnets" {
  count      = length(local.private_subnets)
  vpc_id     = aws_vpc.eks_vpc.id
  cidr_block = local.private_subnets[count.index]
  availability_zone = local.azs[count.index]

  tags = {
    Name = "eks-vpc-private-subnet-${count.index}"
  }
}

resource "aws_subnet" "eks_public_subnets" {
  count      = length(local.public_subnets)
  vpc_id     = aws_vpc.eks_vpc.id
  cidr_block = local.public_subnets[count.index]
  availability_zone = local.azs[count.index]
  map_public_ip_on_launch = true

  tags = {
    Name = "eks-vpc-public-subnet-${count.index}"
  }
}

외부에서 접근할 수 있도록 EIP(Elastic IP)를 생성하여 고정 퍼블릭 IP를 만듭니다.

인터넷 게이트웨이를 생성하여 VPC과 인터넷 간의 통신이 가능하도록 합니다.

NAT 게이트웨이를 통해 프라이빗 서브넷에서도 인터넷을 접근(아웃바운드)할 수 있도록 생성합니다.

 

resource "aws_eip" "lb" {
  domain = "vpc"

  tags = {
    Name = "eks-eip-lb"
  }
}

resource "aws_internet_gateway" "gw" {
  vpc_id = aws_vpc.eks_vpc.id

  tags = {
    Name = "eks-igw"
  }
}

resource "aws_nat_gateway" "ng" {
  allocation_id = aws_eip.lb.id
  subnet_id = aws_subnet.eks_public_subnets[0].id

  tags = {
    Name = "eks-nat-gateway"
  }
}

라우트 테이블 생성하여 퍼블릭 서브넷과 프라이빗 서브넷에 맞춰서 연결합니다.

resource "aws_route_table" "public_route_table" {
  vpc_id = aws_vpc.eks_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.gw.id
  }

  tags = {
    Name = "eks-public-route-table"
  }
}

resource "aws_route_table" "private_route_table" {
  vpc_id = aws_vpc.eks_vpc.id

  route {
    cidr_block = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.ng.id
  }

  tags = {
    Name = "eks-private-route-table"
  }
}

resource "aws_route_table_association" "a" {
  count = length(aws_subnet.eks_public_subnets)

  subnet_id      = aws_subnet.eks_public_subnets[count.index].id
  route_table_id = aws_route_table.public_route_table.id
}

resource "aws_route_table_association" "b" {
  count = length(aws_subnet.eks_private_subnets)

  subnet_id      = aws_subnet.eks_private_subnets[count.index].id
  route_table_id = aws_route_table.private_route_table.id
}

 

테라폼 실행

테라폼 코드 작성이 완료되었으면 테라폼을 실행합니다.

테라폼으로 AWS 리소스를 생성하기 전 init 명령어를 통해 로컬에 aws provider를 설치합니다.

$ terraform init

.terraform/providers/registry.terraform.io/hashicorp/aws

apply 명령어를 통해 테라폼 코드를 작성한대로 AWS 리소스를 생성합니다.

$ terraform apply

Plan: 18 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.eks_vpc: Creating...
aws_eip.lb: Creating...
aws_eip.lb: Creation complete after 1s [id=eipalloc-0c438a05dbfb516c9]
aws_vpc.eks_vpc: Still creating... [10s elapsed]
aws_vpc.eks_vpc: Creation complete after 12s [id=vpc-08546d14e33143fc2]
aws_internet_gateway.gw: Creating...
aws_subnet.eks_public_subnets[2]: Creating...
aws_subnet.eks_public_subnets[0]: Creating...
aws_subnet.eks_private_subnets[2]: Creating...
aws_subnet.eks_private_subnets[0]: Creating...
aws_subnet.eks_private_subnets[1]: Creating...
aws_subnet.eks_public_subnets[1]: Creating...
aws_internet_gateway.gw: Creation complete after 1s [id=igw-0925ec8acc4cf4eb9]
aws_route_table.public_route_table: Creating...
aws_subnet.eks_private_subnets[0]: Creation complete after 1s [id=subnet-0dc7696400c64ee6a]
aws_subnet.eks_private_subnets[1]: Creation complete after 1s [id=subnet-0ad16d11e629ea4fc]
aws_subnet.eks_private_subnets[2]: Creation complete after 1s [id=subnet-002dc8a5f514902ab]
aws_route_table.public_route_table: Creation complete after 0s [id=rtb-0edf0e378dcac2344]
aws_subnet.eks_public_subnets[0]: Still creating... [10s elapsed]
aws_subnet.eks_public_subnets[2]: Still creating... [10s elapsed]
aws_subnet.eks_public_subnets[1]: Still creating... [10s elapsed]
aws_subnet.eks_public_subnets[0]: Creation complete after 11s [id=subnet-0716da3c33da7d83b]
aws_nat_gateway.ng: Creating...
aws_subnet.eks_public_subnets[1]: Creation complete after 11s [id=subnet-0a22fde8e2c65d603]
aws_subnet.eks_public_subnets[2]: Creation complete after 11s [id=subnet-05d18452d0c126bb8]
aws_route_table_association.a[0]: Creating...
aws_route_table_association.a[2]: Creating...
aws_route_table_association.a[1]: Creating...
aws_route_table_association.a[0]: Creation complete after 1s [id=rtbassoc-084e25393b0fede4a]
aws_route_table_association.a[1]: Creation complete after 1s [id=rtbassoc-071910372228a5d37]
aws_route_table_association.a[2]: Creation complete after 1s [id=rtbassoc-0883eabf4615e371b]
aws_nat_gateway.ng: Still creating... [10s elapsed]
aws_nat_gateway.ng: Still creating... [20s elapsed]
aws_nat_gateway.ng: Still creating... [30s elapsed]
aws_nat_gateway.ng: Still creating... [40s elapsed]
aws_nat_gateway.ng: Still creating... [50s elapsed]
aws_nat_gateway.ng: Still creating... [1m0s elapsed]
aws_nat_gateway.ng: Still creating... [1m10s elapsed]
aws_nat_gateway.ng: Still creating... [1m20s elapsed]
aws_nat_gateway.ng: Still creating... [1m30s elapsed]
aws_nat_gateway.ng: Still creating... [1m40s elapsed]
aws_nat_gateway.ng: Still creating... [1m50s elapsed]
aws_nat_gateway.ng: Still creating... [2m0s elapsed]
aws_nat_gateway.ng: Still creating... [2m10s elapsed]
aws_nat_gateway.ng: Creation complete after 2m14s [id=nat-0f1e77a57fae54110]
aws_route_table.private_route_table: Creating...
aws_route_table.private_route_table: Creation complete after 1s [id=rtb-0688a4d80ce4e125f]
aws_route_table_association.b[0]: Creating...
aws_route_table_association.b[1]: Creating...
aws_route_table_association.b[2]: Creating...
aws_route_table_association.b[0]: Creation complete after 1s [id=rtbassoc-0b1cef740bd15a0b0]
aws_route_table_association.b[2]: Creation complete after 1s [id=rtbassoc-0705d817199bc9013]
aws_route_table_association.b[1]: Still creating... [10s elapsed]
aws_route_table_association.b[1]: Creation complete after 14s [id=rtbassoc-024f88f74a71b09ef]

Apply complete! Resources: 18 added, 0 changed, 0 destroyed.

정상적으로 생성이 되었으면 AWS 콘솔에서 VPC 생성이 확인 가능합니다.

EKS용 VPC가 생성되었고 3개의 가용영역에 각각 2개의 서브넷이 생성된 것을 확인할 수 있습니다.

퍼블릭 서브넷 3개, 프라이빗 서브넷 3개 총 6개의 서브넷이 생성된 것을 확인할 수 있습니다.

각 서브넷 대역대가 겹치지 않는 것을 확인할 수 있습니다.

라우트 테이브에 보면 각 인터넷 게이트웨이와 NAT 게이트웨이 설정된 것을 확인할 수 있습니다.

프라이빗 서브넷에 EC2를 생성해보았습니다.

EC2가 생성되고 퍼블릭 IP가 설정되어 있지 않은 것을 확인할 수 있습니다.

 

정리

이번 포스팅에서는 테라폼을 통해 AWS VPC를 구성해 보았습니다.

VPC를 위한 테라폼 코드를 작성해 보면서 VPC의 구성요소에 대해 정리해 볼 수 있었습니다.

다음 포스팅에서는 테라폼을 통해 AWS EKS를 구성해 보겠습니다.

[참고사이트]

더보기
728x90
반응형
Comments