Chuyển tới nội dung

Vibe Code & Triển Khai HA-Master trên ZeroTrust

Vibe Code & Triển Khai HA-Master trên Cloudflare ZeroTrust – Dien Do
https://haproxy-gui.diendo.pro.vn
3
Services chạy
6
Bước triển khai
10
Vấn đề xử lý
1
Ngày hoàn thành

1. Ý Tưởng Ban Đầu

Quản lý nhiều server HAProxy thủ công qua SSH là công việc nhàm chán và dễ sai. Mỗi lần thay đổi config phải SSH từng con, không có lịch sử, không có rollback, không biết node nào đang chạy version nào.

HA-Master ra đời để giải quyết bài toán đó: hệ thống quản lý HAProxy tập trung theo mô hình Master–Agent, hoàn toàn tự động, có versioning và audit log đầy đủ.

Nguyên tắc cốt lõi: Config nằm trong DB (source of truth), không trên node. Agent luôn initiate kết nối ra ngoài — Master không bao giờ SSH vào node. Mọi deployment đều idempotent dựa trên config hash.

2. Kiến Trúc Hệ Thống

Toàn bộ system chạy trên Kubernetes. Frontend (React + nginx) nhận request từ Cloudflare Tunnel, proxy /api//auth/ vào Master. Master giao tiếp với Agent trên các HAProxy node qua gRPC mTLS.

☁ Cloudflare ZeroTrusthaproxy-gui.diendo.pro.vn
HTTPS Tunnel
Frontend · React + nginxKubernetes NodePort :30800
/api/ + /auth/ proxy
Master · Go API + gRPCKubernetes NodePort :30880
▪ PostgreSQL 16nfs-storage 10Gi PVC
gRPC mTLS
lb-prod-01
lb-prod-02
lb-stg-01

3. Vibe Coding Với AI

Toàn bộ codebase được xây dựng theo phong cách vibe coding — mô tả yêu cầu bằng ngôn ngữ tự nhiên, AI generate, review và điều chỉnh theo kiến trúc đã định trong CLAUDE.md.

Key là define constraints trước: architectural decisions, system boundaries, coding guidelines đều được ghi rõ. AI không tự ý thay đổi communication model hay thêm dependency không cần thiết.

Stack

BackendGo 1.25, Echo, pgx/v5, gRPC, golang-migrate
FrontendReact 18 + TypeScript, Vite, TanStack Query, shadcn/ui
DatabasePostgreSQL 16, pgcrypto, nfs-storage PVC
CommsgRPC over HTTP/2 với mTLS, Agent-initiated outbound
ContainerDocker multi-stage build, 3 images: master, agent, frontend
GitOpsGitLab CI + ArgoCD, Kustomize, auto image tag update

Cấu trúc thư mục

haproxy-gui/
├── cmd/
│   ├── master/main.go     — Master service entrypoint
│   └── agent/main.go      — Agent service entrypoint
├── internal/
│   ├── master/            — HTTP handlers, gRPC server
│   ├── agent/             — Agent logic, heartbeat
│   ├── store/             — DB layer (pgx)
│   ├── engine/            — Deployment engine (idempotent)
│   ├── auth/              — JWT + refresh token
│   └── crypto/            — AES-256-GCM cert encryption
├── frontend/src/
│   ├── pages/             — configs, deployments, certs, nodes
│   └── lib/api-client.ts  — typed API client
├── manifests/             — Kubernetes manifests (Kustomize)
├── migrations/            — PostgreSQL migrations
├── Dockerfile.master / Dockerfile.agent / Dockerfile.frontend
└── .gitlab-ci.yml

4. CI/CD Pipeline (GitLab)

GitLab CI tự động build 3 Docker images khi có code push. Mỗi image được tag bằng $CI_COMMIT_SHORT_SHA. Deploy stage dùng Kustomize để update tag trong kustomization.yaml rồi push lại repo — ArgoCD pick up và sync.

git push
Code
test
go test/vet
build
docker build
3 images
push
registry push
SHA + latest
deploy
kustomize
git push
sync
ArgoCD
kubectl apply
.gitlab-ci.yml — deploy stage
deploy:
  stage: deploy
  image: alpine/git
  script:
    - cd manifests
    - kustomize edit set image $IMAGE_MASTER:$CI_COMMIT_SHORT_SHA
    - kustomize edit set image $IMAGE_AGENT:$CI_COMMIT_SHORT_SHA
    - kustomize edit set image $IMAGE_FRONTEND:$CI_COMMIT_SHORT_SHA
    - git commit -m "chore: update image tags [skip ci]"
    - git push origin HEAD:$CI_DEFAULT_BRANCH
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

5. Kubernetes Deployment

Cluster 3 nodes tại 10.100.1.21/22/23. Trước khi deploy cần bootstrap 3 Kubernetes secrets thủ công.

  1. Registry credentials

    Pull images từ private registry registry.perfectkey.vn với robot account

    kubectl create secret docker-registry ha-registry-creds \
      --docker-server=registry.perfectkey.vn \
      --docker-username='robot$pkm+web' \
      --docker-password='<token>' \
      -n ha-master
  2. App secrets

    DATABASE_URL, JWT_SECRET, CERT_ENCRYPTION_KEY (AES-256-GCM), POSTGRES_PASSWORD

    kubectl create secret generic ha-master-secrets \
      --from-literal=DATABASE_URL='postgres://hamaster:pass@postgres:5432/hamaster' \
      --from-literal=JWT_SECRET='<32-char-random>' \
      --from-literal=CERT_ENCRYPTION_KEY='<32-byte-hex>' \
      --from-literal=POSTGRES_PASSWORD='<db-pass>' \
      -n ha-master
  3. mTLS certificates

    CA cert/key + Master server cert — phải generate trong cùng một session để tránh mismatch.

    ⚠ Lưu ý: ca.crt và ca.key từ 2 session khác nhau sẽ gây lỗi tls: private key does not match public key. Luôn generate trong một lần. Trên Windows Git Bash cần MSYS_NO_PATHCONV=1.
    MSYS_NO_PATHCONV=1 openssl req -x509 -newkey rsa:4096 -days 3650 \
      -keyout ca.key -out ca.crt -nodes -subj "/CN=ha-master-ca"
    
    MSYS_NO_PATHCONV=1 openssl req -newkey rsa:4096 -nodes \
      -keyout master.key -out master.csr -subj "/CN=master"
    
    MSYS_NO_PATHCONV=1 openssl x509 -req -in master.csr \
      -CA ca.crt -CAkey ca.key -CAcreateserial \
      -out master.crt -days 3650
    
    kubectl create secret generic ha-tls-certs \
      --from-file=ca.crt --from-file=ca.key \
      --from-file=master.crt --from-file=master.key \
      -n ha-master
  4. Apply Kustomize manifests

    Frontend dùng nginx serve SPA và reverse proxy API về Master service nội bộ.

    nginx.conf — SPA + reverse proxy
    location /api/ {
        proxy_pass http://master-http.ha-master.svc.cluster.local/api/;
        proxy_read_timeout 600s;
    }
    location /auth/ {
        proxy_pass http://master-http.ha-master.svc.cluster.local/auth/;
    }
    location / {
        try_files $uri $uri/ /index.html;  # SPA fallback
    }

6. Cài Đặt ArgoCD

ArgoCD watch repo GitLab, tự đồng bộ Kubernetes khi kustomization.yaml thay đổi. Chạy ở chế độ insecure — TLS terminate tại Cloudflare Tunnel, không cần self-signed cert bên trong.

kubectl create namespace argocd
kubectl apply -n argocd \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Expose UI qua NodePort 32141
kubectl patch svc argocd-server -n argocd \
  -p '{"spec":{"type":"NodePort","ports":[{"name":"https","port":443,"targetPort":8080,"nodePort":32141}]}}'

# Tắt TLS nội bộ — Cloudflare xử lý HTTPS bên ngoài
kubectl patch configmap argocd-cmd-params-cm -n argocd \
  -p '{"data":{"server.insecure":"true"}}'
kubectl rollout restart deployment argocd-server -n argocd

# Lấy password admin
kubectl get secret -n argocd argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d
ArgoCD Application — GitOps config
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: haproxy-ui
  namespace: argocd
spec:
  source:
    repoURL: https://git.perfectkey.vn/pkmx/haproxy-ui.git
    targetRevision: master
    path: manifests
  destination:
    server: https://kubernetes.default.svc
    namespace: ha-master
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

7. Expose Qua Cloudflare ZeroTrust

Cloudflare Tunnel cho phép expose ứng dụng ra internet không cần public IP, không cần mở firewall port. Chỉ cần cloudflared agent kết nối outbound đến Cloudflare edge.

Cấu hình trên Cloudflare Dashboard

Vào Zero Trust → Access → Tunnels → chọn tunnel → Public Hostname → thêm route:

  • Subdomain: haproxy-gui
  • Domain: diendo.pro.vn
  • Service: http://frontend.ha-master.svc.cluster.local:80

Hoặc deploy cloudflared trực tiếp trong cluster để tunnel đi qua K8s DNS:

cloudflared-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cloudflared
  namespace: cloudflared
spec:
  replicas: 2  # HA — 2 tunnel instances
  template:
    spec:
      containers:
        - name: cloudflared
          image: cloudflare/cloudflared:latest
          args: [tunnel, --config, /etc/cloudflared/config.yaml, run]
---
# ConfigMap config.yaml
tunnel: <tunnel-id>
credentials-file: /etc/cloudflared/credentials.json
ingress:
  - hostname: haproxy-gui.diendo.pro.vn
    service: http://frontend.ha-master.svc.cluster.local:80
  - service: http_status:404
Gợi ý: Cloudflare ZeroTrust thay thế hoàn toàn Ingress Controller + cert-manager. HTTPS miễn phí, không cần public IP, không cần LoadBalancer.

8. Kết Quả

Sau khoảng 1 ngày vibe coding + deploy, hệ thống đang chạy ổn định trên cluster 3 nodes.

# kubectl get pods -n ha-master
NAME                      READY   STATUS    RESTARTS   AGE
frontend-c6ccfd7d-grcf9   1/1     Running   0          2h
master-85fc9f6979-chfxs   1/1     Running   0          2h
postgres-0                1/1     Running   0          2h

# curl http://10.100.1.22:30880/health
{"status":"ok"}

GitOps flow hoạt động:

push
git push
CI
GitLab CI
test + build
push
Registry
docker SHA
update
Git Repo
kustomize
sync
ArgoCD
auto detect
apply
K8s Cluster
rolling update
Demo: Truy cập https://haproxy-gui.diendo.pro.vn — Login: [email protected] / Admin@123

9. Vấn Đề Gặp Phải & Cách Xử Lý

Vấn đề Nguyên nhân Giải pháp Type
go.mod requires go 1.25CI dùng golang:1.23Đổi CI image sang golang:1.25-alpineCI
PVC Pending mãiKhông có StorageClassThêm storageClassName: nfs-storageK8s
tls: private key mismatchCA cert/key từ 2 session khác nhauRegenerate certs trong 1 session, dùng MSYS_NO_PATHCONV=1TLS
Image pull từ docker.ioKustomize không apply tag đúngHardcode full registry URL vào manifestK8s
NodePort 30080 conflictWooCommerce đang dùng cổng đóĐổi sang NodePort 30880K8s
Liveness probe restart loopProbe timeout < startup time (~55s)Bỏ liveness probe, giữ readinessK8s
/health trả 404Old image chưa có endpointBuild lại image, thêm handler Go, push thủ côngGo
robot$pkm+web encoding lỗiPowerShell escape ký tự $Dùng Python chr(36) thay vì shell expansionOps
Docker context saidockerDesktopLinuxEngine pipe lỗidocker context use defaultDocker
Build context 252MBnode_modules không bị excludeTạo Dockerfile.frontend.dockerignoreDocker

10. Bài Học Rút Ra

01

Define constraints trước khi vibe code. CLAUDE.md với architectural decisions rõ ràng giúp AI không đi lạc, không tự ý thay đổi communication model.

02

Secrets là điểm dễ sai nhất. mTLS certs phải generate đồng bộ. Encoding ký tự đặc biệt trong password cần cẩn thận qua nhiều shell layer.

03

GitOps + ArgoCD = deploy không lo. Một khi pipeline setup xong, chỉ cần git push là cluster tự đồng bộ. Zero manual kubectl.

04

Cloudflare ZeroTrust thay Ingress + cert-manager. Đơn giản hơn, không cần public IP, HTTPS miễn phí, không cần LoadBalancer.

05

NodePort đủ dùng cho internal tools. Không cần setup LoadBalancer phức tạp khi đã có tunnel.

06

Vibe coding nhanh nhưng cần verify từng bước. Build → test → check logs → next. Đừng giả sử AI generate đúng 100% lần đầu.

Liên hệ