Это вторая часть из цикла статей, посвященных инфраструктуре для стартапов. Всего их будет две, поделенные, по моему мнению, на логически законченные блоки.
Часть первая. Настройка окружения
Часть вторая. CI/CD и советы
Для кого и для чего описывать не буду, см. ч.1 этой статьи.
Предисловие
Предложенная мной архитектура действительно имеет ряд недостатков, которые важно учитывать при её использовании. Некоторые из них были отмечены в комментариях к первой статье, а с другими я столкнулся лично в процессе эксплуатации. Ниже перечислю основные слабые места и риски, которые могут стать критичными в определённых сценариях:
-
Swarm может “сломаться на двух серверах — нужен минимум 3 менеджера, иначе можно поймать split-brain
-
Swarm хотя и жив, но сообщество давно ушло в сторону Kubernetes
-
Отсутствие self-healing-кластера. Docker Swarm не имеет полноценного встроенного механизма для автоматического восстановления после сбоев
> Уже были мысли писать скрипты для автоматического расширения/поднятия дополнительных нод и перезагрузки старых -
Отказоустойчивость искусственная — если оба VPS у одного хостера, это не спасёт от падения
> Больше VPS = больше точек отказа. Не факт, что несколько худых vps могут быть лучше одного хорошего, так у нас периодически падают машинки, особенно неприятно когда с traefik’ом -
Overlay-сети в сварме на дешевых VPS могут чудить
> Мы с товарищем до сих пор не смогли найти, кто конкретно стирает часть хедеров у запросов, несколько ночей дебага не помогли. Отложили на потом.
CI/CD через Portainer
После настройки окружения сразу встал вопрос: как автоматизировать деплой, чтобы не лазить каждый раз по SSH и не обновлять сервисы руками.
Самый простой и рабочий способ — использовать Portainer Webhooks + GitHub Actions (или GitLab CI)ля автосборки и доставки.
Идейно наш процесс ci/cd заключается в следующем:
-
Есть репозиторий с шаблонами пайплайнов
-
Для проектов используем моно-репозитории, где внутри сервисы разделены по директориям
-
В репе сервисов лежат пайпланы на каждый сервис
-
Секреты лежат в GH Secrets (теперь в
Vault). Как перенести или использовать волт — к другим статьям -
Пайплайны делят образы сервисов на dev/prod с помощью тега
:devиlatestсоответственно -
После билда — дергается ручка portainer и по уникальному хешу сервиса, обновляется образ
1. Структура пайплайнов и организация репозитория
В отдельном репозитории для пайплайнов мы храним общий шаблон:
В репозитории проекта, мы переиспользуем следующим образом:
name: deploy-server on: workflow_dispatch: inputs: environment: required: true description: Deploy to PROD/DEV type: choice options: [PROD, DEV] default: DEV jobs: deploy: uses: .shampsdev/cicd-pipelines/.github/workflows/pipeline.yaml@main permissions: packages: write contents: read attestations: write id-token: write secrets: inherit with: dockerfile_path: 'server/Dockerfile.server' context_path: 'server' image_name: 'project-server' environment: ${{ github.event.inputs.environment }} secret-service-hash: ${{ github.event.inputs.environment == 'PROD' && 'SERVER_SERVICE_HASH' || 'SERVER_SERVICE_HASH_DEV' }}
Можно заметить, для билда используется Dockerfile из директории нужного сервиса в репозитории. Его содержимое вы определите сами.
2. Разделение окружений
Для разделения DEV и PROD мы используем workflow_dispatch с выбором окружения.
Внутри пайплайна окружение влияет на:
-
теги Docker-образов (
:dev,:latest); -
секреты (разные
hash‘и дляsecret-service-hash);
В будущем можно автоматизировать запуск от пуша в ветку, но ручной запуск — хорошая защита от случайных релизов. Мы предпочитаем гнать все по кнопочке.
Каждое окружение в отдельном Stack’e project-dev и project-prod
3. Webhook Portainer и обновление сервиса
В Portainer для каждого сервиса можно включить отдельный webhook — он будет просто перезапускать сервис с обновлённым образом.
Где найти:
-
Зайди в Portainer, в Services
-
Выбери нужный сервис
-
Внизу будет блок Webhooks
-
Нажми «Enable webhook» — появится уникальный URL
Он выглядит примерно так:
https://portainer.example.com/api/webhooks/abcdef1234567890
Именно abcdef1234567890 необходимо ввести в SERVICE_HASH
4. Деплоим!
В моем пайплайне следующие особенности:

— Деплой в прод только из ветки main, так мы гарантируем для себя, что в этой ветке только рабочий код
— Деплой в дев из любой ветки. Удобно для гибкой работы в команде
— Для уведомлений используется Shoutrrr, который отправляет сообщения в телеграмм в любом случае.
Все готово, после успешного деплоя вы получите следующее сообщение в телеграмме:
5. Особенности и подводные камни
-
Webhook только перезапускает сервис, он не пересоздаёт его. Если образ не обновился, значит перезапуск будет бесполезным
-
Портейнер возвращает 204, говоря о том, что запустил процесс обновления сервиса. Обновился он или нет — никто не знает
-
В Stack’e обязательно приходится указывать общий тег, без конкретной версии. Пока не нашел способа обойти это
-
Откаты и rolling update надо настраивать самостоятельно в файле стека.
-
Один webhook = одна команда обновления. Нельзя передавать параметры или обновлять сразу все (сразу Stack можно обновить только в Enterprise портейнере 🙂 )
А что дальше?
Далее мы с командой подняли свой registry, его также можно прямо в portainer’e добавить. Пушим и тянем образы туда.
Как накрутить логи/метрики и все прочие сервисы — вы сможете разобраться самостоятельно. Этот процесс особо не отличается от настройки в докере в одном docker-compose.
Заключение
Таким образом мы получили удобный способ автоматизировать деплой без лишней головной боли: сервисы обновляются по команде, без SSH и ручного docker service update.
Если статья вам понравилась или заставила задуматься — обязательно пишите комментарии. Мы открыты к любой конструктивной критике и нам всегда интересно узнать советы коллег с опытом!
ссылка на оригинал статьи https://habr.com/ru/articles/938836/
Добавить комментарий