Springboot 구동시 AWS Secret을 조회하여 Datasource 생성

안녕하세요 오늘은 BESPIN GLOBAL AIX실 전희연님이 작성해주신 ‘Springboot 구동시 AWS Secret을 조회하여 Datasource 생성’에 대해 소개해드리도록 하겠습니다.

목차

  1. DataSourceAutoConfiguration 중지 설정
  2. Springboot Project gradle 설정
  3. SecretManagerConfig Class 구현
  4. DBConnectionInfo Class 구현
  5. MyDataSourceConfiguration Class 구현
  6. Springboot Project를 시작하여 DataSource 생성확인

이번 블로그에서는 Springboot 구동시 AWS Secret을 조회하여 Datasource 생성되도록 구현하는 방법을 설명하고자 합니다.
※ Java로 AWS Secret 조회를 위한 Secret생성 및 Access Key 생성과 Java로 AWS Secret 조회를 위한 구현 및 테스트 작업이 완료된 상태에서 설명을 이어서 진행합니다.

<개발 환경>
개발 OS : Windows
Java : 17
IDE : 인텔리제이
Cloud : AWS

<사전작업>

1) Secret 생성

2) Access token 생성하여 Local PC에 AWS Configure 등록

3) Springboot 프로젝트 생성

<작업 순서>
1) DataSourceAutoConfiguration 중지 설정

2) Springboot Project gradle 설정
3) SecretManagerConfig Class 구현
4) DBConnectionInfo Class 구현
5) MyDataSourceConfiguration Class 구현
6) Springboot Project를 시작하여 DataSource 생성확인

1. DataSourceAutoConfiguration 중지 설정

1.1 DataSourceAutoConfiguration 기본 설정

Spring Boot의 auto-configuration은 추가한 jar 파일에 맞게 application.yaml에 설정값만 입력해 주면 자동적으로 설정을 해줍니다.
예를들면 application.yaml 파일에 JDBC 설정을 해주면 자동으로 DataSource를 만들어주는 것이 좋은 예입니다.

현재 어떤 Auto-configuration이 적용되어있는지 알고 싶다면 Log를 debug로 실행시키면 확인할 수 있습니다.
로그에서 “CONDITIONS EVALUATION REPORT”의 아랫부분을 보면 DataSourceAutoConfiguration의 Auto-configuration을 진행하는 것을 알 수 있습니다.(빨간색 박스)

이 부분을 Exclude 하는 설정을 1.2에서 진행한다.

1.2 DataSourceAutoConfiguration 중지 설정

우리는 그동안 JDBC 설정값을 추가했지만 본 블로그에서는 AWS의 Secret에서 조회한 정보를 이용하여 DataSource를 생성할 것이기 때문에 Auto-configuration을 진행을 제외하는 설정을 application.yaml에 추가하기만 하면 됩니다.

application.yaml

logging:
  level:
    root: debug

spring:
  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  • 실행 화면

이전 로그에서 “DataSourceAutoConfigration matched:” 부분이 제거된 것을 확인할 수 있습니다.

2. Springboot Project gradle 설정

Springboot 프로젝트를 생성후 아래와 같이 Gradle 설정을 해주면 AWSSDK를 사용할 수 있습니다.

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.sample.aws'
version = '0.0.1-SNAPSHOT'

java {
    sourceCompatibility = '17'
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.0'
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation platform('software.amazon.awssdk:bom:2.21.44')
    implementation 'software.amazon.awssdk:secretsmanager'
    implementation 'software.amazon.awssdk:regions'
    implementation 'org.apache.commons:commons-lang3:3.14.0'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    runtimeOnly 'org.postgresql:postgresql'
}

tasks.named('test') {
    useJUnitPlatform()
}

3. SecretManagerConfig Class 구현

새로운 “dev.secret.database” Secret을 만들고 소스에 적용합니다.

package com.sample.aws.secretmanager.config.aws;

import lombok.extern.slf4j.Slf4j;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException;

@Slf4j
public class SecretManagerConfig {
    private String databaseSecretName = "dev.secret.database";

    public String getDBConnectionInfo() {
        String secret = "";
        Region region = Region.AP_NORTHEAST_2;
        SecretsManagerClient secretsClient = SecretsManagerClient.builder()
                .region(region)
                .build();

        try {
            log.info("Secret Name = [{}]", databaseSecretName);
            GetSecretValueRequest valueRequest = GetSecretValueRequest.builder()
                    .secretId(databaseSecretName)
                    .build();

            GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest);
            secret = valueResponse.secretString();
            // log.info(secret);

        } catch (SecretsManagerException e) {
            log.warn(e.awsErrorDetails().errorMessage());
        } catch (Exception e) {
            log.warn(e.getMessage());
        }

        return secret;
    }
}

4. DBConnectionInfo Class 구현

DB Connection 정보를 Object로 변환하기 위해 Object Class를 구현합니다.

package com.sample.aws.secretmanager.config.domain;

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;

@Setter
@Getter
public class DBConnectionInfo {

    private String username;
    private String password;
    private String engine;
    private String host;
    private int port;
    private String dbname;

    public String toStringJson() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
    }
}

5. MyDataSourceConfiguration Class 구현

상기 “1.2” 설정을 통해 Springboot가 기동되면서 Property 파일 설정에 의해 자동으로 생성되는 부분을 막았고 AWS Secret에서 조회한 DB 정보를 이용하여 DataSource 생성하는 소스를 구현합니다.

package com.sample.aws.secretmanager.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sample.aws.secretmanager.config.aws.SecretManagerConfig;
import com.sample.aws.secretmanager.config.domain.DBConnectionInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Slf4j
@Configuration(proxyBeanMethods = false)
public class MyDataSourceConfiguration {

    @Bean
    @Primary
    public DataSourceProperties dataSourceProperties() throws Exception{
        log.info("Start generating a Datasource Using AWS Secret Manager.");
        DataSourceProperties dataSourceProperties = new DataSourceProperties();
        SecretManagerConfig secretManagerConfig = new SecretManagerConfig();
        String secretDBConnectionInfo = secretManagerConfig.getDBConnectionInfo();

        ObjectMapper objectMapper = new ObjectMapper();
        DBConnectionInfo dbConnectionInfo = objectMapper.readValue(secretDBConnectionInfo, DBConnectionInfo.class);
        log.info(dbConnectionInfo.toStringJson());
        dataSourceProperties.setUrl("jdbc:postgresql://"+ dbConnectionInfo.getHost()+":"+dbConnectionInfo.getPort()+"/"+dbConnectionInfo.getDbname());
        dataSourceProperties.setUsername(dbConnectionInfo.getUsername());
        dataSourceProperties.setPassword(dbConnectionInfo.getPassword());
        log.info("Datasource is generated.");
        return dataSourceProperties;
    }
}

6. Springboot Project를 시작하여 DataSource 생성확인

Springboot를 기동하면 아래와 같이 DataSource가 생성되는 것을 확인할 수 있습니다.

여기까지 ‘Springboot 구동시 AWS Secret을 조회하여 Datasource 생성’에 대해 소개해드렸습니다. 유익한 정보가 되셨길 바랍니다. 감사합니다. 

Written by 전 희연/ AIX실

BESPIN GLOBAL