Привет всем! На одном из проектов нам потребовалось внедрить непрерывную интеграцию и доставку CI/CD. Поиск готового решения показал, что все наши потребности может закрыть GitLab CI. Однако GitLab - платный продукт, и на момент принятия решения возникли некоторые трудности с его коммерческим использованием. Поиски альтернативы не увенчались успехом, и было принято решение создать CI/CD своими силами из имеющихся бесплатных OpenSource-инструментов, поддерживаемых сообществами.
В данной серии статей мы хотим рассказать о нашем пути, этапах работы и компонентах платформы.
В первой статье мы обзорно осветим ключевые элементы платформы. В следующих статьях рассмотрим каждый компонент подробно.
Контейнеризация Docker и кластер Kubernetes
Первой важнейшей задачей стала унификация запуска всех компонентов платформы и приложений, которые будут разрабатываться с помощью платформы. Универсальным и модным на сегодняшний день решением является запуск приложений в Docker контейнерах в кластере Kubernetes.
Большинство разработчиков программного обеспечения создали официальные Docker образы для своих продуктов и выкладывают их на сайте hub.docker.com. Если официального образа приложения нет, всегда можно собрать свой Docker образ на базе официального. Мы, например, упаковываем приложения в Docker-образы на базе официальных легковесных образов Alpine Linux.
Кластер Kubernetes для платформы устанавливается на “голое железо” или VPS (Bare Metal) с предустановленной ОС Ubuntu или Debian, что позволяет интегрировать платформу в инфраструктуру на собственном "железе". Для настройки и администрирования Kubernetes используем только kubeadm, kubectl и helm, чтобы платформу мог поддерживать любой DevOps, владеющий штатными инструментами.
Gitea/Forgejo и непрерывная интеграция Continuous Integration
Функции хостинга кода и системы управления версиями в платформе реализует Forgejo - бесплатный fork сервера Gitea, поддерживаемый сообществом. Собственный git-сервер в составе платформы позволяет не зависеть от сторонних продуктов и облачных решений, а также хранить код разрабатываемого приложения в собственной инфраструктуре.
git-сервер решает 2 основные задачи:
организация совместной разработки кода командой программистов
Код каждого приложения хранится в master-ветке git-репозитория. Программист клонирует репозиторий приложения к себе на лаптоп, создает свою под-ветку, выполняет задание и делает запрос pull request на вливание изменений из своей ветки в main-ветку. В команде появляется человек, ответственный за прием запросов на слияние от программистов и организованно создающий запросы merge request на вливание изменений из веток программистов в master-ветку разрабатываемого приложения.
реализация подхода - инфраструктура как код IaC
Для каждого приложения (frontend, backend, mysql, postgresql, redis, memcached и т.д) создали git-репозиторий в Forgejo.

Git-репозиторий приложения содержит: Dockerfile с описанием слоев Docker-образа приложения, конфигурационные файлы приложения, helm-чарт развертывания приложения в кластере Kubernetes и Jenkinsfile с описанием пайплайнов сборки и доставки приложения в кластер Kubernetes. Код самого приложения находится в директории src.

Детально реализацию непрерывной интеграции мы рассмотрели в статье CI/CD Kubernetes платформа Gitorion. Непрерывная интеграция Continuous Integration на базе Gitea/Forgejo
Jenkins и непрерывная доставка Continuous Delivery
Для решения данной задачи установили Jenkins в кластер Kubernetes и для каждого приложения создали пайплайн сборки и доставки.

Для внесения изменений в код или конфигурацию приложения достаточно сделать правки в соответствующие файлы его git-репозитория и сделать push. Push автоматически заставит Forgejo послать web-хук в Jenkins.
Jenkins по web-хуку из Forgejo автоматически вытянет содержимое git-репозитория приложения и выполнит job-ы в Jenkinsfile.

Затем, Jenkins возьмет исходный код приложения из директории /src git-репозитория, скомпилирует его, если приложение написано на компилируемом языке программирования, и упакует в Docker-образ в соответствии со слоями в Dockerfile. Если приложение написано на интерпретируемом языке программирования, Jenkins просто скопирует код из git-репозитория в Docker-образ. На финальной стадии Jenkins выполнит деплой Docker-образа с приложением в кластер Kubernetes, используя helm-чарт.
В пайплайнах Jenkins реализовали следующие Job-ы:
review - сборка и доставка приложения в динамическое окружение разработчика в development контур;
staging - сборка и доставка микросервиса в staging контур;
production - промоушен микросервиса из staging контура в production;
rollback - откат на одну из предыдущих версий в случае ошибки;
production-canary - развертывание canary-релиза.
Подробней аспекты непрерывной доставки мы приводим в отдельной статье CI/CD Kubernetes платформа Gitorion. Непрерывная доставка Continuous Delivery на базе Jenkins
Приватный реестр Docker-образов Harbor
Для хранения Docker-образов установили в платформу приватный реестр Docker-образов Harbor, который позволяет создавать публичные и приватные репозитории. Помимо поддержки стандартных операции загрузки push и выгрузки pull образов Docker, Harbor имеет Web-интерфейс, в котором можно выполнить необходимые настройки приватного реестра. Harbor поддерживает OIDC и интегрируется со сторонними сервисами аутентификации, что позволило настроить аутентификацию пользователей Harbor в Keycloak. Кроме того, Harbor поддерживает разграничение прав на основе ролей RBAC, что дало возможность назначить административные права в Harbor пользователям из группы admins в Keycloak, а разработчикам из группы devs разрешить только загружать и выгружать Docker-образы.

Чтобы не перегружать вводную статью, все тонкости интеграции Harbor мы рассмотрели в статье CI/CD Kubernetes платформа Gitorion. Реестр Docker-образов Harbor c аутентификацией в Keycloak и доступом по ролям RBAC
Keycloak и единый вход SSO
Всем участникам проекта, который будет развернут на платформе, нужно предоставить доступ к web-интерфейсам: Forgejo, Jenkins, Harbor, Grafana, Phpmyadmin, Pgadmin. Каждый из них требует аутентификации и имеет собственную базу данных пользователей и паролей. С ростом числа участников проекта администрирование большого числа аккаунтов в нескольких базах данных станет трудно выполнимой задачей.
Тут на помощь пришел Keycloak и технология единого входа SSO. Установили в платформу Keycloak и создали в нем единую базу данных пользователей. Добавили 2 группы: admins и devs. Группу admins ассоциировали с ролью admins, дающей участникам группы административные права во всех сервисах. Группу devs ассоциировали с ролью devs, дающей участникам группы права только Read Only.
На web-интерфейсе каждого сервиса есть кнопка “Войти через keycloak”.

Пользователь нажимает на эту кнопку, и его направляет на форму аутентификации Keycloak.

Пользователь вводит логин и пароль, и, в случае удачной аутентификации, Keycloak направляет пользователя уже аутентифицированным обратно в сервис, из которого тот пришел в Keycloak. Пройдя аутентификацию в Keycloak, пользователь получает доступ к web-интерфейсам всех сервисов платформы, без надобности повторного ввода логина и пароля в каждом из них.
Подробней реализацию SSO в статье CI/CD Kubernetes платформа Gitorion. Единый вход Single Sign-On (SSO) во все сервисы платформы при помощи Keycloak
Prometheus, Grafana и мониторинг платформы
Задачу мониторинга платформы решили, установив в кластер Kubernetes стек Prometheus + Grafana. Визуализировали в Grafana метрики node-exporter, дающие представление о нагрузке на основные подсистемы хоста, на котором установлена платформа: CPU, ОЗУ, HDD, сетевой стек и т.д.

Настроили Alertmanager и отправку предупреждений о критических проблемах на e-mail.
Подключили сбор бизнес метрик с проекта, развернутого на платформе, и визуализировали в Grafana на отдельном Dashboard.
Аварийное восстановление и горизонтальное масштабирование
Для сокращения времени простоя в случае аварии мы проработали вариант горизонтального масштабирования CI/CD платформы Gitorion на несколько дата центров.
Для Kubernetes заменили штатную базу данных etcd на PostgreSQL, используя Kine от K3S. Это позволило иметь реплики базы данных Kubernetes в нескольких дата центрах и при падении одного из дата центров запустить компоненты управляющего слоя Kubernetes в одном из выживших дата центров. Подробности реализации данного подхода мы вынесли в отдельную статью CI/CD Kubernetes платформа Gitorion. Highly Available кластер Kubernetes
Для хранения данных приложений в нескольких дата центрах мы разработали и подключили к кластеру Kubernetes по протоколу NFS сетевой реплицируемый NAS. В ведущем дата центе расположили Primary реплику DRBD с которой компоненты платформы и разрабатываемого приложения работают в режиме чтения/записи. В ведомые дата центры данные реплицируются в реальном времени с помощью DRBD на Secondary реплики. Подробней рассмотрено в отдельной статье CI/CD Kubernetes платформа Gitorion. Реплицируемый NAS для Highly Available кластера Kubernetes
Для хранения и репликации данных управляющего слоя Kubernetes, компонентов CI/CD платформы Gitorion и разрабатываемого приложения мы используем базу данных PostgreSQL.
В итоге мы добились перезапуска платформы и разрабатываемого с ее помощью приложения из упавшего дата центра в выживший в течение нескольких минут. Прочитать про технику перезапуска вы можете в статье CI/CD Kubernetes платформа Gitorion. Highly Available исполнение
SSL и безопасный доступ к сервисам платформы
Установили в платформу Cert-manager, который сгенерировал инфраструктурный доменный wildcard-сертификат и СА сертификат удостоверяющего центра организации, которым подписал доменный сертификат. Доменный сертификат подключили к web-интерфейсам сервисов платформы, а CA сертификат выдали участникам проекта. Это позволило сделать инфраструктуру платформы независимой от внешних удостоверяющих центров. К главному домену проекта, развернутого на платформе, подключили Let`s Encrypt сертификат и настроили автоматическое его обновление каждые 3 месяца.
Zero Trust и сетевая безопасность
Сетевую безопасность на платформе настроили в соответствии с подходом Zero Trust – закрыли весь трафик, как из Интернета в платформу, так и локальный трафик в кластере Kubernetes. Из Интернета открыли на платформу доступ только к трем портам: 2222 – ssh на хост, 443 – HTTPs к web-интерфейсам и 22 – работа с git по ssh. В кластере Kubernetes инстансы компонентов платформы, контуры development, staging и production разместили в отдельных namespace и изолировали на сетевом уровне друг от друга. Разрешили трафик в пределах namespace и минимальный трафик между namespace, необходимый для взаимодействия инфраструктурных сервисов.
Заключение
Просим тех, кто в данный момент на своих проектах использует CI/CD, в комментариях написать, какой функционал следует добавить в платформу, и проголосовать ниже. Будем рады конструктивной критике, замечаниям и предложениям, которые помогут устранить недостатки и усовершенствовать платформу. Спасибо за внимание!