두 VPC 연결하기: VPC 피어링 설정과 라우트 테이블 업데이트 완전 가이드
같은 계정, 같은 리전에 VPC가 두 개 있는데 서로 프라이빗 IP로 통신이 안 된다 — 이건 AWS를 처음 쓰는 팀이든 오래 쓴 팀이든 한 번씩은 마주치는 상황이다. VPC 피어링(VPC Peering) 자체는 단순하지만, 라우트 테이블과 보안 그룹까지 세 군데를 동시에 맞춰야 트래픽이 흐른다. 하나라도 빠지면 핑이 안 되고, 어디가 문제인지 찾는 데 시간을 쓰게 된다.
TL;DR — VPC 피어링 핵심 요약
| 단계 | 작업 주체 | 핵심 포인트 |
|---|---|---|
| 1. 피어링 연결 요청 | Requester VPC | 상대 VPC ID 지정, 같은 계정이면 자동 수락 가능 |
| 2. 피어링 연결 수락 | Accepter VPC | 수락 전까지 트래픽 불가 |
| 3. 라우트 테이블 업데이트 | 양쪽 VPC 모두 | 상대 CIDR → pcx-xxxxxx, 양방향 필수 |
| 4. 보안 그룹 업데이트 | 양쪽 인스턴스 | 상대 CIDR 또는 SG ID 허용 |
| 5. 통신 검증 | 운영자 | VPC Flow Logs 또는 ping/curl 확인 |
VPC 피어링이 동작하는 방식
VPC 피어링은 두 VPC 사이에 논리적인 1:1 네트워크 경로를 만든다. 인터넷 게이트웨이, NAT, VPN, 별도의 게이트웨이 장비가 전혀 개입하지 않는다. AWS 내부 네트워크를 통해 직접 라우팅되므로 트래픽이 공인 인터넷을 경유하지 않는다.
중요한 제약이 하나 있다. 피어링은 전이적(transitive) 라우팅을 지원하지 않는다. VPC-A ↔ VPC-B, VPC-B ↔ VPC-C가 각각 피어링되어 있어도 VPC-A에서 VPC-C로 VPC-B를 경유해 통신할 수 없다. 세 VPC가 모두 통신해야 한다면 각각 직접 피어링해야 한다.
또한 두 VPC의 CIDR 블록이 겹치면 피어링 자체가 생성되지 않는다. 설계 단계에서 CIDR 충돌을 반드시 확인해야 한다.
- VPC-A (10.0.0.0/16)와 VPC-B (10.1.0.0/16)는 서로 다른 CIDR을 사용한다.
- 피어링 연결(pcx-xxxxxx)이 두 VPC 사이의 논리적 경로 역할을 한다.
- 각 VPC의 라우트 테이블에 상대 CIDR을 피어링 연결로 향하도록 항목을 추가해야 한다.
- 보안 그룹은 라우트 테이블과 독립적으로 동작하므로 별도로 인바운드 규칙을 열어야 한다.
사전 준비: CIDR 충돌 확인
피어링을 만들기 전에 두 VPC의 CIDR 블록이 겹치지 않는지 확인한다. 겹치는 경우 피어링 요청 자체가 실패한다.
# VPC 목록과 CIDR 확인
aws ec2 describe-vpcs \
--query 'Vpcs[*].{VpcId:VpcId,CidrBlock:CidrBlock,Name:Tags[?Key==`Name`]|[0].Value}' \
--output table \
--region us-east-1
출력에서 두 VPC의 CidrBlock 값이 겹치지 않는지 눈으로 확인한다. 예를 들어 VPC-A가 10.0.0.0/16, VPC-B가 10.1.0.0/16이면 문제없다.
VPC 피어링 설정: 단계별 가이드
1단계: 피어링 연결 요청 생성
Requester VPC(VPC-A)에서 Accepter VPC(VPC-B)로 피어링 요청을 보낸다. 같은 계정, 같은 리전이므로 --peer-owner-id와 --peer-region은 생략해도 되지만, 명시하면 실수를 줄일 수 있다.
aws ec2 create-vpc-peering-connection \
--vpc-id vpc-0a1b2c3d4e5f00001 \
--peer-vpc-id vpc-0a1b2c3d4e5f00002 \
--region us-east-1
명령 실행 후 출력에서 VpcPeeringConnectionId 값을 기록해 둔다. 이후 단계에서 계속 사용한다. 형식은 pcx-xxxxxxxxxxxxxxxxx다.
# 피어링 연결 ID 확인
aws ec2 describe-vpc-peering-connections \
--filters Name=status-code,Values=pending-acceptance \
--query 'VpcPeeringConnections[*].{PeeringId:VpcPeeringConnectionId,Requester:RequesterVpcInfo.VpcId,Accepter:AccepterVpcInfo.VpcId,Status:Status.Code}' \
--output table \
--region us-east-1
2단계: 피어링 연결 수락
같은 계정 내 피어링이라도 수락 단계를 거쳐야 한다. 수락하지 않으면 상태가 pending-acceptance로 남고 트래픽은 흐르지 않는다.
aws ec2 accept-vpc-peering-connection \
--vpc-peering-connection-id pcx-xxxxxxxxxxxxxxxxx \
--region us-east-1
수락 후 상태가 active로 바뀌는지 확인한다.
aws ec2 describe-vpc-peering-connections \
--vpc-peering-connection-ids pcx-xxxxxxxxxxxxxxxxx \
--query 'VpcPeeringConnections[0].Status' \
--output json \
--region us-east-1
"Code": "active"가 나오면 피어링 연결 자체는 완료된 것이다. 하지만 아직 트래픽은 흐르지 않는다 — 라우트 테이블이 비어 있기 때문이다.
3단계: 라우트 테이블 업데이트 — 양방향 필수
피어링이 active 상태여도 라우트 테이블에 항목이 없으면 패킷이 어디로 가야 할지 모른다. VPC-A의 라우트 테이블에는 VPC-B의 CIDR을, VPC-B의 라우트 테이블에는 VPC-A의 CIDR을 추가해야 한다. 한쪽만 하면 단방향 통신도 안 된다.
먼저 각 VPC에 연결된 라우트 테이블 ID를 확인한다.
# VPC-A의 라우트 테이블 확인
aws ec2 describe-route-tables \
--filters Name=vpc-id,Values=vpc-0a1b2c3d4e5f00001 \
--query 'RouteTables[*].{RouteTableId:RouteTableId,Main:Associations[0].Main}' \
--output table \
--region us-east-1
# VPC-B의 라우트 테이블 확인
aws ec2 describe-route-tables \
--filters Name=vpc-id,Values=vpc-0a1b2c3d4e5f00002 \
--query 'RouteTables[*].{RouteTableId:RouteTableId,Main:Associations[0].Main}' \
--output table \
--region us-east-1
서브넷마다 별도의 라우트 테이블이 연결되어 있다면, 통신에 참여하는 서브넷의 라우트 테이블 모두에 항목을 추가해야 한다. 메인 라우트 테이블만 업데이트하면 커스텀 라우트 테이블이 연결된 서브넷은 빠진다.
# VPC-A의 라우트 테이블에 VPC-B CIDR 추가
# (VPC-A에서 VPC-B 방향)
aws ec2 create-route \
--route-table-id rtb-0a1b2c3d4e5f00001 \
--destination-cidr-block 10.1.0.0/16 \
--vpc-peering-connection-id pcx-xxxxxxxxxxxxxxxxx \
--region us-east-1
# VPC-B의 라우트 테이블에 VPC-A CIDR 추가
# (VPC-B에서 VPC-A 방향)
aws ec2 create-route \
--route-table-id rtb-0a1b2c3d4e5f00002 \
--destination-cidr-block 10.0.0.0/16 \
--vpc-peering-connection-id pcx-xxxxxxxxxxxxxxxxx \
--region us-east-1
라우트가 제대로 들어갔는지 확인한다.
aws ec2 describe-route-tables \
--route-table-ids rtb-0a1b2c3d4e5f00001 \
--query 'RouteTables[0].Routes[?VpcPeeringConnectionId!=`null`]' \
--output json \
--region us-east-1
4단계: 보안 그룹 인바운드 규칙 추가
라우트 테이블이 완벽해도 보안 그룹이 막으면 통신이 안 된다. 보안 그룹은 라우트 레이어와 완전히 독립적으로 동작한다. VPC-A의 인스턴스가 VPC-B 인스턴스로 접근하려면, VPC-B 인스턴스의 보안 그룹에 VPC-A CIDR에서 오는 트래픽을 허용하는 인바운드 규칙이 있어야 한다.
# VPC-B 인스턴스 보안 그룹에 VPC-A CIDR 허용 (예: TCP 8080)
aws ec2 authorize-security-group-ingress \
--group-id sg-0a1b2c3d4e5f00002 \
--protocol tcp \
--port 8080 \
--cidr 10.0.0.0/16 \
--region us-east-1
# VPC-A 인스턴스 보안 그룹에 VPC-B CIDR 허용 (응답 트래픽은 상태 추적으로 자동 허용되지만,
# VPC-B에서 VPC-A로 먼저 연결을 시작해야 한다면 이쪽도 열어야 함)
aws ec2 authorize-security-group-ingress \
--group-id sg-0a1b2c3d4e5f00001 \
--protocol tcp \
--port 8080 \
--cidr 10.1.0.0/16 \
--region us-east-1
보안 그룹은 상태 추적(stateful) 방화벽이다. 인바운드를 허용하면 해당 연결의 응답 트래픽은 아웃바운드 규칙 없이도 자동으로 허용된다. 하지만 양쪽 모두 먼저 연결을 시작해야 하는 구조라면 양쪽 모두 인바운드 규칙을 열어야 한다.
- 피어링 연결 요청 → 수락 순서로 진행되며, 수락 전에는 라우트 추가가 의미 없다.
- 라우트 테이블은 VPC-A와 VPC-B 양쪽 모두 업데이트해야 한다.
- 보안 그룹은 라우트와 독립적으로 평가된다 — 마지막 관문이다.
VPC 피어링 설정에 필요한 IAM 권한
피어링 설정 작업을 자동화하거나 별도 역할로 실행한다면 최소 권한 원칙에 따라 아래 권한이 필요하다.
🔽 IAM 정책 예시 (클릭하여 펼치기)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VpcPeeringSetup",
"Effect": "Allow",
"Action": [
"ec2:CreateVpcPeeringConnection",
"ec2:AcceptVpcPeeringConnection",
"ec2:DescribeVpcPeeringConnections",
"ec2:CreateRoute",
"ec2:DescribeRouteTables",
"ec2:DescribeVpcs",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:DescribeSecurityGroups"
],
"Resource": "*"
}
]
}
ec2:CreateRoute와 ec2:DescribeRouteTables는 리소스 수준 제한을 지원하는 정도가 다르다. AWS Service Authorization Reference에서 각 액션의 리소스 수준 지원 여부를 확인하고 적용 가능한 경우 ARN으로 범위를 좁히는 것이 좋다.
실제 운영에서 자주 겪는 함정: 라우트는 됐는데 핑이 안 될 때
피어링 active, 라우트 테이블 양쪽 다 추가, 그런데 핑이 안 된다. 이 상황에서 대부분 보안 그룹을 의심하는데, 실제로는 ICMP를 허용하지 않은 경우가 많다. TCP 8080은 열었지만 ICMP echo는 별도로 열어야 한다.
또 다른 패턴은 서브넷에 커스텀 라우트 테이블이 연결되어 있는데 메인 라우트 테이블만 업데이트한 경우다. 콘솔에서 VPC 라우트 테이블 목록을 보면 메인 라우트 테이블이 눈에 잘 띄어서 거기만 수정하고 끝냈다고 생각하기 쉽다. 실제 서브넷이 어떤 라우트 테이블을 쓰는지는 서브넷 상세 페이지에서 확인해야 한다.
# 특정 서브넷에 연결된 라우트 테이블 확인
aws ec2 describe-route-tables \
--filters Name=association.subnet-id,Values=subnet-0a1b2c3d4e5f00001 \
--query 'RouteTables[*].{RouteTableId:RouteTableId,Routes:Routes}' \
--output json \
--region us-east-1
이 명령으로 아무것도 안 나오면 해당 서브넷은 메인 라우트 테이블을 사용하고 있는 것이다. 결과가 나오면 그 라우트 테이블에 피어링 라우트가 있는지 확인한다.
라우트 테이블은 VPC 단위가 아니라 서브넷 단위로 적용된다. VPC 전체에 라우트를 추가했다고 생각했지만 실제로는 메인 라우트 테이블 하나만 바꾼 것일 수 있다.
VPC Flow Logs로 트래픽 흐름 검증
핑이나 curl로 확인이 어려운 환경이라면 VPC Flow Logs를 활성화해서 실제 패킷이 어디서 막히는지 확인한다. ACCEPT/REJECT 필드를 보면 보안 그룹이나 네트워크 ACL 중 어디서 드롭되는지 구분할 수 있다.
# VPC-A에 Flow Logs 활성화 (CloudWatch Logs로 전송)
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-0a1b2c3d4e5f00001 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs \
--deliver-logs-permission-arn arn:aws:iam::123456789012:role/FlowLogsRole \
--region us-east-1
Flow Logs에서 REJECT가 찍히면 보안 그룹 또는 네트워크 ACL 문제다. 로그에 아무것도 안 찍히면 라우트 테이블 문제일 가능성이 높다 — 패킷이 아예 해당 인터페이스에 도달하지 못한 것이다.
네트워크 ACL은 기본적으로 모든 트래픽을 허용하지만, 커스텀 ACL을 적용했다면 인바운드와 아웃바운드 모두 명시적으로 허용해야 한다. 네트워크 ACL은 상태 비추적(stateless)이기 때문이다.
전체 설정 흐름 요약
- CIDR 충돌 여부를 먼저 확인한다 — 겹치면 피어링 생성 자체가 실패한다.
- 피어링 요청 후 반드시 수락 단계를 거쳐야
active상태가 된다. - 라우트 테이블은 통신에 참여하는 모든 서브넷의 라우트 테이블을 양방향으로 업데이트한다.
- 보안 그룹 인바운드 규칙을 추가한다 — 라우트와 독립적으로 평가된다.
- 네트워크 ACL이 커스텀으로 설정되어 있다면 인바운드/아웃바운드 모두 확인한다.
VPC 피어링 설정 완료 후 다음 단계
VPC 피어링은 1:1 연결이다. 연결해야 할 VPC가 늘어날수록 피어링 수가 기하급수적으로 증가한다. VPC가 3개 이상이고 허브-앤-스포크 구조가 필요하다면 AWS Transit Gateway를 검토할 시점이다. Transit Gateway는 전이적 라우팅을 지원하므로 VPC 수가 많아질수록 관리 복잡도가 낮아진다.
피어링 설정 후 DNS 해석이 필요하다면 피어링 연결의 DNS 해석 옵션을 활성화해야 한다. 기본값은 비활성화 상태다.
# 피어링 연결의 DNS 해석 활성화 (Requester 측)
aws ec2 modify-vpc-peering-connection-options \
--vpc-peering-connection-id pcx-xxxxxxxxxxxxxxxxx \
--requester-peering-connection-options AllowDnsResolutionFromRemoteVpc=true \
--region us-east-1
# Accepter 측
aws ec2 modify-vpc-peering-connection-options \
--vpc-peering-connection-id pcx-xxxxxxxxxxxxxxxxx \
--accepter-peering-connection-options AllowDnsResolutionFromRemoteVpc=true \
--region us-east-1
공식 문서: Amazon VPC Peering Guide
용어 정리
| 용어 | 설명 |
|---|---|
| VPC 피어링 (VPC Peering) | 두 VPC 사이에 프라이빗 네트워크 경로를 만드는 AWS 기능. 인터넷을 경유하지 않는다. |
| 피어링 연결 ID (pcx-) | 피어링 연결을 식별하는 고유 ID. 라우트 테이블의 타겟으로 사용된다. |
| 전이적 라우팅 (Transitive Routing) | A→B→C처럼 중간 VPC를 경유하는 라우팅. VPC 피어링은 지원하지 않는다. |
| 네트워크 ACL | 서브넷 수준의 상태 비추적 방화벽. 인바운드/아웃바운드 규칙을 모두 명시해야 한다. |
| VPC Flow Logs | VPC 내 네트워크 인터페이스를 통과하는 IP 트래픽 정보를 캡처하는 기능. |
댓글
댓글 쓰기