lhywk 님의 블로그

AWS 3-Tier 및 Data Pipeline 구축 With Terraform (2) 본문

AWS

AWS 3-Tier 및 Data Pipeline 구축 With Terraform (2)

lhywk 2026. 3. 13. 21:52

참고자료: https://kujung.tistory.com/entry/AWS-3-Tier-Architecture-%EA%B5%AC%EC%B6%95%ED%95%B4%EB%B3%B4%EA%B8%B0-2-1

 

AWS 3 Tier Architecture 구축해보기 2-1

지금까지 위 3-Tier를 구현 하였다. 더나아가 실무에 가깝고 aws의 여러 기능을 추가한 아키텍처를 찾는 도중 깃허브에 올라온 아키텍처를 찾았다. https://github.com/estrellaSia/AWS_3Tier_Infra_Data_Pipeline/tre

kujung.tistory.com

 

Network Architecture

네트워크 아키텍처 부터 구성해보겠습니다.

  • VPC (Virtual Private Cloud): 서비스 전체를 감싸는 독립된 가상 네트워크 공간
  • Multi-AZ (2개의 가용 영역): 서울 리전의 ap-northeast-2a와 2c를 사용
  • 서브넷 구성 (총 8개):
    • Public Subnets (2개): 외부 인터넷과 직접 통신. 로드밸런서와 NAT 게이트웨이가 위치
    • Private Subnets (6개): 외부 접근이 차단된 안전한 영역. 용도에 따라 Web/App 계층(4개)과 DB 계층(2개)으로 나뉨.
  • 게이트웨이: 외부로 나가는 통로인 IGW(Internet Gateway)와 프라이빗 서버들의 패치를 돕는 NAT Gateway를 포함

 

Main.tf 작성

1. VPC 구성

resource "aws_vpc" "vpc" {
    cidr_block = "10.0.0.0/16"
    enable_dns_support = true
    # Amazon의 DNS 서버가 VPC 내부에서 DNS 쿼리를 해석할 수 있도록 함
    enable_dns_hostnames = true
    # VPC에서 인스턴스에 대해 DNS 호스트 이름을 할당할 수 있는지 여부를 결정
    tags = {
        Name = "ho-vpc"
    }
}
  • resource : Terraform에 실제 리소스를 생성할 때 사용하는 블록
  • "aws_vpc" : AWS의 VPC 리소스
  • "vpc": 이 리소스를 Terraform 내부에서 참조할 때 사용할 이름
  • cidr_block = "10.0.0.0/16"
    • VPC에서 사용할 IP 주소 범위를 지정
    • 65,536개의 사설 IP를 확보
  • enable_dns_support = true
    • VPC 내부에서 AWS가 제공하는 DNS 서버(Route 53)를 사용할지 결정
    • 이 설정이 켜져 있어야 VPC 내부 리소스들이 도메인 이름을 통해 서로 통신할 수 있다. 3-tier에서는 ALB나 RDS 접속 시 엔드포인트(도메인)를 사용하므로 필수
  • enable_dns_hostnames = true
    • VPC 내부에 생성되는 인스턴스(EC2 등)에 공인 DNS 이름을 부여할지 결정
    • 퍼블릭 서브넷에 배치될 서버들이 외부에서 도메인으로 접근 가능하게 하려면 이 옵션을 활성
  • tags : AWS 콘솔에서의 이름

 

2. 인터넷 게이트웨이

resource "aws_internet_gateway" "igw" {
    vpc_id = aws_vpc.vpc.id # igw를 해당 vpc에 attach
    tags = {
        Name = "ho-igw"
    }
}
  • "aws_internet_gateway": AWS의 인터넷 게이트웨이 리소스
  • vpc_id = aws_vpc.vpc.id
    • 위에서 만든 VPC의 ID를 가져와서 이 게이트웨이를 해당 VPC에 장착

 

3. NAT Gateway

resource "aws_eip" "eip1" {
    domain = "vpc" # 해당 EIP가 VPC 내에서만 사용 가능하도록 설정
}

resource "aws_eip" "eip2" {
    domain = "vpc"
}

resource "aws_nat_gateway" "nat-gw1" {
    allocation_id = aws_eip.eip1.id # EIP 할당
    connectivity_type = "public"
    subnet_id = aws_subnet.pub-sub1.id # NATGW를 생성할 서브넷
    tags = {
        Name = "ho-nat-gw1"
    }
    depends_on = [aws_internet_gateway.igw]
    # 리소스 간 생성 순서 보장(IGW 생성 후 NATGW 생성)
}

resource "aws_nat_gateway" "nat-gw2" {
    allocation_id = aws_eip.eip2.id # EIP 할당
    connectivity_type = "public"
    subnet_id = aws_subnet.pub-sub2.id # NATGW를 생성할 서브넷
    tags = {
        Name = "ho-nat-gw2"
    }
    depends_on = [aws_internet_gateway.igw]
    # 리소스 간 생성 순서 보장(IGW 생성 후 NATGW 생성)
}
  • "aws_eip": AWS의 Elastic IP 리소스 타입. NAT 게이트웨이는 외부와 통신하기 위해 고정 공인 IP가 반드시 필요
  • domain = "vpc": 이 EIP가 VPC 환경에서 사용될 것임을 명시
  • "aws_nat_gateway": AWS의 NAT 게이트웨이 리소스 타입
  • allocation_id = aws_eip.eip1.id: 위에서 만든 고정 IP(eip1)를 이 게이트웨이에 할당
  • connectivity_type = "public": 인터넷과 통신하는 NAT 게이트웨이임을 설정
  • subnet_id = aws_subnet.pub-sub1.id: NAT 게이트웨이가 위치할 서브넷을 지정. NAT 게이트웨이는 반드시 퍼블릭 서브넷에 위치
  • depends_on = [aws_internet_gateway.igw]: NAT 게이트웨이가 외부와 통신하려면 IGW가 먼저 존재해야 함. 테라폼에게 IGW이 먼저 만들어진 다음에 NAT를 만들라고 순서를 정해주는 것

 

4. Subnet, Routing Table

# Public Subnet, Pbulic Rounting Table
resource "aws_subnet" "pub-sub1" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.1.0/24"
    availability_zone = "ap-northeast-2a"
    map_public_ip_on_launch = true # 퍼블릭 IP 주소 자동 할당
    tags = {
        Name = "ho-pub-sub1"
    }
}

resource "aws_subnet" "pub-sub2" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.2.0/24"
    availability_zone = "ap-northeast-2c"
    map_public_ip_on_launch = true
    tags = {
        Name = "ho-pub-sub2"
    }
}

resource "aws_route_table" "pub-rt" {
    vpc_id = aws_vpc.vpc.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.igw.id # 모든 트래픽은 IGW로 
    }
    tags = {
        Name = "ho-pub-rt"
    }
}

resource "aws_route_table_association" "pub-rt-asso1" {
    # public subnet들을 public rt에 연결
    subnet_id = aws_subnet.pub-sub1.id
    route_table_id = aws_route_table.pub-rt.id
}

resource "aws_route_table_association" "pub-rt-asso2" {
    subnet_id = aws_subnet.pub-sub2.id
    route_table_id = aws_route_table.pub-rt.id
}
  • "aws_subnet": AWS의 서브넷 리소스 타입
  • cidr_block = "10.0.1.0/24": VPC(10.0.0.0/16)의 전체 범위 중 일부인 256개의 IP 주소를 이 서브넷에 할당
  • availability_zone = "ap-northeast-2a": 이 서브넷이 위치할 데이터 센터 위치(서울 리전의 a 구역)를 지정
  • map_public_ip_on_launch = true: 이 설정이 있어야 이 서브넷에 생성되는 EC2 인스턴스들이 자동으로 공인 IP를 부여받아 인터넷 통신이 가능해짐
  • "aws_route_table": AWS의 라우팅 테이블 리소스 타입입니다.
  • route { ... }: 구체적인 경로 규칙을 정의
    • cidr_block = "0.0.0.0/0": "모든 목적지"를 의미. VPC 내부 주소가 아닌 모든 외부 인터넷 트래픽
    • gateway_id = aws_internet_gateway.igw.id: 외부로 나가는 트래픽은 우리가 만든 IGW으로 보냄
  • "aws_route_table_association": AWS의 서브넷과 라우팅 테이블을 연결해 주는 리소스 타입
# Private Subnet, Private Routing Table
resource "aws_subnet" "web-sub1" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.4.0/22"
    availability_zone = "ap-northeast-2a"
    map_public_ip_on_launch = false
    tags = {
        Name = "ho-web-sub1"
    }
}

resource "aws_subnet" "web-sub2" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.8.0/22"
    availability_zone = "ap-northeast-2c"
    map_public_ip_on_launch = false
    tags = {
        Name = "ho-web-sub2"
    }
}

resource "aws_subnet" "was-sub1" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.12.0/22"
    availability_zone = "ap-northeast-2a"
    map_public_ip_on_launch = false
    tags = {
        Name = "ho-was-sub1"
    }
}

resource "aws_subnet" "was-sub2" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.16.0/22"
    availability_zone = "ap-northeast-2c"
    map_public_ip_on_launch = false
    tags = {
        Name = "ho-was-sub2"
    }
}

resource "aws_subnet" "db-sub1" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.20.0/22"
    availability_zone = "ap-northeast-2a"
    map_public_ip_on_launch = false
    tags = {
        Name = "ho-db-sub1"
    }
}

resource "aws_subnet" "db-sub2" {
    vpc_id = aws_vpc.vpc.id
    cidr_block = "10.0.24.0/22"
    availability_zone = "ap-northeast-2c"
    map_public_ip_on_launch = false
    tags = {
        Name = "ho-db-sub2"
    }
}

resource "aws_route_table" "pri-rt1" {
    vpc_id = aws_vpc.vpc.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_nat_gateway.nat-gw1.id
    }
    tags = {
        Name = "ho-pri-rt1"
    }
}

resource "aws_route_table" "pri-rt2" {
    vpc_id = aws_vpc.vpc.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_nat_gateway.nat-gw2.id
    }
    tags = {
        Name = "ho-pri-rt2"
    }
}

# WEB
resource "aws_route_table_association" "pri-rt-asso1" { 
    subnet_id = aws_subnet.web-sub1.id
    route_table_id = aws_route_table.pri-rt1.id
}

resource "aws_route_table_association" "pri-rt-asso2" {
    subnet_id = aws_subnet.web-sub2.id
    route_table_id = aws_route_table.pri-rt2.id
}

# WAS
resource "aws_route_table_association" "pri-rt-asso3" {
    subnet_id = aws_subnet.was-sub1.id
    route_table_id = aws_route_table.pri-rt1.id
}

resource "aws_route_table_association" "pri-rt-asso4" {
    subnet_id = aws_subnet.was-sub2.id
    route_table_id = aws_route_table.pri-rt2.id
}

# DB
resource "aws_route_table_association" "pri-rt-asso5" {
    subnet_id = aws_subnet.db-sub1.id
    route_table_id = aws_route_table.pri-rt1.id
}
resource "aws_route_table_association" "pri-rt-asso6" {
    subnet_id = aws_subnet.db-sub2.id
    route_table_id = aws_route_table.pri-rt2.id
}