CI/CD в 2026: Полное руководство по автоматизации развертывания приложений в production | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

CI/CD в 2026: Полное руководство по автоматизации развертывания приложений в production

17 апреля 2026 12 мин. чтения
Содержание статьи

Автоматизация доставки кода в 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. Алгоритм реализации:

  1. Развертывание «зеленой» версии. Создайте новый Deployment с уникальными лейблами (например, version: green). Убедитесь, что Pod'ы перешли в состояние Ready.
  2. Настройка Service. Service должен селектить Pod'ы по основному лейблу приложения (например, app: my-app), который есть у обоих Deployment'ов. Изначально он направляет трафик на «синие» Pod'ы.
  3. Переключение трафика. Обновите селектор Service, чтобы он выбирал Pod'ы с лейблом version: green. Это можно сделать командой:
kubectl patch service my-app-svc -p '{"spec":{"selector":{"version":"green"}}}'
  1. Верификация. Проверьте метрики и логи «зеленого» окружения. Если все стабильно, можно удалить старый «синий» Deployment.
  2. Откат. В случае проблем просто верните селектор Service обратно на version: blue.

Этот подход обеспечивает мгновенный откат, но требует внимательного управления ресурсами.

Настройка Canary-релизов с Flagger и метриками Prometheus

Для автоматизации Canary-развертываний рекомендуется использовать специализированные инструменты, такие как Flagger. Он работает поверх Service Mesh (Istio, Linkerd) или Ingress-контроллеров (Nginx, Gloo) и автоматически управляет весом трафика между старой и новой версиями на основе метрик из Prometheus.

Базовая настройка Flagger для Deployment в Kubernetes:

  1. Установите Flagger в кластер. Например, с помощью Helm:
helm repo add flagger https://flagger.app
helm upgrade -i flagger flagger/flagger --namespace=istio-system --set meshProvider=istio
  1. Создайте 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/"
  1. Запустите обновление. Обновите образ в исходном 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

  1. Отсутствие механизма автоматического отката. Надейтесь на автоматику, а не на ручное вмешательство. Внедрите откат по метрикам (как в Flagger) или по таймауту health checks.
  2. Использование тега latest для образов в production. Это приводит к непредсказуемым развертываниям. Всегда используйте уникальные теги, основанные на хэше коммита или семантической версии.
  3. Неадекватные requests/limits для Pod в Kubernetes. Не заданные лимиты ведут к contention ресурсов и падению нод. Всегда указывайте resources.requests и resources.limits для CPU и памяти.
  4. Хранение секретов (пароли, токены, kubeconfig) в репозитории. Используйте встроенные механизмы переменных окружения в CI-системах (GitLab CI Variables, GitHub Secrets, Jenkins Credentials) или внешние хранилища вроде HashiCorp Vault.
  5. Игнорирование failed-статусов заданий в пайплайне. Настройте политику так, чтобы пайплайн останавливался при любом сбое на этапах сборки, тестирования или анализа безопасности. Не используйте allow_failure: true для критичных этапов.

Как адаптировать примеры из статьи: версии, провайдеры, окружения

Чтобы примеры заработали в вашем окружении, выполните пошаговую адаптацию:

  1. Замена образов и адресов registry. В конфигурациях замените registry.example.com, ghcr.io/${{ github.repository }} и имена образов (nginx) на актуальные для вашего проекта.
  2. Настройка аутентификации в Kubernetes. Вместо статического kubeconfig в переменной используйте механизмы, предоставляемые вашим облачным провайдером (например, service account для GKE, IAM роль для EKS) или настройте OIDC-интеграцию между CI-системой и кластером.
  3. Учет особенностей облачного провайдера. Тип Service (LoadBalancer) или параметры Ingress могут отличаться. Сверьтесь с документацией вашего провайдера (AWS EKS, GCP GKE, Яндекс.Облако, Selectel).
  4. Работа с несколькими окружениями. Используйте переменные CI/CD для управления контекстами развертывания (stage, prod). Например, в GitLab CI можно определить переменные KUBE_NAMESPACE и KUBE_CONTEXT для каждого окружения.
  5. Проверка версий софта. Убедитесь, что версии kubectl, Helm, Docker и утилит безопасности (Trivy) в образах CI-агентов совместимы с вашим кластером и registry.

Для комплексного внедрения автоматизированных и безопасных практик доставки кода рекомендуем изучить практическое руководство по внедрению GitOps в 2026.

Поделиться:
Сохранить гайд? В закладки браузера