Пример конфигурационного файла Varnish
Submitted by Ромка on Пнд, 25/06/2012 - 16:11
Как обещал в докладе выкладываю пример и описание рабочего конфига для Варниша. Чтобы узнать подробнее о том, что такое Варниш и для чего он нужен ознакомьтесь с разделом Pressflow + Varnish.
Сразу сделаю важный комментарий. Прежде чем запускать Варниш на боевом сервере разработчик должен детально изучить его документацию, понять основные принципы его работы и настройки. Слепое копирование чужих конфигов, без понимания того, что делает та или иная инструкция, может привести к плачевным результатам.
В этом примере рассматривается конфиг для Варниша третьей версии (на данный момент это последняя стабильная версия). Обратите внимание, у Варниша с версии 2.1.0 поменялся движок обработки регулярных выражений, по этому некоторые примеры конфигов, доступные в интернете, могут работать некорректно. Луллаботы, например, обновили свой туториал и предлагают сразу несколько вариантов конфига для разных версий Варниша.
Задача
Варниш должен отдавать анонимам данные из своего кеша, а запросы от авторизованных пользователей передавать бэкенду. Отличать анонимного пользователя от зарегистрированного Варниш будет по наличию/отсутствию куки SESS*. В случае падения бэкенда Варниш должен отдавать кеш в том числе и для авторизованных пользователей.
Скачать мой конфиг вы можете здесь, ниже описание самых интересных и важных его частей.
Бэкенд
Указываем Варнишу с каким веб-сервером нужно работать. Параметры в блоке probe означают, что каждые 5 секунд на бэкенде нужно дергать файл status.php. Если в течение 1 секунды от бэкенда не будет получен ответ с кодом 200, то проверка будет считаться провалившейся. Если провалено 3 проверки из 5, бэкенд помечается "упавшим".
backend web1 { .host = "10.10.10.10"; .port = "80"; .probe = { .url = "/status.php"; .interval = 5s; .timeout = 1s; .window = 5; .threshold = 3; } }
Таким образом, уже через 15 секунд после возникновения проблем на бэкенде, Варниш пометит его упавшим и, в зависимости от настроек, либо начнет передавать запросы другому бэкенду, либо начнет обслуживать запросы зарегистрированных пользователей из своего кеша.
Несколько бэкендов могут быть объединены в логическую группу — director, в случае использования директоров при выходе из строя одного из бэкендов запросы будут автоматически перенаправлены на живые бэкенды из текущего директора. В нашем случае балансировка делается на уровне nginx и эта возможность Варниша не используется.
Status.php мы позаимствовали у Луллаботов, скачать его можно тут. В этом файле осуществляется проверка доступности серверов БД и Мемкеша.
ACL
Создаем списки айпишников, на основе которых позже зададим ограничение доступа к cron.php и очистке кеша Варниша:
# ACL for purging cache acl purge { "127.0.0.1"; "localhost"; "10.1.0.0"/16; } # ACL for access to cron.php acl internal { "127.0.0.1"; "localhost"; "10.10.0.0"/16; }
vcl_recv
Процедура vcl_recv содержит инструкции, которые будут выполняться Варнишем при пролучении запроса от клиента. В ней содержится основная магия. В основном эта процедура должна возвращать одно из двух значений: pass — передать запрос бэкенду или lookup — попытаться найти закешированную версию страницы в кеше, в случае отсутствия кеша, запрос будет передан бэкенду, а ответ закеширован. Полный список возможных значений можно найти тут: https://www.varnish-cache.org/docs/3.0/reference/vcl.html#subroutines, а здесь: https://www.varnish-cache.org/docs/3.0/faq/configuration.html дано описание различий значений pass и pipe.
Для начала отключаем обработку Варнишем некоторых служебных адресов Друпала:
// list of URLS which shouldn't be cached if (req.url ~ "^/status\.php$" || req.url ~ "^/update\.php$" || req.url ~ "^/info/.*$" || req.url ~ "^/flag/.*$" || req.url ~ "^.*/ajax/.*$" || req.url ~ "^.*/ahah/.*$") { return (pass); }
Доступ
Далее, ограничиваем доступ к операции очистки кеша и cron.php на основе ранее определенных списков ip-адресов.
# Clear cache entry if (req.request == "PURGE") { if (!client.ip ~ purge) { error 405 "Not allowed."; } return (lookup); } # Do not allow outside access to cron.php or install.php. if (req.url ~ "^/(cron|install)\.php$" && !client.ip ~ internal) { # Have Varnish throw the error directly. error 404 "Page not found."; # Use a custom error page that you've defined in Drupal at the path "404". #set req.url = "/404.html"; }
Задаем максимальное время жизни кеша 6 часов, а также указываем, что если сервер помечен как "упавший" (опция probe в настройках бэкенда), то у пользователя нужно отрезать все куки. В дальнейшем, для пользователей без кук мы будем отдавать кеш и эта настройка позволит Варнишу отдавать кеш авторизованным юзерам.
set req.grace = 6h; if (!req.backend.healthy) { unset req.http.Cookie; }
cookies
Далее анализируем куки, полученные от пользователя, и удаляем ненужные. Дело в том, что куки могут быть установлены как серверным софтом, так и клиентским, через java-script. Разного рода счетчики и коды баннеров ставят куки для своих целей и эти куки совершенно не интересны серверу, кроме того они мешают нам отличить анонимного посетителя от зарегистрированного. Мы удаляем все куки кроме SESS*, если после такого удаления у нас осталась пустая строка, значит куки SESS* нет и мы считаем пользователя анонимным и можем отдать ему данные из кеша. Если кука SESS* существует, то мы передаем запрос бэкенду.
// clean cookies. Pass only cookies SESS[a-z0-9], xyz if (req.http.Cookie) { set req.http.Cookie = regsub(req.http.Cookie, "^(.*)$", "; \1"); set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); set req.http.Cookie = regsuball(req.http.Cookie, ";(SESS[a-z0-9]|xyz+)=", "; \1="); set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); if (req.http.Cookie == "") { unset req.http.Cookie; } else { return (pass); } }
Код выше требует некоторого пояснения.
Куки представляют из себя текстовую строку вида "ключ=значение" разделенные точкой с запятой. Для примера рассмотрм строку "name1=val1; name2=val2; name3=val3;". Предположим, мы хотим пропустить далее только куку name2.
Приведенный выше код делает следующее:
- сначала, для служебных целей, добавляет точку с запятой перед этой строкой. Наша тестовая строка примет вид ";name1=val1; name2=val2; name3=val3;".
- Затем удаляет пробелы после всех точек с запятой ("; " заменяет на ";"). Тестовая строка примет вид: ";name1=val1;name2=val2;name3=val3;".
- Далее перед разрешенными куками добавляет пробел. Если быть точнее, то пробел добавляется между именем разрешенной куки и точкой с запятой идущей перед ним, именно для этого на первом шаге в начало строки добавлена точка с запятой, чтобы если разрешенная кука идет на первом месте, чтобы можно было перед ней воткнуть пробел. Теперь вся строка имеет вид ";name1=val1; name2=val2;name3=val3;" (перед разрешенной кукой name2 добавлен пробел).
- Теперь удаляются все записи, которые начинаются не с "; ". Вот это регулярное выражение: ";[^ ][^;]*" можно словами озвучить так: все строки первый символ в которых "точка с запятой", второй символ не пробел, затем идет любое число символов кроме точек с запятой нужно заменить на "".
- Последняя строка удаляет лидирующую точку с запятой, добавленную на первом шаге.
В итоге, если после таких преобразований у нас осталась не пустая строка, то значит кука SESS* у пользователя есть и его запрос надо передать бэкенду. Как я уже говорил выше, в случае, если бэкенд отмечен как упавший, мы удаляем из запроса пользователя информацию о куках и любой запрос, даже от авторизованного юзера, будет обслуживаться как анонимный.
Accept-Encoding
Далее приводим к общему виду заголовок "Accept-Encoding". Этот заголовок используется браузером, для того чтобы сообщить серверу какие типы сжатия он поддерживает. Веб-сервер, получив такой заголовок, может (и, по хорошему, должен) заархивировать отдаваемые данные тем алгоритмом, который поддерживается браузером.
Если одна и та же страница запрашивается разными браузерами, передающими разный заголовок "Accept-Encoding" Варниш должен в своем кеше создать несколько копий одной и той же страницы, иначе, если, например, браузер поддерживает только алгоритм сжатия deflate, а данные в кеше Варниша сжаты алгоритмом gzip, браузер, получив такие данные, не сможет их распаковать.
Проблема в том, что разные браузеры, поддерживающие один и тот же алгоритм сжатия, сообщают о нем серверу по разному, например, Хром это делает так: "Accept-Encoding:gzip,deflate,sdch", а Firefox так: "Accept-Encoding: gzip, deflate". По умолчанию, для этих двух заголовков Варниш создал бы разные копии кеша одной и той же страницы. Код ниже, приводит подобные заголовки к универсальному виду и предотвращает излишний расход памяти под кеш.
if (req.http.Accept-Encoding) { if (req.http.Accept-Encoding ~ "gzip") { # If the browser supports it, we'll use gzip. set req.http.Accept-Encoding = "gzip"; } else if (req.http.Accept-Encoding ~ "deflate") { # Next, try deflate if it is supported. set req.http.Accept-Encoding = "deflate"; } else { # Unknown algorithm. Remove it and send unencoded. unset req.http.Accept-Encoding; } }
Вот и всё. Конфиг содержит больше настроек, чем я описал в этом тексте, но остальные не такие интересные и, думаю, их смысл должен быть интуитивно понятен.
Вложение | Размер |
---|---|
![]() | 5.74 KB |
![]() | 2.37 KB |
1 Comment
Спасибо, Роман. Отличная
Submitted by drupal-admin on
Спасибо, Роман. Отличная статья.