Viết ra được 21 microservices bằng Go với kiến trúc chuẩn mực mới chỉ là một nửa cuộc chiến. Nếu quy trình deploy (triển khai) của bạn vẫn phụ thuộc vào một kỹ sư gõ lệnh kubectl apply từ laptop cá nhân của anh ta vào một chiều thứ Sáu, bạn chưa hề xây dựng một nền tảng enterprise — bạn vừa chế tạo ra một quả bom nổ chậm.
Khi thiết kế hệ sinh thái thương mại điện tử composable này, chúng tôi đã đặt ra một quy tắc kiến trúc thép ngay từ ngày đầu tiên: Tuyệt đối không có con người nào được phép chạm trực tiếp vào cluster production. Mọi thứ phải chảy qua Git. ArgoCD sẽ là người ép buộc điều đó.
Mô hình Tư duy GitOps: Git là Nguồn chân lý duy nhất (Source of Truth)
Nguyên tắc cốt lõi: một repository Git chính là trạng thái mong muốn mang tính khai báo (declarative desired state) của toàn bộ hạ tầng của bạn. ArgoCD là một tác tử thi hành (enforcement agent), nó liên tục đối chiếu và đồng bộ (reconcile) trạng thái của cluster sao cho khớp với trạng thái trên Git.
Điều này khác biệt về bản chất so với mô hình push của CI/CD truyền thống:
graph LR
subgraph "CI Pipeline — Code"
DEV["Developer"] -- "git push" --> APP["App Repository"]
APP -- "GitHub Actions" --> DOCKER[("Container Registry")]
end
subgraph "CD Pipeline — GitOps"
DOCKER -- "Cập nhật image tag vào" --> MAN["Manifest Repository"]
ARGO["ArgoCD Controller"] -- "Polls mỗi 3 phút" --> MAN
end
subgraph "Kubernetes Cluster"
ARGO -- "Áp dụng diff, rolling restart" --> K8S["K8s Nodes"]
end
- CI: Kỹ sư push code Go → GitHub Actions chạy test, build binary, push một Docker image lên registry, sau đó mở một Pull Request (PR) sang một Manifest Repository riêng biệt để cập nhật image tag.
- CD: ArgoCD liên tục theo dõi (watch) Manifest Repo đó. Khi PR được merge, ArgoCD phát hiện ra sự sai lệch (drift) giữa trạng thái mong muốn (trên Git) và trạng thái thực tế (dưới cluster), và tự động khắc phục sự sai lệch đó.
Không ai chạm vào kubectl. Không ai cần thông tin đăng nhập của cluster (cluster credentials) trong quá trình CI. Cluster hoạt động theo cơ chế kéo (pull-based), không phải cơ chế đẩy (push-based).
Cấu trúc Repository
Chúng tôi sử dụng 2 repository riêng biệt — một pattern được gọi là split-repo GitOps:
manifest-repo/
├── apps/
│ ├── order-service/
│ │ ├── base/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── kustomization.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ └── kustomization.yaml
│ │ ├── staging/
│ │ │ └── kustomization.yaml
│ │ └── prod/
│ │ ├── kustomization.yaml
│ │ └── hpa.yaml
│ └── checkout-service/
│ └── ...
└── argocd/
├── root-app.yaml ← App-of-Apps root
├── order-service-prod.yaml
└── checkout-service-prod.yaml
Việc giữ các file manifests ở một repo riêng biệt so với repo chứa code application tạo ra một dấu vết kiểm toán (audit trail) cực kỳ rõ ràng: mỗi bản deploy là một Git commit với timestamp, tác giả, và image tag rõ ràng. Việc Rollback (khôi phục bản cũ) chỉ đơn giản là gõ lệnh git revert.
Đối tượng Application CRD của ArgoCD
Mỗi service sẽ được cấp một đối tượng Application CRD để nói cho ArgoCD biết chính xác nó phải theo dõi đường dẫn nào trong manifest repo, và deploy nó vào đâu:
# argocd/order-service-prod.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-service-production
namespace: argocd
labels:
environment: production
team: commerce
spec:
project: ecommerce-prod
source:
repoURL: https://github.com/your-org/manifest-repo.git
targetRevision: main
path: apps/order-service/overlays/prod # trỏ thẳng vào Kustomize overlay của môi trường prod
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # tự động xóa các resource đã bị xóa trên Git
selfHeal: true # tự động lật ngược lại các lệnh kubectl sửa tay của con người
syncOptions:
- CreateNamespace=true
- ServerSideApply=true # tránh lỗi giới hạn độ dài annotation trên các CRD lớn
retry:
limit: 3
backoff:
duration: 30s
factor: 2
maxDuration: 5m
# Health check: ArgoCD sẽ không đánh dấu là Healthy cho tới khi tất cả pod chuyển sang trạng thái Running
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # bỏ qua sự sai lệch về số lượng pod do HPA quản lý
selfHeal: truelà một cấu hình sống còn cho production. Nếu một SRE lỡ tay gõ lệnh vá (patch) một Deployment trực tiếp trên production (vì bất kỳ lý do gì), ArgoCD sẽ ghi đè và lật ngược thao tác đó trong vòng chưa tới 3 phút. Git là nguồn chân lý duy nhất — tuyệt đối không có ngoại lệ.
Thuần phục mớ hỗn độn YAML bằng Kustomize
Việc quản lý file manifests cho 21 services trên 3 môi trường (dev, staging, prod) sẽ đẻ ra hơn 60+ file YAML. Việc copy-paste thủ công sẽ tạo ra sự trôi dạt cấu hình (configuration drift). Kustomize giải quyết triệt để chuyện này bằng mô hình base + overlay (nền tảng + các lớp phủ).
Base Manifest (Không phụ thuộc môi trường)
# apps/order-service/base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 1 # sẽ được override tùy theo môi trường
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: registry.example.com/order-service:latest # tag sẽ được override tùy theo môi trường
ports:
- containerPort: 8080
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 15
# apps/order-service/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
Production Overlay (Chỉ vá những thứ khác biệt)
Một lớp phủ overlay không bao giờ được phép copy lại (duplicate) phần base — nó chỉ được quyền vá (patch) những thứ thay đổi tùy theo môi trường:
# apps/order-service/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- hpa.yaml # Chỉ ở prod mới có cấu hình Auto-scaling (HPA)
# Ghi đè image tag cho bản release cụ thể này
images:
- name: registry.example.com/order-service
newTag: "v1.47.2" # được CI pipeline tự động update sau mỗi bản build
# Vá theo kiểu Strategic merge — chỉ cập nhật các field khác biệt so với thư mục base
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 4 # prod cần 4 replicas thay vì 1 như của dev
template:
spec:
containers:
- name: order-service
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2000m"
memory: "2Gi"
env:
- name: DB_HOST
valueFrom:
secretKeyRef:
name: order-service-prod-secrets
key: db_host
# apps/order-service/overlays/prod/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 4
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Tương tự, lớp phủ dev sử dụng newTag: "latest", replicas: 1, cấp ít RAM/CPU hơn, và không xài HPA. Lớp phủ staging sao chép cấu hình scale của prod nhưng trỏ vào secret của database staging. Cả 3 môi trường đều dùng chung một bộ khung logic deployment cốt lõi nằm ở thư mục base.
Gốc rễ của App-of-Apps
Với 21 services, việc tạo thủ công các đối tượng Application của ArgoCD từng cái một là bất khả thi. Chúng tôi sử dụng pattern App-of-Apps: tạo ra một Application gốc duy nhất có nhiệm vụ quản lý tất cả các đối tượng Application con lại:
# argocd/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ecommerce-platform
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/manifest-repo.git
targetRevision: main
path: argocd/ # ArgoCD theo dõi duy nhất thư mục này để tìm các file Application CRD
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Khi có một microservice mới được thêm vào nền tảng, quy trình chuẩn sẽ là:
- Thêm một file YAML
Applicationmới vào thư mụcargocd/ - Merge PR
- Đối tượng App-of-Apps gốc phát hiện ra file mới này và tự động tạo ra một Application con
- ArgoCD tự động cấp phát (provision) service mới lên cluster với 0% sự can thiệp thủ công
Câu chuyện Rollback: Chỉ cần git revert là đủ
Trước kỷ nguyên GitOps, việc rollback một bản deploy lỗi đồng nghĩa với việc cuống cuồng đào bới trong CI logs để tìm xem cái tag Docker cũ là gì, rồi sau đó gõ thủ công các lệnh kubectl rollout undo trong khi khách hàng đang ăn liên hoàn lỗi 500.
Với ArgoCD, quá trình cứu hộ thảm họa (disaster recovery) gói gọn trong 4 bước:
# 1. Định vị commit gây lỗi
git log --oneline manifests/apps/checkout-service/overlays/prod/kustomization.yaml
# 2. Hoàn tác (Revert) commit đó
git revert <bad-commit-hash> --no-edit
# 3. Đẩy code
git push origin main
# 4. ArgoCD phát hiện ra lệnh revert này trong vòng 3 phút và tự động rollback
# Không cần lệnh kubectl. Không cần chui vào cluster.
Lệnh revert này tạo ra một commit mới với đầy đủ tính năng kiểm toán (auditability): ai là người revert, revert lúc nào, và thay đổi nào vừa bị hoàn tác. Trạng thái của cluster quay trở về version ổn định gần nhất trong chưa tới 5 phút.
Đối với các service sinh tử (như Checkout, Payment), chúng tôi dùng thêm Argo Rollouts kết hợp với các cổng đánh giá sức khỏe dựa trên Prometheus — nếu tỷ lệ lỗi (error rate) hoặc độ trễ p99 vượt quá ngưỡng cho phép trong quá trình rollout, bản canary sẽ tự động bị hủy bỏ và luồng traffic sẽ tự động được dồn ngược lại bản ổn định cũ mà không cần đến con người can thiệp.
Tóm lược: Các Nguyên lý giúp Hệ thống hoạt động
| Nguyên lý | Cách triển khai |
|---|---|
| Git là nguồn chân lý duy nhất | selfHeal: true trên mọi prod Applications |
| Không có sự trôi lệch giữa các môi trường | Kustomize base + overlays — chỉ vá, không copy |
| Rollback là một thao tác Git | git revert → ArgoCD tự động đồng bộ |
| Service mới tự động đăng ký | Gốc App-of-Apps theo dõi thư mục argocd/ |
| Lỗi con người bị ngăn chặn triệt để | Không một ai cần gõ kubectl hay thông tin đăng nhập trong CI |
Chi phí đầu tư cho kiến trúc này sẽ mang lại quả ngọt ở ngay lần đầu tiên bạn gặp sự cố deploy trên production lúc 2h sáng. Thay vì cuống cuồng gõ lệnh kubectl trong hoảng loạn, bạn chỉ cần mở terminal, gõ 3 dòng lệnh git, và nhâm nhi cafe nhìn ArgoCD tự thân nó đi sửa chữa lỗi lầm trên cluster.