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

Топ на най-добрите инциденти в Циан
Така че, ние определено научихме как да установим факта, че инцидент е настъпил.
Сега всеки инцидент е подробно описан и отразен в епика на Jira. За това сме създали отделен проект, наречен FAIL — в него могат да се създават само епики.
Ако съберем всички неуспехи през последните години, водещи са:
- инциденти, свързани с mssql;
- инциденти, причинени от външни фактори;
- грешки на администратора.
Нека се задълбочим по-подробно върху грешките на администраторите, а също и на някои други интересни неуспехи.
Пето място — „Подреждаме DNS“
Това беше един неблагоприятен вторник. Решихме да приведем в ред DNS кластера.
Искахме да мигрираме вътрешните DNS сървъри от bind на powerdns, като отделим напълно специализирани сървъри, на които няма да има нищо освен DNS.
Разположихме по един DNS сървър във всяка локация на нашите дата центрове и настъпи моментът за преместване на зоните от bind към powerdns и за превключване на инфраструктурата към новите сървъри.
В най-разгара на преместването от всички сървъри, които бяха указани в локалните кеширащи bind сървъри на всички сървъри, остана само един, който беше в дата центъра в Санкт Петербург. Този дата център първоначално беше деклариран като некритичен за нас, но внезапно стана единствена точка на неуспех.
Точно в този период на преместване падна канала между Москва и Санкт Петербург. По факто останахме без DNS за пет минути и се възстановихме, когато хостинг доставчикът бяха отстранени проблемите.
Изводи:
Ако преди пренебрегвахме външни фактори по време на подготовката за работите, сега те също са включени в списъка с неща, за които се подготвяме. И сега се стремим всички компоненти да са резервирани n-2, а по време на работите можем да намалим това ниво до n-1.
- Докато изготвяте плана за действия, отбелязвайте пунктовете, където услугата може да падне, и обмислете сценарий, при който всичко е «по-зле не може да стане», предварително.
- Разпределяйте вътрешните DNS сървъри в различни геолокации/дата центрове/стойки/комутатори/въвеждания.
- На всеки сървър инсталирайте локален кеширащ DNS сървър, който пренасочва заявките към основните DNS сървъри, а в случай на недостъпност ще отговаря от кеша.
Четвърто място — «Привеждане на ред в Nginx»
В един прекрасен ден нашият екип реши, че «стига толкова» и започна процеса на рефакториране на конфигурациите на nginx. Основната цел — да приведем конфигурациите в интуитивно разбираема структура. Преди всичко беше «исторически натрупано» и нямаше никаква логика в себе си. Сега всеки server_name беше изнесен в съответствуващ файл и разпределихме всички конфигурации по папки. Между другото - конфигурацията съдържа 253949 реда или 7836520 символа и заема почти 7 мегабайта. Върховното ниво на структурата:
Структура на Nginx
├── 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 на главната. Поради факта, че кодът на отговора не беше 5xx/4xx, това не беше забелязано веднага, а едва сутринта. След това започнахме да пишем тестове за проверка на инфраструктурните компоненти.
Изводи:
- Правилно структурирайте конфигурациите (не само nginx) и обмисляйте структурата на ранен етап от проекта. Така ще ги направите по-разбираеми за екипа, което от своя страна ще намали времето за управление.
- За някои инфраструктурни компоненти пишете тестове. Например: проверка, че всички ключови server_name върнат правилен статус, + съдържание на отговора. Достатъчно е да имате под ръка просто няколко скрипта, които проверяват основните функции на компонента, за да не си спомняте в три часа сутринта какво още трябва да проверите.
Трето място — "Неочаквано свърши мястото в Cassandra"
Данните постепенно нарастваха и всичко беше добре до момента, когато в кластера Cassandra започнаха да падат ремонтите на големи кейсове, защото не може да се извърши компакция.
В един дъждовен ден кластерът почти се превърна в тиква, а именно:
- оставяха около 20% общо в кластера;
- пълноценно добавяне на нодове не е възможно, тъй като не преминава почистването след добавяне на нод поради липса на място в дяловете;
- производителността бавно спада, тъй като компакцията не работи;
- кластерът работи в аварен режим.

Изходът е такъв — добавихме още 5 нода без почистване, след което започнахме поетапно да изключваме от кластера и отново да въвеждаме като празни ноди, на които е свършило мястото. Времето, което бе необходимо, е значително повече, отколкото бихме искали. Имаше риск от частична или пълна недостъпност на кластера.
Изводи:
- На всички сървъри касандра не трябва да бъде заето повече от 60% от мястото на всеки дял.
- Заредени те трябва да бъдат не повече от 50% по CPU.
- Не бива да пренебрегвате планирането на капацитет и то трябва да се мисли за всеки компонент, в зависимост от неговата специфика.
- Колкото повече нода в кластера — толкова по-добре. Сървърите, съдържащи малко количество данни, се пренастройват по-бързо и такъв кластер е по-лесен за реанимация.
Второ място — „Изчезнаха данни от консул key-value хранилище“
За откритие на услуги ние, както и много други, използваме консул. Но нашият key-value се използва също и за blue-green внедряване на монолита. Там се съхранява информация за активни и неактивни апстрийми, които си сменят местата по време на деплой. Беше написан сервис за деплой, който взаимодействаше с KV. В определен момент данните от KV изчезнаха. Възстановихме ги по памет, но с редица грешки. В резултат — при внедряването натоварването на апстриймите се разпредели неравномерно и получихме много грешки 502 заради пренатоварване на бекендовете по CPU. В крайна сметка се преместихме от консул KV на Postgres, откъдето да ги изтрием вече не е толкова лесно.
Изводи:
- Услугите без никаква авторизация не трябва да съдържат критични за работата на сайта данни. Например, ако нямате авторизация в ES — по-добре е да забраните достъпа на ниво мрежа от всякъде, където не е нужен, да оставите само необходимите и също така да направите action.destructive_requires_name: true.
- Заявете механизма за резервно копиране и възстановяване предварително. Например, предварително направете скрипт (например на python), който може както да прави резервни копия, така и да възстановява.
Първо място — „Капитан неочевидност“
В определен момент забелязахме неравномерно разпределение на натоварването към upstream-ите на nginx, когато в бекенда имаше 10+ сървъра. Поради факта, че round-robin насочваше заявките от 1 до последния upstream последователно, а всеки reload на nginx започваше отначало, на първите upstream-и винаги идваха повече заявки, отколкото на останалите. В резултат на това те работеха по-бавно и целият сайт страдаше. Това ставаше все по-очевидно с увеличаването на трафика. Просто обновяването на nginx, за да се включи random, не вървеше — трябваше да преработим много lua код, който не сработи на версия 1.15 (в този момент). Приключихме с патчване на нашия nginx 1.14.2, внедрявайки поддръжка на random. Това реши проблема. Тази грешка печели в номинацията за „капитан очевидност“.
Изводи:
Беше много интересно и увлекателно да проучвам тази грешка).
- Настройте мониторинга така, че да помогне бързо да се открият подобни флуктуации. Например, може да се използва ELK, за да се наблюдават rps на всеки бекенд на всеки upstream и да се следи времето им за отговор от гледна точка на nginx. В този случай това ни помогна да идентифицираме проблема.
В повечето случаи на неуспехи можеше да се избегнат с по-скрупулозен подход към това, което правите. Винаги трябва да помните закона на Мърфи: Всичко, което може да се обърка, ще се обърка, и да изграждате компонентите, ръководейки се от него.
Източник: habr.com
