Vấn đề thực tế: Lệch cấu hình cụm máy chủ sau sự cố mất điện và nỗi ám ảnh “ClickOps”
Mùa đông năm ngoái, một sự cố nhảy aptomat đã làm mất điện toàn bộ cụm ba node Lenovo ThinkCentre M720q tân trang đang chạy K3s của tôi. Khi các node khởi động lại, bốn trong số mười hai dịch vụ cốt lõi không thể hoạt động.
Các volume Longhorn từ chối remount, các route ingress của Traefik biến mất hoàn toàn, còn phiên bản DNS Pi-hole thì kẹt cứng ở trạng thái CrashLoopBackOff. Tôi đã mất bốn tiếng đồng hồ liên tục rà soát lịch sử lệnh bash trên hai chiếc laptop, cố gắng chắp vá lại các tinh chỉnh kubectl edit ngẫu hứng và những tham số Helm không hề được ghi chép mà tôi từng chạy lúc 1 giờ sáng cách đó hai tuần.
Hầu hết người chơi homelab đều từng va phải bức tường này. Bạn dựng một cluster mới tinh với đầy dự định chỉn chu. Nhưng rồi sự tiện lợi nhất thời cuốn bạn đi. Bạn sửa nhanh một biến môi trường trực tiếp trên pod đang chạy. Bạn gõ bừa một lệnh helm install chưa được lưu vết để thử nghiệm media server. Bạn để mặc các file manifest nằm rải rác trong thư mục ~/k8s-test tạm bợ trên máy trạm. Khoảnh khắc phần cứng gặp sự cố, trạng thái thực tế của cluster chẳng còn khớp với bất kỳ thứ gì được lưu trên đĩa. Muốn dựng lại, bạn chỉ còn cách làm lại từ đầu.
Phân tích nguyên nhân gốc rễ: Vì sao triển khai mệnh lệnh (Imperative) lại đổ vỡ
Quản lý cluster theo cách tiếp cận mệnh lệnh (imperative) biến các node của bạn thành những “thú cưng” mong manh. Việc thay đổi trực tiếp bỏ qua hoàn toàn hệ thống kiểm soát phiên bản (source control), kéo theo ba rủi ro kiến trúc nghiêm trọng:
- Trôi dạt cấu hình trong âm thầm (Silent Configuration Drift): Một bản vá nóng áp dụng trực tiếp lên deployment đang chạy sẽ không để lại bất kỳ dấu vết kiểm toán (audit trail) nào. Ba tuần sau, chẳng ai còn nhớ lý do vì sao giới hạn bộ nhớ lại được tăng từ 256Mi lên 1Gi.
- Điểm nghẽn lỗi đơn lẻ từ máy trạm (Workstation SPOF): Nếu các file YAML phục vụ vận hành chỉ nằm trên ổ cứng NVMe của chiếc máy tính bàn chính, toàn bộ cấu trúc cluster của bạn sẽ phụ thuộc hoàn toàn vào sự sống còn của chiếc máy trạm đó.
- Rủi ro bảo mật từ mô hình CI/CD dạng Push: Sử dụng các pipeline như GitHub Actions để đẩy manifest từ ngoài vào buộc bạn phải mở port 6443 trên router gia đình, cấu hình Dynamic DNS, hoặc xoay xở với các tunnel Cloudflare hay WireGuard kém ổn định.
Theo thời gian, cluster của bạn biến thành một chiếc hộp đen bí ẩn mà không một ai dám khởi động lại.
So sánh các mô hình triển khai cho K3s Bare-Metal
Trước khi quyết định lựa chọn một controller GitOps chuyên dụng, tôi đã đánh giá ba phương thức phổ biến mà người vận hành homelab thường dùng để triển khai manifest:
1. Dùng lệnh Kubectl và Helm trực tiếp từ Terminal
Thực thi lệnh kubectl apply -f ./manifests từ terminal mang lại phản hồi tức thì trong quá trình thử nghiệm cục bộ. Tuy nhiên, sau giai đoạn cài đặt ban đầu, cách này nhanh chóng bộc lộ hạn chế. Terminal hoàn toàn không có khả năng phát hiện trôi dạt cấu hình (drift detection). Nếu một script cập nhật hay một operator bị lỗi làm biến đổi định nghĩa dịch vụ của bạn, thư mục cục bộ của bạn sẽ không có cách nào biết được.
2. Pipeline đẩy từ bên ngoài (GitHub Actions / GitLab CI)
Trong các môi trường đám mây như AWS EKS, các runner do nhà cung cấp quản lý có thể triển khai trực tiếp vào VPC của bạn. Nhưng ở homelab, điều đó đồng nghĩa với việc bạn phải mở ingress từ bên ngoài vào control plane của Kubernetes. Tệ hơn nữa, nếu pipeline báo thành công nhưng pod lại bị crash mười phút sau đó vì lỗi tràn bộ nhớ (OOMKilled), công cụ CI bên ngoài vẫn đánh dấu pipeline là xanh mướt rồi bỏ qua.
3. GitOps nội cụm (Argo CD)
Argo CD đảo ngược hoàn toàn mô hình mạng này. Một agent chạy ngay bên trong cluster của bạn và liên tục theo dõi một Git repository từ xa. Cứ mỗi ba phút một lần—hoặc ngay lập tức thông qua webhook—nó sẽ so sánh các manifest YAML đã commit với trạng thái thực tế đang chạy bên trong etcd. Khi bạn commit thay đổi, Argo CD sẽ tự động kéo chúng về từ bên trong ra. Cổng 6443 trên tường lửa của bạn vẫn được đóng kín an toàn.
Từng bước thực hiện: Cài đặt Argo CD trên K3s
Bộ ứng dụng cốt lõi của Argo CD chỉ tiêu tốn khoảng 350Mi RAM cho toàn bộ API server, repo server và controller. Mức tiêu thụ này hoàn toàn vừa vặn ngay cả trên cấu hình khiêm tốn 8GB RAM.
Bước 1: Cài đặt Argo CD
Tạo một namespace riêng biệt và tải các manifest chính thức từ upstream:
# Tạo namespace riêng
kubectl create namespace argocd
# Áp dụng các manifest triển khai phiên bản ổn định (stable)
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Chờ 5 pod controller chính chuyển sang trạng thái Running
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=argocd-server -n argocd --timeout=120s
Lấy mật khẩu admin được tạo tự động:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
Chuyển tiếp cổng (port-forward) 8080 để truy cập web dashboard an toàn trên máy cục bộ của bạn:
kubectl port-forward svc/argocd-server -n argocd 8080:443
Truy cập https://localhost:8080, bỏ qua cảnh báo chứng chỉ TLS tự ký (self-signed), và đăng nhập với tài khoản admin.
Bước 2: Cấu trúc Git Repository của bạn
Khởi tạo một repository sạch sẽ trên GitHub, GitLab, hoặc một instance Gitea tự host. Việc nhóm các ứng dụng vào các thư mục con chuyên biệt giúp các dependency luôn được cô lập:
homelab-gitops/
├── apps/
│ ├── whoami/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ └── monitoring/
│ ├── prometheus.yaml
│ └── grafana.yaml
└── bootstrap/
└── whoami-app.yaml
Thêm nội dung sau vào file apps/whoami/deployment.yaml với một web server gọn nhẹ để kiểm thử:
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
namespace: default
labels:
app: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:v1.10.1
ports:
- containerPort: 80
resources:
limits:
memory: 64Mi
cpu: 100m
---
apiVersion: v1
kind: Service
metadata:
name: whoami-service
namespace: default
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
selector:
app: whoami
Commit và đẩy các thay đổi này lên nhánh main của bạn.
Bước 3: Tạo Manifest khai báo Application
Hãy hạn chế tối đa việc thao tác click trên giao diện web để tạo ứng dụng. Quản lý chính Argo CD bằng YAML sẽ giúp quy trình khôi phục sau thảm họa (disaster recovery) có thể tái lặp chuẩn xác bất kỳ lúc nào. Lưu manifest này thành file bootstrap/whoami-app.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: whoami-service
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: 'https://github.com/your-username/homelab-gitops.git'
targetRevision: HEAD
path: apps/whoami
destination:
server: 'https://kubernetes.default.svc'
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Hai chỉ thị sau đây giúp cấu hình này hoàn toàn tự vận hành:
prune: truechỉ định cho Argo CD tự động xóa tài nguyên thực tế trên cluster khi bạn xóa bỏ file YAML của chúng khỏi Git.selfHeal: truetự động đưa cluster trở về đúng trạng thái khai báo bất cứ khi nào có ai đó can thiệp chỉnh sửa tài nguyên thủ công bằngkubectl.
Triển khai manifest:
kubectl apply -f bootstrap/whoami-app.yaml
Bước 4: Kiểm chứng tính năng tự động khắc phục sai lệch (Drift Correction)
Chỉ trong vài giây, giao diện Argo CD sẽ hiển thị whoami-service ở trạng thái Synced và Healthy.
Bây giờ, hãy thử mô phỏng một sự thay đổi ngoài ý muốn bằng cách scale deployment trực tiếp từ terminal:
kubectl scale deployment whoami --replicas=6
Kiểm tra các pod ngay lập tức bằng lệnh kubectl get pods -l app=whoami. Bạn có thể sẽ thấy bốn pod bổ sung được khởi tạo trong thoáng chốc. Nhưng ngay sau đó, Argo CD sẽ lập tức can thiệp. Nó phát hiện sự chênh lệch giữa số lượng thực tế (6) và số lượng mục tiêu trong Git repository (2), lập tức chấm dứt các bản sao thừa và khóa deployment trở lại đúng con số 2. Kho lưu trữ Git của bạn luôn luôn nắm quyền kiểm soát tuyệt đối.

