Модуль: DevOps · Уровень: Middle+/Senior

TL;DR#

  • CI (Continuous Integration) — на каждый push/PR автоматически прогоняются lint, тесты, сборка. Цель: ловить интеграционные проблемы рано, держать main всегда зелёным.
  • CD — двусмысленно: Continuous Delivery (автоматически готовим релиз, кнопка деплоя ручная) vs Continuous Deployment (каждый зелёный коммит в main автоматически едет в prod). На senior-собеседовании важно разделять эти два понятия.
  • Типичные стадии Go-пайплайна: linttest (unit + race + coverage) → build (бинарь/Docker-образ) → scan (уязвимости) → publish (push в registry) → deploy.
  • Артефакт — иммутабельный результат сборки (Docker-образ по digest, бинарь, helm chart). Билдим один раз, продвигаем по окружениям (build once, deploy many). Не пересобираем для каждого env.
  • Версионирование: SemVer для библиотек/API, git SHA или git describe для сервисов. Образы тегируем и неизменяемым (sha-abc123), и плавающим (latest, v1.2) тегом.
  • Стратегии деплоя: rolling (по умолчанию в k8s), blue-green (мгновенное переключение + быстрый откат), canary (постепенный rollout на % трафика с метриками).

Теория#

Pipeline as Code#

Пайплайн описывается в репозитории (.github/workflows/*.yml, .gitlab-ci.yml, Jenkinsfile). Преимущества: версионируется вместе с кодом, review через PR, воспроизводимость. Конфиг в UI — антипаттерн (нет истории, нет review).

Стадии классического пайплайна#

СтадияЧто делаетИнструменты (Go)
lint / staticформат, vet, статанализgofmt, go vet, golangci-lint
testunit, race detector, coveragego test -race -cover
buildкомпиляция, Docker-образgo build, docker build, ko, buildx
security scanSAST, зависимости, образgovulncheck, gosec, trivy, grype
publishpush артефактаregistry (ECR/GCR/GHCR)
deployвыкатка в окружениеkubectl/helm/argocd/terraform
# минимальный CI-набор для Go
gofmt -l .                      # список неотформатированных файлов (должен быть пуст)
go vet ./...
golangci-lint run ./...
go test -race -coverprofile=cover.out ./...
go build -trimpath -ldflags "-s -w -X main.version=$(git describe --tags --always)" ./cmd/app
govulncheck ./...               # CVE в зависимостях и стандартной библиотеке

Артефакты и принцип “build once”#

Главное правило надёжного CD: собранный артефакт неизменен и продвигается по окружениям. Один и тот же Docker-образ (адресуемый по digest image@sha256:...) деплоится сначала в staging, потом ровно он же в prod. Пересборка под каждое окружение убивает воспроизводимость: «в staging работало, в prod нет» — потому что это два разных бинаря.

Конфигурация окружений выносится наружу (env vars, ConfigMap, Secrets), а не вшивается в образ. Это 12-factor: один артефакт, разная конфигурация.

# тег по неизменяемому digest, а не по плавающему latest
docker build -t ghcr.io/org/app:sha-$(git rev-parse --short HEAD) .
docker push ghcr.io/org/app:sha-$(git rev-parse --short HEAD)
# в deploy ссылаемся на digest, который вернул push:
# ghcr.io/org/app@sha256:9f2c...

Версионирование#

  • SemVer (MAJOR.MINOR.PATCH) — для публичных API и Go-модулей. MAJOR — несовместимые изменения, MINOR — обратносовместимые фичи, PATCH — фиксы. В Go модули с v2+ требуют суффикс в module path (module github.com/org/lib/v2).
  • Сервисы обычно версионируют git-тегами + git describe (v1.4.2-3-gabc123 = 3 коммита после тега v1.4.2). Версия вшивается в бинарь через -ldflags -X.
  • Образы: каждый build получает неизменяемый тег (git SHA) + опционально плавающие (latest, мажорный/минорный). Никогда не деплойте latest в prod — невозможно понять, что именно крутится, и нет детерминированного отката.
go build -ldflags "-X main.version=$(git describe --tags --always --dirty)" ./...

Стратегии деплоя#

Rolling update (дефолт в Kubernetes). Поды заменяются пачками: новый поднимается, проходит readiness, старый гасится. Параметры maxSurge / maxUnavailable.

  • Плюсы: без даунтайма, не нужны двойные ресурсы.
  • Минусы: во время выката одновременно живут обе версии (требует обратной совместимости API и схемы БД), откат не мгновенный.
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%        # сколько лишних подов сверх replicas
      maxUnavailable: 0    # 0 => без просадки ёмкости

Blue-Green. Две идентичные среды: blue (текущая) и green (новая версия). После прогрева и проверки green трафик переключается целиком (через изменение Service/LB/DNS).

  • Плюсы: мгновенный откат (вернуть указатель на blue), нет смешанных версий в момент переключения.
  • Минусы: 2x ресурсов, переключение БД/состояния — сложная часть (миграции должны быть совместимы с обеими версиями).

Canary. Новая версия получает малую долю трафика (1% → 5% → 25% → 100%), на каждом шаге смотрят метрики (error rate, latency p99, бизнес-метрики). При деградации — автоматический откат. Реализуется через mesh (Istio/Linkerd), ingress weights, или Argo Rollouts / Flagger.

  • Плюсы: минимальный blast radius, раннее обнаружение проблем на реальном трафике.
  • Минусы: сложность (нужны хорошие метрики и автоматизация анализа), дольше по времени.
# Argo Rollouts: canary со ступенями и автоанализом
apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
  strategy:
    canary:
      steps:
        - setWeight: 5
        - pause: { duration: 5m }
        - analysis:
            templates: [{ templateName: success-rate }]
        - setWeight: 25
        - pause: { duration: 10m }
        - setWeight: 100
СтратегияОткатРесурсыСмешанные версииКогда
Rollingмедленный1xдадефолт, стабильные релизы
Blue-Greenмгновенный2xнет (момент switch)критичные, нужен быстрый rollback
Canaryавто/быстрый~1.1xдавысокий трафик, нужна проверка на реальных юзерах

Связь с миграциями БД#

Любая стратегия без даунтайма требует, чтобы старая и новая версии кода работали с одной схемой БД одновременно. Отсюда паттерн expand/contract (parallel change): сначала добавляем новое (nullable-колонку), деплоим код, читающий и старое и новое, бэкфиллим данные, переключаем запись, и только потом отдельным релизом удаляем старое. Никогда не совмещайте несовместимую миграцию схемы с деплоем кода в одном шаге.

GitOps#

Декларативный CD: желаемое состояние кластера описано в git, контроллер (ArgoCD/Flux) непрерывно синхронизирует кластер с репозиторием. Деплой = merge в git. Плюсы: git как source of truth, аудит, лёгкий откат через revert, автокоррекция дрейфа.

Подводные камни / gotchas#

  • latest в prod — нет детерминированного состояния и отката. Всегда иммутабельный тег/digest.
  • Пересборка артефакта на каждый env — теряете «build once», получаете расхождение staging/prod.
  • Флаки-тесты в CI — убивают доверие к пайплайну, люди начинают перезапускать «на всякий случай» или мерджить красное. Изолировать/чинить, а не ретраить вслепую.
  • Несовместимая миграция БД при rolling/canary — старые поды ломаются на новой схеме. Нужен expand/contract.
  • Секреты в логах CIecho $TOKEN, или утечка через set -x. Маскируйте, используйте secret-стораджи.
  • Долгий пайплайн убивает скорость команды. Кэширование, параллелизм, разделение fast (lint+unit на PR) и slow (e2e, нагрузка на nightly).
  • Canary без хороших метрик бессмысленен: если нечем измерить деградацию, это просто медленный rolling.
  • Отсутствие защиты ветки — прямой push в main мимо CI. Required status checks + review обязательны.
  • Деплой = только обновление образа, но забыли про конфиг/секреты/feature flags — частичный rollout, который ведёт себя непредсказуемо.

Вопросы на собеседовании#

В: В чём разница между Continuous Delivery и Continuous Deployment? О: Delivery — пайплайн автоматически готовит релиз, пригодный к деплою, но выкатка в prod — ручное решение (кнопка/approval). Deployment — каждый коммит, прошедший проверки, автоматически едет в prod без участия человека. Deployment требует очень зрелого тестирования, мониторинга и фич-флагов.

В: Почему важно «build once, deploy many»? О: Чтобы в prod уезжал ровно тот артефакт, что проверен в staging. Пересборка под env вносит риск расхождения (разные версии зависимостей, флаги компиляции, время сборки). Конфигурацию выносим наружу, а образ остаётся неизменным и адресуется по digest.

В: Сравните blue-green и canary. Когда что выбрать? О: Blue-green — мгновенное переключение всего трафика и мгновенный откат, но 2x ресурсов и нет постепенной проверки. Canary — постепенный rollout на % трафика с анализом метрик, минимальный blast radius, но сложнее (нужны метрики и автоматизация). Blue-green для критичных релизов, где нужен быстрый rollback; canary для высоконагруженных сервисов, где важно проверить на реальном трафике.

В: Как делать деплой без даунтайма при изменении схемы БД? О: Expand/contract (parallel change): миграции обратносовместимы, схема меняется в несколько шагов так, чтобы старая и новая версии кода работали одновременно. Сначала добавить новое (nullable), задеплоить код, бэкфилл, переключить запись, и отдельным релизом удалить старое. Несовместимую миграцию нельзя совмещать с деплоем кода.

В: Как версионировать Docker-образы для сервиса? О: Неизменяемый тег по git SHA (sha-abc123) или git describe плюс ссылка по digest в деплое. Опционально плавающие теги (latest, минорный) для удобства, но в prod деплоим только иммутабельный. Версия дополнительно вшивается в бинарь через -ldflags -X main.version.

В: Что такое GitOps и чем он лучше push-деплоя? О: Желаемое состояние кластера хранится в git, контроллер (ArgoCD/Flux) непрерывно подтягивает и синхронизирует кластер. Деплой = merge. Плюсы: git как единый source of truth, полный аудит изменений, откат через revert, автокоррекция дрейфа, не нужен доступ CI к кластеру (pull-модель безопаснее).

В: Как ускорить медленный CI-пайплайн? О: Кэш зависимостей (go mod, build cache), параллелизация независимых job, разделение быстрых проверок (lint+unit на каждый PR) и медленных (e2e/нагрузка на merge или nightly), сборка Docker с layer-кэшем и buildx, запуск тестов только для изменённых пакетов где возможно, отказ от лишних шагов на PR.

В: Что такое flaky-тесты и почему они опасны в CI? О: Тесты, которые проходят/падают недетерминированно (гонки, таймауты, зависимость от порядка/времени/сети). Опасны тем, что разрушают доверие: команда привыкает перезапускать красное и в итоге мерджит реально сломанное. Лечение — изоляция, детерминизм (фиксированные часы, моки), -race, карантин с обязательным тикетом, а не слепой retry.

На что копают на senior+#

  • Проектирование пайплайна для монорепо/множества сервисов: что собирать инкрементально, как определять затронутые сервисы (path filters, affected graph).
  • Безопасность supply chain: подпись образов (cosign/sigstore), SBOM, провенанс (SLSA), сканирование зависимостей, защита от typosquatting в go.mod.
  • Стратегия откатов: автоматический rollback по метрикам, совместимость миграций, feature flags для decouple деплоя от релиза (деплой кода ≠ включение фичи).
  • Промоушн между окружениями и approval-гейты, environments с защитой, OIDC-федерация вместо долгоживущих ключей.
  • Метрики DORA (deployment frequency, lead time, MTTR, change failure rate) и как пайплайн на них влияет.
  • Эфемерные/preview-окружения на PR и их стоимость.