서버에서 DB 커넥션 풀 설정 정리
현재 웹 서비스 및 별도 서비스 제공 목적으로 총 세개의 백엔드 프레임워크를 사용하고 있다.
- FastAPI
- Spring Boot
- Node.js Express
대학생때 개발했던 토이 프로젝트들은 어차피 실제 서비스하는 프로그램들이 아니기도 했고, 성능 최적화에 큰 의미가 없었기에 기능 개발하기에만 바빴는데 실무에서는 성능 최적화 및 보안 점검 부분들에 대해 고려해야하는 부분들이 많이 있다.
현재 개발하면서 많이 신경쓰고 있는 부분들을 나열해보면
- Fiddler Classic 트래픽 캡쳐를 통한 Injection 시도 및 패킷 Parameter 변조
- Http Watch를 사용하여 API 호출에 몇초의 시간이 걸리는지 점검
- 클러스터 내 백엔드 서버 파드에 연결되어있는 PVC를 통한 로그 모니터링
- 클러스터 내 백엔드 서버 CPU 및 메모리 리소스 모니터링
단순한 데이터 인터페이스하는 API 개발일지 언정 해외에서도 이용하는 대규모 서비스다 보니 신경써야 할 부분이 많이 있다.
※ 서비스 지연이나 서비스 중단은 운영팀에게 치명적이다..
최근에 DB 서버에 Max Pool Connection 관련하여 과부하가 걸려 서비스 제공에 문제가 되는 일이 발생하였다.
원인을 역추적한 결과..
Node.js Express DB 설정쪽에 Connection Pool이 너무 과도하게 많이 잡혀있었어서 발생하였고
아래와 같이 여러 문서들을 보면서 Connection Pool을 최적화를 진행하였었다.
여러 문서들을 찾아보았을 때 거의 공식이다 싶이 다들 추천하는 Connection Pool이 있었는데,
pool_connection = ((cpu_core_count * 2) + effiective_spindle_count)
대부분이 이 공식을 추천하였고, 나 또한 이 공식을 참조하여 적용을 했었었다.
당시 개발 서버의 CPU 코어수는 6개 였기 때문에 6 * 2 + 1 해서 13인데 그래도 여유롭게 20으로 설정했었다.
공식대로 적용했기 때문에 당연히 문제가 없을것이라 생각하고 개발 서버에 배포를 진행하였는데..
※ 운영 서버까지 안가고 개발 서버에서 중단된 것이 참 다행이라고 생각한다. 개발 서버도 많은 임직원분들이 테스트하는데 사용하시기에 해당 문제를 운영서버 반영 전 미리 찾을 수 있었다.
이번엔 반대로 백엔드 개발 서버가 oom(Out Of Memory)가 발생하여 서비스가 중단되었다.
처음 연락을 받았을때, Nginx에 설정되어있는 데이터 I/O Body Size에 의해 서비스 중단된 것으로 추측된다고 연락을 받았었다.
하지만 확인 결과, 서비스 중단되어 내려갔던 파드의 PV에 적재되어있는 마지막 로그를 확인해보니 실제 발생 원인은 Out Of Memory가 원인이였다.
Connection Pool을 줄이게 되면 동시에 DB I/O를 처리할 수 있는 수가 줄어들고, 나머지 큐들은 Memory에 대기큐로 존재한다는 것인데
이 또한 대기큐에 걸려 순차적으로 처리할 수 있도록 의도적으로 설계하여 Connection Pool을 최적화한 것이였는데
대기큐에 걸려있는 데이터의 양이 아무래도 국내 및 해외 모든 공장의 시스템 데이터를 관리하는 데이터이고 사이즈가 다들 크기 때문에 메모리를 많이 사용하여 결론적으로 Out Of Memory를 발생시켰다.
물론 이 상황에서 Connection Pool을 최적화한 공식으로 냅두고 서버 메모리를 증설하는 것도 정답일 수도 있다.
하지만 클러스터의 리소스를 생각하지 않고 맘대로 무한정으로 서버 메모리를 증설시킬 수 없고
또한 그렇다고 Connection Pool을 최적화 한 후 타임 아웃 시간을 늘려서 대기큐에 두어 서비스를 느리게 제공할 수 없기 때문에
메모리를 운영서버와 동일하게 개발서버 또한 2Gi에서 6Gi로 늘리고 Connection Pool을 조금 더 여유롭게 두기로 결정하였다.
공식은 다음과 같이 적용하였다.
connection_pool = ((cpu_core_count * 2) + current_user_count + effective_spindle_count)
단순 Small size 데이터 I/O라면 커넥션 풀 최적화한 공식이 맞겠지만, I/O하는 데이터 사이즈가 매우 크다보니
분산 스토리지나 스트리밍을 구현하기 어려운 환경이라면 다음과 같이 적용하는게 좋지 않을까 싶다.
물론 정답이라고 할 수 없고, 이와 같이 적용하여서 다시 해당 문제가 개발서버에 발생하지 않도록 해결하였다.
DB 커넥션 풀에 대해서 진지하게 고려해본적이 없었고, 매번 그저 여유있게 설정해두기 마련이었는데
최근 이와 관련하여 여러 이슈가 발생했다 보니 개발할 때 사소한 것이라도 신경써서 진행해야 한다는 생각이 종종 들게 되는 것 같다.
