Введение
Технические работы неожиданно случаются у всех проектов и площадок — избежать нельзя, можно только подготовиться. В этом обзоре собран наш опыт перевода front фермы на автономный режим работы — без хранилища и backend.
- заглушка
- proxy_store
- proxy_cache_use_stale + memcache ttl=0
1. Заглушка
Это самый простой способ — положить статичную страничку на локальный диск по всей ферме и настроить rewrite на нее всех запросов.
server {
listen 80;
location / {
rewrite ^.*$ /maintance.html;
}
location /maintance.html {
alias .../maintance.html;
}
}
Преимущества
- скорость подготовки
- отсутствие сюрпризов во время работы
- это лучше сообщений браузера о невозможности подключиться к серверу
Проблемы
- пользователь получает не то, что хотел бы
- проекты теряют деньги во время проведения технических работ
1.5
Долго так продолжаться конечно же не могло и, перед следующим плановым downtime на 8 часов, нам поставили задачу
откручивать рекламу любой ценой
Для понимания масштаба — у нас в сутки около полутора миллионов уников на двух сотнях новостных проектов, десятки (близко к ста) миллионов хитов на разный контент на front ферму, большая часть графики и видео лежит на CDN. Front ферма состоит из трех nginx узлов, над которыми стоит аппаратный балансировщик.
2. proxy_store
На время проведения работ в ферму был включен nginx-night со следующими настройками
- балансировщик отправлял на него четверть запросов пользователей
- в качестве upstream по всем проектам выступали три основных узла фермы
- все проходящие ответы записывались на SSD-массив с помощью директивы nginx proxy_store
location / {
proxy_pass http://nginx-farm;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass_header X-Accel-Redirect;
proxy_pass_header X-Accel-Expires;
proxy_ignore_headers X-Accel-Redirect;
set $store_path ---$request_uri---$query_string;
if ($store_path ~ "(.*)(.{1})(.{2})"){
set $new_store_path $3/$2/$store_path;
}
proxy_store /data/cache/store/$host/$new_store_path;
}
location ~ .(flv|asf|mp4)$ {
proxy_pass http://nginx-farm;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
В час Х основные узлы были выведены из балансировки, конфиг nginx-night изменен на примерно такой
location / {
root /data/cache/store/$host/;
set $store_path ---$request_uri---$query_string;
if ($store_path ~ "(.*)(.{1})(.{2})"){
set $new_store_path $3/$2/$store_path;
}
rewrite ^.*$ /$new_store_path break;
expires 1m;
}
location ~ css {
default_type text/css;
root /data/cache/store/$host/;
set $store_path ---$request_uri---$query_string;
if ($store_path ~ "(.*)(.{1})(.{2})"){
set $new_store_path $3/$2/$store_path;
}
rewrite ^.*$ /$new_store_path break;
expires 1m;
}
По сравнению с заглушкой это был огромный шаг вперед, но
- 700 MB/sec — именно такой была скорость записи на массив в режиме накопления данных
- нет возможности сохранять статусы ответов и заголовки
- для обработки user friendly urls, разбиения страниц на подкаталоги пришлось разбивать uri регуляркой и дописывать query_string — это исключает возможность определения content type для большинства сохраненных файлов (именно для этого пришлось уже в боевом режиме вводить дополнительный location для css)
- никто не гарантирует, что 1 url = 1 страница (персонализированные блоки, pjax)
- при накоплении больше 200GB SSD-массив начинал уходить в себя
2.5
По итогам у нас даже было желание решить найденные проблемы самописным решением, но на глобальные задачи не набралось приоритета, а площадка тем временем успела немного измениться
- локальные кеши легли на ramdisk, размеры проектных кешей за счет этого увеличились более чем на порядок, inactive выставлен в десять часов
- чтение статики с хранилища вынесено на отдельный пул процессов nginx на каждом узле, раздающий проксирует запросы на него, для мелкой статики организованный локальный кеш на ramdisk
- на нескольких узлах memcache организован глобальный кеш, управляемый приложениями
И тут нам снова хотят на восемь часов отключить внутренний мир сайтов, в том числе перевозят сетевые устройства. Proxy_store использовать уже не было желания и мы попытались перейти на следующий уровень.
3. proxy_use_stale
На всех проектах выставлен
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
Директива указывает nginx отдавать из кеша данные в случае ошибок, даже если данный элемент кеша уже считается устаревшим.
Дополнительно к этому
- на nginx узлах подняты memcache, все проекты, пишущие данные в memcache, дополнительно записывали их в эти instance с ttl=0 (бесконечным временем жизни)
- наведен некоторый порядок в проектных конфигах — все настройки upstream (app и memcache) вынесены в отдельные файлы
- для персонализированных блоков предусмотрен downgrade при недоступности backend
И далее идет штатная эксплуатация всей площадки до момента Х, когда проводятся следующие манипуляции
- опускаем пул процессов, читающих с хранилища — мелкая статика, не попавшая в CDN, отдается с кеша по use_stale
- переписываем app upstream's на несуществующий локальный порт и ставим ему connection_timeout 5ms, снова работает use_stale
- переписываем адреса memcache, эта часть работает штатно
Во время учений перед работами техподдержка радовала закрытием тикетов фразой «жалоб со стороны пользователей не поступало». В боевом режиме — девять часов автономной работы, схема оправдала все ожидания — новости читались, видео смотрелось, реклама крутилась. Хотя конечно и здесь нашлись некоторые проблемы
- потребовались некоторые изменения в приложениях
- часть проектов у нас делают no-cache, часть из них из-за ошибок в конфигурациях, для таких кусков стоит делать degrade
- локальность кешей не гарантирует наличие свежих статей на всех узлах, пользователи натыкались на 404-ые и 502-ые
- отдельно стоят малопосещаемые проекты, их кеши перед такими работами следует прогревать
- по прежнему не имеем защиты от некорректных данных в кешах
В качестве приятного бонуса после всех доделок мы получили возможность переключать любой проект в статичный режим, если возникнет такая необходимость.
3.5
Для следующего шага основной целью станет научиться говорить «ДА» в ситуации «все пропало, нам срочно надо откатить проект на 15/30 минут назад, можете ли вы это сделать, а мы пока исправим причины» )
proxy_store
proxy_cache_use_stale
proxy_cache_path
Memcache::set
Автор: woyager