Terraform AWS IaC 작성

Son_Doobu96·2023년 2월 8일
0

sprint Troubleshooting

목록 보기
3/7
post-thumbnail

구성하려는 아키텍처 구성도

진행 단계

▶ STEP 1 VPC 생성

1. VPC 및 서브넷 생성

  • 프라이빗 서브넷과 퍼블릭 서브넷이 각각 두개, 총 네개가 있어야 합니다.

2. VPC 보안 그룹 생성

  • 퍼블릭 웹 서버가 사용할 VPC 보안 그룹을 만들어야 합니다.
  • 프라이빗 DB 웹 서버가 사용할 VPC 보안 그룹을 만들어야 합니다.

3. DB 서브넷 그룹 생성

  • RDS 인스턴스가 사용할 VPC 서브넷 그룹을 만들어야 합니다.

▶ STEP 2 EC2 생성

만들어야 하는 사양

  • AMI: Ubuntu Server 18
  • 인스턴스 타입: t2.micro
  • 사용자 데이터
#!/bin/bash
echo "Hello, World" > index.html
nohup busybox httpd -f -p ${var.server_port} &
  • 키 페어: 수동으로 만들고 EC2에 할당합니다.

Advanced Challenges

STEP 3: 자습서: DB 인스턴스 생성

  • 자습서에 표시된 사양대로 RDS 인스턴스를 생성합니다.

STEP 4: 애플리케이션 로드 밸런서 및 Auto Scaling Group 적용

  • Auto Scaling Group은 최소 2개, 최대 10개로 설정해놓습니다.

이 포스팅에서는 STEP 2번까지만을 다루도록 하겠다.


IaC 작성 참고 : Terraform Registry

▶ 진행 순서

가장 먼저 구성해야 할 아키텍처들이 서로 어떤 연관을 가지고 있고 어떤 연결 구성이 필요한지에 맞춰 Terraform IaC를 구성할 순서를 계획하고 필요한 개념들을 알아봤습니다.

이후 아래의 단계에 맞춰 인스턴스의 생성 및 연결 확인까지의 과정을 진행했습니다.

1. VPC 생성

  • 2개의 가용영역
  • 서울 리전
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

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

# VPC 생성
resource "aws_vpc" "Sprint_Terraform_VPC" {
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "Sprint_Terraform_VPC"}
}   

이렇게 서울 리전에 cidr_block 10.0.0.0/16의 대역대를 가진 VPC를 생성했습니다.

2. Subnet 생성 (퍼블릭2, 프라이빗2)

  • 2개의 인스턴스가 위치할 2개의 서브넷 생성
# 서브넷 생성
resource "aws_subnet" "sprint_public1" {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = "10.0.10.0/24"
  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "sprint_public1"
  }
}

resource "aws_subnet" "sprint_public2" {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = "10.0.11.0/24"
  availability_zone = "ap-northeast-2c"

  tags = {
    Name = "sprint_public2"
  }
}

resource "aws_subnet" "sprint_private1" {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = "10.0.128.0/24"
  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "sprint_private1"
  }
}

resource "aws_subnet" "sprint_private2" {
  vpc_id            = aws_vpc.Sprint_Terraform_VPC.id
  cidr_block        = "10.0.144.0/24"
  availability_zone = "ap-northeast-2c"

  tags = {
    Name = "sprint_private2"
  }
}  

위의 IaC를 통해 서브넷 4개를 구성했습니다. Public 서브넷에는 인스턴스가 위치할 예정이고 프라이빗 서브넷은 그룹화 하여 RDS가 위치할 예정입니다.
가용 영역을 2a와 2c로 선정한 이유는 사용할 인스턴스 유형인 t2.micro가 지원하는 가용 영역이 2a와 2c이기 때문입니다.

3. 보안 그룹 생성 (Public, DB)

  • 인스턴스용, DB용 보안 그룹 생성
# Public 보안그룹 생성 (SSH, HTTP, busybox allow)
resource "aws_security_group" "Sprint_security_PW" {
  name        = "Sprint_security_PW"
  description = "Allow TLS inbound traffic"
  vpc_id      = aws_vpc.Sprint_Terraform_VPC.id

  ingress {
    description      = "SSH from VPC"
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
  }
  ingress {
    description      = "HTTP from VPC"
    from_port        = 80
    to_port          = 80
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
  }
    ingress {
    description      = "busybox from VPC"
    from_port        = 8080
    to_port          = 8080
    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"]
  }

  tags = {
    Name = "Sprint_security_PW"
  }
}

# DB 보안그룹 생성 (MySQL/Aurora allow)
resource "aws_security_group" "Sprint_security_DB" {
  name        = "Sprint_security_DB"
  description = "Allow TLS inbound traffic"
  vpc_id      = aws_vpc.Sprint_Terraform_VPC.id

  ingress {
    description      = "MySQL/Aurora from VPC"
    from_port        = 3306
    to_port          = 3306
    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"]
  }

  tags = {
    Name = "Sprint_security_DB"
  }
}   

이후 각각 인스턴스와 RDS에서 활용할 보안그룹을 생성했습니다.
인스턴스에 접근하기 위한 ssh와 외부 접근을 위한 http, test를 위한 8080 총 3개의 포트를 allow 설정했습니다.

처음에는 Terraform Registry를 참고해 ingress 설정을 아래와 같이 구성했습니다.

cidr_blocks      = [aws_vpc.Sprint_Terraform_VPC.cidr_block]
ipv6_cidr_blocks = [aws_vpc.Sprint_Terraform_VPC.ipv6_cidr_block]

하지만 확인 단계에서 접근이 불가능하였습니다. 이때 보안그룹의 설정이 vpc 대역대에서의 접근만이 가능하도록 설정된 것을 확인하였고 아래와 같이 코드를 수정하여 진행하였습니다. (ipv6는 사용 예정이 없어 삭제했습니다.)

cidr_blocks      = ["0.0.0.0/0"]

4. DB 서브넷 그룹 생성

  • RDS를 서브넷의 한 공간에만 위치시킬 예정이기에 그룹화
# DB 서브넷 그룹 생성
resource "aws_db_subnet_group" "sprint_subnetgroup" {
  name       = "sprint_subnetgroup"
  subnet_ids = [aws_subnet.sprint_private1.id, aws_subnet.sprint_private2.id]

  tags = {
    Name = "sprint_subnetgroup"
  }
}

5. 인터넷 게이트웨이 생성

  • VPC가 외부 통신이 가능하도록 설정
# 인터넷 게이트웨이 생성
resource "aws_internet_gateway" "sprint_gw" {
  vpc_id = aws_vpc.Sprint_Terraform_VPC.id

  tags = {
    Name = "sprint_gw"
  }
}

6. 라우팅 테이블 생성

  • 인터넷 게이트웨이와 라우팅 테이블 연결
# 라우팅 테이블 생성
resource "aws_route_table" "Sprint_route_table" {
  vpc_id = aws_vpc.Sprint_Terraform_VPC.id

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

  tags = {
    Name = "Sprint_route_table"
  }
}   

7. 라우팅 테이블 - 서브넷 public1 & 2 연결

# 라우팅 테이블 - 서브넷 public1 연결
resource "aws_route_table_association" "Connect_rt_p1" {
  subnet_id      = aws_subnet.sprint_public1.id
  route_table_id = aws_route_table.Sprint_route_table.id
}

# 라우팅 테이블 - 서브넷 public2 연결
resource "aws_route_table_association" "Connect_rt_p2" {
  subnet_id      = aws_subnet.sprint_public2.id
  route_table_id = aws_route_table.Sprint_route_table.id
}

처음에는 리소스를 구성하며 인터넷 게이트웨이와 중복 연결을 구성하여 문제가 발생했지만 문제 확인 후 제거하여 진행에 큰 문제는 없었습니다.

8. 키 페어 생성

  • 인스턴스에 접속하기 위한 키페어 생성
#키 페어 생성
resource "aws_key_pair" "sprint_1234" {
key_name = "sprint_1234"
public_key = tls_private_key.rsa.public_key_openssh
}
resource "tls_private_key" "rsa" {
algorithm = "RSA"
rsa_bits  = 4096
}
resource "local_file" "sprint_1234" {
content  = tls_private_key.rsa.private_key_pem
filename = "sprint_1234"
}   

key-pair를 생성하고 로컬에 저장하도록 리소스를 구성했습니다.
이후 ignore 처리를 통해 key-pair를 보호중에 있습니다.

하지만 한가지의 문제점이 있습니다. 바로 destroy 후 다시 생성할 경우 기존 key-pair를 destroy후 새로 생성한다는 점입니다. 접속에 있어 문제는 없지만 보안적으로 문제가 될 수 있기에 추후 해결책을 2편에 같이 포스팅하도록 하겠습니다.

9. EC2 인스턴스 생성
- 각 서브넷에 EC2 인스턴스 생성

# EC2_2a 인스턴스 생성
resource "aws_instance" "sprint_terraform_EC2_2a" {
  ami           = "ami-0cb1d752d27600adb" # ap-northeast-2
  instance_type = "t2.micro"
  associate_public_ip_address = true
  vpc_security_group_ids = [aws_security_group.Sprint_security_PW.id]
  availability_zone = "ap-northeast-2a"
  subnet_id = aws_subnet.sprint_public1.id
  key_name = "sprint_1234"

  credit_specification {
    cpu_credits = "unlimited"
  }

# 사용자 데이터 입력
  user_data = <<-EOF
  #!/bin/bash
  echo "Hello, World" > index.html
  nohup busybox httpd -f -p ${var.server_port} &
  EOF

  depends_on = [
    aws_key_pair.sprint_1234
  ]

    tags = {
    Name = "sprint_terraform_EC2_2a"
  }
}

# EC2_2b 인스턴스 생성
resource "aws_instance" "sprint_terraform_EC2_2c" {
  ami           = "ami-0cb1d752d27600adb" # ap-northeast-2
  instance_type = "t2.micro"
  associate_public_ip_address = true
  vpc_security_group_ids = [aws_security_group.Sprint_security_PW.id]
  availability_zone = "ap-northeast-2c"
  subnet_id = aws_subnet.sprint_public2.id
  key_name = "sprint_1234"


  credit_specification {
    cpu_credits = "unlimited"
  }

# 사용자 데이터 입력
  user_data = <<-EOF
  #!/bin/bash
  echo "Hello, World" > index.html
  nohup busybox httpd -f -p ${var.server_port} &
  EOF
  
  depends_on = [
    aws_key_pair.sprint_1234
  ]

    tags = {
    Name = "sprint_terraform_EC2_2c"
  }
}

Registry 문서를 참고하여 필요한 옵션을 추가하여 구성하였습니다.
퍼블릭 IP에 대한 설정과 보안그룹 가용 영역, key-pair를 지정해 주었습니다.

profile
DevOps를 꿈꾸는 엔지니어 지망생!

0개의 댓글