GitOps Principles
GitOps lấy Git repository làm single source of truth cho cả application code và infrastructure state.
4 nguyên tắc cốt lõi:
- Declarative - Toàn bộ hệ thống mô tả dưới dạng declarative (YAML, Bicep, HCL)
- Versioned & Immutable - Mọi thay đổi qua Git commit, có audit trail
- Pulled Automatically - Agent trong cluster tự pull và apply thay đổi
- Continuously Reconciled - Agent liên tục so sánh desired state vs actual state
Flux vs ArgoCD
| Tiêu chí | Flux v2 | ArgoCD |
|---|---|---|
| Architecture | Set of controllers | Server + UI |
| UI | Không có built-in (dùng Weave GitOps) | Web UI đẹp, đầy đủ |
| Multi-tenancy | Native (Kustomize-based) | AppProject-based |
| Helm support | HelmRelease CRD | Native Helm support |
| Kustomize | Native, first-class | Supported |
| Notification | Built-in (Slack, Teams, webhook) | Notification engine |
| Image automation | Built-in image reflector | Argo Image Updater (separate) |
| Learning curve | Trung bình (CLI-focused) | Thấp (UI trực quan) |
| RBAC | Kubernetes RBAC | Built-in RBAC + SSO |
| Resource usage | Nhẹ hơn | Nặng hơn (server + redis + dex) |
Trade-off: ArgoCD phù hợp team cần UI trực quan, dễ onboard thành viên mới. Flux phù hợp team thích CLI-first, muốn lightweight, và tích hợp sâu với Kustomize.
Environment Strategy
Cấu trúc GitOps Repository
gitops-repo/
base/ # Shared manifests
deployment.yaml
service.yaml
kustomization.yaml
overlays/
dev/
kustomization.yaml # Dev-specific patches
replicas-patch.yaml
staging/
kustomization.yaml
replicas-patch.yaml
production/
kustomization.yaml
replicas-patch.yaml
hpa-patch.yaml # HPA chỉ production
yaml# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- replicas-patch.yaml
- hpa-patch.yaml
namespace: myapp-prod
commonLabels:
env: production
yaml# overlays/production/replicas-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-api
spec:
replicas: 3
template:
spec:
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "250m"
memory: "256Mi"
Branching Strategies
Trunk-Based Development vs GitFlow
| Tiêu chí | Trunk-Based | GitFlow |
|---|---|---|
| Branch chính | main (trunk) | main + develop |
| Feature branch | Short-lived (< 2 ngày) | Long-lived |
| Release | Tag on main hoặc release branch ngắn | release/* branch |
| Hotfix | Fix on main, cherry-pick | hotfix/* branch |
| CI/CD | Deploy từ main liên tục | Deploy từ release branch |
| Merge conflict | Ít (branch ngắn) | Nhiều (branch dài) |
| Phù hợp | Team mature, CI/CD mạnh | Team lớn, release cycle dài |
Trade-off phân tích:
- Trunk-based yêu cầu feature flags, test coverage cao, CI pipeline nhanh. Reward: deploy frequency cao, feedback loop ngắn.
- GitFlow dễ quản lý release, phù hợp mobile app hoặc on-premise software. Cost: merge hell, delayed integration.
Recommendation cho .NET web app: Trunk-based development + feature flags + short-lived branches.
Environment Parity
Giữ các environment càng giống nhau càng tốt:
yaml# docker-compose.override.yml - local dev giả lập production
services:
api:
build: .
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__Default=Server=db;Database=myapp;User=sa;Password=Dev@12345;TrustServerCertificate=true
depends_on:
- db
- redis
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=Dev@12345
redis:
image: redis:7-alpine
| Aspect | Dev | Staging | Production |
|---|---|---|---|
| Database | SQL Server container | Azure SQL Basic | Azure SQL S2+ |
| Cache | Redis container | Azure Cache Basic | Azure Cache Standard |
| SSL | Self-signed | Azure managed cert | Custom domain cert |
| Replicas | 1 | 1 | 3+ |
| Logging | Console | Application Insights | Application Insights + Log Analytics |
Secret Management
Nguyên tắc
- Never commit secrets vào Git
- Dùng external secret store (Key Vault, Sealed Secrets)
- Mỗi environment có secret riêng
- Rotate secrets định kỳ
Azure Key Vault Integration
csharp// Program.cs - load secrets từ Key Vault
builder.Configuration.AddAzureKeyVault(
new Uri(builder.Configuration["KeyVault:VaultUri"]!),
new DefaultAzureCredential());
Kubernetes - External Secrets Operator
yaml# ExternalSecret syncs từ Azure Key Vault → Kubernetes Secret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
namespace: myapp-prod
spec:
refreshInterval: 1h
secretStoreRef:
name: azure-keyvault
kind: ClusterSecretStore
target:
name: myapp-secrets
creationPolicy: Owner
data:
- secretKey: ConnectionStrings__Default
remoteRef:
key: SqlConnectionString
- secretKey: Redis__Password
remoteRef:
key: RedisPassword
Promotion Workflow
yaml# GitHub Actions - promote image từ staging → production
name: Promote to Production
on:
workflow_dispatch:
inputs:
image_tag:
description: 'Image tag to promote'
required: true
jobs:
promote:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
repository: myorg/gitops-repo
token: ${{ secrets.GITOPS_PAT }}
- name: Update production image tag
run: |
cd overlays/production
kustomize edit set image myapp-api=myacr.azurecr.io/myapp-api:${{ inputs.image_tag }}
- name: Commit and push
run: |
git config user.name "github-actions"
git config user.email "actions@github.com"
git add .
git commit -m "promote: myapp-api:${{ inputs.image_tag }} → production"
git push
# GitOps agent (Flux/ArgoCD) sẽ tự động detect và deploy
Tip: Dùng Pull Request thay vì direct push cho production promotion - thêm một lớp review trước khi deploy.