NAT 인스턴스가 왜 필요함? (패킷: 어디로 가야하오)

ALB 뒤에 숨긴 인스턴스가 인터넷이 안 되는 이유. IGW, Private Subnet, 그리고 t3.micro NAT 인스턴스를 활용한 격리 기록.

Jun Noh

어제 호기롭게 퍼블릭 IP를 싹 다 밀어버리고 ALB 하나로 트래픽을 몰아받는 세팅을 마쳤다.

나름? 배운 것도 많고, 생각보다는 시간을 덜 쓴 거 같아서 뿌듯해하고 있었는데.. 역시나, 세상살이 내 마음대로 되는 게 하나도 없다.

그냥 아무 생각 없이 새 서비스를 테스트하고 있었는데, 연동 기능이 먹통이다.

엥? 또 AWS Bedrock API 응답도 안 온다. 뿐만 아니다.

프론트엔드 빌드 찌꺼기를 지우고 S3에 재업로드하려는데 업로드도 실패고, 하다못해 npm install 마저 무한 로딩이다.

100% 인터넷에 문제가 있다.

원인을 파악해보니, ALB 뒤로 숨어버린 서버들이 외부 인터넷(IGW)으로 나갈 길을 잃고 패킷 미아가 된 것이었다.

서버들이 퍼블릭 서브넷 공간 안에 있는데 정작 본인들의 퍼블릭 IP가 없으니, 밖으로 나가려고 아무리 발버둥을 쳐도 IGW(인터넷 게이트웨이)가 문을 열어주지 않는 환장할 노릇.

AWS 공식 문서에서는 이럴 때 ‘NAT Gateway’를 쓰라고 친절하게 안내하지만, 월 6만 원이 넘는 고정 비용을 내야 한다.

내가 돈 아끼려고 퍼블릭 IP 떼고 이 사달을 냈는데, 그 통행료를 낼 바엔 차라리 원래대로 돌아가는 게 맞다.

그래서 결국, 고정 비용 없이 외부와 통신하는 커스텀 NAT 인스턴스를 직접 구축하기로 했다.

이 과정에서 겪은 어이없는 삽질과 설정을 기록해 두려고 한다.


1. 1단계: 대장 서버(NAT 인스턴스) 준비

우선, 인터넷과 맞닿아 있는 ‘퍼블릭 서브넷’에 제일 싸고 만만한 t3.micro 인스턴스를 하나 띄웠다.

이 녀석이 이제 다른 서버들의 인터넷 통신을 대신해 줄 통로이자 대장 역할을 할 거다.

  • AWS 콘솔 설정: 인스턴스 선택 -> 네트워킹 -> [소스/대상 확인 변경] -> [중지(Disable)] 상태로 바꿨다. (이걸 안 하면 남의 패킷이 들어왔을 때 자기 게 아니라고 냅다 버린다. 핵심 중의 핵심.)
  • OS 내부 설정 (Ubuntu): 이 인스턴스에 접속해서 IP 포워딩과 라우팅(마스커레이딩) 규칙을 심어줬다.
# 1. IP 포워딩 활성화 (내부 패킷을 외부로 토스 가능하게)
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# 2. iptables 규칙 추가 (ens5는 네트워크 인터페이스 이름)
# 프라이빗에서 온 패킷들의 출발지 주소를 자기(NAT 인스턴스) IP로 속여서 내보냄
sudo iptables -t nat -A POSTROUTING -o ens5 -j MASQUERADE

자, 이렇게 통로 준비를 마쳤다. 이제 라우팅 테이블만 고치면 끝날 줄 알았다.


2. “왜 퍼블릭 서브넷에서는 NAT가 안 될까?”

이 구간에서 소중한 시간을 제일 많이 날려먹었다.

앞서 세팅해 둔 앱 서버들은 여전히 퍼블릭 서브넷에 있었다.

‘어차피 퍼블릭 IP 뗐으니까 라우팅 테이블에서 외부(0.0.0.0/0) 타겟만 방금 만든 NAT 인스턴스로 돌리면 되겠지?‘라고 생각했다.

통신이 전혀 안 된다.

단 1바이트도 오가지 않았다.

  • 원인: AWS의 네트워크는 냉혹했다. 퍼블릭 서브넷에 속한 인스턴스는 태생적으로 인터넷 게이트웨이를 통해 직접 밖으로 나가려는 본능(?)이 있다. 아무리 라우터에 대고 NAT 인스턴스로 가라고 설정해봤자, 퍼블릭 서브넷의 기본 속성과 충돌이 나면서 패킷이 갈 곳을 잃고 멍청하게 소멸해 버리는 것이다.
  • 교훈: NAT 인스턴스를 타고 인터넷을 나가게 하려면, 앱 서버들을 반드시 프라이빗 서브넷이라는 완전히 격리된 ‘별도의 방’으로 멱살 잡고 끌고 가야 한다.

3. 프라이빗 서브넷으로의 대이사

문제는 이미 돌아가고 있는 인스턴스는 콘솔에서 버튼 띡 누른다고 서브넷이 바뀌지 않는다는 거다. 어쩔 수 없이 조금 귀찮지만 AMI(이미지) 복사 방식을 선택했다.

  1. 기존 무능력해진 앱 서버의 AMI(이미지)를 구워버린다.
  2. 해당 AMI로 인스턴스를 새로 런칭하되, 네트워크 설정을 우리가 미리 만들어둔 slowflowsoft-subnet-private1(프라이빗 서브넷)으로 강제 지정한다.
  3. 당연히 퍼블릭 IP 자동 할당은 비활성화한다.

4. 최종 결과: 완벽한 격리와 숨통 트인 인터넷

대이사를 마치고 나니 드디어 완벽한 아키텍처와 네트워크 지도가 완성됐다.

  • Public Subnet: 대장 NAT 인스턴스 (퍼블릭 IP 보유) -> IGW를 통해 인터넷과 직접 통신.
  • Private Subnet: 우리의 앱 서버들 (사설 IP만 보유, ALB 뒤에 꼭꼭 숨음) -> 라우팅 테이블(0.0.0.0/0) 타겟을 NAT 인스턴스로 설정.

이렇게 세팅하고 프라이빗 서버에 접속해 테스트를 돌려보니 꽉 막혀있던 체증이 확 내려갔다. 망할 Bedrock API 응답도 1초 만에 날아오고, npm install의 진행 바가 미친 듯이 올라가기 시작했다.


결론

  1. 다 귀찮고 넉넉하다면 NAT Gateway 쓰면 된다. 하지만 나처럼 월 6만 원이 아깝다면 t3.micro (혹은 t4g.nano) NAT 인스턴스가 짱이다.
  2. 무조건 서브넷 분리해라. 퍼블릭 IP를 뗐다고 그 서버가 프라이빗해지는 게 아니다. 프라이빗 서브넷 안으로 넣어야 진짜 격리다.

마침.

다른 글 보기