Водих лекция на темата инфраструктура на “дни на софтуерната архитектура”. Нещо не се хареса на публиката, но се надявам да е по-смилаема в текстов вид.
Ето и презентацията.
[slide 2 Кой съм аз]
Здравейте.
Казвам се Васил Колев и съм дошъл да ви говоря по темата за инфраструктурата.
Аз съм системен администратор, и тук на слайда може да видите някакво описание на нещата, с които съм се занимавал. Като цяло съм си играл и с малки и големи неща, в различни области и съм имал досег с всякакви странни неща, вкл. такива, които ме е срам да си напиша в презентацията (държавни и финансови неща).
Накратко, аз съм човекът, който ще бъде събуден в 4 сутринта, ако нещо не работи. Това ми докарва голямо желание всичко да работи максимално безпроблемно, защото аз доста обичам да спя…
[slide 3 За какво ще ви говоря]
Това е кратък списък на нещата, за които ще ви говоря днес – всичките са неща, които съм срещал в моята практика и с които съм се борил (и още се боря). Повечето от тях са тривиални за решаване, някои не толкова, но като цяло всички продължават да се срещат, въпреки че поне някои от тях са известни от десетилетия.
[slide 4 За какво няма да говоря]
Storage в последните години толкова много се променя, че каквото и да ви говоря за него, може да се окаже съвсем грешно утре.
Също така няма да ви говоря какво е това облак и колко е полезно, велико, важно и т.н.. Ако се интересувате, написал съм отдолу дефиницията…
[slide 5 Какво имам в предвид под “state”]
И ще искам да изясня един-два термина, които ще споменавам от време на време в лекцията си.
Първото е “state”, на български “състояние” – това са всичките данни на една система, реално погледнато всичко без кода и хардуера. Това е важната част на системата за нейните потребители, другите части са интересни на вас и на мен. Този state създава доста проблеми и сигурно щеше да е много по-лесно, ако го нямаше…
[slide 6 Какво е инфраструктура?]
Терминът “инфраструктура” буквално значи “структурата отдолу”, т.е. това, на което се крепите (например подът и столовете в залата са инфраструктура, както и климатизацията и, електричеството и т.н.). Може да мрежите, които ползвате, може да е директно разни услуги (всичките “cloud” неща могат да се кръстят “infrastructure as a service”), но е нещо, което не контролирате пряко и на което разчитате.
То може да е и на сравнително високо ниво, като база данни, web или application сървър, в зависимост от това вие къде се намирате.
Нещата, за които ще говоря важат за по-голямата част от тази инфраструктура, като една прилична част се отнася нивото към сървърите, в/у които работите.
[slide 7 CAP теорема]
И нещо доста важно, т.нар. CAP теорема – която казва, че ако имате дистрибутирана система за съхраняване на state (т.е. например база данни), тя може да има само две от следните три свойства: консистентност (данните да са същите и на двете места), достъпност (винаги да работи) и partition tolerance (да може да работи при разцепване). Има някои подробности по дефиницията на консистентност и доколко съвпада с дефиницията от реалния свят, но за нашите цели теоремата важи в този си вид, и накратко ни казва “трудно е да се направи дистрибутирана база данни”.
[slide 8 ]
И нека покажа две примерни системи. Ето така изглеждат на хартия стандартните архитектури на разни услуги, да речем web-базирани.
Забелязвате ли как съм кръстил схемите “сферичен кон”? Това, защото в реалния свят системите изглеждат ето така:
[slide 9 ]
Това е система, чиято схема рисувах при последния проект, с който се занимавах. Така изглеждат нещата в реалния свят, на нормален не-особено-голям проект, който си е растял органично няколко години – не е нещо, което е направено зле, писано е както трябва от умни хора.
[slide 10 Каква е моята работа]
Та, моята работа е да карам ето такива системи да работят, като им планирам и изграждам така инфраструктурата, че да преживяват всякакви проблеми. Около това съм се нагледал на проблемите, за които ще ви говоря.
[slide 11 ]
Single points of failure са може би основния проблем, който съм виждал в практиката.
[slide 12 ]
На този пример можем да видим системи, които са в общи линии изградени от такива точки – отпадането на който и да е компонент или връзка ще спре работата на системата. Това в общи линии е стандартното нещо, което съм виждал.
Класическият случай на това е всички тези компоненти да са в/у един сървър, в който случай целия му хардуер и софтуер се превръщат в нещо, чието отпадане убива услугата.
[slide 13 Как се създават в софтуера]
Единствената причина да съществуват е, че в тях има local state. Ако нямате потребителски данни в някой компонент, е тривиално да го дублирате и скалирате почти колкото си искате, без да имате сериозни проблеми.
На теория това трябва да е просто – всичките учебници казват да си държите всичките данни в базата. На практика обаче се оказва, че дори сериозни и добри програмисти започват да държат сесии, разни ticket-и и такива неща я локално на диска, я директно в паметта, което от своя страна прави голям проблем съществуването на две копия на компонента.
[slide 14 Как се избягват]
Избягването им не е сложно. След като се запознаете с CAP теоремата и преживеете ужасните и последици, намирате подходящ компромис за съхраняването на state, изнасяте го там и дублирате останалото.
В наше време това е все по-лесно и по-лесно. В последния проект имаше такъв случай с потребителски сесии и още някакви неща, и се оказа че с 20на реда код проблемът се премахва, като всичките тези данни отидат в базата.
(нищо общо с времената, в които се налагаше да препишем половината код при подобна промяна)
[slide 15 Дистрибутиран/подсигурен state (1/2)]
Дистрибутираният state не е тривиална задача. Това, което винаги работи (и което се използва в крайни случаи, е синхронно репликирано копие на базата (или каквото-там-ни-съхранява данните), което да поеме работата на главното, ако то отпадне. Това върши работа за почти всичко, но не се поддава на скалиране и е подходящо основно ако нямате голямо натоварване на системата. Това е setup, който се среща прилично често при финансови институции, понеже е стар колкото света, много добре разбран и лесно може да се приложни за всичко.
[slide 16 Дистрибутиран/подсигурен state (2/2)]
Вариантът да си смените базата или да я разширите до нещо, което се скалира/дистрибутира е един от най-хубавите, за съжаление и от най-трудните. CAP теоремата ни създава доста ограничения, физиката ни създава други, и в крайна сметка се налага да правим компромиси или да променяме цялостната архитектура. В наши дни има доста eventual consistency бази, както и всякакви NoSQL решения, голяма част от които са си жива подигравка с идеята за бази данни, но някои от тях са сравнително използваеми.
И да, всички искаме една безкрайна SQL ACID база данни, но такава няма – дори това, което ни предлагат за огромни пари има някакви странни ограничения, в които се удряме докато ги ползваме.
Няма да продължавам с въпроса, понеже вероятно може да се запълни едно-семестриален курс с него, вероятно и повече.
[slide 17 Всичко останало]
Та, както казах всичко останало се решава лесно с дублиране и load balancing.
[slide 18 ]
На схемата може да се види един не-толкова-сферичен кон, просто дублирана система, при която няма какво да отпадне, че да ни направи генерален проблем (може би с изключение на връзката към internet :) ).
[slide 19 TL;DR]
Та, няколко кратки извода – ползвайте подходящото място да си държите данните, не се ограничавайте какво да ползвате, и не искайте невъзможното.
[slide 20 ]
Сравнително близък до предния проблем е хоризонталния scaling, т.е. възможността да увеличите колко натоварване може да издържи системата чрез добавяне на още хардуер към нея (вместо с upgrade-ване на текущия).
[slide 21 ]
Ето една тъжна картинка, която показва как производителността на един core на процесор не се увеличава толкова много, колкото едно време, т.е. клоним все повече и повече към повече ядра на процесор, отколкото към по-бързи ядра. Въпреки всичкия research и мъки, физиката и някои математически проблеми ни спират да постигнем по-голяма производителност.
[slide 22 Накъде вървим]
Това ще ни доведе до големи клъстери от памет, без споделена памет, или с бавен достъп до споделената памет. Все повече и повече и инфраструктурата, която се предоставя е такава – забележете как това, което amazon ви предлага са много отделни не-особено-мощни виртуални машини, които обаче може да вдигате в огромни количества.
Може да очаквате да има още по-голяма нужда от паралелизация на нещата, които правите, като не-паралелните ви задачи да трябва да са малки и кратки, за да не се създават ненужни закъснения и забавяния.
Като пример мога да дам, че в момента един от най-бързите архитектурни модели за правене на сървъри, които process-ват много заявки е на принципа на co-routines, познати в windows-кия свят като “fibers”, а в erlang като “actors”. Идеята е, че вместо да имате цели thread-ове, които да изпълняват всеки по дадена задача, имате гигантско количество малки задачки, които се schedule-ват и изпълняват когато им е възможно. В книгата “The performance of open-source applications” има два такива примера (за съжаление на erlang и на haskell), но ако някой се интересува, мога да разкажа за такова решение с човешки езици за програмиране, която трябва да се появи като open-source някой ден.
[slide 23 Какво следва]
Това ни води до няколко важни неща, идващи от паралелното програмиране.
Трябва да избягваме бавните непрекъсваеми процеси и да оптимизираме за латентност. Дълги години слушах “защо да оптимизирам, те компютрите стават по-бързи”, но това вече не е факт, този процес се забавя и вече не можем да разчитаме на закона на Мур да ни спаси.
Трябва да избягваме зависимостите, доколкото можем, както и създаването на bottlenecks – единични компоненти, през които трябва да мине всичко (и които освен това ще са single points of failure).
[slide 24 ]
Нещо, което хората не разбират, са мрежите. Не е като да са много нови, но липсва яснота доколко зависим от тях и къде могат да ни спънат.
[slide 25 Всичко е мрежа]
Например вече всичко е мрежа. От вътрешната архитектура на процесорите, през дънните плати, SATA/SAS – всичките тези технологии представляват някакви мрежи в звездоподобна технология, със switch-ове и router-в тях, по някакви собствени протоколи. Да не говорим, че вече почти нищо не ни е локално (например базата данни ще ви е на същата машина само ако сте много малка услуга).
Като комбинираме това с навлизането на всякаквите облачни услуги, вече остават малко неща, за които да не зависим от мрежата.
[slide 26 List of network fallacies]
И ще ви говоря по този списък, който е от около 1994та, но по някаква причина не се преподава в училище.
[slide 28 Internet и мрежите не са reliable]
Да почнем от там, че internet и мрежите като цяло не са reliable. Нямате гаранция за нищо, всичко е best-effort и за да постигнете някаквите си нужни гаранции е необходимо да вкарате логика при вас, която да се справя с такива проблеми.
Ужасно важно е да не разчитате много на мрежата. Виждал съм (при едни хора, които са в top 100 на посещаемост в internet) да се синхронизират бази данни през internet, което водеше до всякакви странни неконсистентности и голямо количество логика, което да се бори с тях.
По същия начин ако имате каквато и да е синхронна операция през internet (например да отбележите някъде брояч или да проверите нещо, преди да отговорите на потребителя), това ще ви създава от време на време сериозни проблеми и ще е трудно да се хване откъде идват.
[slide 29 Да забравим латентността]
Дори да нямаме проблеми със загуби по мрежата, самата мрежа има някаква латентност в себе си, която освен че не е постоянна, доста често е по-голяма, отколкото сме свикнали при директната комуникация. Тази латентност се натрупва, и често при малка промяна на ситуацията може да ни създаде големи проблеми.
Един прост пример, който мога да дам е, че по принцип SSL протокола прави от 3 до 6 round-trip-а, т.е. отивания на пакети от единия до другия край, за да осъществи връзка. По принцип ако си говорите със сървър в същата държава, едно RTT е под 20ms, и това няма да се усети лесно като проблем, но ако говорите от Европа до Щатите и едно rtt е 150ms, изведнъж тази разлика става наистина осезаема – потребителите започват да усещат забавянето и да не искат да ви ползват сайта. Това може да се реши в софтуера с настройка на самия SSL протокол, но малко хора се занимават да му обръщат внимание.
[slide 30 Твърде много данни по твърде малка тръба]
Трудно ще намерите мрежа, която да достига скоростта на копирането в паметта. Лоша идея е да се опитвате да прекарвате много данни от едно място, или като цяло да прекарвате ненужно количество данни през някакво тясно място от мрежата.
Има неща от типа на remote dma – т.е. да накарате две системи да си предават данни, вместо да ги копирате през себе си. Може да погледнете bittorrent протокола и какво умее – в момента това е най-бързия начин за прехвърляне на информация до много места в internet.
[slide 31 Голи данни по пробита мрежа]
За всички, които не знаят кой е Edward Snowden, моля да отворят wikipedia и да погледат. За останалите – вече се видя колко тривиално се подслушват данни, т.е. и обществото го знае, та няма нужда да обясняваме на всички колко страшни са мрежите и можем да заделим нужния ресурс да криптираме.
На теория трябва да се криптират само важните данни, но понеже няма начин да се определи кои данни са важни, е далеч по-лесно и по правилно да се криптира всичко.
Също така, криптирането изисква познаване на схемите и криптографията като цяло, и препоръчвам всички да се запознаете с подробностите – нещата не са прости, но могат да ви докарат неочаквани екстри, като например силна проверка за валидността на данните.
[slide 32 Мрежата ще отговаря на всичките ви изисквания]
Мрежата е голямо животно. Не цялата се контролира от едно място и рядко е достатъчно хомогенна, за да предполагате какво може. Препоръчвам ви да знаете какво се случва с нея и как изглежда, преди да проектирате нещо, което трябва да работи в нея.
[slide 33 … и т.н.]
Мрежите са нещо прекрасно, те са бъдещето, пътя, истината и живота :) Могат много да ви помогнат, могат да ви съсипят и е важно да ги познавате в подробности.
[slide 34 ]
И ще поговоря малко по темата дизайн на системи, понеже има голямо отношение към инфраструктурата и комуникацията с нея. Ще започна с нещо просто, разликата м/у store&forward архитектури и pass-through.
[slide 35 Дефиниция]
Това е доста често срещан проблем – дали да съхраним данните, или да ги обработваме докато идват. Това зависи от данните и протоколите, но има и сериозно отношение към reliability-то на системата.
[slide 36 Примерна система]
Примерът, който ще дам е с една проста услуга, която получава писма и ги обработва. Можем да я реализираме по два начина – да получим писмото, да го съхраним и да го предаем за обработка, или да започнем да го предаваме, докато още го получаваме.
[slide 37 Предимства и недостатъци]
Тук и двата начина си имат предимства и недостатъци. Pass-through има по-ниска латентност, което понякога може да се окаже наистина важно, но от друга страна може да се претовари по-лесно и е по-чупливо от store&forward. Pass-through и са по-сложни за дистрибутиране.
[slide 38 Извод]
Pass-through като цяло е доста по-ефективно решение, ако ви е нужна скорост, но препоръчвам store&forward за всичко, което не е критично – може да издържи доста повече на проблеми и по-лесно се възстановява след проблеми.
[slide 40 ]
Ако гледаме дизайна на системи, в общи линии имаме два варианта – линейни и комплексни. В линейните всичко върви в едната или другата посока и няма сложни интеракции, в комплексната има.
[slide 41 (почти) Всичките ни системи са комплексни]
Ние почти нямаме линейни системи в практиката. Всичко, което правим има странни интеракции в него и е огромно като размер. Дал съм пример как Airbus A380 без да броим софтуера има 4 милиона компонента, а само linux kernel-а е над 12 милиона реда код, или ако го компилираме, в себе си има над милион условни прехода.
Ако хванем всичко, което ползваме, ще се окаже гигантско. За сравнение с нещо, което повече разбираме – един двигател с вътрешно горене е под 1000 компонента (даже като броим всяко винтче и гайка).
[slide 42 Тъжни факти]
Ние на практика нямаме толкова голяма система, която наистина да разбираме, която да може да бъде разбрана от един човек – а от друга страна очакваме, че всеки един програмист може да се справи със съществуващите ни системи. Като комбинираме това, че средно имаме по 10-15 грешки на 1000 реда код в изтествани и release-нати продукти е учудващо, че нещо изобщо работи.
В механичния свят е по-лесно да направим система, с чиито failure mode-ове да сме наясно при отпадане на един или два компонента – например сме сравнително наясно с двигателите на колите, но реално погледнато всичко по-сложно от това ни е сложно.
[slide 43 Може да се мине със следния цитат]
Винаги обичам да давам този цитат. За който не е запознат, Jim Gray е човекът, измислил голяма част от теорията за backend-ите на базите данни, които ползваме в наши дни (например цялата ACID идея).
[slide 44 Изроди]
Изводите мисля, че са ясни, нека да правим прости неща, доколкото можем, и да се стремим към простота. Неколкократно съм виждал система да бъде реализирана добре в 10к реда код, и лошо в 100к, и си мисля, че си заслужава усилието да правим минималистично нещата.
[slide 45 ]
И ще приключа с нещо наистина страшно.
[slide 46 Какво е bitrot]
Bitrot е повреждането на данните, без вие да знаете. Очаквате, че нещо се е записало правилно, че е ок, след което го прочитате и доста пъти дори без да разберете в него има някакви повреди.
[slide 47 Статистика от wikipedia]
От wikipedia може да видите ето тези прости статистики, които са доста неприятни, но не са достатъчно близки до вас, за това ето няколко, с които директно съм се сблъскал.
[slide 48 Случки от реалния свят]
В една система с 395 милиона файла имаха около 300 грешки в рамките на месец, и в последната седмица – 3 (това беше проверка по sha1 checksum-а на файловете). Това са свестни, малко по-стари сървъри, с raid5 масиви, а софтуерът е съвсем малко и много внимателно изтестван и прегледан – и все пак се появяват такива проблеми. Скоро 1PB няма да е огромно число, а нещо нормално и очаквано, и можем да очакваме при всички ни да се видят такива проблеми.
И нещо още по-неприятно – едни приятели имат софтуер, на който правят сериозни regression test-ове. В техния lab се появили някакви crash-ове, които се появявали по странно време без никаква логика, и след известно време открили, че това се случва само на сървърите без ECC памет. Машините имали 16-32 GB памет и не са били купувани на битака :)
[slide 49 row-hammer exploit]
И, нещо съвсем скорошно, т.нар. row-hammer exploit, че ако пишете много някъде по паметта, можете да flip-нете бит на друго място. С това даже бяха успели да напишат и exploit, с който да може през browser-а и sandbox-а му да се излезе и да се получи root достъп.
[slide 50 Инструкции за цивилното население]
Това е ужасно.
Няма хубаво решение на тия проблеми – реално няма много research как да работим със счупен хардуер – но трябва да знаем, че ще ни се случи. Решенията, които имам са съвсем малко (освен първото) – максимално прости системи, failfast – т.е. да се спира при първия възможен шанс, ако се открие нещо такова, и run-time проверки за коректност (познати ви като assert-и), за да може да се ограничат последиците от проблема.
[slide 51 ]
И няколко книги, които могат да са ви полезни:
* “The Architecture of Open-source Applications”, vol. 1 и 2
* “The Performance of Open-source Applications”
* “Beautiful Data”
* “Normal Accidents: Living with High-Risk Technologies”, Charles Perrow
* “Cryptography Engineering”, Schneier, Ferguson & Konho