Автоматизация доставки кода в production — критический навык для DevOps-инженера. В 2026 году CI/CD-пайплайн должен быть не просто быстрым, а надежным, безопасным и адаптивным. Это руководство предоставляет готовые, проверенные на практике конфигурации для GitLab CI, GitHub Actions и Jenkins, а также детальный разбор стратегий развертывания (Blue-Green, Canary, Rolling Update) в Kubernetes. Вы получите конкретные примеры файлов, которые можно скопировать и адаптировать под свой проект, и узнаете, как встроить в процесс автоматический откат, мониторинг и проверки безопасности.
Мы фокусируемся на практической реализации: от сборки Docker-образа до управления трафиком в кластере. Все примеры основаны на реальном опыте настройки production-сред и учитывают современные тренды, такие как управление инфраструктурой как код (IaC) с помощью Pulumi. Это позволит вам создать отказоустойчивый конвейер, который минимизирует риски и время простоя при обновлениях.
Готовые конфигурации CI/CD для Kubernetes: от кода до контейнера в 2026
Чтобы сэкономить часы на поиск и отладку, начнем с готовых к использованию конфигураций для популярных инструментов. Эти файлы прошли проверку в рабочих средах и служат надежной отправной точкой для автоматизации сборки, тестирования и развертывания контейнеризированных приложений в Kubernetes.
GitLab CI: .gitlab-ci.yml для сборки Docker-образа и деплоя в K8s
Приведенный ниже файл .gitlab-ci.yml реализует полный пайплайн: проверку кода, сборку образа с кэшированием слоев, публикацию в registry и развертывание в кластер Kubernetes. Ключевые директивы прокомментированы для понимания.
stages:
- lint
- test
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# Используем Docker-in-Docker для сборки образов
docker-build:
stage: build
image: docker:24.0
services:
- docker:24.0-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $DOCKER_IMAGE -t $CI_REGISTRY_IMAGE:latest .
- docker push $DOCKER_IMAGE
- docker push $CI_REGISTRY_IMAGE:latest
# Кэшируем слои Docker для ускорения последующих сборок
cache:
key: docker-layer-cache
paths:
- /cache/docker
only:
- main
- merge_requests
kubernetes-deploy:
stage: deploy
image: bitnami/kubectl:1.28
script:
# Настройка доступа к кластеру через kubeconfig из переменной
- echo "$KUBE_CONFIG" > /tmp/kubeconfig
- export KUBECONFIG=/tmp/kubeconfig
# Обновление образа в манифесте Deployment
- kubectl set image deployment/my-app app=$DOCKER_IMAGE --record
- kubectl rollout status deployment/my-app --timeout=300s
only:
- main
Обратите внимание на использование CI_COMMIT_SHORT_SHA для тегирования образа — это гарантирует уникальность и прослеживаемость. Для продакшена рекомендуется отказаться от тега latest и использовать семантическое версионирование.
GitHub Actions: Workflow для автоматического развертывания с Helm
GitHub Actions позволяет гибко настроить пайплайн с использованием Helm — стандартного менеджера пакетов для Kubernetes. В этом примере workflow собирает образ, публикует его в GitHub Container Registry и обновляет релиз Helm в кластере.
name: Deploy to Kubernetes
on:
push:
branches: [ main ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:latest
cache-to: type=inline
- name: Set up Helm
uses: azure/setup-helm@v3
with:
version: '3.13'
- name: Deploy to Kubernetes with Helm
uses: azure/k8s-deploy@v4
with:
namespace: 'production'
manifests: |
./helm/charts/my-app
images: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
kubectl-version: 'latest'
Использование secrets.GITHUB_TOKEN для аутентификации в registry безопасно и не требует дополнительной настройки. Для работы с приватными кластерами необходимо добавить секрет с kubeconfig в настройках репозитория.
Jenkins Pipeline: Declarative Pipeline (Jenkinsfile) с агентами в Docker
Для команд, использующих Jenkins, declarative pipeline обеспечивает структурированность и удобство поддержки. В этом примере каждый этап выполняется в изолированном Docker-контейнере, что гарантирует воспроизводимость сборок.
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
IMAGE_TAG = "${env.DOCKER_REGISTRY}/my-app:${env.BUILD_ID}"
}
stages {
stage('Build') {
agent {
docker {
image 'docker:24.0'
args '-v /var/run/docker.sock:/var/run/docker.sock'
}
}
steps {
sh 'docker build -t ${IMAGE_TAG} .'
}
}
stage('Test') {
agent {
docker { image 'node:20-alpine' }
}
steps {
sh 'npm ci'
sh 'npm test'
}
}
stage('Deploy to Kubernetes') {
agent {
docker { image 'bitnami/kubectl:1.28' }
}
steps {
withCredentials([file(credentialsId: 'kubeconfig-prod', variable: 'KUBECONFIG_FILE')]) {
sh '''
export KUBECONFIG=${KUBECONFIG_FILE}
kubectl set image deployment/my-app app=${IMAGE_TAG} --record
kubectl rollout status deployment/my-app --timeout=5m
'''
}
}
}
}
}
Declarative синтаксис делает pipeline более читаемым и менее подверженным ошибкам по сравнению со scripted pipeline. Хранение учетных данных для доступа к кластеру осуществляется через встроенный в Jenkins механизм withCredentials.
Современный тренд: Управление Kubernetes как кодом с Pulumi (вместо YAML)
В 2026 году управление инфраструктурой через статические YAML-файлы уступает место подходу Infrastructure as Code (IaC) с использованием языков программирования. Pulumi позволяет определять ресурсы Kubernetes на JavaScript, TypeScript, Python или Go, что дает преимущества: проверку типов, модульность, возможность использования циклов и условий прямо в коде инфраструктуры.
Пример развертывания простого приложения Nginx с помощью Pulumi на JavaScript:
const k8s = require("@pulumi/kubernetes");
// Создание Namespace
const appNamespace = new k8s.core.v1.Namespace("my-app-ns", {
metadata: { name: "my-app-production" }
});
// Создание Deployment
const appDeployment = new k8s.apps.v1.Deployment("my-app", {
metadata: {
namespace: appNamespace.metadata.name,
labels: { app: "my-app" }
},
spec: {
replicas: 3,
selector: { matchLabels: { app: "my-app" } },
template: {
metadata: { labels: { app: "my-app" } },
spec: {
containers: [{
name: "my-app",
image: "nginx:1.25-alpine", // В реальном сценарии подставляется образ из CI
ports: [{ containerPort: 80 }],
resources: {
requests: { cpu: "100m", memory: "128Mi" },
limits: { cpu: "200m", memory: "256Mi" }
}
}]
}
}
}
}, { dependsOn: appNamespace });
// Создание Service для доступа к Pod'ам
const appService = new k8s.core.v1.Service("my-app-service", {
metadata: {
namespace: appNamespace.metadata.name,
labels: { app: "my-app" }
},
spec: {
type: "ClusterIP",
selector: appDeployment.spec.template.metadata.labels,
ports: [{ port: 80, targetPort: 80 }]
}
}, { dependsOn: appDeployment });
// Экспорт имени сервиса для удобства
module.exports = {
serviceName: appService.metadata.name,
namespace: appNamespace.metadata.name
};
Pulumi также упрощает работу с Custom Resource Definitions (CRD), позволяя управлять ими так же, как и нативными ресурсами Kubernetes. Этот код можно запустить как отдельный этап в любом CI-пайплайне после успешной сборки образа, что создает целостный цикл «код приложения — код инфраструктуры».
Для более глубокого понимания различий между подходами к автоматизации инфраструктуры, рекомендуем ознакомиться с практическим сравнением GitOps и Infrastructure as Code (IaC).
Стратегии развертывания в production: Blue-Green, Canary и Rolling Update
Выбор стратегии развертывания напрямую влияет на доступность сервиса и риск при обновлениях. В Kubernetes доступны несколько подходов, каждый со своей областью применения. Ниже приведено их сравнение, а затем — пошаговые инструкции по реализации.
Сравнение стратегий: какую выбрать для вашего приложения?
Критерии выбора зависят от требований к доступности (downtime), толерантности к риску, сложности инфраструктуры и необходимой скорости отката.
| Стратегия | Время простоя | Контроль риска | Сложность реализации | Затраты ресурсов | Идеальный сценарий |
|---|---|---|---|---|---|
| Rolling Update (нативная для K8s) | Минимальный, но пользователи могут столкнуться с ошибками во время обновления | Низкий. Проблема затронет часть трафика сразу. | Низкая. Управляется полями maxUnavailable и maxSurge в Deployment. | Минимальные. Не требует дополнительных ресурсов. | Стабильные приложения с хорошим тестовым покрытием, где кратковременные ошибки допустимы. |
| Blue-Green | Практически нулевой. Переключение трафика происходит мгновенно. | Высокий. Проблемы видны только после переключения всего трафика, но откат так же быстр. | Средняя. Требует управления двумя полными копиями окружения и механизмом переключения трафика (Service/Ingress). | Высокие. Необходим двойной набор ресурсов (Pods, Services) на время развертывания. | Критичные сервисы, где downtime недопустим, а бюджет позволяет содержать два окружения. |
| Canary | Нулевой для основной массы пользователей. | Очень высокий. Позволяет выявить проблемы на небольшом проценте трафика до полного релиза. | Высокая. Требует настройки инструментов для управления трафиком (Flagger, Istio, Nginx Ingress) и продвинутого мониторинга. | Средние. Требует дополнительных ресурсов для Canary-подов и инфраструктуры управления трафиком. | Сложные распределенные системы, где необходимо минимизировать влияние потенциальных багов на всех пользователей. |
Для управления базовыми развертываниями в Kubernetes необходимо понимать объект Deployment. Подробнее об этом читайте в полном руководстве по Kubernetes Deployment.
Пошаговая реализация Blue-Green развертывания в Kubernetes
Blue-Green стратегия предполагает наличие двух идентичных окружений: «синего» (активная версия) и «зеленого» (новая версия). Трафик направляется на одно из них через общий Service. Алгоритм реализации:
- Развертывание «зеленой» версии. Создайте новый Deployment с уникальными лейблами (например,
version: green). Убедитесь, что Pod'ы перешли в состояние Ready. - Настройка Service. Service должен селектить Pod'ы по основному лейблу приложения (например,
app: my-app), который есть у обоих Deployment'ов. Изначально он направляет трафик на «синие» Pod'ы. - Переключение трафика. Обновите селектор Service, чтобы он выбирал Pod'ы с лейблом
version: green. Это можно сделать командой:
kubectl patch service my-app-svc -p '{"spec":{"selector":{"version":"green"}}}'- Верификация. Проверьте метрики и логи «зеленого» окружения. Если все стабильно, можно удалить старый «синий» Deployment.
- Откат. В случае проблем просто верните селектор Service обратно на
version: blue.
Этот подход обеспечивает мгновенный откат, но требует внимательного управления ресурсами.
Настройка Canary-релизов с Flagger и метриками Prometheus
Для автоматизации Canary-развертываний рекомендуется использовать специализированные инструменты, такие как Flagger. Он работает поверх Service Mesh (Istio, Linkerd) или Ingress-контроллеров (Nginx, Gloo) и автоматически управляет весом трафика между старой и новой версиями на основе метрик из Prometheus.
Базовая настройка Flagger для Deployment в Kubernetes:
- Установите Flagger в кластер. Например, с помощью Helm:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger --namespace=istio-system --set meshProvider=istio- Создайте Canary-ресурс. Этот CRD (Custom Resource Definition) описывает процесс развертывания:
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: my-app
namespace: production
spec:
# Целевой Deployment, которым будет управлять Flagger
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
service:
port: 9898
analysis:
interval: 1m # Частота проверки метрик
threshold: 5 # Количество проверок перед продвижением
maxWeight: 50 # Максимальный процент трафика на canary
stepWeight: 10 # Шаг увеличения трафика
metrics:
- name: request-success-rate
thresholdRange:
min: 99 # Минимальный процент успешных HTTP-запросов
interval: 1m
- name: request-duration
thresholdRange:
max: 500 # Максимальная задержка в миллисекундах
interval: 30s
webhooks:
- name: load-test
url: http://flagger-loadtester.production/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://my-app.production/"- Запустите обновление. Обновите образ в исходном Deployment (например, через
kubectl set image). Flagger обнаружит изменение, создаст canary-деплоймент и начнет постепенно перенаправлять на него трафик, анализируя метрики успешности запросов и задержки. Если метрики ухудшатся, Flagger автоматически инициирует откат.
Этот метод минимизирует человеческий фактор и позволяет безопасно тестировать новые версии в production. Более сложные схемы управления трафиком рассмотрены в статье о продвинутых стратегиях развертывания в Kubernetes.
Автоматический откат (Rollback): страховка от сбоев в production
Механизм отката должен быть неотъемлемой частью любой стратегии развертывания. Вот как он реализуется для каждого подхода:
- Rolling Update: Kubernetes хранит историю ревизий Deployment. Для отката выполните команду
kubectl rollout undo deployment/my-app. Вы можете настроить лимит истории с помощьюspec.revisionHistoryLimit. - Blue-Green: Откат — это быстрое переключение селектора Service обратно на предыдущую версию (например, с green на blue). Процесс занимает секунды.
- Canary (с Flagger): Откат происходит автоматически при нарушении пороговых значений метрик, заданных в analysis. Flagger вернет 100% трафика на стабильную версию.
Ключевой принцип: откат должен быть автоматизирован и запускаться по объективным критериям (метрики, результаты health checks), а не по решению человека после поступления жалоб от пользователей.
Безопасность и надежность: интеграция проверок и мониторинга в пайплайн
Современный CI/CD — это не только скорость, но и гарантия качества и безопасности поставляемого кода. Интеграция проверок на ранних этапах и мониторинга после развертывания значительно снижает риски.
Тестирование безопасности: SAST/DAST в CI на примере OWASP Juice Shop
Статический (SAST) и динамический (DAST) анализ безопасности должны быть частью CI-пайплайна. В качестве тестового стенда можно использовать OWASP Juice Shop — преднамеренно уязвимое веб-приложение, которое включает все уязвимости из OWASP Top 10.
Пример этапа SAST и DAST в GitLab CI:
sast-scan:
stage: test
image: aquasec/trivy:latest
script:
# Сканирование Docker-образа на уязвимости (SAST)
- trivy image --exit-code 1 --severity HIGH,CRITICAL $DOCKER_IMAGE
allow_failure: false # Пайплайн должен падать при критических уязвимостях
dast-test:
stage: test
image: docker:24.0
services:
- docker:24.0-dind
before_script:
- docker run -d --name juice-shop -p 3000:3000 bkimminich/juice-shop
- sleep 30 # Ждем запуска приложения
script:
# Запуск динамического сканера (например, OWASP ZAP в упрощенном режиме)
- docker run --network host -i owasp/zap2docker-stable zap-baseline.py -t http://localhost:3000 -I
after_script:
- docker stop juice-shop && docker rm juice-shop
Запуск Juice Shop в Docker (docker run --rm -p 127.0.0.1:3000:3000 bkimminich/juice-shop) позволяет безопасно тестировать инструменты безопасности, не затрагивая реальные среды. Результаты сканирования Trivy (SAST) и ZAP (DAST) необходимо анализировать и учитывать перед продвижением сборки в production.
Мониторинг развертывания: как отслеживать успешность деплоя в реальном времени
Развертывание считается успешным не тогда, когда Pod'ы запустились, а когда приложение стабильно обрабатывает пользовательский трафик. Для этого необходим мониторинг ключевых метрик сразу после деплоя.
Какие метрики отслеживать в первые 5-15 минут после деплоя:
- HTTP Error Rate: Процент HTTP-запросов с кодами 5xx и 4xx. Резкий рост — прямой индикатор проблемы.
- Latency (p95, p99): Задержка обработки запросов. Увеличение может указывать на проблемы с производительностью новой версии.
- Traffic Rate: Общее количество запросов в секунду. Резкое падение может означать, что нагрузочный балансер снял Pod'ы с обслуживания из-за failed health checks.
- Resource Usage: Потребление CPU и памяти новыми Pod'ами по сравнению с лимитами, заданными в
resources.limits.
Настройте в Grafana дашборд с этими метриками из Prometheus и добавьте алерты в Slack или Telegram, которые срабатывают при отклонении от baseline. В Kubernetes также используйте Readiness и Liveness Probes: Readiness Probe определяет, когда Pod готов принимать трафик, а Liveness Probe — жив ли контейнер. Их правильная настройка позволяет kubelet и Service Mesh автоматически изолировать нерабочие Pod'ы.
Принципы надежного развертывания контейнеров, включая health checks и мониторинг, подробно описаны в руководстве по Docker в production.
Типичные ошибки и адаптация решений под ваш кейс
Даже с готовыми конфигурациями можно столкнуться с проблемами из-за особенностей окружения. Рассмотрим частые ошибки и способы адаптации примеров из статьи.
5 частых ошибок, которые ломают CI/CD-пайплайн в production
- Отсутствие механизма автоматического отката. Надейтесь на автоматику, а не на ручное вмешательство. Внедрите откат по метрикам (как в Flagger) или по таймауту health checks.
- Использование тега
latestдля образов в production. Это приводит к непредсказуемым развертываниям. Всегда используйте уникальные теги, основанные на хэше коммита или семантической версии. - Неадекватные requests/limits для Pod в Kubernetes. Не заданные лимиты ведут к contention ресурсов и падению нод. Всегда указывайте
resources.requestsиresources.limitsдля CPU и памяти. - Хранение секретов (пароли, токены, kubeconfig) в репозитории. Используйте встроенные механизмы переменных окружения в CI-системах (GitLab CI Variables, GitHub Secrets, Jenkins Credentials) или внешние хранилища вроде HashiCorp Vault.
- Игнорирование failed-статусов заданий в пайплайне. Настройте политику так, чтобы пайплайн останавливался при любом сбое на этапах сборки, тестирования или анализа безопасности. Не используйте
allow_failure: trueдля критичных этапов.
Как адаптировать примеры из статьи: версии, провайдеры, окружения
Чтобы примеры заработали в вашем окружении, выполните пошаговую адаптацию:
- Замена образов и адресов registry. В конфигурациях замените
registry.example.com,ghcr.io/${{ github.repository }}и имена образов (nginx) на актуальные для вашего проекта. - Настройка аутентификации в Kubernetes. Вместо статического kubeconfig в переменной используйте механизмы, предоставляемые вашим облачным провайдером (например, service account для GKE, IAM роль для EKS) или настройте OIDC-интеграцию между CI-системой и кластером.
- Учет особенностей облачного провайдера. Тип Service (
LoadBalancer) или параметры Ingress могут отличаться. Сверьтесь с документацией вашего провайдера (AWS EKS, GCP GKE, Яндекс.Облако, Selectel). - Работа с несколькими окружениями. Используйте переменные CI/CD для управления контекстами развертывания (stage, prod). Например, в GitLab CI можно определить переменные
KUBE_NAMESPACEиKUBE_CONTEXTдля каждого окружения. - Проверка версий софта. Убедитесь, что версии
kubectl, Helm, Docker и утилит безопасности (Trivy) в образах CI-агентов совместимы с вашим кластером и registry.
Для комплексного внедрения автоматизированных и безопасных практик доставки кода рекомендуем изучить практическое руководство по внедрению GitOps в 2026.