Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
다음과 같은 AWS 웹서비스 인프라를 테라폼으로만 구성해 보자.
1. VPC 생성
VPC는 논리적으로 자원을 격리시켜 주는 네트워크 리소스로, 클라우드에서도 온프레미스처럼 동일한 네트워크 세팅을 가능하도록 만들어 준다.
VPC는 리전에 속하고, 여러 AZ의 자원들을 묶어줄 수 있다.
provider "aws" {
region = "ap-northeast-2"
}
resource "aws_vpc" "main" {
cidr_block = "10.10.0.0/16" #10.10.0.0/16로 설정
tags = {
Name = "tf-lab-vpc"
}
}
2. 퍼블릭 서브넷 생성
생성한 vpc_id를 사용해서 subnet을 만든다.
서브넷이 실제로 자원들이 배치가 되고 라우팅 테이블이 세팅될 네트워크 집합이 된다.
배치할 AZ를 설정해주고, public ec2 를 배치하기 위해 public ip 할당을 활성화해준다.
resource "aws_subnet" "public_subnet1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.1.0/24"
availability_zone = "ap-northeast-2a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-1"
}
}
resource "aws_subnet" "public_subnet2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.2.0/24"
availability_zone = "ap-northeast-2b"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet-2"
}
}
3. Internet Gateway 생성
인터넷 게이트웨이는 AWS가 완전관리형으로 제공하는 VPC의 인터넷 관문이 된다.
AWS가 가용성과 확장성을 모두 제공하기 때문에 많은 트래픽도 감당할 수 있고, 트래픽에 대해서만 비용을 지불하면 된다.
공인 IP를 가지고 있는 자원이라면, 인터넷 게이트웨이에서 해당 공인 IP로 변환해서 외부로 보내주게 된다. 반대로 공인 IP는 인터넷 게이트웨이에서 사설 IP로 변환되어 들어온다.
따라서 VPC 내부에서는 VPC의 사설 IP만 사용되게 된다.
생성한 vpc_id로 설정한다.
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.main.id
tags = {
Name = "main-igw"
}
}
4. 퍼블릭 서브넷의 라우팅 테이블 생성
//public subnet 라우팅 테이블 생성
resource "aws_route_table" "public_route_table" {
vpc_id = aws_vpc.main.id
tags = {
Name = "public-route-table"
}
}
퍼블릭 서브넷들을 생성한 라우팅 테이블과 연결한다.
이때 사용되는 것이 aws_route_table_association이다.
resource "aws_route_table_association" "route_table_association_1" {
subnet_id = aws_subnet.public_subnet1.id
route_table_id = aws_route_table.public_route_table.id
}
resource "aws_route_table_association" "route_table_association_2" {
subnet_id = aws_subnet.public_subnet2.id
route_table_id = aws_route_table.public_route_table.id
}
이제 해당 라우팅 테이블의 기본 라우팅 경로를 IGW로 세팅해 주어야 외부로 트래픽이 나갈 수 있다.
resource "aws_route" "public_igw" {
route_table_id = aws_route_table.public_route_table.id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
5. 프라이빗 서브넷 생성
프라이빗 서브넷의 자원들은 퍼블릭 IP가 없기 때문에 않아서 외부에서 트래픽이 도달할 수 없다.
만약 서브넷 자원들이 외부에 요청을 보내려면 NAT GW를 통해 퍼블릭 주소로 변환을 해야 한다.
resource "aws_subnet" "private_subnet1" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.128.0/22"
availability_zone = "ap-northeast-2a"
tags = {
Name = "subnet-private-1"
}
}
resource "aws_subnet" "private_subnet2" {
vpc_id = aws_vpc.main.id
cidr_block = "10.10.132.0/22"
availability_zone = "ap-northeast-2b"
tags = {
Name = "subnet-private-2"
}
}
6. NAT 게이트웨이 생성
AWS에서 NAT Gateway는 프라이빗 서브넷에서 외부로 나가는 트래픽을 공인 IP로 변경하여 보내준다. 반대로 외부에서는 프라이빗 서브넷의 자원에 접근할 수 없기 때문에, 보안을 유지하면서 자원들을 인터넷에 액세스 할 수 있도록 해준다.
NAT GW도 VPC외부로 나가려면를 거쳐야 한다.
따라서 다음과 같이 ec2 private ip -> Nat GW의 private ip -> IGW에서는 NAT의 public ip로 IP는 이중 NAT로 바뀌어서 나간다.
NAT Gateway는 AWS에서 완전관리 형으로 제공해 주는 만큼, 트래픽에 대한 대처, 가용성과 이중화를 보장해 주지만 그만큼 비용이 발생한다.
따라서 간단한 실습을 위해서는 EC2 인스턴스를 NAT GW로 활용하기도 한다.
일단 Lifecycle를 통해 리소스의 생명 주기 세팅을 한다.
resource "aws_eip" "nat_1" {
vpc = true
lifecycle {
create_before_destroy = true
}
}
resource "aws_eip" "nat_2" {
vpc = true
lifecycle {
create_before_destroy = true
}
}
만약 새로 적용할 세팅 때문에 기존의 NAT GW를 제거해야 한다면, 외부로의 요청이 중단될 수 있다.
이럴 때 create_before_destroy 세팅을 통해, 기존의 NAT GW를 새로운 NAT GW 생성 전까지 유지시키고 생성이 완료되면 기존의 NAT를 삭제한다.
//NAT GW 배치
resource "aws_nat_gateway" "nat_gateway_1" {
allocation_id = aws_eip.nat_1.id
subnet_id = aws_subnet.public_subnet1.id
tags = {
Name = "NAT-GW-1"
}
}
resource "aws_nat_gateway" "nat_gateway_2" {
allocation_id = aws_eip.nat_2.id
subnet_id = aws_subnet.public_subnet2.id
tags = {
Name = "NAT-GW-2"
}
}
7. 프라이빗 서브넷 라우팅 경로 세팅
프라이빗 서브넷은 기본 GW를 NAT로 가도록 라우팅 테이블을 세팅하면 된다.
라우팅 테이블을 생성한다.
resource "aws_route_table" "route_table_private_1" {
vpc_id = aws_vpc.main.id
tags = {
Name = "route-table-private-1"
}
}
resource "aws_route_table" "route_table_private_2" {
vpc_id = aws_vpc.main.id
tags = {
Name = "route-table-private-2"
}
}
서브넷을 라우팅 테이블과 연결한다.
resource "aws_route_table_association" "route_table_association_private_1" {
subnet_id = aws_subnet.private_subnet1.id
route_table_id = aws_route_table.route_table_private_1.id
}
resource "aws_route_table_association" "route_table_association_private_2" {
subnet_id = aws_subnet.private_subnet2.id
route_table_id = aws_route_table.route_table_private_2.id
}
0.0.0.0/0을 NAT GW로 보내도록 세팅한다.
//기본 경로를 nat로 전송
resource "aws_route" "private_nat_1" {
route_table_id = aws_route_table.route_table_private_1.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_gateway_1.id
}
resource "aws_route" "private_nat_2" {
route_table_id = aws_route_table.route_table_private_2.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat_gateway_2.id
}
8. 퍼블릭 서브넷에 EC2 웹서버 배치
EC2 ubuntu 인스턴스를 public subnet에 배치하여, apache2를 설치하고 웹 서비스에 직접 접근해 보자.
Ec2의 보안 그룹 세팅 문제
resource "aws_security_group" "instance" {
name = var.security_group_name
vpc_id = aws_vpc.main.id
ingress {
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"]
}
}
variable "security_group_name" {
description = "The name of the security group"
type = string
default = "terraform-my-instance"
}
여기서 apache2를 설치하기 위해서 Egress(out bound)도 세팅해주어야 한다는 것이다.
이는 보안그룹이 stateful로 동작하기 때문이다. 이미 설치가 된 httpd 웹서버를 사용하면, SG의 outbound 설정이 없더라도 inbound로 들어온 요청은 outbound rule 검사 없이 그대로 나갈 수 있다.
하지만 서버 자체가 무언가 설치를 위해서 요청을 보내면, SG의 outbound rule을 검사해야 한다.
여기서 웹 콘솔의 디폴트 세팅과 다르게 terraform에서 어떠한 outbound 설정도 없이 생성하면 outbound rule에 모든 트래픽을 허용하는 rule이 들어가지 않는다.
웹 콘솔에서는 원래 SG를 생성하면 outbound에 모든 트래픽 허용 정책이 자동으로 들어가기 때문에 자동으로 나갈 수 있다고 생각할 수 있지만, terraform은 그런 세팅을 해주지 않는다.
따라서 이렇게 outbound 규칙이 있어야만 서버가 외부로 요청을 보낼 수 있다.
EC2 webserver 생성
위의 SG를 사용해서 EC2를 생성해 준다.
Subnet은 public subnet 중에중에 t2.micro를 지원하는 az에 존재하는 public subnet1에 배치하도록 설정한다.
resource "aws_instance" "example" {
ami = "ami-09a7535106fbd42d5"
instance_type = "t2.micro"
subnet_id = aws_subnet.public_subnet1.id //public subnet1에 배치
vpc_security_group_ids = [aws_security_group.instance.id]
user_data = <<-EOF
#!/bin/bash
apt update -y
apt install -y apache2
service start apache2
echo "public subent web server- " > /var/www/html/index.html
service restart apache2
service enable apache2
EOF
user_data_replace_on_change = true
tags = {
Name = "public webserver"
}
}
User_data에서는 apache2를 설치하고 간단한 index.html를 만들고 서버를 시작시켜주었다.
결과 확인
준비된 tf파일을apply 하자.
VPC는 웹 콘솔에서 다음과 같이 세팅을 시각적으로 확인할 수 있다.
웹서버에 public IP로 접속했을 때, 확인이 된다.
'Cloud > Terraform' 카테고리의 다른 글
Terraform으로 AWS EC2 인스턴스 배포하기 (0) | 2024.04.17 |
---|