~/icsd.ir — bash
SYSTEM_ONLINE

CI/CD برای میکروسرویس

با ده‌ها میکروسرویس، CI/CD ضروری است. Manual deployment غیرقابل مدیریت می‌شود. در این فصل یاد می‌گیریم چطور یک pipeline حرفه‌ای بسازیم.

۱۴.۱ مقدمه

با ده‌ها میکروسرویس، CI/CD ضروری است. Manual deployment غیرقابل مدیریت می‌شود. در این فصل یاد می‌گیریم چطور یک pipeline حرفه‌ای بسازیم.

۱۴.۲ Multi-repo vs Monorepo

📦 Multi-repo

هر سرویس یک repository جداگانه.

✅ Independent، ساده، ownership واضح

❌ shared library سخت، code search پراکنده

📚 Monorepo

همه سرویس‌ها در یک repository.

✅ shared code آسان، atomic changes

❌ build سنگین‌تر، tooling پیچیده‌تر

Google، Facebook، Twitter از monorepo استفاده می‌کنند. اما برای پروژه‌های متوسط multi-repo معمولاً ساده‌تر است.

۱۴.۳ GitHub Actions Pipeline


# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]
    paths:
      - "src/**"
      - "Dockerfile"
      - "requirements.txt"
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: shop/user-service
  PYTHON_VERSION: "3.11"

jobs:
  # 1. Code Quality
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: pip
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install ruff black mypy bandit
      
      - name: Lint (Ruff)
        run: ruff check src/
      
      - name: Format check (Black)
        run: black --check src/
      
      - name: Type check (mypy)
        run: mypy src/
      
      - name: Security scan (Bandit)
        run: bandit -r src/ -ll

  # 2. Tests
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: testpass
          POSTGRES_DB: testdb
        ports: ["5432:5432"]
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      
      redis:
        image: redis:7
        ports: ["6379:6379"]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: pip
      
      - name: Install
        run: pip install -r requirements.txt -r requirements-dev.txt
      
      - name: Run tests
        env:
          DATABASE_URL: postgresql://postgres:testpass@localhost/testdb
          REDIS_URL: redis://localhost:6379
        run: |
          pytest --cov=src --cov-report=xml --cov-report=term --junitxml=junit.xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml
      
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results
          path: junit.xml

  # 3. Security
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Dependency check (Safety)
        run: |
          pip install safety
          safety check -r requirements.txt
      
      - name: Trivy filesystem scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          severity: CRITICAL,HIGH
          exit-code: 1

  # 4. Build & Push
  build:
    needs: [quality, test, security]
    if: github.event_name == "push"
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=sha,prefix=sha-,format=short
            type=raw,value=latest,enable={{is_default_branch}}
            type=semver,pattern={{version}}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64
      
      - name: Trivy image scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH

  # 5. Deploy to staging
  deploy-staging:
    needs: build
    if: github.ref == "refs/heads/develop"
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/checkout@v4
      
      - name: Update GitOps repo
        run: |
          git clone https://x:${{ secrets.GITOPS_TOKEN }}@github.com/shop/gitops.git
          cd gitops
          # Update image tag
          yq e -i ".image.tag = "sha-${{ github.sha:0:7 }}"" 
            apps/staging/user-service/values.yaml
          git config user.email "ci@shop.com"
          git config user.name "CI Bot"
          git add .
          git commit -m "chore: deploy user-service sha-${{ github.sha }} to staging"
          git push

  # 6. Deploy to production (manual approval)
  deploy-production:
    needs: build
    if: github.ref == "refs/heads/main"
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://api.shop.com
    steps:
      - uses: actions/checkout@v4
      
      - name: Update GitOps repo
        run: |
          # ... مشابه staging ولی برای production
          yq e -i ".image.tag = "sha-${{ github.sha:0:7 }}"" 
            apps/production/user-service/values.yaml
          # commit and push
    

۱۴.۴ GitOps با ArgoCD

GitOps: Git یگانه «منبع حقیقت» برای infrastructure است. هر تغییر از طریق Git pull request انجام می‌شود.


[Developer] ──pr──► [Code Repo]
                         │
                         │ CI builds image
                         ▼
                    [Container Registry]
                         │
                         │ CI updates manifest
                         ▼
                    [GitOps Repo]
                         │
                         │ ArgoCD watches
                         ▼
                    [Kubernetes Cluster]
                         │
                         │ Continuous reconciliation
                         ▼
                    [Live State == Git State]
    

ArgoCD Application


apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: user-service
  namespace: argocd
spec:
  project: shop
  source:
    repoURL: https://github.com/shop/gitops
    targetRevision: HEAD
    path: apps/production/user-service
    helm:
      valueFiles:
        - values.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: shop
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
    

مزایای GitOps

  • تاریخچه کامل تغییرات در Git
  • Rollback ساده (git revert)
  • Audit trail خودکار
  • Disaster recovery (cluster را از Git rebuild کنید)
  • Pull request review قبل از deploy

۱۴.۵ Deployment Strategies

۱. Recreate

همه pod ها down، سپس new version up. downtime دارد!

۲. Rolling Update

به‌تدریج pod های قدیمی با جدید جایگزین می‌شوند. پیش‌فرض Kubernetes.

۳. Blue-Green

دو محیط کامل (Blue: قدیمی، Green: جدید). traffic switch ناگهانی.


# Switch با تغییر selector در Service
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user
    version: green  # تغییر از blue به green
  ports:
    - port: 80
    

۴. Canary

درصد کمی ترافیک به نسخه جدید، اگر OK بود گسترش.


# Istio VirtualService
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: user-service
spec:
  hosts: [user-service]
  http:
  - route:
    - destination:
        host: user-service
        subset: v1
      weight: 95
    - destination:
        host: user-service
        subset: v2
      weight: 5  # ۵٪ به canary
    

۵. A/B Testing

routing بر اساس header یا کاربر — برای feature testing.


http:
- match:
  - headers:
      x-beta-user:
        exact: "true"
  route:
  - destination:
      host: user-service
      subset: v2
- route:
  - destination:
      host: user-service
      subset: v1
    

۶. Shadow Traffic

ترافیک به دو نسخه فرستاده می‌شود ولی فقط پاسخ یکی برمی‌گردد. برای تست بدون تأثیر بر کاربر.

۱۴.۶ Helm برای Deployment

یک chart واحد، چند environment با values متفاوت.


charts/user-service/
├── Chart.yaml
├── values.yaml          # default
├── values-staging.yaml  # staging overrides
├── values-prod.yaml     # production overrides
└── templates/
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    ├── hpa.yaml
    └── _helpers.tpl
    

# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "user-service.fullname" . }}
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      {{- include "user-service.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "user-service.selectorLabels" . | nindent 8 }}
    spec:
      containers:
      - name: user
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        resources:
          {{- toYaml .Values.resources | nindent 10 }}
        env:
        {{- range $key, $value := .Values.env }}
        - name: {{ $key }}
          value: {{ $value | quote }}
        {{- end }}
    

# values-prod.yaml
replicas: 5
image:
  tag: 1.2.3
resources:
  limits:
    cpu: "1"
    memory: "1Gi"
env:
  LOG_LEVEL: warning
  DATABASE_POOL_SIZE: "20"

# Deploy
helm upgrade --install user-service ./charts/user-service 
    -f values-prod.yaml 
    --namespace shop-prod
    

۱۴.۷ بهترین تجربیات

  1. Branch protection. direct push به main ممنوع.
  2. PR requires review + CI green.
  3. Trunk-Based Development یا Git Flow.
  4. Semantic versioning. v1.2.3
  5. Image immutability. هیچ‌گاه tag را override نکنید.
  6. SHA-based tags در production. reproducibility.
  7. Rollback آسان. با تغییر tag در Git.
  8. Canary deployment. برای سرویس‌های critical.
  9. Automated rollback. اگر error rate افزایش یافت.
  10. Pre-deployment migration. با backward compat.
  11. Smoke tests post-deployment.
  12. GitOps for production. declarative، auditable.

۱۴.۸ خلاصه فصل

آنچه آموختیم:
  • Multi-repo vs Monorepo
  • GitHub Actions Pipeline کامل: lint، test، security، build، deploy
  • GitOps با ArgoCD
  • Deployment Strategies: Rolling، Blue-Green، Canary، A/B
  • Helm برای multi-environment
در فصل بعد: پروژه عملی پایانی — پیاده‌سازی کامل یک سیستم e-commerce میکروسرویس از صفر.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.