일단 내가 구성한 아키텍처는 다음과 같았다.
public subnet, private subnet을 각각 1개씩 두고 public subnet에 ec2를 1개, private subnet에 ec2를 2개 두는 것이다.
public subnet 내의 인스턴스에는 API Gateway, Eureka Server를 위치하도록 하고, private subnet 내의 인스턴스 2개는 비즈니스 로직을 처리하는 모듈들을 분리해 위치하도록 구성하는 것이다. 따라서 public EC2 instance에만 인터넷 게이트웨이를 연결해두었다.
하지만 Private EC2에서도 도커 등을 설치하기 위해서 인터넷 접근이 가능하도록 구성해야했다. 그렇다면 NAT를 구성해주어야 하는데 인프라 비용을 최대한 절감하고 싶었기 때문에 별도로 NAT 인스턴스를 구성하는 것은 지양하고자 했다.
따라서 public subnet 내의 EC2 인스턴스를 NAT 처럼 사용하도록 구성하고자 했다.
구성 목표
1. private EC2에서 인터넷 접근이 가능
2. public EC2를 NAT 인스턴스처럼 사용
3. Internet Gateway는 public subnet에만 연결
📍초기 설정
이에 설정에 관해 작성된 블로그 글들도 많았고 이전에 이 내용을 실습했었기 때문에 쉽게 설정할 수 있었다. 나는 다음과 같이 진행했다.
1. Public subnet에 internet gateway 연결

인터넷 게이트웨이를 하나 생성해준다.

퍼블릭 서브넷에 연결된 라우팅 테이블에 들어가 0.0.0.0/0을 위에서 생성한 인터넷 게이트웨이로 라우팅해준다.
2. private subnet의 라우팅 테이블 수정

0.0.0.0/0의 트래픽을 public EC2 instance로 연결해준다.
3. 보안그룹 설정
퍼블릭 서브넷 보안그룹 (인바운드 규칙 편집)
- private CIDR을 허용하도록 설정해준다.

4. Public EC2 인스턴스 설정 변경

AWS EC2 인스턴스에서 public EC2 인스턴슬르 선택하고 작업 > 네트워킹 > 소스/대상 확인 변경을 설정한 다음

중지를 체크한다.
다음으로 public EC2에 접속한 다음
# Public EC2에서 실행
# 1. IP 포워딩 영구 활성화
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
# 2. NAT 규칙 추가
sudo iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o ens5 -j MASQUERADE
# 3. iptables 규칙 영구 저장
sudo apt update
sudo apt install iptables-persistent -y
# 설치 중 "현재 규칙 저장?" → Yes 선택
# 또는 수동 저장
sudo netfilter-persistent save
다음과 같이 설정해준다.
- net.ipv4.ip_forward = 1을 설정하는 이유
기본적으로 리눅스는 내가 목적지인 패킷만 처리한다. 패킷이 들어왔을 때 목적지 IP가 자기 자신이 아니면 라우팅하지 않고 버리게된다. (Host 모드) 따라서 '내가 목적지가 아니어도 다른 네트워크로 패킷을 전달해도 된다'는 설정을 해줘야한다. - iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o ens5 -j MASQUERADE 을 설정하는 이유
인터넷으로 나가기 직전 Source IP를 NAT 인스턴스의 퍼블릭 IP로 변경해주는 것이다. 이 설정을 해주지 않으면 Source IP는 사설 IP로 설정되어있기 때문에 공인 인터넷 라우터는 패킷을 그냥 버린다.
📍발생한 문제
private EC2에서 ping 8.8.8.8 명령어를 실행하면
public EC2에서 tcpdump 결과를 확인했을 때 ICMP echo request(10.0.1.x → 8.8.8.8)만 보이고 echo reply는 돌아오지 않았다. 즉 요청을 받는데 이것이 인터넷으로 못하고 있는 것이다.
📍원인 분석
NAT 인스턴스, 즉 퍼블릭 EC2인스턴스에 이미 전에 도커를 설치한 상황이었다. 도커는 설치 시에 다음을 자동으로 수행한다.
- FORWARD 체인의 기본 정책을 DROP으로 변경한다.
- 다음 체인을 자동으로 삽입한다.
DOCKER-USER
DOCKER-FORWARD
Docker가 없는 경우 패킷은 본래 다음과 같이 처리된다. (policy ACCEPT)
[패킷 도착]
↓
PREROUTING
↓
ROUTING DECISION
↓
FORWARD
↓
POSTROUTING
↓
[다음 홉으로 전달]
Docker가 설치된 경우 패킷은 다음과 같이 처리된다. (policy DROP)
[패킷 도착]
↓
PREROUTING
↓
ROUTING DECISION
↓
FORWARD
├─ DOCKER-USER (사용자 정의, 비어있으면 그냥 통과)
├─ DOCKER-FORWARD (도커 전용 허용)
└─ (나머지 규칙)
↓
POSTROUTING
↓
[다음 홉으로 전달]
이 경우 도커가 허용한 것만 통과된다.
따라서 private → NAT instance까지는 패킷이 도착하지만 NAT → 인터넷으로 나가는 포워딩 패킷이 FORWARD 단계에서 도커 관련이 아니기 때문에 전부 DROP되는 것이다.
[문제 상황 파악 방법]
sudo iptables -L FORWARD -v -n
을 실행했을 때
Chain FORWARD (policy DROP 325 packets, 20764 bytes)
pkts bytes target prot opt in out source destination
325 20764 DOCKER-USER 0 -- * * 0.0.0.0/0 0.0.0.0/0
325 20764 DOCKER-FORWARD 0 -- * * 0.0.0.0/0 0.0.0.0/0
다음과 같이 policy DROP이 설정되어있다면 문제가 되는 상황이다.
📍해결 방법
Docker는 패킷을 다음 순서로 처리함:
FORWARD
└─ DOCKER-USER ← 여기가 최우선
└─ DOCKER-FORWARD
└─ DROP
따라서 NAT 허용은 반드시 DOCKER-USER에 작성해야 한다.
DOCKER-USER 체임에 명시적으로 허용 규칙을 추가해주었다.
# FORWARD 체인을 ACCEPT로 변경
sudo iptables -P FORWARD ACCEPT
# 영구 저장
sudo netfilter-persistent save
📍결과
private EC2에서 ping 8.8.8.8을 실행했을 때 정상적으로 응답을 받음을 확인하면 성공한 것이다.

'AWS' 카테고리의 다른 글
| EC2 저장공간 부족 문제 해결 (0) | 2026.02.20 |
|---|---|
| IntelliJ에서 EC2 원격 접속 (0) | 2026.01.02 |
| EC2 서버에 젠킨스 도커 컨테이너 띄우기 (0) | 2025.12.02 |
| AWS 스왑 메모리 설정 (0) | 2025.12.01 |