Вернуться к блогу
8 июня 2025
20 мин

Продвинутые стратегии CI/CD: от Blue-Green до GitOps

CI/CD
GitOps
Deployment Strategies

Редакция DevBobs · Дата публикации: 8 июня 2025

Современные CI/CD системы выходят далеко за рамки простой автоматизации тестирования и деплоя. В этой статье разберем продвинутые стратегии, которые используют ведущие технологические компании для обеспечения надежных и быстрых релизов.

Эволюция стратегий деплоя

От простых rolling updates до сложных многоэтапных стратегий — путь к zero-downtime deployments и минимизации рисков при релизах новых версий.

Сравнение стратегий:

  • Rolling Update — постепенная замена экземпляров
  • Blue-Green — переключение между двумя средами
  • Canary — постепенное переключение трафика
  • A/B Testing — тестирование на части пользователей
  • Feature Flags — управление функциональностью в runtime

Blue-Green Deployment

Blue-Green deployment — это стратегия, при которой поддерживаются два идентичных production окружения. Одно активное (Blue), другое ждет (Green).

Преимущества:

  • Мгновенный откат в случае проблем
  • Zero-downtime deployment
  • Возможность полного тестирования перед переключением
  • Простота реализации

Реализация с GitLab CI:

# .gitlab-ci.yml
stages:
  - build
  - test
  - deploy-green
  - smoke-test
  - switch-traffic
  - cleanup

variables:
  BLUE_ENVIRONMENT: "production-blue"
  GREEN_ENVIRONMENT: "production-green"

deploy-green:
  stage: deploy-green
  script:
    - kubectl apply -f k8s/green-deployment.yaml
    - kubectl rollout status deployment/app-green
    - kubectl apply -f k8s/green-service.yaml
  environment:
    name: production-green
    url: https://green.myapp.com

smoke-test:
  stage: smoke-test
  script:
    - curl -f https://green.myapp.com/health
    - npm run e2e-tests -- --base-url=https://green.myapp.com
  retry: 3

switch-traffic:
  stage: switch-traffic
  script:
    - kubectl patch service app-production -p '{"spec":{"selector":{"version":"green"}}}'
    - kubectl patch ingress app-ingress -p '{"spec":{"rules":[{"host":"myapp.com","http":{"paths":[{"path":"/","backend":{"serviceName":"app-green","servicePort":80}}]}}]}}'
  when: manual
  environment:
    name: production
    url: https://myapp.com

cleanup-blue:
  stage: cleanup
  script:
    - kubectl delete deployment app-blue
    - kubectl delete service app-blue
  when: manual

Kubernetes манифесты:

# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-green
  labels:
    app: myapp
    version: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      version: green
  template:
    metadata:
      labels:
        app: myapp
        version: green
    spec:
      containers:
      - name: app
        image: myapp:${CI_COMMIT_SHA}
        ports:
        - containerPort: 8080
        env:
        - name: ENVIRONMENT
          value: "production"
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10

---
apiVersion: v1
kind: Service
metadata:
  name: app-green
spec:
  selector:
    app: myapp
    version: green
  ports:
  - port: 80
    targetPort: 8080

Canary Deployments

Canary deployment позволяет постепенно перенаправлять трафик на новую версию, мониторя метрики и откатываясь при необходимости.

Реализация с Istio:

# canary-virtual-service.yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: app-canary
spec:
  http:
  - match:
    - headers:
        canary:
          exact: "true"
    route:
    - destination:
        host: app-service
        subset: v2
  - route:
    - destination:
        host: app-service
        subset: v1
      weight: 90
    - destination:
        host: app-service
        subset: v2
      weight: 10

---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: app-destination
spec:
  host: app-service
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

Автоматизация с Flagger:

# flagger-canary.yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: app-canary
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: app
  service:
    port: 80
    targetPort: 8080
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
    - name: request-success-rate
      threshold: 99
      interval: 1m
    - name: request-duration
      threshold: 500
      interval: 30s
    webhooks:
    - name: load-test
      url: http://flagger-loadtester.test/
      metadata:
        cmd: "hey -z 1m -q 10 -c 2 http://app.production/"

GitOps подход

GitOps — методология, при которой желаемое состояние системы декларативно описано в Git репозитории, а специальные агенты обеспечивают синхронизацию.

Принципы GitOps:

  • Декларативное описание инфраструктуры
  • Версионирование в Git
  • Автоматическое применение изменений
  • Непрерывная сверка и коррекция

ArgoCD Configuration:

# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: webapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/company/webapp-config
    targetRevision: HEAD
    path: kubernetes/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
    - PrunePropagationPolicy=foreground
    - PruneLast=true
  revisionHistoryLimit: 10

Multi-environment GitOps:

# Directory structure
gitops-config/
├── apps/
│   ├── webapp/
│   │   ├── base/
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   ├── overlays/
│   │   │   ├── development/
│   │   │   │   ├── kustomization.yaml
│   │   │   │   └── patches.yaml
│   │   │   ├── staging/
│   │   │   │   ├── kustomization.yaml
│   │   │   │   └── patches.yaml
│   │   │   └── production/
│   │   │       ├── kustomization.yaml
│   │   │       └── patches.yaml
└── clusters/
    ├── development/
    ├── staging/
    └── production/

Feature Flags и Progressive Delivery

Feature flags позволяют разделить деплой кода и активацию функциональности, обеспечивая гибкость в управлении релизами.

Интеграция с приложением:

// feature-flags.js
class FeatureFlagService {
  constructor(configUrl) {
    this.configUrl = configUrl;
    this.flags = new Map();
    this.refreshInterval = 30000; // 30 seconds
    this.init();
  }

  async init() {
    await this.refresh();
    setInterval(() => this.refresh(), this.refreshInterval);
  }

  async refresh() {
    try {
      const response = await fetch(this.configUrl);
      const config = await response.json();
      
      config.flags.forEach(flag => {
        this.flags.set(flag.name, {
          enabled: flag.enabled,
          rules: flag.rules || [],
          rolloutPercentage: flag.rolloutPercentage || 0
        });
      });
    } catch (error) {
      console.error('Failed to refresh feature flags:', error);
    }
  }

  isEnabled(flagName, context = {}) {
    const flag = this.flags.get(flagName);
    if (!flag) return false;

    // Check rules first
    if (flag.rules.length > 0) {
      return this.evaluateRules(flag.rules, context);
    }

    // Check rollout percentage
    if (flag.rolloutPercentage > 0) {
      const hash = this.hashString(context.userId || 'anonymous');
      return (hash % 100) < flag.rolloutPercentage;
    }

    return flag.enabled;
  }

  evaluateRules(rules, context) {
    return rules.some(rule => {
      switch (rule.type) {
        case 'user_id':
          return rule.values.includes(context.userId);
        case 'country':
          return rule.values.includes(context.country);
        case 'plan':
          return rule.values.includes(context.plan);
        default:
          return false;
      }
    });
  }

  hashString(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32-bit integer
    }
    return Math.abs(hash);
  }
}

// Usage
const featureFlags = new FeatureFlagService('/api/feature-flags');

// In your component
if (featureFlags.isEnabled('new_checkout_flow', { 
  userId: user.id, 
  country: user.country 
})) {
  renderNewCheckoutFlow();
} else {
  renderOldCheckoutFlow();
}

Многоступенчатые пайплайны

Enterprise проекты требуют сложных пайплайнов с множественными этапами валидации, approval процессами и интеграцией с внешними системами.

GitHub Actions Enterprise Pipeline:

# .github/workflows/enterprise-pipeline.yml
name: Enterprise CI/CD Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        format: 'sarif'
        output: 'trivy-results.sarif'
    - name: Upload Trivy scan results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

  quality-gate:
    runs-on: ubuntu-latest
    needs: [security-scan]
    steps:
    - uses: actions/checkout@v3
    - name: SonarCloud Scan
      uses: SonarSource/sonarcloud-github-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    - name: Quality Gate check
      uses: sonarqube-quality-gate-action@master
      timeout-minutes: 5
      env:
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

  build-and-test:
    runs-on: ubuntu-latest
    needs: [quality-gate]
    strategy:
      matrix:
        node-version: [16, 18, 20]
    steps:
    - uses: actions/checkout@v3
    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build
    - run: npm test -- --coverage
    - name: Upload coverage reports
      uses: codecov/codecov-action@v3

  integration-tests:
    runs-on: ubuntu-latest
    needs: [build-and-test]
    services:
      postgres:
        image: postgres:14
        env:
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
    - uses: actions/checkout@v3
    - name: Setup test environment
      run: |
        docker-compose -f docker-compose.test.yml up -d
        sleep 30
    - name: Run integration tests
      run: npm run test:integration
    - name: Cleanup
      run: docker-compose -f docker-compose.test.yml down

  performance-test:
    runs-on: ubuntu-latest
    needs: [integration-tests]
    if: github.ref == 'refs/heads/main'
    steps:
    - uses: actions/checkout@v3
    - name: Run performance tests
      run: |
        docker run --rm -v ${{ github.workspace }}:/workspace           -w /workspace loadimpact/k6 run scripts/performance-test.js

  deploy-staging:
    runs-on: ubuntu-latest
    needs: [performance-test]
    if: github.ref == 'refs/heads/main'
    environment:
      name: staging
      url: https://staging.myapp.com
    steps:
    - name: Deploy to staging
      run: |
        helm upgrade --install myapp ./helm-chart           --namespace staging           --set image.tag=${{ github.sha }}           --set environment=staging

  smoke-tests-staging:
    runs-on: ubuntu-latest
    needs: [deploy-staging]
    steps:
    - name: Run smoke tests
      run: |
        curl -f https://staging.myapp.com/health
        npm run test:smoke -- --base-url=https://staging.myapp.com

  approval-gate:
    runs-on: ubuntu-latest
    needs: [smoke-tests-staging]
    environment:
      name: production-approval
    steps:
    - name: Request approval
      run: echo "Waiting for manual approval..."

  deploy-production:
    runs-on: ubuntu-latest
    needs: [approval-gate]
    environment:
      name: production
      url: https://myapp.com
    steps:
    - name: Blue-Green deployment
      run: |
        helm upgrade --install myapp-green ./helm-chart           --namespace production           --set image.tag=${{ github.sha }}           --set environment=production           --set variant=green
    - name: Health check
      run: |
        kubectl wait --for=condition=ready pod -l app=myapp,variant=green -n production --timeout=300s
    - name: Switch traffic
      run: |
        kubectl patch service myapp -n production -p '{"spec":{"selector":{"variant":"green"}}}'
    - name: Cleanup old version
      run: |
        sleep 300  # Wait 5 minutes for monitoring
        helm uninstall myapp-blue --namespace production || true

Мониторинг и наблюдаемость в CI/CD

Deployment метрики:

  • Deployment frequency
  • Lead time for changes
  • Mean time to recovery (MTTR)
  • Change failure rate

Интеграция с мониторингом:

# deployment-tracking.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: deployment-webhook
data:
  webhook.sh: |
    #!/bin/bash
    
    # Send deployment event to monitoring
    curl -X POST "https://api.datadog.com/api/v1/events"       -H "Content-Type: application/json"       -H "DD-API-KEY: ${DATADOG_API_KEY}"       -d '{
        "title": "Deployment: '$APP_NAME' v'$VERSION'",
        "text": "Deployed '$APP_NAME' version '$VERSION' to '$ENVIRONMENT'",
        "priority": "normal",
        "tags": ["deployment", "app:'$APP_NAME'", "env:'$ENVIRONMENT'"],
        "alert_type": "info"
      }'
    
    # Create Grafana annotation
    curl -X POST "https://grafana.company.com/api/annotations"       -H "Content-Type: application/json"       -H "Authorization: Bearer ${GRAFANA_TOKEN}"       -d '{
        "dashboardId": 1,
        "time": '$(date +%s000)',
        "timeEnd": '$(date +%s000)',
        "tags": ["deployment"],
        "text": "Deployed '$APP_NAME' v'$VERSION' to '$ENVIRONMENT'"
      }'

Заключение

Продвинутые CI/CD стратегии требуют зрелых команд и процессов, но взамен дают высокую скорость доставки изменений при минимальных рисках. Начните с простых стратегий и постепенно внедряйте более сложные по мере роста команды и требований.

Ключ к успеху — это правильное сочетание автоматизации, мониторинга и человеческого контроля в критических точках процесса. Помните: лучший CI/CD — тот, который работает надежно и прозрачно для всей команды.

Нужна помощь с DevOps в вашем проекте?

Наши эксперты готовы помочь настроить CI/CD, оптимизировать инфраструктуру и автоматизировать процессы

Наши услуги