Skip to content

CI / CD

Работал с доставкой проектов от простого ручного деплоя через ssh/rsync до более управляемой схемы с GitLab CI, Docker-образами и Deployer.

На практике использовал схему: CI выполняет проверки и сборку артефактов/образов, а production-deploy запускается отдельно и контролируемо.

Уровни опыта CI/CD

  1. Ручной деплой (ssh, rsync, bash-скрипты)
  2. Deployer как слой CD (release-based deploy, rollback, повторяемые задачи)
  3. GitLab CI + Docker Registry + ручной запуск deploy в production

GitLab CI (pipeline, проверки, сборка образов)

Настраивал GitLab CI pipeline с разделением по стадиям (lint, test, build) и правилами запуска через workflow/rules.

Типичный pipeline для production-ветки включает:

  • lint:
    • проверка форматирования кода;
    • запуск линтеров для backend- и frontend-части;
    • typecheck для клиентского кода;
    • dependency audit для composer и npm;
  • test:
    • запуск автотестов приложения;
    • проверка интеграционных сценариев с реальными сервисами;
    • поднятие PostgreSQL и Redis как CI services;
    • ожидание готовности инфраструктуры перед стартом тестов;
  • build:
    • сборка production-образов приложения и nginx;
    • тегирование образов immutable-тегами и служебными floating-тегами;
    • push готовых образов в Docker Registry;
    • публикация тегов и метаданных сборки для следующего шага deploy.

GitLab Runner на VPS (установка и настройка)

Поднимал self-hosted gitlab-runner на VPS под проектный pipeline:

  • установка gitlab-runner и регистрация runner в проект GitLab;
  • настройка runner под Docker-джобы (образы CI_PHP_IMAGE, node, сервисы postgres/redis);
  • привязка по тегу recipes-deploy для целевого запуска pipeline;
  • настройка доступа к Docker на VPS (включая работу со сборкой и push образов в GitLab Container Registry).

В результате pipeline для ветки production выполняется на своём раннере предсказуемо и без ручного вмешательства.

Кастомный CI runtime image (CI_PHP_IMAGE)

Использую отдельный CI-образ для PHP-задач (CI_PHP_IMAGE), чтобы:

  • сократить время подготовки джоб;
  • зафиксировать версию PHP и набор расширений;
  • приблизить CI-окружение к production-стеку.

Обычно собираю такой образ на базе Ubuntu/Debian и заранее добавляю:

  • нужную версию PHP;
  • необходимые расширения (pgsql, redis, intl и др.);
  • composer;
  • CLI-инструменты для работы с БД (postgresql-client и т.п.).

При изменении Dockerfile CI-образ пересобирается и пушится вручную (пример):

bash
docker build --no-cache -t <registry>/<group>/<project>/php-ci:<tag> -f docker/ci/php/Dockerfile.ci .
docker push <registry>/<group>/<project>/php-ci:<tag>

В подобных схемах полезно использовать отдельный базовый CI runtime image для PHP-джоб.

Docker Registry и production-образы

В production-потоке использую prebuilt Docker-образы вместо сборки на сервере.

Что реализовано:

  • сборка app-образа (php-fpm) и nginx-образа в CI;
  • публикация в GitLab Container Registry;
  • двойная стратегия тегов:
    • immutable-теги с commit sha;
    • плавающий latest;
  • ручной выбор версии deploy через runtime/env-конфиг:
    • образ приложения;
    • образ веб-сервера.

Такой подход упрощает rollback и делает deploy предсказуемым: сервер только pull-ит готовые образы.

На практике production-образы полезно разделять по ролям:

  • php-fpm runtime приложения;
  • nginx image со статикой и production-конфигом.

Deployer (release-based CD на VPS)

Использую Deployer как управляемый слой CD для ручного production-деплоя.

Типовые задачи, которые настраивал через Deployer:

  • deploy через shell-обертку над Deployer;
  • SSH precheck перед запуском deploy;
  • release-структура (releases / current / shared);
  • автоматическая загрузка локального production .env в shared/.env на VPS;
  • валидация обязательных production-ключей (секреты/ключи приложения);
  • загрузка runtime-файлов на VPS без git clone на сервере;
  • docker login в registry на VPS;
  • docker compose pull + docker compose up -d --remove-orphans;
  • post-deploy шаги:
    • миграции БД;
    • прогрев кэшей приложения;
    • подготовка симлинков/статических директорий;
    • перезапуск очередей/воркеров;
    • рестарт сервисов runtime (например, websocket/nginx);
    • healthcheck URL.

Дополнительно:

  • настройка и обновление cron-задачи для Let's Encrypt renew;
  • настройка ежедневного backup PostgreSQL в production:
    • запуск pg_dump -Fc через docker compose exec pgsql;
    • сжатие backup-артефактов (gzip);
    • хранение в shared/backups, чтобы backup не терялся при переключении release;
    • cleanup старых backup-файлов по retention policy;
    • установка/обновление cron-задачи через deploy-процесс;
  • подготовка shared-storage директорий и прав;
  • синхронизация WWWUSER / WWWGROUP под UID/GID deploy-пользователя.

Защитные проверки:

  • precheck текущей ветки перед production deploy;
  • SSH-проверку доступности сервера с retry;
  • валидацию deploy/env-конфига до запуска основных шагов.

Типовой flow деплоя

  1. Переключиться на ветку production, перенести изменения (обычно через rebase), выполнить push.
  2. Дождаться успешного pipeline в GitLab (lint, test, сборка production-образов).
  3. Запустить ручной deploy в production (через Deployer/shell-обертку).
bash
./deploy.sh deploy production

Баланс между автоматизацией и контролем: проверки и сборка выполняются в CI, а production-переключение запускается осознанно вручную.

Подготовка production-конфигов

Подготовка production-конфигурации под Docker-деплой:

  • Dockerfile для production-образов (app, nginx);
  • docker-compose.prod.yml;
  • nginx production-конфиг;
  • .env.production (runtime-переменные приложения);
  • .env.deploy (параметры deploy-процесса и доступа к VPS/registry).

Сайт обновлен и проверен: