NGINX Cache: Microcaching의 이점
NGINX Cache: Microcaching은 매우 짧은 시간 동안 캐싱하여 동적이고 개인화되지 않은 콘텐츠의 전송을 가속화하는 효과적인 방법입니다. 이 포스트에서는 Microcaching 기술을 사용하여 WordPress 기반 애플리케이션을 최대 400배까지 가속하는 방법을 살펴보겠습니다.
NGINX 및 NGINX Plus는 개별 웹사이트부터 MaxCDN 및 CloudFlare와 같은 세계 최대 규모의 콘텐츠 전송 네트워크(CDN)에 이르기까지 웹 콘텐츠 캐시로 일반적으로 사용됩니다.
목차
1. 콘텐츠를 캐시하는 이유
2. 동적 콘텐츠의 NGINX Cache: Microcaching
2-1. 테스트 애플리케이션
2-2. NGINX를 사용한 간단한 Microcaching
2-2-1. 1단계: NGINX를 통한 Proxy
2-2-2. 2단계: 단기 NGINX Cache 활성화
2-3. NGINX Cache 서버 Microcaching 최적화
1. 콘텐츠를 캐시하는 이유
캐싱(Caching)은 콘텐츠를 더 빠르게 전송하여 웹 성능을 개선하고 Origin 서버의 부하를 줄이는 두 가지 이점이 있습니다. 캐싱의 효과는 콘텐츠의 캐시 가능성에 따라 달라집니다. 콘텐츠를 얼마나 오래 저장할 수 있는지, 업데이트를 어떻게 확인할 수 있는지, 캐시된 동일한 콘텐츠를 얼마나 많은 사용자에게 전송할 수 있는지 등입니다.

이미지, JavaScript, CSS 파일과 같은 정적 콘텐츠
와 거의 변경되지 않는 웹 콘텐츠를 캐싱하는 것은 비교적 간단한 프로세스입니다. 캐시 업데이트는 정기적인 Timeout, 조건부 가져오기, 그리고 필요한 경우 참조된 객체의 URL을 변경하는 Cache‑Busting 기법으로 처리할 수 있습니다.
동일한 리소스에 대한 각 요청에 대한 서버의 응답이 다르기 때문에 일반적으로 개인화된 콘텐츠
(즉, 서버 애플리케이션에서 각 사용자에 맞게 사용자 정의한 콘텐츠)를 캐싱하는 것은 불가능합니다. Server Side Include(SSI) 및 Edge Side Include(ESI)와 같은 기술을 사용하면 페이지를 구성하는 데 도움이 될 수 있지만 구현하기가 복잡하고 반드시 성능이 향상되지는 않습니다.
캐싱에 대한 흥미로운 이유는 예측할 수없이 변경될 수 있지만 사용자별로 맞춤화되지 않은(또는 클라이언트 기기에서 JavaScript를 사용하여 맞춤화되는) 동적 콘텐츠
입니다. 이러한 콘텐츠는 생성 비용이 많이 들 수 있으며 오래된 버전을 제공하면 여러 가지 문제가 발생할 수 있습니다.
캐싱에 적합한 동적 콘텐츠의 예는 다음과 같습니다.
- 몇 초마다 새 기사가 게시되는 분주한 뉴스 또는 블로그 사이트의 첫 페이지
- 최신 정보에 대한 RSS 피드
- 지속적 통합(CI) 또는 빌드 플랫폼의 상태 페이지
- 재고, 상태 또는 모금 카운터
- 추첨 결과
- 캘린더 데이터쿠키 데이터를 사용하여 산출된 광고 콘텐츠 또는 데이터와 같은 맞춤형 동적 콘텐츠가 클라이언트 기기에서 생성됩니다.
2. 동적 콘텐츠의 NGINX Cache: Microcaching
Microcaching은 콘텐츠를 1초 정도의 매우 짧은 시간 동안 캐싱하는 캐싱 기술입니다. 이는 사이트의 업데이트가 1초 이상 지연되지 않는다는 것을 의미하며, 대부분의 경우 이는 완벽하게 허용될 수 있는 수준입니다.
2-1. 테스트 애플리케이션
이 테스트에서는 표준 WordPress 설치를 사용하고 몇 가지 샘플 콘텐츠로 구성하겠습니다.

기본적인 콘텐츠가 있더라도 ab로 벤치마킹했을 때 초당 5.53건의 요청만 처리하는 등 WordPress 서버에 성능 문제가 있는 것은 확실합니다.
root@nginx-client:~# >ab -c 10 -t 30 -k http://nginx-server/
Requests per second: 5.53 [#/sec] (mean)
Time per request: 1809.260 [ms] (mean)
Time per request: 180.926 [ms] (mean, across all concurrent requests)
Transfer rate: 319.74 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.3 0 3
Processing: 1430 1735 259.4 1580 2228
Waiting: 537 683 119.7 624 980
Total: 1430 1735 259.4 1580 2228
테스트하는 동안 vmstat는 병목 현상이 PHP를 사용하여 페이지를 생성하는 데 드는 CPU 비용 때문이라고 표시했습니다(cpu 아래의 us 열의 값은 96에서 98 사이입니다).
root@nginx-server:/var/www/html# vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
10 0 0 136076 44944 585920 0 0 0 0 476 1665 96 4 0 0 0
10 0 0 140112 44952 585924 0 0 0 4 506 1773 98 2 0 0 0
10 0 0 136208 44952 585924 0 0 0 0 576 2057 97 3 0 0 0
최상위 유틸리티는 PHP 인터프리터를 실행하는 10개의 Apache httpd 프로세스가 CPU를 소비하고 있음을 보여주었습니다.
이 설정에 문제가 있습니다. – 웹 사이트의 용량을 초당 5건 이하의 요청으로 제한하고, 서비스 거부 공격이 매우 쉬우며, CPU를 추가하여 문제를 해결하면 연간 호스팅 비용에 1,000달러가 추가될 수 있습니다.
2-2. NGINX를 사용한 간단한 Microcaching
NGINX로 이 서비스를 가속하는 과정은 2단계로 이루어집니다.
2-2-1. 1단계: NGINX를 통한 Proxy
WordPress 서버에 NGINX 또는 NGINX Plus를 설치하고 들어오는 트래픽을 수신하여 내부적으로 WordPress 서버로 전달하도록 구성합니다.

NGINX Proxy 구성은 비교적 간단합니다.
server {
listen external-ip:80; # External IP address
location / {
proxy_http_version 1.1; # Always upgrade to HTTP/1.1
proxy_set_header Connection ""; # Enable keepalives
proxy_set_header Accept-Encoding ""; # Optimize encoding
proxy_pass http://wordpress-upstreams;
}
status_zone wordpress; # NGINX Plus status monitoring
}
upstream wordpress-upstreams {
zone wordpress 128k;
keepalive 20; # Keepalive pool to upstream
server localhost:80;
}
또한 Apache 구성(수신 포트 및 가상 서버)을 수정하여 Apache가 localhost:80에만 바인딩 되도록 합니다.
Proxy Hop을 추가하면 성능에 부정적인 영향을 미칠 것이라고 생각할 수 있지만 실제로 성능 변화는 미미한 수준입니다.
root@nginx-client:~# ab -c 10 -t 30 -k http://nginx-server/
Requests per second: 5.63 [#/sec] (mean)
Time per request: 1774.708 [ms] (mean)
Time per request: 177.471 [ms] (mean, across all concurrent requests)
Transfer rate: 324.44 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 1
Processing: 1423 1709 341.3 1532 2794
Waiting: 554 703 165.0 608 1165
Total: 1423 1709 341.4 1532 2794
부하가 많은 서버(동시 요청 처리량이 많은 서버)에서는 NGINX가 제공하는 Keepalive 최적화만으로도 상당한 성능 향상을 가져올 수 있습니다.
2-2-2. 2단계: 단기 NGINX Cache 활성화
서버 구성에 두 개의 지시문만 추가하면 NGINX 또는 NGINX Plus가 캐시 가능한 모든 응답을 캐시합니다. 상태 코드가 200인 응답은 1초 동안만 캐시됩니다.
proxy_cache_path /tmp/cache keys_zone=cache:10m levels=1:2 inactive=600s max_size=100m;
server {
proxy_cache cache;
proxy_cache_valid 200 1s;
# ...
}
벤치마크 테스트를 다시 실행하면 성능이 크게 향상되는 것을 확인할 수 있습니다.
root@nginx-client:~# ab -c 10 -t 30 -k http://nginx-server/
Complete requests: 18022
Requests per second: 600.73 [#/sec] (mean)
Time per request: 16.646 [ms] (mean)
Time per request: 1.665 [ms] (mean, across all concurrent requests)
Transfer rate: 33374.96 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.5 1 10
Processing: 0 16 141.5 3 2119
Waiting: 0 6 54.6 1 818
Total: 1 17 141.5 4 2121
초당 5건에서 600건으로 120배의 성능 개선이 이루어졌으니 대단해 보이지만 문제가 있습니다.
캐싱이 완벽하게 작동하고 있고 콘텐츠가 매초 새로 고쳐지는 것을 확인할 수 있지만(따라서 콘텐츠가 오래된 것이 아님), 예상치 못한 문제가 발생하고 있습니다. 처리 시간에 큰 표준 편차(141.5ms)가 있음을 알 수 있습니다. CPU 사용률은 여전히 100%이며(vmstat로 측정한 결과), 상단에 활성 httpd 프로세스 10개가 표시됩니다.
NGINX Plus Live Activity 모니터링 대시보드에서 추가 힌트를 얻을 수 있습니다.
테스트 전:

테스트 후:

대시보드에 따르면 NGINX는 테스트 중에 18032건의 요청을 처리했습니다(ab에서 보고한 18022건과 벤치마크가 30초가 지났을 때 미처리된 요청 10건). 그러나 NGINX는 150개의 요청을 Upstream으로 전달했는데, 이는 30초 테스트 동안 1초 동안 콘텐츠를 캐싱하는 경우 예상되는 것보다 훨씬 많은 양입니다.
무슨 문제인가요? CPU 사용률이 높고 캐시 새로 고침 횟수가 예상보다 많은 이유는 무엇인가요?
캐시된 항목이 만료될 때마다 NGINX는 해당 항목의 사용을 중단하기 때문입니다. NGINX는 응답을 받고 새로운 콘텐츠로 캐시를 다시 채울 수 있을 때까지 모든 요청을 Upstream WordPress 서버로 전달합니다.
이로 인해 WordPress 서버에 최대 10개의 요청이 발생하는 빈도가 잦아집니다. 이러한 요청은 CPU를 소모하고 캐시에서 제공되는 요청보다 훨씬 더 높은 지연 시간을 가지므로 결과의 표준 편차가 높아집니다.
2-3. NGINX Cache 서버 Microcaching 최적화
캐시를 최신 상태로 유지하는 데 필요한 만큼의 요청만 Upstream Origin 서버로 전달하고자 하는 전략은 분명합니다. 캐시된 콘텐츠가 업데이트되는 동안에는 캐시에서 오래된 응답(1초 또는 2초 전)을 제공할 수 있습니다. 이를 위해 두 개의 지시문을 추가합니다.
proxy_cache_lock
– 캐시를 채우기 위한 동시 시도 횟수를 제한하여 캐시된 항목이 생성될 때 해당 리소스에 대한 추가 요청이 NGINX에 Queue에 대기하도록 합니다.proxy_cache_use_stale
– 캐시된 항목이 업데이트되는 동안 오래된(현재 캐시된) 콘텐츠를 제공하도록 NGINX를 구성합니다.
앞서 추가한 캐싱 지시문과 함께 다음과 같은 서버 구성을 얻을 수 있습니다.
server {
proxy_cache one;
proxy_cache_lock on;
proxy_cache_valid 200 1s;
proxy_cache_use_stale updating;
# ...
}
벤치마킹 결과의 변화는 상당합니다. 초당 요청 수가 600건에서 거의 2200건으로 급증했습니다.
root@nginx-client:~# ab -c 10 -t 30 -n 100000 -k http://nginx-server/
Concurrency Level: 10
Time taken for tests: 30.001 seconds
Complete requests: 65553
Failed requests: 0
Keep-Alive requests: 0
Total transferred: 3728905623 bytes
HTML transferred: 3712974057 bytes
Requests per second: 2185.03 [#/sec] (mean)
Time per request: 4.577 [ms] (mean)
Time per request: 0.458 [ms] (mean, across all concurrent requests)
Transfer rate: 121379.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.3 1 5
Processing: 1 4 8.1 3 661
Waiting: 0 1 2.6 1 250
Total: 1 5 8.1 4 661
CPU 사용률이 훨씬 낮습니다(CPU 아래의 ID 열에서 유휴 시간을 확인하세요).
root@nginx-server:/var/www/html# vmstat 3
procs -----------memory---------- ---swap-- -----io---- -system--- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 106512 53192 641116 0 0 0 37 11016 3727 19 45 36 0 0
1 0 0 105832 53192 641808 0 0 0 68 17116 3521 13 56 31 0 0
1 0 0 104624 53192 643132 0 0 0 64 14120 4487 15 51 33 0 0
전송 속도(초당 121379.72KB 또는 121MBps)는 0.97Gbps에 해당하므로 테스트는 네트워크 바운드입니다. 평균 CPU 사용률이 66%인 경우 이 서버의 최고 성능은 약 2185/0.66 = 초당 3300건의 요청이 됩니다.

또한 대시보드에서 보고한 30초 테스트 동안 ab가 보고한 일관된 응답 시간(표준 편차는 8.1ms에 불과)과 Upstream 서버로 전달된 적은 수의 요청(16개)을 관찰합니다.

왜 16번만 요청할까요? 캐시가 1초 후에 시간 초과되고 업데이트에 최대 0.661초가 소요될 수 있다는 것을 알고 있으므로 업데이트가 1.66초마다 발생하지 않을 것으로 예측할 수 있습니다. 30초 동안 18회(30/1.66)를 넘지 않는 요청을 예상할 수 있습니다.
이 간단한 데모는 매우 짧은 기간 동안 동적 콘텐츠를 캐싱할 때의 잠재적인 이점과 캐시 구성을 조정하고 진단할 때 NGINX Plus의 Live Activity 모니터링 데이터의 유용성을 보여줍니다. Production 환경에서 Microcaching을 사용하려면 동적 콘텐츠를 Microcaching하고 정적 콘텐츠를 장기간 캐싱하는 보다 정교한 캐싱 정책을 생성하고 테스트하는 것이 좋습니다.
또한 NGINX Plus에는 캐시 Purge 기능이 포함되어 있어 지정된 콘텐츠를 NGINX 캐시에서 즉시 제거할 수 있습니다. 이 기능은 콘텐츠를 장기간 캐시하고 싶지만 원본 콘텐츠를 변경할 때 즉시 업데이트해야 하는 경우 프로그래밍 방식으로 사용할 수 있습니다.
NGINX Plus를 직접 사용해 보거나 테스트해 보려면 지금 30일 무료 평가판을 신청하거나 사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.
댓글을 달려면 로그인해야 합니다.