[AWS] VPC의 NAT 비용을 줄여보자 :: Ubuntu로 NAT 인스턴스 만들기
두 배가 되어버린 AWS 비용..
1달 전에는 테스트용으로 그냥 Public IP로 다 통신하도록 하고 최저 사양으로 다 올렸었다.
하지만 보안이나 비용적인 측면, 나중에 할 부하 테스트를 생각해서 Amazon EC2 리소스가 인터넷에 노출되는 방식을 제어하기 위해 싹다 갈아엎었다.
이 글(https://coding-groot.tistory.com/165)처럼 VPC를 구성해서 AWS 리소스를 재배치했다.
글 요약
- VPC를 구성해서 Private, Public Subnet을 만들었다.
- 외부 인터넷에 노출되지 않아도 되는 인스턴스들은 Public IP를 할당하지도 않았고 Private Subnet으로 격리시켜서 Internet Gateway와의 직접적인 접점을 없앴다.
- Private Subnet에 있는 친구들은 Log를 수집되도록 다른 서버에 쏴줘야 하고 주기적으로 서버 업데이트도 해야 해서 NAT를 달아줬다.
그랬더니 비용이 쉽지 않아졌다.
매일 2.75$ 정도 나오던 비용이 7.10$ 정도 나오기 시작했다.. 물론 그래도 싼 편이긴하다. (학생 기준 비싸졌당ㅎㅎ)
비용 증가의 원인
VPC 자체는 무료다.
VPC 내부적으로 Private IP로 오가는 트래픽도 대부분 무료이다(그래서 부하 테스트가 가능해진다).
비용 증가의 원인은... 아래의 사진을 보면 나온다.
바로 보인다.
APN2-NatGateway-Hours... 이놈이 원인이다.
AWS는 서버 생성, 사용 비용 말고도 데이터가 전이하는 것도 비용이 부과된다.
기본적으로 AWS 안으로 들어오는 In-Bound 트래픽은 무료이다. 그리고 대부분 AWS에서 밖으로 나가는 것은 비용이 부과된다.
그렇다.. NAT Out-Bound 비용이 계속 부과되고 있었던 것이다.
한국은 NAT를 통해 나가는 Out-Bound 트래픽 비용이 시간당 (처리된 GB 당 0.059$)이다.
그냥 간단하게 Log만 Push하고 있어서 시간당 1GB는 쓰지 않았다. 그래서 한 NAT당 0.059*(24시간) = 1.416$가 부과되었다.
이중화해서 NAT를 AZ별로 두 개를 써서 매일 2.832$가 부과되었다.
NAT는 주로 주기적으로 (VPC 외부에 위치한) New Relic으로 Log를 Push하는데에 쓰고 있다. 진짜 별로 안 쓴다.
얼핏보면 싸 보이지만 한 달이 누적되면 84.96$(=2.832*30)이다.
별로 사용하지도 않는데... 하는 것에 비해 시간 당 0.59$는 너무 돈낭비같았다.
해결법: AWS NAT Gateway말고 NAT를 EC2로 만들어서 쓰자
비용 절감을 위한 해결법은 AWS NAT Gateway를 사용하지 않고 리눅스 인스턴스를 하나 뛰운 뒤 마스커레이드 설정해서 NAT로 사용하는 것이다.
리눅스에는 iptable이나 netfilter와 같은 유용한 네트워크 기능이 많고 커널단에서 NAT와 같은 기능이 지원된다. 관련 설정을 활성화하면 바로 사용할 수 있다.
엄청나게 많은 트래픽을 보내고 있는 것이 아니고 상태를 모니터링하는데 쓰는 로그라 몇 개 드랍되어도 크게 상관이 없었다.
때문에 성능이나 안정성은 큰 상관이 없었다. 게다가 대부분의 Router도 대부분 리눅스 커널 기반으로 돌아가기 때문에 NAT를 인스턴스로 써도 상관 없을 것이라 생각했다.
NAT의 기본 요금인 시간당 0.59달러는 하는거에 비해 너무 비싸다.. 적당한 EC2 리눅스 인스턴스로 NAT 비용을 아껴보자.
참고로 EC2는 송신(Out-Bound) 인터넷 데이터 100GB를 매달 무료로 준다. 그래서 시간 당 EC2 인스턴스 비만 내면 된다.
NAT Gateway 대신 리눅스 NAT 사용하는 법
1. EC2를 만든다
MultiAZ 구성을 하려면 모든 Zone마다 하나의 NAT가 필요하다.
나는 ap-northeast-2a AZ(Availabilty Zone)에 NAT를 하나 만들어서 그 AZ에 있는 NAT Gateway를 대체하는 법을 보여주겠다.
ap-northeast-2a의 Public Subnet에 Ubuntu EC2 하나를 만든다.
(Ubuntu는 내가 편해서 선택했다. 얘도 잉여스럽게 자원이 낭비되고 있으면 bastion host로 사용할까 싶다)
인스턴스 타입은 t2.micro로 싸게 싸게 설정해준다.
찾아보니 t2.micro는 2018년도 기준 네트워크 대역폭이 ~70 MBit/s 정도 나온다고 하는데 이정도면 충분하다 판단했다.
(출처: https://stackoverflow.com/questions/18507405/ec2-instance-typess-exact-network-performance)
ssh하기 위한 Key Pair도 하나 넣어주자.
제일 중요한 네트워크 설정을 해보자.
내 NAT가 위치할 VPC를 선택하자.
그리고 미리 만들어둔 Public용 Subnet을 선택한다.
나는 위에서 미리 말했듯이 multiAZ구성을 할 것이라서 AZ별로 다 만들어야한다.
일단 ap-northeast-2a에 먼저 만들어보자. ap-northeast-2a에 위치한 Public Subnet을 선택했다.
외부와 통신하려면 Public IP도 하나 필요하다. 내 상황에서는 굳이 Static IP일 필요는 없어서 Elastic IP는 따로 할당하지 않았다.
Security Group도 NAT용으로 새로 만들어주자.
그리고 나중에 이걸 만들고 이 Security Group 말고도 인스턴스에 Default Security Group도 추가해줘야 한다. 그래야지 Private에서 발생한 모든 트래픽이 차단되지 않고 오갈 수 있다. 하지만 이 방식은 모든 통신이 허용되어서 보안에 더 취약하다. 더 세부적으로 제어하고 싶다면 인바운드 규칙을 하나씩 추가해서 어떤 Source가 올 때 어떤 포트를 허용할지 추가해주자.
참고로 AWS에서 권장하는 NAT의 Security Group은 다음과 같다.
나는 일단 In-Bound 규칙에 접속용으로 ssh만 추가해주고 나중에 VPC의 Default 보안 그룹을 추가해줘도 모든 내부 트래픽을 허용해줄 예정이다.
이제 나머지 용량 설정같은 것도 원하는 대로 하고 Launch Instance를 누른다.
2. 만들어진 Instance로 접속(ssh)한다
만들 때 등록한 키페어로 ssh하자.
ssh -i 키페어_위치 ubuntu@인스턴스_Public_IP
3. 서버 사전 작업
Root 비밀번호도 설정해주고 미리 패키지도 업데이트하고 업글해주자.
# set root password
sudo passwd
# update and upgrade apt package
sudo apt install && sudo apt upgrade -y
업그레이드 후에는 reboot을 한번해주자.
sudo reboot
재부팅 후에 다시 ssh한다.
4. 네트워크 명령어를 깔아주자
ifconfig와 같은 명령어를 사용하기 위해 net-tools를 깔아주자. 나중에 테스트용으로 사용할 것이면 traceroute도 깔아주자.
sudo apt install net-tools
# sudo apt install traceroute
현재는 EC2 인스턴스의 hostname이 Private IP로 출력된다. 이쁘게 바꿔주자. (굳이 안 해줘도 된다)
sudo hostnamectl set-hostname 이쁜이름
이제 다시 재접속을 하면 public-nat-anp2a로 이름이 출력된다.
5. IP Forwarding과 마스커레이딩을 활성화한다
먼저 IP Forwarding를 활성화 한다.
sudo sysctl -w net.ipv4.ip_forward=1
/proc/sys/net/ipv4/ip_forward 값을 1로 바꿔도 되고 위와 같이 명령어로 해도 된다. 하지만 영구적으로 적용되지는 않는다.
Reboot을 할 때마다 이 설정이 풀리는 것이 싫으면 root권한으로 /etc/sysctl.conf 파일을 열어서 ip_forward 부분의 주석을 해제하고 저장해주자.
이제 iptables 명령어로 마스커레이딩을 활성화한다.
sudo /sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
Private Subnet에 위치한 다른 컴퓨터들도 마스커레이드(우분투 인스턴스)로 패킷을 보내면 인터넷에 연결할 수 있다.
NAT와 비슷한 기능이다.
Iptables도 Reboot할 때마다 풀린다. 설정한 정책을 영구적으로 가져가고 싶으면 iptable-persistent 같은 것을 찾아보자.
우분투 22부터는 아래와 iptables-save 명령어로 현재 iptable 정책을 영구적으로 적용할 수 있다. (없을 수도 있다)
sudo /sbin/iptables-save
이제 EC2 인스턴스 내에서 할 작업은 끝났다.
6. Source/Destination check을 비활성화한다
라우터에는 Reverse Path Forwarding이라는 개념이 있다.
기본적으로 쓸데없는 패킷이 router간에 왔다갔다 돌아나오는 것 방지하는 설정이 되어 있다.
인스턴스를 NAT로 사용하려면 이것을 꺼야 한다. 아니면 모든 것이 잘되어 있어도 이상하게 패킷이 오지 않는다.
EC2에서 Public NAT용으로 만든 인스턴스를 선택하고 Action > Networing > Change source/desination check을 클릭한다.
Stop을 체크하고 Save를 누른다.
친절하게 NAT 인스턴스면 꺼라고 안내가 나온다.
우분투를 NAT로 사용하기 위한 준비는 끝났다.
이제 Route Table로 Private Subnet에서 외부로 나가는 패킷을 NAT Gateway 대신 마스커레이드로 설정된 우분투 인스턴스로 보내주면 된다!
7. Route Table을 수정한다
Route Table을 통해 외부 트래픽을 마스커레이드를 설정한 EC2 인스턴스로 보내면 NAT로써 작동된다.
기존의 NAT Gateway를 비활성화하고 이 EC2 인스턴스를 사용하도록 바꿔보자.
VPC 대시보드의 Route Table로 가서 northeast-2a에 위치한 Private Subset의 Route Table을 선택한다.
Routes 탭에서 Edit routes를 누른다.
지금은 아래와 같이 외부로 나가는 트래픽은 northeast-2a에 위치한 NAT Gateway로 가도록 되어있을 것이다.
NAT로 가고 있는 것을 Ubuntu EC2로 가도록 수정해보도록 하겠다.
외부 트래픽(0.0.0.0/0)의 Target을 지우고 Instance를 선택한다.
이때 Public NAT용으로 만든 EC2 인스턴스를 선택한다.
Sava changes를 누른다.
끝이당. 이제 잘 작동되는지 확인하면 된다.
8. 테스트
잘 통신이 되는지 ping이나 미리 깔아둔 traceroute 명령어 같은 것으로 확인해보자.
Private to Private Ping Test
같은 VPC에 있는 애들끼리 private로 ping을 주고 받을 수 있는지 먼저 테스트합니다.
ping PRIVATE_IP
만약 이것이 안 되면 Security Group(자세한 것은 아래에 Default Security Group추가 방법 참고)이나 local로 보내는 Route Table을 다시 한번 확인해보자.
"Private Subnet -> EC2 Instance -> Internet" Ping Test
다음으로 Private Subnet에 위치한 인스턴스에서 구글 DNS로 Ping이 가는지 테스트해보겠다.
Public NAT에서는 tcpdump를 실행해서 icmp 패킷을 덤프를 뜨도록한다.
# tcpdump로 icmp 덤프, -nn 호스트명이랑 port를 resolve하지 않겠다는 옵션이다.
sudo tcpdump icmp -nn
이 명령어를 실행한 상태에서 새로운 터미널에서 같은 AZ의 Private Subnet에 위치한 아무 인스턴스로 ssh한다.
(Private Instance에 ssh는 같은 VPC에 위치한 Public Instance를 프록시로 이용해서 접속 할 수 있다. 다음과 같은 글을 참고하자. https://help.dreamhost.com/hc/en-us/articles/215879497-How-to-SSH-to-a-private-instance-without-a-public-IP-address)
Private Instance에서 구글 DNS(8.8.8.8)로 ping을 날려보자.
ping 8.8.8.8
위 영상처럼 Private Instance에게 ping 응답이 와야하고 NAT에서 덤프로 확인한 것은 아래와 같이 Request가 가고 Reply가 와야한다.
ubuntu@public-nat-anp2c:~$ sudo tcpdump icmp -nn
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:58:40.566194 IP PRIVATE_IP > 8.8.8.8: ICMP echo request, id 32646, seq 1, length 64
17:58:40.566216 IP PRIVATE_IP > 8.8.8.8: ICMP echo request, id 32646, seq 1, length 64
17:58:40.595394 IP 8.8.8.8 > PRIVATE_IP: ICMP echo reply, id 32646, seq 1, length 64
17:58:40.595405 IP 8.8.8.8 > PRIVATE_IP: ICMP echo reply, id 32646, seq 1, length 64
...
Private IP로 다른 인스턴스끼리 ping 자체가 안 가면 Security Group 문제일 가능성이 높다. 인바운드 규칙으로 Private끼리 통신이 되도록 해놓았거나 인스턴스의 Security Group으로 해당 VPC의 Default Group을 추가해줬는지 확인해보자.
Default Security Group을 추가하는 방법
Security Group을 추가하는 방법은 다음과 같다.
default를 검색해서 선택한 후 Add security group을 누른다.
그리고 Save를 누르면 인스턴스의 Security Group을 추가할 수 있다.
9. NAT Gateway를 지운다
비용 청구 멈춰! NAT Gateway를 없애버리자.
NAT에 붙어있던 Elastic IP address도 삭제(Release)되었는지 확인하자.
끝!
나머지 AZ도 이런식으로 NAT Gateway를 EC2 Instance로 갈아끼우면 된다!
댓글
이 글 공유하기
다른 글
-
[AWS] Elastic Beanstalk graceful shutdown (feat. AutoScaling Lifecycle Hook - TERMINATING)
[AWS] Elastic Beanstalk graceful shutdown (feat. AutoScaling Lifecycle Hook - TERMINATING)
2022.11.10 -
[AWS] AWS SES API를 사용해서 메일 보내기 (feat. NodeJS)
[AWS] AWS SES API를 사용해서 메일 보내기 (feat. NodeJS)
2022.10.09 -
[AWS] S3를 통해 정적인 Asset 호스팅하기
[AWS] S3를 통해 정적인 Asset 호스팅하기
2022.08.22 -
[AWS] VPC에 Subnet, NAT Gateway, Internet Gateway를 구성해보자
[AWS] VPC에 Subnet, NAT Gateway, Internet Gateway를 구성해보자
2022.08.07