본문 바로가기

Newb/Terraform

Terraform으로 AWS에서 많이 사용하는 기본 서비스 구성 구축해보기

반응형
CloudNet@ T101 스터디 진행 후 정리한 글 입니다.

목표 서비스 구성도

T101 스터디에서 배운 것들을 정리 할겸 위의 이미지와 같이 가장 기본적으로 많이 쓰이는 서비스의 구성을 테라폼 코드를 이용하여 AWS 서비스를 구축 해보려고 합니다.

 

테라폼 코드들은 생성 이후 수정, 유지보수 등을 위하여 가독성을 높이기 위해 서비스 별로 나누어 총 9개의 파일로 구성하였습니다.

테라폼 구성 파일

$ tree
.
|-- alb.tf
|-- asg.tf
|-- aws.tf
|-- ec2.tf
|-- outputs.tf
|-- rds.tf
|-- sg.tf
|-- variables.tf
`-- vpc.tf

0 directories, 9 files

 

aws.tf

aws.tf 파일에서는 테라폼에서 사용할 프로바이더를 지정하였습니다.

더보기
provider "aws" {
  region  = "ap-northeast-2"
}

 

vpc.tf

vpc.tf 파일에서는 VPC 등 네트워크와 관련 된 내용을 담당하며,

아래와 같이 총 6개의 설정을 넣어두었습니다.

    1. vpc ( 서비스에서 사용할 VPC )
    2. Public subnet ( Bastion Host에서 사용할 인터넷망 )
    3. Private subnet ( Web 서버가 사용할 내부망 )
    4. Nat Gateway ( Web 서버에서 외부로 나갈때 사용할 Nat Gateway )
    5. Internet Gateway ( 인터넷망에서 외부로 나갈때 사용할 Internet Gateway )
    6. RDS subnet ( RDS가 사용할 내부망 )
더보기
## vpc
resource "aws_vpc" "wp-vpc" {
  cidr_block       = "10.10.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
 
  tags = {
    Name = "wp-vpc"
  }
}
 
## public
resource "aws_subnet" "pub-subnet-a" {
  vpc_id     = aws_vpc.wp-vpc.id
  cidr_block = "10.10.1.0/24"
  availability_zone = "ap-northeast-2a"
 
  tags = {
    Name = "pub-subnet-a"
  }
}
 
resource "aws_subnet" "pub-subnet-c" {
  vpc_id     = aws_vpc.wp-vpc.id
  cidr_block = "10.10.10.0/24"
  availability_zone = "ap-northeast-2c"
 
  tags = {
    Name = "pub-subnet-a"
  }
}
 
## private
resource "aws_subnet" "pri-subnet-a" {
  vpc_id     = aws_vpc.wp-vpc.id
  cidr_block = "10.10.2.0/24"
  availability_zone = "ap-northeast-2a"
 
  tags = {
    Name = "pri-subnet-a"
  }
}
 
resource "aws_subnet" "pri-subnet-c" {
  vpc_id     = aws_vpc.wp-vpc.id
  cidr_block = "10.10.20.0/24"
  availability_zone = "ap-northeast-2c"
 
  tags = {
    Name = "pri-subnet-c"
  }
}
 
## Nat Gateway
resource "aws_eip" "nat" {
  vpc = true  
  tags = {
    Name = "wp-natip"
  }
}
 
resource "aws_nat_gateway" "pri-ngw"{
    allocation_id = aws_eip.nat.id
    subnet_id   = aws_subnet.pub-subnet-a.id
    tags = {
        Name = "pri-nat-gw"
    }
}
 
## Internet Gateway
resource "aws_internet_gateway" "pub-igw" {
  vpc_id = aws_vpc.wp-vpc.id
 
  tags = {
    Name = "pub-igw"
  }
}
 
## public rt
resource "aws_route_table" "pub-rt" {
   vpc_id = aws_vpc.wp-vpc.id
  route {
    cidr_block ="0.0.0.0/0"
    gateway_id = aws_internet_gateway.pub-igw.id
  }
 
  tags = {
    Name = "pub-rt"
  }
}
 
resource "aws_route_table_association" "pub-rtassociation1" {
  subnet_id      = aws_subnet.pub-subnet-a.id
  route_table_id =   aws_route_table.pub-rt.id
}
 
resource "aws_route_table_association" "pub-rtassociation2" {
  subnet_id      = aws_subnet.pub-subnet-c.id
  route_table_id = aws_route_table.pub-rt.id
}
 
## private rt
resource "aws_route_table" "pri-rt" {
  vpc_id = aws_vpc.wp-vpc.id
 
  tags = {
    Name = "pri-rt"
  }
}
 
resource "aws_route_table_association" "wp-rtassociation1" {
  subnet_id      = aws_subnet.pri-subnet-a.id
  route_table_id = aws_route_table.pri-rt.id
}
 
resource "aws_route_table_association" "wp-rtassociation2" {
  subnet_id      = aws_subnet.pri-subnet-c.id
  route_table_id = aws_route_table.pri-rt.id
}
 
resource "aws_route" "private_rt_route" {
    route_table_id              = aws_route_table.pri-rt.id
    destination_cidr_block      = "0.0.0.0/0"
    nat_gateway_id              = aws_nat_gateway.pri-ngw.id
}
 
## RDS Subnet
resource "aws_db_subnet_group" "wp-dbsubnet" {
  name       = "wp-dbsubnetgroup"
  subnet_ids = [aws_subnet.pri-subnet-a.id, aws_subnet.pri-subnet-c.id]
 
  tags = {
    Name = "wp DB subnet group"
  }
}

 

ec2.tf

ec2.tf에서는 EC2와 관련 된 코드들을 넣어두었으며,

아래와 같은 역할의 코드를 넣어두었습니다.

  1. ec2에서 사용할 AMI를 지정
  2. Bastion Host 서버 생성
  3. Web 서버 생성
더보기
data "aws_ami" "amazonlinux2" {
  most_recent = true
  filter {
    name   = "owner-alias"
    values = ["amazon"]
  }
 
  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-ebs"]
  }
 
  owners = ["amazon"]
}
 
## Bastion Host
resource "aws_instance" "bastion-ec2" {
 
  depends_on = [
    aws_internet_gateway.pub-igw
  ]
 
  ami                         = data.aws_ami.amazonlinux2.id
  associate_public_ip_address = true
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.bastion-sg.id}"]
  subnet_id                   = aws_subnet.pub-subnet-a.id
  key_name                    = "dhkim"
 
  tags = {
    Name = "bastion-ec2"
  }
}
 
## Web Server
resource "aws_instance" "wordpress" {
  ami                         = data.aws_ami.amazonlinux2.id
  associate_public_ip_address = false
  instance_type               = "t2.micro"
  vpc_security_group_ids      = ["${aws_security_group.wp-sg.id}"]
  subnet_id                   = aws_subnet.pri-subnet-a.id
  key_name                    = "dhkim"
 
  user_data = <<-EOF
              #!/bin/bash
              sudo amazon-linux-extras enable php7.4
              sudo yum install httpd -y
              sudo yum install php-cli php-pdo php-fpm php-json php-mysqlnd -y
              sudo systemctl start httpd
              cd /var/www/html
              sudo wget https://wordpress.org/latest.tar.gz
              sudo tar -xzf latest.tar.gz
              sudo cp /usr/share/httpd/noindex/index.html /var/www/html/index.html
              EOF
 
  user_data_replace_on_change = true
 
  tags = {
    Name = "web-wp"
  }
}

 

sg.tf

sg.tf에서는 SecurityGroup의 내용을 담당하고 있으며,

Bastion Host, Web Server, RDS 등에서 사용할 SecurityGroup을 세팅합니다.

더보기
## bastion SecurityGroup
resource "aws_security_group" "bastion-sg" {
  vpc_id      = aws_vpc.wp-vpc.id
  name        = "bastion SG"
  description = "bastion SG"
}
 
resource "aws_security_group_rule" "bastion-ssh-sg-inbound" {
  type              = "ingress"
  from_port         = var.ssh_port
  to_port           = var.ssh_port
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.bastion-sg.id
}
 
resource "aws_security_group_rule" "bastion-sg-outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.bastion-sg.id
}
 
## Web SecurityGroup
resource "aws_security_group" "wp-sg" {
  vpc_id      = aws_vpc.wp-vpc.id
  name        = "WordPress SG"
  description = "WordPress SG"
}
 
resource "aws_security_group_rule" "ssh-sg-inbound" {
  type              = "ingress"
  from_port         = var.ssh_port
  to_port           = var.ssh_port
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.wp-sg.id
}
 
resource "aws_security_group_rule" "wp-web-sg-inbound" {
  type              = "ingress"
  from_port         = var.http_port
  to_port           = var.http_port
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.wp-sg.id
}
 
resource "aws_security_group_rule" "wp-sg-outbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.wp-sg.id
}
 
## RDS SecurityGroup
resource "aws_security_group" "rds-sg" {
  vpc_id      = aws_vpc.wp-vpc.id
  name        = "WordPress RDS"
  description = "WordPress RDS"
}
 
resource "aws_security_group_rule" "rdssginbound" {
  type              = "ingress"
  from_port         = var.db_port
  to_port           = var.db_port
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.rds-sg.id
}
 
resource "aws_security_group_rule" "rdssgoutbound" {
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.rds-sg.id
}

 

rds.tf

rds.tf 파일에서는 rds의 엔진, 사양 등 기본적인 사항들을 지정하며,

db_name, username, password 는 variables.tf 파일에서 가져옵니다.

더보기
resource "aws_db_instance" "wprds" {
  identifier_prefix      = "wprds"
  engine                 = "mysql"
  allocated_storage      = 10
  instance_class         = "db.t2.micro"
  db_subnet_group_name   = aws_db_subnet_group.wp-dbsubnet.name
  vpc_security_group_ids = [aws_security_group.rds-sg.id]
  skip_final_snapshot    = true
 
  db_name                = var.db_name
  username               = var.db_username
  password               = var.db_password
}

 

alb.tf

alb.tf파일 에서는 LB의 Listener, TargetGroup 등을 지정하며,

위의 ec2.tf에서 만들어진 웹서버도 LB에 포함하도록 설정하였습니다.

더보기
resource "aws_lb" "wp-alb" {
  name               = "wordpress-alb"
  load_balancer_type = "application"
  subnets            = [aws_subnet.pub-subnet-a.id, aws_subnet.pub-subnet-c.id]
  security_groups = [aws_security_group.wp-sg.id]
 
  tags = {
    Name = "wordpress-alb"
  }
}
 
## LB Listener
resource "aws_lb_listener" "wp-http" {
  load_balancer_arn = aws_lb.wp-alb.arn
  port              = 80
  protocol          = "HTTP"
 
  # By default, return a simple 404 page
  default_action {
    type = "fixed-response"
 
    fixed_response {
      content_type = "text/plain"
      message_body = "404: page not found"
      status_code  = 404
    }
  }
}
 
## Target Group
resource "aws_lb_target_group" "wp-alb-tg" {
  name = "wp-alb-tg"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.wp-vpc.id
 
  health_check {
    path                = "/"
    protocol            = "HTTP"
    matcher             = "200-299"
    interval            = 5
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 2
  }
}
 
resource "aws_lb_listener_rule" "wp-albrule" {
  listener_arn = aws_lb_listener.wp-http.arn
  priority     = 100
 
  condition {
    path_pattern {
      values = ["*"]
    }
  }
 
  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.wp-alb-tg.arn
  }
}
 
## EC2 LB에 포함
resource "aws_lb_target_group_attachment" "wp-web"{
    target_group_arn    = aws_lb_target_group.wp-alb-tg.arn
    target_id   = aws_instance.wordpress.id
    port    = 80
}

 

asg.tf

asg.tf에서는 AutoScaling Group에 대한 설정을 해두었습니다.

더보기
resource "aws_launch_configuration" "wp-lauchconfig" {
  name_prefix     = "wp-lauchconfig-"
  image_id        = data.aws_ami.amazonlinux2.id
  instance_type   = "t2.micro"
  security_groups = [aws_security_group.wp-sg.id]
  associate_public_ip_address = false
  key_name                    = "dhkim"
 
  user_data = <<-EOF
              #!/bin/bash
              sudo amazon-linux-extras enable php7.4
              sudo yum install httpd -y
              sudo yum install php-cli php-pdo php-fpm php-json php-mysqlnd -y
              sudo systemctl start httpd
              cd /var/www/html
              sudo wget https://wordpress.org/latest.tar.gz
              sudo tar -xzf latest.tar.gz
              sudo cp /usr/share/httpd/noindex/index.html /var/www/html/index.html
              EOF
 
  # Required when using a launch configuration with an auto scaling group.
  lifecycle {
    create_before_destroy = true
  }
}
 
resource "aws_autoscaling_group" "wp-asg" {
  name                 = "wp-asg"
  launch_configuration = aws_launch_configuration.wp-lauchconfig.name
  vpc_zone_identifier  = [aws_subnet.pri-subnet-a.id, aws_subnet.pri-subnet-c.id]
  min_size             = 0
  max_size             = 10
  default_instance_warmup = 300
  health_check_type    = "ELB"
  target_group_arns    = [aws_lb_target_group.wp-alb-tg.arn]
 
  tag {
    key                 = "Name"
    value               = "wp-asg"
    propagate_at_launch = true
  }
}

 

variables.tf

variables.tf 파일은 파일명을 보면 어떤 역할을 하는지 느낌이 오듯, 자주 사용하는 부분들을 변수처럼 사용이 가능하도록 해주는 파일입니다.

variable 에서는 type에는 number, string 등 다양한 유형의 타입이 있으며, db_username, db_password 세팅 부분은 string 타입을 사용하며, sensitive를 사용하여 테라폼 실행 시 텍스트를 입력받아 값을 지정합니다.

Sensitive

더보기
# ---------------------------------------------------------------------------------------------------------------------
# REQUIRED PARAMETERS
# You must provide a value for each of these parameters.
# ---------------------------------------------------------------------------------------------------------------------
variable "db_username" {
  description = "The username for the database"
  type        = string
  sensitive   = true
}
 
variable "db_password" {
  description = "The password for the database"
  type        = string
  sensitive   = true
}
 
# ---------------------------------------------------------------------------------------------------------------------
# OPTIONAL PARAMETERS
# These parameters have reasonable defaults.
# ---------------------------------------------------------------------------------------------------------------------
variable "db_name" {
  description = "The name to use for the database"
  type        = string
  default     = "tstudydb"
}
 
# ---------------------------------------------------------------------------------------------------------------------
# SG variable
# ---------------------------------------------------------------------------------------------------------------------
variable "ssh_port" {
  description = "The port the server will use for ssh"
  type        = number
  default     = 22
}
 
variable "http_port" {
  description = "The port the server will use for HTTP requests"
  type        = number
  default     = 80
}
 
variable "db_port" {
  description = "The port the server will use for db"
  type        = number
  default     = 3306
}

 

outputs.tf

outputs.tf 파일은 테라폼으로 인프라가 구축 된 뒤 아웃풋 받고 싶은 정보들을 기입하여 두었습니다.

저는 EC2에서 워드프레스를 설치하였으므로, 워드프레스를 처음 세팅하는데 필요한 RDS의 엔드포인트 주소, Bastion Host의 주소 등을 불러오도록 하였습니다.

Output

 

더보기
output "bastion_public_ip" {
  value       = aws_instance.bastion-ec2.public_ip
  description = "The public IP of the Instance"
}
 
output "wordpress_private_ip" {
  value       = aws_instance.wordpress.private_ip
  description = "The public IP of the Instance"
}
 
output "rds_db_instance_address" {
  value       = aws_db_instance.wprds.endpoint
  description = "The address of the RDS instance"
}
 
output "wp-alb_dns" {
  value       = aws_lb.wp-alb.dns_name
  description = "The DNS Address of the ALB"
}

 

구축 완료 된 후 정상 접근 여부 확인

EC2 워드프레스 생성 페이지 접근 확인
Bastion Host 서버에서 Web 서버 접근(통신) 확인

위에서는 서비스 구성을 구축해보는 것이 목표이기 때문에 워드프레스의 소스를 한곳으로 뭉치지 않았지만,

실제로 서비스를 구성한다면 EFS 등을 이용하여 소스를 한 곳으로 몰거나, AutoScaling의 이미지를 워드프레스 구축 후 구운 이미지로 변경하는 등의 작업이 추가로 필요합니다.

반응형

'Newb > Terraform' 카테고리의 다른 글

Trraform으로 지정한 AWS 리소스 비용 계산 해보기  (0) 2022.12.08
Terraform 기본 명령어 & Flow  (0) 2022.11.08
Terraform ?  (0) 2022.10.30