Pull to refresh

Автономная работа frontend (заглушка, proxy_store, use_stale)

Reading time4 min
Views9K

Введение


Технические работы неожиданно случаются у всех проектов и площадок — избежать нельзя, можно только подготовиться. В этом обзоре собран наш опыт перевода 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;
    expires -1;
  }
}


Преимущества
  • скорость подготовки
  • отсутствие сюрпризов во время работы
  • это лучше сообщений браузера о невозможности подключиться к серверу

Проблемы
  • пользователь получает не то, что хотел бы
  • проекты теряют деньги во время проведения технических работ


upd: В комментариях предложили еще использовать директиву try_files, которая решает эту задачу без rewrite и разных location. И ставить expires заведомо в прошлое, чтобы браузер не закешировал у себя заглушку.

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-массив начинал уходить в себя


upd: Вообще-то здесь правильней было использовать proxy_cache, но у нас была всего одна неделя от «показать рекламу» до «выключаем рубильник» и мы сделали выбор в пользу пусть ущербного, но гарантированного решения.

2.5

По итогам у нас даже было желание решить найденные проблемы самописным решением, но на глобальные задачи не набралось приоритета, а площадка тем временем успела немного измениться
  • локальные кеши легли на ramdisk, размеры проектных кешей за счет этого увеличились более чем на порядок, inactive выставлен в десять часов
  • чтение статики с хранилища вынесено на отдельный пул процессов nginx на каждом узле, раздающий проксирует запросы на него, для мелкой статики настроен локальный кеш на ramdisk
  • на нескольких узлах memcache организован глобальный кеш, управляемый приложениями

И тут нам снова хотят на восемь часов отключить внутренний мир сайтов, в том числе перевозят сетевые устройства. Proxy_store использовать уже не было желания и мы попытались перейти на следующий уровень.

3. proxy_cache_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) вынесены в отдельные файлы
  • для персонализированных блоков предусмотрена отдача пустышки при недоступности backend

И далее идет штатная эксплуатация всей площадки до момента Х, когда проводятся следующие манипуляции
  • опускаем пул процессов, читающих с хранилища — мелкая статика, не попавшая в CDN, отдается с кеша по use_stale
  • переписываем app upstream's на несуществующий локальный порт и ставим ему connection_timeout 5ms, снова работает use_stale
  • переписываем адреса memcache, эта часть работает штатно

Во время учений перед работами техподдержка радовала закрытием тикетов фразой «жалоб со стороны пользователей не поступало». В боевом режиме — девять часов автономной работы, схема оправдала все ожидания — новости читались, видео смотрелось, реклама крутилась. Хотя конечно и здесь нашлись некоторые проблемы
  • потребовались некоторые изменения в приложениях
  • часть проектов у нас делают no-cache, некоторые из них из-за ошибок в конфигурациях
  • локальность кешей не гарантирует наличие свежих статей на всех узлах, пользователи натыкались на 404-ые и 502-ые
  • отдельно стоят малопосещаемые проекты, их кеши перед такими работами следует прогревать
  • по прежнему не имеем защиты от некорректных данных в кешах


В качестве приятного бонуса после всех доделок мы получили возможность переключать любой проект в статичный режим, если возникнет такая необходимость.

3.5

Для следующего шага основной целью станет научиться говорить «ДА» в ситуации «все пропало, нам срочно надо откатить проект на 15/30 минут назад, можете ли вы это сделать, а мы пока исправим причины» )

proxy_store
proxy_cache_use_stale
proxy_cache_path
Memcache::set
Tags:
Hubs:
+4
Comments13

Articles

Change theme settings