최근 교내 해커톤을 진행하던 중 CI/CD 파이프라인이 동작하지 않는 상황이 발생해서 관련 코드를 싹 다 지우고 EC2에 서버를 수동으로 배포했었다. 또 이런 일이 생길 수도 있을 것 같아서 구글링 하지 않아도 되도록 따로 정리해두려고 한다. 참고로 AWS에 대한 자세한 설명은 나중에 하고, VPC를 통해 EC2를 구축한 뒤 RDS DB 서버를 만들어서 접속하는 방법만 작성한다.
1. AWS EC2 서버 배포
대략적으로 이해한 내용만 적어보면, AWS VPC는 사설 IP를 제공해 준다. 보통 10.0.0.0/16 같은 식으로 부여받고, 그 안에 서브넷(10.0.1.0/24, 10.0.2.0/24 등)을 나눠 EC2나 같은 RDS 인프라 자원을 배치할 수 있다.
- public 서브넷은 외부와 통신할 때 Internet Gateway를 거친다. 물론 라우팅 테이블을 통해 설정해야 한다.
- VPC엔 라우팅 테이블이 존재하기 때문에 같은 VPC 내부 서브넷끼리 통신이 가능하다.
- private 서브넷은 같은 VPC 내부에 있는 서브넷의 컴퓨팅 리소스를 통해 접속하거나 public 서브넷에 존재하는 Bastion host를 통해 원격으로 접속할 수 있다. → 추가로 공부해 보기
참고: 10.0.0.0/16은 10.0.0.0/8의 서브넷이다.
VPC 생성
우선 VPC를 생성한다.
방금 만든 VPC에 서브넷을 생성한다.
- 한 VPC에 서브넷을 여러 개 만들 땐 CIDR을 10.0.1.0/24, 10.0.2.0/24, ...처럼 만들면 된다.
- 보통 AWS에서 무료로 지원하는 프리 티어에서는 가용 영역-a와 가용 영역-c를 지원하기 때문에 서브넷을 만들 때 가용 영역을 a와 c로 지정해서 하나씩 만들면 된다.
이후 외부와의 통신을 담당할 인터넷 게이트웨이를 생성하고 VPC에 연결한다.
다음으로 VPC의 public 서브넷이 외부로 나갈 때 인터넷 게이트웨이를 거쳐 나가야 외부와 통신이 가능하므로 라우팅 테이블을 편집한다.
- 라우팅 테이블은 VPC를 만들면 자동으로 생성되므로 라우팅 편집만 하면 된다.
- 0.0.0.0/0(외부 아이피 대역)이 대상일 때, 인터넷 게이트웨이로 나가도록 설정한다.
- 라우팅 테이블에서 명시적 서브넷 연결까지 해주면 인터넷 게이트웨이로 연결된 두 서브넷은 public 서브넷이 되고 외부와 통신이 가능하다.
보안 그룹 생성
보안 그룹은 EC2를 생성할 때 적용할 수 있다. EC2에서 외부로 나갈 때(아웃바운드)와 외부에서 EC2로 들어올 때(인바운드) 어떤 포트를 허용할 것인지에 대한 규칙을 설정해 두는 거라고 보면 된다.
아웃바운드 규칙은 딱히 건드릴 필요 없고 인바운드 규칙만 추가한다.
- 인바운드 규칙 = 외부에서 EC2에 접근할 수 있는 유형과 포트 범위 등을 설정
유형 | 설명 |
SSH | shell |
HTTP, HTTPS | Web |
MYSQL/Aurora | Database |
TCP 8080 | Spring Boot |
EC2 생성
EC2 이름을 작성하고 운영체제를 선택한다.
인스턴스는 프리 티어에서 사용 가능한 유형을 선택하면 된다.
EC2에 접속할 때 사용할 키 페어를 선택하고 /.ssh 폴더에 넣어둔다.
만들어둔 VPC와 서브넷 하나를 선택하고 퍼블릭 IP를 자동 할당하도록 한다. 이렇게 하면 12.345.678.90 같은 퍼블릭 IP가 자동으로 부여된다. 또, 만들어둔 보안 그룹을 꼭 적용해야 외부에서 EC2에 접속할 수 있다.
탄력적 IP 설정
탄력적 IP를 설정해 두면, EC2 인스턴스의 인터넷 게이트웨이를 거쳐 통신할 때 부여받는 퍼블릭 IP 주소를 고정시킬 수 있다. EC2 인스턴스를 종료하고 재부팅하게 되면 새로운 IP가 늘 할당되는데 이런 경우 EC2에 접속할 때 매번 IP 주소를 변경해야 하기 때문에 꼭 탄력적 IP를 설정해야 한다.
아무것도 건드리지 않고 할당한 뒤 만들어둔 EC2 인스턴스와 연결하면 된다.
이때, 탄력적 IP를 만들어 놓고 EC2 인스턴스와 연결하지 않으면 과금이 꽤나 발생하므로 주의해야 한다. EC2를 없앤 후에 탄력적 IP를 릴리스해야 한다는 것도 꼭 잊지 말자. 정말 알고 싶지 않았는데...
IntelliJ에서 Spring Boot EC2 원격 접속
IntelliJ에서 EC2 인스턴스에 원격으로 접속해 보자.
우선 [Tools - Deployment - Browse Remote Host]를 선택한다.
옆에 뜬 창에서 아래 버튼을 클릭하고 이름은 ubuntu로 Type은 SFTP로 만든다.
순서대로 클릭하고 생성한 EC2 인스턴스의 퍼블릭 IP 주소를 Host로 입력하고, 이름은 ubuntu로 적은 뒤 EC2 인스턴스를 만들 때 지정한 key-pair를 선택한다.
Test Connection을 눌렀을 때 아래처럼 뜬다면 원격 접속이 성공적으로 된 것이다.
SSH 설정
컴퓨터에서 Git Bash를 열어 SSH로 EC2에 접속할 수 있도록 해야 한다. 아래 명령어를 하나하나 입력하면 된다.
- mkdir -p ~/.ssh
- mkdir = 디렉터리를 생성하는 명령어
- -p 옵션을 사용하면 상위 경로가 없더라도 함께 생성한다.
- /.ssh 디렉터리가 이미 만들어져 있다면 입력하지 않아도 된다.
- cp -f /Downloads/temp-key.pem ~/.ssh
- cp = 경로에 있는 파일을 다른 경로로 이동하는 명령어
- /.ssh 디렉터리에 이미 key-pair를 옮겨놨다면 입력하지 않아도 된다.
- chmod 400 ~/.ssh/temp-key.pem
- chmod = 해당 파일에 대한 권한 변경
- .pem 파일을 공개적으로 볼 수 없도록 400 권한을 부여해야 한다.
- nano ~/.ssh/config
- nano = 파일 편집기
- 복잡한 명령어를 작성하지 않고 Host 하나로 쉘을 통해 명령어를 입력하면 EC2 인스턴스에 접속할 수 있도록 한다.
$ mkdir -p ~/.ssh
$ cp -f /Downloads/temp-key.pem ~/.ssh/
$ chmod 400 ~/.ssh/temp-key.pem
$ nano ~/.ssh/config
nano로 연 config 파일에 아래와 같이 작성한다.
- 작성 후 Ctrl + x를 누르고 y를 누른 뒤 엔터를 눌러 저장한다.
Host {고유한 이름}
User {우분투로 설정한 경우 ubuntu | 리눅스로 설정한 경우 ec2-user}
HostName {만든 EC2 인스턴스의 퍼블릭 IP 주소 | 퍼블릭 IP DNS}
IdentityFile {.pem이 있는 절대 경로}
이후 아래 명령어를 입력하면 쉘에서 명령어를 통해 인스턴스에 접근 가능하다.
$ ssh {고유한 이름}
Spring Boot 빌드 및 배포 방법
EC2에 수동으로 Spring Boot 서버를 배포하는 방법을 알아보자. 아래 RDS DB 서버 구축까지 진행하고 해야 정상적으로 동작한다.
우선 IntelliJ Local Terminal에 아래 명령어를 입력해 Spring Boot 서버를 jar 파일로 빌드한다.
$ ./gradlew clean build
빌드에 실패한다고 뜬다면 아래 명령어를 입력해 자세한 로그를 출력할 수 있다.
$ ./gradlew -i test
IntelliJ에서 Git Bash를 열면 해당 프로젝트에서 Git Bash를 여는 것과 동일하다. 아래 명령어를 입력하면 빌드한 스냅샷을 SSH를 사용해 EC2 인스턴스에 안전하게 옮길 수 있다.
- scp = SSH 원격 접속 프로토콜을 사용하는 원격 서버 간 파일 전송 프로토콜
$ scp build/libs/{프로젝트 이름}-0.0.1-SNAPSHOT.jar {위에서 설정한 고유한 이름}:~/
이후 IntelliJ에 연결된 EC2 인스턴스 Terminal을 열고 아래 명령어를 입력해 자바를 설치한다.
$ sudo apt update
$ sudo apt upgrade -y
$ sudo apt install openjdk-17-jdk -y
$ java -version
설치가 끝나면 아래 명령어를 입력해 빌드된 jar 파일을 EC2에 배포한다.
$ sudo java -jar {프로젝트 이름}-0.0.1-SNAPSHOT.jar
{EC2 인스턴스 퍼블릭 IP 주소 | 퍼블릭 IP DNS}:8080을 웹 주소에 입력했을 때 아래 페이지가 뜬다면 배포에 성공했다는 것이다.
2. RDS 서버 구축
실제 서비스를 런칭하거나 할 땐 DB를 private subnet에 생성해야 한다. 런칭용이 아닐 때(해커톤 등)는 public subnet에 만드는 게 DataGrip이나 다른 DB 원격 접속 서비스로 쉽게 접속할 수 있어서 좋다.
참고: EC2에 데이터베이스를 설치해서 사용하는 것보다 RDS를 사용하면 데이터베이스를 더 유연하게 사용할 수 있다.
VPC DNS 호스트 설정
VPC에서 아래 설정을 활성화해놓지 않으면 VPC에 대한 RDS를 생성할 수 없다.
- DNS 호스트 이름 속성은 VPC에서 시작된 인스턴스가 퍼블릭 IP 주소에 해당하는 퍼블릭 DNS 호스트 이름을 받을지 여부를 결정한다. 이걸 활성화해두지 않으면 DNS에서 호스트 이름을 풀어 IP 주소로 바꾸는 등의 resolution을 할 수 없어서 DB를 생성할 수 없다고 오류가 뜬다.
- 활성화하면 EC2 인스턴스에 아래처럼 퍼블릭 IP DNS가 생성된다.
DB 서브넷 그룹 생성
이후 새로 만들 DB를 원하는 VPC의 public 서브넷에 적용하기 위해 서브넷 그룹을 만든다.
데이터베이스 생성
엔진 옵션에서 MySQL을 선택하고, 템플릿으로 프리 티어를 선택한다.
이후 마스터 사용자 이름과 마스터 암호를 만들고 잊어버리지 않도록 따로 적어둔다.
아래 스토리지 자동 조정을 활성화해 두면 과금이 발생하므로 주의한다.
VPC와 연결하고 public 액세스를 허용한다.
기존 보안 그룹을 선택하고 가용 영역을 아무거나 고른다. 이때, 보안 그룹에 인바운드 규칙으로 3306 포트(MYSQL/Aurora)가 anywhere로 설정돼있어야 한다.
과금이 발생하지 않도록 자동 백업을 해제한다.
IntelliJ에서 RDS 원격 접속
원래는 DataGrip을 썼지만 IntelliJ에서도 RDS에 원격 접속할 수 있다. 우선 RDS의 엔드포인트를 복사해 둔다.
Host에 엔드포인트를 붙여 넣고 User과 Password에 RDS를 생성할 때 적은 마스터 사용자 이름과 암호를 입력한다. Test Connection을 눌렀을 때 저렇게 뜬다면 연결에 성공했다고 보면 된다.
applicaion.yml에 DataSource 정보 설정
우선은 DB 쿼리 콘솔에서 아래 명령어를 작성해 database를 생성한다.
이후 Spring Boot에서 연결된 datasource 정보를 알 수 있도록 application.yml 파일을 수정한다. RDS를 사용하는지 로컬 DB를 사용하는지에 따라 입력해야 할 정보가 다르므로 주의한다.
- RDS를 사용한다면 유저 이름과 암호가 노출되면 남들이 마음대로 사용할 수 있기 때문에 IntelliJ 환경변수를 이용하든 AWS 환경변수를 사용하든 해서 숨겨야 한다.
spring:
datasource:
url: jdbc:mysql://{localhost | RDS 엔드포인트}:3306/{데이터베이스 이름 temp}
username: {설정한 유저 이름}
password: {설정한 암호}
driver-class-name: com.mysql.cj.jdbc.Driver
sql:
init:
mode: never
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
show_sql: true
format_sql: true
use_sql_comments: true
hbm2ddl:
auto: create
default_batch_fetch_size: 1000
3. UTC 시간대 영국에서 서울로 변경하기
AWS는 AWS에서 지원하는 컴퓨팅 서비스(리소스)를 이용하게 된다. 기본 시간대가 영국 기준이기 때문에 EC2에 올려둔 서버가 돌아가거나 RDS에 데이터를 저장할 때 한국 시간보다 9시간 정도 빠르다는 걸 확인할 수 있다. API를 호출할 때 데이터를 저장한 시간이 중요하다면 꼭 설정해둬야 한다.
EC2 시간대 변경
위에서 Spring Boot를 EC2에 배포하고 나서의 사진을 보면 시간대가 영국 기준인 걸 알 수 있다. main 메서드에 아래 명령어 한 줄을 추가해 시간대 기본값을 영국에서 서울로 변경할 수 있다.
@EnableJpaRepositories
@EnableJpaAuditing
@EnableScheduling
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
// 시간대 기본값 서울로 변경
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
SpringApplication.run(Application.class, args);
}
}
RDS 시간대 변경
RDS도 동일하게 영국 시간이 기본값으로 설정돼 있다. 서울 시간으로 변경하려면 AWS RDS에서 파라미터 그룹을 생성해야 한다.
이후 파라미터 그룹 편집을 통해 time_zone을 검색하고 기본값을 Asia/Seoul로 설정한다.
RDS DB 수정 버튼을 눌러 방금 만든 파라미터 그룹을 선택해 주면 된다.
빠르게 적용하기 위해 즉시 적용을 선택하자.
RDS를 재부팅하면 시간대가 변경된 걸 확인할 수 있다.
4. 참고자료