Особливості архітектури розподіленого сховища Dropbox



Увазі читачів «Хабрахабра» подається розшифровка відеозапису (в кінці публікації) виступи В'ячеслава Бахмутова на сцені конференції HighLoad++, що пройшла в підмосковному Сколково 7-8 листопада минулого року.

Мене звуть Слава Бахмутов, я працюю в Dropbox. Я Site Reliability Engineer (SRE). Я люблю Go і просуваю його. З хлопцями ми записуємо подкаст golangshow.

Що таке Dropbox?
Це хмарне сховище, в якому користувачі зберігають свої файли. У нас 500 мільйонів користувачів, у нас більше 200 тисяч бізнесів, а також величезну кількість даних і трафіку (більш 1.2 млрд нових файлів в день).

Концептуально, архітектура — це два великих шматки. Перший шматок — це сервер метаданих, у ньому зберігається інформація про файли, зв'язку між файлами, інформація про користувачів, якась бізнес логіка, і все це пов'язано з базою даних. Другий великий шматок — це блоковий storage в якому зберігаються дані користувачів. Спочатку, в 2011 році, всі дані зберігалися в Amazon S3. У 2015 році, коли нам вдалося перекачати всі эксабайты до себе, ми розповіли про те, що написали свій хмарний storage.

Ми назвали його Magic Pocket. Він написаний на Go, частково на Rust і досить багато на Python. Архітектура Magic Pocket, він крос-зоный, є кілька зон. В Америці це три зони, вони об'єднані в Pocket. Є Pocket в Європі, з американським він не перетинається, тому що жителі Європи не хочуть, щоб їхні дані були в Америці. Зони між собою реплікують дані. Всередині зони є осередки. Є Master, який керує цими осередками. Є реплікації між зонами. У кожній клітинці є Volume Manager, який стежить за серверами, на яких зберігаються ці дані, там досить великі сервера.

На кожному із серверів це все об'єднано в bucket, bucket – це 1 GB. Ми оперуємо bucket'ами, коли перекидаємо дані куди-то, коли-то видаляємо, очищаємо, дефрагментируем, тому що самі блоки даних, які ми зберігаємо від користувача, — це 4 MB, та оперувати ними дуже складно. Всі компоненти Magic Pocket добре описані в нашому технічному блозі, про них я не буду розповідати.

Я буду розповідати по архітектуру з точки зору SRE. З цієї точки зору дуже важлива доступність і збереження даних. Що ж це таке?

Доступність — це складне поняття та обчислюється для різних сервісів по-різному, але зазвичай це відношення кількості виконаних запитів до загальної кількості запитів, зазвичай описується дев'ятками: 999, 9999, 99999. Але по суті це певна функція від часу між катастрофами або якимись проблемами до часу за скільки ви полагодите це проблему. Неважливо як ви це полагодите, в продакшне або просто откатите версію.

Що таке збереженість?
Як ми обчислюємо збереження? Ви берете якісь дані і зберігати їх на жорсткий диск. Далі ви повинні дочекатися поки вони синхронізуються на диск. Це смішно, але багато користуються nosql-рішеннями, які просто пропускають цей крок.

Як вираховується збереження?
Є AFR – це річна частота помилок дисків. Є різні статистики щодо того, які помилки бувають, наскільки часто бувають помилки в тих чи інших жорстких дисках. Ви можете ознайомитися з цими статистиками:



Далі ви можете копіювати ваші дані на різні жорсткі диски щоб підвищити durability. Ви можете використовувати RAID, ви можете тримати репліки на різних серверах або навіть в різних дата-центрах. І якщо ви порахуєте ланцюгами Маркова, наскільки ймовірність втрати одного байта, то у вас вийде щось близько 27 дев'яток. Навіть на масштабах Dropbox, де у нас эксабайты даних, ми не втратимо жоден байт в найближчому майбутньому практично ніколи. Але все це ефемерне, що будь-яка помилка оператора, або логічна помилка в коді та даних немає.

Як же ми насправді покращуємо доступність і збереження даних?
Я поділяю це на 4 категорії, це:

  1. Ізоляція;
  2. Захист;
  3. Контроль;
  4. Автоматизація.
Автоматизація – це дуже важливо.

Ізоляція буває:

  • Фізична;
  • Логічна;
  • Експлуатаційна.
Фізична ізоляція. На масштабах Dropbox або компанії на зразок Dropbox нам дуже важливо спілкуватися з дата-центром, ми повинні знати, як наші сервіси розташовуються всередині дата центру, як до них підводиться енергія, яка мережева доступність у цих сервісів. Ми не хочемо в одній стійці тримати сервіси баз даних, які нам потрібно постійно бэкапить. Припустимо, кожен бекап у нас 400 Мбіт/с, і у нас просто каналу не вистачить. Чим глибше ви йдете в цей стек, тим дорожче ваше рішення, і тим воно стає складніше. Наскільки низько спускатися — це ваше рішення, але, само собою, ви не повинні класти всі репліки ваших баз даних в одну стійку. Тому що енергія відключиться і у вас баз даних немає більше.

Можна подивитися на все це в іншому вимірі, з точки зору виробника обладнання. Дуже важливо користуватися різними виробниками обладнання, різними прошивками, різними драйверами. Чому? Хоч виробники обладнання і кажуть, що їх рішення надійні, насправді вони брешуть, і це не так. Добре хоч не вибухають.

Виходячи з усього цього, важливо критичні дані класти не тільки у себе десь в бэкапах на своїй інфраструктурі, але і в зовнішній інфраструктурі. Наприклад, якщо ви у хмари Google, то важливі дані кладете в Amazon, і навпаки. Тому що якщо ваша інфраструктура згасне, то бекап буде взяти нізвідки.

Логічна ізоляція. Про неї практично все всі знають. Основні проблеми: якщо один сервіс починає створювати якісь проблеми, то інші сервіси теж починають відчувати проблеми. Якщо баг був у коді в одного сервісу, то цей баг починає поширюватися на інші сервіси. У вас починають надходити неправильні дані. Як з цим впоратися?

Слабка зв'язаність! Але це дуже рідко працює. Є такі системи, які не слабо пов'язані. Це бази даних, ZooKeeper. Якщо у вас велике навантаження пішла на ZooKeeper, у вас кластер кворум впав, то він весь впав. З базами даними приблизно те ж саме. Якщо велике навантаження на master, то швидше за все весь кластер впаде.

Що ми зробили в Dropbox?


Це високорівнева діаграма нашої архітектури. У нас є дві зони, і між ними ми зробили дуже простий інтерфейс. Це практично put і get, це саме для storage. Це було дуже складно для нас, тому що ми хотіли зробити все складніше. Але це дуже важливо, тому що всередині зон все дуже складно, там і ZooKeeper, і бази даних, кворуми. І це все періодично падає, прям все відразу. І щоб це не захопило інші зони, між ними є цей простий інтерфейс. Коли одна зона падає, то друга швидше за все буде працювати.

Експлуатаційна ізоляція. Як би ви добре не розкидали ваш код з різних серверів, як би добре його логічно ні ізолювали, завжди знайдеться людина, яка зробить щось не так. Наприклад, в «Однокласниках» була проблема: людина розкотив на всі сервери Bash shell, в якому щось не працювало, і всі сервера відключилися. Такі проблеми теж бувають.

Ще жартують, що якби всі програмісти і системні адміністратори пішли куди-небудь відпочивати, то система працювала набагато стабільніше, ніж коли вони працюють. І це дійсно так. Під час фризів багато компаній мають таку практику робити заморожування перед новорічними канікулами, система працює набагато стабільніше.

Контроль доступу. Реліз процес: все це активно тестуєте, потім тестуєте на staging, який використовує, наприклад, ваша компанія. Далі ми викладаємо зміни в одну зону. Як тільки ми впевнилися, що все нормально, ми розкладаємо на інші дві зони. Якщо щось не нормально, то ми з них реплицируем дані. Це все стосується storage. Продуктові сервіси ми постійно оновлюємо, раз на день.

Захист. Як же нам потрібно захищати наші дані?
Це валідація операцій. Це можливість відновити ці дані. Це тестування. Що таке валідація операцій?

Найбільший ризик для системи — це оператор.
Є історія. Маргарет Гамільтон, яка працювала в програмі «Аполлон», в якийсь момент під час тестування до неї прийшла донька й повернула якийсь важіль, і вся система навернулася. Цей важіль очищав систему навігації цього «Аполлона». Гамільтон звернулася в НАСА і сказала, що є така проблема, запропонувала зробити захист. Якщо корабель летить, то не буде очищати дані в цьому кораблі. Але в НАСА сказали, що астронавти — професіонали, і вони ніколи не повернуть цей важіль. В наступному польоті один з астронавтів ненавмисно цей повернув важілець, і все очистилося. У них було чітке опис проблеми, і вони змогли відновити ці навігаційні дані.
У нас був схожий приклад, у нас є такий інструмент, який називається JSSH, це такий distributed shell, який дозволяє виконувати команди на безлічі серверів.



По суті, нам в цій команді потрібно виконати на серверах memcache, який знаходиться в стані перевстановити, тобто вони вже не працюють, і нам їх потрібно оновити. Нам потрібно виконати скрипт upgrade.sh. Зазвичай це все робиться автоматично, але іноді потрібно вручну це робити.

Тут є проблема, це потрібно було зробити все лапках:



Так як це все було без лапок, то у нас для всіх memcach'їй встановився аргумент lifecycle=перевстановити і вони всі перезавантажити. Це не дуже добре позначилося на сервісі. Оператор не винен, всяке буває.

Що ми зробили?
Ми поміняли синтаксис команди (gsh), щоб не було більше таких проблем. Ми заборонили виконувати деструктивні операції на живих сервісах (DB, memcache, storage). Тобто нам не можна ні до жодному разі перезавантажити базу даних, не зупинивши її і не забравши її з production, так само з memcache. Всі такі операції ми намагаємося автоматизувати.



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

Другий приклад. Це SQLAlchemy. Це бібліотека для Python для роботи з базами даних. І в ній для update, insert, delete є такий аргумент, який називається whereclause. У ньому ви можете вказати що ви хочете видалити, що ви хочете апдейт. Але якщо ви туди передасте не whereclause, а where, sqlalchemy нічого не скаже, він просто видалить всі без where, це дуже велика проблема. У нас є кілька сервісів, наприклад, ProxySQL. Це проксі для MySQL, який дозволяє заборонити багато деструктивні операції (DROP TABLE, ALTER, updates без where тощо). Так само в цьому ProxySQL можна зробити throttling і для тих запитів, які ми не знаємо, обмежити їхню кількість, щоб розумний запит не поклав нам master випадково.

Відновлення. Дуже важливо не тільки створювати бекапи, але і перевіряти, що ці бекапи відновляться. Facebook нещодавно виклав статтю, де вони розповідають, як вони постійно роблять бекапи і постійно відновлюються з цих бекапів. У нас по суті все теж саме.
Ось приклад з нашого Orchestrator, за якийсь короткий відрізок часу:



Видно, що у нас постійно робляться клони баз даних, тому що у нас на одному сервері до 32 баз даних розташоване, ми постійно їх переміщаємо. Тому у нас постійно відбувається клонування. Постійно promotion йдуть в master і slave і т. д. Так само величезна кількість бекапів. Ми бэкапимся до себе, також в Amazon S3. Але у нас також постійно відбувається recovery. Якщо ми не перевіримо, що кожна база даних, яку ми забекапілі, може відновитися, то по суті у нас цього бекапа немає.

Тестування
Всі знають, що корисно і юніт-тестування, і інтеграційне тестування. З точки зору доступності, тестування — це MTTR, час для відновлення, воно по суті дорівнює 0. Тому що ви цей баг знайшли не production, а до production і пофиксили його. Availability не прогнулося. Це теж дуже важливо.

Контроль
Хтось завжди напартачить: або програмісти, або оператори щось зроблять не так. Це не проблема. Потрібно мати можливість знаходити і виправляти це все.

У нас для storage існує величезна кількість верифікаторів.



Насправді їх 8, а не 5, як тут. У нас коди верифікаторів більше кодів самого storage. У нас 25% внутрішнього трафіку — це верифікації. На самому низькому рівні працюють disk scrubber'и, які просто читають блоки з жорсткого диска і перевіряють контрольні суми. Чому ми це робимо? Тому що жорсткі диски брешуть, S. M. A. R. T. брешуть. Виробникам невигідно, щоб S. M. A. R. T знаходив помилки, тому що їм доводиться повертати ці жорсткі диски. Тому це потрібно завжди перевіряти. І як тільки ми бачимо проблему, ми намагаємося відновити ці дані.

У нас є trash inspector. Коли ми видаляємо, або переміщуємо, або щось деструтивное робимо з даними, ми спочатку кладемо ці дані в якусь кошик і потім перевіряємо ці дані, чи справді ми хотіли їх видалити. Зберігаються вони там два тижні, наприклад. У нас capacity на це двотижневе час видалених даних, це дуже важливо, тому ми витрачаємо на це гроші. Ми так само постійно частина трафіку, який приходить у storage, зберігаємо ці операції у Kafka. Потім ці операції повторюємо вже на storage. Ми звертаємося до storage як до blackbox, щоб подивитися, чи дійсно там є дані трафіку, який до нас прийшов, і ті, які записалися, ми їх можемо забрати.

Ми постійно перевіряємо, що дані среплицированны з одного storage на інший, тобто вони відповідають один одному. Якщо дані є в одному storage, вони зобов'язані бути в іншому storage.

Верифікатори — це дуже важливо, якщо ви хочете домогтися високого і якісного durability.

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

Тому дуже важливо проводити Disaster Recovery Testing (DRT). Що це таке? Це коли ви, наприклад, повністю відключаєте якийсь внутрішній сервіс, від якого залежать інші сервіси, і дивіться, а чи змогли ви визначити, що у вас щось не працює, або ви вважаєте, що все чудово. Чи змогли ви зреагувати на це швидко, полагодити, відновити це все. Це дуже важливо, тому що ми ловимо проблеми у production. Production відрізняється від staging тим, що там зовсім інший трафік. У вас просто інша інфраструктура. Наприклад, у вас в staging може бути на одному реке одна кількість сервісів, і вони різні, наприклад, веб-сервіс, база даних, storage. А в production це може бути зовсім інша.

У нас була така проблема з-за того, що у нас база даних стояла з веб-сервісами, ми не могли вчасно її забекапіть. За цим теж потрібно стежити.

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

Якщо чергові знають, що буде якась проблема, то вони до цього готові. Вони знають, як це відновити в разі реальної проблеми. Це потрібно регулярно робити і в production.

Автоматизація
Найважливіша річ. Коли у вас кількість серверів зростає лінійно або експоненціально, то кількість людей не народжується лінійно. Вони народжуються, навчаються, але з якоюсь періодичністю. І ви не можете збільшувати кількість людей рівнозначне кількості ваших серверів.

вам потрібна автоматизація, яка буде виконувати роботу за цих людей.

Що таке автоматизація?
В автоматизації дуже важливо збирати метрики з вашої інфраструктури. Я практично нічого не говорив про метрики в своїй доповіді, тому що метрики — це ядро вашого сервісу і про нього згадувати не треба, тому що це найважливіша частина вашого сервісу. Якщо у вас немає метрик, ви не знаєте, як працює ваш сервіс чи ні. Тому дуже важливо збирати метрики швидко. Якщо, наприклад, у вас метрики збираються раз в хвилину, а проблема у вас в хвилині, то ви про неї не дізнаєтеся. Також дуже важливо швидко реагувати. Якщо людина реагує, наприклад, у нас пройшла хвилина, коли щось сталося, щось закладається на те, що баг був у метриці, вам приходить alert. У нас policy протягом 5 хвилин. Ви повинні почати щось робити, реагувати на alert в цей час. Ви починаєте щось робити, починаєте розбиратися, по суті у вас проблема вирішується в середньому 10-15 хвилин, в залежності від проблеми. Автоматизація дозволяє вам прискорити, але не вирішити, в тому плані, що вона дає інформацію про цю проблему до того, як ви займетеся вирішенням даної проблеми.

У нас є такий інструмент Naoru — параноїдальна автоматизація.



Автоматизація складається з декількох компонентів.

Вона складається з якихось алертів, які приходять. Це може бути простий пайтоновский скрипт, який коннектітся до сервера і перевіряє, що він доступний. Це може приходити з Nagios або Zabbix, неважливо що ви використовуєте. Головне, щоб це приходило швидко. Далі ми повинні зрозуміти, що з цим робити, ми повинні продіагностувати. Наприклад, якщо сервер недоступний, ми повинні спробувати підключитися за SSH, підключиться за IPMI, подивитися, якщо немає відповіді, він завис або ще щось, ви прописуєте якесь лікування.

Далі, коли ви пишіть автоматизацію, все повинно пройти через оператора. У нас є policy, що ми будь-автоматизацію, приблизно 3-6 місяців вона вирішується через оператора.

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

Тут існує велика проблема, це лінь операторів. Вони починають автоматизувати автоматизацію. Вони автоматично вставляють сюди yes:



Тому певний час ми писали не yes/no, а: «Так, я дійсно хочу те-то, те-то». В різному регістрі, рандомно перевіряли що це дійсно консоль. Це дуже важливо.

Також важливо мати хуки. Тому що не все завжди можна перевірити. Хуки можуть бути дуже простими, наприклад, якщо на сервері запущено ZooKeeper, то нам потрібно перевірити, що всі члени групи працюють (скрипт з помилками), по суті, ми просто перевіряємо щось:



Ці хуки розташовані по всьому flow, скрізь після алертів є хуки, в діагностичному плагіні і т. д. Ви можете для вашого сервісу створити свій хук.

Далі йде вирішення проблеми:



Воно може бути дуже простим. Наприклад, перезапустити сервер. Запустити puppet, chef і т. д.

Це Naoru. Це reactive remediation. Що дозволяє нам реагувати на подію дуже швидко і лагодити їх дуже швидко.

Також є open-source рішення. Найпопулярніший — це StackStorm від російських хлопців. У них дуже гарне рішення, досить популярне. Також це можна зробити своїми рішеннями на зразок riemann або OpsGenie і т. д.

Є рішення пропрієтарні. Від Facebook це FBAR (facebook auto-remediation). Nurse від Linkedin'a. Вони закриті, але вони постійно розповідають про ці рішення. Наприклад, Facebook нещодавно робив доповідь про проблему перенесення всієї стійки через свій інструмент.
Але автоматизація буває не тільки реактивна (тут і зараз), але і автоматизація, яку потрібно зробити за довгий проміжок часу. Наприклад, нам потрібно оновити 10 тисяч серверів, перезавантаживши їх. Наприклад, потрібно оновити драйвер, ядро і ще щось зробити. Це може зайняти великий проміжок часу: місяць, рік і т. д. реактивної системі не можна це постійно контролювати.

Тому у нас є ще така система Wheelhouse.

Є схема, як вона працює. Зараз я розповім, що там.



По суті у нас є кластер бази даних, у якій є один master. A і є два slave. Нам потрібно замінити цей master, наприклад, ми хочемо оновити ядро. Щоб його замінити нам потрібно запромоутить slave, депромоутить той master, видалити його. У нас є вимога в Dropbox, у нас в кластері завжди повинно бути два slave для такої конфігурації. У нас є певний стан цього кластера. HostA знаходиться у production, він master, він ще не звільнений, кількість slave у нас два, але нам потрібно три для цієї операції.

У нас є якась стейт-машина, яка все це робить.



З replace_loop (синя стрілочка) ми бачимо, що наш сервер знаходиться в production, і нам не вистачає slave'ів, і ми переходимо в стан виділити ще slave. Ми приходимо в цей стан, ми ініціюємо роботу створити новий клон з master'новий a slave. Це запускається десь в Orchestrator, ми чекаємо. Якщо робота завершена, і все добре, то ми переходимо до наступного кроку. Якщо був fail, то переходимо в стан failure. Далі ми додаємо в цей хост новий slave у production, теж ініціюємо в Orchestrator цю роботу, чекаємо і повертаємося в replace_loop.

У нас тепер приблизно така схема:



Один хост — це master, є три slave. Ми задовольняємо умова що нам вистачає slave'ів. Після цього ми переходимо в стан promote.



Насправді це depromote. Тому що нам потрібно master зробити slave'ом, а якийсь slave master'ом. Тут все теж саме. Додається робота в Orchestrator, перевірятися умова і так далі, решта кроки приблизно такі ж. Ми після цього видаляємо master з production, прибираємо з нього трафік. Видаляємо master в інсталятор, щоб люди, які займаються цим сервером, могли щось оновити на ньому.

Цей блок дуже маленький, але він бере участь у більш складних блоках. Наприклад, якщо нам потрібно перенести цілу стійку в іншу стійку, тому що драйвера на свитчах змінюються, і нам потрібно просто відключити всю стійку щоб перезавантажити світч. У нас багато описано таких діаграм станів для різних сервісів, як їх гасити, як їх піднімати. Це все працює. Навіть математично можна довести, що у вас система завжди буде перебувати в робочому стані. Чому це зроблено через STM, а не в режимі процедур? Тому що якщо це довгий процес, наприклад, клонування може займати годину або близько того, то у вас може статися щось ще, і стан вашої системи зміниться. У випадку з машиною станів ви завжди знаєте, який стан і як на нього реагувати.

Тепер ми використовуємо це в інших проектах. Ці 4 методу, які ви можете використовувати, щоб підвищувати доступність і збереження ваших даних. Я не розповідав про конкретні рішення, тому що в будь-якій великій компанії рішення пишуть все самі. Ми звичайно використовуємо якісь стабільні блоки, наприклад, для баз даних ми використовуємо MySQL, але зверху ми навернули свій графовый storage. Для реплікації ми використовували semi sync, але зараз у нас своя реплікація, щось на зразок Apache BookKeeper, також proxdb ми використовуємо. Але в цілому всі рішення у нас свої, тому я не згадував про них, тому що вони швидше за все не схожі на ваші. Але ось ці методи ви можете використовувати з будь-якими вашими рішеннями, з open source чи ні. Щоб поліпшити вашу доступність.

На цьому все, спасибі!


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.