cloudwithbass

[Terraform] 테라폼 연습하기2: 퍼블릭 서브넷에 인터넷 연결 본문

카테고리 없음

[Terraform] 테라폼 연습하기2: 퍼블릭 서브넷에 인터넷 연결

여영클 2024. 8. 25. 23:40

지난 포스팅에서 VPC와 subnet을 생성해봤습니다. (https://cloudwithbass.tistory.com/43)

이번 포스팅에선 public subnet이 인터넷과 통신할 수 있도록 만들어보겠습니다.

 

인터넷과 통신하기 위해서 다음과 같은 리소스들이 필요합니다.

이름 클릭 시 해당 테라폼 문서로 이동합니다.

  1. IGW (Internet Gateway): VPC와 인터넷이 통신할 수 있게 해주는 모듈입니다.
  2. Route Table: 네트워크의 이동 경로를 정의하는 규칙입니다.
  3. Route table association: Route Table을 서브넷과 연결합니다.

모든 코드는 아래 github에서 확인할 수 있습니다.

https://github.com/Dminus251/practice-terraform/tree/main/demo02-igw_rt_rta

목차


     

    1. IGW 구성하기

    테라폼 문서를 따라 구성해줍니다.

    간단하므로 코드만 보여드린 후 빠르게 넘어가겠습니다.

     

    modules/t-aws-igw/main.tf

    resource "aws_internet_gateway" "igw" {
      vpc_id = var.vpc-id
      tags = {
        Name = var.igw-name
      }
    }

     

    modules/t-aws-igw/variable.tf

    variable "vpc-id"{
      type = string
    }
    
    variable "igw-name"{
      type = string
      default = "practice-igw"
    }

    2. Route Table 구성하기

    modules/t-aws-rt/main.tf

    모든 IPv4 트래픽을 인터넷 게이트웨이로 보낼 것이므로 다음과 같이 작성합니다.

    resource "aws_route_table" "internet" {
      vpc_id = var.vpc-id
    
      route { #route 1: 모든 트래픽을 igw로
        cidr_block = "0.0.0.0/0" #from
        gateway_id = var.igw-id   #to
      }
    
      tags = {
        Name = var.rt-name
      }
    }

    인터넷 게이트웨이로 라우팅하기 위해 인터넷 게이트웨이의 id가 필요합니다. (line 6)

    즉, 다른 모듈의 값이 필요하므로 igw 모듈에서 outputs.tf를 작성해줍니다. 

     

    modules/t-aws-igw/outputs.tf (route table 모듈이 아닙니다)

    output "igw-id" {
      value = aws_internet_gateway.igw.id
    }

     

    다시 route table 모듈로 돌아가서, vars.tf를 작성하겠습니다.

    modules/t-aws-rt/vars.tf

    variable "vpc-id" {
      type = string
    }
    
    variable "igw-id" {
      type = string
    }
    
    variable "rt-name" {
      type = string
      default = "practice-rt"
    }

    3. Route Table Association 구성하기

    • Route Table Association은 어떤 route table을 어떤 subnet에 연결할 지 정의합니다.
    • 따라서 route table의 id와 subnet의 id가 필요합니다.

     

    하지만 여기서 문제가 생깁니다.

    인터넷 트래픽은 private subnet에 접근해선 안 되는데, 기존 서브넷 모듈에선 public과 private 서브넷을 함께 생성하므로 subnet 모듈의 id가 private 서브넷의 id를 포함합니다.

    (기존 서브넷 모듈 코드: https://github.com/Dminus251/practice-terraform/blob/main/demo01-vpc_subnet/modules/t-aws-subnet/main.tf)

    따라서 subnet 모듈을 public과 private 용도로 분리할 필요가 생겼습니다.


    3-1. private subnet

    이번 포스팅에선 private subnet은 라우팅 대상이 아니기 때문에 기존 코드와 유사합니다.

     

    modules/t-aws-private_subnet/main.tf

    resource "aws_subnet" "private_subnets"{
      vpc_id = var.vpc-id
    
      for_each = var.private_subnets
      cidr_block = each.value.cidr_block
      availability_zone = each.value.availability_zone
    
      tags = {
        Name = each.key
      }
    }

     

     

     

    modules/t-aws-private_subnet/main.tf

    variable "vpc-id" {
      type = string
    }
    
    variable "private_subnets" {
      type = map(object({
        cidr_block              = string
        availability_zone       = string
      }))
    
      default = {
        "private_subnet-1" = {
          cidr_block        = "10.0.2.0/24"
          availability_zone = "ap-northeast-2a"
        },
        "private_subnet-2" = {
          cidr_block        = "10.0.4.0/24"
          availability_zone = "ap-northeast-2a"
        }
      }
    }

    3-2 public subnet

    modules/t-aws-public_subnet/main.tf

    private subnet과의 차이점은 map_public_ip_on_launch attribute의 유무입니다.

    default 값은 false인데, 이 속성을 true로 설정하면 이 서브넷에서 생성된 ec2 인스턴스에 자동으로 퍼블릭 ip가 할당됩니다.

    resource "aws_subnet" "public_subnets"{
      vpc_id = var.vpc-id
    
      for_each = var.public_subnets
      cidr_block = each.value.cidr_block
      availability_zone = each.value.availability_zone
      map_public_ip_on_launch = each.value.map_public_ip_on_launch
    
      tags = {
        Name = each.key
      }
    }

     

     

    modules/t-aws-public_subnet/vars.tf

    variable "vpc-id" {
      type = string
    }
    
    variable "public_subnets" {
      type = map(object({
        cidr_block              = string
        availability_zone       = string
        map_public_ip_on_launch = bool
      }))
    
      default = {
        "public_subnet-1" = {
          cidr_block        = "10.0.1.0/24"
          availability_zone = "ap-northeast-2a"
          map_public_ip_on_launch = true
        },
        "public_subnet-2" = {
          cidr_block        = "10.0.3.0/24"
          availability_zone = "ap-northeast-2a"
          map_public_ip_on_launch = true
        }
      }
    }

     

     

    modules/t-aws-public_subnet/outputs.tf

    두 개의 output을 추가했습니다.

    output "public_subnet-length"{
      value = length(var.public_subnets)
    }
    
    output "public_subnet-id" {
      value = [for i in aws_subnet.public_subnets: i.id]
    }

     

    • public_subnet-length는 생성될 public subnet의 길이를 의미합니다.
      • rta(route table association) 모듈에서 이 길이만큼 반복해서 라우팅 테이블을 연결할 것입니다.
    • public_subnet-id 또한 rta에 사용됩니다. 각 퍼블릭 서브넷의 id가 리스트로 저장되어 있습니다.

    4. rta(route table association) 구성하기

    (24.08.27 추가) 이 코드는 모듈 내에서 반복문을 사용해서 코드 가독성이 좋지 않습니다. 따라서 테라폼 연습하기3 포스팅에서 반복문을 루트 모듈에서 사용하도록 수정했습니다. 따라서 해당 포스팅의 코드를 참고 바랍니다.

    https://github.com/Dminus251/practice-terraform/tree/main/demo03-nat

     

    practice-terraform/demo03-nat at main · Dminus251/practice-terraform

    Contribute to Dminus251/practice-terraform development by creating an account on GitHub.

    github.com

     

     

    modules/t-aws-rta/main.tf

    resource "aws_route_table_association" "pulic" {
      count = var.public_subnet-length
      subnet_id = var.public_subnet-id[count.index]
      route_table_id = var.rt-id
    }

    count는 반복문입니다.

    count의 수만큼 이 리소스를 생성합니다.

     

    modules/t-aws-rta/vars.tf

    variable "public_subnet-id" {
      type = list(string)
    }
    
    variable "rt-id" {
      type = string
    }
    
    variable "public_subnet-length" {
      type = number
    }

    rta는 라우팅 테이블과 서브넷을 연결하므로 각각의 id가 필요합니다.

    public_subnet-length는 퍼블릭 서브넷의 길이를 할당할 변수입니다.

     

    5. 루트 모듈 구성하기

    최종적으로 완성된 루트 모듈의 main.tf입니다.

    module "vpc" {
      source = "./modules/t-aws-vpc"
    }
    
    module "public_subnet" {
      source = "./modules/t-aws-public_subnet"
      vpc-id = module.vpc.vpc-id
    }
    
    module "private_subnet" {
      source = "./modules/t-aws-private_subnet"
      vpc-id = module.vpc.vpc-id
    }
    
    module "igw" {
      source = "./modules/t-aws-igw"
      vpc-id = module.vpc.vpc-id
    }
    
    module "route_table" {
      source = "./modules/t-aws-rt"
      vpc-id = module.vpc.vpc-id
      igw-id = module.igw.igw-id
    }
    
    module "rta-internet_to_public_subnet" {
      source = "./modules/t-aws-rta"
      public_subnet-length = module.public_subnet.public_subnet-length
      public_subnet-id = module.public_subnet.public_subnet-id
      rt-id = module.route_table.rt-id
    }

    main.tf에선 각 모듈에서 정의한 variable에 값을 할당해야 합니다.

    rta-internet_to_public_subnet 모듈을 제외하곤 간단하니, 이 부분만 설명하겠습니다.


    public_subnet-length = module.public_subnet.public_subnet-length

     

    • 퍼블릭 서브넷 모듈의 output인 public_subnet-length를 할당합니다. (다홍색)
    • 이 다홍색 output은 퍼블릭 서브넷 리스트의 길이 ( = 퍼블릭 서브넷 개수)가 할당되어 있습니다.
      • 아래 코드인 modules/t-aws-public_subnet/outputs.tf를 참고해 주세요.
    #modules/t-aws-public_subnet/outputs.tf 중 일부
    output "public_subnet-length"{
      value = length(var.public_subnets)
    } #var.public_subnets에는 퍼블릭 서브넷들이 list(object)타입으로 default로 있음

     

     


    public_subnet-id = module.public_subnet.public_subnet-id

    • 좌측 다홍색은 rta 모듈의 variable이고, 우측 파란색은 public_subnet 모듈의 output입니다.
      • modules/t-aws-public_subnet/outputs.tf의 public_subnet-id를 참조해주세요.
    • 이 output은 list 타입이며, 각 인덱스에는 퍼블릭 서브넷의 id들이 할당되어 있습니다. 
    • 따라서 public_subnet-id 변수에는 리스트가 할당됩니다.
    • modules/t-aws-rta/main.tf의 코드 중 subnet_id = var.public_subnet-id[count.index] 코드가 인덱스를 반복하며 모든 퍼블릭 서브넷에 라우팅 테이블을 할당할 것입니다.
    • 즉, 이번 예제에서 count = 2니까 rta를 두 번 반복해서 생성합니다.
      • public_subnet-id[0]과 라우팅 테이블을 연결하는 rta
      • public_subnet-id[1]과 라우팅 테이블을 연결하는 rta
    #modules/t-aws-rta/main.tf
    resource "aws_route_table_association" "pulic" {
      count = var.public_subnet-length
      subnet_id = var.public_subnet-id[count.index] #이곳!
      route_table_id = var.rt-id
    }

     

     

    terraform apply 명령 결과, 퍼블릭 서브넷에 라우팅 테이블이 잘 연결되어 있는 것을 확인할 수 있습니다.

     


    코드를 모듈화하면 코드가 분산돼서 글로 설명하기엔 한계가 있는 것 같습니다.

    최대한 자세히 작성했으나, 혹시 이해가 안 되거나 궁금한 점이 있다면 언제든 댓글로 남겨주세요!