이번 포스팅은 지난 오디오 스트리밍 개발기보다 조금 더 기술 얘기입니다.
프로그래밍보다는 시스템, 인프라에 가까운 내용입니다.
개인적으로 저는 {서버|인프라|시스템|아키텍쳐|백엔드|등}으로 불리는 이 분야는
정해진 정석의 학습방법이 없다고 생각합니다. 잘 짜여진 커리큘럼도 없다고 생각하고요.
아니 모른다고 하는게 더 맞겠네요.
왜냐하면 제가 삽질에 삽질에 삽질을 더해서.. 익혀서 그렇습니다.. 하지만 분명 제대로된 커리큘럼이 있을거에요
이 얘기를 시작한 이유는, 지난 약 8월 초부터 9월 초까지 있었던 고객사 장애이슈를 해결하면서
겪은 이야기를 하기위해서 입니다.
8월 초에 갑자기 일부 사용자가 고객사 서비스에 접속이 안된다는 보고를 받았습니다.
벌써부터 어렵습니다. ‘일부’ 사용자가 안된다니.. 당연히 고객사에서도 재현이 안되고 개발 장비에서도 재현이 안되었습니다.
이슈가 시작했던 8월 초에는 대부분의 사용자는 문제없이 접속되고, 소수가 안들어가지거나 매우 오래걸리는 현상이 발생했습니다.
당연히 제일 먼저 의심한 부분은 버그입니다.
이 당시 신규 개발중인 기능들은 개발서버에 반영되고 있었고, 이 과정에서 발견된 Hotfix만 본서버에 적용되고 있었습니다.
커밋 로그를 살펴봤습니다. 8일에 Hotfix 한 내용이 있었습니다.
어떤걸 Fix했는지 알수없는 Hotfix 커밋입니다. Hotfix는 대개 버그로 인해 빠르게 적용을 하기 위해서 커밋 메시지를 짧게 쓰긴 하지만
이 메시지는 정말 한심스럽기 짝이 없습니다.
어찌되었건, 수정한 코드를 다시 리뷰했으나 관리자 페이지쪽 Hotfix만 존재하고 서비스에는 영향이 없는 수정이었습니다.
즉, 건든 게 없는데 그냥 갑자기 안되는 것이였죠. 나참
이제 웹서버 구성을 의심해봐야겠죠. LAH에서 만드는 웹 서비스들은 Vue(Nuxt) + Python(Django)로 개발이 됩니다.
Nuxt의 경우 기본 적으로 제공하는 Production Deployment 를 사용하고 있습니다. 물론 도커를 이용해서 하고 있습니다.
Django의 경우 gunicorn 을 통해서 배포하고 있습니다. 이렇게 배포된 백엔드와 프론트엔드는 nginx를 통해 서비스 됩니다.
처음에는 가장 쉬운.. 라이브러리들을 업데이트 했습니다. 아무래도 실 서비스 중이기 때문에 Major버전을 올릴수는 없고 Minor 버전을 전부 업데이트 했습니다.
당연히 해결이 안되었습니다.
그 다음으로는 gunicorn 백엔드를 의심했습니다. 백엔드에서 외부 서비스 API 호출시 SSL 인증 과정에서 시간이 오래걸리는 경우가 있는데,
이 현상이 여러 클라이언트에서 발생하면 프로세스를 할당받지 못하고 종료되는 것 같았습니다.
이러한 현상을 해결하려면 SSL 인증 과정이 오래걸릴 경우 timeout처리를 하고, gunicorn의 abort 요청과 nginx의 abort 요청을 일치시켜야 했습니다.
(사실 진작 했어야 할 작업들이죠.)
그리고 백엔드 프로세스도 늘렸습니다.
이런 조치를 하고 나면 10~30분 이슈가 잠잠하다가 다시 발생합니다.
그 말인 즉슨, 해결된 것이 아니라는 얘기입니다. 아마 10~30분 잠잠해진 이유도 웹 서버를 재시작해서 일 것으로 보입니다.
그 후 진행한 작업은 다음과 같습니다..
- Font 리소스를 직접 호스팅하지 않고 외부 CDN으로 개선
- 프론트엔드 도커 이미지를 여러개 띄우고 nginx로 LB(Load Balancing) 구성
- 프론트엔드 nuxt 서버 사용이 아닌 PM2(process manager for node.js)로 구성 (클러스터 모드로 8개 인스턴스 사용)
- PM2 대시보드를 통한 실시간 평균 응답 시간 모니터링
- nginx keepalive 설정으로 nginx<->PM2, nginx<->gunicorn 간의 통신 오버헤드 감소
- SSL 인증서 버퍼 부족 이슈 해결
- nginx 캐시 점검
- nginx 캐시 off 상태를 on 으로 구성 (캐시 파일 1GB로 구성)
- assets, static 모두 파일 캐시 활성화
진행한 작업을 돌이켜보면 안해도 괜찮지 않을까.. 싶지만 성능을 위해서라면 당연히 구성해야되는 것들입니다.
7번 작업인 캐시 점검에서 구성을 확인해보니, 예전 기억에 간혹 npm run build로 생성된 빌드 파일이 캐시를 타면서 사이트 업데이트가 제대로 되지 않던 기억때문에
캐시를 전부 비활성화 해두었었습니다. 그러다보니 모든 assets과 js와 데이터를 매번 가져오다보니 nginx <-> pm2 간의 통신이 매우 오래걸렸던 것이었죠.
(하지만 어째서인지 pm2 로그에는 응답시간이 매우 짧게 나옵니다.. T^T)
이 과정에서 nginx가 새 요청을 받지 못하고 클라이언트(크롬)에 의해 요청이 끊기는 것이었습니다.
결국 캐시 설정을 최적화하고나서는 문제가 발생하지 않았습니다.
아마 서버 구성에 대해 아는 분들이 본다면, 정말 기본이 안되있다고 생각할 수 있고 동감합니다.
그래도, 이제 기본은 되었고 성장해나가니 기쁘네요!
물론 캐시 설정까지 도달하고나서도, 쿠키마저 캐시를 태워서.. 모든 사용자가 마지막 사용자를 공유하는 정신나간 문제도 일으켰지만
정말 정말 얻은 것이 많은 기간이었습니다.
실제로 적용했던 설정들이나 우여곡절들을 기술 블로그나, 별도 채널을 통해 꼭 공유하도록 하겠습니다.
다음 포스팅에서는 기술 없는 얘기로 만나요 🙂