Если вы именно тот человек, который определяет технический кругозор компании, возможно, вам следует больше времени уделять общению с персоналом, не имеющим отношения к технике (или с ее бизнес-составляющей, как эти люди часто себя именуют). Это поможет понять, чем руководствуется бизнес и как можно изменить его взгляды.
Принципы являются не чем иным, как правилами, выведенными с целью выверить все, что делается с некой более крупной целью, и эти правила иногда подвергаются изменениям. Например, если одной из стратегических целей организации является сокращение времени вывода на рынок новых функций, можно определить принцип, определяющий, что командам доставки дается полный контроль над жизненным циклом поставок их программных средств по готовности независимо от любых других команд. Если еще одной целью организации является проведение агрессивной политики предложений своих продуктов в других странах, можно принять решение о реализации принципа переносимости всей системы, позволяющей развертывать ее локально с соблюдением независимости данных.
Наверное, вам не хотелось бы придерживаться слишком большого количества принципов. Лучше, чтобы их было не более десяти, поскольку такое количество вполне можно запомнить или поместить на небольшой плакат. Чем больше принципов, тем выше вероятность того, что они станут накладываться друг на друга или противоречить один другому.
Двенадцать факторов Heroku — это набор конструкторских принципов, сформулированных с целью помочь вам в создании приложений, которые смогли бы неплохо работать на платформе Heroku. Имеет смысл применять их и при других обстоятельствах. Некоторые из этих принципов фактически являются ограничениями, накладываемыми на поведение ваших приложений, чтобы они благополучно работали на платформе Heroku. Под ограничениями понимается то, что очень трудно (или практически невозможно) изменить, но мы ведь решили выбрать принципы. Чтобы отличить то, что нельзя изменить, от всего остального, можно назвать это ограничениями, а все не подпадающее под эту категорию четко назвать принципами. Я думаю, что временами неплохо было бы включать спорные ограничения в список с принципами и смотреть, действительно ли эти ограничения настолько непоколебимы!
Инструкции описывают способы соблюдения принципов. Это набор подробных практических предписаний для выполнения задач. Зачастую они будут носить сугубо технологический характер и должны быть достаточно простыми и понятными любому разработчику. Инструкции могут включать в себя правила программирования, требования о том, что все регистрационные данные должны фиксироваться централизованно, или предписание использовать технологию HTTP/REST в качестве объединяющего стандарта. Из-за своей чисто технологической сути инструкции изменяются чаще принципов.
Как и в принципах, в инструкциях иногда отображают ограничения, принятые в вашей организации. Например, если в ней поддерживается только CentOS, этот факт должен быть отображен в инструкциях.
В основу инструкций должны быть положены наши принципы. Принцип, утверждающий, что полный жизненный цикл систем подконтролен командам, занимающимся доставкой, может означать наличие инструкции, согласно которой все сервисы, развертываемые внутри изолированных учетных записей AWS, обеспечивают возможность самостоятельного управления ресурсами и изолированность от других команд.
Чьи-то принципы становятся чьими-то инструкциями. К примеру, использование HTTP/REST можно назвать принципом, а не инструкцией, и в этом не будет ничего плохого. Дело в том, что ценность представляет наличие всеобъемлющих идей, дающих представление о ходе развития системы, и наличие достаточной детализации, позволяющей описать способы осуществления этих идей. Возможно, в объединении принципов и инструкций не будет ничего плохого, если речь идет о небольшой группе команд или даже об одной команде. Но для более крупных организаций, в которых могут применяться различные технологии и рабочие инструкции, для разных мест могут понадобиться разные инструкции, и при этом все они будут соответствовать общему набору принципов. Например, у. NET-команды может быть один набор инструкций, а у Java-команды — другой наряду с наличием набора инструкций, общего для обеих команд. А вот принципы у них должны быть общими.
В ходе работы с одним из наших клиентов мой коллега, Эван Ботчер (Evan Bottcher), разработал схему, показанную на рис. 2.1. На рисунке продемонстрирована взаимосвязь целей, принципов и инструкций в очень ясной форме. В течение нескольких лет инструкции будут меняться, в то время как принципы останутся прежними. Такую схему можно распечатать на большом листе бумаги и повесить на видном месте.
Неплохо бы иметь документацию, разъясняющую некоторые из этих пунктов. Но все же мне больше нравится идея иметь в развитие этих замыслов примеры кода, которые можно было бы рассмотреть, изучить и запустить. Еще лучше создать инструментальные средства, допускающие только правильные действия. Все это скоро будет рассмотрено более подробно.
При проработке инструкций и размышлении насчет компромиссов, на которые необходимо пойти, нужно определить и такой очень важный момент, как допустимая степень изменчивости вашей системы. Одним из основных способов определения неизменных качеств для всех сервисов является установка критериев отбора тех из них, поведение и качество которых не выходят за рамки допустимого. Какой же из сервисов станет добропорядочным жителем вашей системы? Какими возможностями он должен обладать, чтобы гарантировать управляемость системы, и то, что один негодный сервис не приведет к сбою всей системы? Ведь здесь как с людьми: добропорядочность жителя в одних условиях не определяет то, на что он может стать похож в каких-либо других условиях. И тем не менее есть некие общие характеристики подходящих сервисов, которых, на мой взгляд, очень важно придерживаться. Существует ряд ключевых областей, в которых допустимость слишком больших отклонений может привести к весьма жарким временам. Согласно определению Бена Кристенсена (Ben Christensen) из компании Netflix, когда мы мысленно рисуем себе крупную картину, «должна быть стройная система из множества мелких деталей с автономными жизненными циклами, способная представлять собой единое целое». Следовательно, нужно найти разумный баланс оптимизации автономии отдельно взятого микросервиса, не теряя при этом из виду общую картину.
Рис. 2.1. Практический пример принципов и инструкций
Одним из способов выявления сути такого баланса является определение четких признаков, наличие которых обязательно для каждого сервиса.
Очень важно иметь возможность составить логически последовательное, распространяемое на все сервисы представление о работоспособности системы. Это представление должно быть общесистемным, а не специфичным для конкретного сервиса. Конечно, в главе 8 будет определено, что быть в курсе работоспособности отдельного сервиса также немаловажно, но зачастую только при попытке диагностировать более широкую проблему или разобраться в более крупной тенденции. Чтобы максимально облегчить задачу, я хотел бы предложить использование однообразных критериев работоспособности и общих отслеживаемых параметров.
Можно выбрать применение механизма активной доставки данных, при котором каждому сервису нужно доставлять данные в центр. Для метрик это может быть Graphite, а для отслеживания работоспособности — Nagios. Или можно принять решение об использовании систем опроса, самостоятельно собирающих данные из узлов. Внутри приемника технологию нужно сделать непрозрачной и не требующей от ваших систем мониторинга изменений с целью ее поддержки. Журналирование здесь входит в ту же категорию: оно должно осуществляться в одном месте.
Выбор небольшого числа определенных технологий интерфейса помогает интегрировать новых потребителей. Лучше всего иметь один стандарт. Два — тоже неплохо. А вот наличие 20 различных стилей объединения уже никуда не годится. Это относится не только к выбору технологии и протокола. Если, к примеру, выбор пал на HTTP/REST, что вы будете использовать, глаголы или существительные? Как станете работать со страничной организацией ресурсов? Как будете справляться с управлением версиями конечных точек?
Мы не можем позволить, чтобы один плохо работающий сервис нарушил общую работу. Нужно убедиться в том, что наши сервисы сами себя защищают от вредных нисходящих вызовов. Чем больше окажется сервисов, неспособных правильно обработать потенциально сбойный характер нисходящих вызовов, тем более хрупкими будут наши системы. Это означает, что вам как минимум нужно будет выдвинуть требование, чтобы каждый нисходящий сервис получил собственный пул подключений, и вы даже можете дойти до того, чтобы сказать, что каждый из сервисов должен использовать предохранитель. Более подробно этот вопрос будет обсуждаться в главе 11 при рассмотрении микросервисов в более широком масштабе.
Игра по правилам важна и тогда, когда речь заходит о кодах. Если предохранители полагаются на HTTP-коды, а в отношении одного из сервисов принято решение о возврате для ошибок кодов вида 2XX или коды 4XX перепутаны с кодами 5XX, то все меры безопасности окажутся тщетными. Те же опасения возникнут, даже если вы не используете HTTP. Знание того, в чем заключается разница между подходящим и корректно обработанным запросом, плохим запросом, при котором сервис удерживается от каких-либо действий по отношению к нему, и запросом, который может быть в порядке, но точно этого сказать нельзя, поскольку сервер не работал, является ключом уверенности в возможности быстрого отказа и отслеживания возникших проблем. Если наши сервисы будут пренебрегать этими правилами, система получится более уязвимой.