Nginx의 캐시가 누락되었습니다.

Nginx의 캐시가 누락되었습니다.

Yuki Network Mirror에서 Nginx에 캐시를 적용하는 과정에서 간헐적으로 몇몇 파일이 빈 값으로 응답된 문제를 다룹니다.


왜 캐시를 적용하나요?

원본 서버의 대역폭을 확보하기 위함입니다. 사용자가 늘어나면서 점차 트래픽이 증가하였고, 이에 따라 원본 서버의 대역폭이 부족하게 되었습니다. 이러한 상황에서는 서비스를 유지하기 어려울 것으로 판단하여, 원본 서버의 대역폭 확보가 문제 해결의 핵심이라고 판단하였습니다.

캐시 서버 도입

저는 캐시 서버를 도입하여 이 문제를 해결하였습니다. 원본 서버보다 더 높은 대역폭을 가진 캐시 서버를 만들고, 이를 원본 서버의 앞 단에 배치하였습니다. 그 결과 캐시 서버에서 많은 트래픽을 견뎌내, 원본 서버로 향하는 트래픽이 줄게 되었습니다. 캐시 서버를 사용하는 전략이 옳았던 것입니다.

처음 캐시 서버를 구축했을 당시에는 아무런 문제가 없었습니다. 하지만 로그를 분석하던 중 캐시 서버에 HEAD로 요청할 경우 원본 서버에서 GET으로 응답하여 불필요한 트래픽이 사용되는 것을 알게 되었고, 이를 해결하기 위해 다음과 같은 코드를 추가하여 캐시 서버에 HEAD로 요청할 경우 원본 서버에서 HEAD로 응답하도록 하였습니다.
proxy_cache_convert_head off;

적용 당시에는 몰랐지만, 위 변경사항이 추후에 알게 된 캐시 누락의 원인이 되었습니다.

캐시 누락 문제와 해결 과정

캐시 서버를 구축하고 대략 2주 후에 몇몇 파일의 응답이 빈 값으로 응답되는 문제를 알게 되었습니다. 문제 해결을 위해 캐시를 모두 삭제하는 작업을 진행하였고 정상적으로 파일이 응답되는 것처럼 보였습니다. 하지만 몇 시간 뒤 해당 문제가 또다시 발생하였고, 문제 파악을 위해 직접 캐시 파일을 조회하게 되었습니다.

Nginx 캐시 조회

Nginx의 기본 캐시 키는 $scheme$proxy_host$request_uri 이와 같은 구조로 이루어져 있으며, 이를 MD5 해시화하여 캐시를 저장하고 있습니다. 이를 통해 특정 URL에 캐시 파일을 조회할 수 있습니다.

캐시 파일을 조회하는 과정

문제 파악

빈 값으로 응답이 오는 캐시 파일을 조회해 보니 Body 값이 비어있는 것을 발견하게 되었습니다. 캐시 파일에 남겨진 시간을 통해 원본 서버에서 해당 시간 로그를 조회하게 되었고 원본 서버의 HEAD 응답을 캐싱 한 것을 알게 되었습니다.
proxy_cache_convert_head off;를 적용하기 이전에는 원본 서버의 GET 응답을 캐싱하여 문제가 발생하지 않았지만, 해당 코드 적용 이후 HEAD 응답을 캐싱하면서 GET 요청에도 동일 키에 있는 Body가 비어있는 HEAD 응답을 보낸 것이었습니다.

해결 방법

해당 문제를 하기 위해 캐시 키를 수정하였습니다. 기존 캐시 키에 Method 항목을 추가하여 GET과 HEAD 응답을 각각 아래와 같이 다른 키로 저장되어 응답하도록 한 것입니다. 해당 방법은 성공하였고 캐시 누락 문제가 해결되었습니다.
$request_method | $proxy_host$request_uri

마무리하며

실제 당시 해결 과정에서는 문제가 발생한 이유를 파악하지 못해 많은 시간이 소요되었었습니다. 사실 해당 문제는 Nginx 공식 문서를 읽었다면 바로 해결되었을 문제였습니다. 문서상에서 문제가 된 코드를 사용할 경우 캐시 키를 수정할 것을 명시하고 있었습니다.

Enables or disables the conversion of the “HEAD” method to “GET” for caching. When the conversion is disabled, the cache key should be configured to include the $request_method.
Nginx Docs

이러한 문제 해결 과정을 통해 문서 확인의 중요성을 다시 깨닫게 되었습니다. 문서를 읽음으로써 이러한 문제를 예방하고 불필요한 시간 소요를 줄일 수 있기 때문입니다. 읽어주셔서 감사합니다.