이 글은 RDS 생성 글과도 연결됩니다.
들어가며
프리티어 계정으로 최소 비용의 아키텍처를 구성하고자 한다. 이 글은 EC2를 한 대만 사용하는 경우를 다루고 있다. EC2 한 대만 운영하므로 필요한 서브넷은 퍼블릭 서브넷 하나면 충분하지만, 고가용성과 확장성을 고려하여 2개의 가용영역을 만들고 퍼블릭/프라이빗 서브넷을 나눠줄 것이다.
AWS에서는 최소 두 개 이상의 가용영역을 활성화하도록 권장하고 있다. (VPC에 대한 보안 모범 사례)
- EC2가 외부와 직접 통신해야 한다면? → public 서브넷 배치 + public IP 할당
- EC2가 내부 서비스 전용이라면? → private 서브넷 배치 + NAT Gateway 사용
API 서버를 만들고 있다면? EC2가 직접 외부(클라이언트)와 통신해야 하므로 외부와 직접 통신해야 한다면 EC2를 퍼블릭 서브넷에 배치해야 한다.
EC2가 외부와 직접 통신할 필요가 없다면 프라이빗 배치한다. 그런데 이렇게 되면 외부 트래픽이 필요한 경우, NAT 게이트웨이나 프록시 서버를 통해 인터넷과 연결할 수 있다. NAT Gateway는 비용이 또 든다.
EC2를 적절한 서브넷에 배치하는 것이 중요하다.
시스템 아키텍처는 다음과 같다.
따라서 다음과 같이 네트워크 환경을 구성할 것이다. 이를 한 번 훑고, 구성한다면 조금 더 따라가기 편할 것 같다.
1. 2개의 가용영역 (a,c) 사용
2. 퍼블릭과 프라이빗 서브넷 분리
3. 각 가용 영역에 퍼블릭과 프라이빗 서브넷 배치
4. 인터넷 게이트웨이를 통한 외부 연결
5. 보안 그룹과 네트워크 ACL을 이용한 트래픽 제어
먼저 기본 네트워크 환경을 구성해보자.
Apple M2 칩이 장착된 Mac에서 macOS를 사용 중입니다.
1. 기본 네트워크 환경 구성
- VPC 생성
- DNS 호스트 이름 활성화
- Subnet 생성
- Internet Gateway 생성
- Route table 생성 및 Route 설정
1.1 VPC 생성
VPC 생성을 클릭한다.
다음과 같은 항목을 입력해준다.
각 항목에서 ✅ 표시 되어 있는 것만 설정해주면 된다.
- ✅ 이름 태그
- 이름 태그는 원하는 이름으로 설정한다. 나는 프로젝트의 이름이 carevision이기 때문에 carevision-vpc라고 이름 지었다.
- ✅ IPv4 CIDR 블록
- CIDR : 간단하게 IP 주소를 할당하는 방법
- CIDR 블록 : 할당된 IP 주소들의 모음
- 즉, 내가 만들 VPC가 사용할 IP 대역, IP 범위라고 생각하면 됨
- 10.1.0.0/16를 입력한다.
- 끝에 있는 '/16' 은 앞에 있는 16bit는 언제나 고정이며 나머지 뒤에 있는 16bit는 변경이 가능하다 라는 뜻이다. (총 32bit) 2의 16승인 65,530개의 IP 주소를 가질 수 있는 네트워크 대역이다. VPC를 생성할 때도 CIDR 대역을 사용하지만 방화벽에도 사용된다.
- IPv6 CIDR 블록
- 사용하지 않을 것이기 때문에 없음
- 테넌시
- 이 VPC에서 EC2를 생성할 때 전용 하드웨어를 사용할 것인지
VPC 생성을 클릭하면 다음과 같은 화면이 나올 것이다.
1.2 DNS 호스트 이름 활성화
VPC 생성했으면, 웬만하면 DNS 호스트 이름을 활성화해주는 게 좋다.
이 설정은 퍼블릭 IP주소가 할당될 때, 그에 상응하는 DNS 주소를 제공하도록 하는 설정이며, 이것을 해주지 않으면 VPC 내에 생성되는 서버에 도메인이 붙지 않아 당장에 문제는 없을 수 있지만 향후 유연한 호출에 제약이 많아질 수 있다고 한다.
ec2-43-202-247-136.ap-northeast-2.compute.amazonaws.com과 같은 주소가 EC2 인스턴스의 퍼블릭 DNS 주소이다. 이 주소는 EC2 인스턴스에 퍼블릭 IP가 할당되어 있고, VPC에서 DNS 호스트 이름이 활성화된 경우 자동으로 생성된다.
지금까지 퍼블릭 DNS 주소 잘 생성됐었는데? 싶었다면 지금까지 새 VPC를 생성하지 않고 AWS가 기본으로 제공한 VPC에서 EC2를 생성한 것일 것이다. 우리가 직접 VPC를 만들 때는 수동으로 활성화해줘야 한다.
1.3 서브넷 생성
이제 4개의 서브넷을 생성해보자. 서브넷은 선택한 VPC의 CIDR 블럭, 즉 IP 대역을 나누는 것이기 때문에 서브넷의 CIDR 블럭이 해당 VPC의 CIDR 블럭보다는 작아야 한다.
VPC의 CIDR 블록을 10.1.1.0/16으로 설정했으니, 서브넷은 10.1.X.0/24로 나눌 것이다. 여기서 /16은 상위 범위이고, 서브넷을 나눌 때는 /24로 세분화하는 방식이다.
10.1.1.0/16: 서브넷 마스크는 255.255.0.0으로, IP 범위는 10.1.0.0부터 10.1.255.255까지, 총 65,536개의 IP 주소를 포함
10.1.1.0/24: 서브넷 마스크는 255.255.255.0으로, IP 범위는 10.1.1.0부터 10.1.1.255까지, 총 256개의 IP 주소를 포함
좌측 메뉴에서 서브넷을 클릭하고 서브넷 생성을 클릭한다.
4개의 서브넷은 VPC ID는 방금 만든 VPC를 선택하고 (기본 VPC로 설정되지 않게 주의) 각각 다음과 같이 설정해주면 된다.
새 서브넷 추가를 통해 총 4개를 만들어주면 된다.
- 첫 번째 서브넷 (public)
- 서브넷 이름 : public-subnet-a1
- 가용 영역 : ap-northeast-2a
- IPv4 서브넷 CIDR 블록 : 10.1.1.0/24
- 두 번째 서브넷 (public)
- 서브넷 이름 : public-subnet-c1
- 가용 영역 : ap-northeast-2c
- IPv4 서브넷 CIDR 블록 : 10.1.2.0/24
- 세 번째 서브넷 (private)
- 서브넷 이름 : private-subnet-a1
- 가용 영역 : ap-northeast-2a
- IPv4 서브넷 CIDR 블록 : 10.1.3.0/24
- 네 번째 서브넷 (private)
- 서브넷 이름 (원하는 이름) : private-subnet-c1
- 가용 영역 : ap-northeast-2c
- IPv4 서브넷 CIDR 블록 : 10.1.4.0/24
그럼 다음과 같이 만들어졌을 것이다.
이제 트래픽이 이동하는 경로를 만들어줘야 한다. 인터넷 게이트웨이 & 라우트 테이블을 생성해서 트래픽이 이동할 수 있는 경로를 만들어주자.
1.4 인터넷 게이트웨이 생성
좌측의 인터넷 게이트웨이 메뉴로 들어가 인터넷 게이트웨이 생성을 클릭한다.
원하는 이름을 설정한다. (나는 {프로젝트명}-{vpc}-ig로 설정했다.)
상단의 VPC에 연결을 클릭해 인터넷 게이트웨이가 어떤 VPC의 게이트웨이 역할을 할 것인지 설정할 수 있는데, 여기서 우리가 만든 VPC를 클릭해 연결해주면 된다.
아래 화면이 아니더라도 인터넷 게이트웨이 메뉴에서 원하는 인터넷 게이트웨이를 우클릭해 VPC에 연결할 수도 있다.
1.5 Route table 생성 및 Route 설정
라우트 테이블은 네트워크 트래픽이 어디로 이동해야 하는지 규칙을 정의해놓은 테이블이라고 보면 된다.
public 서브넷에 대한 라우트 테이블과 private 서브넷의 각 가용영역(AZ)에 대한 라우트 테이블 1개씩 해서 3개의 라우트 테이블을 만들어줄 것이다.
public 라우트 테이블은 1개, private 라우트 테이블은 각 가용 영역(AZ)에 대해 1개씩 만드는 이유
- 인터넷과의 연결을 담당하는 public 라우트 테이블은 public 서브넷이 동일한 인터넷 게이트웨이를 통해 외부와 연결된다.
- private 라우트 테이블은 인터넷과 직접 연결되지 않은 프라이빗 서브넷에서 사용된다. 한 AZ에 문제가 발생해도 다른 AZ에서는 서비스가 중단되지 않도록 각 가용 영역에 별도로 private 라우트 테이블을 배치하는 것을 권장한다.
(1) public 라우트 테이블 생성 (a1, c1 영역)
좌측 메뉴에서 라우트 테이블을 클릭하고 라우트 테이블 생성을 클릭한다.
- 이름 : public-subnet-rt
- VPC : 아까 만든 VPC 선택
이 라우트 테이블이 어떤 서브넷에 대한 라우트 테이블인지 정해줘야 한다. 이 라우트 테이블에는 아까 만든 두 개의 public 서브넷을 연결해줘야 한다.
(2) public 서브넷 연결 (a1, c1 영역)
서브넷 연결 탭에서 서브넷 연결 편집을 누른다.
public 서브넷을 클릭해주고 연결해준다.
이제는 public 서브넷에 위치한 리소스들이 외부 인터넷과 통신할 수 있도록 인터넷 게이트웨이를 연결해보자.
인터넷 게이트웨이 연결
라우팅 탭에 들어가서 라우팅 편집을 누른다.
라우팅 추가를 클릭하고
외부 IP 대역인 0.0.0.0/0이 목적지일 때 인터넷 게이트웨이로 나가도록 설정해주면 된다.
- 0.0.0.0/0 선택
- 인터넷 게이트웨이 클릭
- 우리가 만든 인터넷 게이트웨이를 선택
그리고 저장을 누른다.
여기서 처음에 기본적으로 설정되어 있는 (내 VPC의 CIDR 블럭에서 local로 가고 있는) 라우트는 내 VPC 영역 내에서는 트래픽 이동이 가능하다는 것을 의미한다.
그럼 다음과 같이 라우팅이 되어 있을 것이다.
(3) private 라우트 테이블 생성 (a1 영역)
private 라우트 테이블도 public과 비슷한 방법으로 진행하면 된다.
- 이름 : private-subnet-a1-rt
- VPC : 아까 만든 VPC 선택
(4) private 서브넷 연결 (a1 영역)
이제 a1 영역에 대한 private subnet을 연결해준다.
(5) private 라우트 테이블 생성 (c1 영역)
- 이름 : private-subnet-c1-rt
- VPC : 아까 만든 VPC 선택
(6) private 서브넷 연결 (c1 영역)
c1 영역에 대한 private 서브넷과 연결해준다.
2. EC2 인스턴스 생성
일단, EC2가 직접 외부와 통신해야 한다면 외부와 직접 통신해야 한다면 public-a1 또는 public-c1에 배치해야 한다.
2.1 EC2 인스턴스 생성
EC2 인스턴스 시작을 클릭한다.
- 이름 : 원하는 이름
- AMI : Amazon Linux (프리티어 사용 가능 버전)
- 인스턴스 유형 : t2.micro (프리티어 지원)
- 키페어 : 기존에 만든 것이 있다면 그것을 사용하고, 없다면 새로 생성하자
만든 키페어 파일은 잘 보관해두자. 보통 ~/.ssh 위치에 저장한다. 키가 다운로드 폴더에 저장되었다면 터미널에 다음과 같은 명령어를 작성해보자.mv ~/Downloads/{생성한 키} ~/.ssh
네트워크 설정은 편집을 클릭하고 다음과 같이 기입한다.
- VPC : 내가 만든 VPC
- 서브넷 : public-subnet-a1
- 보안 그룹 생성
- 보안 그룹 이름 : {서비스명}-ec2-sg (원하는 이름)
- 설명 : 아무렇게나 작성
- 보안 그룹 규칙
- 0.0.0.0/0 (누구한테나 허용하기 때문에)
- HTTP
퍼블릭 IP 자동 할당을 비활성화하는 이유는 나중에 탄력적 IP를 따로 할당할 것이기 때문이다.
볼륨은 30기가까지 무료이므로 30으로 설정해주었다.
2.2 탄력적 IP 주소 할당
EC2 인스턴스는 시작하거나 정지할 때마다 공인 IP 주소가 변경된다. 그 이유는 IP 주소는 000.000.000.000 ~ 255.255.255.255로 약 40억 대까지 한정되어있기 때문이다. 따라서 AWS에서 인스턴스를 중지하면 IP 주소를 회수해가기 때문에 IP 주소가 계속 바뀐다. 이를 방지하는 방법은 고정 IP (Elastic IP)를 할당하면 된다.
@danger
[주의] 2024년 2월 1일부터 서비스 연결 여부에 관계없이 모든 탄력적 IP 주소(IPv4)에 대해 시간당 $0.005의 요금이 부과된다.
인스턴스와 프라이빗 IP 주소를 선택하고 연결을 누른다.
2.3 EC2 접속해보기
EC2 인스턴스에 접속하는 방법은 사용하는 운영체제에 따라 다르다. 필자는 Mac 터미널을 이용해 ssh 연결을 시도할 것이다.
키 권한 변경
먼저 키 권한을 변경해준다.
cd ~/.ssh # 키 페어 경로를 ~/.ssh로 설정해주었다면
# 키 권한 변경
chmod 400 {키페어명}.pem
접속 시도
그 다음 다음 명령어로 접속 시도한다. EC2 상세 정보 > 연결 > SSH 클라이언트 탭에서 명령어를 복사할 수 있다. 명령어 형식은 다음과 같다.
$ ssh -i {YOUR_KEY_PAIR_FILE.pem} {USER_NAME}@{AWS_PUBLIC_DNS}
서버 초기 세팅
# 패키지 목록 업데이트 및 업그레이드
sudo apt-get update -y # 시스템의 패키지 목록을 최신 상태로 업데이트
sudo apt-get upgrade -y # 기존에 설치된 패키지를 최신 버전으로 업그레이드
# 서버 시간대 설정
sudo timedatectl set-timezone Asia/Seoul # 서버의 시간대를 한국(Asia/Seoul)으로 설정
# NTP 동기화 상태 확인
chronyc tracking # chrony(시간 동기화 서비스)의 현재 동기화 상태 및 시간 오차 확인
chronyc sources -v # 연결된 NTP 서버 목록 및 동기화 상태 상세 확인
timedatectl # 현재 시스템 시간, 시간대 및 NTP 동기화 활성화 여부 확인
(옵션) Swap 메모리 설정하기
AWS 프리티어 EC2는 기본적으로 메모리가 1GB 정도로 매우 적다. 그래서 EC2에 Docker로 여러 개의 컨테이너를 실행시키거나 CI/CD를 하는 과정에서도 EC2가 죽어버리는 현상이 발생할 수 있다. 그래서 미리 Swap 메모리를 설정해 두었다.
💡 Swap이란?
Swap은 물리적인 RAM이 부족할 때 디스크의 일부를 가상 메모리처럼 사용하는 기술이다. 다만, 디스크 I/O 속도에 따라 성능이 저하될 수 있으므로 최소한으로 설정하는 것이 좋다.
swap 파일을 생성하고 활성화해보자.
# 2GB의 Swap 파일 생성 (128MB 블록 16개 할당)
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
# 생성한 swapfile의 권한 설정 (READ, WRITE 허용)
sudo chmod 600 /swapfile
# Swap 영역으로 설정
sudo mkswap /swapfile
# Swap 활성화
sudo swapon /swapfile
# Swap 활성화 상태 확인
free -h
free -h 명령어를 통해 메모리 상태를 확인하면 Swap 메모리가 정상적으로 할당된 것을 볼 수 있다. (이미 swap 사용된 상태에서 명령어를 입력했기 때문에 결과는 조금 다를 수 있다.)
total used free shared buff/cache available
Mem: 980388 962324 48916 8 262396 18064
Swap: 2097148 1126264 970884
다음과 같이 재부팅되어도 유지되도록 설정할 수 있다.
# /etc/fstab 파일에 스왑 영역 등록
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab
- '/swapfile swap swap defaults 0 0' → 스왑 파일을 시스템 부팅 시 자동으로 마운트하도록 설정
- sudo tee -a /etc/fstab → /etc/fstab 파일에 해당 내용을 추가 (-a 옵션: 기존 내용 유지하며 추가)