План статьи является примерным и не говорит о прямом содержании глав, это лишь памятка для взаимодействия:
Вступление с подводками, что за клиент пришёл, какие у него были проблемы, что нужно было закрыть первым приоритетом, какие задачи были в долгой перспективе?
Описание инфраструктуры.
Представление решений. Рассказ про схему унифицированности подхода CI/CD.
Внутренняя инфраструктура и как проще, но правильнее ЛИ?
"Скорость" и "качество" синонимы?
Итоги с “грустными” выводами, но хорошей моралью.
Ценность
Для инженеров: понимание общей концепции деплоя и осознания, какой путь правильный, а какой ведёт к проблемам или провалу при будущем администрировании.
Для бизнеса: понимание того же подхода в своём контексте и осознание необходимости его существования в своей инфре, чтобы соответствовать реалиям рынка и быть готовым к возможным изменениям в кадрах.
Глава 1: Надо было вчера
Каждый человек, который имеет отношение к IT, будь то ПМ, разработчик, Sales или DevOps, рано или поздно за свою карьеру столкнётся с фразой “надо было вчера”. К сожалению, при погружении в эту ситуацию можно прийти к выводам, что никто не виноват в причинах ее возникновения.
Не получилось найти общий язык с предыдущим подрядчиком.
Долго происходил розыгрыш тендера, а бизнес не ждет.
Предыдущие сотрудники не смогли закрывать необходимый объем задач.
Нехватка своих кадров.
В целом, всё это не удивительно и знакомо, волей случая и мне удалось стать участником таких событий. Относительно недавно к нам пришёл проект, у которого уже были сформулированы требования к будущей инфраструктуре, содержащие в себе следующие задачи: несколько окружений для разработки, отказоустойчивость, быстрота деплоя будущих приложений, мониторинг и стабильная доступность работоспособности приложений. Исходно сроки весьма сжатые, а список микросервисов составляет около 18 штук. Что же делать и как быть?
Глава 2: Пойми свой путь и сможешь его пройти
Чтобы не пугаться больших поставленных задач, рекомендуется изначально разбивать их на более мелкие. Если речь идёт про 3 контура для разработки, то нам будет достаточно лишь одной схемы архитектуры для понимания всех будущих задач. В нашем случае она имеет весьма простые составляющие, о каждой из которых по порядку:
Первое, о чем хочется сказать — это оркестратор контейнеров. Ранее уже был развернут стандартный кластер kubernetes, как managed решение от Azure. Важным пунктом в этом процессе является выбор 3-х worker нод кластера в различных зонах доступности (AZ1,AZ2,AZ3). Посыл данного решения весьма понятен и не завязан на ограничении по количеству допустимых конфигураций виртуальных машин в регионе. Его целью является повышение отказоустойчивости и доступности микросервисов, так как каждый знает, что могут возникнуть проблемы в одной из зоны облака, хотя и вероятность данного события для каждого уважаемого облака меньше 1%.
Второй немаловажный момент — нам нужно понимать, как запрос будет поступать и проксироваться внутри нашего кластера. Здесь стандартным решением является использование излюбленного и гибкого продукта ingress-controller-nginx, разворачивание которого создает LoadBalancer в рамках облака, готового принимать запросы на внешний IP адрес и проксировать в дальнейшем внутри инфраструктуры запрос в приложение.
Открывая любой сайт в интернете, мы всегда используем https протокол, и тут перед нами снова встает выбор: “как нам организовать наличие SSL сертификата при запросе к нашим доменам?”. Но чтобы ответить на этот вопрос, нужно понимать, с чем мы будем иметь дело.
У нас будет использоваться 1 единый домен 2-ого уровня с разделением трафика до микросервисов через URL path?
Мы будем использовать различные домены 3-его уровня, создавая А-запись с “*” и резолвя все запросы на External IP?
Мы будем использовать различные виды доменов не подвязываясь под всё что описано выше?
Не важно какой случай ваш, ведь у вас всегда будет 3 варианта пути.
Деплой cert-manager и создания сущности ClusterIssuer для автоматического выпуска и перевыпуска бесплатных SSL сертификатов от центра сертификации Let’s Encrypt. Это практично, легко и удобно, а самое главное — бесплатно.
Покупка платных сертификатов и прокидывание их на уровне каждого ingress в виде secret в том же namespace, где располагается pod с контейнером.
P.S. Не самый удобный вариант реализации, как на уровне деплоя микросервиса, так и в дальнейшем обновлении сертификатов.
Использовать один единный default сертификат для всех наших ingress на уровне ingress-controller. Для этого необходимо лишь во время деплоя нашего Helm чарта указать extra arguments в следующем виде:
controller:
kind: Deployment
extraArgs:
default-ssl-certificate: "kube-system/ssl-crt"
Где в первую очередь идёт имя namespace, а во вторую имя secret-а.
Третий немаловажный момент, про который все любят забывать и из-за которого можно встрять в очень неприятную ситуацию — мониторинг. В рамках стандартных задач мониторинга чаще всего используется PGA (Prometheus Grafana Alertmanager), их разворачивание, путём использования Helm чарта kube-prometheus-stack, при правильной настройке может покрыть весь ваш кластер и помочь обложиться минимально необходимым набором метрик. В совокупности эти инструменты могут с легкостью дать визуальное отображение работоспособности инфраструктуры и всех ее основных моментов, а также при настройке триггеров могут оповестить заранее о наличии проблемы в любой удобный канал общения (Telegram, Slack, Mattermost, mail). Согласитесь, с точки зрения бизнеса о наличии возможной или текущей проблемы лучше всего узнавать самому, а не от конечного пользователя.
Пункт четыре: не надо делать всё, что есть только для администраторов. Иногда помощь нужна и разработчикам.
Логи — это хлеб насущный каждого инженера, который помогает производить как debug текущих микросервисов, так и хранить информацию о различных событиях при взаимодействии пользователей с системой.
Способов сбора и хранения логов существует множество. Стек технологий, которые это покрывают, весьма большой, а выбор того или иного приложения в вашем случае должен зависеть от конечных потребностей. Если ваши запросы весьма простые и у вас нет необходимости обрабатывать и хранить большое количество информации, то ваш выбор Promtail и Loki, но несмотря на новаторство данных средств, у них есть значительных ряд минусов, которые нас не устраивали. Именно по этим причинам был с радостью выбран стек ELK (ElasticSearch Logstash Kibana) и за ними прячется filebeat.
«Почему именно они?»:
Предполагается большой поток данных в рамках каждого контура
Нам нужна возможность кластеризации.
Наличие web-интерфейса с детальной настройкой паттернов.
Возможность распределение привилегий доступов различным пользователям.
Индивидуальные настройки ротации.
Немного про архитектурные особенности микросервисов. Нам требовались Redis для хранения кеша микросервисов и Minio для хранения статики, например фотографий. Каждый, кто хоть немного понимает принцип работы облака, с ходу может задать вопрос:
- Зачем вам Minio?
- Храните статику в PVC предоставляемых на уровне облака Azure или же используйте просто blob-fuse-azure.
Но всё не так просто, как кажется. Разработчики, как и само приложение, нуждались в бакетах, имеющих работу протокола по принципу S3. И этими словами уже нельзя описать работу blob-fuse от Azure. Также была необходимость в web-интерфейсе для работы со статикой и возможность раздачи контента с URL path.
Любимый и часто возникающий вопрос демагогий, в рамках которого каждый имеет своё мнение — это база данных. Чтобы ускорить время обработки запроса между микросервисом и БД, можно произвести ее разворачивание в кластере k8s через statefullset и pvc, но является ли это решение правильным с точки зрения поддержки и обслуживания? Я думаю нет, так как БД всегда лучше всего располагать либо на отдельных серверах, либо использовать managed, ведь это принесёт вам следующие плюсы:
Минимизация затрат. В рамках каждого облачного manage решения вы всегда платите только за то, что используете.
Легкость масштабируемости
Безопасность и производительность как гарант со стороны облака.
Отсутствие необходимости в детальном тюнинге и настройке.
Встроенные возможности создания snapshot-ов и бэкапов.
Глава 3: Размер имеет значение
Любая крупная архитектура всегда сложна как в правлении, так и в контроле, но самое сложное — разворачивание. Так как kubernetes в своей основе имеет декларативный подход, отсюда можно сделать вывод о том, что нам нужно не говорить системе “что” надо делать, а описывать “как” она должна выглядеть. И здесь появляется золотое правило, которое гласит:
/// успех всего процесса — это IaC (Infrastructure As Code).
Инструменты, помогающие нам решить этот вопрос — Terraform и Helm. Почему же они полезны?
Сохранение состояния релиза приложения или ресурса.
Увеличение скорости разворачивания новых сущностей облака или продукта, так как ранее подход уже отработан и может быть переиспользован на другие окружения.
И как бы это странно и смешно не звучало, но ещё один пункт — это уважение к будущим или текущим коллегам. Все правки в инфраструктуру должны быть закоммичены и описаны в виде манифестов. Это дает всем единую точку управления ресурсом и позволяет понять, как реализована инфраструктура без глубокого погружения.
Из личного опыта хочется очень сильно рассказать про последний пункт или даже поделиться печальным опытом, который никому не пожелаешь. При подключении к одному из проекту от предыдущего DevOps инженера не осталось репозиториев, в рамках которых он разворачивал сущности. Вся дальнейшая работа в рамках задач, которые необходимо было выполнить, вынуждали тратить дополнительное время на разбор манифестов внутри кластера. Весь процесс тратит дополнительно время как инженера, так и клиента, и не гарантирует стабильный результат в сжатые сроки.
//// А нужно ли это кому-то?
Думаю, ответ на этот вопрос каждый способен дать сам. Если понимание инфраструктуры уже сложилось, то встаёт вопрос деплоя микросервисов в эту инфраструктуру, и тут от слов хочется перейти маленько к практике.
Способов деплоя в Gitlab существует бесконечно много, ручное создание и генерация ServiceAccount-ов c необходимыми ClusterRole или обычными Role. Использование интеграций внутри системы управления репозиториями, создание шаблонных .gitlab-ci.yml и универсальных чартов или же кастомизированные решения. В случае, если вам нужен единый и быстрый деплой, а все ваши микросервисы имеют идентичную структуру, то вы можете использовать универсальный CI/CD. Создаём отдельный репозиторий и описываем общие блоки для билда и деплоя нашего микросервиса.
Единожды описав 2 стадии общим шаблоном для build Dockerfile:
.build_template: &build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [ "" ]
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context . --dockerfile ./Dockerfile --destination ${CI_REGISTRY_IMAGE}:${TAG}
И деплоя:
.deploy_template: &deploy
image:
name: alpine/k8s:1.22.6
entrypoint: [""]
script:
- helm upgrade --create-namespace --install ${CI_PROJECT_NAME} --namespace ${NAMESPACE} helm -f ${FILE} --insecure-skip-tls-verify --kubeconfig ~/.kube/config --set "pods.env=${IMAGES_ENV}" --set "microservicename=${CI_PROJECT_NAME}" --debug
Можно создать общий default-gitlab-ci.yml с включением в себя этих глобальных job:
include:
- project: ‘$PROJECT_PATH’ ## имя проекта/путь до вызываемого файла
ref: main ## ветка репозитория, в рамках которой лежит файл
file: '.build.yml' ## имя вызываемого файла
- project: ‘$PROJECT_PATH’
ref: main
file: '.deploy.yml'
stages:
- build
- deploy
build:
extends: .build_template ## вызов шаблона деплоя из include
stage: build ## имя стадии
variables:
TAG: "${CI_PIPELINE_ID}"
only:
refs:
- /^release\/.*$/
deploy-dev:
extends: .deploy_template ## вызов шаблона деплоя из include
when: manual ## включение ручного деплоя
stage: deploy ## имя стадии
variables:
TAG: "${CI_PIPELINE_ID}" ## образ build-а контейнера
NAMESPACE: "dev" ## namespace куда деплоится микросервис
FILE: "helm/dev.yml" ## путь до файла values Helm внутри репозитория микросервиса
environment:
name: dev ## окружение для деплоя
only:
refs:
- /^release\/.*$/ ## тригер джобы только в release ветках
/// Как быть дальше?
Ответ прост: указываем на уровне настроек каждого репозитория, какой .gitlab-ci.yml мы желаем использовать:
Глава 4: Скорость и качество это синонимы?
После проделанной работы всегда нужно ответить на вопрос: что мы получаем в конечном счёте?
У нас есть отказоустойчивая инфраструктура в различных зонах доступности для наших микросервисов. Если у нас корректно настроены nodeAffinity и мы имеем по 3 реплики для каждого микросервиса, то даже падение двух нод не приведет к недоступности нашего ПО.
Мы имеем мониторинг и логирование, что повышает время анализа инцидентов и исключает возможные простои.
У нас есть бэкапы данных наших пользователей и мы имеем гарантирую сохранения всех данных.
У нас есть описанная инфраструктура, которую может с лёгкостью администрировать другой человек и завязки процессов на одном сотруднике.
У нас имеется общая концепция гибкого процесса CI/CD, которая позволяет быстро добавлять новые микросервисы/фичи для клиента и соответствовать принципам Time To Market на рынке.
Скорость — это не всегда качество, но при правильно настроенных процессах можно попытаться превратить эти слова в синонимы. Однако не стоит забывать, что для этого требуется усердная подготовка и выстраивание концепции на всех уровнях работы.
Существует множество различных решений для различных проблем. Возможно, что пока статья создавалась, были разработаны новые подходы в деплое микросервисов и в способах построения архитектуры. Нельзя почти всегда говорить, что какой-то вариант правильный, а какой-то нет. Каждый случай нужно разбирать сугубо индивидуально, но зачастую банальная шаблонизация ведёт к улучшению понимания концепции инфраструктуры и к её дальнейшему масштабированию. Придя на весьма большой проект и имея сжатые сроки, нужно всегда понимать, как разбить большую задачу на маленькие.
И как не сорвать сроки сдачи, а перенести в зону комфорта.
zergon321
А почему вообще за ошибки мэнэджмента при планировании, организации и т.д. должны расплачиваться разработчики?
AlexSheverev Автор
Вопрос немного риторический, однако ответ можно сформулировать как "потому что мы все одна команда и у нас есть задача выполнить свою работу, прикрывая друг-друга" ;)
zergon321
Вот только крайним всегда будет далеко не мэнэджмент