NAT Instance를 통해 Private Subnet에서 인터넷 접근하기(2024)
작성 일자 : 2024년 04월 19일
본 포스팅에서 다루는 내용
- 발단
- NAT Instance를 위한 VPC 생성하기
- NAT Instance를 위한 보안그룹 생성하기
- NAT AMI(Amazon Machine Image) 생성하기
- NAT Instance 시작하기
- source/destination 체크 해제하기
- Private Subnet 라우팅 테이블 수정하기
- 테스트 사전 작업
- 테스트하기
발단
현재 필자가 작업하고 있는 프로젝트의 스프링부트 백엔드 테스트 서버는 AWS의 Public Subnet에 위치하고 있으며, ECS(EC2 Instance 기반) 클러스터를 통해 배포되어 있습니다. 백엔드 로직에는 사용자가 곡의 음원과 이미지를 업로드하고, 스프링부트 서버는 이를 S3 버킷에 저장하는 기능이 포함되어 있습니다. 그런데, 이 기능을 테스트 서버에서 테스트하던 중, S3 버킷에 파일이 업로드되지 않는 문제가 발생하였습니다.
2024-04-19T09:22:06.488Z ERROR 1 --- [nio-8080-exec-1] c.e.d.g.utils.uploader.ImageUploader : Failed to upload thumbnail image: Unable to execute HTTP request: Connect to this-is-the-bucket.s3.ap-northeast-2.amazonaws.com:443 [this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0] failed: Connect timed out, use default image instead
2024-04-19T09:23:11.966Z ERROR 1 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/api/v1] threw exception [Request processing failed: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: Connect to this-is-the-bucket.s3.ap-northeast-2.amazonaws.com:443 [this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0, this-is-the-bucket.s3.ap-northeast-2.amazonaws.com/0.0.0.0] failed: Connect timed out] with root cause
java.net.SocketTimeoutException: Connect timed out
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:551) ~[na:na]
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:602) ~[na:na]
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
이 문제는 테스트 서버가 Public Subnet에 위치해 있음에도 불구하고 Public IP가 할당되지 않아 컨테이너가 인터넷에 접근할 수 없었기 때문에 발생한 문제입니다. 현재 사용 중인 ECS 클러스터는 EC2 Instance를 기반으로 하며, 이 EC2 Instance는 awsvpc
네트워크 모드로 구성된 태스크를 호스팅하고 있습니다. 그러나 AWS에서는 awsvpc
네트워크 모드로 구성된 태스크를 호스팅할 경우, 태스크에 대한 ENI(Elastic Network Interface)에 Public IP 주소를 제공하지 않습니다.
따라서 기존에 Public Subnet에 위치한 EC2 Instance를 Private Subnet으로 전환하고, NAT Instance를 통해 컨테이너가 인터넷에 접근할 수 있도록 구성을 변경하기로 결정했습니다. 이번 포스팅에서는 NAT Gateway와 NAT Instance의 차이점에 대해 알아보고, NAT Instance를 생성하는 방법에 대해 알아보겠습니다.
When hosting tasks that use the awsvpc network mode on Amazon EC2 Windows instances, your task ENIs aren't given public IP addresses. To access the internet, launch tasks in a private subnet that's configured to use a NAT gateway. - AWS - awsvpc network mode
NAT 이란?
인터넷에 엑세스하려면 하나의 Public IP 주소가 필요합니다. 그러나 저희는 사설 네트워크에서 Private IP를 사용하는 여러 인스턴스들을 운영할 수 있으며, 이러한 Private IP 주소를 Public IP 주소로 변환해 인터넷에 접근할 수 있도록 하는 기술이 NAT(Network Address Translation)입니다. NAT는 Private IP 주소를 Public IP 주소로 변환하거나 그 반대로 변환하는 기술로, Private IP 주소를 사용하는 여러 호스트들이 하나의 Public IP 주소를 공유하여 인터넷에 접근할 수 있도록 합니다.
NAT 기능을 사용하면, Public IP 주소의 부족 문제를 해결할 수 있고, 내부 네트워크의 보안을 강화하는데도 도움이 됩니다. 사설 네트워크 내부의 구조가 외부에 노출되지 않기 때문에, 외부에서 내부 네트워크의 특정 장치를 직접적으로 타겟팅하기 어렵게 합니다.
AWS 환경에서는 NAT Gateway나 NAT Instance를 통해 이러한 NAT 서비스를 구현할 수 있습니다. 이들은 Private Subnet에 위치한 리소스가 인터넷에 접근할 수 있도록 해주며, Public IP 주소를 이용하여 인터넷과 통신하게 됩니다.
NAT Gateway 와 NAT Instance 의 차이점
NAT(Network Address Translation) 서비스를 AWS 환경에서 구현할 때, 선택할 수 있는 두 가지 주요 옵션이 있습니다: NAT Gateway와 NAT Instance. 이 둘은 기본적으로 같은 기능을 제공하지만 관리, 성능, 비용 면에서 몇 가지 중요한 차이점이 있습니다.
NAT Gateway
NAT Gateway는 AWS에서 관리하는 서비스로, 사용자가 관리할 필요가 없습니다. 이는 고가용성과 확장성을 자동으로 제공하며, AWS에서 패치와 업데이트를 관리합니다. NAT Gateway는 일반적으로 높은 처리량과 낮은 지연 시간을 제공하며, 설정이 간단하고 사용하기 쉽습니다. 다만, NAT Gateway 서비스는 사용하는 시간과 데이터 양에 따라 비용이 발생하며, 이는 NAT Instance를 사용할 때보다 비용이 더 클 수 있습니다.
NAT Instance
NAT Instance는 사용자가 EC2 Instance를 직접 관리하는 방식입니다. 사용자는 NAT Instance를 생성하고, 필요에 따라 유지보수를 직접 관리해야 합니다. 이는 고가용성을 구현하기 위해 여러 NAT Instance를 수동으로 설정하여 관리해야 하는 경우가 있습니다. NAT Instance는 사용자의 필요에 따라 더 세밀하게 설정을 조정할 수 있다는 장점이 있지만, 관리와 유지보수에 추가적인 노력이 필요합니다. 비용 면에서는 NAT Gateway보다 저렴할 수 있지만, 성능과 관리 측면에서는 NAT Gateway가 더 우세합니다.
NAT Instance를 위한 VPC 생성하기
첫 번째 단계로, NAT Instance용 VPC(Virtual Private Cloud)를 만들어보겠습니다. 이 VPC는 한 개의 가용 영역(AZ) 내에서 Public Subnet과 Private Subnet 각 하나씩을 포함하고 있습니다. NAT Instance는 Public Subnet에 배치되며, Private Subnet에 있는 EC2 Instance가 이를 통해 인터넷에 연결할 수 있도록 설정할 예정입니다.
실제 운영 환경에서는 여러 가용 영역에 걸쳐 NAT Instance를 배치하여 고가용성을 확보하는 것이 좋습니다.
1. AWS의 VPC 서비스에서 Create VPC를 클릭합니다.
2. VPC settings에서 VPC and more를 선택하고, 아래와 같이 설정합니다.
- Resources to create:
VPC and more
- Name tag auto-generation:
Auto-generate
및 이름 입력 - IPv6 CIDR block:
No IPv6 CIDR block
- Tenancy:
Default
- Number of Availability Zones(AZs):
1
- Number of public subnets:
1
- Number of private subnets:
1
- NAT gateways:
NONE
- VPC endpoints:
NONE
- Private Subnet에서 S3로 통신할 경우, S3 Gateway를 추가할 수 있습니다.
3. Create VPC를 클릭하여 VPC를 생성합니다.
NAT Instance를 위한 보안그룹 생성하기
다음으로, NAT Instance를 위한 보안그룹(Security Group)을 생성해보겠습니다.
1. EC2 서비스로 이동하여 Security Groups를 클릭합니다.
2. Create security group을 클릭하여 아래와 같이 보안그룹을 생성합니다.
- Basic details
- Security group name:
YourProjectName-nat-EC2-SG
- Description:
security group for YourProjectName-nat-EC2
- VPC: 위에서 생성한 VPC 선택
- Security group name:
- Inbound rules
Type Source HTTP VPC의 Private Subnet CIDR HTTPS VPC의 Private Subnet CIDR SSH 자신의 IP 주소 - Outbound rules
Type Destination HTTP 0.0.0.0/0 HTTPS 0.0.0.0/0
3. Create security group을 클릭하여 보안그룹을 생성합니다.
NAT AMI(Amazon Machine Image) 생성하기
AMI(Amazon Machine Image)는 EC2 Instance를 생성할 때 사용하는 이미지 파일입니다. NAT Instance를 생성하기에 앞서, NAT Instance를 위한 AMI를 생성해보겠습니다.
NAT AMI 제작용 EC2 Instance 생성
1. AWS의 EC2 서비스에서 Instances를 클릭합니다.
2. Launch instances 버튼을 클릭합니다.
3. 아래와 같이 EC2 Instance를 설정합니다.
- Name and tags
- Name :
YourProjectName-nat-EC2
- Name :
- Application and OS Images
- Amazon Machine Image (AMI) :
Amazon Linux 2023 AMI
- Amazon Machine Image (AMI) :
- Instance type
- Instance type :
t2.micro
- Instance type :
- Key pair
- Key pair name : 생성한 키페어 선택
- Network settings
- VPC (중요!) : 위에서 생성한 VPC 선택
- Subnet (중요!) :
Public Subnet
- Auto-assign Public IP (중요!) :
Enable
- Firewall (security groups) :
YourProjectName-nat-EC2-SG
위에서 생성한 보안그룹 선택
- Configure storage :
1 x 8 GiB gp3 Root volume
4. Launch instance 버튼을 클릭하여 EC2 Instance를 생성합니다.
EC2 Instance에서 NAT을 실행하도록 설정하기
1. 생성한 NAT Instance에 SSH로 접속합니다.
2. 아래의 명령어를 입력하여 iptables를 활성화합니다.
sudo yum install iptables-services -y
sudo systemctl enable iptables
sudo systemctl start iptables
3. 재부팅 후에도 IP 포워딩이 유지되도록 sysctl
설정을 변경하겠습니다. vi 에디터를 사용하여 아래의 파일을 생성합니다.
sudo vi /etc/sysctl.d/custom-ip-forwarding.conf
4. 아래의 내용을 입력하고 저장합니다.
net.ipv4.ip_forward = 1
5. 아래의 명령어를 입력하여 설정을 적용합니다.
sudo sysctl -p /etc/sysctl.d/custom-ip-forwarding.conf
6. NAT을 구성하기 이전에 netstat
명령어를 사용하여 기본 네트워크 인터페이스의 이름을 기록해놓겠습니다.
netstat -i
위의 명령어를 실행하면 아래와 같은 결과가 나옵니다.
Kernel Interface table
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
enX0 9001 19177 0 0 0 5563 0 0 0 BMRU
lo 65536 12 0 0 0 12 0 0 0 LRU
enX0
라는 인테페이스의 이름을 기록해놓습니다. 아래와 같이 다른 인터페이스가 나오면 해당 인터페이스의 이름을 기록해놓습니다.
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
ens5 9001 14036 0 0 0 2116 0 0 0 BMRU
lo 65536 12 0 0 0 12 0 0 0 LRU
7. 아래의 명령어를 입력하여 NAT을 구성합니다.
sudo /sbin/iptables -t nat -A POSTROUTING -o enX0 -j MASQUERADE
sudo /sbin/iptables -F FORWARD
sudo service iptables save
위의 명령어에서 enX0
는 앞서 기록해놓은 인터페이스의 이름을 입력합니다.
8. NAT Instance의 설정이 완료되었습니다. 이제 AMI를 생성하겠습니다.
NAT Instance의 AMI 생성
1. AWS의 EC2 서비스로 이동하여 Instances를 클릭합니다.
2. 생성한 NAT Instance를 선택하고, Actions 버튼을 클릭한 후 Image and templates > Create image을 클릭합니다.
3. 아래와 같이 설정을 입력합니다.
- Image name :
al2023-nat-ami
- Image description :
amazon2023 nat ami
- No reboot :
Uncheck
4. Create image 버튼을 클릭하여 AMI를 생성합니다.
5. 상단 알림창을 클릭해 현재 생성 중인 AMI의 상태를 확인할 수 있습니다.
NAT Instance 시작하기
위에서 생성한 NAT AMI 제작용 EC2 Instance를 Terminate하고, NAT AMI를 사용하여 NAT Instance를 생성하겠습니다.
1. AWS의 EC2 서비스에서 Instances를 클릭합니다.
2. Launch instances 버튼을 클릭합니다.
3. 아래와 같이 EC2 Instance를 설정합니다.
- Name and tags
- Name :
YourProjectName-nat-EC2
- Name :
- Application and OS Images
- MY AMIs (중요!) :
al2023-nat-ami
위에서 생성한 AMI 선택
- MY AMIs (중요!) :
- Instance type
- Instance type :
t2.micro
- Instance type :
- Key pair
- Key pair name : 생성한 키페어 선택
- Network settings
- VPC (중요!) : 위에서 생성한 VPC 선택
- Subnet (중요!) :
Public Subnet
- Auto-assign Public IP (중요!) :
Enable
- Firewall (security groups) :
YourProjectName-nat-EC2-SG
위에서 생성한 보안그룹 선택
- Configure storage :
1 x 8 GiB gp3 Root volume
4. Launch instance 버튼을 클릭하여 EC2 Instance를 생성합니다.
source/destination 검사 중지하기
모든 EC2 Instance는 기본적으로 source/destination 검사가 활성화되어 있습니다. source/destination 검사란, 해당 인스턴스가 반드시 송수신 하는 모든 트래픽의 출발지와 목적지가 되어야 한다는 것을 의미합니다. NAT Instance는 이러한 제약을 받으면 안되므로 source/destination 검사를 중지해야 합니다.
1. AWS의 EC2 서비스로 이동하여 Instances를 클릭합니다.
2. 생성한 NAT Instance를 선택하고, Actions 버튼을 클릭한 후 Networking > Change source/destination check를 클릭합니다.
3. Change source/destination check 창에서 Source/Destination check를 Stop으로 체크해주고, Save 버튼을 클릭합니다.
Private Subnet 라우팅 테이블 수정하기
NAT Instance를 생성하고 source/destination 체크를 비활성화했다면, Private Subnet의 라우팅 테이블을 수정하여 Private Subnet의 인스턴스들이 인터넷 트래픽을 NAT Instance로 전달할 수 있도록 설정하겠습니다.
1. AWS의 VPC 서비스로 이동하여 Route Tables를 클릭합니다.
2. VPC를 생성할 때 같이 생성된 Private Subnet의 라우팅 테이블을 선택합니다.
3. Edit routes 버튼을 클릭하여 아래와 같이 설정을 추가합니다.
- Destination :
0.0.0.0/0
- Target :
Instance
에서 생성한 NAT Instance 선택
4. Save routes 버튼을 클릭하여 라우팅 테이블을 수정합니다.
테스트 사전 작업
먼저 테스트를 진행하기 이전에, 아래와 같은 추가 작업들을 진행합니다.
- NAT Instance의 보안그룹 설정
- Private Subnet으로부터의 ICMP 트래픽을 허용하는 Inbound 규칙 추가
- Internet으로 나가는 ICMP 트래픽을 허용하는 Outbound 규칙 추가
- Private Subnet으로 SSH 접속을 하기 위한 Outbound 규칙 추가
- Private Subnet의 테스트 Instance를 위한 보안그룹 생성
- Private Subnet의 테스트 Instance 생성
ICMP란?
ICMP(Internet Control Message Protocol)는 인터넷 프로토콜 스위트의 일부로, 네트워크 기기들 사이에서 오류 메시지와 운영 정보를 전달하기 위해 사용됩니다. 이 프로토콜은 주로 네트워크 장치가 서로를 찾고, 문제가 발생했을 때 이를 알리기 위해 사용됩니다. 가장 잘 알려진 ICMP의 사용 예는 "ping" 명령입니다. 이 명령을 통해 사용자는 특정 IP 주소를 가진 호스트가 네트워크에 존재하며 응답하는지를 확인할 수 있습니다.
NAT Instance의 보안그룹 설정
1. AWS의 EC2 서비스로 이동하여 Security groups를 클릭합니다.
2. 생성한 NAT Instance의 보안그룹을 선택합니다.
3. Inbound rules 탭에서 Edit inbound rules 버튼을 클릭합니다.
4. 아래와 같이 설정을 추가해 Private Subnet으로부터의 ICMP 트래픽을 허용합니다.
- Type :
ALL ICMP - IPv4
- Source :
Custom
>Private Subnet의 CIDR
5. Save rules 버튼을 클릭하여 Inbound 규칙을 추가합니다.
6. Outbound rules 탭에서 Edit outbound rules 버튼을 클릭합니다.
7. 아래와 같이 설정을 추가해 NAT Instance에서 테스트 Instance SSH 접속을 가능하도록 하며, ICMP 트래픽을 외부 인터넷으로 전송하도록 설정합니다.
- Outbound rules
Type Destination SSH VPC의 Private Subnet CIDR ALL ICMP - IPv4 0.0.0.0/0
8. Save rules 버튼을 클릭하여 Outbound 규칙을 추가합니다.
Private Subnet의 테스트 Instance를 위한 보안그룹 생성
1. EC2 서비스로 이동하여 Security Groups를 클릭합니다.
2. Create security group을 클릭하여 아래와 같이 보안그룹을 생성합니다.
- Basic details
- Security group name:
YourProjectName-test-EC2-SG
- Description:
security group for YourProjectName-test-EC2
- VPC: 생성한 VPC 선택
- Security group name:
- Inbound rules
Type Source SSH NAT Instance의 보안그룹 - Outbound rules
Type Destination ALL traffic 0.0.0.0/0
- Create security group을 클릭하여 보안그룹을 생성합니다.
Private Subnet의 테스트 Instance 생성
1. AWS의 EC2 서비스에서 Instances를 클릭합니다.
2. Launch instances 버튼을 클릭합니다.
3. 아래와 같이 EC2 Instance를 설정합니다.
- Name and tags
- Name :
YourProjectName-test-EC2
- Name :
- Application and OS Images
- Amazon Machine Image (AMI) :
Amazon Linux 2023 AMI
- Amazon Machine Image (AMI) :
- Instance type
- Instance type :
t2.micro
- Instance type :
- Key pair
- Key pair name : 생성한 키페어 선택
- Network settings
- VPC : 위에서 생성한 VPC 선택
- Subnet (중요!) :
Private Subnet
- Auto-assign Public IP (중요!) :
Disable
- Firewall (security groups) :
YourProjectName-test-EC2-SG
위에서 생성한 보안그룹 선택
- Configure storage :
1 x 8 GiB gp3 Root volume
4. Launch instance 버튼을 클릭하여 EC2 Instance를 생성합니다.
테스트하기
마지막으로, Private Subnet의 테스트 Instance에서 google.co.kr
로 ping 명령을 실행하여 NAT Instance를 통해 인터넷에 접속 가능한지 확인합니다.
우선, SSH agent forwarding을 사용하여 NAT Instance를 Bastion 호스트로 사용 가능하도록 설정하겠습니다.
SSH agent forwarding이란?
SSH agent forwarding은 사용자가 다른 서버에 안전하게 접속할 수 있게 해주는 기능입니다. 예를 들어, 사용자가 A 서버에 접속해 있는데 B 서버에도 접속해야 한다면, 사용자의 개인 키 정보를 A 서버에 두지 않고도 B 서버에 접속할 수 있게 해줍니다.
SSH agent forwarding 설정
- 터미널을 열고,
ssh-add
명령을 사용하여 키페어를 추가합니다.
ssh-add YourKeyPair.pem
ssh-add -L
명령을 사용하여 추가된 키페어를 확인합니다.
ssh-add -L
ssh -A
옵션을 사용하여 NAT Instance에 접속합니다.
ssh -A ec2-user@NAT-Instance-Public-IP
NAT Instance에서 google.co.kr
로 ping 명령 실행
Private Subnet의 테스트 Instance에서 인터넷에 접속 가능한지 확인하기 앞서, NAT Instance에서 google.co.kr
로 ping 명령을 실행하여 인터넷에 접속 가능한지 확인합니다.
- 아래의 명령어를 통해 NAT Instance에서
google.co.kr
로 ping을 보냅니다.
ping google.co.kr
- 아래와 같이 정상적으로 ping이 전송되는지 확인합니다.
PING google.co.kr (142.250.206.227) 56(84) bytes of data.
64 bytes from kix06s10-in-f3.1e100.net (142.250.206.227): icmp_seq=1 ttl=100 time=33.2 ms
64 bytes from kix06s10-in-f3.1e100.net (142.250.206.227): icmp_seq=2 ttl=100 time=33.3 ms
64 bytes from kix06s10-in-f3.1e100.net (142.250.206.227): icmp_seq=3 ttl=100 time=33.2 ms
^C
--- google.co.kr ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms
rtt min/avg/max/mdev = 33.162/33.213/33.275/0.046 ms
Private Subnet의 테스트 Instance에서 google.co.kr
로 ping 명령 실행
- NAT Instance에서 Private Subnet의 테스트 Instance로 SSH 연결합니다.
ssh ec2-user@Test-EC2-Private-IP
- 아래의 명령어를 통해
google.co.kr
로 ping을 보냅니다.
ping google.co.kr
- 아래와 같이 정상적으로 ping이 전송되는지 확인합니다.
PING google.co.kr (172.217.161.195) 56(84) bytes of data.
64 bytes from kix07s03-in-f3.1e100.net (172.217.161.195): icmp_seq=1 ttl=99 time=33.6 ms
64 bytes from kix07s03-in-f3.1e100.net (172.217.161.195): icmp_seq=2 ttl=99 time=33.8 ms
64 bytes from kix07s03-in-f3.1e100.net (172.217.161.195): icmp_seq=3 ttl=99 time=33.7 ms
^C
--- google.co.kr ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 33.630/33.706/33.759/0.055 ms
이번 포스팅에서는 AWS 환경에서 NAT Instance를 활용하여 Private Subnet에 위치한 인스턴스들이 인터넷에 접속할 수 있도록 하는 방법을 자세히 알아보았습니다. 궁금하신 점이나 추가적으로 알고 싶은 내용이 있다면 언제든지 댓글을 남겨주시길 바랍니다. 다음 포스팅에서도 유용한 정보로 찾아뵙겠습니다. 감사합니다!