Private Subnet에 설치된 MySQL Connection Error 트러블슈팅 일기

지누·2024년 8월 2일
0

이전 글에서, AWS parameter store를 이용하여 환경 변수를 관리하고, CI-CD를 구축하는 과정에서 발생한 문제에 대해 적어보도록 하겠다.

참고로, 현재 프로젝트의 아키텍처는 다음과 같다. 아래에 있는 모니터링 모듈은 구현이 되지 않았으니 위의 두 서브넷만 보면 될 것 같다.

❓문제 1. CI 과정에서 의존성 주입 실패

gradlew build 과정에서 에러가 발생했다.

❗️문제 1. 원인

빌드를 하게 되면 테스트코드도 정상 동작하는지 실행하게 된다. 스프링부트 프로젝트를 처음 생성하면 기본적으로@SpringBootTest가 붙은 테스트 메서드가 생성된다. 이 어노테이션은 통합테스트를 실행하고, 실제 스프링부트 동작 환경처럼 의존성을 주입하게 된다.

Spring-data-jpa 의존성 설치 후 DB_URL, DB_PASSWORD 등을 로컬 환경변수로 관리하였고, 배포 환경에서는 parameter store로 관리하였다.

그러나 ci가 동작하는 github-action의 컨테이너 환경에서는 값을 읽어올 수 없으므로, 테스트코드를 실행하는 과정에서 에러가 발생했던 것이다.

❗️문제 1. 해결

아직 테스트코드를 작성하기 이전의 단계였기 때문에, 일단은 @SpringBootTest 어노테이션을 제거하고 단위테스트만 작성하기로 하였다.

그러나 이것은 임시방편에 불과하고, 이후 통합테스트 도입을 위한 방법을 찾아보았다.

test/resources 디렉터리 내부에 application-test.yml 파일을 생성하여 테스트코드를 위한 환경을 구성하는 것이다. 그리고 CI 과정에서 빌드를 할 때 profile.active = test로 사용한다면 구성된 테스트환경에서 테스트 할 수 있다.

이 때 사용하는 데이터베이스는 freeDB등을 이용하여 구축하는 것을 제안했는데, 도입하는 시점에 자세히 알아보도록 하겠다.

사실 진짜 문제는 2번이었다...


❓문제 2. CI-CD 이후 배포 환경에서 DB Connection이 연결되지 않는 문제

현재는 오류를 해결해서, 오류 내용을 캡처하지를 못했다. 괜히 해당 상황을 재연하기가 두렵다..
아무튼 ci-cd가 정상 동작하였는데, 서버에서 실행하는 도커 컨테이너를 살펴보면 죽어있는 상태였다.

도커 로그를 살펴보니 SQL~~NullException과 함께 JDBC connection fail이라는 에러 문구가 눈에 들어왔다. 커넥션을 맺는 과정에서 실패를 했다는걸 알았고, 원인을 찾기로 하였다.

❓Try 1. parameter store 접근 권한 체크

DB와 관련된 환경 변수는 전부 parameter store에서 관리를 하고 있다. parameter store에 대한 접근 권한은 IAM Role을 통해 부여한 상황이었으나, 처음 사용하는 기술이다보니 잘 가져오는지 확인을 할 필요가 있었다.
aws cli 설치 이후, 아래 명령어를 통해 parameter store에 저장된 값을 확인할 수 있다.

aws ssm get-parameter --name 저장한파라미터이름

스프링부트가 돌아가는 ec2에 접속하여 확인했을 때 값을 가져오는 것을 확인했다. 따라서 parameter store의 권한 문제가 아니라는 것을 파악했다.

물론 위 방법을 알아내기까지 많은 시간이 걸렸고, 처음 시도하는 애꿎은 parameter store의 문제가 아닌가 하고 온갖 방법을 시도했던 것 같다.

❓Try 2. MySQL에서 모든 소스에서 요청을 허가하는 사용자 생성

mysql이 설치되어있는 private subnetpublic subnet에서만 접근 가능하므로, 두 번의 ssh연결을 통해 mysql이 설치된 ec2로 접속하였다.

그리고 root계정을 통해 진입을 시도하였으나, sudo 명령어 없이는 root계정으로 접속이 불가능했다. 그래서 root 계정을 이용한 것이 문제가 되는가 싶어서, 사용자를 하나 만들기로 하였다.

사실, root가 아니라 특정 권한을 부여한 user를 만드는 것이 옳은 방법이다. 하지만 고려할 사항이 많다보니 일단은 root 계정으로 진행을 하고 있었다.

그래서 caecae라는 이름의 사용자를 만들고, 모든 소스에서 요청을 받을 수 있도록 %로 설정하고, 데이터베이스 대한 접근 권한을 모두 open하였다.

커맨드는 잘 기억이 나지 않는데, 검색을 통해 아래와 비슷한 명령어를 날렸다.

CREATE USER 'caecae'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON *.* TO 'caecae'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

이렇게 하면 모든 곳에서 로그인 가능한 사용자가 생성되었는데, 해당 사용자를 이용하여도 로그인이 되지 않았다.

❓Try 3. public ec2에서 private ec2:3306으로 접속 시도

스프링부트 어플리케이션에서 접속을 시도할 때는 22번포트인 ssh가 아닌 3306 번 포트로 접속을 하게 된다. 스프링부트가 돌아가는 ec2mysql을 설치하기 싫어서 테스트를 안했는데, 결국엔 설치하여 직접 접속을 시도하였다.

mysql -h [DB EC2의 프라이빗IP] -P 3306 -u your_username -p
-P 3306은 생략해도 무관함

놀라운 점은, 여기서 접속이 되어야 한다고 생각했는데 접속이 되지 않았다.

ERROR 2003 (HY000): Can't connect to MySQL server on '1xx.xx.4.136' (111)

위와 비슷한 에러코드를 뱉었다는 것이다. 결국 스프링부트의 문제도 아니고, parameter store의 문제도 아니라는 것을 알아냈다.

그렇다면 왜 접속이 되지 않았을까?

고민고민하며 많은 시도를 하다 화장실에서 영감이 떠올랐다. 보안그룹의 인바운드 설정도 문제 없이 해 주었고, 22번포트를 사용한 접속은 가능한데 3306번 포트를 사용한 접속이 되지 않는다? 그렇다면 mysql이 존재하는 ec2mysql 설정에 문제가 있겠구나..!라고 생각을 했다.

그리고 열심히 검색을 한 결과, 12년전에 올라온 스택오버플로우의 글에서 정답을 찾아냈다.

❗️❗️문제2. 해결


/etc/mysql 디렉터리에 들어가면 다음과 같은 설정 파일이 존재한다.my.cnf파일을 열었을 때 중간 즈음에 아래와 같은 값이 작성되어 있는지 확인하자

bind-address = 127.0.0.1

만약 없다면, /etc/mysql.conf.d 디렉터리로 한 단계 더 들어가면 mysqld.cnf 파일에 저장되어 있을 것이다. 나는 후자의 경우에 해당되었다.

해당 파일을 열어서 아래처럼 값을 바꿔주자

bind-address = 0.0.0.0

즉, 위의 경우에는 로컬호스트에서 요청한 요청만 수락하겠다는 뜻이고, 아래는 모든 ip에 대해서 요청을 수락하겠다는 뜻이다. 그래서 public ec2에서 아무리 접속하려고 해도 실패했던 것이었다...

그리고 이제는 문제가 해결되었고, 서버도 정상적으로 배포가 가능하게 되었다..!!

profile
열심히 살자😱

0개의 댓글