Codeit/스프린트 과제

[sprint8] RDS/EC2, ECR, ECS 배포

leejunkim 2025. 8. 27. 16:35

RDS 연결 - DataGrip 

General 탭

  • 실제 목적지인 RDS, 즉 DB와 연결시켜주는 역할을 한다. RDS, EC2 생성 이후에 DataGrip 들어가서 하면 된다.
  • 입력값
    • Host: RDS > 연결 및 보안 > 엔드로인트 및 포트 > 엔드포인트 복붙하면 된다
    • Authentication: User & Password
      • RDS 생성 당시 사용했던 마스터 사용자 이름과 암호를 각각 User, Password에 입력해주면 된다
    • Database: 이것 또한 RDS 생성 당시 정할 수 있다. 기본값은 postgres이다 (PostgreSQL을 사용할 경우).

 

만약에 설정 정보를 잊었다면, RDS 인스턴스 > 구성 (Configuration) 에서 찾아보면 된다.

SSH/SSL 탭

  • 위와 같이 아무리 RDS 설정 정보를 넣어준다 해도 EC2가 없으면 연결할 수 없다. RDS 데이터베이스는 외부 인터넷에서 직접 접근할 수 없도록 Private Network에 격리되어 있기 때문이다.
  • EC2는 그 Private Network에 접근할 수 있는 유일한 '관문' 역할을 한다 -> 외부에서 접속 가능한 Public IP 주소를 가지고 있다.
  • 따라서, 먼저 외부에서 접근 가능한 EC2에 SSH로 접속해서 일단 AWS 내부망으로 진입하고, 이 SSH 연결이 바로 그 '보안 통로'가 되는 것이다.

 

  • 우선 SSH 터널링을 통해 RDS에 연결하기 위해서 먼저 EC2의 SSH 정보를 등록줘야 한다. Datasource -> SSH/SSL 옵션을 클릭해서 Add configuration 하면 된다.
  • 입력값
    • Host: EC2 인스턴스의 호스트 주소/퍼블릭 IP를 사용한다.
    • Authentication Type: Key Pair
      • EC2 인스턴스 생성 시 다운로드 했던 pem 파일을 사용한다.
  • Test Connection 버튼을 누르고 Successfully connected!라고 뜨면 된거다.

흐름 정리

  1. DataGripEC2로 SSH 터널을 만든다. (성공)
  2. 그 터널을 통해 EC2RDS 엔드포인트로 데이터베이스 연결을 전달한다.
  3. 연결이 성공적으로 이루어진다. (굿)

여기까지 이해한 후 조금 의아했는데, 어떻게 EC2와 RDS가 연결돼었을지 생각해봤다. 그래서 AWS 콘솔을 봤는데, 이 흐름이 가능한 이유는 EC2와 RDS는 같은 보안 그룹에 있기 때문이었다.

데이터베이스와 사용자, 테이블을 초기화

이제 DataGrip에서 초기화 시켜준다.

 

console [sprint8]:

-- 1. 새 유저 'discodeit_user' 생성 (비밀번호는 원하는 값으로 설정)
CREATE USER discodeit_user WITH PASSWORD 'discodeit1234';

-- 2. postgres 계정은 AWS RDS 환경 특성상 완전한 super user가 아니므로, discodeit_user에 대한 권한을 추가로 부여해야함.  
GRANT discodeit_user TO postgres;

-- 3. 'discodeit' 데이터베이스 생성 (소유자는 'discodeit_user')
CREATE DATABASE discodeit OWNER discodeit_user;

-- 4. schema.sql 실행하여 테이블 생성

 

 

 

  • schema.sql을 DataGrip에서 새로운 창으로 연다음에, 오른쪽 위에 보이는 것처럼 discodeit.public과 sprint8을 설정해주면 된다.
  • 그러면 이렇게 discodeit db를 열어보면 잘 생성된걸 볼 수 있다.

 

여기까지 왔으면 EC2는 삭제하라고 스펙에 나와있어서 삭제를 했다.

왜 삭제해도 괜찮은지 내가 이해한 바로는... 내가 만들었던 EC2 인스턴스(rds-ssh)는 데이터베이스 초기 설정을 위한 임시 연결 통로 역할이여서다.

  • 이제 RDS 연결 + DB 생성 + 테이블 생성이 다 끝났으므로 EC2는 필요없게 된것이다.
  • (또 과금을 막기 위해..!)

RDS 인스턴스와 데이터베이스 이해하기

일단 방금 만들었던 discodeit DB나 postgres DB는 AWS 콘솔에서 볼 수 없고, 그게 정상이다.

  • RDS: PostgreSQL 데이터베이스 소프트웨어가 실행될 가상 서버 환경을 설정하고 제어하는 역할이다
    • 관리하는 것들: 
      • DB 인스턴스(서버)의 사양 (CPU, 메모리, 저장 공간 크기)
      • 네트워크 및 방화벽 설정 (보안 그룹)
      • 자동 백업 및 유지보수 일정
      • 서버의 전반적인 상태 모니터링 (CPU 사용률 등)
    • 한마디로, PostgreSQL이라는 소프트웨어가 잘 동작할 수 있도록 하드웨어와 환경을 제공하는 것에 집중하는게 역할이다. 즉, 데이터베이스들을 담는 서버라고 이해하면 된다.
  • DataGrip
    • DataGrip은 데이터베이스 소프트웨어 자체를 직접 제어하고 관리하는 도구다. RDS가 제공한 서버 환경에 접속해서, 그 안에서 동작하는 PostgreSQL에 직접 명령을 내린다.
    • 이미 만들어진 서버 환경 내부로 들어가서 실제 데이터와 구조를 다우는 것에 집중한다.

ECR 연결

  • ECR에서 discodeit이라는 public repository를 만들어줬다.
  • 이제 intellij에서 프로젝트를 빌드하고 이 discodeit repo에 올려주면 된다
    • IAM 유저를 만들고 AmazonElasticContainerRegistryPublicFullAccess 권한을 주고 aws configure해준다
    • AWS CLI로 터미널에서 로그인 해준다
      • aws ecr-public get-login-password --region us-east-1 
        	| docker login --username AWS --password-stdin public.ecr.aws/q1g5n6n3
         
    • AWS 콘솔에서 discodeit 리포의 URI를 가져온다 
    • 이 URI와 buildx를 사용해서 push하면 된다

사용하는 buildx 명령어

docker buildx build --platform linux/amd64,linux/arm64 
	-t <URI>/discodeit:latest 
        -t <URI>/discodeit:1.2-M8 
        --push .

 

내 경우는:

docker buildx build --platform linux/amd64,linux/arm64 
	-t public.ecr.aws/q1g5n6n3/discodeit:latest 
        -t public.ecr.aws/q1g5n6n3/discodeit:1.2-M8 
        --push .

이렇게 해주면 됐었다.

 

작동하는 이유

  • 이미지 태그(URI) 자체는 어디로 푸시할지에 대한 완전한 주소 역할을 한다.
  • 도커 이미지 형식: [레지스트리 주소]/[저장소 이름]:[태그]
    • Docker Hub - username 사용
      •  Docker Hub에 푸시할 때는 my-username/my-repo:latest 와 같은 형식을 사용한다
      • 이것은 사실 단축된 형태다 -> 도커 클라이언트는 앞에 레지스트리 주소가 없으면 기본값인 Docker Hub의 주소, docker.io를 자동으로 붙힌다!
      • 그래서 my-username/my-repo의 전체 주소는 사실 docker.io/my-username/my-repo다
    • ECR - URI 사용
      • ECR URI는 이미지를 저장할 서버의 완전한 주소 그 자체다. 그래서 도커는 그 주소로 이미지를 바로 전송할 수 있다

ECS

이제 ECR을 만들고 거기에 도커 이미지를 저장했으면 그 저장된 도커 이미지를 실제로 실행하고 관리하기 위해서 ECS를 만들어야한다.

  • ECR은 이미지 보관창고 역할만 가지고 있고, ECS 는 그 이미지를 가져와서 실제로 어플리케이션으로 동작시키는 엔진 역할을 한다.
  • 그래서 ECS는 ECR에서 이미지를 가져와 AWS의 서버(EC2 또는 Fargate) 위에서 컨테이너로 실행시켜야한다.

먼저 discodeit.env를 만들어서 이미 만들어놓은 S3 버킷에 올려뒀다:

# Spring Configuration
SPRING_PROFILES_ACTIVE=prod

# Application Configuration
STORAGE_TYPE=s3
AWS_S3_ACCESS_KEY=엑세스_키
AWS_S3_SECRET_KEY=시크릿_키
AWS_S3_REGION=ap-northeast-2
AWS_S3_BUCKET=버킷_이름
AWS_S3_PRESIGNED_URL_EXPIRATION=600

# DataSource Configuration
RDS_ENDPOINT=RDS_엔드포인트(포트 포함)
SPRING_DATASOURCE_URL=jdbc:postgresql://${RDS_ENDPOINT}/discodeit
SPRING_DATASOURCE_USERNAME=RDS_유저네임(DataGrip을 통해 생성했던 유저)
SPRING_DATASOURCE_PASSWORD=RDS_비밀번호

# JVM Configuration (프리티어 고려)
# JVM_OPTS="-Xmx384m -Xms256m -XX:MaxMetaspaceSize=64m -XX:+UseSerialGC"
JAVA_TOOL_OPTIONS=-Xmx384m -Xms256m -XX:MaxMetaspaceSize=128m -XX:+UseSerialGC

 

참고로 저렇게 JVM_OPTS를 사용하면 에러가 떠서 JAVA_TOOLS_OPTIONS를 사용하는 것으로 바꿔줬다.

(또한 자꾸 OutOfMemoryError이 떠서 MaxMetaspaceSize=64를 128로 늘려주었다.

 

그리고

  • 주어진 스펙대로 AWS ECS 콘솔에서 클러스터를 생성해줬는다.
    클러스터 구성 > 클러스터 이름 discodeit-cluster  
    인프라 > AWS Fargate(서버리스) 체크해제 과금 주의
    인프라 > Amazon EC2 인스턴스 체크  
    인프라 > EC2 인스턴스 유형 t3.micro (원래 t2.mirco) 과금 주의
    인프라 > 원하는 용량 최소 0, 최대 1 과금 주의
    인프라 > SSH 키 페어 새 키 페어 생성 후 지정  
    원래 스펙에는 t2.micro를 사용하라고 적혀져있었다. 하지만 t2.micro를 사용해보니까 배포가 자꾸 실패했었는데, 이런 에러가 떴었다: Launching a new EC2 instance. Status Reason: The specified instance type is not eligible for Free Tier. For a list of Free Tier instance types, run 'describe-instance-types' with the filter 'free-tier-eligible=true'. Launching EC2 instance failed.

    그래서 찾아보니까 t2.micro는 더이상 프리티어가 아니라서 t3.micro를 써야한다는 것이였다.
  • 태스트 정의
    태스크 정의 구성 > 태스크 정의 패밀리 discodeit-task  
    인프라 요구 사항 > 시작 유형 AWS Fargate: 체크 해제, Amazon EC2 인스턴스: 체크  
    인프라 요구 사항 > 네트워크 모드 bridge  
    인프라 요구 사항 > 태스크 크기 CPU: 0.25 vCPU, 메모리: 0.5 GB  
    컨테이너-1 > 컨테이너 세부 정보 이름: discodeit-app, 이미지 URI: 이전에 배포한 이미지  
    컨테이너-1 > 포트 매핑 호스트 포트: 80, 컨테이너 포트: 80  
    컨테이너-1 > 리소스 할당 제한 - 조건부 CPU: 0.25 vCPU, 메모리 하드 제한: 0.5 GB, 메모리 소프트 제한: 0.25 GB  
    컨테이너-1 > 환경 변수 - 선택 사항 > 파일에서 추가 이전에 S3에 업로드한 discodeit.env 파일 지정
    discodeit 클러스터 상세 화면에서 서비스를 생성
    배포 구성 > 태스크 정의 패밀리 discodeit-task  
    배포 구성 > 서비스 이름 discodeit-service  
    배포 구성 > 원하는 태스크 1 기본값
    배포 구성 > 상태 검사 유예 기간 30초  
  •   태스크의 EC2 보안 그룹의 인바운드 규칙을 설정하여 어디서든 접근할 수 있도록 변경.
    • EC2 보안 그룹 인바운드 규칙을 편집: 규칙 유형으로 HTTP를 선택 + 소스로 Anywhere-IPv4를 선택하여 모든 IP를 허용

 

discodeit-service > 로그로 들어가보면 잘 작동하는 것을 볼 수 있다!

 

이제 만들어지고 실행중인 EC2 인스턴스의 퍼플릭 IP 주소에 연결해보면:

이런식으로 에러가 떴었다.......

그래서 diescodeit-service에서 로그를 읽어보니까 이런 에러가 있었다:

?? 하면서 로그 에러를 구글링하면서 찾아보고 있었는데, 알고보니까 그냥 https -> http로 바꿔서 접속해주면 되는거였다..

  • 서버(Tomcat)는 현재 HTTP라는 언어로만 대화할 수 있도록 설정되어서 간단한 POST/GET 메서드로 요청이 들어올거라고 예상을 한다. 하지만 HTTPS는 http method 앞에 암호 신호를 넣어주므로 톰캣 입장에선 http method가 아니니까 IllegalArgumentException을 던지는 것이다.

 

이제 http로 접속해주면 잘 뜬다!