IaC

[Terraform]Full Stack 애플리케이션 구성

마손(Mason) 2022. 4. 20. 18:54

위 아키텍처를 terraform을 이용해 작성해볼 것이다. 순서는 다음과 같다.

 

IaC 코드를 작성하는 순서

  1. AWS Management Console을 이용해 최종 인프라 상태를 만든다
  2. 잘 작동하는지 확인한다.
  3. 리소스를 하나씩 terraform 코드로 옮긴다.

VPC 생성

아키텍처를 본다. 한 VPC 안에 두 개의 가용영역이 존재한다. 각 가용영역에는 하나의 퍼블릭 서브넷과 하나의 프라이빗 서브넷이 있다. 그 중 퍼블릭 서브넷에 EC2 인스턴스가 존재하고 프라이빗 서브넷에 RDS가 존재한다. Loadbalancer와 auto scaling group은 나중에 처리한다.

 

콘솔을 활용해 다음과 같이 VPC를 구성한다. 클릭 몇 번이면 되므로 편하다. 손쉽게 구성했지만, 되게 많은 것들이 생성되었고, 서로 연결되었다. 생성된 것을 참고하여 테라폼으로 옮겨보자.

 

//provider.tf 파일
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

# Configure the AWS Provider
provider "aws" {
  region = "ap-northeast-2"
}

위와 같이 porvider를 aws로 사용한다.

# VPC 생성
resource "aws_vpc" "terraform-vpc" {
  tags = {
    Name = "테라폼 VPC"
  }
  cidr_block = "10.0.0.0/16"
  enable_dns_hostnames = true
}

# Public 서브넷 생성
resource "aws_subnet" "public1" {
    availability_zone = "ap-northeast-2a"
    vpc_id = aws_vpc.terraform-vpc.id
    cidr_block = "10.0.0.0/20"
    tags = {
      "Name" = "public subnet1"
    }
}

resource "aws_subnet" "public2" {
    availability_zone = "ap-northeast-2b"
    vpc_id = aws_vpc.terraform-vpc.id
    cidr_block = "10.0.16.0/20"
    tags = {
      "Name" = "public subnet2"
    }
}

#Private 서브넷 생성
resource "aws_subnet" "private1" {
    availability_zone = "ap-northeast-2a"
    vpc_id = aws_vpc.terraform-vpc.id
    cidr_block = "10.0.128.0/20"
    tags = {
      "Name" = "private subnet1"
    }
}

resource "aws_subnet" "private2" {
    availability_zone = "ap-northeast-2b"
    vpc_id = aws_vpc.terraform-vpc.id
    cidr_block = "10.0.144.0/20"
    map_public_ip_on_launch = false
    tags = {
      "Name" = "private subnet2"
    }
}

#인터넷 게이트웨이 생성
resource "aws_internet_gateway" "terraform-igw" {
  vpc_id = aws_vpc.terraform-vpc.id
  tags = {
    Name = "terraform-igw"
  }
}

#eip 생성
resource "aws_eip" "terraform-eip" {
  vpc      = true
}

#NAT 게이트웨이 생성
resource "aws_nat_gateway" "nat" {
  allocation_id = aws_eip.terraform-eip.id
  subnet_id     = aws_subnet.public2.id #NAT가 사용될 서브넷 지정, 여기서는 db 서브넷 그룹? ㄴㄴ 퍼블릭 b
  connectivity_type = "public"

  tags = {
    Name = "terraform Nat gw"
  }
  depends_on = [aws_internet_gateway.terraform-igw]
}

#Public route table 생성
resource "aws_route_table" "public-routing-table-terraform" {
  vpc_id = aws_vpc.terraform-vpc.id
  route {
      cidr_block = "0.0.0.0/0"
      gateway_id = aws_internet_gateway.terraform-igw.id
  }
  tags = {
    "Name" = "public-routing-table"
  }
}

#Private route table 생성
resource "aws_route_table" "private1-routing-table-terraform" {
  vpc_id = aws_vpc.terraform-vpc.id
  route {
      cidr_block = "0.0.0.0/0"
      gateway_id = aws_nat_gateway.nat.id
  }
  tags = {
    "Name" = "private1-routing-table"
  }
}
resource "aws_route_table" "private2-routing-table-terraform" {
  vpc_id = aws_vpc.terraform-vpc.id
  route {
      cidr_block = "0.0.0.0/0"
      gateway_id = aws_nat_gateway.nat.id
  }
  tags = {
    "Name" = "private2-routing-table"
  }
}

#Public Route table 연결
resource "aws_route_table_association" "public1-association" {
  subnet_id = aws_subnet.public1.id
  route_table_id = aws_route_table.public-routing-table-terraform.id
}
resource "aws_route_table_association" "public2-association" {
  subnet_id = aws_subnet.public2.id
  route_table_id = aws_route_table.public-routing-table-terraform.id
}

##Private Route table 연결
resource "aws_route_table_association" "private1-association" {
  subnet_id = aws_subnet.private1.id
  route_table_id = aws_route_table.private1-routing-table-terraform.id
}
resource "aws_route_table_association" "private2-association" {
  subnet_id = aws_subnet.private2.id
  route_table_id = aws_route_table.private2-routing-table-terraform.id
}

위와 같이 네트워크 인프라를 구성한다. 테라폼 문법은 어렵지 않다. terraform registry에 모두 나와있다.

 

기본적인 보안그룹 설정과 ec2, rds도 생성한다.

#ec2 security group 생성
resource "aws_security_group" "public" {
  name        = "public"
  description = "Allow Web, SSH inbound traffic"
  vpc_id      = aws_vpc.terraform-vpc.id

  ingress {
    description      = "SSH"
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
  }
  ingress {
    description      = "HTTP"
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

#DB security group생성
resource "aws_security_group" "private"{
    name        = "private"
    description = "Allow DB inbound traffic"
    vpc_id      = aws_vpc.terraform-vpc.id
    ingress {
        description = "mysql_aurora"
        from_port   = 3306
        to_port     = 3306
        protocol    = "tcp"
        cidr_blocks = ["0.0.0.0/0"]
    }
    egress {
        from_port   = 0
        to_port     = 0
        protocol    = "-1"    # any protocol
        cidr_blocks = ["0.0.0.0/0"]
    }
}
#ec2 생성
resource "aws_instance" "terraform-instance"{
    ami = "ami-092dfb48456a3b119"
    instance_type = "t2.micro"
    key_name = var.key_name
    subnet_id = aws_subnet.public1.id
    vpc_security_group_ids = [aws_security_group.public.id]
    associate_public_ip_address = true
    user_data = file("user_data.sh")
    lifecycle {
        create_before_destroy = true
    }
    tags = {
        "Name" = "terraform-instance"
    }
}
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p 80 &

잘되는지 보기위한 유저데이터를 ec2에 삽입하여 ec2 실행시 hello world를 출력하는 아파치서버 실행

#DB subnet group 생성
resource "aws_db_subnet_group" "db_subnet_group" {
    name = "db_subnet_group"
    subnet_ids = [aws_subnet.private1.id, aws_subnet.private2.id]

    tags = {
      "Name" = "DB subnet group"
    }
}

#DB 생성
resource "aws_db_instance" "db" {
  allocated_storage    = 10
  engine               = "mysql"
  engine_version       = "5.7"
  instance_class       = "db.t2.micro"
  name                 = "terraform_db"
  username             = "admin"
  password             = "12345678"
  parameter_group_name = "default.mysql5.7"
  skip_final_snapshot  = true
  availability_zone = "ap-northeast-2b"
  db_subnet_group_name = aws_db_subnet_group.db_subnet_group.id
  vpc_security_group_ids = [aws_security_group.private.id]
}

자주 쓰이거나 반복적으로 쓰이는 것들은 variables.tf에서 한번에 관리할 수도 있다.

#variables.tf
variable "key_name" { default = "mac-keypair" }

비록 alb를 못달았지만, 시간이 있다면 충분히 공식문서를 보고 해낼 수 있다.

이렇게 테라폼으로 인프라를 코드로 표기하면, 나중에 리전 옮길때나 팀으로 작업하기에 엄청 편리하다. 

중요한 것은 인프라를 테라폼 코드로 옮기는 것이 아니라 어떻게 작동하는지 원리를 익히는 것이라 생각한다.