일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- clusterrolebinding
- 테라폼
- 클러스터 보안 그룹
- fruition
- helm_release
- 코드커버리지
- aws-loadbalacner-controller
- Gateway
- assumerole
- IRSA
- docker
- jenkins
- Pipeline
- s3
- httpasswd
- NAT
- node group
- Terraform
- saa-c03 #saa #aws certified solutions architect - associate
- 에이전트 구성
- aws ses #aws lambda
- kubernetes
- route53
- instances failed to join cluster
- 에이전트 유형
- 추가 보안 그룹
- ingestion
- Amazon CloudWatch
- Docker0
- Service Account
- Today
- Total
cloudwithbass
[Terraform] 테라폼 연습하기3: NAT gateway 본문
지난 포스팅에서 public subnet을 인터넷과 통신하도록 구성했습니다. (https://cloudwithbass.tistory.com/44)
이번 포스팅에선 private subnet이 인터넷에 연결할 수 있도록 구성할 것입니다.
전체 코드는 https://github.com/Dminus251/practice-terraform/tree/main/demo03-nat에서 확인하실 수 있습니다.
목차
1. NAT에 관해서
왜 private subnet이 인터넷에 연결해야 할까?
- private subnet은 보안을 위해 인터넷에서 온 트래픽을 허용해선 안 됩니다.
- 하지만 private subnet에서 인터넷으로 나가는 트래픽을 허용해야 하는 경우가 있습니다.
- 패키지 설치/업데이트 (apt update, apt upgrade 등), api 호출 등
- 이런 상황에서 NAT gateway가 사용됩니다.
NAT gateway란?
- 프라이빗 서브넷에서 인터넷으로의 연결을 지원하는 네트워크 주소 변환 서비스입니다.
- 이를 통해 프라이빗 서브넷의 인스턴스는 인터넷에 접속할 수 있지만, 외부 인터넷에서 프라이빗 서브넷으로의 직접적인 연결은 차단됩니다.
NAT gateway 종류
- public NAT gateway: 프라이빗 서브넷의 인스턴스를 인터넷에 접속할 수 있도록 합니다.
- private NAT gateway: 프라이빗 서브넷의 인스턴스를 다른 VPC나 온프레미스에 접속할 수 있도록 합니다.
따라서 다음과 같은 리소스들이 필요합니다.
- public NAT Gateway: 인터넷과 연결해야 하므로 public subnet에 배치합니다.
- elastic IP: NAT gateway가 인터넷과 통신할 주소가 필요합니다.
- route table과 route table association: 인터넷에서 온 트래픽을 nat로 라우팅하는 라우팅 테이블을 생성하고, 이 라우팅 테이블을 private subnet에 연결해야 합니다.
1. Elastic IP 구성하기
테라폼 문서를 참고해 Elastic IP 리소스를 생성합니다.
modules/t-aws-eip/main.tf
resource "aws_eip" "eip" {
count = var.private_subnet-length
tags = {
Name = var.eip-name
}
}
private subnet의 수만큼 eip를 생성할 것이기 때문에 반복문을 사용했습니다.
modules/t-aws-eip/output.tf
output "eip-id" {
value = aws_eip.eip.id
}
NAT gateway를 생성할 때 사용할 eip의 id입니다.
modules/t-aws-eip/output.tf
variable "eip-name" {
type = string
default = "practice-eip"
}
모듈의 재사용성을 위해 변수를 사용해 이름을 생성합니다.
2. NAT gateway 구성하기
코드가 간단하므로 바로 넘어가겠습니다.
modules/t-aws-nat/main.tf
resource "aws_nat_gateway" "nat" {
allocation_id = var.eip-id #eip의 id
subnet_id = var.subnet-id #subnet id
tags = {
Name = var.nat-name
}
}
modules/t-aws-nat/vars.tf
variable "eip-id" {
type = string
}
variable "subnet-id" {
type = string
}
variable "nat-name" {
type = string
default = "practice-nat"
}
modules/t-aws-nat/outputs.tf
output "nat-id" {
value = aws_nat_gateway.nat.id
}
3. Route Table 구성하기
인터넷에서 들어오는 트래픽을 NAT gateway로 라우팅합니다. (0.0.0.0/0 → NAT)
코드는 이전 포스팅과 유사하지만, 가독성을 위해 변수 이름을 수정했고(igw-id → gateway-id), Name tag를 용도에 따라 설정하도록 수정했습니다.
modules/t-aws-rt/main.tf
resource "aws_route_table" "internet" {
vpc_id = var.vpc-id
route {
cidr_block = "0.0.0.0/0" #from
gateway_id = var.gateway-id #to
}
tags = {
Name = "prctice-rt-${var.rt-usage}"
}
}
modules/t-aws-rt/vars.tf
variable "vpc-id" {
type = string
}
variable "gateway-id" {
type = string
}
variable "rt-usage" {
type = string
}
modules/t-aws-rt/outputs.tf
output "rt-id"{
value = aws_route_table.internet.id
}
route table association에서 사용하기 위해 output으로 id를 사용합니다.
4. RTA(Route Table Association) 구성하기
이전 포스팅에선 모듈 내에서 count를 사용했지만, 코드의 일관성과 확장성을 높이기 위해 count를 루트 모듈에서만 사용하도록 수정했습니다. 따라서 rta 모듈 내에서 count를 사용하지 않습니다.
modules/t-aws-rta/main.tf
resource "aws_route_table_association" "rta" {
subnet_id = var.subnet-id
route_table_id = var.rt-id
}
variable "subnet-id" {
type = string
}
modules/t-aws-rta/vars.tf
variable "subnet-id" {
type = string
}
variable "rt-id" {
type = string
}
5. 루트 모듈 구성하기
이번 포스팅에서 추가된 부분만 다루겠습니다.
전체 코드는 깃허브 코를 참고 바랍니다.
https://github.com/Dminus251/practice-terraform/blob/main/demo03-nat/main.tf
5-1. Eastic IP Module
module "eip" { #Elastic IP
source = "./modules/t-aws-eip"
count = module.private_subnet.private_subnet-length
}
line 3 count = module.private_subnet.private_subnet-length
- private_subnet-length output은 프라이빗 서브넷 리스트의 길이를 반환합니다.
- 현재 default로 두 개의 프라이빗 서브넷이 존재하므로 길이는 2가 될 것이고, 따라서 eip도 2개 생성합니다.
- NAT gateway는 eip가 필요한데, 가용성을 위해 프라이빗 서브넷 당 NAT를 하나씩 구성할 것입니다.
5-2. NAT Gateway Module
module "nat" { #NAT Gateway
source = "./modules/t-aws-nat"
count = module.private_subnet.private_subnet-length
eip-id = module.eip[count.index].eip-id
subnet-id = module.public_subnet.public_subnet-id[count.index] #nat는 public subnet에 위치해야 함
}
line 3 count = module.private_subnet.private_subnet-length
- 5-1과 같은 이유로 private subnet의 개수만큼 NAT Gateway를 생성합니다.
line 4 eip-id = module.eip[count.index].eip-id
- module.eip[count.index].eip-id를 사용했습니다.
- 이건 루트 모듈에서 count를 사용해 eip를 생성했기 때문입니다.
- terraform apply 후 terraform.tfstate 파일에서, 테라폼이 관리하는 리소스들의 내용을 확인할 수 있습니다.
위 사진처럼 module.eip[index] 꼴로 리소스 목록에 접근할 수 있기 때문에 module.eip[count.index].eip-id를 사용한 것입니다. 마지막에 참조하는 eip-id는 eip 모듈의 output입니다.
line 5 subnet-id = module.public_subnet.public_subnet-id[count.index]
- NAT Gateway는 인터넷 트래픽을 받아야 하므로 public subnet에 위치해야 합니다.
- NAT Gateway 리소스에서 subnet-id attribute는 NAT가 위치할 서브넷을 지정할 때 사용하므로 public subnet을 사용해야 합니다.
Q. line 4에선 module.eip[count.index].eip-id 처럼 리소스에 인덱스로 접근했는데, 왜 line 5에서는 module.public_subnet.public_subnet-id[count.index]처럼 output에 인덱스로 접근하나요?
A.
public subnet은 모듈 내에서 반복문을 사용했으며, output이 string type입니다.
반면 eip는 루트 모듈에서 반복문을 사용했으며, output은 string type입니다.
두 방법 모두 정상적으로 작동하나, 후자의 방식이 유연성, 확장성, 재사용성 측면에서 더 유용하다고 생각하여 이번 포스팅부터는 루트 모듈 내에서 count를 사용합니다.
5-3. Route Table
module "route_table-internet_to_nat" { #Route Internet Traffic to NAT
source = "./modules/t-aws-rt"
count = module.private_subnet.private_subnet-length
vpc-id = module.vpc.vpc-id
gateway-id = module.nat[count.index].nat-id
rt-usage = "nat"
}
line 3 count = module.private_subnet.private_subnet-length
- 5-1과 같은 이유로 private subnet 수만큼 라우트 테이블을 생성합니다.
line 4 vpc-id = module.vpc.vpc-id
- Route Table을 생성할 vpc의 id입니다.
line 5 gateway-id = module.nat[count.index].nat-id
- NAT gateway를 루트 모듈에서 반복문을 사용해 생성했으므로 리소스에 인덱스로 접근해 nat-id output을 사용합니다.
line 6 rt-usage = "nat"
- 라우트 테이블의 NAme tag에 사용될 변수입니다.
5-4. Route Table Association
module "rta-internet_to_nat" {
source = "./modules/t-aws-rta"
count = module.private_subnet.private_subnet-length #이만큼 반복해서 생성
subnet-id = module.private_subnet.private_subnet-id[count.index]
rt-id = module.route_table-internet_to_nat[count.index].rt-id
}
line 3 count = module.private_subnet.private_subnet-length
- 5-1과 같은 이유로 private subnet 개수만큼 route table association을 생성합니다.
- 그런데.. 지금 당장은 문제가 없지만, 지금 생각해보니 항상 private subnet 수만큼 rta를 생성하는 것은 아닐 테니까 추후 인프라 확장 시 문제가 생길 것 같기도 합니다.
line 4, line 5
- 각 index의 subnet에 route table을 연결합니다.
6. 테스트 하기
테스트를 위해 다음과 같이 두 인스턴스를 구성했습니다.
ec2-bastion
- public_subnet-1 서브넷에 생성했습니다.
- public subnet에 위치하므로 인터넷의 ssh를 허용합니다.
ec2-private
- private_subnet-2 서브넷에 생성했습니다.
- ec2-bastion이 속한 보안 그룹의 인바운드만을 허용합니다.
아래 사진은 localhost에서 ec2-bastion에 ssh 연결한 후, ec2-bastion에서 ec2-private으로 ssh 연결한 모습입니다.
ping 명령이 정상적으로 작동하는 걸 보아 private subnet에 있음에도 불구하고 인터넷에 트래픽이 도달한 것을 확인할 수 있습니다.
'Terraform' 카테고리의 다른 글
[Terraform] ★ 테라폼 연습하기 6: eks cluster 구성과 Assume Role, kubeconfig (0) | 2024.09.01 |
---|---|
[Terraform] 테라폼 연습하기 5: security group (0) | 2024.08.31 |
[Terraform] 테라폼 연습하기 4: ec2 instance (0) | 2024.08.29 |
[Terraform] 모듈화 연습하기1: VPC와 Subnet, 모듈화 (0) | 2024.08.25 |
[Terraform] 공부 내용 정리 (1) | 2024.08.22 |