Русский
Русский
English
Статистика
Реклама

Delivery

Перевод Регрессионная спираль смерти

27.07.2020 20:17:32 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Автоматизация тестирования на JavaScript




История, которая может показаться вам до боли знакомой:


Вы работаете QA в небольшой команде разработчиков; сегодня утро четверга второй недели вашего двухнедельного спринта. У вашей команды еще несколько историй (story) в разработке, которые должны быть утверждены к утру пятницы, чтобы быть готовыми к демонстрации днем. Ваши планы на вечер четверга начинают таять на ваших глазах.


На утреннем стендапе разработчики говорят, что они уже вот-вот заканчивают. Вы знаете, что они пишут модульные и компонентные тесты в рамках процесса разработки, и вы уже выполнили с ними несколько прогонов фич, так что вы уверены, что получите качественный код. Тем не менее, в дополнение к тестированию истории, вам все равно придется завершать автоматизацией UI и API, проверять некоторые нефункциональные требования и провести специфическое тестирование работоспособности И один день это не так много времени на все это. Вы, вероятно, можете уложиться в это время с тестированием, но успеть с автоматизацией будет нереально. Вы делитесь со скрам-мастером этими неприятными новостями.


ОК, говорит скрам мастер, как насчет, и вы точно знаете, что будет дальше. как насчет того, чтобы мы передвинули автоматизацию на следующий спринт? Автоматизация, стоящая непосредственно перед отметкой выполнено, заложенная в историю и всегда занимающая часть оцененного времени, выталкивается в будущее. В очередной раз. Мне это тоже не нравится, говорит ваш скрам-мастер, видя выражение вашего лица, но сейчас наша скорость на виду, это очень важно, поскольку мы приближаемся к этому гигантскому майлстоуну мы просто не можем не добить наши стори поинты.


Я понимаю, отвечаете вы с улыбкой, чтобы продемонстрировать, что вы командный игрок и тоже стремитесь к тому, чтобы команда выглядела так, как будто она успешно справляется.


Конец истории


Держу пари, что эта история, к сожалению, знакома многим из вас. Контекст или причина не имеют значения, мы неизбежно оказываемся в ситуациях, когда нас заставляют откладывать, сокращать или просто пропускать автоматизацию тестирования. К сожалению, важность автоматизации в Agile Delivery и последствия ее отсрочки уходят из виду многих правообладателей, специалистов по планированию, менеджеров и т. д. Для них автоматизация тестирования кажется требованием, которое в общем неплохо было бы иметь, но которым можно пренебречь, когда поджимают дедлайны.


Я оказался именно в такой ситуации и обнаружил, что фраза регрессионная спираль смерти является хорошим иллюстрированием нескольких концепций тестирования и полезна для информирования правообладателей о рисках неадекватной автоматизации тестирования. Если вам приходится постоянно отстаивать необходимость полной автоматизации тестирования в своих Agile-командах, добавьте ее в свой арсенал, и, надеюсь, вы добьетесь такого же успеха на этом поприще, как и я.


Регрессионное тестирование


Прежде чем мы перейдем к регрессионной спирали смерти, мы должны понять идею регрессионного тестирования, и в частности, что тестирование фичи это больше, чем просто тестирование фичи.


Программное обеспечение это по сути строки кода, написанные в разное время разными людьми. Иногда код пишется годами людьми, которые никогда не встретятся. И весь этот код должен работать вместе. Это непростая задача, и разработчики программного обеспечения разработали стратегии, подходы, шаблоны и методы, чтобы сделать процесс создания сложных систем немного проще, менее рискованным и более предсказуемым. Эти стратегии помогают гарантировать, что изменения, сделанные здесь в программном обеспечении, будут иметь ожидаемые эффекты именно здесь, и не будут иметь неожиданных последствий в других местах.


К сожалению, независимо от того, насколько мы осторожны при реализации нового кода, всегда есть вероятность, что изменения приведут к непредвиденным побочным эффектам, что код для нашей новой фичи каким-то образом повлияет на старый функционал в соседних или даже совершенно не связанных областях системы. Учитывая, как программный код переплетается и взаимосвязывается, так как новые фичи накладываются на предыдущие новыми людьми или новыми командами, вероятность непредвиденных побочных эффектов может быть любой, от в принципе возможной до вполне ожидаемой.


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


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


Правообладатели или люди, не имеющие опыта разработки, часто упускают риск внесения критических изменений за пределами функции в разработке и необходимости регрессионного тестирования. Я даже слышал, как говорят, что тестирование функции это просто проверка перечисленных критериев приемлемости этой функции(!!!). К сожалению, игнорирование или недооценка реальной цены регрессии является ключевым фактором для входа в регрессионную спираль смерти.


Agile Delivery и бремя регрессии


Agile Delivery вводит две концепции, относящиеся к регрессионной спирали смерти. Во-первых, Agile Delivery делит фичи на небольшие, отдельные пользовательские истории (user stories). Каждая пользовательская история должна быть атомарной, чтобы ее можно было разрабатывать и тестировать как отдельный объект. Во-вторых, Agile Delivery способствует коротким циклам разработки с небольшими итеративными релизами. Все виды и гибриды Agile, Scrum, Kanban, SAFE, LeSS, ScrumBan и т. д. по-своему используют эти концепции. Давайте посмотрим на каждую в отдельности.


Agile Delivery отходит от использования больших функций, определенных (даже более крупными) документами спецификаций, к пользовательским историям, каждая из которых представляет собой небольшую часть общего объема работ. Истории Agile независимо разрабатываются, тестируются, ревьюятся и утверждаются.


Использование небольших отдельных историй может создать ложное чувство изоляции между изменениями кода в разных историях, подрывая при этом регрессионное тестирование. Да, пользовательские истории независимы, но под этими историями находится тот же взаимосвязанный код и тот же риск внесения непреднамеренных изменений. Неважно, насколько четко вы разделяете свои истории или насколько хорошо вы определяете критерии приемлемости, регрессионное тестирование так же необходимо в Agile-разработке, как и при разработке одной большой функции в каскадном процессе.



История 13 была внедрена и протестирована. Нам не о чем беспокоиться?
/ НЕТ! Изменения в коде могли повлиять на другие истории (как связанные, так и не связанные с этой историей)


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


Давайте перейдем ко второй концепции что Agile Delivery поощряет небольшую, инкрементную доставку. Если бы необходимо было регрессировать приложение каждые шесть месяцев, стоимость регрессионного тестирования, даже медленного ручного, не была бы ощутимой. Фактически, именно так мы раньше и поставляли программное обеспечение до перехода к Agile разработке у нас был бы план регрессионного тестирования, и каждые шесть месяцев мы выполняли этот план перед релизом. Это часто занимало дни или недели, но в рамках шестимесячного цикла релиза это было не слишком обременительно.


Сдвиг к небольшой инкрементной доставке значительно увеличивает стоимость регрессии за счет уменьшения интервала между проходами регрессии. Теперь регрессия может потребоваться каждые две недели или даже несколько раз в неделю. Это огромный сдвиг со времен каскадной доставки. Ощутите всю бесполезность запуска ручного двухнедельного регрессионного прохода, когда длина вашего спринта составляет как раз ровно две недели!


Давайте рассмотрим простой гипотетический набор спринтов, чтобы прояснить, насколько быстро стоимость регрессии может отягощать скорость команды.


Предположим, у нас есть недавно созданная Agile-команда, которая только начинает свою первую итерацию разработки (спринт). На первой итерации (или некоторой начальной продолжительности в потоковой модели) у нас может быть три истории для тестирования. Тестирование этих историй довольно простое мы просто тестируем эти три истории. Во второй итерации мы добавляем две дополнительные истории. Сколько усилий будет затрачено на тестирование этих историй? Наивный ответ несколько меньше усилий, чем для первого спринта, так как мы тестируем только две истории. Однако, основываясь на нашем понимании того, что изменения кода могут вызвать регрессию в любой области приложения, мы знаем, что мы должны протестировать обе новые истории, а также регрессировать три ранее завершенные истории.



Таким образом, наши реальные усилия по тестированию во второй итерации фактически больше, чем в первой, несмотря на то, что мы взяли на одну историю меньше.


Очевидно, что это слишком упрощенная модель, поскольку она предполагает, что усилия, необходимые для регрессии ранее завершенной истории, эквивалентны проверке новой истории. Конечно, не все должно быть полностью регрессировано, и тестировать что-либо всегда легче во второй раз. Однако стоимость регрессии не равна нулю, и для сложного программного обеспечения она может приближаться к первоначальной стоимости тестирования. Точное отношение первоначального тестирования к регрессионному тестированию не меняет того факта, что значительное количество времени на второй итерации будет потрачено на регрессию историй, завершенных на первой итерации, мы не можем просто полагаться на то, что они все еще будут работать.


Если мы экстраполируем этот шаблон на множество спринтов, мы увидим, что в модели Agile-разработки с ручной регрессией стоимость регрессии ранее завершенных историй быстро затмевает стоимость тестирования новых историй количество ранее завершенных историй накапливается в каждом спринте, и весь набор должен быть регрессирован на каждой итерации.



Это фундаментальная задача тестирования в Agile методологии инкрементный, итеративный характер процесса требует огромного количества регрессионного тестирования на каждой итерации, поскольку новые функции и истории добавляются в систему гораздо быстрее, за несколько дней или недель, а не месяцев или лет. Каждый раз, когда история завершена, она не только должна быть проверена, но и все ранее завершенные истории должны, в некоторой степени, быть проверены повторно. Ручная регрессия не может масштабироваться с увеличенной скоростью доставки Agile.


Это регрессионная спираль смерти с каждой итерацией все сложнее и сложнее адекватно регрессировать существующее приложение. Производительность команды схлопывается, так как бремя тестирования предыдущей работы занимает все большую и большую часть общего объема усилий. Оставленная без контроля, начальная скорость команды замедлится, а затем остановится, так как каждое новое изменение требует существенного и постоянно растущего набора предыдущих историй, которые также должны быть проверены.



Автоматизация в Agile Delivery


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


Автоматизация тестирования существует во многих формах модульные тесты, API-тесты, тесты компонентов, интеграционные тесты, тесты E2E, визуальные регрессионные тесты пользовательского интерфейса и т. д. Неважно, кто их пишет или как они взаимодействуют с приложением, все автоматизированные тесты помогают проверить существующую функциональность и обеспечить уверенность в том, что предыдущая работа не была нарушена, что значительно снижает бремя ручной регрессии.


Важно понимать, что автоматизация тестирования не ускоряет первоначальное тестирование. На самом деле, внедрение автоматизации тестирования часто замедляет базовую скорость разработки. Автоматизация это процесс разработки, который требует продумывания и опыта, как и всё остальное! Автоматизация не тестирует это просто запрограммированная проверка того, что уже было протестировано. К счастью, это именно то, что нам нужно для смягчения регрессионной спирали смерти!


Если учесть моменты двух предыдущих разделов то, что изменения в программном обеспечении могут иметь непреднамеренные последствия, и что agile delivery это просто непрерывный поток небольших изменений становится очевидным, что agile delivery не может функционировать без автоматизации тестирования. Попытка итеративной доставки без него это все равно, что отправиться в дальнюю дорогу, но отказаться от остановок, потому что остановки замедляют вас. Топливо фундаментальная часть автомобильного путешествия (на данный момент), так же, как автоматизация тестирования фундаментальная часть Agile Delivery. Автоматизация это единственное, что может смягчить удушающее бремя регрессии и избежать резкого падения скорости разработки.



Зачастую правообладатели или менеджеры утверждают, что автоматизацию тестирования следует полностью игнорировать, перенаправив 100% усилий по тестированию будущих спринтов на ручную регрессию. Гораздо чаще команды вынуждены принимать истории с частичной или достаточно хорошей автоматизацией. Выполнение этого требования все еще передает некоторый процент от общей стоимости регрессии в будущие спринты на ручное неавтоматическое тестирование. К сожалению, любая незаконченная автоматизация, независимо от того, насколько она мала, обязательно увеличивает нагрузку регрессии в будущих спринтах.


Оставляя любое количество регрессии на трудоемкое, ручное тестирование, вы встаете на скользкую дорожку, которая может быстро привести к регрессионной спирали смерти. Если вы тестируете и автоматизируете историю, что произойдет, если из-за нехватки времени мы согласимся с правообладателем и признаем историю законченной с неполной автоматизацией? Некоторое количество бремени регрессии передается на следующую итерацию, которая затем потребляет даже больше времени на тестирование в последующем спринте. Это дает еще меньше времени для автоматизации в этой итерации, в результате чего автоматизируется еще меньше тестов, еще больше спихивая ее в будущее и т. д. Этот шаблон повторяется из итерации в итерацию, создавая цикл положительной обратной связи, пока у QA совсем не останется времени на автоматизацию, из-за долгих часов и вечеров, едва удовлетворяющих потребность в ручном тестировании, результирующих в уходе с работы и поиске компании с более зрелой agile практикой.


Я твердо убежден в том, что без полной автоматизации тестирования не может быть здоровой Agile Delivery. Полная автоматизация историй с набором средств автоматизации, охватывающим все виды тестов (модуль, компонент, интеграция, пользовательский интерфейс и т. д.), абсолютно необходима для поддержания скорости доставки на протяжении всего жизненного цикла. Любое предложение о послаблении этого требования для каждой истории следует отвергать и объяснять, что это такое краткосрочное отвлечение для создания восприятия успеха, в то же время ставящее под угрозу долгосрочные цели развития. Не говоря уже о здравом уме.


Спираль смерти


Вернемся к нашей маленькой истории.


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


Вы собираетесь протестовать, когда ваш скрам-мастер вмешивается: Мы не можем этого сделать! Это первый шаг к регрессионной спирали смерти!


Автоматизация всегда является частью определения готовности, добавляет разработчик, это часть продукта, как код, ориентированный на клиента.


Вы хотите, чтобы мы преуспели или просто казались преуспевающими? Эти слова вашего бизнес-аналитика, вызывает улыбку на вашем лице.


Вы решаете отменить собеседование на следующей неделе.


Слова благодарности


Я не изобрел термин регрессионная спираль смерти, но никогда не видел его в письменной форме. Самое раннее использование термина, которое я могу найти, находится в Тестировании экстремального программирования (2002) Лизы Криспин и Тип Хаус.


Несколько других ссылок можно найти с помощью быстрого поиска в Google:


http://ivory.idyll.org/blog/software-quality-death-spiral.html (2008)


https://xebia.com/blog/regression-testing-with-an-agile-mindset (2010)




Узнать всё о курсе Автоматизация тестирования на JavaScript



Подробнее..

Скрываем номера курьеров и клиентов с помощью key-value хранилища

17.06.2021 18:06:38 | Автор: admin

В условиях пандемии курьерские сервисы стали востребованы как никогда прежде. Чтобы клиент и курьер могли созвониться для уточнения информации по заказу, им нужно знать номера телефонов друг друга. А что насчет соблюдения прайваси? Многие сервисы доставок уже озаботились этим вопросом после не очень приятных инцидентов, о которых вы могли читать в новостях.

Каждый сервис использует свои решения для маскировки номеров клиентов и курьеров. В данной статье я расскажу, как сделать это с помощью key-value хранилища в Voximplant.

Как это будет работать

Мы создадим сценарий, который позволит курьеру и клиенту созваниваться, не зная при этом личные номера телефонов друг друга.

У нас будет только один нейтральный номер, на который будут звонить и клиент, и курьер. Номер мы арендуем в панели Voximplant. Затем создадим некую структуру данных, где клиент и курьер будут связаны между собой номером заказа (то есть ключом в терминологии key-value storage).

Так при звонке на арендованный номер звонящий введёт номер заказа, и если такой заказ есть в базе, наш сценарий проверит номера телефонов, привязанные к нему. Далее если номер звонящего будет идентифицирован как номер клиента, произойдет соединение с курьером, ответственным за заказ, и наоборот.

Например, звонок курьера клиенту будет выглядеть следующим образом:

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

Перейдем непосредственно к реализации.

Вам понадобятся

1) Чтобы начать разработку, войдите в свой аккаунт: manage.voximplant.com/auth. В меню слева нажмите Приложения, затем Создать приложение в правом верхнем углу. Дайте ему имя, например, numberMasking и снова кликните Создать.

2) Зайдите в новое приложение, переключитесь на вкладку Сценарии и создайте сценарий, нажав на +. Назовём его kvs-scenario. Здесь мы будем писать код, но об этом чуть позже.

3) Сначала перейдем во вкладку Роутинг и создадим правило для нашего сценария. Маску (регулярное выражение) оставим .* по умолчанию, так правило будет срабатывать для всех номеров.

4) Далее арендуем реальный городской номер. Для этого перейдем в раздел Номера, выберем и оплатим номер. На него будут звонить и клиент, и курьер, и он будет отображаться вместо их настоящих номеров.

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

5) Осталось привязать его к нашему приложению. Заходим в приложение, открываем вкладку Номера Доступные и нажимаем Прикрепить. В открывшемся окне можно также прикрепить наше правило, тогда оно будет автоматически назначено для входящих вызовов, а все остальные правила будут проигнорированы.

6) Далее необходимо верифицировать аккаунт, чтобы использовать этот номер для звонков.

Отлично, структура готова, осталось заполнить key-value хранилище и добавить код в сценарий.

Key-value хранилище

Чтобы сценарий заработал, нужно положить что-то в хранилище. Это можно сделать, воспользовавшись Voximplant Management API. Я буду использовать Python API client, он работает с Python 2.x или 3.x с установленным pip и setuptools> = 18.5.

1) Зайдем в папку проекта и установим SDK, используя pip:

python -m pip install --user voximplant-apiclient

2) Создадим файл с расширением .py и добавим в него код, при выполнении которого данные о заказе попадут в key-value хранилище. Применим метод set_key_value_item:

from voximplant.apiclient import VoximplantAPI, VoximplantExceptionif __name__ == "__main__":    voxapi = VoximplantAPI("credentials.json")        # SetKeyValueItem example.    KEY = 12345    VALUE = '{"courier": "79991111111", "client": "79992222222"}'    APPLICATION_ID = 1    TTL = 864000        try:        res = voxapi.set_key_value_item(KEY,            VALUE,            APPLICATION_ID,            ttl=TTL)        print(res)    except VoximplantException as e:        print("Error: {}".format(e.message))

Файл с необходимыми credentials вы сможете сгенерировать при создании сервисного аккаунта в разделе Служебные аккаунты в настройках панели.

APPLICATION_ID появится в адресной строке при переходе в ваше приложение.

В качестве ключа (KEY) будет использоваться пятизначный номер заказа, а в качестве значений телефонные номера: courier номер курьера, client номер клиента. TTL нам здесь необходимо для указания срока хранения значений.

3) Осталось запустить файл, чтобы сохранить данные заказа:

python3 kvs.py

Если мы больше не захотим, чтобы клиент и курьер беспокоили друг друга, можно будет удалить их данные из хранилища. Информацию о всех доступных методах key-value storage вы найдёте в нашей документации: management API и VoxEngine.

Код сценария

Код, который необходимо вставить в сценарий kvs-scenario, представлен ниже, его можно смело копировать as is:

Полный код сценария
require(Modules.ApplicationStorage);/** * @param {boolean} repeatAskForInput - была ли просьба ввода произнесена повторно * @param longInputTimerId - таймер на отсутствие ввода * @param shortInputTimerId - таймер на срабатывание фразы для связи с оператором * @param {boolean} firstTimeout - индикатор срабатывания первого таймаута * @param {boolean} wrongPhone - индикатор совпадения номера звонящего с номером, полученным из хранилища * @param {boolean} inputRecieved - получен ли ввод от пользователя *  */let repeatAskForInput;let longInputTimerId;let shortInputTimerId;let firstTimeout = true;let wrongPhone;let inputRecieved;const store = {    call: null,    caller: '',    callee: '',    callid: '74990000000',    operator_call: null,    operatorNumber: '',    input: '',    data: {        call_operator: '',        order_number: '',        order_search: '',        phone_search: '',        sub_status: '',        sub_available: '',        need_operator: '',        call_record: ''    }}const phrases = {    start: 'Здр+авствуйтте. Пожалуйста, -- введите пятизначный номер заказa в тт+ооновом режиме.',    repeat: 'Пожалуйста , , - - введите пятизначный номер заказа в т+оновом режиме,, или нажмите решетку для соединения со специалистом',    noInputGoodbye: 'Вы - ничего не выбрали. Вы можете посмотреть номер заказа в смс-сообщении и позвонить нам снова. Всего д+обровоо до свидания.',    connectToOpearator: 'Для соединения со специалистом,, нажмите решетку',    connectingToOpearator: 'Ожидайте, соединяю со специалистом',    operatorUnavailable: 'К сожалению,, все операторы заняты. Пожалуйста,,, перезвоните позднее. Всего д+обровоо до свидания.',    wrongOrder: 'Номер заказа не найден. Посмотрите номер заказа в смс-сообщении и введите его в т+оновом режиме. Или свяжитесь со специалистом,, нажав клавишу решетка.',    wrongOrderGoodbye: 'Вы ничего не выбрали, всего д+обровоо до свидания.',    wrongPhone: 'Номер телефона не найден. Если вы кли+ент, перезвоните с номера, который использовали для оформления заказа. Если вы курьер, перезвоните с номера, который зарегистрирован в нашей системе. Или свяжитесь со специалистом,,- нажав клавишу решетка.',    wrongPhoneGoodbye: 'Вы ничего не выбрали. Всего доброго, до свидания!',    courierIsCalling: `Вам звонит курьер по поводу доставки вашего заказа, - - ${store.data.order_number}`,    clientIsCalling: `Вам звонит клиент по поводу доставки заказа, - - ${store.data.order_number} `,    courierUnavailable: 'Похоже,,, курь+ер недоступен. Пожалуйста,,, перезвоните через п+ару мин+ут. Всего д+обровоо до свидания.',    clientUnavailable: 'Похоже,,, абонент недоступен. Пожалуйста,,, перезвоните через пп+ару мин+ут. Всего д+обровоо до свидания.',    waitForCourier: 'Ожидайте на линии,, - соединяю с курьером.',    waitForClient: 'Ожидайте на линии,, соединяю с клиентом.'}VoxEngine.addEventListener(AppEvents.Started, async e => {    VoxEngine.addEventListener(AppEvents.CallAlerting, callAlertingHandler);})async function callAlertingHandler(e) {    store.call = e.call;    store.caller = e.callerid;    store.call.addEventListener(CallEvents.Connected, callConnectedHandler);    store.call.addEventListener(CallEvents.Disconnected, callDisconnectedHandler);    store.call.answer();}async function callDisconnectedHandler(e) {    await sendResultToDb();    VoxEngine.terminate();}async function callConnectedHandler() {    store.call.handleTones(true);    store.call.addEventListener(CallEvents.RecordStarted, (e) => {        store.data.call_record = e.url;    });    store.call.record();    store.call.addEventListener(CallEvents.ToneReceived, dtmfHandler);    await say(phrases.start);    addInputTimeouts();}function dtmfHandler(e) {    clearInputTimeouts();    store.input += e.tone;    Logger.write('Введена цифра ' + e.tone)    Logger.write('Полный код ' + store.input)    if (e.tone === '#') {        store.data.need_operator = "Да";        store.call.removeEventListener(CallEvents.ToneReceived);        store.call.handleTones(false);        callOperator();        return;    }    if (!wrongPhone) {        if (store.input.length >= 5) {            repeatAskForInput = true;            Logger.write(`Получен код ${store.input}. `);            store.call.handleTones(false);            store.call.removeEventListener(CallEvents.ToneReceived);            handleInput(store.input);            return;        }    }    addInputTimeouts();}function addInputTimeouts() {    clearInputTimeouts();    if (firstTimeout) {        Logger.write('Запущен таймер на срабатывание фразы для связи с оператором');        shortInputTimerId = setTimeout(async () => {            await say(phrases.connectToOpearator);        }, 1500);        firstTimeout = false;    }    longInputTimerId = setTimeout(async () => {        Logger.write('Сработал таймер на отсутствие ввода от пользователя ' + longInputTimerId);        store.call.removeEventListener(CallEvents.ToneReceived);        store.call.handleTones(false);        if (store.input) {            handleInput(store.input);            return;        }        if (!repeatAskForInput) {            Logger.write('Просим пользователя повторно ввести код');            store.call.handleTones(true);            store.call.addEventListener(CallEvents.ToneReceived, dtmfHandler);            await say(phrases.repeat);            addInputTimeouts();            repeatAskForInput = true;        } else {            Logger.write('Код не введен. Завершаем звонок.');            await say(inputRecieved ? phrases.wrongOrderGoodbye : phrases.noInputGoodbye);            store.call.hangup();        }    }, 8000);    Logger.write('Запущен таймер на отсутствие ввода от пользователя ' + longInputTimerId);}function clearInputTimeouts() {    Logger.write(`Очищаем таймер ${longInputTimerId}. `);    if (longInputTimerId) clearTimeout(longInputTimerId);    if (shortInputTimerId) clearTimeout(shortInputTimerId);}async function handleInput() {    store.data.order_number = store.input;    Logger.write('Ищем совпадение в kvs по введенному коду: ' + store.input)    inputRecieved = true;    let kvsAnswer = await ApplicationStorage.get(store.input);    if (kvsAnswer) {        store.data.order_search = 'Заказ найден';        Logger.write('Получили ответ от kvs: ' + kvsAnswer.value)        let { courier, client } = JSON.parse(kvsAnswer.value);        if (store.caller == courier) {            Logger.write('Звонит курьер')            store.callee = client;            store.data.sub_status = 'Курьер';            store.data.phone_search = 'Телефон найден';            callCourierOrClient();        } else if (store.caller == client) {            Logger.write('Звонит клиент')            store.callee = courier;            store.data.sub_status = 'Клиент';            store.data.phone_search = 'Телефон найден';            callCourierOrClient();        } else {            Logger.write('Номер звонящего не совпадает с номерами, полученными из kvs');            wrongPhone = true;            store.data.phone_search = 'Телефон не найден';            store.input = '';            store.call.handleTones(true);            store.call.addEventListener(CallEvents.ToneReceived, dtmfHandler);            await say(phrases.wrongPhone);            addInputTimeouts();        }    } else {        Logger.write('Совпадение в kvs по введенному коду не найдено');        store.data.order_search = 'Заказ не найден';        store.input = '';        store.call.handleTones(true);        store.call.addEventListener(CallEvents.ToneReceived, dtmfHandler);        await say(phrases.wrongOrder);        Logger.write(`Очищаем таймер ${longInputTimerId}. `);        addInputTimeouts();    }}async function callCourierOrClient() {    clearInputTimeouts();    Logger.write('Начинаем звонок курьеру/клиенту');    await say(store.data.sub_status === 'Курьер' ? phrases.waitForClient : phrases.waitForCourier, store.call);    const secondCall = VoxEngine.callPSTN(store.callee, store.callid);    store.call.startPlayback('http://cdn.voximplant.com/toto.mp3');    secondCall.addEventListener(CallEvents.Connected, async () => {        store.data.sub_available = 'Да';        await say(store.data.sub_status === 'Курьер' ? phrases.courierIsCalling : phrases.clientIsCalling, secondCall);        store.call.stopPlayback();        VoxEngine.sendMediaBetween(store.call, secondCall);    });    secondCall.addEventListener(CallEvents.Disconnected, () => {        store.call.hangup();    });    secondCall.addEventListener(CallEvents.Failed, async () => {        store.data.sub_available = 'Нет';        store.call.stopPlayback();        await say(store.data.sub_status === 'Курьер' ? phrases.clientUnavailable : phrases.courierUnavailable, store.call);        store.call.hangup();    });}async function callOperator() {    Logger.write('Начинаем звонок оператору');    await say(phrases.connectingToOpearator, store.call);    store.call.startPlayback('http://cdn.voximplant.com/toto.mp3');    store.operator_call = VoxEngine.callPSTN(store.operatorNumber, store.callid);    store.operator_call.addEventListener(CallEvents.Connected, async () => {        store.data.call_operator = 'Оператор свободен';        VoxEngine.sendMediaBetween(store.call, store.operator_call);    });    store.operator_call.addEventListener(CallEvents.Disconnected, () => {        store.call.hangup();    });    store.operator_call.addEventListener(CallEvents.Failed, async () => {        store.data.call_operator = 'Оператор занят';        await say(phrases.operatorUnavailable, store.call);        store.call.hangup();    });}async function sendResultToDb() {    Logger.write('Данные для отправки в БД');    Logger.write(JSON.stringify(store.data));    const options = new Net.HttpRequestOptions();    options.headers = ['Content-Type: application/json'];    options.method = 'POST';    options.postData = JSON.stringify(store.data);    await Net.httpRequestAsync('https://voximplant.com/', options);}function say(text, call = store.call, lang = VoiceList.Yandex.Neural.ru_RU_alena) {    return new Promise((resolve) => {        call.say(text, lang);        call.addEventListener(CallEvents.PlaybackFinished, function callback(e) {            resolve(call.removeEventListener(CallEvents.PlaybackFinished, callback));        });    });};

Код тщательно прокомментирован, но в некоторые моменты углубимся подробнее.

Вводим номер заказа

Первое, что мы делаем при звонке просим звонящего ввести номер заказа и обрабатываем введенное значение с помощью функции dtmfHandler.

store.input += e.tone;

Если звонящий ввел #, сразу соединяем его с оператором:

if (e.tone === '#') {    store.data.need_operator = "Да";    store.call.removeEventListener(CallEvents.ToneReceived);    store.call.handleTones(false);    callOperator();    return;}

Если он ввел последовательность из 5 цифр, вызываем функцию handleInput:

if (store.input.length >= 5) {    repeatAskForInput = true;    Logger.write('Получен код ${store.input}. ');    store.call.handleTones(false);    store.call.removeEventListener(CallEvents.ToneReceived);    handleInput(store.input);    return;}

Ищем заказ в хранилище

Здесь мы будем сравнивать введенный номер заказа с номером в хранилище, используя метод ApplicationStorage.get(), в качестве ключа используем введенную последовательность:

store.data.order_number = store.input;Logger.write('Ищем совпадение в kvs по введенному коду: ' + store.input)inputRecieved = true;let kvsAnswer = await ApplicationStorage.get(store.input);

Если заказ найден, получаем для него номера клиента и курьера:

if (kvsAnswer) {    store.data.order_search = 'Заказ найден';    Logger.write('Получили ответ от kvs: ' + kvsAnswer.value)    let { courier, client } = JSON.parse(kvsAnswer.value);

Теперь осталось разобраться, кому звонить. Если номер звонящего принадлежит курьеру, будем выполнять переадресацию на клиента, если клиенту на курьера. В этом нам поможет функция callCourierOrClient:

if (store.caller == courier) {    Logger.write('Звонит курьер')    store.callee = client;    store.data.sub_status = 'Курьер';    store.data.phone_search = 'Телефон найден';    callCourierOrClient();} else if (store.caller == client) {    Logger.write('Звонит клиент')    store.callee = courier;    store.data.sub_status = 'Клиент';    store.data.phone_search = 'Телефон найден';    callCourierOrClient();}

Если номера нет в хранилище, просим перезвонить с другого номера, который указывался при оформлении заказа:

else {    Logger.write('Номер звонящего не совпадает с номерами, полученными из kvs');    wrongPhone = true;    store.data.phone_search = 'Телефон не найден';    store.input = '';    store.call.handleTones(true);    store.call.addEventListener(CallEvents.ToneReceived, dtmfHandler);    await say(phrases.wrongPhone);    addInputTimeouts();}

И наконец, обрабатываем вариант, когда номер заказа не был найден в базе. В этом случае просим попробовать ввести его снова, предварительно удостоверившись, что номер верный:

else {    Logger.write('Совпадение в kvs по введенному коду не найдено');    store.data.order_search = 'Заказ не найден';    store.input = '';    store.call.handleTones(true);    store.call.addEventListener(CallEvents.ToneReceived, dtmfHandler);    await say(phrases.wrongOrder);    Logger.write(`Очищаем таймер ${longInputTimerId}. `);    addInputTimeouts();}

Звоним клиенту/курьеру

Переходим непосредственно к звонку клиенту/курьеру, то есть к логике функции callCourierOrClient. Здесь мы сообщим звонящему, что переводим его звонок на курьера/клиента, и включим музыку на ожидание. С помощью метода callPSTN позвоним клиенту или курьеру (в зависимости от того, чей номер был ранее идентифицирован как номер звонящего):

await say(store.data.sub_status === 'Курьер' ? phrases.waitForClient : phrases.waitForCourier, store.call);const secondCall = VoxEngine.callPSTN(store.callee, store.callid);store.call.startPlayback('http://cdn.voximplant.com/toto.mp3');

В этот же момент сообщим второй стороне о том, что звонок касается уточнения информации по заказу:

secondCall.addEventListener(CallEvents.Connected, async () => {    store.data.sub_available = 'Да';    await say(store.data.sub_status === 'Курьер' ? phrases.courierIsCalling : phrases.clientIsCalling, secondCall);    store.call.stopPlayback();    VoxEngine.sendMediaBetween(store.call, secondCall);});

Обработаем событие дисконнекта:

secondCall.addEventListener(CallEvents.Disconnected, () => {    store.call.hangup();});

И оповестим звонящего, если вторая сторона недоступна:

secondCall.addEventListener(CallEvents.Failed, async () => {    store.data.sub_available = 'Нет';    store.call.stopPlayback();    await say(store.data.sub_status === 'Курьер' ? phrases.clientUnavailable : phrases.courierUnavailable, store.call);    store.call.hangup();});

За все фразы, который произносит робот, отвечает функция say, а сами фразы перечислены в ассоциативном массиве phrases. В качестве TTS провайдера мы используем Yandex, голос Alena:

function say(text, call = store.call, lang = VoiceList.Yandex.Neural.ru_RU_alena) {    return new Promise((resolve) => {        call.say(text, lang);        call.addEventListener(CallEvents.PlaybackFinished, function callback(e) {            resolve(call.removeEventListener(CallEvents.PlaybackFinished, callback));        });    });};

Кроме всего прочего, наш сценарий записывает звонки, используя метод record, и показывает, как можно сохранить статистику в базу данных (в нашем коде за это отвечает функция sendResultToDb). Это очень важно для бизнеса, поскольку позволяет анализировать статистику, обеспечивать контроль качества и оперативно решать спорные ситуации, которые могли возникнуть в процессе доставки заказа.

Тестируем

Когда полный код добавлен в сценарий, а данные по заказу в хранилище, смело начинайте тестировать.

Позвоним с телефона клиента или курьера на номер, арендованный в панели. Затем введем номер заказа (в нашем случае это 12345) и будем ждать соединения со второй стороной.

Если все сделано верно, клиент и курьер смогут созваниваться и обсуждать детали заказа, не зная настоящих номеров друг друга, а значит, не нарушая прайваси. Круто, не так ли?) Желаем вам успешной разработки и беспроблемной доставки!

P.S. Также мой коллега недавно рассказал, как обезопасить общение клиента и курьера с помощью Voximplant Kit (наш low-code/no-code продукт). Если эта тема вас заинтересовала, переходите по ссылке :)

Подробнее..

Из песочницы Как делать в два раза больше и получать от этого удовольствие

29.07.2020 18:17:33 | Автор: admin
Привет, Хабр! Я Максим, бизнес-аналитик в Тинькофф. В этой статье я поделюсь опытом нашей команды: как выполнять в два раза больше задач, переписать с нуля легаси-проект и при этом не умереть.



Наша команда развивает кредитные и гарантийные продукты для юридических лиц. Направление молодое, первые выдачи начались в 2018 году. Мы касдевили и изучали рынок, запустили овердрафт, кредит на увеличение оборота и банковские гарантии. В планах был запуск кредита под контракт, залогового кредитования и других продуктов.

Симптомы


Наш процесс от быстрого запуска продуктовых механик накапливал костыли. С ними мы мирились и, как несложно догадаться, однажды заметили, что задачи на процессы стали разрабатываться слишком долго. Это выражалось в постоянном переносе релизов, задачи не выполнялись в срок. Копились баги, влияющие на небольшой, специфичный сегмент клиентов.

Возникали ситуации, когда релиз по очередной задаче, который уже переезжал на два месяца, снова переносился на неопределенный срок. В это же время релиз фичи на овердрафте ломал банковские гарантии. Случилась рассинхронизация понимания продукта. Разработчики считали важными вещи, на которые в бизнес-команде даже не обращают внимание. Ключевые особенности продуктов же, наоборот, оставались неизвестными.

Для выхода из кризисной ситуации создали рабочую группу, перед которой встала задача из плохо и долго сделать быстро и хорошо. Для себя мы ставили цели повысить производительность и уменьшить число багов.

Проблемы


Углубившись в процессы команды, мы нашли клубок проблем, которые нельзя было решить по отдельности, они требовали комплексного подхода:

  • старый технологический стек;
  • долгий и кривой канбан-процесс;
  • бизнес лез во внутренние дела разработки;
  • команда разработчиков теряла интерес к проекту;
  • общая токсичность.

Расскажу более подробно, в чем это выражалось.

Старый технологический стек. Наш процесс был написан на IBM ODM системе с рядом особенностей, которые мешали команде. Детально их описал наш архитектор Денис Kotskin в кейсе соседнего проекта (правда, там был IBM BPM, но в целом все справедливо). Отдельно отмечу, что на рынке нет специалистов с опытом работы в этой системе.

Долгий процесс delivery. Официально мы позиционировали себя как канбан-команда, но процессы выглядели как нечто среднее между ватерфолом и скрамом. Наследие каскадной разработки в том, что бизнес, разработка и тест общаются только в карточке Jira. У всех была четкая мысль: Я свою часть работы сделал, моя хата с краю.

К канбану мы пришли не сразу. На самом старте использовали скрам со спринтами, который лучше себя показывает на молодых проектах. Потом поняли, что команде морально тяжело переносить задачи из спринта в спринт, и перешли на канбан. Дальше стало понятно, что никто не умеет работать с входным потоком, начала возникать цикличность разработки. Она проявлялась в том, что задачи от бизнеса поступали раз в неделю, потом их оценивала команда, становилось понятно, что ничего не понятно, и задача отлетала на следующую неделю. При этом на словах мы делали канбан и искали узкие места.

Я понимаю, что идеи канбана и скрама не противоречат друг другу и есть примеры, когда комбинация методологий показывает хорошие результаты. Но хочу подчеркнуть, что имела место радикальная позиция чистого канбана. Сильно тормозило и большое число возвратов из теста в разработку, что сигнализировало о низком качестве проработки задачи на ранних этапах.

Нарушение ролевой модели. Бизнес-аналитики занимались архитектурой придумывали техническое решение задачи. Это приводило к тому, что часто отказывались от проведения глубокого discovery в пользу проработки и спецификации решения, и эта халтура в паре со слабой архитектурой кратно тормозила разработку.

Потеря командой интереса к проекту. Талантливая, амбициозная, молодая команда. В чистом виде стартап. После запуска и масштабирования начались болезни роста. Постоянное давление со стороны бизнеса, сложность разработки из-за отсутствия рефакторинга, накопившиеся внутренние проблемы, бэклог на месяцы вперед привели к банальной усталости и выгоранию.

Из-за всего вышеперечисленного атмосфера в команде испортилась. Проблемы фиксировались на ретро, но не решались, а кочевали из недели в неделю. Общая токсичность зашкаливала, любой диалог по работе скатывался во взаимные упреки.

Что мы сделали


Откровенно говоря, в начале мы понимали только то, что надо переписывать процесс с нуля, чтобы избавиться от костылей, и усилить команду опытными разработчиками. Спустя полгода могу назвать еще две вещи, которые нам помогли:

  • Перестроение канбан-процесса. Спасибо деливери-центру Тинькофф Бизнеса, который оперативно вник в нашу проблематику и помог адаптировать Jira.
  • Синхронизация бизнеса и IT. Тут нас двигала вера в то, что команда должна хорошо понимать продукт, а не просто выполнять задачи, которые ей принесут.

В итоге переписывание процесса решило проблему стека технологий и помогло избавиться от костылей. Переосмысление канбан-процесса помогло перестроить ролевую модель и уменьшить число возвратов, то есть увеличило скорость поставки задач на прод. Ряд синхронизационных мероприятий и переосмысление текущих форматов выправили общую атмосферу.

Часть 1. Переписывание процесса


Итак, мы начали переписывать процесс с IBM ODM на Camunda. Причины выбора Camunda описаны в статье Николая nshipyakov.

В заявочных процессах мы используем такой термин, как стадия логически замкнутая часть процесса, с понятным смыслом для клиента, например Сбор документов или Подписание кредитного договора. Первой задачей для нас был запуск кредита под контракт. Мы поняли, что логика трех стадий для него специфична, а остальные ничем не отличаются от аналогичных стадий оборотного кредита. Собственно, мы написали три стадии нового продукта на Camunda. В дальнейшем переписывали целую стадию при появлении бизнес-задачи на серьезное ее изменение.

Возникает естественный вопрос: как мы договаривались с бизнесом? Понятно, что переписывать уже работающий функционал это дольше, чем доработать его на старом движке. Все получилось достаточно просто: коллеги были готовы инвестировать в новый процесс, потому что видели, как круто это сработало на соседнем проекте (и снова привет, Денис Kotskin). При этом сроки разработки на новом движке были не намного больше, так как мы начали ротацию: выгоревшие ребята перешли в другие проекты, на замену наняли сотрудников с опытом разработки и проектирования бизнес-процессов.

Часть 2. Изменение порядка ведения задач


При изменении процесса разработки опирались на следующие установки:

  • Не должно быть этапов, которые не отражены на доске.
  • Техническую экспертизу надо отдать в команду.
  • Команда должна понимать, как задача влияет на бизнес.

Изменив канбан-процесс, мы выделили новые этапы, которые раньше неявно проходили на стадии разработки: это архитектура и встреча трех амиго. Естественно, архитектура не проводится по минорным изменениям, но вот встречу трех амиго мы стараемся проводить по любой задаче. Про способ Три амиго есть статья у Насти Travieso. Хочу сказать отдельное спасибо Насте: ее тренинг по Agile-тестированию вдохновил нас на многие изменения внутри команды.

Данные о ценности продукта команда получает в формате user story и оценки влияния задачи на продукт. Бывает трудно отличить блеф ушлых бизнес-заказчиков. Например, оценка Это правило важное, будет проверяться на всех заявках намного менее информативна, чем Мы провели аналитику, правило будет отказывать по 10 дополнительным заявкам в неделю. Поэтому перед тем, как отдать задачу в разработку, качество написанной ценности валидирую я, как представитель бизнес-команды, который разделяет ценности разработчиков.

Также мы отказались от практик, которые для нас не работали. Например, теперь мы реже проводим ретро, только по необходимости когда накапливается потребность что-то обсудить. Случается это примерно раз в месяц. Обязательно решаем проблемы, обозначенные на ретро, так как важно, чтобы каждый член команды видел положительные изменения по вопросам, которые его тяготят.

Мы перестали пользоваться сторипоинтами и командной оценкой задачи работаем с due-датами, которые получаем от бизнеса, и в зависимости от них управляем входным потоком. На крупных задачах, которые делаются пару месяцев, проводим декомпозицию: она дает возможность делать своеобразные чек-поинты и повысить точность due. Чтобы следить за прогрессом, периодически встречаемся и обсуждаем, успеваем ли мы к сроку. Если видим, что нет, корректируем входной поток и берем меньше маленьких задач. По точности попадания в срок могу сказать только то, что по текущей нашей крупной задаче укладываемся в due.

Касательно перераспределения ролей усилили команду архитектором и вторым системным аналитиком. Бизнес-команда старается понятно объяснять, что нужно в задаче, какую ценность она несет, но не советовать и не лезть во внутреннюю кухню разработки. За бизнес-командой присматриваю тоже я.

Часть 3. Синхронизация IT- и бизнес-команды


Для синхронизации бизнеса и разработчиков используем несколько форматов.

Демо по задаче. Это встреча всех заинтересованных портфельных аналитиков, рискового департамента, маркетологов и айтишников с обсуждением ценности, проблематики задачи и технического решения.

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

Ретро по задаче. На этой встрече обсуждаем проблемы разработчиков и заказчиков, с которыми столкнулись в процессе разработки задачи. Проводим после пострелизной аналитики когда страсти утихли и все готовы к конструктивному диалогу. После выяснения причин формируем точки роста и облако идей, которые попробуем в дальнейшем.

Проводим лекции о продуктах в формате ликбеза и последующего обсуждения. Их цель погрузить ребят из IT в бизнес-контекст. По собираемой обратной связи в виде опроса с максимально общей формулировкой Оцените сегодняшнюю лекцию средняя оценка 8,5 из 10.

Итоги


Через полгода мы переписали больше 80% процессов, запустили кредит под контракт полностью на новом движке. Атмосфера в команде улучшилась, и мы стали эффективнее. Чтобы убедиться в этом, провели опрос команды и сняли статистику с Jira.

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

Производительность (количество задач в день) за это время выросла в два раза. Естественно, не за счет декомпозиции: мы заранее договорились о неких стандартах, которых придерживаемся.

Количество возвратов из теста в разработку незначительно уменьшилось. То есть при кратном увеличении числа выводимых на прод задач количество возвратов не возросло. Это свидетельствует об улучшении качества проработки задачи на этапах discovery и архитектуры. Количество багов, находимых на проде, не изменилось.

Чему мы научились


Теперь сформулирую несколько идей, которые мы с командой извлекли из нашего опыта. Если у вас в командах похожие проблемы надеюсь, они помогут и вам.

  • Отказывайтесь от практик, которые мешают. Выявить их легко они приносят дискомфорт команде. Сторипоинты, планирования и ретро не панацея, а инструмент. Если инструмент неудобен используйте другой.
  • Обязательно делитесь опытом, даже самым, на ваш взгляд, незначительным, и впитывайте все знания, которые до вас доходят. Помогают внутренние митапы, ликбезы по конкретным темам и все в таком духе.
  • Возвраты зло, которое надо побороть, чтобы работать эффективно. Это касается возвратов из теста в разработку, возвратов с прода в виде багов, из разработки в discovery в виде вопросов к заказчику.
  • Один человек с позитивным опытом тимлида кратно повышает эффективность за счет инвестиций в развитие команды. Организация one-one-встреч, нахождение точек роста сотрудников, поддержание общей нетоксичной атмосферы. Этим человеком у нас был Саша Shoom3301, спасибо ему огромное.
  • У каждого своя задача: у бизнеса делать бизнес, у IT придумывать крутые решения. Нельзя лезть в вотчину другого подразделения, это убивает креатив и вызывает взаимную неприязнь.
Подробнее..

Геопространственное моделирование с применением методов машинного обучения

18.06.2021 18:20:53 | Автор: admin


Всем привет! Меня зовут Константин Измайлов, я руководитель направления Data Science в Delivery Club. Мы работаем над многочисленными интересными и сложными задачами: от формирования классических аналитических отчетов до построения рекомендательных моделей в ленте приложения.

Сегодня я расскажу про одну из задач, которую мы решали: про автоматизацию построения зон доставки ресторанов. Зона доставки это область вокруг заведения, и когда вы в ней находитесь, этот ресторан отображается в списке доступных для заказа. Несмотря на всю простоту формулировки, построение зон доставки ресторанов достаточно непростая задача, в которой встречается много подводных камней не только со стороны технической реализации, но и на этапе внедрения. Я расскажу про предпосылки появления этой задачи, подходы (от более простого к сложному) и подробно рассмотрю алгоритм построения зоны доставки.

Статья будет полезна не только техническим специалистам, которые могут вдохновиться нашими подходами по работе с геоданными, но и менеджерам, которые смогут прочитать про процесс интеграции нашей модели в бизнес, увидев грабли, а самое главное результаты, которых удалось добиться.

Статья написана по мотивам выступления с Евгением Макиным на конференции Highload++ Весна 2021. Для тех, кто любит видео, ищите его в конце статьи.

Бизнес-модель работы Delivery Club


Бизнес-модель Delivery Club состоит из двух частей:

  • ДДК (доставка Деливери Клаб): мы передаем заказ в ресторан и доставляем его клиенту, то есть ресторану остается только приготовить заказ к определенному времени, когда придет курьер.
  • МП (маркетплейс): мы передаем заказ в ресторан, а он своими силами доставляет заказ в пределах своей согласованной зоны.

В ДДК-модели мы отвечаем за все, кроме приготовления блюд. Это порождает много алгоритмов автоматизации процессов, и построение зон доставки один из них, поэтому будем рассматривать его на примере этой бизнес-модели.

При этом построить зону доставки это полбеды. Ведь география ДДК огромна и охватывает более 170 городов России с тысячами ресторанов в каждом из них, и у каждого ресторана свои индивидуальные особенности, которые необходимо учитывать при построении эффективного сервиса доставки.

Рисуем зону доставки ресторана


С одной стороны, мы хотим сохранить качество нашего сервиса, минимизируя время доставки до клиента: меньше зона курьер быстрее добирается от ресторана до клиента. При этом все партнеры разные, и к каждому должен быть индивидуальный подход. Например, кофейня хочет доставлять в близлежащий офис и супербыстро, иначе горячий кофе остынет и будет невкусным. А вот для ресторана, который специализируется на кейтеринге, время доставки хоть и важно, но остается вторичным фактором по сравнению с максимальным охватом потенциальных клиентов. Также не стоит забывать про курьеров, которым, к примеру, придется доставлять заказ на другую сторону реки :)



Как процесс выглядел раньше


Исторически сложилось, что за построение зон доставки отвечали координаторы операционной команды, которые вручную перерисовывали зоны для каждого из наших партнеров. Это было трудоемко, требовало должной сноровки и, самое главное, терпения. Несмотря на набор правил, в процессе присутствовала субъективная оценка. Даже можно было заметить индивидуальный стиль отрисовки зон, который прослеживался у каждого из менеджеров.

В итоге мы могли получить вот такие зоны: ошибка ли это уставшего менеджера или индивидуальный стиль остается лишь догадываться.



Стоит упомянуть и про SLA (Service Level Agreement соглашение о максимальной длительности отрисовки зоны доставки для одного партнера): онбординг партнера или подготовка его зоны для внедрения в нашу платформу составляли порядка 40 минут для одного заведения. Представьте, что к вам подключилась городская сеть с сотней ресторанов, а если это ещё и жаркий сезон, например, после проведения рекламной акции Вот наглядное доказательство неэффективности ручной отрисовки:

$T = R * SLA = 100 * 40\ минут =\ \sim 67\ часов\ ручной\ работы$


где $T$ время, которое будет затрачено на отрисовку всех зон доставки партнера,
$R$ количество ресторанов,
$SLA$ время на отрисовку одной зоны.

Проблемы ручной отрисовки зон:


  • непостоянство результата;
  • непрозрачная логика;
  • субъективизм в принятии решений о форме зоны и ее характеристиках;
  • однообразный подход без учета индивидуальных особенностей партнера;
  • крайне низкая скорость построения зон;
  • ручной труд менеджеров;
  • отсутствие гибкости в управлении характеристиками зон доставки (зона представляла собой единую нераздельную сущность, внутри которой нельзя было установить разную стоимость доставки в зависимости от расстояния между клиентом и рестораном).

Baseline


Первый простой подход к решению построение зоны в форме круга вокруг ресторана. Очень быстро реализовать, понятный алгоритм.

При этом оставались недостатки:

  • артефакты в зонах доставки (стандартный случай с переходом через реку);
  • единообразный подход к партнерам (не учитываются индивидуальные KPI партнеров).

Через Delivery Club ежедневно проходят сотни тысяч заказов, и артефакты зон доставки могли дать большую нагрузку на колл-центр.



Пробовали алгоритмы на основе Convex Hull и Alpha Shape, с их помощью можно было бы устранить артефакты. Например, используя границы водных массивов как опорные точки для построения форм удавалось обходить реки. Однако всё равно отсутствовал индивидуальный подход к партнерам.

Преимущества технологии H3


Мы стремились прийти к универсальному решению. Каких-то простых и популярных подходов найти не удалось, поэтому мы сосредоточились на разработке собственного алгоритма. А за основу взяли технологию H3 компании Uber.


Источник: eng.uber.com/h3

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

Также стоит отметить, что:

  1. Существует хорошая библиотека для работы с H3, которую и выбрала наша команда в качестве основного инструмента. Библиотека поддерживает многие языки программирования (Python, Go и другие), в которых уже реализованы основные функции для работы с гексагонами.
  2. Наша реляционная аналитическая база Postgres поддерживает работу с нативными функциями H3.
  3. При использовании гексагональной сетки благодаря ряду алгоритмов, работающих с индексами, можно очень быстро получить точную информацию о признаках в соседних ячейках, например, определить вхождение точки в гексагон.
  4. Преимуществом гексагонов является возможность хранить информацию о признаках не только в конкретных точках, но и в целых областях.

Алгоритм построения зон доставки


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


  2. Убираем точки-выбросы. Анализируем все постройки и сразу отбрасываем те, которые нас не интересуют. Например, какие-то мелкие нежилые объекты. Далее с помощью DBSCAN формируем кластеры точек и отбрасываем те, которые для нас не являются важными: например, если кластер находится далеко от ресторана или нам экономически невыгодно доставлять туда.


  3. Далее на основе очищенного набора точек применяем триангуляцию Делоне.


  4. Создаем сетку гексагонов H3.


  5. Определяем, к какому треугольнику принадлежит каждый гексагон H3, и для этого гексагона определяем расстояние от ресторана с помощью аппроксимации по трем вершинам треугольника (кто хочет чуть больше разобраться в геоматематике и понять рациональность использования тех или иных геоформ, вот ссылка на оригинал статьи Uber).


  6. Далее оптимизируем функцию ошибки, которая зависит от нашей задачи, постепенно удаляя с внешней стороны зоны по одному гексагону. При этом мы следим за формой нашей зоны, она должна оставаться цельной.



    В текущей версии алгоритма мы используем следующие функции ошибок:

    • минимизацию времени доставки при фиксированном покрытии;
    • максимизацию охвата пользователей при фиксированном времени доставки.

    Пример функции ошибки для минимизации времени доставки:

    ${L_{min}}_{time}\;=\;min(\sum_{i=1}^n\;({t_{rest}}_i)/n),$


    где $L_{min_ {time}}$ функция ошибки минимизации времени доставки с фиксированным покрытием,
    $t_{rest_ {i}}$ время от ресторана i до клиента,
    $n$ количество клиентов в зоне доставки.

  7. Далее строим временной градиент в получившихся зонах (с очищенными выбросами) и с заранее определенными интервалами (например, по 10-минутным отрезкам пешего пути).



Зона построена, теперь очередь оптимизации бизнес-метрики. В алгоритме используются оптимизации покрытия клиентов с фиксированием времени доставки и оптимизация времени доставки с фиксированным покрытием.

Для оптимизации покрытия мы жадно удаляем гексагоны один за другим исходя из прогноза количества заказов. Делаем это с помощью алгоритма градиентного бустинга, где, в первую очередь, используется информация о конкурентах ресторана, населении, конверсионности, а также информация о популярности ресторана из внешних источников.

Внедрение


Алгоритм написан, технические и нагрузочные тесты пройдены, остается самое важное: внедрить алгоритм в бизнес-процессы.

Здесь мы столкнулись с тем, что очень трудно было менять сложившиеся бизнес-процессы, которые были налажены годами. Операторы очень не хотели передавать свои KPI машине.

С другой стороны, сами партнеры с опаской подходили к новомодной фиче, которую им предлагали протестировать в бою. У них не было уверенности, что такую сложную задачу можно автоматизировать с сохранением прежнего качества. А те, кто отваживались, тестировали странно: предоставляли заведения с маленьким количеством заказов, для которых было достаточно трудно накопить историю для нахождения статистически значимых различий.

Но тут пришел COVID-19

  • тысячи ресторанов закрывают свои филиалы;
  • увеличиваются зоны доставки;
  • сокращается меню и меняется целевая аудитория;
  • меняется формат работы ресторанов;
  • поменялось еще и географическое распределение заказов: до начала пандемии много заказов приходило из офисов, а во время пандемии все сидели дома и заказывали оттуда.

Всё это отчасти помогло нам убедить партнёров активнее тестировать и переходить на алгоритм автоматического построения зон доставки. В итоге, за первую неделю после начала пандемии и введения ограничения для ресторанов порядка 50% наших партнеров перешло на новые зоны доставки, которые уже были построены автоматически, а в течение следующих 3 недель уже почти не осталось партнеров с зонами, построенными вручную.

Оценка


После решения всех горящих проблем нам нужно было немного отдышаться и понять, что мы вообще наделали. Для этого воспользовались A/B-тестом, а точнее его вариацией switch-back. Мы сравнивали зоны ресторанов с одинаковыми входными параметрами, оценивали GMV и время доставки, где в качестве контроля у нас были простые автоматически отрисованные зоны в виде окружностей и прямоугольников, либо зоны, отрисованные операторами вручную.



В итоге для разных алгоритмов мы получили прирост GMV в тестовых алгоритмических зонах при одинаковом времени доставки, либо уменьшение времени доставки в случае с алгоритмом с фиксированным клиентским покрытием.

А время, затрачиваемое на построение зон для партнера из примера выше, теперь выглядит более оптимистично:

$T = 100 * 3,6\ секунды =\ \sim 6\ минут$


Ускорение в 670 раз!

Текущая ситуация и планы


Сервис работает в production. Зоны автоматически строятся по кнопке. Появился более гибкий инструмент для работы со стоимостью доставки для клиентов в зависимости от их удаленности от ресторана. 99,9% ресторанов (изредка ещё нужно ручное вмешательство) перешли на алгоритмические зоны, а наш алгоритм поспособствовал переходу бэкенда на H3.

В начале пандемии мы благодаря алгоритму смогли быстро адаптироваться под меняющиеся условия, ускорили онбординг партнеров и сэкономили время на отказе от ручного труда. В денежном выражении выгода значительна.

Работа с географией и зонами открыла нам дорогу к новым проектам: рекомендации для размещения, гранулярная работа с зонами доставки, более точный прогноз длительности доставки для клиента, ценообразование.

Теперь мы планируем начать работать с зонами динамически, автоматически изменяя их в реальном времени в зависимости от внешних обстоятельств (например, погоды), оптимизировать скорость работы, внедрить новые оптимизации.

Всем спасибо!

Подробнее..

Почему бизнес хочет DevOps и что нужно знать инженеру, чтобы говорить с ним на одном языке

27.10.2020 14:04:24 | Автор: admin
Последние несколько лет мы при каждом удобном случае снова и снова обсуждаем, что же такое DevOps. Это уже порядком надоело, но раз всё еще происходит, значит есть проблема проблема взаимодействия бизнеса и инженеров.

Я часто вижу, как люди, пришедшие к применению DevOps из разных сфер, говорят на разных языках и понимают под словом DevOps совершенно разное. В итоге в какой-то момент времени оказывается, что участники проекта по трансформации абсолютно не понимают и друг друга, и зачем нужен весь этот DevOps тоже.



Я не хочу рассуждать о том, что такое DevOps и какое его понимание более правильное. Мне интересно проследить за эволюцией процессов в IT и разобраться, что бизнес хочет получить от внедрения DevOps, что это значит для инженеров и как нам быть на одной волне.

N.B.: Материал для этой статьи лёг в основу моего выступления на DevOps Live, поэтому вместо чтения можно послушать доклад он вполне сойдёт за подкаст.

На примере клиентов ITSumma я вижу, как изменились запросы бизнеса в последние несколько лет. Мы занимаемся сопровождением сложных информационных систем с 2008 года, и поначалу это в основном была работа по обеспечению отказоустойчивости сайтов, но теперь появились кардинально новые запросы. Раньше важнее всего было, чтобы продакшен стабильно работал, был готов к масштабированию, а в случае аварии быстро поднимался. Теперь не менее важным стало обеспечение отказоустойчивости платформ разработки и систем выкладки.
В крупных компаниях произошло смещение системы ценностей: стабильная работа develop-окружения теперь так же важна, как и надежность продакшена.
Чтобы проследить траекторию и понять, что изменилось в мышлении и где мы теперь, погрузимся ненадолго в историю разработки программного обеспечения.

Немного истории разработки


Я считаю, что есть, условно, четыре эпохи в развитии принципов разработки программного обеспечения. И что важно на всех этапах существовала и доставка ПО. Просто в разное время эту доставку обеспечивали разные люди.

Мейнфреймы. 19601980-е годы


Особенности периода. Начальный этап с появления компьютеров как таковых до примерно 1985-го года характеризуется тем, что программы разрабатывают для себя. Небольшие группы разработчиков реализуют довольно ограниченную функциональность, которая отвечает нуждам и используется внутри одной компании или госструктуры. Этой ограниченной функциональностью может быть запуск людей на Луну, но всё равно сфера применения этой программы гораздо-гораздо уже, чему у современных сервисов.

Пользователи: сотрудники компании. Число пользователей программ того времени очень ограниченно: это 3 космонавта корабля Аполлон, 20 человек, рассчитывающих государственный бюджет, 100 человек, обрабатывающих результаты переписи населения.

Дистрибуция ПО: физическая, на мейнфрейме. Пробиваются перфокарты, загружаются в ЭВМ, и минут через 10 программа готова к использованию. За доставку, по сути, отвечает человек, который пробивает перфокарты. При этом если программист допустил ошибку, то исправлять её долго: заново писать и отлаживать код, пробивать перфокарты, загружать. На всё это уйдут дни и впустую будет потрачено время многих людей цена ошибки очень высока, возможно, катастрофа.

На этом этапе IT-бизнеса как такового еще не существует. Если посмотреть в Википедии количество компаний по производству ПО, основанных в 1975-м, то их там будет всего четыре. Правда, одна из них Microsoft, которая тогда была очень маленькой и нишевой.

Персональные компьютеры и ООП. 1980-1990-е годы


Все начинает меняться примерно в 1985 году с массовым распространением персональных компьютеров: в 77-м началось производство Apple II, в 81-м появился IBM PC, чуть раньше стали популярны минимейнфреймы от DEC.

Особенности периода: создание программного обеспечения начинает формироваться в бизнес-направление. Это становится возможным, потому что пользователей программ стало больше и можно строить программы на продажу.

Например, в 79-м появляется электронная таблица VisiCalc, которая заменяет собой бухгалтера-калькулятора человека, который фактически работал excel'ем. Бухгалтер заносил в большую бумажную таблицу числа и как-то по формулам их обрабатывал. Потом аналитик спрашивал, что будет, если доход в третьем квартале будет в два раза больше, и бухгалтер-калькулятор менял одно значение и снова всё пересчитывал на бумаге.

Пользователи: внешние по отношению к производителю ПО компании. VisiCalc перевернул всю индустрию компьютерного бизнеса, программа, вместо узконаправленной штуки, которая пишется для конкретного набора людей, стала массовой. Например, экономисты и аналитики стали покупать компьютеры, чтобы использовать электронные таблицы.

Поскольку пользователей стало много и программы начали продавать частным лицам, возникает вопрос: как сделать так, чтобы программа работала у такого большого количества людей, и как разрабатывать такие сложные программы.

Поскольку расширяется аудитория, то нужна и большая функциональность. Соответственно, нужно увеличивать команду разработки это уже не десяток программистов, а 100-500 человек, которые работают над сложным программным продуктом.

Интересно, что в каждой эпохе становления IT есть несколько ключевых книг, которые в своё время вызвали переворот в мышлении. Период, когда зарождается бизнес разработки программного обеспечения и команды разработчиков растут, на мой взгляд, характеризуется двумя книгами: Мифический человеко-месяц и Приёмы объектно-ориентированного проектирования. Паттерны проектирования. То есть с одной стороны появляется понимание, что невозможно, увеличив команду в 4 раза, сократить время разработки в 4 раза. А с другой что есть решение, которое способно помочь масштабироваться.

Популярным решением проблемы роста сложности ПО становится ООП. Считается, что если взять большую программу, допустим, Microsoft Excel, и разбить на какие-то объекты, то группы разработчиков смогут работать над ними независимо. Таким образом получится масштабировать работу на отдельные элементы функциональности и ускорить разработку нашего Microsoft Excel. Причем под ускорить, скорее всего, понимается цикл разработки в несколько лет.
Аргументация внедрения ООП звучит очень похоже на плюсы микросервисной архитектуры.
Но по сути, мы по-прежнему упаковываем программу в один пакет exe-файл во времена повсеместного использования Microsoft DOS, а потом Windows и поставляем пользователю.

Доставка ПО: на физических носителях. Но так как теперь это массовое производство, то доставка ПО заключается в том, чтобы: записать наш exe-шник на дискеты, наклеить на них наклейки с названием продукта, упаковать в коробки и буквально доставить пользователям в разных странах. При этом необходимо не допустить существенное количество брака, потому что если произвести дискеты где-нибудь в Америке, доставить в Россию и обнаружить, что половина из них с браком, это будут огромные издержки и мы потеряем пользователей насовсем.

Цена ошибки: пользователи вернут деньги и больше никогда не купят продукт, что может погубить весь бизнес.

Цикл разработки ПО просто потрясающе долог, этапы занимают месяцы:

  • планирование 12 месяцев;
  • разработка 24 месяца;
  • тестирование 12 месяцев;
  • доставка 12 месяцев.

Новая версия выпускается раз в несколько лет, поэтому никак нельзя допускать ошибки на этапе кодирования.

Однако главный риск связан с тем, что на протяжении всего этого длинного цикла разработки отсутствует обратная связь от пользователей.

Представьте: мы придумали новый IT-продукт, провели небольшое исследование и решили, что, возможно, эта программа понравится пользователю. Но убедиться в этом мы никак не можем! Мы можем только два года писать софт, а потом попросить бухгалтеров у себя в округе, например, в Редмонде, поставить новую версию Excel и попробовать её использовать. И это по сути все наши возможности для тестирования продукта. Поэтому не исключено, что через два года разработки окажется, что наше ПО никому не нужно.

Если же наш софт всё-таки покупают, но он имеет баги и плохо работает, то пользователи легко могут его вернуть это еще физический товар, коробка, которую можно принести обратно в магазин. И заодно решить, что покупать ПО этого производителя больше никогда не стоит.

Agile. 20012008


Следующая эра начинается с распространением интернета в 2000-х.

Особенности периода: IT-бизнес переходит в интернет, но браузеры работают еще так себе.

Microsoft создаёт Internet Explorer и бесплатно поставляет его всем пользователям Windows. Интернет становится доступен огромному числу людей, но при этом браузер Microsoft целенаправленно сделан не очень пригодным для реализации динамической функциональности, чтобы защитить свои программы от конкуренции со стороны браузерных приложений и Netscape (подробнее по запросу Война браузеров). Поэтому интернет еще используется в основном для загрузки файлов, но этого уже достаточно, чтобы бизнес перешёл в интернет.

Доставка ПО: дистрибутивы становится возможно распространять через интернет.

Дистрибуция ПО через интернет позволяет гораздо чаще раз в несколько месяцев выпускать обновления и новые версии. Больше не обязательно записывать ПО на дискеты и диски пользователи могут скачать обновления из интернета, а разработчики могут допускать больше ошибок.

Цена ошибки: риск для бизнеса уже не так велик, потому что пользователь может получить обновление и продолжить пользоваться продуктом.

Где-то в это время зарождается Agile и выходят книги о гибкой разработке и экстремальном программировании, которые и для современного IT-менеджмента являются классической основой, например: Extreme Programming Explained: Embrace Change, Refactoring. Improving the Design of Existing Code, Test Driven Development.
Основная идея в том, что раз доставлять ПО можно через интернет, то цикл производства может быть гораздо короче, а новую версии можно выпускать каждые полгода.
Цикл разработки в начале 2000-х выглядит примерно так:

  • планирование 2 месяца;
  • разработка 6-12 месяцев;
  • тестирование 1-3 месяца;
  • доставка несколько недель.

Можно, например, больше не тестировать так тщательно. Если у 10% пользователей проявятся баги, то для них можно выпустить патч, а не тратить заранее год на то, чтобы убедиться, что наше ПО работает как надо абсолютно везде. Таким образом, можно гораздо быстрее проверять гипотезы, но быстрее тут это по-прежнему полгода-год.

То, что можно меньше вкладываться в тестирование и детальное планирование, позволяет сократить расходы на проведение эксперимента. И слово эксперимент станет ключевым в следующую эпоху.

DevOps. 20092020


Особенности периода: устанавливаемого ПО практически не осталось, а немногое существующее обновляется через интернет. Интернет используется повсеместно, становятся популярны социальные сети и развлекательный софт, которые живут исключительно в интернете. Реализовывать сложную динамическую функциональность интерфейсов можно непосредственно в браузере и мы переходим туда.

Доставка ПО: посредством Облака. Если раньше, программа всё равно устанавливалась на компьютер пользователя и для него должна была быть отлажена, то теперь по сути нужно сделать программу лишь под один компьютер сервер в Облаке. Для нас это удобно тем, что у нас есть полный контроль над этим компьютером и тем, как работают на нём наши программы. Конечно, остаются некоторые трудности с отрисовкой интерфейсов в браузере, но они гораздо менее серьезные чем те, что были раньше.

Всё это позволяет ускорить планирование, реализацию, тестирование. Больше нам не нужно месяцами или годами жить с чёрной дырой неизвестности: выстоит ли проект или нет, нужна ли в нём такая функциональность и т.д. Обновлять ПО можно практически в реальном времени.

Но в 2006-2008 идеологически ПО еще разрабатывается по-прежнему как некое цельное ядро. Это, конечно, не exe'шник, но это всё равно цельный монолит, состоящий из набора очень плотно связанных объектов. Такое ПО слишком тяжеловесное, для того чтобы меняться так быстро, как этого хочет рынок.

Для решения проблемы неповоротливости монолита те же самые ребята, которые принесли нам ООП, говорят, что приложение можно и в целом разделить на отдельные программы, которые будут между собой коммуницировать. И тогда будет возможным ещё больше увеличить команду разработчиков с сотен до тысяч и создавать новую функциональность непрерывно. Тем самым проводить как можно больше экспериментов, проверять гипотезы, адаптировать бизнес под запросы рынка и действия конкурентов и двигать его вперед.

В 2009 году выходит первый доклад о том, как Dev и Ops объединяются, чтобы обеспечить 80 деплоев в день, и это становится одной из главных ценностей в разработке. Цикл разработки теперь выглядит совершенно по-другому:

  • планирование несколько недель;
  • разработка несколько недель;
  • тестирование несколько дней;
  • доставка несколько минут.

Ошибки можно исправлять практически на лету и разрабатывать гипотезу очень быстро. Тут же появляется всем известное теперь слово MVP.

Если в 70-е у разработчика почти не было права на ошибку и ПО было фактически недвижимым при запуске космонавтов на луну требования к функциональности не часто меняются то теперь софт становится абсолютно динамическим. Все готовы к тому, что будут баги, поэтому и нужна поддержка и команда, которая будет обеспечивать работу системы в условиях динамичного развития.
В новой эпохе впервые за историю развития IT специальность, связанная с доставкой ПО, стала действительно айтишной.
До этого профессия человека, который обеспечивал систематическую доставку программного обеспечения, вообще говоря, не была айтишной. В 70-80-е это были люди, которые засовывали перфокарты в компьютеры, в 80-90-е люди, которые грамотно вели переговоры с фабриками и заводами по производству дисков и занимались логистикой. Это всё абсолютно не связано ни с разработкой, ни с системным администрированием.

Не погруженные глубоко в тему DevOps люди часто думают, что DevOps это новое клёвое название для админов, которые больше общаются с разработчиками. Но бизнес (и википедия) считает, что DevOps это методология внутри процесса разработки ПО. И не так важно, что конкретно она означает, важно, что она дает. А дает DevOps как методология возможность максимально быстро адаптироваться к изменениям рынка и менять сам процесс разработки ПО.

Если компания не хочет отставать от конкурентов, то ей нужно переходить от длинных циклов разработки и релизов раз в месяц к DevOps. DevOps-трансформация при этом означает обеспечение Agile-процесса целиком от разработки до выкладки. И таким образом процесс доставки ПО включается в процесс разработки и становится айтишной специальностью.

И да, поскольку процесс доставки обычно включает в себя работу, связанную с серверными и инфраструктурными штуками, то кажется, что это должен или может делать человек с админским бэкграундом. Но это приводит к проблеме во взаимопонимании инженеров и бизнеса. Особенно когда речь идет об админах, переживающих DevOps-трансформацию и выполняющих запросы бизнеса, который хочет быть гибче.

У большинства админов, которые занимаются обеспечением отказоустойчивости, идеология работает не трожь! находится во главе угла. Они хоть и не запускают Аполлон на Луну, но мыслят теми же категориями надежности. А бизнес в новых условиях, пускай и ценой сбоев, хочет:

  • минимальное время от идеи до продакшена;
  • максимальное количество проверенных гипотез за короткое время;
  • минимальное влияние ошибок на работу продакшена.

Пускай оно сразу упадет это ничего, мы откатим, поправим и снова выложим. Лучше быстрее проверить, чем вкладываться во что-то, на что не будет спроса.
Подход к отказоустойчивости меняется: больше не нужно, чтобы текущая версия ПО была стабильной на протяжении долгого времени нужно, чтобы ошибка в текущей версии ПО минимально влияла на работоспособность всей системы.
Не нужно обеспечивать стабильность маленького кусочка нового кода; нужно иметь возможность выкинуть его как можно быстрее и вернуть тот, который работал. Это опять про гибкость: ценность не в стабильности кода, который выложен в инфраструктуре, а в способности инфраструктуры быть максимально гибкой.

Как инженеру пойти навстречу бизнесу


Итак, что же мы можем сделать, чтобы найти больше пересечений с ценностями бизнеса?

Если мы говорим, что внедрение DevOps служит нуждам бизнеса, то должен быть способ понять, делает ли инженер то, что нужно бизнесу. Вот какие метрики можно ввести, если изучить отчёт DORA State of DevOps и исследование состояния DevOps в России:

  • Частота деплоев (deployment frequency) как часто вы деплоите код в продакшен или как часто ваши конечные пользователи получают новые релизы.
  • Время внесения изменений (lead time for changes) от коммита кода в репозиторий до его выкладки в продакшен.
  • Время, за которое сервис восстанавливается после сбоя или аварии (time to restore).
  • Частота аварий, вызванных выкладкой изменений (change failure rate) какой процент деплоев приводит к ухудшению качества обслуживания пользователей и требует исправлений, например, откатов.

Эти метрики позволяют оценить, как компания использует возможности DevOps. В то же время, посмотрев на эти метрики, инженер может понять, какими своими действиями он может помочь бизнесу.

Частота деплоев


Понятно, что чем чаще компания деплоится, тем вернее она движется по пути DevOps-трансформации. Но деплоиться часто страшно. Однако DevOps-инженер может помочь справиться с этими страхами.

Страх 1: выложим что-то плохо протестированное, из-за чего продакшен упадет под нагрузкой.

Роль DevOps: обеспечить возможность легкого отката, помочь автоматизировать тестирование в инфраструктуре.

Страх 2: выложим новую функциональность с багами, а её внедрение изменит данные или структуру данных настолько, что откатиться будет невозможно.

Роль DevOps: работать в связке с разработчиками и помочь в архитектурных решениях, предложить эффективные методы миграции данных и т.д.

Страх 3: выкладывать долго и сложно (практика показывает, что 20 минут на сборку docker обычная история).

Роль DevOps: обеспечить скорость деплоя и отката, ускорить процесс сборки.

Время внесения изменений


Это отчасти менеджерская метрика, потому что именно менеджер должен организовать процесс так, чтобы код, который написал разработчик, можно было максимально быстро закоммитить и задеплоить. Устранить препятствия на этом пути тоже может помочь DevOps-инженер.

Проблема 1: долго проходит pull request. Например, потому что есть ревью и ревью на ревью. В корне этой проблемы лежит всё та же боязнь выложить код.

Роль DevOps: работать с менеджером разработки в вопросе автоматизации принятия PR.

Проблема 2: долгий ручной процесс тестирования.

Роль DevOps: помочь автоматизировать тестирование.

Проблема 3: долгая сборка.

Роль DevOps: мониторить время сборки, уменьшать его.

Поэтому DevOps-инженеру нужно, во-первых, понять, как тестируется софт, как тестирование автоматизируется и как автоматизацию тестирования можно интегрировать в процесс сборки ПО. Во-вторых, разложить весь пайплайн выкладки ПО на составляющие и найти, где его можно ускорить. Замониторьте весь процесс от коммита до выкладки в продакшен, узнайте, сколько идет сборка, сколько pull request ждёт аппрува, и вместе с менеджером и разработчиками придумайте, на чём можно сэкономить.

Время восстановления работоспособности


Time to restore время, за которое сервис восстанавливается после сбоя или аварии, метрика, гораздо более близкая к SRE.

Проблема 1: сложно локализовать техническую проблему.

Роль DevOps: организовать observability, вместе с разработкой обеспечить инфраструктуру для мониторинга и настроить систему мониторинга так, чтобы эффективно следить за работой сервиса.

Проблема 2: инфраструктура не готова к легкому процессу отката.

Роль DevOps: обеспечить техническую возможность.

Проблема 3: после миграции невозможно откатиться.

Роль DevOps: научить разработчиков практикам миграции данных, позволяющим легкие откаты, и практикам отказоустойчивости.

Частота аварий


Change failure rate как часто выложенный код падает это снова про менеджмент и идеологию, но здесь есть интересный момент: код часто падает, если его редко выкладывать.

К сожалению, я видел случаи, когда компания берет курс на DevOps-трансформацию, внедряет GitOps и Kubernetes, но частота релизов от этого не меняется. Подход остаётся прежним, и разработка новой версии как занимала полгода, так и занимает полгода. А если вы пишете код, который на протяжении месяцев не выкладываете в продакшен, то он упадет с гораздо большей вероятностью, чем код, который выкладывается каждую неделю. Этого разрушает весь процесс целиком: если компания взяла курс на DevOps, а при этом цикл разработки занимает шесть месяцев, то это какая-то беда.

В таком случае DevOps должен забить тревогу и попытаться ещё раз объяснить суть DevOps-подхода и то, как изменился подход к разработке за последние годы.

Дивный новый мир


Нам как инженерам нужно понимать, что же важно для бизнеса, и на это и направлять свои усилия.

  • Стабильность текущей версии кода не так важна, как стабильность инфраструктуры целиком и возможность откатиться к предыдущей версии, изолировать проблему и быстро её исправить.

  • Стабильность окружения разработки, его производительность критически важна, особенно если в команде сотни разработчиков. Когда разработчики из-за проблем со средой разработки вынуждены ждать, это равносильно простою фабрики.

  • Мониторинг процесса доставки ПО стал частью мониторинга всей инфраструктуры. Если что-то выкладывается 20 минут, то надо попытаться это ускорить.
  • Скорость доставки ПО становится одной из ключевых задач, нужно обеспечить такую молотилку и чтобы она при этом работала как часы.
  • Создание удобных окружений для разработки ещё одна ключевая задача. Если разработчику удобно, он пишет код быстрее, чаще выкладывает и всё работает хорошо.

Подписывайтесь на наш блог, здесь мы регулярно публикуем интересное из мира DevOps и рассказываем о случаях из нашей практики, которые помогут, например, подготовить сервисы к чёрной пятнице. Или подключайтесь к моему телеграм-каналу там я собираю мысли на профессиональные (и не только) темы и делюсь любопытными ссылками.
Подробнее..

Как готовить Helm правильно несколько полезных рецептов

16.05.2021 18:12:28 | Автор: admin
(источник фото -https://unsplash.com/photos/XWNbUhUINB8(источник фото -https://unsplash.com/photos/XWNbUhUINB8

Когда-то давно, когда ножей не знали,х@#$ говядину рубили... ой нет, это другая сказка, простите. Вот, правильная эта. Когда-то давно, деды сказывают, для обновления сервиса в среде инженерам приходилось самим писать скрипты для деплоймента, а некоторые даже запускали их сами из консоли, руками. Были даже причудливые инструменты вроде ClusterSSH, чтобы делать сеанс одновременной игры на всех нодах кластера.

Но тотальная контейнеризация, ввод в обиход универсального понятия "workload", и оркестризация привели к появлению Kubernetes, на котором сейчас работает примерно 3/4 мировых production сред.

Kubernetes ввёл в обиход концепцию управления конфигурацией при помощи дескрипторов - kubectl apply что в переводе означает "я не хочу объяснять что делать, я говорю как хочу чтобы в среде всё стало - а ты делай что должен, блестящий металлический зад!".

Как с любой инновацией, выход на базовый уровень оркестрации привёл к осознанию того, что дескрипторы должен делать не человек руками, а другой "металлический зад", которому задачу ставить можно на ещё более высоком уровне - "накати обновление сервиса на среду Ъ, согласно правилам подготовки конфигурации среды".

Поэтому Helm стал де-факто стандартом в автоматизированных деплойментах Kubernetes, добавляя новые интересные возможности, которые идут гораздо дальше простой альтернативы выполнению kubectl apply -f ...

Ниже мы расссмотрим несколько примеров того, что можно получить от Helm по сравнению с простой командой kubectl apply, с чем разработчик сталкивается в реальной жизни. Мы покажем, как организовать доставку новых версий сервисов в разные среды, описывая параметры так, что не требуется копипастных повторений, которые ведут к ошибкам. Мы также увидим несколько других полезных приёмов - для удобного управления секретами и организации надёжных пайплайнов.

Итак, напомним, что Helm в принципе может делать три основные вещи:

  • сделать дескрипторы по шаблонам

  • накатить дескрипторы на кластер

  • использовать репозитории charts - взять шаблоны дескрипторов из центральной среды

В данном тексте мы фокусируемся на первых двух моментах, шаблонах и состоянии кластера, не затрагивая репозитории.

Шаблонизатор Helm работает примерно так:

Другими словами, Helm берёт исходники:

  • файлы, текстовые и бинарные

  • шаблоны .yaml

  • значения из одного или нескольких .yaml файлов, и, возможно, командной строки

и производит файлы .yaml для того, чтобы применить их на кластер Kubernetes.

При этом файлы вставляются в Secrets и ConfigMaps, а значения занимают свои места в шаблонах.

Получившиеся шаблоны можно скормить в кластер непосредственно через kubectl, или же (что открывает новые возможности) наложить их через Helm. Helm имеет встроенные средства управления состоянием кластера - несколько последних сделанных деплойментов сохраняются в отдельном Secret, что даёт возможность полного и точного отката при ошибке - то есть обеспечивает атомарность операции и целостность кластера.

Рассмотрим некоторые полезные техники Helm для решения типичных задач, с которыми девопс инженер сталкивается за пределами простого, понятного, нежно-розового мира hello world:

  • управление конфигурацией множества сред

  • использование секретов

  • повышение стабильности пайплайнов

Конфигурация множества сред

Скорее всего, ваши сервисы работают в нескольких средах. Простейшим разделением может быть Test и Production, но на практике всё бывает чуть сложнее. Тестовые среды бывают разных видов: некоторые из них одноразовые, нестабильные и экспериментальные, выполняющие код из новых пулл реквестов, который может часто ломаться.

Другие тестовые среды предназначены, например, для приёмочного тестирования реальными пользователями (по уровню ответственности не Production - но весьма похожие). Такие среды обычно используются несколькими командами, поэтому все ожидают от них стабильности.

Production-среды имеют общие моменты, характерные для production, например реальный секретный ключ для платёжной системы. Но, тем не менее, могут различаться в других аспектах: например по региону, среда для "канареечных" релизов для тестов на малой группе, или по регионам. Или же может быть несколько production сред для каждого клиента или их группы, если ваш сервис больше похож на SaaS.

12 заповедей

Возможно, вы слышали о 12 заповедях - наборе "золотых" правил для организации сервисов, которым контейнерные оркестрации типа Kubernetes соответствуют чуть менее чем полностью. Под номером 3 сформулирована важность сохранения настроек и конфигураций в самой среде, а не в приложении.

Однако в скрижалях с 12 заповедями не сообщается подробностей - как именно передать эти значения в среды прозрачным, безопасным и контролируемым образом и где все это хранить. Особенно когда вам нужно поддерживать много сред, похожих по одним аспектам, но разных по другим. И как быть с некоторыми значениями конфигурации, которые лучше держать в секрете? Чтобы успешно справиться со всей этой сложностью с помощью Helm, необходимо внести некоторые улучшения в стандартную компоновку chart.

Иногда встречаются еретические варианты трактовки третьей заповеди, когда некоторые юноши понимают сохранение настроек не в приложении буквально, насмотревшись на эту картинку из манифеста.

Код с конфигурацией - как мясное с молочным? Код с конфигурацией - как мясное с молочным?

Они решают, что заповедь запрещает мешать молочное с мясным хранить конфигурацию приложения в том же гит-репозитории что и его код.
Истинно говорю вам, они заблуждаются! Третья заповедь - про то, что артефакт сервиса (результат build) должен быть один и тот же, и не содержать конфигурации, которая считывается при запуске из среды - в коей среде она должна присутствовать.

Каждый новый билд порождает неизменяемые (immutable) контейнер(ы) с кодом, и Helm chart, который можно применить для любой среды - от локального docker-desktop до production кластера в AWS.

При этом код конфигурации, в нашем случае - шаблоны и файлы Helm chart, с полным набором настроек для всех сред, за исключением секретных значений, проще хранить вместе с кодом сервиса. В особенности, если вам всё-таки удалось сделать гармоничную (микро)сервисную архитектуру, и у каждого сервиса есть возможность обновляться независимо. Если такой возможности нет - не льстите себе, у вас распределённый монолит, а не (микро)сервисы.

Чтобы сделать всё красиво и надёжно, мы прежде всего хотим устранить избыточность и дублирование схожих элементов конфигурации и шаблонов и сделать их такими же простыми, как приложение, работающее на ноутбуке разработчика.

Следовательно:

  • мы не хотим, чтобы конфигурация нашего приложения зависела от способа его развертывания - запуск контейнера на лаптопе разработчика и запуск в оркестраторе не должны по сути отличаться

  • ... ведь в подавляющем большинстве случаев все, что нужно сервису - это просто набор из нескольких файлов конфигурации в одном, доступном ему каталоге (conf или properties)

  • а ещё больше мы не хотим ничего копипастить - это ведёт к визуальному мусору, глупым ошибкам, повышению затрат на поддержку - поэтому

    • каждое значение конфигурации и/или файл должны храниться в одном и только в одном месте,

    • если значения или файлы различаются в зависимости от среды, в каждой среде сохраняются только отличающиеся части,

    • добавление файла в конфигурацию должно требовать именно этого - добавление файла: никаких изменений в пицот других yaml-ов!

Мы будем хранить конфигурации всех наших сред в диаграмме Helm как часть chart.

Пример ниже, дополнительные каталоги выделены (+).

/env             // (+) значения для сред /<chart-name>    // chart    /files         // (+) конфигурационные файлы и их шаблоны     /templates     // шаблоны Helm   Chart.yaml     // заглавный файл chart  values.yaml    // значения по умолчанию

Файлы со значениями для сред

Давайте посмотрим подробнее на структуру ваших сред.

Допустим у вас есть два типа, TEST и PROD для тестовых и продакшен сред, соответственно. тип TEST делится на две разновидности -STABLE и -PR (пул реквест, нестабильная среда), а для типа PROD у вас разновидности это регионы, EU и US.

Получается примерно такая структура для /env (значения для сред) и /files (файлы конфигурации):

/env                  TEST.yaml            // общие значения для всех тестовых сред  TEST-PR.yaml         // только для PR/нестабильной  TEST-STABLE.yaml     // только для стабильной     PROD.yaml            // общие значения для всех продакшен сред  PROD-EU.yaml         // продакшен EU     PROD-US.yaml         // продакшен US /files                     /TEST                // общие файлы для всех тестовых сред  /TEST-PR             // ...  /TEST-STABLE         // ...    /PROD                // общие файоы для всех продакшен сред   /PROD-EU             // ...  /PROD-US             // ...  /shared              // файлы общие для всех средvalues.yaml             // значения общие для всех сред

Теперь посмотрим что внутри /files - текстовые и бинарные файлы конфигурации.

.../PROD    /binary    /text/PROD-EU    /binary    /text .../shared    /binary    /text    /secret-text

Если какого-то типа файлов нет для комбинации тип/разновидность среды, то директория может отсутствовать.

Не все типы файлов представлены на всех уровнях, но всему есть причина, давайте подробнее рассмотрим.

1. Мы предполагаем что секретные файлы только текстовые а не бинарные. Почему? Потому что так проще. Секретный двоичный файл (например .p12 сертификат) становится несекретным, если зашифровать его секретным ключом с достаточной (скажем 120+ бит) энтропией - и можно хранить его просто в гите, даже если он размеров в несколько (десятков) килобайт. С точки зрения безопасности - это просто рандомный набор битов. А вот ключ, отпирающий этот бинарник, мы будем хранить в строгом секрете - как и все другие-остальные пароли.

2. С другой стороны, вряд ли будут секретные текстовые файлы, которые при этом не общие (shared) для всех сред. Обычно такой файл - это шаблон (например конфигурация пула соединений к базе данных), в который надо вставить несколько секретных значений, например пароль пользователя. Ниже мы чуть подробнее раскроем тему секретов в шаблонах.

Итак, когда структура выше подготовлена, всё работает примерно так - значения из нескольких файлов сначала превращаются в один плоский yaml, накладываясь друг на друга, в порядке уменьшающегося приоритета:

  • среда - разновидность (TEST-STABLE.yaml)

  • среда - тип (TEST.yaml)

  • общие значения (values.yaml)

Helm позволяет это сделать, указав файлы со значениями как последовательность опций --values:

helm upgrade --install <chart> path/to/<chart> --strict \     --values env/<env>.yaml --values env/<env>-<flavour>.yaml

В .yaml-файлах разновидности и типа среды содержатся атрибуты, например TEST-STABLE.yaml среди прочего содержит:

envFlavour: STABLE

а TEST.yaml содержит

envClass: TEST

В плоском финальном наборе значений всё это объединяется, для того чтобы, как мы увидим в следующем разделе, можно было удобно используем эти значения, чтобы взять нужные файлы из /files.

Один ConfigMap и один Secret для всех файлов

Смотрим внимательно - вот одна, единая конфигурация для одного единственного ConfigMap и одного Secret, куда поместятся все ваши файлы. Бинарные файлы и текстовые несекретные файлы пойдут в ConfigMap, а секретные текстовые файлы в Secret.

# это нужно для вложенных range{{- $self := . -}} # список директорий, откуда берутся файлы - среда-разновидность, среда-тип и потом общие файлы из shared{{ $sources := (list "shared" .Values.envClass (printf "%s-%s" .Values.envFlavour .Values.envClass ) }}apiVersion: v1kind: ConfigMapmetadata:  name: myapp# вставить несекретные текстовые файлы как шаблоныdata:{{ range $env := $sources }}{{ range $path, $bytes := $self.Files.Glob (printf "files/%s/text/*" $env) }}  {{ base $path }}: {{ tpl ($self.Files.Get $path) $ | quote }}{{ end }}{{ end }}# вставить двоичные файлыbinaryData:{{ range $env := $sources }}{{ range $path, $bytes := $self.Files.Glob (printf "files/%s/binary/*" $env) }}  {{ base $path }}: {{ $self.Files.Get $path | b64enc | quote }}{{ end }}{{ end }}---apiVersion: v1kind: Secretmetadata:  name: myapp  labels:type: Opaquedata:# вставить секретные текстовые файлы как шаблоны{{ range $env := $sources }}{{ range $path, $bytes := $self.Files.Glob (printf "files/%s/secret-text/*" $env) }}  {{ base $path }}: {{ tpl ($self.Files.Get $path) $ | b64enc | quote }}{{ end }}{{ end }}

И это всё что нужно! Ничего не надо менять, если добавляется новый файл. Как только файл оказывается в нужном месте, он будет автоматически найден и добавлен в дескриптор, никаких изменений не требуется.

Тотальная шаблонизация

Имеющие опыт с Helm, возможно, задаются вопросом, к чему все эти хитрые танцы, если у Helm уже есть набор функций для ConfigMaps и Secrets?

А вот почему. Заметили маленькую функцию tpl, применяемую на каждый файл? Правильно, именно поэтому она нужна, чтобы каждый текстовый файл обрабатывался как отдельный шаблон - вы можете вставлять ссылки на значения как {{ .Values.myValue }}в любое место ваших текстовых файлов конфигурации.

Шаблон может использоваться для любого типа конфигурации, .properties, yaml, HOCON, например:

akka {  persistence {    cassandra {        keyspace = {{ .Values.cassandra.keyspace | quote }}        table = "{{ .Values.cassandra.tablePrefix }}messages"

Коварные кавычки

Кавычки в значении могут оказаться как лишними, так и наоборот, в зависимости от формата файла.

Каким-то значениям, содержащим скобки, запятые и прочее, что может смутить парсер конфигурации, полезно быть взятым в кавычки:

databasePassword: {{ .Values.databasePassword | quote }}

Для других значений, например в файле .properties, кавычки наоборот, вредны, поэтому их можно убрать вот так:

param/username={{ .Values.username | trimAll "\"" }}

Проецируемые тома (projected volumes)

С точки зрения работающего сервиса всё равно, являются ли файлы секретными или нет: в контексте запущенного контейнера это просто файлы в одном и том же контексте безопасности, к которым есть доступ только у процесса в контейнере. Поэтому ничего страшного нет в том, чтобы положить всех файлы - текстовые, бинарные, содержащие секреты и не содержащие - в один каталог контейнера.

Для этого в Kubernetes удачно завезли projected volumes - проецируемые тома, которые можно собирать из нескольких ConfigMaps и Secrets.

volumes:  - name: properties    projected:      defaultMode: 0640      sources:        - configMap:            name: myapp        - secret:            name: myapp

Очень просто смонтировать такой "сборный" том в директорию/confвашего приложения.

Линтер

Не вредно бывает запускать линтер для проверки chart на вменяемость, чтобы при ошибке пайплайн падал не в момент деплоймента, а существенно раньше.

У Helm есть для этого команды lint и template:

helm lint --debug path/to/<chart> --strict --values env/<env>.yaml \  --values env/<env>-<flavour>.yaml  helm template <chart> path/to/<chart> --strict --values env/<env>.yaml \  --values env/<env>-<flavour>.yaml

В процессе подготовки chart в пайплайне эти команды должны выполниться для всех валидных комбинаций типа и разновидности сред.

Но совершенству нет пределов (почти) - можно пойти ещё на шаг дальше и использовать yamllint для валидации yaml-дескрипторов. Это позволит отловить проблемы, которые иначе не получается ловить - например два файла с одним и тем же именем, которые оказались в PROD и PROD-EU, будут дважды вставлены в ConfigMap, что приведёт к ошибке при деплойменте.

Управление секретами

Теперь немного про то, как обращаться с секретами - например ключами платных API, паролями для баз данных и паролями от TLS-сертификатов, которые мы не хотим показывать никому, кроме самого сервиса.

Обычно секретные значения хранятся в отдельном сервисе, типа Heroku Vault, Azure Vault, Google Cloud KMS и подобных. В Helm даже есть плагин для управления секретами,но в большинстве компаний управление секретами централизовано, и инженерам не позволено, да и не нужно, трогать секреты из production.

Когда пайплайн запускает обновление сервиса в подобной среде, то сам пайплайн имеет возможность считать секреты и вставить их в конфигурационные файлы. В большинстве случаев (Gitlab, Circle, Azure, ...) секреты становятся переменными среды, и всё что требуется - это вставить их в шаблон в самом начале.

Для этого мы добавим секцию в values.yaml

# secretsdatabase_username: "${UserNameSecret}"database_password: "${DatabasePasswordSecret}"

И в нашем пайплайне испольуемenvsubstили нечто похожее:

cat <chart>/values.yaml | envsubst > <chart>/values-injected.yamlmv <chart>/values-injected.yaml <chart>/values.yaml

Такой подход позволяет ссылаться на секреты просто как {{ .Value.xxx }} в любом шаблоне, вставляя их туда куда им положено быть.

Но полезно помнить, однако, что ничто автоматически не предотвратит ошибочное вставление секретного значения в несекретный текстовый файл.

Чтобы решить этот вопрос, можно ввести конвенцию называть секреты как XXXSecret, и добавить что-то типа такого:

EXPOSED_SECRETS=$(grep Secret <chart>/files | grep -v secret-files | wc -l)if [ $EXPOSED_SECRETS -gt 0 ]; then fail "Secrets are exposed"; fi

Это добавит уровень защиты против случайной потери секрета.

Атомарные обновления сред

Теперь последний раздел - как использовать Helm hooks для того, чтобы повысить надёжность ваших пайплайнов.

Предположим, вы только что развернули новую версию приложения или новый набор значений конфигурации. Теперь вы определенно хотите знать, не поломалось ли что-то? Если все в порядке, вы можете продолжить работу над другими задачами вашего спринта, но если что-то не так - то вы скорее всего очень хотите, чтобы среда была автоматически восстановлена до последнего известного исправного состояния, без вашего вмешательства и всех этих надоедливых сообщений фрустрированных коллег: Почему я снова получаю ошибку 5xx?.

Это может показаться сложным - особенно если вы хотите реализовать это самостоятельно, вооружившись простым kubectl apply -f ... Но, к счастью, Helm многое делает "прямо из коробки".

Флаг --atomic

Просто добавьте флаг --atomicдля автоматической отмены текущего обновления, если поды не запустились по любой причине:

helm upgrade --install my-chart some/path/to/my-chart \    --atomic --debug --timeout 300s

Helm откатит обновление, если проверки проб health/readiness не дали положительного результата в указанный срок. Хорошо, но можно лучше.

Hooks

Можно, и нужно, добавить KubernetesJobв ваш дескриптор, чтобы запускать тесты "на дым" после каждого обновления. Обычно это не полный набор тестов, но несколько базовых сценариев, которые затрагивают все ключевые внешние системы, к которым сервис обращается в среде - например базу данных, внешний кеш, какие-то другие сервисы и тп.

И разумеется, сигнализировать Helm откатить обновление, если тест не прошёл.

apiVersion: batch/v1kind: Jobmetadata:  name: myapp  labels:  annotations:     "helm.sh/hook": post-install,post-upgrade     "helm.sh/hook-delete-policy": before-hook-creation,hook-succeededspec:  template:    metadata:      name: myapp-smoke-test    spec:      restartPolicy: Never      containers:        - name: tests          image: test-image:           command: ['/bin/sh',                    '-c',                    '/test/run-test.sh']

Если вы используете--atomicфлаг и post-upgrade/post-install hook для тестов в пайплайне, вы можете со значительной долей уверенности считать пайплайн надёжным. Он будет иметь "зелёный" статус тогда, и только тогда, когда сервис был успешно обновлён, запущен и прошёл базовые тесты.

А если что-то пошло не так - Helm возвратит среду в то состояние, которое было до этого обновление, включая все конфигурации и дескрипторы.

Автоматически!

Заключение

Helm это бесплатный инструмент с открытым кодом для управления обновлениями сервисов и их конфигураций в кластерах Kubernetes.

Используя шаблоны и hooks, можно добиться того, что ваш continuous delivery плавен и надёжен настолько, что его практически никто не замечает.

***

Этот текст является авторским переводом, оригинал статьи этого же автора
https://jacum.medium.com/advanced-helm-practices-for-perfect-kubernetes-deployments-7fc4e00cc41c

Подробно об авторе - https://www.linkedin.com/in/tim-evdokimov/

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru