Топ факапів Ціан

Топ факапів Ціан

Всім добра! 

Мене звуть Микита, я тимлід команди інженерів Ціан. Одним із моїх обов'язків у компанії є зниження кількості інцидентів, пов'язаних з інфраструктурою на продажі, до нуля.
Те, про що йтиметься далі, принесло нам багато болю, і мета цієї статті — не дати іншим повторити наші помилки або хоча б мінімізувати їхній вплив. 

преамбула

Давним-давно, коли Циан складався з монолітів, і жодних натяків на мікросервіси ще було, ми вимірювали доступність ресурсу перевіркою 3–5 сторінок. 

Відповідають все добре, не відповідають протягом тривалого часу алерт. Скільки часу вони мають не працювати, щоб це вважалося інцидентом, — вирішували люди на нарадах. Команда інженерів завжди брала участь у розслідуванні інциденту. Коли розслідування було закінчено, писали постмортем — своєрідний звіт на пошту у форматі: що було скільки тривало, що зробили в моменті, що зробимо в майбутньому. 

Основні сторінки сайту або як ми розуміємо, що пробили дно

 
Для того, щоб можна було якось розуміти пріоритет помилки, ми виділили найбільш критичні для бізнес-функціоналу сторінки сайту. За ними ми вважаємо кількість успішних/неуспішних запитів та таймаутів. Таким чином ми вимірюємо uptime. 

Припустимо, ми з'ясували, що є низка суперважливих розділів сайту, які відповідають за основний сервіс – пошук та подачу оголошень. Якщо кількість запитів, які завершилися помилкою, перевищує 1%, це критичний інцидент. Якщо протягом 15 хвилин у прайм-тайм відсоток помилок перевищує 0,1% — це також вважається критичним інцидентом. Ці критерії покривають більшу частину інцидентів, решта виходить за рамки цієї статті.

Топ факапів Ціан

Топ найкращих інцидентів Ціан

Отже, ми навчилися визначати той факт, що інцидент трапився. 

Тепер кожен інцидент у нас детально описано та відображено в епіку Jira. Для цього ми завели окремий проект, назвали його FAIL — у ньому можна створювати лише епіки. 

Якщо зібрати всі фейли за останні кілька років, то лідирують: 

  • інциденти, пов'язані з mssql;
  • інциденти, спричинені зовнішніми факторами;
  • помилки адміну.

Зупинимося детальніше на помилках адмінів, а також на деяких інших цікавих фейлах.

П'яте місце - "Наводимо порядок у DNS"

То справді був непогожий вівторок. Вирішили ми навести лад у DNS-кластері. 

Захотілося перевести внутрішні dns-сервери c bind на powerdns, виділивши під це окремі сервери, де крім dns нічого немає. 

Розмістили ми по одному dns-серверу в кожній локації наших ДЦ, і настав момент переїзду зон з bind у powerdns та перемикання інфраструктури на нові сервери. 

У розпал переїзду з усіх серверів, які були вказані в локальних кешуючих bind-ах на всіх серверах, залишився тільки один, який був у дата-центрі в Санкт-Петербурзі. Цей ДЦ спочатку був задекларований як некритичний для нас, але несподівано став single point of failure.
Саме в такий період переїзду впав канал між Москвою та Санкт-Петербургом. Ми фактично залишилися без DNS на п'ять хвилин і піднялися, коли хостер усунув неполадки. 

Висновки:

Якщо раніше ми нехтували зовнішніми факторами під час підготовки до робіт, то зараз їх також включили до списку того, до чого готуємось. І тепер прагнемо, що це компоненти зарезервовані n-2, але в час робіт ми можемо опускати цей рівень до n-1.

  • Під час складання плану дій відзначайте пункти, де сервіс може впасти, і продумуйте сценарій, де все пішло «гірше нікуди», заздалегідь.
  • Розподіляйте внутрішні dns-сервери по різних геолокаціях/датацентрах/стійках/комутаторах/вводах.
  • На кожному сервері ставте локальний кешуючий dns-сервер, який перенаправляє запити на основні dns-сервери, а у разі його недоступності відповідатиме з кеша. 

Четверте місце - "Наводимо порядок у Nginx"

Одного дня наша команда вирішила, що «вистачить це терпіти», і запустився процес рефакторингу конфігів nginx. Основна мета – привести конфіги до інтуїтивно зрозумілої структури. Раніше все було «історичним» і логіки в собі ніякої не несло. Тепер кожен server_name винесли до однойменного файлу та розподілили всі конфіги по папках. До слова конфіг містить у собі 253949 рядків або 7836520 символу і займає майже 7 мегабайт. Верхній рівень структури: 

Nginx structure

├── 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хх, це помітили не відразу, а лише під ранок. Після цього ми почали писати випробування на перевірку інфраструктурних компонентів.

Висновки: 

  • Правильно структуруйте конфіги (не тільки nginx) та продумуйте структуру на ранньому етапі проекту. Так ви зробите їх більш зрозумілими команді, що, у свою чергу, зменшить ТТМ.
  • Для деяких інфраструктурних компонентів напишіть тести. Наприклад: перевірка, що всі ключові server_name віддають правильний статус + тіло відповіді. Достатньо буде мати під рукою просто кілька скриптів, які перевіряють основні функції компонента, щоб судомно не згадувати о 3 годині ночі, що ще треба перевірити. 

Третє місце - "Раптом закінчилося місце в Cassandra"

Дані планомірно зростали, і все було добре до того моменту, коли в кластері Cassandra почали падати repair великих кейспейсів, тому що на них не може відпрацювати compaction. 

Одного дня кластер майже перетворився на гарбуз, а саме:

  • місця залишалося близько 20% сумарно за кластером;
  • повноцінно додати ноди не можна, тому що не проходить cleanup після додавання ноди через брак місця на розділах;
  • продуктивність потроху падає, тому що не працює компакція; 
  • кластер працює в аварійному режимі

Топ факапів Ціан

Вихід додали ще 5 нід без cleanup, після чого почали планомірно виводити з кластера і знову вводити, як порожні ноди, на яких закінчилося місце. Часу витрачено набагато більше, ніж хотілося б. Існував ризик часткової або повної недоступності кластера. 

Висновки:

  • На всіх серверах cassandra має бути зайнято не більше 60% місця на кожному розділі. 
  • Завантажені вони повинні бути не більше ніж на 50% по CPU.
  • Не варто забивати на capacity planning та продумувати його треба для кожного компонента, виходячи з його специфіки.
  • Чим більше нід у кластері – тим краще. Сервери, що містять невеликий обсяг даних, швидше переналиваються, і такий кластер легко реанімувати. 

Друге місце - "Зникли дані з consul key-value storage"

Для service discovery ми, як і багато хто, використовуємо consul. Але у нас його key-value використовується ще й для blue-green викладення моноліту. Там зберігається інформація про активні та неактивні апстрими, які змінюються місцями під час деплою. Для цього було написано сервіс деплою, який взаємодіяв із KV. Якоїсь миті дані з KV зникли. Відновили по пам'яті, але з кількома помилками. Як наслідок - при викладанні навантаження на апстрими розподілилося нерівномірно, а ми отримали багато помилок 502 через перевантаження бекендів по CPU. У результаті переїхали з consul KV на postgres, звідки їх видалити не так просто.  

Висновки:

  • Сервіси без авторизації не повинні містити в собі критичних для роботи сайту даних. Наприклад, якщо у вас немає авторизації в ES - краще заборонити доступ на рівні мережі звідусіль, де він не потрібен, залишити тільки необхідні, а також зробити action.destructive_requires_name: true.
  • Відпрацьовуйте механізм резервного копіювання та відновлення заздалегідь. Наприклад, заздалегідь зробіть скрипт (наприклад, на python), який вміє і бекапит і відновлювати.

Перше місце - "Капітан неочевидність" 

У якийсь момент ми помітили нерівномірний розподіл навантаження на апстрими nginx у випадках, коли в бекенді було 10 серверів. Через те, що round-robin направляв запити з 1 по останній апстрім по порядку, і кожен релоад nginx починав спочатку, на перші апстрими завжди припадало більше запитів, ніж на решту. Як наслідок - вони працювали повільніше і страждав весь сайт. Це ставало дедалі помітнішим у міру збільшення кількості трафіку. Просто оновити nginx для включення random не вийшло - треба переробляти купу lua коду, який не злетів на версії 1.15 (на той момент). Довелося пропатчити наш Nginx 1.14.2, впровадивши в нього підтримку Random. Це вирішило проблему. Цей баг перемагає у номінації «капітан неочевидність».

Висновки:

Було дуже цікаво та захоплююче досліджувати цей баг). 

  • Вибудуйте моніторинг так, щоб він допомагав знаходити подібні флуктуації швидко. Наприклад, можна використовувати ELK для того, щоб спостерігати за rps на кожен backend кожного upstream, стежити за їхнім часом відповіді з погляду nginx. У цьому випадку це нам допомогло виявити проблему. 

В результаті більшої частини фейлів можна було б уникнути за більш скрупульозного підходу до того, що робиш. Потрібно завжди пам'ятати про закон Мерфі: Anything that can go wrong will go wrong, та будувати компоненти, керуючись ним. 

Джерело: habr.com

Додати коментар або відгук