[Nginx] 04. 대규모 확장 가능한 콘텐츠 캐싱
카테고리: NGINX
태그: nginx
01. 서론
캐싱은 한 번 처리한 요청에 대한 응답을 저장해두었다가 다시 사용할 수 있게 해주는 기능이다. 이로 인해 서버에 불필요한 부담을 줄이고, 콘텐츠를 더 빠르게 제공할 수 있게 되었다.
특히, 캐싱 서버를 사용자와 가까운 위치에 배치하면 속도가 크게 개선되어 사용자 경험이 좋아지고, NGINX를 사용하면 원하는 위치에 캐싱 서버를 설치해 자체 CDN처럼 운영할 수 있으며, 상위 서버에 문제가 생겼을 때도 저장된 캐시로 서비스를 계속 제공할 수 있다. (단, 캐싱 기능은 http 컨텍스트 안에서만 사용할 수 있어요.)
02. 캐시 영역
캐시를 저장할 위치와 방법을 설정하려면 NGINX에서 proxy_cache_path라는 지시어를 사용한다.
이 지시어로 캐시 데이터를 저장할 디렉토리와 메모리 공간을 정의하고, 파일 구조와 캐시 보관 시간, 최대 크기 등을 설정할 수 있습니다. 캐시는 해시된 캐시 키를 기반으로 저장되며, 오랫동안 사용되지 않은 캐시는 자동으로 삭제됩니다.
# proxy_cache_path 지시어를 사용하여 공유 메모리 캐시 존과 캐시 콘텐츠의 저장 위치를 정의함
proxy_cache_path /var/nginx/cache
# 60MB 메모리 크기의 CACHE라는 이름의 공유 메모리 공간.
keys_zone=CACHE:60m
# 파일 구조를 정의하는 캐시 디렉터리 수준.
levels=1:2
# 3시간 동안 요청되지 않은 캐시된 응답을 제거.
inactive=3h
# 캐시의 최대 크기 20GB.
max_size=20g;
# proxy_cache 지시어는 특정 컨텍스트에서 이 캐시 영역을 사용하도록 지정
proxy_cache CACHE;
캐싱을 설정하려면 캐시 경로와 영역을 선언해야 한다.
1) proxy_cache_path는 캐시 정보를 저장할 디렉터리와 응답 메타데이터를 저장할 공유 메모리 공간을 정의한다.
2) 여러 옵션 매개변수를 사용하여 캐시 관리 및 접근 방식을 제어할 수 있다.
3) levels는 파일 구조를 정의하고, inactive는 캐시 항목이 마지막으로 사용된 후 유지되는 시간을 제어하며, max_size는 캐시의 크기를 제한한다.
03. 캐시 락
캐시 락은 캐시된 리소스에 대한 동시 접근을 관리하여 여러 요청이 동시에 동일한 리소스를 캐시하려고 할 때 발생할 수 있는 문제를 방지하는 기술이다.
■ 작동 방식
1) 클라이언트가 요청한 리소스가 캐시에 없을 경우, Nginx는 백엔드 서버에 해당 리소스를 요청한다.
2) 첫 번째 요청이 백엔드 서버에 요청을 보내는 동안, 다른 요청들은 같은 리소스에 대해 대기하게 된다. 이때 Nginx는 해당 리소스에 대한 “락(lock)”을 설정한다.
3) 첫 번째 요청이 완료되면, Nginx는 백엔드 서버로부터 받은 데이터를 캐시에 저장하고, 대기 중인 다른 요청들을 처리한다.
4) 대기 중인 요청들은 캐시된 데이터를 사용하여 클라이언트에게 응답한다.
# proxy_cache_lock 지시어를 사용하여 한 번에 하나의 요청만 캐시에 쓸 수 있도록 하고, 후속 요청은 응답이 기록될 때까지 대기한다.
# 캐시 쓰기 중인 요청이 있는 동안 다른 요청은 대기.
proxy_cache_lock on;
# 캐시 항목을 쓰고 있는 요청이 최대 10초 동안 캐시 쓰기를 독점할 수 있음.
proxy_cache_lock_age 10s;
# 3초 동안 대기한 후, 요청이 업스트림 서버로 전송되어도 캐시에 기록되지 않도록 설정.
proxy_cache_lock_timeout 3s;
proxy_cache_lock 지시어는 캐시에 쓰기 작업 중인 요청에 대해 나머지 요청을 대기시키도록 NGINX에 지시한다.
proxy_cache_lock_age는 다른 요청이 캐시 쓰기를 시도하기 전까지 요청이 캐시를 점유할 수 있는 최대 시간을 정의하며, 기본값은 5초이다.
proxy_cache_lock_timeout은 대기 시간이 길어질 경우 해당 요청을 업스트림 서버로 전송하되, 캐시에 기록하지 않고 응답을 직접 가져오도록 설정하는 지시어이다.
04. 해시 키 값 캐시
해시 키 값 캐시는 캐시된 콘텐츠를 식별하고 저장할 때 사용하는 해시 키의 형식과 방식을 정의하는 기술이다. 이를 통해 요청 URL, 헤더, 쿼리 매개변수 등을 기준으로 캐시를 효율적으로 관리할 수 있으며, 각 요청에 맞는 고유한 캐시 항목을 생성하여 사용자 맞춤형 콘텐츠 제공이 가능하다.
■ 작동 방식
1) 클라이언트가 요청한 리소스가 캐시에 없을 경우, NGINX는 백엔드 서버에 리소스를 요청한다.
2) 첫 번째 요청이 백엔드 서버에 요청을 보내는 동안, 같은 리소스에 대한 다른 요청들은 대기하게 됩니다. 이때 NGINX는 해당 리소스에 “락(lock)”을 설정해 중복 요청을 막는다.
3) 첫 번째 요청이 완료되면, NGINX는 백엔드 서버에서 받은 데이터를 캐시에 저장하고, 대기 중이던 다른 요청들도 캐시된 데이터를 사용해 응답을 처리한다.
4) 대기 중이던 요청들은 캐시된 데이터를 기반으로 클라이언트에 응답한다.
# proxy_cache_key 지시어와 변수를 사용해 캐시 히트 또는 미스를 결정하는 캐시 키를 정의한다.
proxy_cache_key "$host$request_uri $cookie_user";
http {
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m;
server {
location / {
proxy_cache my_cache;
proxy_cache_key "$scheme$request_method$host$request_uri$args";
...
}
}
}
이 캐시 해시 키는 NGINX가 요청된 호스트와 URI뿐만 아니라 사용자를 식별하는 쿠키를 기준으로 페이지를 캐시하도록 지시한다. 이를 통해 특정 사용자에게 맞는 동적 페이지가 다른 사용자에게 잘못 제공되는 일을 방지할 수 있다.
기본 proxy_cache_key는 대부분의 경우 적합하며, 기본 형식은 “$scheme$proxy_host$request_uri”이다. 여기에는 프로토콜(HTTP 또는 HTTPS), 프록시 호스트, 요청된 URI가 포함된다. 그러나 애플리케이션마다 고유한 요청을 정의하는 요소들(예: 요청 매개변수, 헤더, 세션 식별자 등)이 있을 수 있으므로, 필요에 따라 해시 키를 사용자 정의해야 할 때도 있다.
해시 키를 선택할 때는 애플리케이션의 동작을 잘 이해하고 신중히 설정하는 것이 중요하다. 정적 콘텐츠의 경우 호스트 이름과 URI만으로 충분하지만, 대시보드와 같은 동적 페이지는 사용자 경험 차이를 고려하여 더 정교한 해시 키가 필요할 수 있다. 특히 보안 문제로, 사용자 간에 캐시된 데이터를 잘못 제공하지 않도록 주의해야 합니다.
05. 캐시 우회 (Cache Bypass)
proxy_cache_bypass 지시어는 특정 조건에서 캐시를 우회하고, 서버에서 최신 데이터를 직접 받아오도록 설정하는 기능이다. 즉, 캐시된 데이터를 사용하지 않고 서버로부터 최신 정보를 가져올 수 있게 해준다.
이 지시어는 HTTP 요청 헤더 값을 활용하여 동적으로 캐시 우회를 제어할 수 있다. 주로 문제 해결, 디버깅, 또는 사용자 맞춤형 데이터 제공 등에서 캐시된 페이지 대신 최신 데이터를 가져와야 할 때 유용하다.
이 설정을 통해 특정 쿠키, 헤더, 요청 파라미터에 따라 캐시를 우회할 수 있으며, 필요에 따라 캐시 기능을 완전히 비활성화할 수도 있다. (proxy_cache off;)
■ 왜 캐시 우회가 필요한가?
캐시된 페이지를 계속 사용하면 최신 데이터를 확인하기 어렵고, 문제를 재현하거나 디버깅하는 데 어려움이 생길 수 있다. 또한, 사용자별 맞춤형 데이터나 특정 상황에 맞는 최신 정보를 제공해야 할 때도 캐시를 우회하는 것이 필요하다.
# HTTP 요청 헤더 'cache_bypass' 값이 0이 아닌 경우 NGINX가 캐시를 우회하도록 설정합니다.
# 클라이언트가 이 헤더를 명시적으로 요청에 추가해야 캐시를 우회할 수 있습니다.
proxy_cache_bypass $http_cache_bypass;
06. 캐시 성능 (Cache Performance)
웹사이트 성능을 높이기 위해 클라이언트 측에서 파일을 캐싱하는 방법
사용자가 웹사이트를 방문할 때, 이미 캐시된 리소스가 있다면 서버에 다시 요청하지 않고 자신의 기기에서 바로 불러올 수 있다. 이렇게 하면 서버 부하를 줄이고, 사용자에게 더 빠른 응답 속도를 제공할 수 있다. 특히, 이미지, CSS, JavaScript 같은 정적 파일은 자주 변경되지 않기 때문에 캐시하면 웹사이트 성능을 크게 개선할 수 있다.
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public";
}
expires 지시어는 클라이언트에게 해당 리소스가 1년 동안 유효하다고 알려준다. 그 이후에는 다시 서버에서 새로운 버전을 요청하도록 설정된다.
add_header 지시어는 HTTP 응답에 Cache-Control 헤더를 추가하여, 이 리소스가 모든 캐싱 서버(예: 프록시 서버)에서도 캐시될 수 있도록 허용한다. 만약 private 옵션을 사용하면, 해당 리소스는 클라이언트에서만 캐시되며 다른 서버에는 저장되지 않는다.
07. 캐시 퍼지 (Purging) - NGINX Plus
웹사이트에 캐시된 콘텐츠가 있을 때, 해당 콘텐츠를 일부 변경했다고 가정해 보자. 새로 업데이트된 콘텐츠가 사용자에게 제공되기를 원하지만, 여전히 캐시된 이전 버전이 사용되고 있을 수 있다. 이럴 때 캐시를 삭제하는 것은 이전 버전의 콘텐츠를 ‘잊어버리라’고 지시하여 새 버전을 제공할 수 있도록 하는 것과 같다.
NGINX Plus는 proxy_cache_purge 지시문을 사용하여 이러한 작업을 수행하는 방법을 제공한다. 특정 URL에 PURGE 요청이 들어오면 캐시가 삭제되고, 그 대신 새 버전의 콘텐츠가 제공된다.
map $request_method $purge_method {
PURGE 1;
default 0;
}
server {
# ...
location / {
# ...
proxy_cache_purge $purge_method;
}
}
여기서 map 지시어를 사용하여 요청 방법이 PURGE일 경우에만 특정 변수를 1로 설정하고, 기본적으로는 0으로 설정한다. 이를 통해 PURGE 요청에 대해서만 캐시가 삭제될 수 있다.
# main.js 파일의 캐시를 삭제하는 curl 명령어는 다음과 같다.
$ curl -XPURGE localhost/main.js
NGINX Plus를 사용하면 특정 리소스의 캐시를 쉽게 삭제할 수 있어, 사용자가 항상 최신 콘텐츠를 받을 수 있도록 보장한다. 또한, 캐시 삭제 권한을 설정하여 승인된 사용자만이 캐시를 삭제할 수 있도록 관리할 수 있다.
08. 캐시 분할 (Slicing)
캐시 슬라이싱 기능은 큰 파일을 작은 조각으로 나누어 캐시하는 방법이다. 이 모듈은 HTML5 비디오 전송을 위해 개발되었으며, 바이트 범위 요청을 통해 콘텐츠를 브라우저로 전송한다.
전체 파일을 캐싱하는 대신, NGINX의 캐시 슬라이스 모듈을 사용하면 파일을 “슬라이스”라는 작은 조각으로 분해할 수 있다. 각 슬라이스는 개별적으로 캐시되므로, 사용자가 동영상의 특정 부분을 요청할 때 NGINX는 캐시에서 해당 슬라이스를 빠르게 서비스할 수 있다. 이렇게 하면 대용량 파일을 더 빠르고 효율적으로 제공할 수 있다.
proxy_cache_path /tmp/mycache keys_zone=mycache:10m;
server {
# ...
proxy_cache mycache;
slice 1m;
proxy_cache_key $host$uri$is_args$args$slice_range;
proxy_set_header Range $slice_range;
proxy_http_version 1.1;
proxy_cache_valid 200 206 1h;
location / {
proxy_pass http://origin:80;
}
}
slice 지시어를 사용하면 NGINX가 응답을 지정된 크기(예: 1MB)로 나누어 저장한다. 이때 사용되는 $slice_range 변수를 통해 각 조각을 식별하고, HTTP 요청 시 Range 헤더로 전달한다.
이를 통해 클라이언트는 필요한 조각만 요청할 수 있고, NGINX는 원본 서버에서 필요한 조각만 가져와 캐시한다.
캐시 슬라이싱은 대용량 파일을 효율적으로 관리하는 데 유용하다. 예를 들어, 비디오 파일이나 큰 이미지 파일을 스트리밍할 때 전체 파일을 한 번에 다운로드하는 대신 필요한 부분만 요청할 수 있다.
또한, NGINX가 필요한 조각만 원본 서버에서 요청하고 캐시함으로써 서버의 부하를 줄이고, 네트워크 대역폭을 절약할 수 있다. 다만, 변경되지 않는 큰 파일에 대해서만 사용해야 하며, 파일이 자주 변경되거나 크기가 작은 경우에는 다른 캐시 관리 방법을 사용하는 것이 더 효과적이다.
댓글 남기기