У ме­ня слу­чи­лось огром­ное "го­ре" - проект, ко­то­рый был взра­щен мной и не­большой компа­ни­ей про­грам­ми­стов прак­ти­че­ски для лич­но­го поль­зо­ва­ния, вдруг стал по­пу­ля­рен, под­верг­ся ред­дит, дигг и стам­бла­пон эф­фек­там (и пере­жил их по­чти без даун­таймов, эта отдель­ная гор­дость и те­ма для дру­го­го по­ста), об­за­вел­ся се­рьезны­ми ин­ве­сто­ра­ми и на­пол­нил­ся хо­ро­шей ре­ле­вант­ной ак­тив­ной ауди­то­ри­ей. Обой­дем­ся без имен и на­зва­ний - нас ин­тере­сует ис­клю­чи­тель­но тех­но­ло­ги­че­ская сто­ро­на. Слу­чи­лось это око­ло го­да на­зад, под управ­ле­ни­ем та­лантли­во­го PMа всего че­рез 5 ме­ся­цев по­сле пуб­лич­но­го стар­та уда­лось мо­не­ти­зи­ро­вать сер­вис без по­терь ло­яль­но­сти кли­ен­тов - прав­да па­ру злых му­да­ков в бло­жи­ках все же плю­ну­ли злым слов­цом в сто­ро­ну на­шей корыстно­сти. И вот, в один пре­крас­ный день мы ут­кну­лись но­сом в сте­ну. Дан­ный пост по­мо­жет вам не по­вто­рять на­ши ошиб­ки и при­зван по­де­лить­ся опы­том вы­бо­ра инстру­мен­та­рия раз­ра­ботки и деплой­мен­та в самом на­ча­ле дол­го­го пу­ти.

Ос­нов­ная пробле­ма в проек­тах на­ше­го ти­па - не­даль­но­вид­ность, по этим же при­чи­нам facebook в свое вре­мя был на­пи­сан на PHP, ко­то­рый в по­след­ствии пы­та­лись со­би­рать в С++ в HipHop, не знаю,чем там все за­кон­чи­лось, но подоб­ных при­ме­ров мил­лио­ны, к со­жа­ле­нию, лишь круп­ные компа­нии на эта­пе пер­вич­но­го пла­ни­ро­ва­ния мо­гут поз­во­лить се­бе раз­ра­ботку ар­хи­тек­ту­ры го­ри­зонталь­но­го и вер­ти­каль­но­го масшта­би­ро­ва­ния и пред­ви­деть даль­ней­шее раз­ви­тие проек­та. Все ма­лень­кие ко­манды со­зда­ют проек­ты на од­ной ле­вой ко­лен­ке с по­мо­щью про­сто­го и бы­стро­го инстру­мен­та­рия, а да­лее на­чи­на­ет дей­ство­вать пра­ви­ло снеж­но­го ко­ма - ста­рый код об­рас­та­ет но­вым и за­ме­нить яд­ро мож­но толь­ко пол­ность рас­ко­пав верх­ние слои. Не обо­шла сто­ро­ной эта пробле­ма и нас, для ре­а­ли­за­ции был вы­бран фрейм­ворк Django, воз­мож­но­стей ко­то­ро­го хва­та­ло при­мер­но с год, а дальше мы столк­ну­лись с пробле­мой большинства. При ку­че до­сто­инств у это­го инстру­мен­та есть один большу­щий ми­нус - уз­ко­лобое и со­вер­шен­но не гиб­кое ORM на ко­то­ром за­вя­за­но аб­со­лют­но все: CRUD ин­тер­фейс па­не­ли адми­ни­стри­ро­ва­ния, мо­дуль аутен­ти­фи­ка­ции, ге­не­ра­тор форм и то­му подоб­ное. Пробле­ма бы­ла до­ста­точ­но се­рьезная - нуж­но бы­ло ли­бо вне­дрять пря­мые SQL инъ­ек­ции, ис­поль­зуя сто­ронние raw sql бил­де­ры, све­дя на нет пере­но­си­мость, аб­стра­ги­ро­ван­ность от син­так­си­са и са­му идею ORM, ли­бо ис­кать аль­тер­на­тив­ные ре­ше­ния. Вто­рой ва­ри­ант по­ка­зал­ся нам бо­лее пред­по­чти­тель­ным, при­ни­мая во вни­ма­ние тот факт, что из Django мы ис­поль­зо­ва­ли лишь верх­ний слой ша­бло­ни­за­то­ра, URL Routing, ORM (ну и ге­не­ра­тор форм) и memcached backend. Для ша­бло­нов сра­зу был найден jinja2, прак­ти­че­ски пол­но­стью сов­ме­сти­мый с django-templates, не­большие син­так­си­че­ские раз­личия со­сто­я­ли лишь в пере­да­че па­ра­мет­ров фильтрам и от­сут­ствии не­ко­то­рых ме­то­дов (вро­де то­го же {% url %}, он по­все­мест­но ис­поль­зо­вал­ся на­ми для ге­не­ра­ции ссы­лок), ко­то­рые бы­ли на­пи­са­ны само­сто­я­тель­но с уче­том осо­бен­но­стей но­во­го ядра. Вто­рая часть ко­манды за­ни­ма­лась пор­ти­ро­ва­ни­ем контрол­ле­ров в но­вый фрейм­ворк Pyramid (про­дол­жа­тель идей Pylons), а мне оста­ва­лась са­мая "вкус­ная" ра­бо­та - пере­пи­сы­вать мо­де­ли с Django ORM на SQLAlchemy, ну и пере­но­сить дан­ные. Не бу­ду углуб­лять­ся в де­та­ли, ска­жу лишь, что ни­че­го не ме­ша­ло ис­поль­зо­вать ал­хи­мию по­верх та­блиц, ге­не­ри­ро­ван­ных джан­го - в от­личии от сво­е­го ин­ва­лид­но­го со­брата, sqlalchemy уме­ет custom database scheme, но хо­те­лось аб­стра­ги­ро­вать­ся от ужас­ных many-to-many relations с та­бли­цей в 500K за­пи­сей, остав­лен­ных в на­след­ство. Пробле­ма ге­не­ра­ции и ва­ли­да­ции форм то­же бы­ла све­де­на на нет с по­мо­щью formalchemy. В осталь­ном, это бы­ла впол­не вы­пол­ни­мая ру­ти­на. Че­рез 10 дней в SVN вет­ке с за­вет­ным на­зва­ни­ем migration уже бы­ла ра­бо­чая те­сто­вая вер­сия, ко­то­рую нуж­но бы­ло скарм­ли­вать обе­зьян­кам из отде­ла те­сти­ров­ки и QA.

Те­перь по­го­во­рим про deployment. В да­та­цен­тре компа­ния арен­ду­ет несколь­ко сер­ве­ров в стой­ке - фронт­энд с nginx для про­стой про­пор­ци­о­наль­но­го upstream load-balancing, 2 backend-а для python и memcache, 3 сер­ве­ра для pgpool-II PostgreSQL кла­стера. Ста­ти­ка и файлы поль­зо­ва­телей хра­нят­ся в Amazon S3. Рань­ше на бэкэн­дах мы ис­поль­зо­ва­ли FastCGI ин­тер­фейс manage.py пря­мо из ко­роб­ки Django ру­ко­водству­ясь при­ци­пом "нам ле­ни­во", с пере­хо­дом на Pyramid был вы­бран род­ной для Python WSGI и Green Unicorn в ка­че­стве сер­ве­ра, ко­то­рый уме­ет ра­бо­тать с Paste-сов­ме­сти­мы­ми фрейм­вор­ка­ми. Для дан­но­го окру­же­ния был со­здан отдель­ный virtualenv на каж­дом сер­ве­ре, поз­во­ля­ю­щий ве­сти те­сти­ро­ва­ние и уста­нов­ку но­вых вер­сий необ­хо­ди­мых биб­лио­тек, не бо­ясь по­вре­дить хруп­кие dependancies для уже ра­бо­та­ю­ще­го проек­та. В ка­че­стве сто­ро­же­во­го пса бы­ла вы­бра­на то­же не чу­жая для python си­сте­ма контро­ля про­цес­са supervisord. Для управ­ле­ния на­ши­ми но­вы­ми окру­же­ни­я­ми бы­ло по­строе­но не­большое при­ло­же­ние на осно­ве fabric, ини­ци­и­ру­ю­щее svn up об­нов­ле­ние bleeding edge вер­сий че­рез SVN post-commit хук и пе­реза­гру­жа­ю­щее gunicorn workers про­стой и удоб­ной ком­мандой kill -HUP `cat /var/run/g_be.pid`. 

Пред­ва­ри­тель­ные вы­во­ды по­ка де­лать до­ста­точ­но ра­но - но­вая вер­сия все еще не ушла в production, но син­те­ти­че­ские на­груз­ки с по­мо­щью ути­лит аля siege по­ка­за­ли за­мет­ную эко­но­мию на вре­ме­ни ге­не­ра­ции стра­ни­цы и ре­сур­сах CPU\RAM. Что ка­са­е­мо Django - по­ис­ти­не ве­ли­ко­леп­ный инстру­мент, Ruby On Rails, но толь­ко для Python и я ни в ко­ем слу­чае не спи­сы­ваю его со сче­тов. Этот блог был це­ли­ком и пол­но­стью на­пи­сан на Django за 5 ча­сов, но если вы пла­ни­ру­е­те со­здать что-то, что ги­по­те­ти­че­ски бу­дет услож­нять­ся струк­тур­но - вам необ­хо­ди­мо смот­реть в сто­ро­ну аль­тер­на­тив­ных инстру­мен­тов. Удач­ный при­мер ми­гра­ции, опи­сан­ный в этом по­сте - ско­рее success story и большая уда­ча, чем пра­ви­ло. Про­сто большинство на­ше­го функ­ци­о­на­ла бы­ло вы­не­се­но в packages, а Django был про­сто про­слой­кой для со­зда­ния ло­ги­ки. Вам же мо­жет по­вез­ти на­много мень­ше.

И в ка­че­стве по­сле­сло­вия: опыт­ный про­грам­мист и си­стем­ный ар­хи­тек­тор (оху­и­ти­тель­но же зву­чит) в ли­це ме­ня ищет ин­терес­ную ра­бо­ту на рас­сто­я­нии. Ин­терес­ную не в смыс­ле де­неж­ной компен­са­ции или комплекс­ных обе­дов, ин­терес­ным и тех­ни­че­ски слож­ным дол­жен быть проект и ко­манда. Хо­тя ко­манда мо­жет быть и не слож­ной, про­сто опыт­ной и при­ят­ной в об­ще­нии. Рассмот­рю лю­бые предло­же­ния, от­прав­лен­ные мне на элек­трон­ную по­чту, ука­зан­ную в шап­ке.