오늘은 워드프레스 속도를 빠르게 하는 방법 중 상당히 효과가 좋다고 알려진 FastCGI 적용 방법에 대해서 알아보도록 하겠습니다. 이는 웹서버로 NGINX를 사용하는 경우 유용합니다.
웹서버로 아파치를 많이 사용하고 점유율도 높지만 얼마 전부터는 단순하면서 성능이 좋은 NGINX를 웹서버로 사용하는 경우도 늘었습니다. 특히 동접자가 많은 경우 NGINX가 메모리 관리 등에서 효율성이 있다고 알려져 그런 흐름이 가속화 되기도 했습니다.
그렇지만 아파치에서도 NGINX만의 장점들을 쉽게 구현하는 방법들이 등장해 아파치를 사용에도 별 문제가 없어져 차별화가 많이 희석되기도 했습니다.
1. FastCGI cache란?
일반적으로 사용자가 브라우저를 통해서 특정 페이지를 보겠다고 요청하면 NGINX는 PHP와 MySQL이라는 데이타베이스에게 명령을 내려 사용자가 요청한 페이지를 구성하게 하는데요. FastCGI는 NGINX와 PHP 사이에서 기존에 불러왔던 페이지를 저장해 놓고 있다가 사용자 요청이 오면 PHP나 MySQL의 도움없이 바로 사용자에게 보여주어 굉장히 빨리 처리해 속도를 높이고 시스템의 부하를 줄이는 역활을 합니다. 이를 업계에선 Full page caching이라고 부르고 있네요.
아래는 FastCGI가 작동하는 다이어그램인데요. NGINX, PHP-FPM, MySQ 사이의 관계를 잘 보여주고 있습니다.
FastCGI cache는 서버 부하를 줄여주고, 사이트 속도를 빠르게 해주는데요. cache된 페이지 중심이긴 하지만 속도가 빨라지고 TTFB(Time To First Byte, 서버에 요청해 첫 데이타를 받기까지 걸리는 시간)가 절반 이하로 줄어듭니다.
2. FastCGI 적용 방법
그러면 이런 FastCGI cache를 적용하려면 어떻게 해야 할까요?
아래와 같이 순서대로 적용해 보시지요. FastCGI cache는 서버를 설치(NGINX, PHP, MariaDB 등) 시 자동으로 설치되므로 설되어 있는 기준으로 세팅을 어떻게 하는냐를 말씀드리겠습니다.
2.1. Nginx의 설정파일 nginx.conf 설정하기
먼저 Nginx의 설정파일인 nginx.conf에서 FastCGI cache 관련 내용을 설정합니다.
여기는 Nginx의 설정 파일 중 http 구간에 적용하는 코드가 되겠습니다.
FastCGI cache path 위치 지정
FastCGI cache path 위치는 아무 곳이라도 상관이 없습니다. 심지어 별도로 위치를 지정하지 않아도 NGINX가메모리 여유가 있으면 자동으로 Cache를 램에 올려 사용하고 모자라면 스압을 사용한다고 합니다.
그렇지만 필요에 따라서 FastCGI Cache 파일을 지우는 등 Cache 관리를 하려면 일정 폴더를 지정해 주어야 합니다. FastCGI Cache를 관리해주는 플러그인들은 대부분 Cache 폴더를 지정토록 하고 있습니다.
물론 폴더와 상관없이 FastCGI cache를 관리하는 방법이 있기는 한데 불안정하고 설치 버젼에 따라서 작동하지 않은 경우도 많기 때문에 cache 폴더 지정해 관리하는 것이 좋습니다.
그리고 몇가지 옵션을 테스트했지만 명확히 메모리 폴더를 지정하지 않으면, 우선 순위에서 밀리기 때문에 램에서 작동하지 않는 경우도 많아지는 것 같습니다.
그렇기 때문에 램에서 작동토록 하려면 Cache 폴더를 지정해 주어 확실하게 우선 순위를 설정하는 것이 나은 선택입니다.
또한 메모리가 충분하지 않다면 SSD로 구성된 스토리에 FastCGI cache 폴더를 지정해 운영하는 것도 차선책이 될 수 있습니다. 그렇지만 예전 느려빠진 HDD 스토리지라면 별로 추천하고 싶지는 않습니다. 콘텐츠 내용이 많지만 자주 업데이트 되지 않는 사이트의 경우 Cache 기간을 며칠 정도로 길데 잡아 효율성을 높일 때 유용할 것 같습니다.
제가 아는 사이트는 콘텐츠 양이 많다보니 8시간 정도로 길게 잡으니 cache 용량이 2GB를 넘어 버리드라구요. 이런 경우는 램을 업그레이드 할 수 없다면 SSD 스토리에 Cache 폴더를 설정해 운영하는 것도 아주 나쁘지는 않습니다.
당연히 비즈니스 사이트라면 램을 업그레이드해서 Cache 전체를 램에 올리는게 좋겠죠.
저는 용량은 적지만 빠른 RAM 메모리에 했는데요. 여기서는 두가지 경우를 모두 표현 했습니다.
# 일반 디스크에 6999MB 용량에 cache해 캐시 유효기간을 10분으로 유지하는 WORDPRESS라는 캐시 이름
#fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=WORDPRESS:2048mb inactive=8h; # Disk애 적용 시
# RAM 메모리 512MB 용량을 할당, 10분동안 유지하는 WORDPRESS라는 캐시 이름
fastcgi_cache_path /dev/shm/fastcgi levels=1:2 keys_zone=WORDPRESS:512m inactive=10m; #RAM에 적용 시
Code language: PHP (php)
여기서 정말 강조하고 싶은 것은 위치 지정 후 정말 제대로 작동하는지를 꼭 확인하라는 것입니다. path 설정해 놓는다고 다 작동하지는 않으며 RAM에 올리는 방식 또는 Disk에 설정 시 스왑등이 제대로 설정되어 있는지에 따라서 제대로 작동하지 않을 수 있습니다.
FastCGI cache 중 발생 상황 대응 지정
그 다음은 FastCGI가 디스크에 파일을 배치하는 방법, FastCGI 서버와의 통신 중에 오류 발생 시 하는 경우 등 오래된 캐시를 사용할 수 있는 경우, 워드프레스 테마 중 종종 부적절한 헤더값을 보내주는 경우 무시하라는 옵션을 표기합니다.
fastcgi_cache_key "$scheme$request_method$host$request_uri$mobile_request";
fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
Code language: PHP (php)
FastCGI cache 조건 설정
fastcgi_cache_valid 200 301 302 60m;
fastcgi_cache_valid 404 30s;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_keep_conn on;
Code language: PHP (php)
- 여기서 fastcgi_cache_valid 200 301 302 60m;은 응답코드가 200 301 302인 것을 캐시하고 그 캐시파일의 유효기간을 60분을 잡는 것입니다. 개인 사이트는 변경 내용이 많지 않으니 넉넉하게 시간을 주어도 좋을 것이고, 커뮤니티 사이트는 빨리 내용이 갱신되므로 1m(1분) 또는 30s(30초) 단위로 짧은 시간을 주는게 좋다고 합니다.
- fastcgi_cache_valid 404 30s;은 응답코드가 404인 것은 30초마다 다시 갱신토록 했습니다.
- fastcgi_buffers 16 16k; , fastcgi_buffer_size 32k;는 캐시 버터 크기 설정입니다.
FastCGI 캐시를 메모리에서 작동시키기
FastCGI를 일반 디스크에서 실행하는 것보다도 RAM 메모리에서 실행시키면 더욱 빠르겠죠.
RAM 시스템 메모리는 디스크보다 훨씬 빠르니 반응시간도 훨씬 빨라질 것 입니다.
먼저 RAM 디스크를 등록해 줍니다. /etc/fstab 파일을 nano 편집기를 열어 편집할 수 있도록 합니다.
nano -w /etc/fstab
Code language: PHP (php)
fstab에서 아래와 같이 RAM 디스크를 등록해 줍니다.
tmpfs /dev/shm/fastcgi tmpfs defaults,size=128M 0 0
Code language: PHP (php)
다음에는 RAM Disk를 구성할 디렉토리와 소유권등을 정리해 줍니다. RAM Disk로 /dev/shm/fastcgi를 나들겠습니다.
mkdir /dev/shm/fastcgi
chown www-data:www-data /dev/shm/fastcgi
chmod +x /dev/shm/fastcgi
Code language: PHP (php)
이러면 자동으로 디렉토리를 만들고 그리고 나서 NGINX 시스템을 다시 실행합니다.
service nginx restart
Code language: PHP (php)
이제 새로운 RAM Disk를 마운트합니다.
mount -a
Code language: PHP (php)
참고로 RAM Disk를 없애려면 /etc/fstab에서 Ram 디스크를 지우고, 터미널에서 umount 명령을 사용합니다.
umount /dev/shm/fastcgi
Code language: PHP (php)
Nginx에 캐시폴더 권한을 줍니다
chown www-data:www-data /dev/shm/fastcgi
Code language: PHP (php)
이렇게 별도 폴더를 만들지 않고 우분투 설치 시 자동으로 만들어지는 /dev/shm 폴더를 그대로 사용하는 것도 방법입니다. 별도 폴더를 만들지 않아도 되기 때문에 더욱 간편하긴 한데요. 이 폴더에 다른 프로그램도 사용할 수 있어 fastcgi 폴더를 만드는 것도 나쁘진 않습니다.
2.2. 서버 블락에서 FastCGI cache 설정
지금부터는 서버 블락에서 FastCGI cache 관련 옵션 정리 방법입니다.
캐싱을 하지 말아야 할 경우 설정
캐싱을 하지 말아야 할 경우가 있는데 이 구간을 정의하는 부분입니다.
set $skip_cache 0;
# ---------------------------------------------------------------------
# 캐싱을 하지 말아야 할 경우가 있는데 이를 정의하는 구간 CACHE SKIP RULES - START
# ---------------------------------------------------------------------
# Do not cache POST requests - they should always go to PHP
if ($request_method = POST) {
set $skip_cache 1;
}
# Do not cache URLs with a query string - they should always go to PHP
if ($query_string != "") {
set $skip_cache 1;
}
# WooCommerce-specific cache skip rules
if ($request_uri ~* "/store.*|/cart.*|/my-account.*|/checkout.*|/addons.*") {
set $skip_cache 1;
set $skip_cache_reason WP_WooCommerce;
}
if ($cookie_woocommerce_items_in_cart) {
set $skip_cache 1;
set $skip_cache_reason WP_WooCommerce;
}
if ($request_uri ~* ("/cart.*")) {
set $skip_cache 1;
}
# Don't cache URIs containing the following segments (admin panel, sitemaps, feeds, etc.)
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php|sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") {
set $skip_cache 1;
}
# Don't use the cache for logged-in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
set $skip_cache 1;
}
# ---------------------------------------------------------------------
# CACHE SKIP RULES - END
# ---------------------------------------------------------------------
Code language: PHP (php)
FastCGI와 PHP-FPM과 통합
이제 FastCGI cache와 PHP간 통합을 정의합니다.
이 부분도 마찬가지로 server 블락에 아래 내용을 추가 합니다.
# Add PHP handler
location ~ \.php$ {
try_files $uri =404; # comment out this line if php-fpm is hosted on a remote machine
include /etc/nginx/fastcgi.conf;
fastcgi_cache WORDPRESS;
add_header X-Cache $upstream_cache_status;
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
}
Code language: PHP (php)
3. NGINX 재가동 및 작동 테스트
FastCGI 설정이 완료되면 nginx를 다시 가동시키고 제대로 작동하는지 테스트 해 봅니다.
먼저 문법 상 충돌되거나 오류가 없는지 config test를 진행합니다.
service nginx configtest
Code language: PHP (php)
“the configuration file /etc/nginx/nginx.conf syntax is ok” 라는 문구가 나오면 정상적으로 작동 가능한 것이므로 nginx를 다시 가동시킴니다.
service nginx restart
Code language: PHP (php)
이제 FastCGI가 제대로 작동하는지 확인해 봅니다.
ls -alhR /dev/shm/fastcgi
Code language: PHP (php)
그러면 현재 cache되고 있는 리스트가 주욱 나옵니다. 그러면 정상적으로 작동하는 것이니 기쁜 마음으로 마무리 합니다.
4. 시행 착오
무사히 FastCGI를 설치하고 작동하는 것을 확인하고부터 사이트의 속도가 굉장히 빨라졌습니다. 그리고 욕심을 내어서 몇가지를 조정했습니다.
- 먼저 cache를 램에 올리고 메모리를 512MB을 할당했습니다.
- 다음 cache 교체 시간을 1440m으로 상당히 길게 잡았습니다. 내용이 많이 바뀌지 않기 때문에 여유있게 잡아본 것입니다.
이렇게 설정을 바꾼 후 처음 엄청 빠른 속도를 보여 주었던 것이 시간이 지나면서 엄청 느려지기 시작했습니다. 사이트 메인의 TTFB가 처음에는 0.3ms정도였는데 어느 순간 1.3s까지 느려지더군요.
일정 시간 경과 후 webpagetest.org 측정 결과
왜 이런 문제가 발생했을까 여러가지 설정 테스트를 해보니 cache 유지 시간이 길고, RAM 512MB에 세팅한 cache가 꽉 찬 상태에서는 무엇인가가 제대로 작동하지 않는다는 결론을 얻었습니다.
cache를 비우고 cache 설정 시간을 30m 정도로 줄이니 제대로 작동했으니깐요.
결국 cache 메모리가 적다면 cache 교체 시간을 짧게 설정해야 한다는 결론을 얻었습니다.
하지만 나중에 생각해보면 Fastcgi는 설정한 용량이 차면 순서대로 Cache를 지우도록 되어 있기 때문에 위와 같은 해석은 근거가 없습니다. 어느 분이 스왑으로 변경하기 때문에 그럴 것이라는 의견을 주셨는데 앞에서 설명드린대로 SSD도 상당히 빠르기 때문에 설령 나머지가 스왑으로 전환되어 SSDㅇ서 작동한다고 해도 그 정도로 느려지지는 않습닏.
그후 FastCGI와 구글 모드 페이지 스피드를 같이 운영 시 나타나는 문제라는 것을 알았습니다. 구글 모드 페이지 스피드는 너무 열심히 일을 해서 구글 모드 페이지 스피드 캐시를 끊임없이 업데으이트하면서 CPU자원을 100%이상으로 사용하고 여기에 FastCGI Cache도 만드느라 이중 일을 하면서 속도가 느려지는 현상이었습니다. 이후 구글 모드 페이지 스피드를 제거 후 이런 문제를 만나적은 없습니다.