- PVSM.RU - https://www.pvsm.ru -

Всем добра!
Меня зовут Никита, я тимлид команды инженеров Циан. Одной из моих обязанностей в компании является снижение количества инцидентов, связанных с инфраструктурой на проде, до нуля.
То, о чем пойдет речь далее, принесло нам много боли, и цель этой статьи — не дать другим людям повторить наших ошибок или хотя бы минимизировать их влияние.
Давным-давно, когда Циан состоял из монолитов, и никаких намеков на микросервисы еще не было, мы измеряли доступность ресурса проверкой 3–5 страниц.
Отвечают — все хорошо, не отвечают в течение длительного времени — алерт. Сколько времени они должны не работать для того, чтобы это считалось инцидентом, — решали люди на совещаниях. Команда инженеров всегда участвовала в расследовании инцидента. Когда расследование было закончено, писали постмортем — своеобразный отчет на почту в формате: что было, сколько длилось, что сделали в моменте, что сделаем в будущем.
Для того чтобы можно было как-то понимать приоритет ошибки, мы выделили наиболее критичные для бизнес-функционала страницы сайта. По ним мы считаем количество успешных/неуспешных запросов и таймаутов. Таким образом мы измеряем uptime.
Допустим, мы выяснили, что есть ряд суперважных разделов сайта, которые отвечают за основной сервис — поиск и подачу объявлений. Если количество запросов, которые завершились ошибкой, превышает 1%, — это критический инцидент. Если в течение 15 минут в прайм-тайм процент ошибок превышает 0,1% — то это также считается критическим инцидентом. Эти критерии покрывают бОльшую часть инцидентов, остальные выходят за рамки этой статьи.

Итак, мы точно научились определять тот факт, что инцидент случился.
Теперь каждый инцидент у нас детально описан и отражен в эпике Jira. К слову: для этого мы завели отдельный проект, назвали его FAIL — в нем можно создавать только эпики.
Если собрать все фейлы за последние несколько лет, то лидируют:
Остановимся более детально на ошибках админов, а также на некоторых других интересных фейлах.
Это был ненастный вторник. Решили мы навести порядок в DNS-кластере.
Захотелось перевести внутренние dns-серверы c bind на powerdns, выделив под это полностью отдельные серверы, где кроме dns ничего нет.
Разместили мы по одному dns-серверу в каждой локации наших ДЦ, и наступил момент переезда зон из bind в powerdns и переключения инфраструктуры на новые серверы.
В самый разгар переезда из всех серверов, которые были указаны в локальных кэширующих bind-ах на всех серверах, остался только один, который был в дата-центре в Санкт-Петербурге. Этот ДЦ изначально был задекларирован как некритичный для нас, но внезапно стал single point of failure.
Как раз в такой период переезда упал канал между Москвой и Санкт-Петербургом. Мы фактически остались без DNS на пять минут и поднялись, когда устранил неполадки.
Выводы:
Если раньше мы пренебрегали внешними факторами во время подготовки к работам, то сейчас их тоже включили в список того, к чему готовимся. И теперь стремимся к тому, что все компоненты зарезервированы n-2, а на время работ мы можем опускать этот уровень до n-1.
В один прекрасный день наша команда решила, что «хватит это терпеть», и запустился процесс рефакторинга конфигов nginx. Основная цель — привести конфиги к интуитивно понятной структуре. Раньше все было «исторически сложившимся» и логики в себе никакой не несло. Теперь каждый server_name вынесли в одноименный файл и распределили все конфиги по папкам. К слову — конфиг содержит в себе 253949 строк или 7836520 символа и занимает почти 7 мегабайт. Верхний уровень структуры:
├── access
│ ├── allow.list
...
│ └── whitelist.conf
├── geobase
│ ├── exclude.conf
...
│ └── geo_ip_to_region_id.conf
├── geodb
│ ├── GeoIP.dat
│ ├── GeoIP2-Country.mmdb
│ └── GeoLiteCity.dat
├── inc
│ ├── error.inc
...
│ └── proxy.inc
├── lists.d
│ ├── bot.conf
...
│ ├── dynamic
│ └── geo.conf
├── lua
│ ├── cookie.lua
│ ├── log
│ │ └── log.lua
│ ├── logics
│ │ ├── include.lua
│ │ ├── ...
│ │ └── utils.lua
│ └── prom
│ ├── stats.lua
│ └── stats_prometheus.lua
├── map.d
│ ├── access.conf
│ ├── ..
│ └── zones.conf
├── nginx.conf
├── robots.txt
├── server.d
│ ├── cian.ru
│ │ ├── cian.ru.conf
│ │ ├── ...
│ │ └── my.cian.ru.conf
├── service.d
│ ├── ...
│ └── status.conf
└── upstream.d
├── cian-mcs.conf
├── ...
└── wafserver.conf
Стало значительно лучше, но в процессе переименования и распределения конфигов часть из них имела неправильное расширение и не попала в директиву include *.conf. Как следствие — часть хостов стала недоступна и возвращала 301 на главную. Из-за того что код ответа был не 5хх/4хх, это заметили не сразу, а лишь под утро. После этого мы начали писать тесты на проверку инфраструктурных компонентов.
Выводы:
Данные планомерно росли, и все было хорошо до того момента, когда в кластере Cassandra начали падать repair больших кейспейсов, потому что на них не может отработать compaction.
В один ненастный день кластер почти превратился в тыкву, а именно:

Выход — добавили еще 5 нод без cleanup, после чего начали планомерно выводить из кластера и снова вводить, как пустые ноды, на которых закончилось место. Времени затрачено сильно больше, чем хотелось бы. Был риск частичной или полной недоступности кластера.
Выводы:
Для service discovery мы, как и многие, используем consul. Но у нас его key-value используется еще и для blue-green выкладки монолита. Там хранится информация об активных и неактивных апстримах, которые меняются местами во время деплоя. Для этого был написан сервис деплоя, который взаимодействовал с KV. В какой-то момент данные из KV пропали. Восстановили по памяти, но с рядом ошибок. Как следствие — при выкладке нагрузка на апстримы распределилась неравномерно, а мы получили много ошибок 502 из-за перегрузки бекендов по CPU. В итоге переехали с consul KV на postgres, откуда удалить их уже не так просто.
Выводы:
В какой-то момент мы заметили неравномерное распределение нагрузки на апстримы nginx в случаях, когда в бекенде было 10+ серверов. Из-за того что round-robin направлял запросы с 1 по последний апстрим по порядку, и каждый релоад nginx начинал сначала, на первые апстримы всегда приходилось больше запросов, чем на остальные Как следствие — они работали медленнее и страдал весь сайт. Это становилось все более заметным по мере увеличения количества трафика. Просто обновить nginx для включения random не вышло — надо переделывать кучу lua кода, который не взлетел на версии 1.15 (в тот момент). Пришлось пропатчить наш nginx 1.14.2, внедрив в него поддержку random. Это решило проблему. Этот баг побеждает в номинации «капитан неочевидность».
Выводы:
Было очень интересно и увлекательно исследовать этот баг).
В результате бОльшей части фейлов можно было бы избежать при более скрупулезном подходе к тому, что делаешь. Надо всегда помнить о законе Мерфи: Anything that can go wrong will go wrong, и строить компоненты, руководствуясь им.
Автор: Nikita Maslyannikov
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/sistemnoe-administrirovanie/352611
Ссылки в тексте:
[1] хостер: https://www.reg.ru/?rlink=reflink-717
[2] Источник: https://habr.com/ru/post/499542/?utm_source=habrahabr&utm_medium=rss&utm_campaign=499542
Нажмите здесь для печати.