본문 바로가기

advanced/웹 - 백엔드

[uWSGI] 메모리 개선

EC2의 메모리가 튀어서 알림을 받고 조마조마한(?) 일이 있었는데, 해결 과정을 공유합니다

 

[이슈]

2023년 6월 23일, 기존 EC2에서 사용하고 있는 15GB의 메모리 중 약 14GB를 사용해서 alert이 발생한 케이스가 있었습니다.

평소 약 10GB를 사용하고 있어서 50% 정도의 여유분이 있었는데, 메모리가 몇 시간에 걸쳐서 계단식으로 증가했습니다.

 

[최초 문제 인지]

몇 시간에 걸쳐서 계단식으로 메모리가 점프했기 때문에, 최초에는 memory leak으로 의심했습니다.

 

[해결 과정]

datadog에서 특정 메모리가 튀는 시점들을 파악했습니다(대략 오후 1시 30분, 오후 2시, 오후 2시 30분, 오후 3시, ...)

헤당 시점에 요청되었던 API들 중에서 특이한 API를 찾았습니다(response time이 너무 느린 API, 요청 수가 많은 API...).

response가 너무 느린 API를 발견했고, 그 API의 로그값을 확인하니 큰 요청들을 하루 13만번 기준으로 매우 많이 보내고 있었습니다.

같은 요청이 계속해서 들어왔기 때문에 캐싱하는 것으로 결정이되었습니다.

 

[문제 해결]

1. 특정 IP에서만 구버전의 chromeOS에서 반복적으로 하루 13만번 호출되었기 때문에 특정 사용자의 동일한 요청에 대해서 5분동안 캐싱하도록 설정했습니다.

2. 너무 요청이 많이 들어오는 경우에 차단할 필요가 있었기 때문에, 초당 호출 횟수 기준으로 요청 차단할 수 있도록 요청했습니다(aws WAF rate limit 설정)

 

[사후 이슈 분석]

uWSGI에서 메모리 릭이 있던 것은 아니었습니다. 이틀 정도 모니터링하니, 원래대로 메모리 반환이 이루어졌고, 약 10GB를 초과하지 않는 선에서 메모리가 유지되었습니다.

또한 검색해서 uWSGI의 특성을 확인해보니 하나의 request마다 메모리를 사용하게 되는데, request가 많아지면 전체 메모리를 많이 차지 할 수 밖에 없었습니다.

 

관련된 옵션으로 uWSGI의 max-requests 옵션은 지정된 request의 개수만큼 도달하면 worker를 하나 삭제 후 하나를 새로 생성합니다(새로 프로세스를 만들기 때문에, 적은 메모리 공간을 차지하게 됩니다).

적절한 max-requests 값 선택으로 메모리 공간 차지 vs 빠른 요청 처리 사이의 trade-off를 할 수 있습니다. max-requests가 극단적으로 1이면 매번 요청마다 새로 process를 만들기 때문에 요청이 느려지고, max-requests값이 매우 높다면 기존의 프로세스를 비우지 않기 때문에 메모리를 많이 차지하게 됩니다.

 

캐싱으로 인해서 메모리가 더이상 증가하지 않았기 때문에, 적절한 조치로 볼 수 있고, aws WAF 설정으로 인해서 동일한 요청이 많이 들어오면 차단될 것입니다.

max-requests는 어차피 요청이 단순하게 많은 상황이라면 통하지 않을 방법입니다(어차피 process 개수가 늘어나서 메모리를 더 차지할 수 밖에 없음).

 

reference.

https://blog.gingerlime.com/2011/django-memory-leaks-part-i/comment-page-1/#comment-59726