Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

마로의 개발일지

Golang Lambda DB 커넥션 증가 해결 후기 본문

기록

Golang Lambda DB 커넥션 증가 해결 후기

maro0201 2023. 6. 11. 17:51

 이번 글은 결론부터 말하자면 Lambda Function내에 DB 커넥션이 요청이 들어올 때마다 생성이 되게 되어있어 발생한 에러였다. 해당 문제는 이전에 vertical-api에서 redis 커넥션을 요청마다 생성해서 발생한 에러와 유사한 에러였으며 Lambda에서 DB 커넥션의 관리가 얼마나 중요한지 또 한 번 깨닫는 좋은 경험이었다. 이번 글에서는 해당 문제를 해결하기 위해 진행했던 테스트 방법과 결과들을 소개할 예정이며 결과적으로 해당 테스트들이 큰 유의미한 결과를 가져오진 못했지만 이런 방법들을 통해 이런 결과들이 나왔다 정도만 참고하면 좋을 것 같다.

 

배경

 커넥션 증가로 인한 장애가 발생하고 장애 보고서가 작성된 뒤 작업한 담당자가 휴가라 휴가 동안 내가 해당 장애를 분석하는 일을 맡게 되었다. 해당 api는 golang 1.9x 버전과 Serverless Framework 3.x버전으로 작성되어 있었고, 난 golang의 기본 문법은 알지만 실제 개발 해본 내용은 간단한 기능 추가가 다였었다. 담당자분은 휴가 가기 전 DB 커넥션 관련하여 몇 가지 설정을 변경한 후 아래 항목들을 수정하여 테스트를 진행했지만 별다른 소득이 없는 상태였다.

 

생각의 흐름에 따른 테스트 진행

 처음든 생각은 Lambda가 종료될 때 DB 커넥션 연결이 제대로 끊어지지 않아서 발생했다고 생각했다. 그래서 'Lambda가 종료될 때 DB 커넥션을 종료시키는 로직을 추가하면 해결되지 않을까?'라고 생각했다. 그래서 Lambda의 생명주기를 찾아보았다. Lambda의 생명주기는 다음과 같이 되어 있는데 여기서 SHUTDOWN phase의 종료 시점을 인지해서 커넥션을 종료한다면 되지 않을까 생각했다.

 그래서 Lambda Shutdown이라는 키워드로 검색을 했다니 어떤 누군가 만든 Lambda Extension을 발견했다. Graceful shutdown with AWS Lambda라는 Extension이었는데 간단히 설명하자면, Lambda가 종료되기 직전 SIGTERM 신호를 보낸 뒤 SIGKILL이 일어나고 SHUTDOWN이 되는데 SIGTERM 신호를 catch 해서 개발자가 자원을 정리할 수 있게 해주는 Extension이었다. 아쉽게도 해당 글에는 Node.js, Python, Java는 있었지만 Golang은 존재하지 않았고 Golang SIGTERM 키워드로 다시 검색을 진행했다.

 

 해당 키워드로 검색을 하니 aws-lambda-go 패키지를 찾을 수 있었고 해당 문서에서 WithEnableSIGTERM 메서드를 찾을 수 있었고 해당 메서드의 예시와 해당 문서의 모드를 참고해서 테스트를 진행했다. (참고 aws-lambda-go의 sigtem 객체)


 최초에는 테스트를 위해 작성한 문구가 출력이 되어 해결했다고 생각했다. 그래서 DB 커넥션을 종료시키는 로직을 WithEnableSIGTERM 아래에 추가해서 테스트를 돌렸는데 결과는 별 차이가 없었다. 저 메서드에 적혀있는 may be provided가 마음에 걸렸다. 아마 실행이 된다는건 실행이 안될 수도 있다는 뜻이니... '이게 문제인 건가?'라는 생각이 들었다.

 

 그러다 문득 이거 뭔가 Lambda에서 연결을 비정상적으로 많이 만드는게 이상하다는 생각이 들었다. 최초의 init() 함수에서 DB 연결은 하나만 생성하는데 gorutine에서 뭔가 추가적인 연결을 만드는 건가? 하는 의심이 들어 DB 커넥션 초기화 부분과 DB 설정 부분을 살펴봤다. 그랬더니 최초에 초기화하는 DB 커넥션 이외에 다른 DB 커넥션(Raw Query용)이 반환되고 있는 것을 찾을 수 있었고 의심되는 부분에 log를 추가해 테스트를 진행했다. 그랬더니 최초의 DB 커넥션은 한 번만 초기화되지만 Raw Query용 커넥션은 매번 새롭게 생성되어 반환되는 것을 확인할 수 있었다. 해당 커넥션을 기존에 초기화된 DB 커넥션을 사용하게 수정한 뒤 테스트를 진행하니 별다른 이상이 없음을 확인할 수 있었다.

 

원인을 파악해서 sigterm은 굳이 필요없을 것 같아 코드에서 제거하고 부하 테스트를 진행했다. 부하 테스트는 담당자분이 가기 전에 만들고 간 문서를 참고해서 진행했으며 AWS 공식 문서에서 테스트시 사용한 Artillery를 사용했다. 해당 tool을 통해 부하 테스트를 진행했고 기존에 부하가 발생했던 요청 이상에도 별다른 문제없이 잘 호출되는 것을 확인할 수 있었다.

설치
npm i -D artillery

설치 완료 후 호출
artillery dino

test yml 파일 생성 (ex. test1.yml) 
config:
  target: "https://{{domain}}.com"
  phases:
    - duration: 10 # 요청 지속 시간 (sec)
      arrivalRate: 10 # 가상유저가 초당 요청을 보내는 비율 (가상유저(VU) / 초(s) 로 계산)

scenarios:
  - name: "설정1 테스트 진행"
    flow:
      - get:
          url: "/api/test"
          headers:
            User-Agent: "Artillery"
            Accept: "application/json"
      - log: "요청완료"
      
실행
artillery run ./test1.yml (해당 파일이 존재하는 폴더에서 실행)

 

결론 및 후기

 Lambda에서 DB 커넥션 증가 문제가 발생하여 간헐적으로 장애가 발생한다면 우선적으로 Lambda마다 DB 커넥션 생성을 싱글톤으로 생성해서 사용하는지 확인을 먼저 해야 한다는 생각이 들었다. 이전에도 이런 문제로 고생했는데 왜 그 생각을 못했는지... 당연히 DB 커넥션을 최초에 한 번 생성해서 공통으로 가져다 쓸 줄 알았는데 생각해 보니 그 당연한걸 나도 신입때 하지 않았었다. 에러가 발생한다면 우선적으로 코드 레벨에서부터 의심을 해야겠다고 다시 한번 생각했고, 코드레벨에서 이상이 없다면 그제야 애플리케이션 설정이나 그 외적인 요소를 고려해야 한다는 좋은 교훈을 얻을 수 있었다.

Comments