인프라 지식이 거의 없는 나에게 AWS 이슈는 재앙과 같다. 이번에는 직장 동료와 함께 5시간 가까이 이 문제만 갖고 머리를 싸매고 있었다. 그 경험을 공유해본다.
사내 프로젝트 Git에서 dev 브랜치의 소스를 개인 브랜치에 merge를 하고 난 뒤 생긴 이슈였다. 먼저 확인된 에러 로그의 일부는 다음과 같다. (보안적인 이유로 일부 내용을 수정하였다.)
Error creating bean with name '★★★★★★' defined in file [/Users/★★★★★.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name '♥︎♥︎♥︎♥︎' defined in class path resource [com/♥︎♥︎♥︎♥︎♥︎♥︎.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [com.amazonaws.services.s3.AmazonS3]: Factory method '♥︎♥︎♥︎♥︎' threw exception; nested exception is com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.
결과부터 얘기하자면 AWS 구성 설정을 하지 않아서 생긴 이슈였다. 하지만 위에 공유된 이슈 외에도 여러가지 에러가 출력이 되면서 원인 파악을 하는데 상당히 오랜 시간이 걸렸다. 좀 더 정확하게 얘기하자면… 코틀린 컴파일 에러와 같이 이번 이슈와 직접적인 관계가 없는 부분에서 원인을 찾으려 했고 Run이 되는 직장동료와 되지 않는 사람(나, It's me)의 차이가 개발 환경의 언어 및 Tool의 의존성 이슈라고 생각 했기 때문에… 원인 파악을 하는데 오래 걸렸다. 해결했던 과정을 좀 더 설명해보고자 한다.
혹시나 IDE 문제가 아닐까?
IDE인 IntelliJ에서 build를 할 때, 내부에 세팅된 Java, Gradle 등의 의존성 문제가 아닐까라는 생각을 해 보았다. 그래서 CLI 상에서 프로젝트를 build or bootRun을 해보았다. 나중에 ‘원인 추정 5’에서 나오겠지만 중간 중간에 Java나 Gradle 버전을 계속 변경하였다. 그래서 빌드 환경이 계속 바뀌기 때문에 Gradle Wrapper(gradlew)로 빌드하지 않고 로컬 Gradle로 진행했다.
$ ./gradle build
$ ./gradle bootRun
위와 같이 실행했을 때도 동일한 현상이 나타나서 본격적인 원인 추정을 시작했다.
원인 추정 1. 빈 생성 에러
Error creating bean with name 'xxxxx' defined in file라는 문장에서 단순한 빈 생성 에러라고 생각했다. 이 형태의 에러에는 여러가지 원인으로 나타날 수 있지만 직접적인 원인이 아닐 것으로 생각했다. 협업하는 분들과 동일한 스프링 환경에서 나만 안된다는게 말이 안된다고 판단했다. 직장동료와 동일한 application.yml을 사용하고 있기 때문이다.
답답한 나머지 위 에러 메세지에 보여지는, Bean에 등록하는 것들을 모두 주석처리를 해보았다. 그러자 아래와 같은 에러 메세지가 나타났다. 이 때 눈치를 챘어야 했다. AWS에서 원인을 찾아야 한다는 것을.
com.amazonaws.SdkClientException: Failed to connect to service endpoint:
(...생략...)
Caused by: java.net.SocketTimeoutException: connect timed
원인 추정 2. 환경변수
DB나 AWS에 필요한 환경변수가 없어서 일 수 있다는 가정을 해 보았다. gitignore로 env 파일이 git에 올라와 있지 않아서 동일한 환경변수로 동작하지 않을 수 있겠다라는 생각이었다. 그래서 직장 동료의 env 파일을 받았다. IntelliJ의 버전에 따라 env 파일을 Run/Debug Configurations에서 등록 못할 수 있다. 그래서 이를 등록할 수 있도록 아래와 같이 플러그인을 설치하였다. (EnvFile 설치)
설치를 하고 나면 아래와 같이 EnvFile 탭이 생기는 것을 볼 수 있다. 여기에 프로젝트 내부로 옮겨진 env 파일 경로를 등록한다.
그러고나서 다시 테스트를 해 보았는데 역시나 해결이 되지 않았다. 나중에 안 사실이지만 AWS 구성 설정이 되지 않은 상태에서 AWS 관련 환경 변수가 있는 것은 의미가 없다.
원인 추정 3. 변경점 확인
main 브랜치에서는 문제가 없이 제대로 Run 동작을 한다. dev 브랜치는 최근에 다른 분들의 작업한 것이 merge된 상태. 그래서 최근에 릴리즈된 main의 소스 코드와 dev의 소스 코드의 차이를 확인해보려고 했다. 하지만 변경점이 너무나 많아서 특정 몇군데를 추려내는 것이 불가능에 가까웠다.
원인 추정 4. Cache의 영향
현재 코드를 제대로 컴파일하는 것이 아닌 이전 코드의 Cache 영향으로 인해 build가 안되는 것이 아닐까 라는 생각도 해보았다. IntelliJ의 invalidate caches부터 gradle cache 제거 등 캐쉬 제거를 해 보았지만 이것도 정답은 아니었다.
원인 추정 5. 의존성 관계
해당 프로젝트 내에서는 Java부터 Gradle, Kotlin 등 의존성 관계를 가지는 것들이 있다. SDK가 버전별 지원되는 스펙이 달라서 너무 최신의 버전 혹은 특정 버전 이전에서는 의존 관계가 있는 다른 SDK를 제대로 지원하지 못할 수 있다. 그래서 구동이 잘 되는 개발 환경의 SDK 버전을 참고하여 SDK를 재설치해 보았다.
기존의 SDK는 제거하고 default 버전을 쉽게 세팅하기 위해서 SDKMAN!을 설치하고 사용했다. 자세한 사용법은 본 주제와 거리가 멀기 때문에 생략한다. SDKMAN 설치 후 기존의 SDK(Java, Kotlin, Gradle 등)를 제거하고 잘 동작하는 개발 환경의 PC를 참고하여 새로운 SDK를 설치했다. 온갖 조합을 다 해보았지만 이 역시도 실패로 돌아갔다. 당연하게도 이 부분이 원인이 아니기 때문. 본문의 가장 상단의 에러메세지에 com.amazonaws.SdkClientException가 아마존에서 제공하는 Java SDK로 오해하기도 했다.
원인 추정 6. Kotlin compile error
Execution failed for task ':kaptKotlin'.
진행하고 있는 스프링 프로젝트는 Kotlin으로 작성되어 있다. Application을 build할 때마다 Kotlin compile error가 나타났다. 혹시나 이것이 주요 원인이 아닐까 했지만 이는 Java 버전을 옳바르게 쓰지 않아서 생긴 이슈였다.
원인 추정 7. AWS Setting
다시 에러 메세지를 살펴보았다. 그리고 지금까지 상황을 돌이켜 보았다.
- 3명이 테스트를 진행했을 때, 나만 이 에러가 나타난다.
- 에러 메세지는 AWS 서비스와 관련된 Bean을 가리키고 있다.
- dev 브랜치에서만 문제가 생기고, 다른 브랜치는 문제없이 Build가 된다.
그렇다면 코드상 문제는 아니다. 브랜치별로도 결과가 다르니 개발환경의 근본적인 부분은 문제가 아닐 것이다. 그렇다면 AWS 세팅에 있어서 별도로 해줘야 하는 것이 있는 것일까. 내 디바이스에는 AWS CLI가 설치되어 있지 않고 AWS 구성 설정도 되어 있지 않았다. 우선 AWS CLI 설치(Mac 기준, Download 후 설치)를 먼저 했다. 그러곤 ECR list-images 명령어를 입력해 보았다. (cluster-autoscaler는 예시용 repository)
// cluster-autoscaler 대신 실제 레포지토리 이름을 입력한다.
aws ecr list-images \
--repository-name cluster-autoscaler
// 정상적이지 않은 메세지가 출력됨
You must specify a region. You can also configure your region by running "aws configure".
예상대로 출력이 제대로 되지 않았다. AWS 구성 설정이 안되어 있기 때문이다. aws configure 명령어로 구성 설정을 해보자. 이 때 Access key와 Secret key가 필요하기 때문에 IAM에서 Access key를 발급해야 한다. 이 때 Access key와 Secret key는 반드시 별도로 잘 저장해서 가지고 있어야 한다.
발급받은 뒤 aws configure를 터미널에 입력해서 구성 설정을 한다. 아래 ID와 Key값은 예시값이다. 또한 Default region name을 미입력시 오류가 나타날 수 있다.
$ aws configure
AWS Access Key ID [None]: ABCD1234
AWS Secret Access Key [None]: 1234ABCD
Default region name [None]: ap-northeast-2
Default output format [None]:
자, 다시 ECR list-images 명령어를 입력해보면 제대로 된 Output이 나온다. 이제 프로젝트로 돌아가 build를 하면 정상적으로 동작하는 것을 확인할 수 있다. 빨리 찾지 못했던 이유는…
- 너무 방대한 에러 로그가 노출되어 이를 바탕으로 수많은 원인 추정이 있었고
- 이미 AWS 구성 설정을 한 개발환경과 비교를 했으니 겉으로 봤을 때는 이 부분이 차이라는 것을 바로 눈치챌 수 없었다
그나마 다행이라고 한다면 언젠가 발견될 이슈였다는 것이다. 물론 새로 합류하는 동료가 처음부터 AWS부터 세팅한다면 이런 사건사고(?)를 만나지 않을 수 있겠지만 앞으로 발생이 안되리라는 보장은 없다. 하루 통으로 날린 나와 직장동료의 고생이 그만큼 가치 있었다고 굳게 믿으면서 마무리한다.
참고자료
- 구성 및 자격 증명 파일 설정 - AWS Command Line Interface
- [AWS] IAM 사용자 추가와 aws cli 설정
- AWS CLI(Command Line Interface) 초기 설정 및 프로파일 추가방법
'Dev Memo' 카테고리의 다른 글
스프링에서 local.yml 또는 dev.yml 환경으로 전환하고 싶을 때 (0) | 2021.11.29 |
---|---|
ant-design의 typescript 버전이 보이지 않는 경우 (0) | 2021.11.03 |
[Spring] HTTP Request 전송 시, Broken pipe 이슈 (0) | 2021.08.15 |
License for package Android SDK Build-Tools not accepted. (0) | 2021.03.18 |
[안드로이드] Logger로 각종 로그 및 Json 살펴보기 (0) | 2021.03.07 |
Comment