반응형
개요
- 마지막 글에서 측정 결과 System CPU와 Nginx CPU가 과도하게 사용되는 것을 확인할 수 있었고, 그에 대해 NGINX가 정말로 필요할까? 라는 의문을 내렸고, Disk I/O가 과도하게 발생해 이에 대한 CPU 자원이 많이 사용되는 것이 아닐까? 하는 가설을 세웠습니다.
- 그에 따라 NGINX를 제거하고, 로그 저장 방식을 바꾸어 성능 테스트를 다시 해보기로 결정했습니다.
현재 보고 계신 글은 공연 조회 API 최적화 시리즈 입니다!
1편 :공연 정보 조회 API 쿼리 분석하고 개선하기
2편 :공연 조회 API 성능 측정 및 개선 사안 찾아보기
3편 :공연 조회 API에 캐싱을 적용하고 성능 테스트하기
4편 :아키텍처 최적화, 로그 방식 변경을 통한 공연 정보 조회 API 최적화 하기
그럼 어떤 것이 NGINX를 대신할 수 있을까?
- 기존의 NGINX는 SSL Handshake를 대신해주고, Web Server로서 정적 리소스를 제공하는 역할을 했습니다. 이를 대신할 대체재를 한번 찾아보았습니다.
1. OCI LoadBalancer에 OCI Certification 사용
- 첫번째로 생각한 방식은 OCI Loadbalancer에 OCI 인증서를 설치해서, Loadbalancer에 Spring 서버를 물려서 사용하는 것입니다. 관련 글은 여기서 보실 수 있습니다.
- 근데 OCI Free Tier 로드밸런서는 네트워크 대역폭 10mps를 지원하는데요, 10mps면 어느정도의요청을 처리할 수 있을까? 한번 계산해 보았습니다.
- 현재 요청 + 응답은 약 2.4mb(gzip 미적용)
- 초당 가능한 요청-응답 수: 최대 505회(10mbps가 10Mega Byte가 아니라 Mega Bit라고 하더라고요..)
- 이미 RPS는 800을 넘겼기 때문에 Load Balancer가 병목지점으로 될 것으로 보입니다. 따라서 이방식은 채택하지 않았습니다.
2. CloudFlare 사용
- CloudFlare은 DNS에 대해 무료로 SSL 인증서를 제공해 주고, SSL 처리까지 자동으로 해준다고 합니다. 또 대역폭 제한도 없다고 하네요.
- 물론 위 방식의 아키텍쳐를 가져가는 것은 두가지의 큰 문제점이 있는데요.
- CloudFlare - 서버 간의 암호화가 이루어 지지 않기 때문에 단순한 눈속임용 HTTPS가 됩니다(관련 글)
- 원본 서버에서 80번 포트에 대한 제한을 CloudFlare로 걸 수 없기 때문에 HTTP 프로토콜 기반의 80번 포트를 전체 개방 해야합니다.
- Spring 서버는 HTTP로 요청을 받기 때문에 HSTS를 적용할 수 없어 HTTP로도 Spring 서버와 연결이 가능합니다.
- 처음에는 어차피 개인 프로젝트고 실제 운영되는 서비스도 아니니까 이 방식을 활용할까 했으나, 회원가입할 때 이메일을 수집하기 때문에 혹시나 하는 마음에 이 방식도 적용하지 않았습니다.
3. Spring 서버에 직접 HTTPS 적용
- 마지막으로 생각한 방식은 Spring 서버에 직접 HTTPS를 적용하는 방식인데요, Spring 서버에 SSL Handshake 과정이 추가되어 사용되는 자원이 많아질 수는 있겠지만, 그래도 보안을 위해 이 방식을 채택하였습니다.
- 인증서는 Let’s Encrypt로 발급을 받도록 하였습니다.
- 그리고 CloudFlare에는 CloudFlare Page라는 정적 파일 호스팅 기능도 제공하기 때문에, 이를 사용하기 위해 CloudFlare를 사용하지 않는 것 보다는, TLS 설정을 “전체”로 변경해 전체 서비스 간의 통신이 암호화 되도록 하였습니다.
RabbitMQ를 사용한 로깅 시스템 구축
- 다음으로 RabbitMQ를 사용해 로깅 시스템을 구축했습니다.
- 사실 RabbitMQ를 사용하기 전에 Spring 서버에서 RabbitMQ를 사용하지 않고 바로 Logstash로 로그를 바로 전송하는 방식도 고려하였습니다. (관련 글)
- 다만 이 방식은 Logstash가 장애가 발생한다거나 TCP 연결이 중지될 경우 로그 데이터가 유실될 수 있기 때문에, RabbitMQ가 제공하는 메시지 영속화나 백프레셔 기능을 활용하면 로그 데이터를 더 안정적으로 저장할 수 있다고 판단하여 RabbitMQ를 적용하였습니다.
(주의 사항) Docker Container의 로그 정책
- 저는 Spring 서버의 실행을 Docker로 하고 있습니다. Docker은 컨테이너 내부에서 발생하는 표준 입출력을 json파일에 저장하는데요, 이러면 마찬가지로 Disk I/O가 발생하기 때문에, 이를 방지하고자 표준 입출력의 로깅 레벨을 WARN으로 설정하여 Disk I/O를 최소화 하도록 하였습니다.
<appender name="PROD_CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
<encoder>
<pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} [%level] [%thread] [%logger{36}] - %msg%n </pattern>
</encoder>
</appender>
K6 성능 테스트 결과
- 먼저 K6 전반적인 지표인데요, 마지막 테스트(842 RPS, p95 970ms)에 비해 최대 RPS는 1483으로, p95 사용자의 응답은 450ms 정도로 RPS는 약 76%, 응답시간은 약 54% 개선이 이루어진 것을 확인할 수 있었습니다.
JVM
- 그 다음으로 CPU 매트릭과 Thread Pool에 대한 지표인데요, 시스템 CPU 사용량의 대부분이 웹 요청에 사용되는 제가 생각한 이상적인 그림이 나타난다고 보았습니다.
- 마지막으로 RabbitMQ와 RabbitMQ로 부터 수집한 로그 정보를 나타내는 그림인데요, Spring 서버가 Publish한 데이터를 Consumer이 잘 소모하여, ElasticSearch에 정상적으로 저장된 모습을 볼 수 있었습니다.
결론
- 일단 제가 생각했을때는 웹 서버에 대한 개선은 Thread Pool의 갯수를 늘리는 것 정도 말고는 더 생각나지 않습니다. 다음에 시간날 때 HTTP 설정을 변경해서(HTTP 버전 변경이나 응답 압축 등) 다시 성능 테스트를 해볼 생각 중입니다.
반응형
'Spring > Ticketing 프로젝트' 카테고리의 다른 글
공연 조회 API에 캐싱을 적용하고 성능 테스트하기 (0) | 2024.11.16 |
---|---|
공연 조회 API 성능 측정 및 개선 사안 찾아보기 (0) | 2024.11.16 |
공연 조회 API에 캐싱 적용하기 (0) | 2024.11.16 |
공연 예매 시 Proxy를 사용해 DB 부하 줄이기 (2) | 2024.11.16 |
공연 정보 조회 API 쿼리 분석하고 개선하기 (0) | 2024.11.15 |