NAT 인스턴스가 왜 필요함? (패킷: 어디로 가야하오)
ALB 뒤에 숨긴 인스턴스가 인터넷이 안 되는 이유. IGW, Private Subnet, 그리고 t3.micro NAT 인스턴스를 활용한 격리 기록.
어제 호기롭게 퍼블릭 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(이미지) 복사 방식을 선택했다.
- 기존 무능력해진 앱 서버의 AMI(이미지)를 구워버린다.
- 해당 AMI로 인스턴스를 새로 런칭하되, 네트워크 설정을 우리가 미리 만들어둔
slowflowsoft-subnet-private1(프라이빗 서브넷)으로 강제 지정한다. - 당연히 퍼블릭 IP 자동 할당은 비활성화한다.
4. 최종 결과: 완벽한 격리와 숨통 트인 인터넷
대이사를 마치고 나니 드디어 완벽한 아키텍처와 네트워크 지도가 완성됐다.
- Public Subnet: 대장 NAT 인스턴스 (퍼블릭 IP 보유) -> IGW를 통해 인터넷과 직접 통신.
- Private Subnet: 우리의 앱 서버들 (사설 IP만 보유, ALB 뒤에 꼭꼭 숨음) -> 라우팅 테이블(
0.0.0.0/0) 타겟을 NAT 인스턴스로 설정.
이렇게 세팅하고 프라이빗 서버에 접속해 테스트를 돌려보니 꽉 막혀있던 체증이 확 내려갔다. 망할 Bedrock API 응답도 1초 만에 날아오고, npm install의 진행 바가 미친 듯이 올라가기 시작했다.
결론
- 다 귀찮고 넉넉하다면 NAT Gateway 쓰면 된다. 하지만 나처럼 월 6만 원이 아깝다면 t3.micro (혹은 t4g.nano) NAT 인스턴스가 짱이다.
- 무조건 서브넷 분리해라. 퍼블릭 IP를 뗐다고 그 서버가 프라이빗해지는 게 아니다. 프라이빗 서브넷 안으로 넣어야 진짜 격리다.
마침.
다른 글 보기
공부 하기 싫어서 만든 프로젝트: 지구 패치노트
오늘도 평화로운 지구서버
로드밸런서 셋팅 후 연결 지연 현상
ALB 연결 후 새 창에서 발생하는 21초 대기 시간의 원인과 해결 과정 기록.
아, 당신이 ALB시군요... (feat. 퍼블릭 IP 한도 초과)
퍼블릭 IP 부족 사태로 인한 ALB 도입과 최적화 기록.
[DevOps] init-letsencrypt.sh 분석 (매번 찾기 귀찮아서 정리)
Nginx + Let's Encrypt 닭과 달걀 문제 해결 스크립트 분석 및 개인 메모.