Обзор конфигурации Varnish
Существует множество статей о том, как можно настроить Varnish. Спешу сообщить, что единого подхода к настройке не существует. Чем больше опций Вы укажете в файле конфигурации, тем больше может появиться непредвиденных обстоятельств.
В этой статье хочу провести обзор языка VCL, который используется при настройке Varnish и рассмотреть варианты настройки. Спешу обратить Ваше внимание на то, что Varnish не поддерживает SSL.
Итак, конфигурация Varnish описывается в двух файлах:
/etc/default/varnish
для Debian/Ubuntu (/etc/sysconfig/varnish
для RedHat/CentOS)/etc/varnish/default.vcl
Первый содержит описание конфигурации демона Varnish
. Можно установить следующие параметры:
START=yes
INSTANCE=$(uname -n)
Ключевой секцией в нем является раздел DAEMON_OPTS
:
DAEMON_OPTS="-a публичный_ip_адрес:порт \
-f путь_к_файлу_vcl \
-T ip_адрес_для_админки:порт_для_админки \
-t значение_ttl \
-w минимальное_количество_процессов_varnish,максимальное_количество_процессов_varnish,время_жизни_процесса \
-s хранилище_кэша"
Ниже приведен пример секции DAEMON_OPTS. В данном случае Varnish будет работать с файлом /etc/varnish/default.vcl
. Кэш объектов будет храниться в файле размером 1Gb, расположенном на диске /var/lib/varnish/$(uname -n)/varnish_storage.bin
:
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-t 120 \
-w 10,30,50\
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"
Ниже приведен пример секции DAEMON_OPTS. В данном случае Varnish будет работать с файлом /etc/varnish/website.vcl
. Кэш объектов будет храниться в оперативной памяти (malloc) и под него будет отводиться 256Mb.
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-t 120\
-w 10,30,50\
-f /etc/varnish/website.vcl \
-S /etc/varnish/secret \
-s malloc,256m"
Возвращаюсь к утверждению о том, что единого подхода к настройке не существует, хочу заметить, что в зависимости от того кэшируете ли Вы статику (картинки, css файлы и js скрипты) нужно указывать разное хранилище для кэша.
В моей практике были сайты, у которых страницы имели много статических объектов в виде картинок, что существенно увеличивало время отгрузки страниц. Картинки редко менялись. В этом случае можно хранить кэш в файле на локальном диске и кэшировать страницы целиком, включая статические объекты. Скорость отгрузки страниц существенно увеличивалась при таком подходе (3-5 раз).
Этот блог крутится на слабом сервере, страницы легкие и не требуют много ресурсов для отдачи. Varnish настроен таким образом, что бы запросы статики отправлять прямиком на Nginx
, а остальное кешировать/выдавать с кэша. В данном случае объекты хранятся в оперативной памяти с лимитом в 256 MB. Этого вполне достаточно.
Рассмотрим файл с расширением vcl в папке /etc/varnish
и задается ключом -f
в секции DAEMON_OPTS. Начинается он с описания так называемых бэк-эндов. В этом случае backend - тот самый Apache
или NginX
, на котором вертится сайт. Выглядит эта секция вот так:
backend default {
.host = "127.0.0.1";
.port = "8080";
}
Можно добавить проверку жизнеспособности:
backend default {
.host = "127.0.0.1";
.port = "8080";
.probe = {
.url = "/";
.timeout = 0.3 s;
.window = 8;
.threshold = 3;
.initial = 3;
}
}
Если у Вас несколько серверов и Вы используете Varnish еще и в роли балансировщика нагрузки, тогда можно объявить несколько бэк-эндов и объединить их в директор:
backend www1 { .host = "192.168.0.10"; .port = "80";}
backend www2 { .host = "192.168.0.20"; .port = "80";}
backend www3 { .host = "192.168.0.30"; .port = "80";}
backend static { .host = "192.168.0.45"; .port = "80";}
director www round-robin {
{ .backend = www1; }
{ .backend = www2; }
{ .backend = www3; }
}
Дальше идет описание так называемых подпрограмм
или Subroutines
, которые применяются ко всем запросам, проходящим через Varnish.
Стандартные подпрограммы:
- vcl_recv
- vcl_pipe
- vcl_pass
- vcl_hash
- vcl_hit
- vcl_miss
- vcl_fetch
- vcl_deliver
- vcl_error
Общую последовательность работы Varnish можно описать с помощью следующей диаграммы:
1. vcl_recv отвечает за первоначальную обработку запроса. Дальнейшая судьба обработки запроса определяется именно здесь с помощью функции return(). Можно решить дальнейшую судьба запроса с помощью:
- pass - отправить запрос в vcl_pass.
- pipe отправить запрос в vcl_pipe.
- lookup - искать запрошенный объект в хранилище кэша
Для того что бы отключить использование кэша для всех запросов кроме типа GET, добавьте следующие строки в эту секцию:
if (req.request != "GET") { return (pass); }
Для того что бы отключить использование кэша для форм авторизации (Basic auth), добавьте следующие строки в эту секцию:
if (req.http.Authorization || req.request == "POST")
{
return (pass);
}
Следующая конструкция отключит использование кэша для админки блога WordPress. Соответственно wp-admin можно поменять на нужное Вам значение:
if (req.url == "^/wp-admin") {
return (pass);
}
Для того что бы отключить использование кэша для статических файлов и убрать информацию о cookie из запросов, добавьте следующие строки в эту секцию:
if (req.url ~ "^/(/wp-content|media|images|**ваш_вариант**/.*\.(css|js|ico|gif|jpeg|jpg|png|eot|ttf|swf|woff)$") {
unset req.http.Cookie;
return (pass);
}
Аналогично можно отправить все запросы статических файлов на отдельный бэк-энд,если он у Вас предусмотрен для этих целей:
if (req.url ~ "^/(/wp-content|media|images|**ваш_вариант**/.*\.(css|js|ico|gif|jpeg|jpg|png|eot|ttf|swf|woff)$") {
unset req.http.Cookie;
set req.backend = static;
}
Можно использовать конкретный бэкэнд в зависимости от значения HOST в заголовках запроса. Допустим у Вас есть dev версия сайта и лежит она на одном из описанных бэк-эндов (допустим www2):
if (req.http.host ~ "dev.website.com") {set req.backend = www2 ;}
else if (req.http.host ~ "www.website.com") {set req.backend = www; }
Полезной может быть нормализация кодирования:
if (req.http.Accept-Encoding) {
if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
}
elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
}
else {
remove req.http.Accept-Encoding;
}
}
Закрывается секция функцией return (), с аргументом pass, pipe или lookup.
sub vcl_recv { if (req.request != "GET") { return (pass); } if (req.url == "^/wp-admin") { return (pass); } if (req.url ~ "^/(/wp-content|media|images|ваш_вариант/.*\.(css|js|ico|gif|jpeg|jpg|png|eot|ttf|swf|woff)$") { unset req.http.Cookie; set req.backend = static; } if (req.http.Accept-Encoding) { if (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } elsif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { remove req.http.Accept-Encoding; } } return (lookup); }
2. При попадании запроса в vcl_pass он доставляется на прямую бэк-энду, минуя кэш. 3. При попадании запроса в vcl_pipe происходит своеобразное замыкание клиента на бэк-энд. Varnish курит в сторонке или обрабатывает другие запросы. Если честно большой разницы между vcl_pass и vcl_pipe я не вижу. 4. vcl_hash отвечает за создание хэш-слепка объекта в хранилище кэша. Дополнительных опций не имеет. 5. vcl_miss срабатывает, если запрашиваемый объект не был найден в хранилище кэша. По умолчанию отправляет объект на обработку vcl_fetch. 6. vcl_hit срабатывает, если запрашиваемый объект был найден в хранилище кэша. По умолчанию отправляет объект на обработку vcl_deliver. 7. vcl_fetch срабатывает, когда запрашиваемый объект был получен от бэк-энда.
Если для Вас критично, что бы в логи бэк-энд серверов попадали реальные значения ip адресов посетителей, добавьте следующие строки в эту секцию:
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;
Для того, что бы задать время жизни объекта в хранилище кэша, нужно сначала убрать заголовок, обозначающий время жизни объекта:
unset beresp.http.expires;
А потом указать время жизни объекта в хранилище (в секундах):
set beresp.ttl = 86400 s;
В конце секции:
return (deliver);
sub vcl_fetch { remove req.http.X-Forwarded-For; set req.http.X-Forwarded-For = client.ip; set beresp.ttl = 86400 s; return (deliver); }
8. vcl_deliver срабатывает перед доставкой объекта с хранилища кэша клиенту. Здесь можно либо добавить, либо убрать нужные заголовки (headers) в http ответе:
remove resp.http.X-Varnish;
remove resp.http.Via;
remove resp.http.Age;
remove resp.http.X-Purge-URL;
remove resp.http.X-Purge-Host;
remove resp.http.X-CF-Powered-By;
set resp.http.X-Custom-name custom-value
sub vcl_deliver { remove resp.http.X-Varnish; remove resp.http.Via; remove resp.http.Age; remove resp.http.X-Purge-URL; remove resp.http.X-Purge-Host; remove resp.http.X-CF-Powered-By; return (deliver); }
Так же обратите внимание на статью Очистка кэша Varnish и Размышления о кластеризации: Часть 3 — Varnish кэш