Elastic beanstalk 배포 시 nginx 111:Connection refused, HTTP 502 에러
⚡ 문제 발생
언제나처럼 평화롭게 기능 개발을 마치고, main branch로 각 branch를 merge하여 GitHub Action을 통해 AWS에 자동 배포를 진행했다.
특별하거나 많은 기능 개발을 한 것이 아닌, 그저 Swagger 문서를 추가하고, 원래 개발되어 있던 엔티티 관련 로직을 살짝 추가한 정도였기 때문에 배포에서 문제가 발생할 것이라고는 꿈에도 상상을 못했다…
(3일 동안 이것만 붙잡고 있었다.)
더 미궁으로 빠졌던 점은, 배포 실패의 이유가 납득이 되지 않는다는 것이었다.
이를 해석해보면, AWS 상태 검사에 실패했다는 것이다.
나는 이전에 AWS 상태 검사에 대한 에러를 마주한 적이 있었고, (https://chominho96.github.io/aws/AWS-%EC%83%81%ED%83%9C-%EA%B2%80%EC%82%AC/) 이 때 상태 검사 관련 조치를 취해놨기 때문에, 이 에러가 다시 발생했다는 것에 멘탈을 놓았다…
이때부터 3일간 에러 로그를 가지고 비슷한 에러란 에러는 다 찾아다니면서 해결책을 적용해봤던 것 같다. 결론부터 말하자면 어이없게? 스스로 해결하게 되었는데 수많은 구글링을 하면서 얻은 지식들도 많아서 정리하고자 글을 남기려 한다.
지금 생각해보면 이 부분에 대해서 먼저 찾아봤어야 한다고 생각한다. Elastic beanstalk에 뭔가 오류가 발생한다면 어디엔가 로그가 남겨져 있을 것이고, 그 로그로부터 원인을 찾는 것은 당연하니까 말이다.
하지만 부끄럽게도 난 위에서 설명한 에러 로그를 보고 단순히 GitHub Action에서 출력된 에러 메시지가 이전에 마주친 에러 메시지와 동일하다는 이유만으로 괜히 Spring Security의 모든 필터를 해제하고, 추가한 Swagger에 문제가 있는지 살펴보는 등 엉뚱한 곳에서 삽질을 무수히 많이 했다..
어쨌든 3일 간 삽질했던 것을 모두 나열하면 끝이 없으므로(…) 각설하고 알게된 지식들을 정리해보자.
🧑💻 Elastic Beanstalk에 오류가 발생한다면 취할 수 있는 조치들
먼저 Elastic Beanstalk은 빌드할 때부터 가동 중일 때에도 모든 순간순간 필요한 로그를 기록해둔다.
이는 Elastic Beanstalk의 EC2에 /var/log 디렉토리에 모두 저장되어 있다.
여기까진 쉽게 찾을 수 있는 내용인데, 배포에 실패했을 때가 핵심이다.
GitHub Action으로 배포를 진행하고 배포에 실패한다면, 자동으로 이전에 올려져있던 버전으로 롤백하게 된다.
따라서 배포 실패 후 다시 EC2에 SSH로 접속하여 로그를 확인하면, 정상적으로 돌아가고 있던 기존의 EC2에 대한 로그밖에 보이지 않는다.
우리가 원하는 것은 배포가 실패한 EC2의 (배포 실패 후엔 사라진) 로그이다.
이는 AWS 콘솔에서 얻을 수 있다.
배포가 실패하고나서 바로 AWS 콘솔에 들어가 위의 화면처럼 [로그] 탭에 들어가면, 가장 최근의 로그 파일을 다운로드 받을 수 있다.
(배포 실패를 보자마자 바로 들어가도록 하자! 이유는 모르겠지만 사진에서도 나와있듯이 어느정도 시간이 지나면 로그 파일이 없어진다.)
이 로그 파일들을 다운로드 받으면, 배포 실패 후 영영 사라진 EC2의 /var/log 디렉토리를 통째로 볼 수 있다.
여기서 살펴볼 로그 목록은 다음과 같다. (필요한 정보만 축약해서 설명하겠습니다.)
- messages : Spring Boot 관련 로그를 볼 수 있다.
- nginx/error.log : nginx에서 발생한 에러를 볼 수 있다.
- nginx/access.log : 상태검사 로그와 그에 대한 결과를 볼 수 있다.
이 로그들을 찬찬히 살펴보면서 (IDE로만 에러들을 봐왔더니 텍스트 에디터로 로그 보는게 익숙치 않아서 대충 보게 되는 것 같다..) 어디에서 에러가 발생했는지 디버깅을 하면 된다.
여기서 나는 구글링을 통해 error.log와 access.log에 대해서 알게 되었고, 이를 열어봤을 때 다음과 같은 로그를 볼 수 있었다. (더 깊은 미궁으로 향한 순간)
이제 이 에러에 대한 설명을 하고자 한다.
🧑💻 nginx 111:Connection refused, HTTP 502 에러
이 에러를 보자마자 바로 에러 로그를 가지고 또 다시 무한 구글링을 시작했다.
생각보다 유명한(?) 에러이고, 여러 글들을 찾을 수 있었다.
내용은 생각보다 다양했지만, 천천히 살펴봤더니 별로 상관없어 보이는? 답변들도 많았고, 가장 맞다고 생각하는 (결국엔 나에게도 해당이 되었던) 원인과 해결책은 다음과 같다.
원인
일단 access.log의 로그를 자세히 살펴보자. HTTP 요청에 실패했는데, HTTP Status가 502를 나타낸다. 502 에러는 게이트웨이 관련 에러를 의미하는데, AWS에서는 대부분 로드 밸런서에 관련된 문제일 가능성이 높다.
또한, error.log에서 111:Connection refused는 현재 웹 서버로 연결 자체가 안 되었다는 뜻이다. 즉, 502 에러와 어느정도 상통하는 뜻을 가지고 있다.
이는 즉, 로드 밸런서가 정상적으로 5000번 포트에서 실행되고 있는 웹 서버로부터 요청을 받지 못함을 의미한다.
위의 그림을 살펴보자. 왼쪽의 그림과 같은 경우 502 에러가 발생하게 된다. Nginx 서버가 5000번 포트를 사용하여 돌아가고 있는 WAS로부터 response를 받아와야 하는데, 5000번 포트에 실행되고 있는 WAS가 없거나, 정상적으로 실행되고 있지 않은 경우 502 에러를 띄우는 것이다.
왼쪽 그림과 같은 경우, WAS가 8080포트에서 실행되고 있기 때문에 이러한 오류가 발생하는 것이다.
Elastic Beanstalk에서는 default로 Nginx 서버가 5000번 포트랑 연결하는 것으로 설정되어 있다.
그런데 WAS는 5000번 포트가 아닌 8080번에서 실행되고 있기 때문에, 당연히 Nginx는 정상적으로 response를 받을 수 없는 것이다.
해결책
위의 그림과 같은 경우에서 해결책은 간단하다. 둘 중 하나만 해주면 된다.
- Nginx가 8080번 포트를 바라보게 한다.
- WAS가 5000번 포트로 실행되게 한다.
그런데 나의 경우 굳이 Nginx 포트를 바꾸는 것보다. yml에서 한 줄만 추가하면 바꿀 수 있는 2번 방식을 택했다.
이 경우 배포할 때 사용되는 yml 파일에 다음과 같이 입력하면 된다.
1
2
3
4
5
6
...
server:
port: 5000
...
이런 식으로 입력해주면 배포 시에 Spring 서버가 5000번 포트로 실행되게 되고, 정상적으로 Nginx가 바라볼 수 있게 된다.
근데 왜 나한테..??
통상 502 에러의 이유는 저런 이유들이라고 구글링 결과 알 수 있었다.
하지만 이는 더더욱 나를 미궁에 빠뜨렸는데, 애초에 5000번 포트로 내 프로젝트는 설정되어 있었고 (이전에 배포를 성공했으니까 당연하다.) 실행이 안될리가 없었기 때문이다.
이로 인해 나는 엉뚱한 Elastic Beanstalk에 SERVER_PORT 환경 변수를 추가하는 등 (Stack Overflow 답변을 너무 신뢰하지 말자.. 너무 많은 답변이 있었는데 에러를 해결한 지금 다시 보니 아예 엉뚱한 답변도 많았다..) 별의 별 짓을 다 해봤다.
😊 해결 : Spring Boot 상에서의 문제
이렇게 에러가 발생한지 3일째 되는 날, 나는 똑같은 에러 로그만 바라보고 있다가, 문득 에러 폴더의 messages를 들어가보게 된다.
messages의 첫 부분들이다. 나는 이걸 보고 Ubuntu 관련 로그이겠거니 하고 아무 생각 없이 로그의 끝으로 내려보는데..!!
????
Spring이 실행되는 로그가 보였고, 에러 로그로 인해 run에 실패한 모습이 보였다. 그 순간 진짜 아차 싶었다.
에러 로그를 바로 뒤져봤고, 1분 만에 DB 컬럼명 관련 오류임을 알 수 있었다.
그제서야 커밋 목록을 뒤져봤더니, 몇몇 엔티티에 컬럼 몇 개가 추가된 것을 볼 수 있었다.
로컬에서는 jpa-auto를 create로 설정하고 개발하기 때문에 아무 상관 없었지만, 배포 환경에서는 미리 테이블을 생성해두고 jpa-auto를 none으로 설정했기 때문에, 당연히 jpa가 다시 테이블을 drop하고 create하지 않았고, 추가된 컬럼에 대해 실제 DB와 매핑되지 않았던 것이다.
설상가상으로 프론트엔드와의 연동을 위해 테스트 데이터를 넣어주는 코드를 임시로 넣어둔 상태였는데, 이로 인해 앱이 실행되는 시점에 테스트 데이터를 넣는 코드를 실행하게 되었고, 이때 오류를 내고 종료되었던 것이다. (테스트 데이터를 넣는 로직이 없었다면 그래도 실행은 되고 API 호출 시에 오류가 발생하지 않았을까 싶다.)
즉, 정리하자면 Spring 자체가 run에 실패했고 이로 인해 당연히 5000번 포트에는 돌아가고 있던 WAS가 없었으며, 결국 Nginx가 5000번 포트를 바라봤는데 아무것도 없는 지경에 이르렀던 것이었다.
에러를 해결했다는 기쁨보다는 왜 이걸 생각하지 못했지에 대한 생각과 로그를 제대로 살피지 못한 게 부끄러웠다.
그래도 이 에러를 해결하면서 Elastic Beanstalk에서 할 수 있는 수많은 경우의 수로? 디버깅을 해봤다는 것은 좋은 경험이었던 것 같다.
앞으로 배포에 실패했을 때는 적어도 이번 경험처럼 무수한 삽질을 반복하진 않고, 그래도 무슨 에러가 발생했는지까지는 제대로 파악하고 삽질을 하지 않을까 싶다.
정리
- Elastic Beanstalk 배포 시에 오류가 발생한다면, /var/log 폴더에서 로그를 찾아보면 대부분 이유를 찾을 수 있다. (SSH로 접근하지 말고 AWS 콘솔에서 다운로드!)
- Nginx 502, 111:Connection Refused 오류는 대부분 애플리케이션 자체가 실행되지 않았을 때이다. 그의 원인 중 하나가 Spring에서 포트가 8080으로 설정되어 있을 수 있다는 것인데, 그게 아니라면 messages 로그를 까서 Spring이 왜 실행되지 않았는지를 살피자.
정말 바쁜 시기에 에러를 만나 처음에는 별 것도 아닌 것 같은 걸로 생각해서 당시에는 짜증도 났지만, 지금 와서 생각해보니 매우 큰 배움을 얻었더 좋은 경험이었다.
⚡개인적으로 공부하면서 포스팅한 글입니다. 오류가 있는 경우 지적해주시면 감사하겠습니다!⚡
댓글남기기