16.09.2010

Як мы рабілі GitHub хутчэй

* Source text URL: http://github.com/blog/530-how-we-made-github-fast



Зараз, калі рэчы аселі ад пераезду ў Rackspace, я жадаў бы заняць некаторы час, каб перайсці на архітэктурныя змены, якія мы зрабілі для таго, каб прынесці вам хутчэй, больш якая маштабуецца GitHub.

На мой першы праект гэтага артыкула я правёў шмат часу, тлумачачы чаму мы зрабілі кожны з тэхналагічных варыянтаў, што мы зрабілі. Праз некаторы час, аднак, стала цяжка адлучыць ад архітэктуры дыскурс і ўсё гэта стала ў памылку. Таму я вырашыў проста растлумачыць архітэктуры, а затым напісаць серыю і сачыць за пасадай больш падрабязнага аналізу, менавіта таму мы зрабілі выбар, які мы зрабілі.

Ёсць шмат шляхоў для пашырэння сучасных вэб-прыкладанняў. Я буду тут апісваць метад, які мы абралі. Гэта ніякім чынам не павінна разглядацца як адзіны спосаб для пашырэння ўжывання. Лічыце, што гэта прыклад таго, што працуе для нас з улікам нашых спецыфічных патрабаванняў.

Разуменне пратаколаў

Мы паказваем тры першасных пратакола для канчатковых карыстачоў GitHub: HTTP, SSH і Git. Пры праглядзе сайта з вашага каханага браўзара вы выкарыстоўваеце HTTP. Калі ав штампуеце, цягнеце, пхаеце ці штурхаеце URL падобны git@github.com: mojombo/jekyll.git, вы робіце гэта праз SSH. Пры штампоўцы ці выцягванні з грамадскага сховішча праз URL падобны git://github.com/mojombo/jekyll.git вы выкарыстоўваеце пратакол Git.

Самы просты спосаб зразумець архітэктуру, адсочваць якім чынам кожная з гэтых просьбаў распаўсюджваецца праз сістэму.

Адсочванне HTTP запыту

Для гэтага прыкладу я пакажу, як ў http://github.com/mojombo/jekyll адбываецца запыт на дрэвападобную старонку.

Першай рэччу хіты запыту пасля зніжэння з Інтэрнэту з’яўляецца актыўны балансіроўшчык загрузкі. Для рашэння гэтай задачы мы выкарыстоўваем пары выпадкаў Xen, які працуе ў ldirectord. Яны завуцца lb1a і lb1b. У любы момант часу адзін з іх з’яўляецца актыўным, а іншы чакае, каб узяць на сябе ў выпадку няўдачы ў майстра. Нагрузка не робіць нічога фантазійнага. Яна перадае TCP пакеты на розных серверах у залежнасці ад запытанага IP і порту, і можа выдаліць няправільныя серверы з балансу басейна ў выпадку неабходнасці. У выпадку, калі няма сервера, даступнага для дадзенага басейна, то можа служыць просты статычны сайт, а не адмовы ад падлучэння.

Для запытаў да асноўнага вэб-сайту балансировщик загрузкі адпраўляе запыт да адной з чатырох машын фронтэнда. Кожны з іх 8 асноўных, 16GB RAM чысты сервер металу. Іх імёны fe1, …, fe4. Nginx прымае злучэнне і пасылае яе на даменны сокет Unix, на якой 16 Unicorn працоўных працэсаў, якія выбіраюцца. Адзін з гэтых працоўных хапае запыт і запускае Rails код, неабходны для яго выканання.

Шматлікія старонкі патрабуюць пошуку ў базе дадзеных. Наша база дадзеных MySQL працуе на дзвюх асноўных 8, 32 ГБ аператыўнай памяці сервероў на голых металах з 15K RPM SAS дыскаў. Іх імёны db1a і db1b. У любы момант часу адзін з іх майстраў, і адзін раб. MySQL рэплікацыя выконваецца з дапамогай DRBD.

Калі старонка патрабуе, каб інфармацыя пра рэпазітара і гэтыя дадзеныя не кэшуюцца, то ён будзе выкарыстоўваць нашу Grit бібліятэку для вымання дадзеных. Для таго, каб улічыць нашы Rackspace усталёўкі, мы зменім Grit і зробім нешта асаблівае. Пачнём з таго, что абстрагаванні з кожнага выкліку, якому патрабуецца доступ да файлавай сістэмы ў Grit:: Git аб’екта. Затым заменім Grit:: Git з незавершанай версіі, што робіць RPC выклікі нашага сэрвісу “Smoke”. “Smoke” мае прамы доступ да дыска ў сховішчах і ў сутнасці ўяўляе Grit:: Git у якасці службы. Яна завецца “Дым”, таму што дым проста “Песок” у воблаку. Разумееш?

Патушанны Grit робіць RPC выклікі smoke, які з’яўляецца балансаваннем нагрузкі, што робіць імя карты да fe машын. Кожны інтэрфейс працуе ў 4 ProxyMachine выпадках за HAProxy, якія выступаюць у якасці давераных асоб для маршрутызацыі званкоў “Smoke”. ProxyMachine утрыманне маіх кніг ведаюць (пласт 7) TCP проксі-маршрутызацыі, што дазваляе нам пісаць логіку маршрутызацыі ў Ruby. Проксі аналізуе запыт і здабывае імя карыстача са сховішча, які быў паказаны. Затым мы выкарыстоўваем уласныя бібліятэкі пад назвай комінаў (гэта маршруты паліць!) для пошуку шляху для гэтага карыстача. Маршрут карыстача – проста імя файлавага сервера, на якім сховішчы гэтага карыстача захоўваюцца.

Чимни лічыць, што маршрут, зрабіўшы выклік Redis, працуе на серверах баз дадзеных. Мы выкарыстоўваем Redis як устойлівы ключ/значэнне, сховішча для маршрутызацыі інфармацыі і мноства іншых дадзеных.

Пасля таго, як проксі- Smoke вызначыў маршрут карыстача, ён усталёўвае празрысты проксі-сервер для належнага файл-сервера. У нас ёсць 4 пары файлавых сервераў. Іх імёны fs1a, fs1b, …, fs4a, fs4b. Гэтыя восем асноўных 16GB RAM голых сервераў металу, кожная з якіх 6 300Гб 15K RPM SAS дыскыў размешчаны ў RAID 10. У любы момант часу адзін сервер у кожнай пары з’яўляецца актыўным і іншых чакае, каб узяць на сябе фатальную няўдачу майстра. Усе сховішчы дадзеных увесь час репліцыруюцца з галоўнага сервера на падпарадкаваны праз DRBD.

Кожны файл сервер апрацоўвае 2 Ernie RPC серверамі за HAProxy. Кожны Ernie спараджае Ruby 15 працоўных. Гэтыя працоўныя прынімаюць выклік RPC і аднаўляюць, і выконваюць выклік Grit. Адказ адпраўляецца зваротна праз проксі- Smoke Rails прыкладанне, дзе незавершаная Grit вяртае чаканы адказ Grit.

Калі Unicorn сканчае дзеянне Rails, адказ адпраўляецца зваротна праз Nginx і непасрэдна кліенту (выходныя адказы не вярнуцца праз нагрузкі).

Нарэшце, вы бачыце зробленую вэб-старонку!

Вышэй струменя тое, што адбываецца, калі ёсць не траплення ў кэш. У шматлікіх выпадках Rails код выкарыстоўвае Ruby Evan Weaver’а Memcached кліента, каб запытаць Memcache сервера, якія запускаюцца на кожным серверы файл “рабынь”. Бо гэтыя машыны ў адваротным выпадку прастою мы мясцуем 12 Гб Memcache на кожным. Гэтыя серверы, як псеўданімы memcache1, …, memcache4.

BERT і BERT – RPC

Па нашых дадзеных серыялізацыі і RPC пратаколу мы выкарыстоўваем BERT і BERT – RPC. Вы не чулі пра іх раней, таму што яны новыя. Я прыдумаў іх, таму што я не быў здаволены ні з адным з магчымых варыянтаў, што я ацэніў, і я жадаў эксперыментаваць з ідэяй, якая ў меня была некаторы час. Перад тым, як азнаеміцца з Freak Out о НІЗ сіндром (ці дапаможнік палепшыць Freak Out), азнаёмцеся з маімі суправаджальнікамі-артыкуламі. Уяўляючы BERT і BERT – RPC пра тое, як гэтыя тэхналогіі сталі, і як я маю намер іх вырашаць.

Калі вы не жадаеце проста праверыць спецыфікацыі, то зайдзіце на http://bert-rpc.org.

Для галоднага кода праверце маю Ruby бібліятэку BERT серыялізацыі BERT, мой кліент BERT-RPC Ruby BERTRPC і мой гібрыдны сервер BERT-RPC Erlang/Ruby Ernie.

Адсочванне SSH запыту

Git выкарыстоўвае SSH для зашыфраваных паведамленняў паміж вамі і серверам. Для таго, каб зразумець, як наша архітэктура ўгод з SSH злучэннем, гэта першае, што важна зразумець, як гэта працуе ў простай усталёўкі.

Git абапіраецца на той факт, што SSH дазваляе выконваць каманды на выдаленым серверы. Напрыклад, каманды ssh tom@frost ls -al працуе ls -al у хатнім каталогу карыстача майго frost сервера. Я атрымліваю вынік выканання каманды на маім лакальным тэрмінале. SSH па істоце падлучэння STDIN, STDOUT і STDERR на выдаленай машыне майго лакальнага тэрмінала.

Калі вы працуеце ў камандзе, як git clone tom@frost:mojombo/bert, што робіць за “кулісамі” SSHing на frost, аўтэнтычнасці, як tom, і пасля гэтага выдаленага выканання git upload-pack mojombo/bert. Зараз ваш кліент можа казаць у гэтым працэсе на выдаленым серверы, проста чытаць і пісаць па SSH злучэнні. Выдатна, так?

Вядома, каманды, якія дазваляюць адвольнае выкананне, з’яўляюцца небяспечнымі, таму SSH складаецца з магчымасці абмежавання, каманд, якія могуць быць выкананы. У найпростым выпадку, вы можаце абмежаваць выкананне GIT-абалонкі, якая ўваходзіць у склад Git. Усё гэта робіць скрыпт “праверыць каманду”, якую вы спрабуеце выканаць і пераканайцеся, што гэта адзін з git upload-pack, git receive-pack, ці git upload-archive. Калі гэта сапраўды адзін з тых, то ён выкарыстоўвае Exec, каб замяніць бягучы працэс, а гэта ўжо новы працэс. Пасля гэтага, ён становіцца, як быццам вы толькі што выканалі гэту каманду.

Зараз, калі вы ведаеце, як у Git SSH дзейнічаюць аперацыі працы ў простым выпадку, дазвольце мне паказаць вам, як мы вырашаем гэта ў архітэктуры GitHub.

Па-першае, ваш кліент пачынае Git SSH сесіі. Сувязі спускаюцца праз інтэрнэт, і хіты нашай нагрузкі таксама.

Адтуль, злучэнне адпраўлена ў адзін з абалонкаў, дзе SSHD прымае яго. Мы латаем нашу SSH дэман і выконваем пошук адкрытых ключоў ад нашай базы дадзеных MySQL. Ваш ключ ідэнтыфікуе ваш GitHub карыстача, і гэта інфармацыя адпраўляецца разам з арыгінальнай камандай і аргументамі для нашага уласнага скрыпту Gerve (Git падача). Падумайце о Gerve, як супер разумнай версіі git-shell.

Gerve правярае, што карыстач мае доступ да сховішча, паказаных у аргументах. Калі вы з’яўляецеся ўладальнікам сховішча ці пошуку ў базе дадзеных, то павінны выканаць, у адваротным выпадку некалькі запытаў SQL вырабляецца для вызначэння дазволаў.

Як толькі доступ будзе правераны, Gerve выкарыстоўвае коміна, каб знайсці шляхі для ўладальніка ў сховішча. Цяпер мэтай з’яўляецца выканаць сваю першапачатковую каманду на правільным файл-серверы і гаплікам лакальным кампутары да гэтага працэсу. Што можа быць лепш для гэтага, чым з іншым выдаленым SSH – пакаранне смерцю!

Я ведаю, гэта гучыць дурна, але ён выдатна працуе. Gerve проста выкарыстоўвае exec(3), каб замяніць сябе з заклікам да SSH Git @ <route> <command> <arg>. Пасля гэтага выкліку, кліент падлучаецца да працэсу на франтальнай машыне, якая, у сваю чаргу, падлучае да працэсу на файлавым серверы.

Падумайце пра гэта так: пасля вызначэння мае рацыю доступ і размяшчэнне сховішча, інтэрфейс празрысцее проксі для астатняй часткі сесіі. Адзіным недахопам такога падыходу з’яўляецца тое, што ўнутраныя SSH залішне абцяжараны накладных шыфраванні/дэшыфраванні, калі ніхто не з’яўляецца строга неабходным. Магчыма, мы можам замяніць гэты ўнутраны выклік SSH на штосьці больш эфектыўнае, але такі падыход проста па-чартоўску просты (і дагэтуль вельмі хуткі), каб прымусіць мяне хвалявацца пра яго вельмі шмат.

Адсочванне Git запыту

Выканальнікі грамадскага клона цягнуць з дапамогай Git падобна таму, як SSH апрацоўвае метады. Замест таго, каб выкарыстоўваць SSH для аўтэнтыфікацыі і шыфравання, аднак, яна абапіраецца на боку сервера Git Дэман. Гэты дэман прымае злучэнні, правярае каманду, якую трэба выканаць, а затым выкарыстоўвае fork(2) і exec(3) на нераст, каб працаўнік станавіўся камандай працэсу.

Маючы гэта ў выглядзе, я пакажу вам, як грамадскія працы кланіруюць аперацыі.

Па-першае, ваш кліент Git пытанні запыту ўтрымоўваюць каманду і імя сховішча, якія вы жадаеце кланаваць. Гэты запыт уваходзіць у нашу сістэму на нагрузкі.

Адтуль, запыт адпраўляецца на адзін з вонкавых інтэрфейсаў. Кожны інтэрфейс працуе 4 ProxyMachine выпадках за HAProxy, якія выступаюць у якасці проксі для маршрутызацыі пратаколу Git. Проксі правярае запыт і здабывае імя карыстача (ці сутнасць імя) рэпа. Затым ён выкарыстоўвае коміна для пошуку маршруту. Калі няма маршруту ці любая іншая памылка, проксі кажа пратаколу Git і адпраўляе зваротна адпаведныя паведамленні для кліента. Пасля таго, як маршрут вядомы, рэпа імя (напрыклад, mojombo/bert) перакладаецца на сваім шляху на дыск (напрыклад, a/a8/e2/95/mojombo/bert.git). На нашай старай усталёўцы, якая не мае проксі, мы павінны былі выкарыстоўваць змену дэман, які можа ператварыць карыстачоў/рэпа ў правільны шлях_да_файлу. Гэтым крокам у проксі-серверы мы можам зараз выкарыстоўваць нязменны дэман, што дазваляе значна прасцей абнаўляцца.

Далей проксі-Git усталёўвае празрысты проксі з адпаведным файлавым серверам і адпраўляе запыт змены (з ператвораным шляхам сховішча). Кожны файл-сервер працуе 2 Git Дэман працэсаў за HAProxy. Дэман кажа пратаколу пакет файлаў і струменяў дадзеных зваротна праз проксі-сервер Git і непасрэдна да вашага кліента Git.

Пасля таго, кліент мае ўсе дадзеныя, якія вы кланавалі ў сховішчы і можа прыступіць да працы!

Sub- і Side- сістэмы

У дадатак да асноўнага вэб-прыкладання і Git -хостынг сістэм мы таксама праводзім шэраг іншых Sub- і Side- сістэм. Sub-сістэмы уключаюць у чэргі заданняў загрузкі архіва, рахункі, люстраванне і SVN-імпарцёр. Side-сістэмы уключаюць GitHub старонкі, сутнасць, жамчужыны серверу і кучу ўнутраных прылад. Вы можаце разлічваць на тлумачэнні, якія з гэтых прац у рамках новай архітэктуры, а якія новыя тэхналогіі, зробленыя намі, каб дапамагчы нашым ужыванням працаваць больш плыўна.

Зняволенне

Архітэктуры, выкладзеныя ніжэй, дазволіла нам правільна маштабіраваць сайт і ў выніку рэзкага павелічэння прадукцыйнасці на ўсім сайце. Наш сярэдні адказ Rails чакае нашай папярэдняй усталёўкі, не ўсюды ад 500 мс да некалькіх секунд, а ў залежнасці ад загружанай зрэзы. Пераход на голым жалезе і федэратыўнага захоўвання на Rackspace прывёў наш сярэдні час рэагавання Rails паслядоўна ў 100 мс. Акрамя таго, праца чаргі зараз не мае ніякіх праблем, у нагу са 280 тысяч працоўных месцаў мы апрацоўваем кожны дзень. У нас яшчэ ёсць шмат запас, які гадуецца з бягучым наборам апаратнага забеспячэння, і калі прыйдзе час дадаць больш машын, мы можам дадаць новыя серверы на любым узроўні з лёгкасцю. Я вельмі задаволены тым, як добра ўсё працуе, і калі вы, як я, то табе падабаецца новы і палепшаны GitHub кожны дзень!

ok ok