개발일기

[Spring Boot] RDS public IP 요금 결제 안되게 구성하는 방법 본문

photocard backend server 개발일기

[Spring Boot] RDS public IP 요금 결제 안되게 구성하는 방법

한둥둥 2024. 12. 17. 21:51

 🔥 우선적으로 public IP를 구성하면 IPv4에 대한 요금이 지출되기에 privateIP로 선택해주었다. 완벽하게 RDS를 무료로 쓰기 위해서는 해당 방식으로 구현해야한다. 지금부터 무료로 사용하기 달려가보자..! 🔥

 

 

우선 public ip로 하던 것을 퍼블릭 액세스 불가능 선택 

해당 RDS와 연결할 EC2 선택 ..! 

 

이제부터 본격적으로 스프링에서 어떤방식으로 public IP를 구성하지 않으면 작성해야되는지 알아보자..! 

 

 

spring:
	config:
    	activate:
      		on-profile: "local"
    datasource:
    	driver-class-name: com.mysql.cj.jdbc.Driver
    	url: jdbc:mysql://127.0.0.1:[forwardedPort]/[디비명]?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
    	username: 디비 유저이름
    	password: 패스워드
ssh:
  remote_jump_host: # ssh 연결할 중간 서버 주소, ec2 ip 주소
  ssh_port: 22  # ssh 프로토콜 포트, 보통 22
  user: # 유저명 
  private_key: / # ssh 연결 시 사용할 개인키 파일 경로
  database_url: # RDS 엔드포인트 URL
  database_port: # 포트 번호

 

위에와 같이 .yml파일을 작성해준다. 

 

// ssh 터널링
    implementation 'com.github.mwiede:jsch:0.2.17'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

ssh 터널링을 해주기 위해서는 위에의 임포트가 필수적이다. 

 

 

package com.realworld.global.config.ssh;

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import jakarta.annotation.PreDestroy;
import jakarta.validation.constraints.NotNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import java.util.Properties;

import static java.lang.System.exit;

@Slf4j
@Profile("local")
@Component
@ConfigurationProperties(prefix = "ssh")
@Validated
@Setter
public class SshTunnelingInitializer {

    @NotNull
    private String remoteJumpHost;

    @NotNull
    private String user;

    @NotNull
    private int sshPort;

    @NotNull
    private String privateKey;

    @NotNull
    private String databaseUrl;

    @NotNull
    private int databasePort;

    private Session session;

    @PreDestroy
    public void closeSSH() {
        if(session.isConnected()) {
            session.disconnect();
        }
    }

    public Integer buildSshConnection() {
        Integer forwardedPort = null;

        try{
            log.info("{}@{}:{}:{} with privateKey", user, remoteJumpHost, sshPort, databasePort);

            JSch jSch = new JSch();

            log.info("creating ssh tunneling..");
            jSch.addIdentity(privateKey);
            session = jSch.getSession(user, remoteJumpHost, sshPort);
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            log.info("start creating ssh session");

            log.info("start connecting ssh connection");
            session.connect();
            log.info("success connecting ssh connection");

            log.info("start forwarding");
            forwardedPort = session.setPortForwardingL(0, databaseUrl, databasePort);
        } catch (Exception e) {
            log.info("fail to make ssh tunneling");
            this.closeSSH();
            e.printStackTrace();
            exit(1);
        }

        return forwardedPort;
    }

}

해당 부분의 코드는 ssh 로 연결을 해주기 위한 클래스이다. 

@Component로 등록이 되어있어 Spring Context가 실행될 때, Spring 이 실행될 때 자동으로 로드된다. 이를 통해서 dataSource를 연결할 계획 

 

이런식으로 해주어야 하는 이유는 private ip 이면 ec2 내부에서는 RDS 접근하여 디비에 담을 수 있으나, 로컬에서는 실행 할 수 없다. 그래서 ssh 방식을 통해서 dataSource에 접근하는 것이다. 

 

 

package com.realworld.global.config;

import com.realworld.global.config.ssh.SshTunnelingInitializer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;

import javax.sql.DataSource;

@Slf4j
@Profile("local")
@Configuration
@RequiredArgsConstructor
public class SshDataSourceConfig {

    private final SshTunnelingInitializer initializer;

    @Bean("dataSource")
    @Primary
    public DataSource dataSource(DataSourceProperties properties) {
        Integer forwardPort = initializer.buildSshConnection();
        String url = properties.getUrl().replace("[forwardedPort]", Integer.toString(forwardPort));
        log.info("url={}",url);
        return DataSourceBuilder.create()
                .url(url)
                .username(properties.getUsername())
                .password(properties.getPassword())
                .driverClassName(properties.getDriverClassName())
                .build();
    }

}

 

DataSource에 해당하는 코드를 작성해주었다.