우리 모두는 애플리케이션과 웹사이트의 성능이 성공에 중요한 요소라는 사실을 알고 있습니다. 하지만 애플리케이션이나 웹사이트의 성능을 개선하는 과정이 항상 명확한 것은 아닙니다. 물론 코드 품질과 인프라는 중요하지만, 많은 경우 매우 기본적인 애플리케이션 제공 기술에 집중함으로써 애플리케이션의 최종 사용자 경험을 크게 개선할 수 있습니다. 그러한 예 중 하나는 애플리케이션 스택에 캐싱을 구현하고 최적화하는 것입니다. 이 블로그 게시물에서는 초보자와 고급 사용자 모두가 NGINX와 NGINX Plus에 포함된 콘텐츠 캐시 기능을 활용하여 더 나은 성능을 얻을 수 있도록 돕는 기술을 다룹니다.
콘텐츠 캐시는 클라이언트와 "원본 서버" 사이에 위치하며, 보는 모든 콘텐츠의 사본을 저장합니다. 클라이언트가 캐시에 저장된 콘텐츠를 요청하면 원본 서버에 연결하지 않고 콘텐츠를 직접 반환합니다. 콘텐츠 캐시가 클라이언트에 더 가깝기 때문에 성능이 향상되고, 매번 페이지를 처음부터 생성하는 작업을 할 필요가 없기 때문에 애플리케이션 서버를 더 효율적으로 사용할 수 있습니다.
웹 브라우저와 애플리케이션 서버 사이에는 잠재적으로 여러 개의 캐시가 있을 수 있습니다. 클라이언트의 브라우저 캐시, 중간 캐시, 콘텐츠 전송 네트워크(CDN), 애플리케이션 서버 앞에 있는 로드 밸런서 또는 역방향 프록시입니다. 캐싱은 역방향 프록시/로드 밸런서 수준에서도 성능을 크게 향상시킬 수 있습니다.
예를 들어, 작년에 저는 느리게 로딩되는 웹사이트의 성능을 조정하는 작업을 맡았습니다. 제가 가장 먼저 눈치챈 점 중 하나는 메인 홈페이지를 생성하는 데 1초 이상 걸렸다는 것입니다. 디버깅을 한 후, 해당 페이지가 캐시할 수 없다고 표시되었기 때문에 각 요청에 따라 동적으로 생성된다는 것을 발견했습니다. 페이지 자체는 그다지 자주 바뀌지 않았고 개인화되지도 않았으므로 이 작업은 필요하지 않았습니다. 실험적으로, 로드 밸런서가 홈페이지를 5초 동안 캐시하도록 표시했고, 그렇게 했을 뿐인데도 눈에 띄게 개선되었습니다. 첫 번째 바이트까지 걸리는 시간이 수 밀리초로 줄어들었고 페이지 로드 속도가 눈에 띄게 빨라졌습니다.
NGINX는 일반적으로 애플리케이션 스택에서 역방향 프록시 또는 로드 밸런서로 배포되며 모든 캐싱 기능을 갖추고 있습니다. 다음 섹션에서는 NGINX로 기본 캐싱을 구성하는 방법에 대해 설명합니다.
기본 캐싱을 활성화하려면 proxy_cache_path
및 proxy_cache 라는
두 가지 지시문만 필요합니다. proxy_cache_path
지시어는 캐시의 경로와 구성을 설정하고, proxy_cache
지시어는 이를 활성화합니다.
proxy_cache_path /캐시 경로 레벨=1:2 키 영역=my_cache:10m 최대 크기=10g 비활성=60m use_temp_path=off; 서버 { # ... 위치 / { proxy_cache my_cache; proxy_pass http://my_upstream; } }
proxy_cache_path
지시문의 매개변수는 다음 설정을 정의합니다.
levels는
/path/to/cache/ 아래에 2단계 디렉토리 계층을 설정합니다. 단일 디렉토리에 많은 수의 파일이 있으면 파일 액세스 속도가 느려질 수 있으므로 대부분의 배포에는 2단계 디렉토리 계층을 권장합니다. 레벨
매개변수가 포함되지 않으면 NGINX는 모든 파일을 같은 디렉토리에 넣습니다.keys_zone은
캐시 키와 사용 타이머와 같은 메타데이터를 저장하기 위한 공유 메모리 영역을 설정합니다. 키 사본을 메모리에 저장하면 NGINX가 디스크에 접근하지 않고도 요청이 HIT
인지 MISS
인지 빠르게 판단할 수 있어 검사 속도가 대폭 향상됩니다. 1MB 영역은 약 8,000개 키의 데이터를 저장할 수 있으므로 예시에서 구성된 10MB 영역은 약 80,000개 키의 데이터를 저장할 수 있습니다.max_size는
캐시 크기의 상한을 설정합니다(이 예에서는 10기가바이트). 선택 사항입니다. 값을 지정하지 않으면 캐시가 사용 가능한 디스크 공간을 모두 차지하도록 확장됩니다. 캐시 크기가 한계에 도달하면 캐시 관리자 라는 프로세스가 가장 최근에 사용되지 않은 파일을 제거하여 캐시 크기를 한계 이하로 줄입니다.inactive는
항목이 액세스되지 않고도 캐시에 남아 있을 수 있는 기간을 지정합니다. 이 예에서 60분 동안 요청되지 않은 파일은 만료 여부에 관계없이 캐시 관리자 프로세스에 의해 캐시에서 자동으로 삭제됩니다. 기본값은 10분( 10m
)입니다. 비활성 콘텐츠는 만료된 콘텐츠와 다릅니다. NGINX는 캐시 제어 헤더 (예: Cache-Control:max-age=120
)에 정의된 대로 만료된 콘텐츠를 자동으로 삭제하지 않습니다. 만료된(오래된) 콘텐츠는 inactive
로 지정된 시간 동안 액세스되지 않은 경우에만 삭제됩니다. 만료된 콘텐츠에 액세스하면 NGINX는 원본 서버에서 해당 콘텐츠를 새로 고치고 비활성
타이머를 재설정합니다.use_temp_path=off
지시어는 NGINX에게 캐시될 디렉토리에 해당 파일을 쓰도록 지시합니다. 파일 시스템 간에 불필요한 데이터 복사를 방지하려면 이 매개변수를 off
로 설정하는 것이 좋습니다. use_temp_path는
NGINX 버전 1.7.10 및 NGINX Plus R6 에서 도입되었습니다.마지막으로, proxy_cache
지시문은 부모 위치
블록의 URL(이 예에서는 / )과 일치하는 모든 콘텐츠의 캐싱을 활성화합니다. 서버
블록에 proxy_cache
지시문을 포함할 수도 있습니다. 이 지시문은 자체 proxy_cache
지시문이 없는 서버의 모든 위치
블록에 적용됩니다.
NGINX 콘텐츠 캐싱 의 강력한 기능 중 하나는 NGINX가 원본 서버에서 최신 콘텐츠를 가져올 수 없을 때 캐시에서 오래된 콘텐츠를 전달하도록 구성할 수 있다는 것입니다. 이는 캐시된 리소스의 모든 원본 서버가 다운되었거나 일시적으로 바쁜 경우 발생할 수 있습니다. NGINX는 오류를 클라이언트에 전달하는 대신, 캐시에서 오래된 버전의 파일을 전달합니다. 이를 통해 NGINX가 프록싱하는 서버에 대한 추가적인 수준의 장애 내구성이 제공되며, 서버 장애나 트래픽 급증 시에도 가동 시간이 보장됩니다. 이 기능을 사용하려면 proxy_cache_use_stale
지시문을 포함하세요.
위치 / { # ... proxy_cache_use_stale 오류 시간 초과 http_500 http_502 http_503 http_504; }
이 샘플 구성에서는 NGINX가 원본 서버에서 오류
, 시간 초과
또는 지정된 5xx
오류를 수신하고 캐시에 요청된 파일의 오래된 버전이 있는 경우, 오류를 클라이언트에 전달하는 대신 오래된 파일을 전달합니다.
NGINX에는 캐시 성능을 미세 조정할 수 있는 다양한 옵션 설정이 있습니다. 다음은 그 중 몇 가지를 활성화하는 예입니다.
proxy_cache_path /캐시 경로/레벨=1:2 키_존=my_cache:10m 최대_크기=10g 비활성=60m use_temp_path=off; 서버 { # ... 위치 / { proxy_cache my_cache; proxy_cache_revalidate 켜짐; proxy_cache_min_uses 3; proxy_cache_use_stale 오류 http_500 http_502 http_503 http_504 업데이트 시간 초과; proxy_cache_background_update 켜짐; proxy_cache_lock 켜짐; proxy_pass http://my_upstream; } }
이러한 지침은 다음과 같은 동작을 구성합니다.
proxy_cache_revalidate는
NGINX에 원본 서버에서 콘텐츠를 새로 고칠 때 조건부 GET
요청을 사용하도록 지시합니다. 클라이언트가 캐시 제어 헤더에서 정의한 대로 캐시되었지만 만료된 항목을 요청하는 경우, NGINX는 원본 서버로 전송하는 GET
요청의 헤더에 If-Modified-Since
필드를 포함합니다. 이렇게 하면 대역폭을 절약할 수 있습니다. 왜냐하면 서버는 NGINX가 원래 파일을 캐시할 때 첨부된 Last-Modified
헤더에 기록된 시간 이후에 수정된 경우에만 전체 항목을 전송하기 때문입니다.proxy_cache_min_uses는
NGINX가 항목을 캐시하기 전에 클라이언트가 항목을 요청해야 하는 횟수를 설정합니다. 이 기능은 캐시가 계속 채워지는 경우에 유용합니다. 가장 자주 액세스되는 항목만 캐시에 추가되도록 하기 때문입니다. 기본적으로 proxy_cache_min_uses
는 1로 설정됩니다.proxy_cache_use_stale
지시문에 대한 업데이트
매개변수는 proxy_cache_background_update
지시문을 활성화하는 것과 결합하여 클라이언트가 만료되었거나 원본 서버에서 업데이트 중인 항목을 요청할 때 오래된 콘텐츠를 제공하도록 NGINX에 지시합니다. 모든 업데이트는 백그라운드에서 수행됩니다. 업데이트된 파일이 완전히 다운로드될 때까지 모든 요청에 대해 오래된 파일이 반환됩니다.proxy_cache_lock을
활성화한 경우, 여러 클라이언트가 캐시에 현재 없는 파일( MISS
)을 요청하는 경우, 해당 요청 중 첫 번째 요청만 원본 서버로 허용됩니다. 나머지 요청은 해당 요청이 충족될 때까지 기다린 후 캐시에서 파일을 가져옵니다. proxy_cache_lock을
활성화하지 않으면 캐시 미스를 발생시키는 모든 요청은 원본 서버로 바로 전송됩니다.하드 드라이브가 여러 개 있는 경우 NGINX를 사용하여 캐시를 여러 개로 분할할 수 있습니다. 요청 URI를 기준으로 클라이언트를 두 개의 하드 드라이브에 균등하게 분할하는 예는 다음과 같습니다.
proxy_cache_path /hdd1 경로/레벨=1:2 키_존=my_cache_hdd1:10m 최대_크기=10g 비활성=60m 임시_경로 사용=해제;
proxy_cache_path /hdd2 경로/레벨=1:2 키_존=my_cache_hdd2:10m
최대_크기=10g 비활성=60m 임시_경로 사용=해제;
split_clients $request_uri $my_cache {
50% “my_cache_hdd1”;
50% “my_cache_hdd2”;
}
server {
# ...
location / {
proxy_cache $my_cache;
proxy_pass http://my_upstream;
}
}
두 개의 proxy_cache_path
지시어는 두 개의 서로 다른 하드 드라이브에 두 개의 캐시( my_cache_hdd1
및 my_cache_hdd2
)를 정의합니다. split_clients
구성 블록은 요청의 절반( 50%
) 결과가 my_cache_hdd1
에 캐시되고 나머지 절반은 my_cache_hdd2
에 캐시되도록 지정합니다. $request_uri
변수(요청 URI)를 기반으로 하는 해시는 각 요청에 사용되는 캐시를 결정하며, 결과적으로 주어진 URI에 대한 요청은 항상 동일한 캐시에 캐시됩니다.
이 방법은 RAID 하드 드라이브 설정을 대체하지 않습니다. 하드 드라이브에 오류가 발생하면 시스템에서 예측할 수 없는 동작이 발생할 수 있습니다. 예를 들어, 오류가 발생한 하드 드라이브로 전송된 요청에 대해 500개의 응답 코드가 사용자에게 표시되는 경우가 있습니다. 적절한 RAID 하드 드라이브 설정은 하드 드라이브 오류를 처리할 수 있습니다.
이 섹션에서는 NGINX 콘텐츠 캐싱에 대해 자주 묻는 질문에 답변합니다.
네, add_header
지시어를 사용하면 됩니다.
add_header X-캐시 상태 $upstream_cache_status;
이 예제에서는 클라이언트에 대한 응답에 X-Cache-Status
HTTP 헤더를 추가합니다. $upstream_cache_status
에 가능한 값은 다음과 같습니다.
MISS
– 캐시에서 응답을 찾을 수 없어 원본 서버에서 가져왔습니다. 그러면 응답이 캐시되었을 수도 있습니다.BYPASS
– 요청이 proxy_cache_bypass
지시문과 일치했기 때문에 캐시에서 제공되지 않고 원본 서버에서 응답을 가져왔습니다(아래 캐시에 구멍을 뚫을 수 있나요? 참조). 그러면 응답이 캐시되었을 수도 있습니다.만료됨
– 캐시 항목이 만료되었습니다. 응답에는 원본 서버에서 전송된 최신 콘텐츠가 포함되어 있습니다.STALE
– 원본 서버가 올바르게 응답하지 않고 proxy_cache_use_stale이
구성되었기 때문에 콘텐츠가 오래되었습니다.업데이트 중
– 이전 요청에 대한 응답으로 항목이 현재 업데이트 중이고 proxy_cache_use_stale 업데이트가
구성되어 있기 때문에 콘텐츠가 오래되었습니다.REVALIDATED
– proxy_cache_revalidate
지시문이 활성화되었고 NGINX는 현재 캐시된 콘텐츠가 여전히 유효한지 확인했습니다( If-Modified-Since
또는 If-None-Match
).HIT
– 응답에는 캐시에서 직접 가져온 유효하고 최신 콘텐츠가 포함되어 있습니다.NGINX는 원본 서버에 미래의 날짜와 시간이 포함된 Expires
헤더나, max-age
지시문이 0이 아닌 값으로 설정된 Cache-Control
헤더가 포함된 경우에만 응답을 캐시합니다.
기본적으로 NGINX는 Cache-Control
헤더의 다른 지시문을 존중합니다. 헤더에 Private
, No-Cache
또는 No-Store
지시문이 포함되어 있으면 응답을 캐시하지 않습니다. 또한 Set-Cookie
헤더가 있는 응답은 캐시하지 않습니다. 또한 GET
및 HEAD
요청에 대한 응답만 캐시합니다. 아래 답변에 설명된 대로 이러한 기본값을 재정의할 수 있습니다.
proxy_buffering이
off
로 설정되어 있으면 NGINX는 응답을 캐시하지 않습니다. 기본적으로 켜져
있습니다.
캐시 제어
헤더를 무시할 수 있나요?네, proxy_ignore_headers
지시어를 사용하면 됩니다. 예를 들어, 이 구성을 사용하면:
위치 /images/ { proxy_cache my_cache; proxy_ignore_headers Cache-Control; proxy_cache_valid any 30m; # ... }
NGINX는 /images/ 아래의 모든 항목에 대해 Cache-Control
헤더를 무시합니다. proxy_cache_valid
지시어는 캐시된 데이터의 만료를 적용하며 Cache-Control
헤더를 무시하는 경우 필수입니다. NGINX는 만료되지 않은 파일을 캐시하지 않습니다.
Set-Cookie를
포함한 콘텐츠를 캐싱할 수 있나요?네, 이전 답변에서 설명한 대로 proxy_ignore_headers
지시어를 사용하면 됩니다.
POST
요청을 캐싱할 수 있나요?네, proxy_cache_methods
지시어를 사용하면 됩니다.
proxy_cache_methods 헤드 포스트 가져오기;
이 예제에서는 POST
요청의 캐싱을 활성화합니다.
네, Cache-Control
헤더가 허용한다면 가능합니다. 단시간 동안이라도 동적 콘텐츠를 캐싱하면 원본 서버와 데이터베이스의 부하가 줄어들어 페이지가 요청마다 다시 생성되지 않으므로 첫 번째 바이트까지의 시간이 향상됩니다.
네, proxy_cache_bypass
지시어를 사용하면 됩니다.
위치 / { proxy_cache_bypass $cookie_nocache $arg_nocache; # ... }
해당 지시어는 NGINX가 캐시에서 먼저 찾으려고 시도하는 대신 원본 서버에 즉시 콘텐츠를 요청하는 요청 유형을 정의합니다. 이것을 때때로 캐시에 "구멍을 뚫는 것"이라고도 합니다. 이 예에서 NGINX는 nocache
쿠키나 인수가 있는 요청(예: http://www.example.com/?nocache=true )
에 대해 이를 수행합니다. NGINX는 우회되지 않는 향후 요청에 대해 결과 응답을 캐시할 수 있습니다.
NGINX가 생성하는 키의 기본 형식은 다음 NGINX 변수 의 MD5 해시와 비슷합니다: $scheme$proxy_host$request_uri
. 실제로 사용되는 알고리즘은 약간 더 복잡합니다.
proxy_cache_path /경로/캐시 레벨=1:2 키_존=my_cache:10m 최대_크기=10g 비활성=60m use_temp_path=off;
서버 {
# ...
위치 / {
proxy_cache my_cache;
proxy_pass http://my_upstream;
}
}
이 샘플 구성의 경우 http://www.example.org/my_image.jpg
의 캐시 키는 md5(“http://my_upstream:80/my_image.jpg”)
로 계산됩니다.
해시된 값에서 실제 호스트 이름( www.example.com
) 대신 $proxy_host
변수가 사용된다는 점에 유의하세요. $proxy_host
는 proxy_pass
지시문에서 지정한 프록시 서버의 이름과 포트로 정의됩니다.
키의 기준으로 사용되는 변수(또는 다른 용어)를 변경하려면 proxy_cache_key
지시어를 사용합니다(다음 질문도 참조하세요).
네, 캐시 키는 다음과 같이 임의의 값으로 구성할 수 있습니다.
프록시 캐시 키 $프록시 호스트$요청_uri$쿠키_제시온id;
이 예제에서는 JSESSIONID
쿠키의 값을 캐시 키에 통합합니다. URI는 같지만 JSESSIONID
값이 다른 항목은 고유한 항목으로 별도로 캐시됩니다.
ETag
헤더를 사용하나요?NGINX 1.7.3 및 NGINX Plus R5 이상에서는 ETag
헤더와 If-None-Match가
완벽하게 지원됩니다.
캐시에 파일이 최신 상태이면 NGINX는 바이트 범위 요청을 수락하고 항목의 지정된 바이트만 클라이언트에 제공합니다. 파일이 캐시되지 않았거나 오래된 경우 NGINX는 원본 서버에서 전체 파일을 다운로드합니다. 요청이 단일 바이트 범위에 대한 것이면 NGINX는 다운로드 스트림에서 해당 범위를 발견하자마자 클라이언트에 해당 범위를 전송합니다. 요청에서 동일한 파일 내에 여러 바이트 범위가 지정되면 NGINX는 다운로드가 완료되면 전체 파일을 클라이언트에 전송합니다.
다운로드가 완료되면 NGINX는 전체 리소스를 캐시로 옮겨서 단일 범위나 여러 범위에 대한 모든 이후 바이트 범위 요청이 캐시에서 즉시 처리되도록 합니다.
NGINX가 업스트림
서버에 대한 바이트 범위 요청을 수락하려면 업스트림
서버가 바이트 범위 요청을 지원해야 합니다.
NGINX Plus는 캐시된 파일의 선택적 제거를 지원합니다. 이 설정은 파일이 원본 서버에서 업데이트되었지만 NGINX Plus 캐시에서 여전히 유효한 경우 유용합니다( Cache-Control:max-age
가 여전히 유효하고 proxy_cache_path
지시문의 inactive
매개변수로 설정된 시간 제한이 만료되지 않은 경우). NGINX Plus의 캐시 제거 기능을 사용하면 이 파일을 쉽게 삭제할 수 있습니다. 자세한 내용은 캐시에서 콘텐츠 제거를 참조하세요.
Pragma
헤더를 어떻게 처리하나요?Pragma:no-cache
헤더는 클라이언트가 모든 중간 캐시를 우회하고 요청한 콘텐츠의 원본 서버로 바로 이동하기 위해 추가합니다. NGINX는 기본적으로 Pragma
헤더를 따르지 않지만 다음 proxy_cache_bypass
지시문을 사용하여 해당 기능을 구성할 수 있습니다.
위치 /이미지/ { proxy_cache my_cache; proxy_cache_bypass $http_pragma; # ... }
Cache-Control
헤더에 stale-while-revalidate
및 stale-if-error
확장을 지원합니까?네, NGINX Plus R12 및 NGINX 1.11.10 이상에서 지원됩니다. 이러한 확장 프로그램의 기능:
Cache-Control
HTTP 헤더의 stale-while-revalidate
확장은 현재 업데이트 중일 경우 오래된 캐시된 응답을 사용할 수 있도록 허용합니다.Cache-Control
HTTP 헤더의 stale-if-error
확장은 오류가 발생한 경우에 오래된 캐시된 응답을 사용할 수 있도록 허용합니다.이러한 헤더는 위에 설명된 proxy_cache_use_stale
지시문보다 우선순위가 낮습니다.
Vary
헤더를 지원하나요?네, NGINX Plus R5 및 NGINX 1.7.7 이상에서 지원됩니다. Vary
헤더에 대한 개요는 다음과 같습니다.
NGINX 캐싱을 사용자 정의하고 조정할 수 있는 방법은 훨씬 더 많습니다. NGINX를 사용한 캐싱에 대해 더 자세히 알아보려면 다음 리소스를 살펴보세요.
"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."