RDS 구축하기 + Read Replica 사용하기

민정·2025년 2월 4일
post-thumbnail

RDS를 사용해 쉽고 간편하게 데이터베이스를 구축해보자! 아래에서는 프리티어 수준의 MySQL 엔진을 사용하는 RDS를 구축한다.


RDS란

Amazon Relational Database Service(RDS)는 총 소유 비용에 최적화되고 쉽게 관리할 수 있는 관계형 데이터베이스 서비스입니다. 수요에 따라 간편하게 설정, 운영 및 확장할 수 있습니다. Amazon RDS는 프로비저닝, 구성, 백업, 패치 적용처럼 획일적인 데이터베이스 관리 작업을 자동화합니다.

AWS가 프로비저닝, 백업, 구성 등을 간편하게 관리해주는 관계형 데이터베이스 서비스이다. 특정 DB 엔진과 인스턴스 유형의 경우 프리티어로 제공된다.

Aurora

Aurora는 AWS 자체 서비스로, RDS와 비교해서 더 높은 성능과 가용성을 제공한다. 클러스터로 구성되어 있으며, 클러스터 서비스답게 매우 비싸다. 반드시 비용을 제대로 고려해서 도입하자.

비용(서울 리전 기준)

사용하는 인스턴스, 엔진에 따라 비용이 다르다. read replica나 multi-az를 사용하는 경우 해당 rds 인스턴스 비용의 2배가 발생한다.

rds의 특정 엔진(MySQL, MariaDB, PostgreSQL, SQL Server) 및 특정 인스턴스에서 프리티어를 사용할 수 있다. multi-az는 프리티어 범위를 벗어나니 조심하자.

RDS 구축하기

구축하기

  • MySQL 엔진을 선택한다.

  • 템플릿은 프리티어를 선택한다.

  • 프리티어 탬플릿을 선택했기 때문에, 배포 옵션은 단일 DB 인스턴스로 고정된다. 만약 Multi-AZ를 사용하고 싶다면 프리티어 외의 템플릿을 선택하자.

  • 아래의 설정을 작성한다.

    • 적절한 DB 인스턴스 식별자를 설정하자.
    • 마스터 사용자 이름을 설정하자. 이는 나중에 MySQL에 접속할 때 사용된다.
    • 자격증명 관리는 Secrets Manager를 사용하면 유용하다. 하지만 프리티어 범위를 벗어나며 비용(월 $0.40)이 부과된다. 따라서 자체 관리를 선택하고 암호를 설정한다.
  • 인스턴스 클래스를 선택한다.

    • 프리티어의 경우, db.t4g.micro로 고정된다.
  • 스토리지는 프리티어로 제공되는 범용 SSD(gp2) 또는 마그네틱을 선택한다.

  • 아래의 연결 설정을 작성한다.

    • EC2 컴퓨팅 리소스에 연결을 선택하는 경우, EC2 인스턴스에 RDS에 연결 가능한 인바운드 규칙을 가진 보안그룹이 추가된다. 나중에 EC2 인스턴스 보안그룹에서 연결 설정을 하면 되니 현재는 선택하지 않는다.
    • 적절한 VPC, 서브넷을 선택한다.
    • 보안을 위해 퍼블릭 액세스는 거부한다.
    • 포트는 mysql 기본 포트인 3306을 사용한다.
  • 데이터베이스 인증은 암호 인증을 사용한다.

    • IAM 인증 방식은 생각보다 번거롭다. EC2에 RDS 접속 관련 IAM 권한이 있다고 RDS에 바로 접속할 수 있는 방식이 아니기 때문이다.
    • IAM 인증 방식은 토큰을 발급 받고 토큰과 IAM 역할을 통해 접속할 수 있는 방식이다.
    • 암호 인증이 가장 쉽고 간편하다.
  • 추가 구성에서는 초기 데이터베이스 이름을 지정한다.


파라미터

파라미터 그룹을 통해 데이터베이스의 환경 설정을 진행한다.

  • 파라미터 그룹을 통해 아래의 세 가지를 설정한다.

    • 시간
    • 문자 인코딩
    • 문자 정렬 알고리즘
  • 파라미터 그룹을 생성한다.

    • 파라미터 그룹 패밀리는 사용하는 엔진 버전과 맞춘다.
  • 생성한 파라미터 그룹의 값을 수정한다.

    • time_zone -> Asia/Seoul
    • character_set_xxx -> utf8mb4
    • collation_xxx -> utf8mb4_general_ci
  • 데이터베이스 설정을 수정해 방금 생성한 파라미터 그룹을 적용한다.

    • 수정 예약 시 즉시 적용을 선택해야 한다. 예약 적용 시 적용될 때까지 기다려야 하기 때문에 유의하자.



RDS 접속

MySQL 워크밴치에서 접속하기

MySQL 워크밴치에서 RDS를 접속하기 위해서는 몇 가지 조건을 만족해야 한다.

  • 조건은 아래와 같다.
    • RDS가 Public Subnet에 위치
    • RDS가 퍼블릭 액세스 허용
    • RDS 보안그룹이 적절한 인바운드 규칙을 가짐(RDS 포트가 내 컴퓨터에 열려있어야 한다.)

만약 위에서 구축한 것과 같이 Private Subnet에 RDS를 위치하고, 퍼블릭 액세스를 허용하지 않았다면 워크밴치에서 접속이 불가능하다. 이 경우 아래의 EC2에서 RDS에 접속하는 방법을 참고해 접속하자.

MySQL 워크밴치에서 RDS를 접속하는 방법은 다음과 같다.

  • MySQL 워크밴치를 실행하고 Connection를 추가하자.
    • Connection Name은 원하는 대로 설정한다.
    • Hostname은 RDS 엔드포인트로 작성한다.
    • Port는 RDS 생성 시 설정한 포트로 설정한다. 기본 포트는 3306이다.
    • Username은 마스터 사용자 이름으로 설정한다.
    • Store in Vault를 클릭하고 마스터 암호를 입력한다.
    • Connection을 생성하기 전 Test Connection으로 연결이 제대로 되는지 확인한다.


EC2에서 RDS 접속하기

EC2에서 RDS에 접속하기 위해서는 MySQL Client 설치, RDS 보안그룹 설정이 필요하다.

먼저, EC2에 MySQL Client를 설치한다. MySQL 명령줄 클라이언트 설치를 참고해 적절한 명령어를 입력한다. Amazon linux 2023을 사용하는 경우, 아래의 명령어를 입력한다.

# MySQL Client 설치
sudo dnf install mariadb105

# MySQL Client 설치 확인
mysql --version

RDS 보안 그룹을 설정한다. 아래의 인바운드 규칙을 추가한다.

  • 유형: MySQL/Aurora / 소스 유형: 사용자 지정 / 소스: RDS에 접속할 인스턴스 보안그룹

아래의 명령어를 입력해 MySQL에 접속한다. 접속 시 마스터 암호를 입력해야 한다.

mysql -u [마스터 사용자 이름] -p -h [rds 엔드포인트]

MySQL에 접속한 후 아래의 명령어를 입력해 위에서 설정한 초기 데이터베이스 이름이 있는지 확인한다.

show databases;




Read-repilca 사용하기

Read-repilca 생성하기

  • 읽기 전용 복제본 생성을 누른다.
    • 읽기 전용 복제본이 비활성화 되어있는 경우, RDS를 수정해 백업 보존 기간을 0보다 큰 값으로 수정한다. 수정 시 활성화된다.
    • RDS를 생성하는 것과 유사한 방식으로 생성한다.



스프링부트 서버 설정하기

스프링부트 서버에서 읽기 전용 복제본은 생성한다고 바로 읽기 전용 복제본을 사용하지 않는다. 코드 자체에서도 설정이 필요하다.

  • application.yml
    • jdbc-url이 아닌 url로 작성하면 오류가 발생하니 주의하자.
spring:
  datasource:
    read:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: "jdbc:mysql://[엔드포인트]:3306/[스키마]?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true"
      username: [username]
      password: [pw]
    write:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: "jdbc:mysql://[엔드포인트]:3306/[스키마]?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true"
      username: [username]
      password: [pw]
  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
        dialect: "org.hibernate.dialect.MySQL8Dialect"

  • DataSourceRouter 클래스
public class DataSourceRouter extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        String lookupKey = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "replica" : "master";
        // System.out.println("Current DataSource is " + lookupKey); // 테스트용 로그
        return lookupKey;
    }
}

  • DataSourceConfig 클래스
    • @EnableJpaRepositories에서 basePackages를 잘 명시하자. 이걸 명시하지 않으면 빈 등록이 안되면서 오류가 발생한다.
@Configuration
@RequiredArgsConstructor
@EnableJpaRepositories(basePackages = "패키지")
@EnableConfigurationProperties
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.write")
    public DataSource writeDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.read")
    public DataSource readDataSource() {
        return DataSourceBuilder.create().type(HikariDataSource.class).build();
    }

    @Bean
    @DependsOn({"writeDataSource", "readDataSource"})
    public DataSource routeDataSource(
            @Qualifier("writeDataSource") DataSource writeDataSource,
            @Qualifier("readDataSource") DataSource readDataSource) {
        DataSourceRouter dataSourceRouter = new DataSourceRouter();

        HashMap<Object, Object> dataSourceMap = new HashMap<>();

        dataSourceMap.put("write", writeDataSource);
        dataSourceMap.put("read", readDataSource);

        dataSourceRouter.setTargetDataSources(dataSourceMap);
        dataSourceRouter.setDefaultTargetDataSource(writeDataSource);

        return dataSourceRouter;
    }

    @Bean
    @Primary
    @DependsOn({"routeDataSource"})
    public DataSource dataSource(@Qualifier("routeDataSource") DataSource routeDataSource) {
        return new LazyConnectionDataSourceProxy(routeDataSource);
    }
}
profile
시스템 + 리눅스 + 클라우드

0개의 댓글