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
۱۴.۷ بهترین تجربیات
- Branch protection. direct push به main ممنوع.
- PR requires review + CI green.
- Trunk-Based Development یا Git Flow.
- Semantic versioning. v1.2.3
- Image immutability. هیچگاه tag را override نکنید.
- SHA-based tags در production. reproducibility.
- Rollback آسان. با تغییر tag در Git.
- Canary deployment. برای سرویسهای critical.
- Automated rollback. اگر error rate افزایش یافت.
- Pre-deployment migration. با backward compat.
- Smoke tests post-deployment.
- 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