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
- Автоматизируйте всё — от тестов до деплоя
- Используйте multi-stage builds — для оптимизации образов
- Версионируйте образы — используйте семантическое версионирование
- Тестируйте на каждом этапе — unit, integration, e2e
- Сканируйте на уязвимости — интегрируйте Trivy или Snyk
- Применяйте GitOps — используйте ArgoCD или Flux
- Мониторьте деплои — отслеживайте метрики после релиза
- Автоматизируйте откаты — при обнаружении проблем
Заключение
Эффективный CI/CD процесс — основа успешной микросервисной архитектуры, обеспечивающий быструю и безопасную доставку изменений в продакшн.