프라이빗 서브넷에 아웃바운드 액세스 제공하기

AWSBeginner
지금 연습하기

소개

배송 애플리케이션이 프라이빗 서브넷에서 실행되며 퍼블릭 주소 없이 외부 서비스에 접속해야 합니다. 퍼블릭 NAT 게이트웨이를 만들고 아웃바운드 경로를 추가한 뒤, 실제 HTTP 요청으로 소스 주소 변환과 외부에서 먼저 시작하는 인바운드 연결의 차단을 확인합니다.

먼저 Control Application Access with Security Groups를 완료하세요. 이 새로운 환경은 자체 애플리케이션, 퍼블릭 및 프라이빗 서브넷, 퍼블릭 Internet 경로와 보안 규칙을 제공합니다. CLI는 이미 구성되어 있습니다. 이러한 리소스와 관련 없는 참조 네트워크를 보존하세요. 자신의 NAT 게이트웨이, Elastic IP 주소, 프라이빗 기본 경로만 만들고 나중에 삭제합니다.

자격증 관련성

이 실습은 다음 시험 주제를 연습합니다.

프라이빗 애플리케이션 검사 및 NAT 주소 할당

제공된 네트워크를 검사하고 아웃바운드 경로가 없는 상태를 관찰한 뒤, 만들 예정인 NAT 게이트웨이의 주소를 할당합니다.

CLI 명령은 Terminal에서 실행하고 옆에 AWS View를 여세요. 뷰는 동일한 리소스 상태를 읽습니다. 애플리케이션은 public-subnet이 아닌 private-subnet에 있으며 프라이빗 IPv4 주소는 10.20.2.10입니다.

cd /home/labex/project

Name 태그로 애플리케이션 VPC를 선택합니다. --filters는 결과를 제한하고, --query는 ID를 선택하며, $(...)는 결과를 셸 변수에 저장합니다.

VPC_ID=$(aws ec2 describe-vpcs \
  --filters Name=tag:Name,Values=application-network \
  --query 'Vpcs[0].VpcId' \
  --output text)

이 VPC의 퍼블릭 서브넷을 선택합니다. NAT 게이트웨이는 여기서 Internet 경로를 갖게 됩니다.

PUBLIC_SUBNET_ID=$(aws ec2 describe-subnets \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=public-subnet \
  --query 'Subnets[0].SubnetId' \
  --output text)

프라이빗 서브넷의 기존 라우팅 테이블을 선택합니다. 기존 서브넷 연결을 유지하면서 이 테이블의 기본 경로를 바꿉니다.

PRIVATE_RT_ID=$(aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=$VPC_ID" Name=tag:Name,Values=private-routes \
  --query 'RouteTables[0].RouteTableId' \
  --output text)

제공된 두 라우팅 테이블을 검사합니다. []. 뒤의 투영식은 각 테이블에서 읽기 쉬운 필드를 선택합니다.

aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=$VPC_ID" \
  --query 'RouteTables[].{Name:Tags[?Key==`Name`].Value|[0],Routes:Routes,Associations:Associations}' \
  --output json

이름이 있는 두 테이블에는 VPC의 local 경로가 있습니다. 이름 없는 기본 테이블도 표시될 수 있으며 변경하지 마세요. 이름이 있는 테이블은 명시적으로 서브넷에 연결되므로 선택한 private-routes를 사용합니다. 퍼블릭 테이블은 0.0.0.0/0을 Internet 게이트웨이로 보내지만 프라이빗 테이블에는 기본 경로가 없습니다. 프라이빗 서브넷에는 Internet 게이트웨이로 직접 연결되는 경로가 없습니다. 퍼블릭 NAT 게이트웨이는 프라이빗 애플리케이션이 게이트웨이의 퍼블릭 주소로 IPv4 연결을 시작하도록 합니다. 자체 Internet 경로가 제공된 퍼블릭 서브넷에 배치해야 합니다.

AWS View에서 Request outbound service를 클릭합니다. 외부 서비스 198.51.100.20:9000으로 가는 경로가 없어 프라이빗 애플리케이션의 HTTP 요청이 Connection failed를 반환합니다. Request private application from outside도 클릭하세요. 외부에서 먼저 10.20.2.10:80에 접속하는 HTTP 요청 역시 실패합니다. 애플리케이션은 프라이빗 주소를 유지합니다.

Elastic IP 주소는 할당된 퍼블릭 IPv4 주소입니다. VPC 도메인에서 하나를 할당하고 실습 리소스를 식별할 태그를 붙입니다. 따옴표로 묶인 --tag-specifications 값은 리소스 유형과 태그를 설명하는 단일 인수입니다.

ALLOCATION_ID=$(aws ec2 allocate-address \
  --domain vpc \
  --tag-specifications 'ResourceType=elastic-ip,Tags=[{Key=Name,Value=parcel-nat-address},{Key=Project,Value=parcel}]' \
  --query 'AllocationId' \
  --output text)

할당된 주소를 조회합니다.

aws ec2 describe-addresses \
  --allocation-ids "$ALLOCATION_ID" \
  --query 'Addresses[].{Allocation:AllocationId,PublicIP:PublicIp,Domain:Domain}' \
  --output table

결과에는 할당 ID, 퍼블릭 IPv4 주소, vpc 도메인이 있습니다. 나중에 트래픽과 비교하도록 퍼블릭 주소를 기록하세요. 이 주소는 향후 NAT에 사용할 주소이므로 애플리케이션에 연결하지 마세요. 저장한 ID를 유지하려면 이 Terminal을 열어 두세요.

퍼블릭 NAT 게이트웨이 만들기

퍼블릭 서브넷에 NAT를 배치하고 게이트웨이 생성만으로는 프라이빗 애플리케이션 경로가 설정되지 않음을 확인합니다.

NAT에는 기존 퍼블릭 서브넷과 할당된 Elastic IP가 필요합니다. --subnet-id는 위치를, --allocation-id는 퍼블릭 주소를, --connectivity-type public은 Internet 방향의 게이트웨이를 선택합니다. natgateway 리소스 유형으로 소유 태그를 적용하고 생성된 ID를 저장합니다.

NAT_ID=$(aws ec2 create-nat-gateway \
  --subnet-id "$PUBLIC_SUBNET_ID" \
  --allocation-id "$ALLOCATION_ID" \
  --connectivity-type public \
  --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=parcel-nat},{Key=Project,Value=parcel}]' \
  --query 'NatGateway.NatGatewayId' \
  --output text)

생성 요청은 게이트웨이가 준비되기 전에 반환될 수 있습니다. CLI waiter는 지정한 조건을 만족할 때까지 상태를 반복해서 읽습니다. 경로 추가 전에 available을 기다립니다.

aws ec2 wait nat-gateway-available --nat-gateway-ids "$NAT_ID"

waiter는 성공하면 출력 없이 종료됩니다. 게이트웨이 상태, 서브넷, 주소 연결을 조회합니다.

aws ec2 describe-nat-gateways \
  --nat-gateway-ids "$NAT_ID" \
  --query 'NatGateways[].{ID:NatGatewayId,State:State,Subnet:SubnetId,Addresses:NatGatewayAddresses}' \
  --output json

State는 available, Subnet은 퍼블릭 서브넷과 일치하며 NatGatewayAddresses에는 할당 ID와 퍼블릭 주소가 있습니다. PrivateIp는 퍼블릭 서브넷 범위 10.20.1.0/24 안의 주소입니다. 이는 NAT 인터페이스의 프라이빗 주소로, Elastic IP 및 애플리케이션 주소와 다릅니다. NAT는 별도 리소스이며 프라이빗 애플리케이션이 이 주소를 받은 것이 아닙니다.

이제 AWS View에 NAT가 보입니다. Request outbound service를 다시 클릭하세요. 프라이빗 테이블에 기본 경로가 없어 여전히 실패합니다. 게이트웨이를 만들면 가능한 다음 홉을 제공하지만 프라이빗 서브넷에서 자동 선택하지 않습니다. 퍼블릭 테이블의 기존 Internet 게이트웨이 경로는 유지됩니다.

NAT로 프라이빗 아웃바운드 트래픽 라우팅

프라이빗 기본 경로를 추가하고 애플리케이션의 프라이빗 주소와 외부 서비스에서 관찰한 소스 주소를 비교합니다.

경로는 대상 범위에 대한 다음 홉을 선택합니다. 0.0.0.0/0은 더 구체적인 경로에 일치하지 않는 IPv4 대상을 처리합니다. --nat-gateway-id는 Internet 게이트웨이 대신 자신의 NAT를 선택합니다. 저장한 프라이빗 테이블에만 경로를 추가하세요.

aws ec2 create-route \
  --route-table-id "$PRIVATE_RT_ID" \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id "$NAT_ID"

응답은 생성 성공을 나타냅니다. 프라이빗 테이블의 경로를 조회합니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

로컬 경로는 남아 있습니다. 새 기본 경로는 자신의 NatGatewayId와 active 상태를 갖습니다. 패킷은 프라이빗 애플리케이션 → 퍼블릭 서브넷의 NAT → Internet 게이트웨이 → 외부 서비스로 이동합니다.

AWS View에 프라이빗 기본 경로가 보이면 Request outbound service를 클릭합니다. Success가 반환되고 응답 본문은 외부 HTTP 서비스가 실제로 관찰한 소스 IPv4 주소입니다. 할당한 퍼블릭 주소와 비교합니다.

aws ec2 describe-addresses \
  --allocation-ids "$ALLOCATION_ID" \
  --query 'Addresses[0].PublicIp' \
  --output text

두 주소가 같습니다. **네트워크 주소 변환(NAT)**은 애플리케이션의 프라이빗 소스 주소를 게이트웨이의 퍼블릭 주소로 바꾸고 연결을 추적하여 응답을 시작한 쪽으로 반환합니다. 애플리케이션 자체 주소는 10.20.2.10입니다.

프라이빗 애플리케이션이 NAT 경로로 외부 서비스에 연결

AWS View 예시: 애플리케이션은 10.20.2.10을 유지하며 프라이빗 기본 경로는 사용 가능한 NAT를 선택하고 HTTP 본문은 할당된 퍼블릭 주소를 보여 줍니다. 생성된 리소스 ID와 주소는 다를 수 있습니다.

Request private application from outside를 클릭합니다. 여전히 Connection failed입니다. 아웃바운드 연결과 반환 트래픽은 프라이빗 애플리케이션에 퍼블릭 인바운드 경로를 만들지 않습니다. NAT는 이 애플리케이션을 위한 외부에서 시작하는 Internet 연결을 받아들이지 않습니다.

누락된 프라이빗 경로 진단 및 복원

경로 하나를 제거하고 실패를 관찰한 다음, NAT를 다시 만들지 않고 연결을 복원합니다.

게이트웨이는 사용 가능하지만 클라이언트에 유효한 경로가 없을 수 있습니다. NAT와 퍼블릭 Internet 경로를 유지하며 프라이빗 기본 경로만 삭제합니다.

aws ec2 delete-route --route-table-id "$PRIVATE_RT_ID" --destination-cidr-block 0.0.0.0/0

삭제 성공 시 출력은 없습니다. 프라이빗 테이블을 검사합니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

VPC 로컬 경로만 남습니다. AWS View에는 사용 가능한 NAT가 있지만 프라이빗 기본 경로는 없습니다. Request outbound service를 클릭하면 새로운 요청이 실패합니다. 이전 성공은 과거 기록이지 이 새 요청의 결과가 아닙니다.

같은 게이트웨이로 향하는 같은 경로를 복원합니다.

aws ec2 create-route \
  --route-table-id "$PRIVATE_RT_ID" \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id "$NAT_ID"

다시 경로를 조회합니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

활성 기본 경로가 돌아옵니다. AWS View에서 새 Request outbound service를 보내면 성공하고 동일한 NAT 퍼블릭 주소를 표시합니다. Request private application from outside는 여전히 실패합니다. 이 관찰은 게이트웨이 가용성과 완전한 클라이언트 라우팅 경로를 구분합니다.

실습 NAT 경로 삭제 및 주소 해제

자신의 경로와 NAT를 제거하고 퍼블릭 주소를 해제한 뒤 제공된 프라이빗 네트워크가 온전함을 확인합니다.

다음 홉을 삭제하기 전에 프라이빗 기본 경로부터 삭제합니다. 그렇지 않으면 삭제된 게이트웨이로 향하는 경로가 남을 수 있습니다.

aws ec2 delete-route --route-table-id "$PRIVATE_RT_ID" --destination-cidr-block 0.0.0.0/0

삭제 성공 시 출력은 없습니다. 자신의 NAT만 삭제합니다.

aws ec2 delete-nat-gateway --nat-gateway-id "$NAT_ID"

응답은 요청한 게이트웨이를 식별합니다. 주소 해제 전에 삭제 waiter를 사용합니다.

aws ec2 wait nat-gateway-deleted --nat-gateway-ids "$NAT_ID"

성공한 waiter는 출력 없이 종료됩니다. NAT 삭제는 네트워크 인터페이스를 제거하고 Elastic IP 연결을 해제하지만 주소 할당을 해제하지는 않습니다. 해제 전에 주소를 읽습니다.

aws ec2 describe-addresses \
  --allocation-ids "$ALLOCATION_ID" \
  --query 'Addresses[].{Allocation:AllocationId,PublicIP:PublicIp,Association:AssociationId}' \
  --output json

할당과 퍼블릭 주소는 남고 Association은 null입니다. 주소가 할당되어 있지만 연결되지는 않은 상태입니다. 이제 이 별도 리소스를 해제합니다.

aws ec2 release-address --allocation-id "$ALLOCATION_ID"

해제 성공 시 출력은 없습니다. 제거할 수 있는 태그에 의존하지 말고 전체 게이트웨이 목록을 조회합니다.

aws ec2 describe-nat-gateways \
  --query 'NatGateways[].{ID:NatGatewayId,State:State,Subnet:SubnetId}' \
  --output table

게이트웨이가 deleted 상태로 목록에 남을 수 있지만 활성 실습 NAT는 없어야 합니다. 삭제 기록이 남아도 트래픽을 전달할 수 있다는 뜻은 아닙니다. 전체 주소 목록을 확인합니다.

aws ec2 describe-addresses \
  --query 'Addresses[].{Allocation:AllocationId,PublicIP:PublicIp}' \
  --output table

실습 할당이 없습니다. 이제 프라이빗 경로를 조회합니다.

aws ec2 describe-route-tables \
  --route-table-ids "$PRIVATE_RT_ID" \
  --query 'RouteTables[].Routes' \
  --output json

VPC 로컬 경로는 남고 기본 경로는 없습니다. 제공된 애플리케이션, 서브넷, 보안 규칙, 퍼블릭 Internet 게이트웨이와 참조 네트워크는 유지됩니다. 목록 조회 실패로는 삭제를 증명할 수 없습니다.

AWS View에서 Request outbound service를 클릭합니다. NAT 경로를 의도적으로 제거했으므로 다시 실패합니다. Request private application from outside도 차단된 상태입니다. 결과는 처음의 프라이빗 네트워크 기준 상태와 같습니다.

이 단계의 완료 검사를 실행하세요.

요약

퍼블릭 주소를 할당하고 퍼블릭 서브넷에 NAT를 배치한 뒤 프라이빗 애플리케이션의 아웃바운드 요청을 라우팅했습니다. 외부 서비스는 변환된 NAT 주소를 관찰했고 외부에서 먼저 시작한 인바운드 요청은 차단되었습니다. 프라이빗 기본 경로 제거 및 복원은 게이트웨이 가용성만으로는 연결되지 않는 이유를 보여 줍니다.

이후 경로와 NAT를 제거하고 주소를 해제하여 프라이빗 네트워크 기준 상태의 보존을 확인했습니다.