Cấu hình ArgoCD Auto-rollback khi phát hiện lỗi deployment
Để hệ thống tự động phục hồi khi một lần deploy mới thất bại, ta cần cấu hình cơ chế Auto-rollback trong ArgoCD. Cơ chế này sẽ tự động quay lại phiên bản (revision) trước đó nếu ứng dụng không đạt trạng thái Sync hoặc Healthy trong một khoảng thời gian nhất định.
Tại sao cần làm vậy: Giảm thiểu thời gian hệ thống bị lỗi (downtime) và loại bỏ sự can thiệp thủ công của con người trong các sự cố khẩn cấp ban đêm hoặc cuối tuần.
Kết quả mong đợi: Khi deploy version mới bị lỗi, ArgoCD sẽ tự động revert về version ổn định cuối cùng mà không cần lệnh `kubectl` hay thao tác trên UI.
Trước tiên, ta sẽ chỉnh sửa manifest của Application trong ArgoCD. Ta cần thêm tham số `rollbackWindow` hoặc cấu hình thông qua ArgoCD ConfigMap tùy thuộc vào yêu cầu cụ thể. Ở đây, ta sẽ áp dụng cấu hình trực tiếp vào file Application YAML để dễ quản lý theo GitOps.
Chỉnh sửa file ứng dụng trong repository Git của bạn (ví dụ: `manifests/my-app.yaml`). Thêm khối `healthCheck` và cấu hình `rollback` nếu cần, nhưng quan trọng nhất là kích hoạt `selfHeal` và đảm bảo `syncPolicy` có cơ chế rollback.
Tuy nhiên, ArgoCD mặc định không có "Auto-rollback" theo nghĩa tự động quay lại commit trước đó nếu health check fail trừ khi bạn cấu hình `syncPolicy` với `retry` hoặc sử dụng Plugin. Cách chuẩn nhất hiện nay là sử dụng `rollback` strategy trong `syncPolicy` kết hợp với Health Check. Dưới đây là cấu hình Application đầy đủ:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/your-repo.git
targetRevision: HEAD
path: manifests/my-app
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
# Cấu hình retry để ArgoCD thử lại deploy nhiều lần trước khi coi là lỗi
retry:
limit: 3
backoff:
duration: 5s
maxDuration: 3m
factor: 2
# Cấu hình sync wave để đảm bảo thứ tự deploy nếu có nhiều resource
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
# Thêm health check tùy chỉnh nếu cần
healthCheck:
# Ví dụ: Kiểm tra Pod đang chạy
# (ArgoCD mặc định đã check Pod status, nhưng đây là chỗ để override)
Kết quả mong đợi: Khi bạn đẩy code mới vào Git, ArgoCD sẽ tự động sync. Nếu Pod mới bị CrashLoopBackOff hoặc không Healthy sau 3 lần thử (theo cấu hình retry), ArgoCD sẽ giữ nguyên trạng thái cũ (không tự rollback về revision trước đó trừ khi bạn cấu hình thêm plugin hoặc script hook). Lưu ý: Để có auto-rollback về revision cũ thực sự, ta cần cấu hình `rollback` trong `syncPolicy` hoặc dùng webhook. Cấu hình trên đảm bảo `self-heal` (khôi phục trạng thái mong muốn) nhưng để tự động quay lại commit cũ, ta cần bước tiếp theo với Tekton.
Để có Auto-rollback thực sự (quay lại commit Git trước đó), ta cần cấu hình ArgoCD Application để nó lắng nghe sự kiện Health Check fail và kích hoạt rollback. Một cách phổ biến là dùng `argocd-util` hoặc webhook, nhưng trong môi trường GitOps thuần, ta sẽ cấu hình `rollback` strategy trong `syncPolicy` nếu dùng ArgoCD Enterprise hoặc dùng plugin. Với bản Community, ta sẽ dùng cơ chế `rollback` thủ động tự động hóa qua Tekton (xem phần sau). Tuy nhiên, ta có thể cấu hình `rollbackWindow` để hạn chế rollback quá xa.
Tuy nhiên, một cách đơn giản và hiệu quả nhất với ArgoCD Community là sử dụng `syncPolicy` với `retry` và `selfHeal`, kết hợp với việc cấu hình `rollback` trong manifest nếu cần. Dưới đây là cách cấu hình `rollback` strategy (chỉ có trong một số phiên bản mới hoặc qua plugin):
Để đơn giản và đảm bảo chạy được trên mọi môi trường, ta sẽ sử dụng cơ chế `selfHeal` kết hợp với `retry`. Nếu sau khi retry hết số lần, ArgoCD sẽ báo lỗi và không tự rollback. Để tự rollback, ta cần bước tiếp theo là xây dựng pipeline Tekton.
Vậy nên, bước này ta tập trung vào cấu hình `selfHeal` và `retry` để giảm thiểu lỗi:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/your-repo.git
targetRevision: HEAD
path: manifests/my-app
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
retry:
limit: 3
backoff:
duration: 5s
maxDuration: 3m
factor: 2
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
Kết quả mong đợi: ArgoCD sẽ tự động cố gắng deploy lại tối đa 3 lần. Nếu thành công, hệ thống ổn định. Nếu thất bại, ArgoCD sẽ giữ nguyên trạng thái cũ (không rollback) và báo lỗi. Để tự động rollback, ta cần pipeline Tekton.
Verify kết quả
Chạy lệnh sau để kiểm tra trạng thái của Application:
argocd app get my-app -n argocd
Đợi đến khi trạng thái là `OutOfSync` hoặc `Degraded` sau khi push code lỗi. Kiểm tra lại sau 5 phút. Nếu ArgoCD đã retry 3 lần và vẫn lỗi, trạng thái sẽ là `Failed` hoặc `Degraded`. Đây là tín hiệu để kích hoạt pipeline rollback.
Xây dựng pipeline để rollback version image từ lịch sử Git
Khi ArgoCD báo lỗi và không thể tự hồi phục, ta cần một pipeline CI/CD (sử dụng Tekton) để tự động xác định commit ổn định gần nhất và thực hiện rollback.
Tại sao cần làm vậy: Tự động hóa quy trình phục hồi sự cố, giảm thiểu thời gian phản ứng (MTTR) và đảm bảo tính nhất quán của quy trình.
Kết quả mong đợi: Khi phát hiện lỗi, Tekton sẽ tự động chạy, tìm commit trước đó, cập nhật file Application của ArgoCD về commit đó và kích hoạt sync.
Trước tiên, ta cần tạo một Tekton Pipeline mới. Pipeline này sẽ bao gồm các Task: 1) Lấy lịch sử commit, 2) Xác định commit ổn định, 3) Cập nhật manifest ArgoCD, 4) Trigger ArgoCD sync.
Tạo file Pipeline mới trong thư mục `tekton-pipelines` của repo Git:
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: auto-rollback-pipeline
spec:
params:
- name: app-name
type: string
- name: target-revision
type: string
- name: repo-url
type: string
tasks:
- name: checkout-repo
taskRef:
name: git-clone
params:
- name: url
value: $(params.repo-url)
- name: revision
value: $(params.target-revision)
- name: subdirectory
value: manifests
workspaces:
- name: output
workspace: shared-workspace
- name: update-argo-cd-manifest
taskRef:
name: update-manifest
params:
- name: app-name
value: $(params.app-name)
- name: target-revision
value: $(params.target-revision)
workspaces:
- name: source
workspace: shared-workspace
runAfter:
- checkout-repo
- name: commit-and-push
taskRef:
name: git-commit-push
params:
- name: message
value: "Auto-rollback to $(params.target-revision)"
- name: author.name
value: "ArgoCD-Robot"
- name: author.email
value: "robot@your-org.com"
workspaces:
- name: output
workspace: shared-workspace
runAfter:
- update-argo-cd-manifest
workspaces:
- name: shared-workspace
Kết quả mong đợi: Pipeline đã được định nghĩa. Nó sẽ clone repo, cập nhật file manifest của ArgoCD về revision mong muốn, commit và push lên Git. ArgoCD sẽ tự động phát hiện thay đổi và sync.
Bây giờ, ta cần tạo Task `update-manifest` để thay đổi nội dung file YAML của ArgoCD. Task này sẽ sử dụng script bash để chỉnh sửa file `my-app.yaml`.
Tạo file Task `update-manifest.yaml` trong thư mục `tekton-tasks`:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: update-manifest
spec:
params:
- name: app-name
type: string
- name: target-revision
type: string
workspaces:
- name: source
steps:
- name: update-yaml
image: bitnami/git:latest
script: |
#!/usr/bin/env bash
set -e
cd $(workspaces.source.path)
# Tìm file Application của ArgoCD
APP_FILE="my-app.yaml"
if [ -f "$APP_FILE" ]; then
# Cập nhật targetRevision trong file
sed -i "s/targetRevision: .*/targetRevision: $(params.target-revision)/" "$APP_FILE"
echo "Updated $APP_FILE with revision $(params.target-revision)"
else
echo "File $APP_FILE not found"
exit 1
fi
workspaces:
- name: source
Kết quả mong đợi: Task có thể chạy độc lập để cập nhật file manifest. Nó sẽ thay đổi dòng `targetRevision` trong file YAML.
Để kích hoạt pipeline này khi có sự cố, ta cần tạo một Tekton Trigger hoặc sử dụng webhook từ ArgoCD. Ở đây, ta sẽ tạo một TriggerBinding và TriggerTemplate để lắng nghe sự kiện từ ArgoCD (ví dụ: khi trạng thái là `Degraded`).
Tuy nhiên, để đơn giản, ta sẽ tạo một PipelineRun thủ công để test. Sau đó, ta sẽ tích hợp với ArgoCD webhook.
Tạo file `trigger-binding.yaml` để nhận dữ liệu từ ArgoCD:
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: argocd-rollback-binding
spec:
params:
- name: app-name
value: $(body.metadata.name)
- name: target-revision
value: $(body.status.history.0.revision) # Lấy revision trước đó
- name: repo-url
value: $(body.spec.source.repoURL)
Kết quả mong đợi: TriggerBinding đã được tạo. Nó sẽ trích xuất thông tin từ webhook ArgoCD (tên app, revision, repoURL).
Tạo file `trigger-template.yaml` để tạo PipelineRun từ TriggerBinding:
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
name: argocd-rollback-template
spec:
params:
- name: app-name
- name: target-revision
- name: repo-url
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: auto-rollback-
spec:
pipelineRef:
name: auto-rollback-pipeline
params:
- name: app-name
value: $(tt.params.app-name)
- name: target-revision
value: $(tt.params.target-revision)
- name: repo-url
value: $(tt.params.repo-url)
workspaces:
- name: shared-workspace
persistentvolumeclaim:
claimName: shared-pvc
Kết quả mong đợi: TriggerTemplate đã được tạo. Nó sẽ tạo PipelineRun khi nhận được trigger.
Cuối cùng, tạo Trigger để lắng nghe webhook từ ArgoCD. Ta cần cấu hình ArgoCD để gửi webhook khi trạng thái thay đổi sang `Degraded`.
Tạo file `trigger.yaml`:
apiVersion: triggers.tekton.dev/v1beta1
kind: Trigger
metadata:
name: argocd-rollback-trigger
namespace: tekton-pipelines
spec:
bindings:
- name: argocd-rollback-binding
template:
name: argocd-rollback-template
interceptors:
- name: cel
ref:
name: cel
params:
- name: filter
value: "body.status.health.status == 'Degraded' || body.status.sync.status == 'Failed'"
Kết quả mong đợi: Trigger đã được tạo. Nó sẽ lắng nghe webhook từ ArgoCD và chỉ kích hoạt khi trạng thái là `Degraded` hoặc `Failed`. CEL filter sẽ kiểm tra điều kiện này.
Verify kết quả
Chạy lệnh sau để kiểm tra trạng thái của Trigger:
kubectl get trigger argocd-rollback-trigger -n tekton-pipelines
Để test, ta sẽ giả lập một sự kiện bằng cách gửi webhook thủ công. Đầu tiên, lấy URL của Tekton Trigger:
kubectl get secret argocd-rollback-trigger -n tekton-pipelines -o jsonpath='{.data.token}' | base64 -d
Sau đó, gửi webhook với trạng thái `Degraded`:
curl -X POST -H "Content-Type: application/json" -H "X-GitHub-Event: argocd-roll" -d '{
"metadata": {"name": "my-app"},
"status": {"health": {"status": "Degraded"}, "sync": {"status": "Failed"}, "history": [{"revision": "abc123"}]},
"spec": {"source": {"repoURL": "https://github.com/your-org/your-repo.git"}}
}' https://tekton-triggers-service.tekton-pipelines.svc.cluster.local/triggers/argocd-rollback-trigger
Kiểm tra PipelineRun đã được tạo:
kubectl get pipelinerun -n tekton-pipelines | grep auto-rollback
Quan sát log của PipelineRun để đảm bảo nó đã cập nhật file manifest và push lên Git.
Sao lưu trạng thái ArgoCD và cấu hình Vault
Để phục hồi hệ thống sau sự cố mất cluster (disaster recovery), ta cần sao lưu toàn bộ trạng thái của ArgoCD và các secret trong Vault.
Tại sao cần làm vậy: Đảm bảo có thể khôi phục toàn bộ hệ thống từ đầu mà không mất dữ liệu cấu hình hoặc secret quan trọng.
Kết quả mong đợi: Có file backup chứa toàn bộ resource của ArgoCD và snapshot của Vault. Có thể khôi phục lại cluster mới từ file backup này.
Trước tiên, ta cần sao lưu trạng thái của ArgoCD. ArgoCD lưu trữ trạng thái (state) trong Kubernetes Custom Resource (CR) và trong database (PostgreSQL/MySQL). Ta sẽ sao lưu cả CR và database.
Sao lưu các Custom Resource của ArgoCD (Application, AppProject, etc.):
kubectl get all,configmap,secret,application,appproject -n argocd -o yaml > argocd-resources-backup.yaml
Kết quả mong đợi: File `argocd-resources-backup.yaml` chứa toàn bộ resource của ArgoCD.
Sao lưu database của ArgoCD (giả sử dùng PostgreSQL):
PGPASSWORD=your_password pg_dump -h argocd-redis-postgresql -U argocd argocd > argocd-db-backup.sql
Kết quả mong đợi: File `argocd-db-backup.sql` chứa toàn bộ dữ liệu của database ArgoCD.
Bây giờ, ta cần sao lưu cấu hình và secret trong HashiCorp Vault. Vault lưu trữ secret trong backend (file, Raft, S3, etc.). Ta sẽ sử dụng lệnh `vault operator raft snapshot` để tạo snapshot của Vault.
Trước tiên, đảm bảo bạn đã login vào Vault:
export VAULT_ADDR='http://vault:8200'
export VAULT_TOKEN='your-root-token'
Tạo snapshot của Vault:
vault operator raft snapshot save /tmp/vault-snapshot.bin
Kết quả mong đợi: File `/tmp/vault-snapshot.bin` chứa toàn bộ dữ liệu của Vault.
Ngoài ra, ta cần sao lưu các secret đã được lưu trong Vault (dưới dạng JSON hoặc YAML) để dễ đọc và khôi phục. Ta sẽ sử dụng lệnh `vault kv get` để lấy secret.
Sao lưu tất cả secret trong Vault (giả sử path là `secret/data/my-app`):
vault kv get -format=json secret/data/my-app > vault-secrets-backup.json
Kết quả mong đợi: File `vault-secrets-backup.json` chứa toàn bộ secret của ứng dụng.
Để tự động hóa quá trình sao lưu, ta có thể tạo một Tekton Pipeline chạy định kỳ (cron). Pipeline này sẽ chạy các lệnh trên và upload file backup lên object storage (S3, GCS, etc.).
Tạo file Pipeline `backup-pipeline.yaml`:
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: backup-pipeline
spec:
tasks:
- name: backup-argocd
taskRef:
name: backup-argocd-task
- name: backup-vault
taskRef:
name: backup-vault-task
- name: upload-to-s3
taskRef:
name: upload-to-s3-task
runAfter:
- backup-argocd
- backup-vault
Kết quả mong đợi: Pipeline đã được định nghĩa. Nó sẽ chạy tuần tự các task backup.
Tạo Task `backup-argocd-task.yaml`:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: backup-argocd-task
spec:
steps:
- name: backup
image: bitnami/kubectl:latest
script: |
#!/usr/bin/env bash
set -e
kubectl get all,configmap,secret,application,appproject -n argocd -o yaml > /workspace/argocd-resources-backup.yaml
echo "ArgoCD resources backed up"
Tạo Task `backup-vault-task.yaml`:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: backup-vault-task
spec:
params:
- name: vault-token
type: string
steps:
- name: backup
image: hashicorp/vault:latest
env:
- name: VAULT_ADDR
value: http://vault:8200
- name: VAULT_TOKEN
value: $(params.vault-token)
script: |
#!/usr/bin/env bash
set -e
vault operator raft snapshot save /workspace/vault-snapshot.bin
vault kv get -format=json secret/data/my-app > /workspace/vault-secrets-backup.json
echo "Vault backed up"
workspaces:
- name: output
workspace: shared-workspace
Kết quả mong đợi: Task có thể chạy độc lập để backup Vault.
Tạo Task `upload-to-s3-task.yaml` để upload file backup lên S3:
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: upload-to-s3-task
spec:
params:
- name: bucket-name
type: string
- name: region
type: string
workspaces:
- name: source
steps:
- name: upload
image: amazon/aws-cli:latest
script: |
#!/usr/bin/env bash
set -e
cd $(workspaces.source.path)
aws s3 sync . s3://$(params.bucket-name)/argocd-vault-backup/ --region $(params.region)
echo "Backup uploaded to S3"
workspaces:
- name: source
Kết quả mong đợi: Task có thể chạy độc lập để upload file backup lên S3.
Verify kết quả
Chạy lệnh sau để kiểm tra file backup:
ls -lh /workspace/*.yaml /workspace/*.sql /workspace/*.bin /workspace/*.json
Để test upload lên S3, ta cần cấu hình AWS credentials trong Tekton Task. Sau khi chạy Pipeline, kiểm tra bucket S3 để đảm bảo file đã được upload.
Kịch bản khôi phục hệ thống sau sự cố mất cluster
Khi cluster Kubernetes bị mất (ví dụ: do lỗi phần cứng, cloud region down), ta cần khôi phục toàn bộ hệ thống từ backup đã tạo ở phần trước.
Tại sao cần làm vậy: Đảm bảo tính liên tục của dịch vụ (business continuity) và giảm thiểu mất mát dữ liệu.
Kết quả mong đợi: Cluster mới được tạo, ArgoCD và Vault được khôi phục từ backup, ứng dụng được deploy lại và hoạt động bình thường.
Trước tiên, ta cần tạo một cluster Kubernetes mới (có thể dùng EKS, GKE, AKS, hoặc self-hosted). Đảm bảo cluster mới có cùng cấu hình network và storage như cluster cũ.
Khôi phục database của ArgoCD:
PGPASSWORD=your_password psql -h argocd-redis-postgresql -U argocd argocd -f argocd-db-backup.sql
Kết quả mong đợi: Database ArgoCD đã được khôi phục. Trạng thái của các Application đã được phục hồi.
Khôi phục các Custom Resource của ArgoCD:
kubectl apply -f argocd-resources-backup.yaml -n argocd
Kết quả mong đợi: Các Application, AppProject, ConfigMap, Secret của ArgoCD đã được tạo lại trong cluster mới.
Khôi phục Vault:
Đầu tiên, ta cần khởi động Vault trong cluster mới. Sau đó, apply snapshot:
vault operator raft snapshot restore /tmp/vault-snapshot.bin
Kết quả mong đợi: Vault đã được khôi phục. Tất cả secret và cấu hình đã được phục hồi.
Khôi phục secret từ file JSON (nếu cần):
cat vault-secrets-backup.json | jq -r '.data.data | to_entries[] | "\(.key)=\(.value)"' | xargs -I {} vault kv put secret/my-app {}
Kết quả mong đợi: Secret đã được khôi phục vào Vault.
Sau khi khôi phục ArgoCD và Vault, ta cần đảm bảo ArgoCD có thể kết nối với Vault để lấy secret. Ta cần cấu hình lại ArgoCD để nó biết đường dẫn đến Vault.
Chỉnh sửa ConfigMap của ArgoCD để thêm thông tin kết nối Vault:
kubectl edit configmap argocd-cm -n argocd
Thêm dòng sau vào phần `data`:
vault:
server: http://vault:8200
token: your-root-token
Kết quả mong đợi: ArgoCD đã được cấu hình để kết nối với Vault.
Cuối cùng, ta cần kích hoạt sync lại các Application trong ArgoCD. ArgoCD sẽ tự động sync nếu `syncPolicy.automated.selfHeal` được bật. Nếu không, ta cần chạy lệnh sync thủ công:
argocd app sync my-app
Kết quả mong đợi: ArgoCD sẽ deploy lại ứng dụng từ commit hiện tại (hoặc commit đã rollback) vào cluster mới. Ứng dụng sẽ hoạt động bình thường.
Verify kết quả
Chạy lệnh sau để kiểm tra trạng thái của Application:
argocd app get my-app
Đợi đến khi trạng thái là `Synced` và `Healthy`. Kiểm tra log của Pod để đảm bảo ứng dụng đã khởi động thành công.
Để kiểm tra Vault, chạy lệnh:
vault kv get secret/my-app
Đảm bảo secret đã được khôi phục đúng.
Điều hướng series:
Mục lục: Series: Series: Xây dựng hệ thống CI/CD an toàn và tự động hóa cho Kubernetes với ArgoCD, Tekton và HashiCorp Vault
« Phần 6: Áp dụng chính sách bảo mật và kiểm soát truy cập
Phần 8: Troubleshooting và tối ưu hiệu suất hệ thống »