microservices

CI/CD процессы для микросервисов

Построение эффективных CI/CD пайплайнов для микросервисной архитектуры

#microservices #cicd #devops #automation #kubernetes

CI/CD процессы для микросервисов

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

Архитектура CI/CD для микросервисов

┌──────────────┐
│  Git Push    │
└──────┬───────┘

┌──────▼───────────────────────────────┐
│  CI Pipeline                         │
│  ┌────────┐  ┌────────┐  ┌────────┐ │
│  │ Build  │─►│ Test   │─►│ Scan   │ │
│  └────────┘  └────────┘  └────────┘ │
└──────┬───────────────────────────────┘

┌──────▼───────────────────────────────┐
│  CD Pipeline                         │
│  ┌────────┐  ┌────────┐  ┌────────┐ │
│  │ Deploy │─►│ Verify │─►│ Promote│ │
│  │  Dev   │  │        │  │  Prod  │ │
│  └────────┘  └────────┘  └────────┘ │
└──────────────────────────────────────┘

GitHub Actions Pipeline

.github/workflows/ci.yml

name: CI Pipeline

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

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run unit tests
        run: npm run test:unit
      
      - name: Run integration tests
        run: npm run test:integration
      
      - name: Generate coverage report
        run: npm run test:coverage
      
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/lcov.info

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Log in to Container Registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=sha,prefix={{branch}}-
      
      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          format: 'sarif'
          output: 'trivy-results.sarif'
      
      - name: Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

  deploy-dev:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'
    environment:
      name: development
      url: https://dev.example.com
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup kubectl
        uses: azure/setup-kubectl@v3
      
      - name: Configure kubeconfig
        run: |
          echo "${{ secrets.KUBECONFIG_DEV }}" | base64 -d > kubeconfig
          export KUBECONFIG=./kubeconfig
      
      - name: Deploy to Development
        run: |
          kubectl set image deployment/order-service \
            order-service=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:develop-${{ github.sha }} \
            -n development
          
          kubectl rollout status deployment/order-service -n development
      
      - name: Run smoke tests
        run: |
          npm run test:smoke -- --env=dev

  deploy-prod:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment:
      name: production
      url: https://example.com
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Helm
        uses: azure/setup-helm@v3
      
      - name: Deploy to Production
        run: |
          helm upgrade --install order-service ./helm/order-service \
            --namespace production \
            --set image.tag=${{ github.sha }} \
            --set replicaCount=5 \
            --wait \
            --timeout 10m
      
      - name: Verify deployment
        run: |
          kubectl wait --for=condition=available \
            deployment/order-service \
            -n production \
            --timeout=5m
      
      - name: Run production smoke tests
        run: npm run test:smoke -- --env=prod

GitLab CI/CD

.gitlab-ci.yml

stages:
  - test
  - build
  - deploy-dev
  - deploy-staging
  - deploy-prod

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA

.test-template: &test-template
  image: node:18
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
  before_script:
    - npm ci

unit-test:
  <<: *test-template
  stage: test
  script:
    - npm run test:unit
  coverage: '/Statements\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

integration-test:
  <<: *test-template
  stage: test
  services:
    - postgres:14
    - redis:7
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test_user
    POSTGRES_PASSWORD: test_pass
    DATABASE_URL: postgresql://test_user:test_pass@postgres:5432/test_db
    REDIS_URL: redis://redis:6379
  script:
    - npm run test:integration

lint:
  <<: *test-template
  stage: test
  script:
    - npm run lint
    - npm run type-check

build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $IMAGE_TAG .
    - docker push $IMAGE_TAG
    - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main
    - develop

security-scan:
  stage: build
  image: aquasec/trivy:latest
  script:
    - trivy image --exit-code 0 --severity HIGH,CRITICAL $IMAGE_TAG
  only:
    - main
    - develop

.deploy-template: &deploy-template
  image: alpine/k8s:1.28.0
  before_script:
    - kubectl config use-context $KUBE_CONTEXT

deploy-dev:
  <<: *deploy-template
  stage: deploy-dev
  environment:
    name: development
    url: https://dev.example.com
  variables:
    KUBE_CONTEXT: dev-cluster
  script:
    - |
      kubectl set image deployment/order-service \
        order-service=$IMAGE_TAG \
        -n development
      kubectl rollout status deployment/order-service -n development
  only:
    - develop

deploy-staging:
  <<: *deploy-template
  stage: deploy-staging
  environment:
    name: staging
    url: https://staging.example.com
  variables:
    KUBE_CONTEXT: staging-cluster
  script:
    - |
      helm upgrade --install order-service ./helm/order-service \
        --namespace staging \
        --set image.tag=$CI_COMMIT_SHORT_SHA \
        --set replicaCount=3 \
        --wait
  only:
    - main

deploy-prod:
  <<: *deploy-template
  stage: deploy-prod
  environment:
    name: production
    url: https://example.com
  variables:
    KUBE_CONTEXT: prod-cluster
  script:
    - |
      helm upgrade --install order-service ./helm/order-service \
        --namespace production \
        --set image.tag=$CI_COMMIT_SHORT_SHA \
        --set replicaCount=5 \
        --wait \
        --timeout 10m
  when: manual
  only:
    - main

Dockerfile оптимизация

Multi-stage build

# Build stage
FROM node:18-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY tsconfig.json ./

# Install dependencies
RUN npm ci --only=production && \
    npm cache clean --force

# Copy source code
COPY src ./src

# Build application
RUN npm run build

# Production stage
FROM node:18-alpine

# Add non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# Copy built application
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./

# Switch to non-root user
USER nodejs

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

# Start application
CMD ["node", "dist/main.js"]

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

Blue-Green Deployment

# blue-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service-blue
  labels:
    app: order-service
    version: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
      version: blue
  template:
    metadata:
      labels:
        app: order-service
        version: blue
    spec:
      containers:
      - name: order-service
        image: myregistry/order-service:v1.0.0
---
# green-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service-green
  labels:
    app: order-service
    version: green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
      version: green
  template:
    metadata:
      labels:
        app: order-service
        version: green
    spec:
      containers:
      - name: order-service
        image: myregistry/order-service:v2.0.0
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
    version: blue  # Переключаем на green после проверки
  ports:
  - port: 80
    targetPort: 3000

Rolling Update

# Постепенное обновление
kubectl set image deployment/order-service \
  order-service=myregistry/order-service:v2.0.0

# Мониторинг процесса
kubectl rollout status deployment/order-service

# Откат при проблемах
kubectl rollout undo deployment/order-service

Автоматизация тестирования

E2E тесты в pipeline

// tests/e2e/order-flow.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Order Flow', () => {
  test('should create and process order', async ({ request }) => {
    // Создание заказа
    const createResponse = await request.post('/api/orders', {
      data: {
        customerId: 'test-customer',
        items: [{ productId: 'prod-1', quantity: 2 }]
      }
    });
    
    expect(createResponse.ok()).toBeTruthy();
    const order = await createResponse.json();
    
    // Проверка статуса
    const statusResponse = await request.get(`/api/orders/${order.id}`);
    expect(statusResponse.ok()).toBeTruthy();
    
    const orderStatus = await statusResponse.json();
    expect(orderStatus.status).toBe('pending');
  });
});

Мониторинг деплоя

# ServiceMonitor для Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: order-service
spec:
  selector:
    matchLabels:
      app: order-service
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Best Practices

  1. Автоматизируйте всё — от тестов до деплоя
  2. Используйте multi-stage builds — для оптимизации образов
  3. Версионируйте образы — используйте семантическое версионирование
  4. Тестируйте на каждом этапе — unit, integration, e2e
  5. Сканируйте на уязвимости — интегрируйте Trivy или Snyk
  6. Применяйте GitOps — используйте ArgoCD или Flux
  7. Мониторьте деплои — отслеживайте метрики после релиза
  8. Автоматизируйте откаты — при обнаружении проблем

Заключение

Эффективный CI/CD процесс — основа успешной микросервисной архитектуры, обеспечивающий быструю и безопасную доставку изменений в продакшн.