서버 병목 현상 추적기 - jinkshower/galmanhae GitHub Wiki
성능테스트에서 발견된 패턴
(이미지들은 vUser50에서의 결과 수치임)
- 부하량, 데이터량에 관계 없이 2,3분 가량 요청이 지속될 시 응답시간 p99가 2,30초씩 치솟는 패턴이 동일하게 발견됨(vUser 50, 100, 200,300. 모든 장소 목록, 단일 장소 조회 모든 조합에서) 모두 비슷한 현상이 발생함.
cpu사용량은 응답시간이 치솟는 주기마다 떨어지고 DB 커넥션 타임아웃은 발생하지 않았음. 혹시 몰라 DB의 슬로우 쿼리 로그를 살펴봤지만 깨끗했음. 서버 인스턴스의 메모리량도 거의 변화가 없었음
아무래도 DB 커넥션 때문인 것 같았지만 커넥션 타임아웃 관련 로그, 메트릭이 발생하지 않았기에 다른 메트릭이 추가로 필요하다고 판단되었음.
로컬에서 성능테스트를 실행했을 시 해당 현상이 전혀 발생하지 않음. (응답시간은 당연히 차이가 나겠지만 일정한 패턴이 유지되는지 보기 위해서였음)
이에 따라 클라우드 서버 환경에서의 네트워크 문제라는 결론을 내리게 되고 tcpdump를 wireshark로 분석하게 됨
네트워크 분석
dup ack, retransmission이 반복적으로 발생하는 스트림을 분석. 도커 내부 컨테이너가 가상 네트워크의 포트로 db서버와 직접 통신을 하고 있음. 가상 네트워크 내부에서 외부 IP로 나가는 과정에서 패킷 손실과 NAT 테이블 충돌이 일어남을 확인. docker 공식문서는 네트워크 격리에 관련하여 default인 bridge를 사용할 경우 에러가 발생할 수 있음을 경고하고 있음.
해결
현재 docker로 띄운 것은 애플리케이션 밖에 없고 애플리케이션이 다른 docker 컨테이너끼리의 격리된 네트워크 환경이 필요하지 않은 상태이기 때문에 호스트 서버의 네트워크 인터페이스를 바로 사용하는 docker host network 환경을 선택함.
(응답시간 p99가 1초미만으로 들어온 것을 확인할 수 있음)
HTTP2 적용
Host 네트워크로 도커 네트워크 환경을 바꾼 후 800ms까지 p99가 증가하는 현상을 줄이기 위해 네트워크 패킷을 분석했음. 패킷 전송 중 이전 패킷 전송이후 걸린 시간을 추적하는 컬럼(delta)을 추가함
Nginx에서 443 포트로 https요청을 처리하고 리버스 프록시로 스프링 애플리케이션에 요청을 넘겨주는 방식을 사용하고 있는 상태임. TLS 프로토콜로 패킷을 주고 받을 때 이전 전송에 비해 1초 가량이 걸리는 것을 확인. 클라이언트-nginx간 메시지를 암호화 하는 과정이 느리다는 것을 알 수 있음.
이에 따라 http 메세지의 데이터량을 줄이는 방법을 찾게 되었고 nginx의 443포트의 http 버전을 헤더를 자동으로 압축하는 http2로 변경
(응답시간 p99가 300ms 대를 유지하는 것을 확인할 수 있음)