1. 이전 맥락
2024.04.16 - [BE/AWS] - AWS 정책변경 후, 0원에 이용하기
AWS 정책변경 후, 0원에 이용하기
1. AWS IPv4 정책 변경 https://aws.amazon.com/ko/blogs/korea/new-aws-public-ipv4-address-charge-public-ip-insights/ 공지 – AWS Public IPv4 주소 요금 변경 및 Public IP Insights 기능 출시 | Amazon Web Services AWS에서 퍼블릭(Public) IP
gamxong.tistory.com
앞선 글을 통해서, 저희는 AWS 정책 변경 후에도 0원에 자원들을 사용할 수 있게 되었는데요!
하지만 해당 작업을 하게 되면 크게 2가지에 있어 불편한 점이 생깁니다.
1. DataGrip과 같은 유틸 접근
2. 디버깅을 위한, 로컬 서버에서 rds 접근 불가
해당 문제점에 대해 자세히 다뤄보고, 해결방법까지 제안해보겠습니다!
1. DataGrip 유틸 접근
DataGrip외에도 db관련 유틸은 많지만, 가장 많이 사용하는 툴이기에 해당 툴을 기준으로 설명을 드려보겠습니다!
RDS를 프라이빗으로 설정하면, 더이상 DataGrip에서 연결할 수 없게 되는데요!
사실 너무 당연한 것이라고 볼 수 있습니다.
기존에는 퍼블릭하게 누구나 접근할 수 있도록 했지만, 이제는 EC2를 통해서만 접근할 수 있으니까요!
DataGrip에서는 ssh 연결 옵션을 활성화한다면, 간편하게 EC2를 통한 RDS 연결이 가능해집니다!
사실 이거 관련해서는 이미 너무 훌륭한 글이 있기에, 해당 글을 참고하면 좋을 것 같습니다!
https://jojoldu.tistory.com/623
DataGrip 에서 SSH 터널링으로 DB 접근하기
보안상의 이유로 운영 환경의 데이터베이스에는 직접 접근하는 경우를 거의 차단합니다. 예외적인 경우 별도의 EC2 서버를 SSH 터널링을 통해 접근하는 방식을 사용하게 되는데요. 그럼 아래와
jojoldu.tistory.com
2. 디버깅을 위한, 로컬 서버에서 rds 접근 불가
제가 이 글을 쓰게된 이유이기도 합니다! 해당 내용 관련해서는 자세히 설명해주는 글이 많지 않아 적게 되었습니다!
2-1. 문제 상황
만약, 프론트에서 어떤 에러가 발생했다고 가정해봅시다.
해당 에러에 대한 정확한 이유를 파악하기 위해선 사실 디버깅이 가장 강력한 무기라고 생각하는데요!
로깅을 휼륭하게 구축했다고 하더라도, 정말 정확한 원인을 파악하기엔 저는 디버깅만한게 없다고 생각합니다!
아무리 로컬에서 로컬 db를 통해 확인을 한다고 하더라도, 로컬과 실제 서버 환경은 다를 수 있기 때문입니다!
또는 로컬 db와 rds db에 저장된 데이터가 다르기에 다른 오류가 발생할 수도 있고요!
따라서 로컬에서 db를 서버 rds로 꽂아서 디버깅을 한다면 정확한 원인파악을 금방할 수 있게 됩니다!
그 디버깅을 위해선 인텔리제이를 활용하는 것이 굉장히 편하기도 하고요!
(물론 동일하게 만드는 것이 가장 좋지만, 현실적으로 어려운 부분들이 있습니다..)
(당연히 운영서버보단 개발서버를 대상으로 말씀드린 것입니다!)
(디버깅의 강력함과 간단한 사용법은 아래 글을 통해서...)
하지만 DataGrip과 같은 맥락으로, 로컬환경에서 yml의 url만 실제 RDS url로 수정한다고 해서 연결될 수는 없습니다!
또한, AWS 내에서의 디버깅은 인텔리제이를 활용한 디버깅에 비해 굉장히 불편하고 복잡합니다!
따라서 DataGrip과 같이 ssh 연결하여 local 환경 -> EC2 -> RDS 로의 연결이 필요합니다!
2-2. 해결 방법
그냥 연결해주면 되는거 아니야?
사실 저는 이론적으론 이해했지만 이것을 실제 구현하기는 쉽지 않았는데요!
하는 방법만 알면 사실 금방할 수 있는 것입니다!
그래서 저는 이 글을 읽는 분들이 제 글을 읽고 조금이나마 시간을 절약하면 좋을 것 같습니다 !!
연결하는 방법은 사실 하나의 명령어로 가능합니다!
ssh -f {서버이름}@{나의 EC2 ip} -L {로컬호스트 ip}:{로컬호스트 포트}:{나의 RDS ip}:{나의 RDS에서 열린 포트번호} -N -i {EC2 서버에 대한 pem키}
구체적인 설명을 드려보겠습니다!
- 서버이름 : EC2의 서버 이름 ex) ubuntu
- 나의 EC2 ip : 설명 그대로 ex)1.23.45.678
- 로컬 호스트 ip : RDS와 연결할 나의 ip 입니다 ex) 127.0.0.1
- 로컬호스트 포트 : RDS와 연결할 나의 포트 입니다. 정상적인 연결을 위해선 사용하지 않고 있는 포트 번호 입력이 필요합니다! ex) 3309
- 나의 RDS ip : RDS 프라이빗 ip 를 입력하면 됩니다. ex) 123.45.6.789
- 나의 RDS에서 열린 포트번호 : RDS에서 열어둔 포트번호를 입력하면 됩니다. 저는 Mysql를 사용했기에 3306였습니다. postgresql은 5432 다른 db는 해당 포트번호 입력하면 됩니다!
- EC2 서버에 대한 pem키 : EC2 연결을 위해 필요한 pem키 입니다. pem 키가 저장된 경로를 입력하면 됩니다! ex) /path1/path2/tmp.pem
해당 예시들을 통해 아래 그림만 이해하면 이제 이해해야 할 내용은 모두 끝났습니다! (해당 동작은 ssh 터널링라고 합니다)
뭔가 복잡해 보이지만 이것만 이해하면 모든게 끝났습니다!
이렇게 명령어로 먼저 설명을 드린 이유는 spring 코드도 결국 해당 값들을 넣는 작업을 하는 것이기 때문입니다!
2-3. Spring 코드
build.gradle
// ssh 터널링 세팅
implementation 'com.github.mwiede:jsch:0.2.17'
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
application-stg.yml
저는 "stg" 라는 프로필을 새로 만들어서 진행했습니다! 그냥 "dev"로 하실거면 바꿔도 좋을 것 같아요!
spring:
config.activate.on-profile: stg # 프로파일 이름 등록
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:[forwardedPort]/{db이름}?useSSL=true&serverTimezone=Asia/Seoul&characterEncoding=UTF-8
username: db 유저네임
password: db 암호
jpa:
hibernate:
ddl-auto: validate
show-sql: true
properties:
hibernate:
format_sql: true
ssh:
remote_jump_host: # ssh 연결할 중간 서버 주소, ec2 ip주소
ssh_port: # ssh 프로토콜 포트, 보통 22
user: # ssh 연결할 사용자 계정
private_key: # ssh 연결 시 사용할 개인키 파일 경로
database_url : # rds url
database_port: # rds 열려있는 포트번호
일부만 가져온 것이기에 참고만 해주세요!
기존 dev yml과 다른 부분은 spring.datasource.url과 ssh 부분입니다!
url은 로컬호스트에서 연결이 되기에 127.0.0.1 로 되어있습니다.
포트 번호는 실행 때마다 사용가능한 포트를 동적으로 탐색하기에 [forwarededPort] 로 지정했습니다!
스프링 코드를 통해서 해당 문자열이 포트번호로 대체되도록 구현했습니다! ex) 3309
SshTunnelingInitializer
@Slf4j
@Profile("stg")
@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);
log.info("start ssh tunneling..");
JSch jSch = new JSch();
log.info("creating ssh session");
jSch.addIdentity(privateKey); // 개인키
session = jSch.getSession(user, remoteJumpHost, sshPort); // 세션 설정
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
log.info("complete creating ssh session");
log.info("start connecting ssh connection");
session.connect(); // ssh 연결
log.info("success connecting ssh connection ");
// 로컬pc의 남는 포트 하나와 원격 접속한 pc의 db포트 연결
log.info("start forwarding");
forwardedPort = session.setPortForwardingL(0, databaseUrl, databasePort);
log.info("successfully connected to database");
} catch (Exception e){
log.error("fail to make ssh tunneling");
this.closeSSH();
e.printStackTrace();
exit(1);
}
return forwardedPort;
}
}
해당 코드는 ssh 터널링 연결을 하는 하는 과정입니다! 실제 연결 시도하기 전에 ssh 세션을 만드는 과정인데요!
쉽게 말해, 미리 연결 세팅을 한다고 볼 수 있습니다!
필드 변수는 yml 값들로 주입을 그대로 받습니다!
또한, 해당 코드에서 forwardedPort 에 연결 가능한 포트번호를 찾아 할당합니다. ex) 3309
SshDataSourceConfig
@Slf4j
@Profile("stg")
@Configuration
@RequiredArgsConstructor
public class SshDataSourceConfig {
private final SshTunnelingInitializer initializer;
@Bean("dataSource")
@Primary
public DataSource dataSource(DataSourceProperties properties) {
Integer forwardedPort = initializer.buildSshConnection(); // ssh 연결 및 터널링 설정
String url = properties.getUrl().replace("[forwardedPort]", Integer.toString(forwardedPort));
log.info(url);
return DataSourceBuilder.create()
.url(url)
.username(properties.getUsername())
.password(properties.getPassword())
.driverClassName(properties.getDriverClassName())
.build();
}
}
forwaredPort 번호를 해당 클래스에서 받아 datasource.url의 포트를 알맞게 변경합니다.
예를 들어 3309 를 받았다면, datasource.url은 최종적으로 127.0.0.1:3309가 됩니다!
이렇게 세팅이 끝났다면, 로컬에서도 rds로 연결하는 것이 가능해집니다!
추가적인 질문있다면 환영입니다 :)
'BE > AWS' 카테고리의 다른 글
AWS 프리티어 요금 발생.. 0원에 이용하는 방법 (0) | 2024.04.16 |
---|
댓글