Comments 47
P.S. В сети очень много учебников и статей. Когда учишься сам и дома, нигде не узнать полной картины, кроме как пойти работать в офис и посмотреть как процесс налажен другими.
P.P.S. Спасибо!
а вместо HelloWorld в реальности у вас может быть какой-нибудь высоконагруженный продукт с кучей сложных и крутых микросервисов, и описанный процесс можно применить к нему.
Даже невзирая на это, читаю статью, и понимаю, что что-то не так современным it. Вроде ж не для этого облака создавались.
Высоконагруженный интернет-магазин
Грубый (очень примерный) список сервисов:
- сервис по работе с товарами: описания товаров, вендоров
- сервис по работе с ценами: несколько ценовых колонок для разных категорий покупателей и т.д. и т.п.
- корзина
- заказы
- статика: тонны картинок товаров в разных размерах — всё это надо хранить и отдавать
- склад: наличие, расписание поставок от вендоров и поставщиков, логистика в пределах складов и даже шкафов внутри одного склада
И все сервисы надо скэйлить и обеспечивать high availability (много сервисов в нескольких экземплярах за балансерами и в нескольких дата-центрах). А ещё надо конкуренцию держать и поставлять всё новое и новое на сервера как можно быстрее (десятки, сотни деплоев в день). По мне так для этого облака и придумывались, нет разве?
Я не о скейле, а о тоннах конфигурационного кода в ямлях, помниках. Казалось бы, одна из проблем, которую должны решить облака, — это возможность создавать продукт без сисадминов и сетевиков в штате.
Но пришли к тому, что для прогнозируемого деливери надо держать в каждой команде по девопсу, который стоит, как два сисадмина и сетевик вместе взятые.
Все так. Другой вопрос, что если ты приходишь в Ынтерпрайз, то там все эти проблемы решены. И у разработчика остаётся одно — реализовать бизнес-функциональность в существующих рамках платформенного решения. И это здорово, т.к. не болит голова о вещах — как сделать логирование, как писать деплой и пр. А всем этим заведует платформенная команда.
В стартапе — да, больно, приходится заниматься всем, но часто помогают облачные сервисы типа sentry, splunk, okmeter, newrelic etc., которые при масштабах стартапа могут оказаться легко дешевле, чем з/п "девопса".
Что ещё сказать. Вероятно, если Вы не хотите писать ямлики, то Вам вообще не кубернетес нужен, а платформа типа докку-хероку....
Высоконагруженныйэто сколько? В граммах пожалуйста.
Сейчас абсолютно каждый мнит себя высоконагруженным сервисом, делая безумное количество микросервисов и прочего быдлокода. А чтоб интересней было еще и без архитектора. Причем на полном серьезе, я уже не раз слышал нечто вроде: у нас очень большой каталог товаров, 10 тысяч (!!!).
С базами тоже самое: 10 млн строк в таблице, нужны мощные сервера!
Я правда хочу понять, ну вот правда)
Высоконагруженный? А фиг знает, сколько это, ибо нагрузки у всех разные и все считают своё высоконагруженным (так или иначе).
Статьёй я не хотел побуждать всех перезжать в облака и поднимать свой k8s-кластер, а всего лишь хотел показать, как это можно сделать и как построить примерный процесс доставки продукта в клауд.
Делать это или нет — вопрос вышего выбора :)
Про микросервисы:
За свою карьеру много всякого разного монолита видел и микросервисов тоже (не так много и не такого разного как монолита, но видел), и для меня иногда очень остро стоял вопрос скэйлинга того или иного участка бизнес-логики, но я не мог этого сделать легко, если имел дело с монолитом, отжирающим кучу памяти, диска и процессорного времени, а с микросервисом, который делает ровно одну задачу и делает её хорошо, я могу это сделать легко и быстро, условно указав большее количество реплик в деплойменте кубика
но я не мог этого сделать легко, если имел дело с монолитом, отжирающим кучу памяти, диска и процессорного времени, а с микросервисом, который делает ровно одну задачу и делает её хорошо, я могу это сделать легко и быстро, условно указав большее количество реплик в деплойменте кубика
Все так, не поспоришь. Но есть и обратная сторона, которую много раз видел в своей практике. Факт, что данный способ решения задачи (поднять количество реплик в k8s) доступен для большинства задач скейлинга, говорит о том, что до этого момента было (цифры привожу «свои», в других проектах они другие):
1) уже проинвестировано/потрачено в 3-10 раз больше времени программистов, чем на создание аналогичного монолита (условно, вместо одного хитровыделанного SQL запроса с джойном на 10 таблиц и десятком CTE в случае монолита, пишется (либо модифицируется код уже существующих) 5 микросервисов тремя командами + юнит тесты на каждый + интеграционные тесты + документация).
2) 80+ % процессорного времени идет на сериализацию и десериализацию json, запись и чтение из Kafka, логгирование, поддержание работы самой Кафки.
Вот, это говорит о том, что серебряной пули не бывает в нашем деле. Поэтому решение переезжать в клауд и на микросервисы должно быть очень взвешенным и выверенным.
Ну и не забываем про все практики хорошего тона и кода — кодстайл, чекстайл, код ревью и все прочие пляски с бубном, перед тем как код попадёт в прод, никто не отменяет (это к слову про быдлокодинг)
на самом деле все просто. Оптимальная формула — напишите сначала монолит. А потом, когда он докажет свою жизнеспособность — пилите на микросервисы. Т.к иначе, пропустив этот шаг, получаешь не микросервисы, а нечто более худшее — распределенный монолит (1!!!)
Всё верно, размикросервились тут!
Но для меня микросервисность и клаудность продукта даёт ещё один плюс — удобство и скорость доставки в прод. Да, это даёт накладные расходы тоже и не малые (в том числе на дизайн и продумывание API), но конечный лояльный клиент для бизнеса, думаю, важнее. О чём это я?
А, вот пример:
- Есть условный Василий, владелец крутого сетевого магазина спортивной экипировки. И у Васи есть интернет магазин монолит.
- Есть условный Пётр, владелец крупного интернет-магазина, продающего ВСЁ, спортивную экипировку в том числе. И у него огромная инфраструктура, клауд, k8s и все все все.
- Мне нравится магазин Васи, ибо там всегда есть то, что мне нужно и иногда по очень выгодным ценам. НО интернет-магазин Васи откликается на мои действия по 10-15 секунд!!! и местами очень не устраивает UI (конкретно в мобильной версии сайта)
- У Пети я могу очень комфортно сёрфить по магазину и выбирать себе спорт-товары, даже те, что есть у Васи и иногда по ценам ниже, чем у Васи (Петя много не потеряет, если продаст последнюю пару лыж чуть дешевле, но клиент останется доволен)
- Мы уже дошли до 5-го пункта, и где тут про удобство клауда и про скорость доставки?? Да нету здесь ничего про это. НО когда я пишу Пете, что у него воооот этот вот участок сайта некрасивый для юзера и он чуть тупит, то Петя идёт и решает эту проблему за условных N минут (похачив пару строк во фронтовом компоненте и одной командой задеплоив новую версию микросервиса, отвечающего за этот кусок UI и за этот кусок бизнес логики) — PROFIT. Но когда я Васю прошу о том же, то это может затянуться на несколько дней, недель, месяцев, — монолит не так просто обновлять на боевых серверах.
Вывод: клауд — недешёвое удовольствие как с точки зрения дизайна API, так и с точки зрения содержания инфраструтуры, НО это огромный плюс в пользу скорости решения пользовательских проблем (много пользователей утром пожаловались на медленную корзину в магазине, во время ланча они уже получили скоростное оформление заказов и маленький аддон в виде красивого виджета в корзине, а команде разработчиков это почти ничего не стоило) и доставки апдейтов в прод
А если все сделано нормально, то вам без разницы, поправить шаблон в монолите или в микросервисе или на фронте
Логика есть, если поправить и доставить изменение в прод занимает N секунд вместо N минут (а за оставшееся время попробуем проанализировать тупняк и понять, с фига ли мы так тормозим, может чего-нибудь пооптимизируем?).
Ну или временно поскейлим кусок логики до 100 инстансов, а пока уходят деньги за использование виртуалок в AWS, и клиенты довольны, мы попробуем понять где мы налажили в коде и попробуем исправить.
Да, тормозящий сервис скорее всего тормозит по причине кривых рук разработчиков или админов, но сокорость доставки кусков логики для меня, например, решает много (kubectl apply -f… — ВСЁ)
Я вот правда не в курсе, потому что вот тут у нас хук на коммит в мастер.
А вот тут автодеплой у IDE (для любителей ftp, внезапно)
Скорость доставки — это про то, насколько быстро изменения в проде появятся или насколько быстро решатся проблемы юзеров без изменения в коде (поскейлим всё в x100).
У кого-то это хук в гите, у кото-то watcher на изменения в файловой системе разработчика или директории, куда релизные версии выкладываются и ftp'шкой сразу в прод.
Вариантов масса, но мне одноздачно нравится как, например, k8s решает вопрос скейлинга :)
Были времена, когда я апдейты уносил в прод файлзиллой, были времена, когда энтерпрайз писали на коболе. Но технологии меняются, развиваются и появляются новые, и надо уметь ими пользоваться и применять на практике (иначе окажешься за бортом, ну или миллионером-коболистом :) ).
Ни в коем случае не агитирую на переход в облака и k8s — это ваш выбор и только ваш. Но, если мне это экономит время, то я воспользуюсь этим тулом (а условные облака можно и на своих недорогих железках развернуть, благо некоторые хостеры дают такую возможнсоть)
Вы еще вот совсем оставляет в стороне контроль целостности. В случае с монолитом у вас есть РСУБД. В случае с микросервисами контроль целостности вы будете выполнять руками, а это крайне нетривиальная задача. Где окажется «скорость доставки», когда у вас, например, сервис оплат станет неконсистентным с сервисом заказов?
Вам в любом случае в контейнер нужно код положить, дак кидайте его мимо сразу в прод, без контейнера.
Та не вопрос, только, как я выше написал, тулы меняются, эволюционируют и за эволюцией надо успевать. Если ты продолжишь писать на Коболе и доставлять софт на прод дискетами, то найти к себе в команду людей будет сложновато. Это не значит, что надо всё контейнеризовать и обязательно крутить ими в клауде.
Вы еще вот совсем оставляет в стороне контроль целостности. В случае с монолитом у вас есть РСУБД. В случае с микросервисами контроль целостности вы будете выполнять руками, а это крайне нетривиальная задача. Где окажется «скорость доставки», когда у вас, например, сервис оплат станет неконсистентным с сервисом заказов?
Да, это острый вопрос, и про него я написал в TODO'шечках, и в комментах чуть-чуть уже обсудили, и оставлять в стороне я его не собираюсь и не хочу :)
Есть методика написания кода таким образом, чтобы миграция логики сервиса делалась в несколько шагов, когда разные версии разных сервисов становятся совместимыми (пару if'ов в коде).
В любом случае, задача комплексная и решать её надо с умом.
на прод дискетами,
Утрируете. Если хотите аналогию с дискетами то вы с дискетой таскаете еще и дисковод.
Да, утрирую, прошу прощения.
Но аналогия она примерно такая, да.
Мне k8s даёт удобный тул деплоя и скейлинга сервисов (а кубик работает с докером), и я его использую, и показал в статье как это можно делать в условиях работы с продуктом.
Делать так же в своём продукте вАм или не делать — дело исключительно ваше
Пока мы скейлились в 100 инстансов произошло две вещи:
- Все равно часть клиентов отвалилась, т.к. любой скейлинг работает с запозданием
- И мы разорились, т.к. нам пришел счёт за Амазон на сумму годового оборота магазина
Второй коллега тоже разорился, потому что с его монолитом тоже все клиенты разбежались.
И стали клиенты покупать товары у Амазона и на Алибаба.
Сказке конец.
Плюс.
Не надо забывать, что скорость поставки — это не только узкое место в виде скорости деплоя. А ещё и скорости разработки. Что толку от мгновенного деплоя, если разработать фичу — две недели. (Я знаю ответ на это замечание, надеюсь, тоже к нему придёте).
Ну, и распределенные системы — это всегда дорого. Дорого в эксплуатации. Облака дороже, чем баре метал. Инженеры дорогие. Если что-то ломается — оно может ломаться в неочевидных местах.
А ещё я троллю любителей u-сервисов тем, что в них прокси типа nginx едет через прокси. Ну, смотрите сами. WAF, LB, балансировщик кубера, потом Ingress, потом ещё мы захотим service mesh, потом мы ещё запихнем контейнер с nginx+php-fpm в кластер. И путь запроса станет весьма долгим. Да, это более масштабируемая история. Но не всем это надо
Прекрасная показательная история о том, как не надо слепо надеяться на технологии :)
"Ребята, тут новый API подвезли — заживём!" Нет, не заживём, а потестим, возьмём то, что реально ускоряет процесс, и ещё раз потестим, а потом может быть в прод.
А ещё я троллю любителей u-сервисов тем, что в них прокси типа nginx едет через прокси. Ну, смотрите сами. WAF, LB, балансировщик кубера, потом Ingress, потом ещё мы захотим service mesh, потом мы ещё запихнем контейнер с nginx+php-fpm в кластер. И путь запроса станет весьма долгим. Да, это более масштабируемая история. Но не всем это надо
Да, люди забывают, что сервисам в условиях микросервисного окружения надо общаться друг с другом, и чаще всего они это делают через HTTPv1, а он, зараза, медленный.
Отличный пост. Образцовый пайплайн. Все круто. Вышел с https://t.me/architect_says/242
Чего добавить? Ну, на самом много чего.
Например, squash не нужен. Во-первых, есть опция в последнем докере --squash Во-вторых, можно сделать сквош для нищих:
Dockerfile:
....
FROM your_image as base
.....
FROM scratch as squashed
COPY --from=base / /
# и где-то здесь еще ENV определить
ENTRYPOINT ["lalala"]
CMD ["lalala"]
По опечаткам — отослал.
CMD ["./run.sh"]
не понял — зачем так. Если можно ENTRYPOINT переопределить. И уж если совсем делать красиво, то вызывать jvm напрямую без промежуточных сабшеллов. И, да, я проверял — если все делать правильно, то переменные окружения в конечное приложение прокидываются (там есть пара нюансов с этим).
Запилить степ в пайплайне для сборки helm-чарта, деплоить в k8s с помощью чарта, давать конечным on-prem юзерам возможность деплоиться как с темплейтами, так и из чарта
Разумно. А еще круче — добавить в кластер https://github.com/fluxcd/flux-get-started
Решить проблему рассинхронизации API между сервисами в условиях НЕ-монорепозитория в гите (когда это три сервиса типа HelloWorld, то проблемы нет, но когда будет несколько десятков сложных сервисов, то наступит АД). Если кто-то занет железобетонные способы, то пишите в комментариях — буду рад узнать и обсудить :)
очевидное решение — сделать "интеграционное" репо, которое будет знать о всех микросервисах и содержать информацию об их взаимосвязах и версиях. И деплоить именно из него. Через механизмы типа гит хука можно в это репо кидать информацию о новых версиях деплойментов из репозиториев каждого из "микросервисов". Еще на самом деле нет проблемы, если у вас в экосистеме будут деплойменты с версией API v1 и версией API v2. Здесь больше вопрос декомпозиции — готовы ли вы сказать, что API v2 — это не следующая версия приложения, а НОВОЕ приложение. Тогда как бы проблема рассасывается сама собой. Либо тащить в какой-то момент времени ОБЕ версии апи и каким-то внешним способом контролировать, что как только потребители v1 исчезли, то можно его выпилить из кодовой базы и деплоить новую версию только с v2, но это тогда не ТЕХНИЧЕСКАЯ, а ОРГАНИЗАЦИОННАЯ проблема
В общем — у меня все замечания на самом деле минорные.
Спасибо за замечания!
Например, squash не нужен. Во-первых, есть опция в последнем докере --squash
Про --squash
в последнем докере почитаю.
не понял — зачем так. Если можно ENTRYPOINT переопределить
Почему CMD["./run.sh"]
? Это скорее "исторически сложилось": сдедали сервис, запаковали так в докер, работает — не трогай :) будем рассматривать и другие true way способы.
Разумно. А еще круче — добавить в кластер https://github.com/fluxcd/flux-get-started
Относительно деплоя в куб была ещё идея в оператор это всё завернуть, но то уже тема отдельной статьи. А за ссылку спасибо!
очевидное решение — сделать «интеграционное» репо, которое будет знать о всех микросервисах и содержать информацию об их взаимосвязах и версиях. И деплоить именно из него. Через механизмы типа гит хука можно в это репо кидать информацию о новых версиях деплойментов из репозиториев каждого из «микросервисов». Еще на самом деле нет проблемы, если у вас в экосистеме будут деплойменты с версией API v1 и версией API v2. Здесь больше вопрос декомпозиции — готовы ли вы сказать, что API v2 — это не следующая версия приложения, а НОВОЕ приложение. Тогда как бы проблема рассасывается сама собой. Либо тащить в какой-то момент времени ОБЕ версии апи и каким-то внешним способом контролировать, что как только потребители v1 исчезли, то можно его выпилить из кодовой базы и деплоить новую версию только с v2, но это тогда не ТЕХНИЧЕСКАЯ, а ОРГАНИЗАЦИОННАЯ проблема
Согласен про "организационность" и "не техничность" проблемы рассинхронизации API. Тоже думал про отдельный репозиторий со всеми связями между сервисами и версиями API, но пока не начинаешь это делать, то всё кажется овер сложным и не решаемым. Главное начать. Про версии деплойментов — отлично подмечено, надо попробовать :)
Еще идиотский вопрос — какие выгоды от запуска интеграционных тестов в Concourse-CI в dind? Почему их сразу в кубернетес не гонять на отдельном неймспейсе?
Еще не понял пляску с LE — не проще было кластерный cert-manager сразу настроить? Или это за скоупом статьи? Ну, он просто по инструкции как в статье — можно подкинуть ЛЮБОЙ серт, хоть купленный у DigiCert/thawte/GoDaddy etc.
Еще идиотский вопрос — какие выгоды от запуска интеграционных тестов в Concourse-CI в dind? Почему их сразу в кубернетес не гонять на отдельном неймспейсе?
Потому что тесты пишут разработчики, и у них есть очень классный готовый тул для запуска контейнеров прямо из кода тестов, то есть не надо городить отдельный степ в пайплайне сначала для запуска тестовых контейнеров, а потом для остановки после прогона тестов. А ещё в этом степе надо знать какие именно контейнеры стартовать, а пайплайнами может заниматься отдельный человек, не совсем близкий к коду и ему постоянно надо будет держать руку на пульсе разрабочтиков и знать какие контейнеры когда и как стартовать и когда и как стопать.
Когда я столкнулся с проблемой запуска контейнеров в контейнере, то в голову сразу пришла идея — а нет ли готовой реализации в tescontainers, которая бы позволяла тестовые контейнеры запускать сразу в кубике? Ответ: готовой реализации нет, но работы, похоже, ведутся
Еще не понял пляску с LE — не проще было кластерный cert-manager сразу настроить?
Кластерный cert-manager работает, если ты имеешь дело с регистратором доменов, реализация для работы с которым есть в кубике (проблема не в LE, а в том месте, где ты домен держишь)
Речь сейчас именно про LE-сертификат, чтобы дёшево можно было сделать сертификат на любой домент.
Мой домен (bihero.io
) хостится в GoDaddy и k8s не смог мне автоматом создавать сертификаты для моих поддоменов. Я пробовал, честное слово, но кубик не смог из коробки это сделать, пришлось ручками ходить (там в DNS записи надо добавлять текст руками, который просит LE).
Чуть не забыл: проблема не возможности кубика автоматически создавать и рулить сертификатами была выявлена при работе с wildcard сертификатами LE
Ну, он просто по инструкции как в статье — можно подкинуть ЛЮБОЙ серт, хоть купленный у DigiCert/thawte/GoDaddy etc.
Да, подкинуть можно любой сертификат
Касательно godaddy вижу два варианта. Либо взять что-то вроде https://www.npmjs.com/package/acme-dns-01-godaddy и прикрутить его. И челлендж отправлять через эту промежуточную apiху.
Причины, по которым нет godaddy в ядре cert-manager, описаны тут https://github.com/jetstack/cert-manager/issues/1083
Либо можно сделать лайфхак. Делегируешь всю зону на cloudflare. Там отключаются все платные штуки с проксированиес трафика. А далее пользуемся штатной интеграцией cert-manager и CF
Когда я столкнулся с проблемой запуска контейнеров в контейнере, то в голову сразу пришла идея — а нет ли готовой реализации в tescontainers, которая бы позволяла тестовые контейнеры запускать сразу в кубике? Ответ: готовой реализации нет, но работы, похоже, ведутся
Спасибо, подумаю над этим.
Кластерный cert-manager работает, если ты имеешь дело с регистратором доменовВы, видимо, хотели сказать «с хостером доменов»? В функции регистраторов не входит создание/удаление записей внутри зоны.
Мой домен (bihero.io) хостится в GoDaddy и k8s не смог мне автоматом создавать сертификаты для моих поддоменов.Не знаю как в k8s, но думаю, что как и у всех остальных — есть скрипты (я пользовался certbot, потом перешел на acme.sh), которые в своем составе имеют подключаемые библиотеки по работе с хостерами доменов, у которых есть API. Эти скрипты позволяют писать/подключать свои библиотеки, если для нами используемого хостера оные (библиотеки) отсутствуют.
Если ковырнуть k8s, может он тоже готов предложить что-то подобное?
Вы, видимо, хотели сказать «с хостером доменов»? В функции регистраторов не входит создание/удаление записей внутри зоны.
Да, именно это я имел ввиду :)
Насчёт интеграции godaddy и кубика — вооон там чуть выше gecube расписал совет, как сделать. А подключаемые библиотеки для certbot — не, не слышал) но тема интересная, надо попробовать. Спасибо!
Смотрите сразу в сторону acme.sh.
Насчёт интеграции godaddy и кубика — вооон там чуть выше gecube расписал совет, как сделать.У меня нет кубика и dns-хостингом от godaddy я не пользуюсь, потому мне сложно дать оценку его совету. Но его лайфхак с делегированием зоны на cloudflare поддерживаю полностью.
Про использование cloudflare скажу так — если б не было cloudflare, то я был бы вынужден писать что-то подобное по части управления зонами и записями под какой-либо сервер имен.
Каковы требования по диску (общий объем файлов) и по памяти (16GB? 32GB?) для этой реализации?
Примерные цифры такие (очень примерные):
- CI: диск 200Gb, RAM 8-10Gb (по сути там три сервиса в докер-контейнерах: CI worker node, CI UI node, postgresql, — для более или менее серьёзных намерений надо ноды разносить на разные виртуалки)
- k8s master node: диск 100Gb, RAM 8Gb
- k8s worker node: диск 300Gb, RAM 12Gb
- nexus: диск 1Tb, память 8Gb
Объём файлов отдельно не считал.
Например, такой кейс, по минмуму
Есть БД. 2 инстанса — мастер и слэйв.
Есть миддл-сервис (микросервис), тоже 2 инстанса.
Есть веб-сервер (держит сессии, делает аутентификацию), 2 инстанса.
Есть балансировщик (nginx, допустим).
И есть 100500 клиентов (броузеры + мобильные приложения).
Мы добавляем много нового функционала, в результате которого меняется структура БД, меняется api между фронт-сервером и микросервисами, меняется клиентское api.
Обновить всё сразу мы не можем, так как часть клиентов обновляться не хочет, приложение для iOS проходит проверку и т.п. + мы должны поддерживать старых клиентов.
Вопрос. Как сделать так, чтобы ни один клиент не пострадал? Я описал реальный кейс и нечто похожее (но более простое) делал руками (руками запускал и останавливал контейнеры, запускал скрипты миграции и всё контролировал лично).
Касательно вариантов — во второй части моего сообщения. Ну, и всякие обратно совместимые миграции и пр. И, конечно, есть еще методика feature flag и рекомендую «есть слона по частям»
'Hello World' вам в облако