- [terraform] 코드로 인프라 관리하기2025년 04월 22일 19시 00분 50초에 업로드 된 글입니다.작성자: @kimyu0218
프로젝트를 진행하다보면 초기 인프라 구축이 굉장히 귀찮다. 클라우드 콘솔이 잘 되어있다 하더라도, 애플리케이션이 예상대로 동작하지 않는다면 리소스를 하나 하나 찾아가며 디버깅 해야 한다. 혹시라도 플랫폼을 변경하게 되는 날엔 똑같은 작업을 다시 하게 되는 불상사가 발생한다.
늘 그랬듯 이번에도 크레딧이 만료되어 AWS로 마이그레이션하게 되었다. 나는 반복 작업을 하지 않기 위해 테라폼을 사용하기로 결정했다.
Terraform
테라폼은 대표적인 IaC 도구다. IaC는 Infrastructure as Code로, GUI 대신 코드를 이용하여 클라우드 및 온프레미스 인프라를 관리한다. IaC를 활용하면 인프라 리소스를 안전하고 일관되게 정의/변경하고 재사용할 수 있다. 테라폼은 HashiCorp*에서 제공하는 도구로, 설정 파일을 통해 인프라 리소스를 정의하고 라이프사이클을 관리한다.
- AWS, GCP, Azure 등 다양한 클라우드 플랫폼의 인프라를 관리할 수 있다.
- 사람이 읽기 쉬운 언어/문법을 사용한다.
- 변경 사항을 추적하고 쉽게 공유할 수 있다.
*HashiCorp : 오픈소스 및 엔터프라이즈 소프트웨어를 개발하는 IT 기업으로, Terraform, Vault 등 클라우드 환경에서 필요한 도구들을 개발한다.
테라폼을 사용하는 방법은 간단하다.
- (write) 설정 파일에 인프라를 정의한다.
- (plan) 설정 파일과 이미 존재하는 인프라를 바탕으로 실행 계획을 세운다.
- (apply) 인프라를 변경한다.
지금부터 테라폼을 이용해서 인프라를 구축해보자. 사전에 링크를 참고하여 CLI를 설치했다.
VPC 리소스 생성하기
AWS에서 나만의 네트워크 공간을 만들기 위해서는 VPC가 필수적이다. 테라폼을 사용하여 복잡한 관계의 VPC 리소스들을 간편하게 생성할 것이다. 아래는 테라폼으로 만들 AWS VPC 리소스 목록이다.
- VPC
- 서브넷
- 인터넷 게이트웨이
- 라우팅 테이블
- 네트워크 ACL
variable "vpc_cidr" { type = string default = "10.0.0.0/24" description = "VPC CIDR" } resource "aws_vpc" "vpc" { cidr_block = var.vpc_cidr enable_dns_support = true enable_dns_hostnames = true tags = { Name = "goalpanzi-vpc" } }
VPC는 Virtual Private Network로, 가상의 네트워크 공간을 의미한다. 이 네트워크 안에 여러 리소스들이 존재하므로, 각 리소스가 사용하는 IP 주소 체계를 미리 정의해야 한다.
CIDR(Classless Inter-Domain Routing)은 IP 주소를 네트워크 주소와 호스트 주소로 나누는 IP 주소 할당 방법이다. 네트워크 주소를 얼마나 할당하느냐에 따라 네트워크 내부에 올 수 있는 리소스의 개수가 결정된다. `/24`는 IP 주소의 앞 24비트를 네트워크 주소로 사용하겠다는 의미다. 따라서 이 네트워크에는 `32-24=8`, 2의 8제곱인 256개의 IP 주소를 할당할 수 있다.
`vpc_cidr`은 VPC에서 사용할 IP 대역을 변수로 정의하고 있다. `aws_vpc`의 `cidr_block`에서 이를 참조하여 네트워크 리소스들이 어떤 IP 대역을 가질지 결정한다.
resource "aws_subnet" "public" { vpc_id = aws_vpc.vpc.id count = length(var.azs) cidr_block = cidrsubnet(aws_vpc.vpc.cidr_block, 4, count.index) availability_zone = var.azs[count.index] tags = { Name = "goalpanzi-public-${count.index + 1}" } } resource "aws_subnet" "private" { vpc_id = aws_vpc.vpc.id count = length(var.azs) cidr_block = cidrsubnet(aws_vpc.vpc.cidr_block, 4, count.index + length(var.azs)) availability_zone = var.azs[count.index] tags = { Name = "goalpanzi-private-${count.index + 1}" } }
VPC는 할당 받은 네트워크를 여러 개의 작은 네트워크로 나눌 수 있는데, 일반적으로 퍼블릭 서브넷과 프라이빗 서브넷으로 구분하여 사용한다. 퍼블릭 서브넷은 인터넷과 직접 통신할 수 있는 서브넷으로, 외부와 접근해야 하는 웹 서버나 로드밸런서를 배치시킨다. 반면, 프라이빗 서브넷은 외부와 직접 통신할 수 없는 내부 네트워크로, 보안이 중요한 데이터베이스를 배치해 인터넷으로부터 직접적인 노출을 차단한다.
`aws_subnet`의 `vpc_id`는 어떤 VPC 안에 서브넷을 생성할지 지정하는 부분이다. 퍼블릭과 프라이빗 서브넷 모두 같은 VPC를 사용하므로 앞서 생성한 VPC의 아이디 값을 넣어줬다.
resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.vpc.id tags = { Name = "goalpanzi-igw" } } resource "aws_route_table" "public" { vpc_id = aws_vpc.vpc.id tags = { Name = "goalpanzi-public-rt" } } resource "aws_route" "internet_access" { route_table_id = aws_route_table.public.id gateway_id = aws_internet_gateway.igw.id destination_cidr_block = "0.0.0.0/0" } resource "aws_route_table_association" "public" { route_table_id = aws_route_table.public.id count = length(var.azs) subnet_id = aws_subnet.public[count.index].id }
퍼블릭 서브넷이 외부와 통신할 수 있는 이유는 바로 인터넷 게이트웨이 덕분이다. 인터넷 게이트웨이는 VPC와 외부 인터넷을 연결하는 컴포넌트로 외부와의 통신을 지원한다. 하지만 인터넷 게이트웨이만으로는 충분하지 않는데, 퍼블릭 서브넷 내부의 트래픽이 "어디로 가야 할지" 알아야 하기 때문이다. 라우팅 테이블은 네트워크 트래픽이 어디를 향해야 하는지 정의한다.
`aws_route_table`와 `aws_route` 블록을 사용해 퍼블릭 서브넷을 위한 라우팅 테이블을 정의한다. `aws_route`의 `gateway_id`에 인터넷 게이트웨이를 연결하고, `destination_cidr_block`에 `0.0.0.0/0`을 대입하여 모든 외부 트래픽과 통신할 수 있도록 설정한다.
resource "aws_network_acl" "public" { vpc_id = aws_vpc.vpc.id # outbound egress { rule_no = 100 action = "allow" protocol = "-1" from_port = 0 to_port = 0 cidr_block = "0.0.0.0/0" } # inbound ingress { rule_no = 100 action = "allow" protocol = "tcp" from_port = var.ssh_port to_port = var.ssh_port cidr_block = "0.0.0.0/0" } ingress { rule_no = 110 action = "allow" protocol = "tcp" from_port = var.mysql_port to_port = var.mysql_port cidr_block = "0.0.0.0/0" } ingress { rule_no = 120 action = "allow" protocol = "tcp" from_port = var.redis_port to_port = var.redis_port cidr_block = "0.0.0.0/0" } ### 중략 ### } resource "aws_network_acl_association" "public" { count = length(var.azs) network_acl_id = aws_network_acl.public.id subnet_id = aws_subnet.public[count.index].id }
퍼블릭 서브넷이 인터넷과 통신하면서 VPC는 보안 위협에 노출된다. ACL(Access Control List)은 인바운드, 아웃바운드 규칙을 통해 네트워크 수준에서 트래픽의 접근을 제어하여 보안을 강화한다.
`aws_network_acl`의 `vpc_id`에 생성한 VPC를 연결하고, `ingress`, `outgress`를 작성하여 서브넷으로 들어오는 트래픽, 서브넷으로 나가는 트래픽을 제어한다. 그리고 `aws_network_acl_association`을 사용해 정의한 ACL을 실제 퍼블릭 서브넷에 연결한다.
작성한 설정 파일을 이용하여 CLI 명령을 실행하면 위와 같이 VPC 리소스가 생성된다! 이제 다른 플랫폼으로 변경하거나, 리전이나 계정을 바꾸게 되더라도 CLI로 간편하게 만들 수 있다.
참고자료
'backend > aws' 카테고리의 다른 글
[aws] rds 만들기 (0) 2024.01.08 [aws] ec2 스왑 공간 할당하기 (OOM) (1) 2024.01.08 [aws] vpc 설정하기 (Connection timed out 이슈/네트워크 acl) (0) 2024.01.03 [aws] ec2 인스턴스 만들기 (키 페어/보안 그룹) (0) 2024.01.01 [aws] aws 시작하기 (region/AZ/IAM) (1) 2024.01.01 다음글이 없습니다.이전글이 없습니다.댓글