football-booking-infra-k3s
Infrastructure-as-Code cho hệ thống Football Booking — tự động hóa quá trình provision 4 VM (UTM, local macOS) và bootstrap cụm K3s + Docker Compose stack bằng Ansible, sau đó bàn giao việc deploy app cho ArgoCD (GitOps).
📋 Xem SERVICES.md để biết danh mục đầy đủ tất cả repo trong hệ thống (mục đích, tech stack, docs, CI/CD). 📐 Xem docs/adr/ để biết các quyết định kiến trúc đã chốt (và lý do).
Kiến trúc tổng quan (môi trường utm-local, 4 VM)
Khác với một cụm K3s "gộp" điển hình, môi trường UTM local tách stateful service và observability stack ra khỏi K3s, chạy trên 2 VM riêng bằng Docker Compose — chỉ 2 VM còn lại (master + 1 worker) thực sự chạy K3s.
┌───────────────────────────┐ ┌───────────────────────────┐
│ VM1 — k3s-master │ │ VM3 — k3s-worker │
│ 192.168.64.10 │ │ 192.168.64.12 │
│ │ │ │
│ K3s control plane │ │ K3s node — app pods │
│ ├── Traefik (bundled) │◄──┤ ├── api-gateway │
│ ├── ArgoCD │ │ ├── user/field/booking/ │
│ ├── kube-prometheus-stack│ │ │ payment/notification │
│ │ (Prometheus+Grafana)│ │ ├── chatbot-service (Py) │
│ └── Kubernetes Dashboard │ │ └── 4× Go service │
│ │ │ (document-worker, │
│ node-role: master │ │ availability-engine, │
│ workload: stateful-adj. │ │ analytics-pipeline, │
└─────────────┬──────────────┘ │ webhook-receiver) │
│ Flannel VXLAN │ node-role: worker │
│ (pod network) │ workload: stateless │
│ └───────────────┬──────────────┘
│ │
┌─────────────▼──────────────┐ ┌────────────────▼──────────────┐
│ VM2 — data-node │ │ VM4 — obs-node │
│ 192.168.64.11 │ │ 192.168.64.13 │
│ │ │ │
│ Docker Compose — stateful │ │ Docker Compose — observability│
│ ├── PostgreSQL 16 (5432) │ │ ├── ClickHouse (+ Keeper) │
│ ├── MongoDB 7 (27017) │ │ ├── SigNoz (query+frontend) │
│ ├── Redis (6379) │ │ ├── OTel Collector (4317/4318) │
│ ├── Kafka (9092/9093) │ │ ├── Loki (logs, 3101 cross-VM) │
│ ├── Keycloak (8080) │ │ ├── Tempo (traces, 3200) │
│ ├── OmniRoute LLM (20128) │ │ ├── LangFuse (LLM obs., 3100) │
│ └── MinIO (blob storage) │ │ ├── MinIO (LangFuse blob) │
│ │ │ ├── Redis (chatbot session) │
│ node-role: data │ │ ├── Kafka UI (9095) │
└──────────────────────────────┘ │ └── exporters (kafka/pg/cAd.) │
│ node-role: observability │
└────────────────────────────────┘
Luồng request: Client → Traefik (K3s bundled ingress) → api-gateway → NestJS/Go microservices → data-node (Postgres/Mongo/Redis/Kafka/Keycloak)
Luồng deploy app: git push (app repo) → CI build image → commit tag vào football-booking-gitops → ArgoCD tự sync vào K3s — Ansible không còn tự helm upgrade app nghiệp vụ (xem ADR-0002).
Luồng observability: app pods (K3s) và Traefik export traces/metrics qua OTLP tới OTel Collector trên obs-node → ClickHouse (SigNoz) + Loki + Tempo. Grafana (chạy trong K3s, namespace monitoring) trỏ datasource Prometheus (local) + Loki/Tempo (cross-node, obs-node) để xem chung một chỗ.
Tech Stack
| Layer | Tool | Vai trò |
|---|---|---|
| Orchestration | K3s v1.29.4 | Lightweight Kubernetes, 2 node K3s (1 master + 1 worker) trong tổng 4 VM |
| Provisioning | Ansible | Cài đặt OS, K3s, Docker, bootstrap toàn bộ 4 VM |
| GitOps | ArgoCD | Deploy/quản lý app từ repo football-booking-gitops — nguồn sự thật duy nhất cho manifest app |
| Secret management | Ansible Vault | Encrypt secrets trước khi commit; app pods đọc qua K8s Secret do helm_apps bootstrap |
| Container registry auth | k3s_registry role |
Cấu hình K3s pull image private từ GHCR trên toàn bộ node cluster |
| Stateful services | Docker Compose (data-node) | PostgreSQL, MongoDB, Redis, Kafka, Keycloak, OmniRoute, MinIO |
| Observability | Docker Compose (obs-node) | ClickHouse + SigNoz, Loki, Tempo, LangFuse, exporters, Kafka UI |
| In-cluster monitoring | kube-prometheus-stack + K8s Dashboard | Prometheus/Grafana/node-exporter + Kubernetes Dashboard, deploy trong namespace monitoring |
| CI/CD | GitHub Actions | Reusable workflows (cd-backend.yml, cd-chatbot.yml) + Ansible Lint/Molecule CI cho chính repo này (ci.yml) |
| Networking / Ingress | Traefik (K3s bundled) | Thay nginx-ingress cũ — role ingress cấu hình qua HelmChartConfig + deploy Ingress resources |
| CNI | Flannel VXLAN | Pod networking giữa k3s-master và k3s-worker |
| LLM routing | OmniRoute | Multi-provider router (Groq / Gemini / Anthropic), chạy trên data-node |
Cấu trúc thư mục
football-booking-infra-k3s/
├── ansible.cfg # Cấu hình mặc định Ansible (inventory, SSH key)
├── secrets/
│ └── vault.yml # ⚠ Secrets (PHẢI encrypt trước khi commit)
├── inventory/
│ ├── utm/ # Môi trường local (UTM VM, 4 node — 192.168.64.10-13)
│ │ ├── hosts.ini # groups: k3s_master, k3s_workers, data_nodes, obs_nodes
│ │ └── group_vars/
│ └── oracle/ # Môi trường Oracle Cloud (3 VM — stateful+obs gộp vào master)
│ ├── hosts.ini
│ └── group_vars/
├── playbooks/
│ ├── site.yml # Full provision từ đầu (chạy 1 lần, có --tags cho từng bước)
│ ├── argocd.yml # Cài ArgoCD lên K3s master (1 lần)
│ ├── k3s-registry.yml # Cấu hình K3s pull image private từ GHCR
│ ├── deploy.yml # Re-deploy tất cả apps (legacy/emergency — xem ADR-0002)
│ └── deploy_service.yml # Deploy thủ công 1 service cụ thể (legacy/emergency)
├── roles/
│ ├── common/ # OS baseline: swap, sysctl, UFW, packages (chạy trên cả 4 VM)
│ ├── docker/ # Cài Docker Engine + Compose plugin (master, data-node, obs-node)
│ ├── k3s_master/ # Cài K3s server, lấy node-token
│ ├── k3s_worker/ # Join cluster bằng token từ master
│ ├── k3s_registry/ # Cấu hình containerd pull image private GHCR
│ ├── kubeconfig/ # Fetch kubeconfig về máy local
│ ├── stateful_stack/ # Deploy Docker Compose stateful stack lên data-node
│ ├── obs_stack/ # Deploy Docker Compose observability stack lên obs-node
│ ├── helm_apps/ # Bootstrap namespace + K8s Secrets cho app (KHÔNG deploy app — ArgoCD làm việc đó)
│ ├── ingress/ # Cấu hình Traefik (bundled) + deploy Ingress resources
│ ├── argocd/ # Cài ArgoCD + Ingress cho ArgoCD UI
│ └── monitoring/ # kube-prometheus-stack + Kubernetes Dashboard
├── helm/
│ ├── backend/ # Chart tham chiếu cho NestJS services (đường deploy chính: football-booking-gitops)
│ ├── chatbot/ # Chart tham chiếu cho chatbot (Python)
│ └── ingress/ # Ingress routing rules — deploy trực tiếp bởi role `ingress`
├── docker-compose/
│ ├── stateful-stack.yml # PostgreSQL, MongoDB, Redis, Kafka, Keycloak, OmniRoute, MinIO
│ ├── observability-stack.yml # ClickHouse, SigNoz, Loki, Tempo, LangFuse, exporters, Kafka UI
│ ├── config/ # otel-collector, clickhouse-cluster/keeper, loki, tempo config
│ └── init-scripts/ # PostgreSQL & MongoDB init scripts
├── docs/
│ ├── adr/ # Architecture Decision Records — đọc trước khi đổi quyết định hạ tầng
│ ├── ARCHITECTURE.md / .vi.md # Chi tiết vai trò từng component
│ └── READING_GUIDE.md / .vi.md # Sequential reading & debug map
├── .github/workflows/
│ ├── ci.yml # Ansible Lint + Molecule cho chính repo này
│ ├── cd-backend.yml # Reusable: deploy 1 backend service
│ ├── cd-chatbot.yml # Reusable: deploy chatbot service
│ ├── docs-hub.yml # Gom docs/ từ các repo khác → Cloudflare Pages
│ └── README.md # Hướng dẫn setup GitHub Actions
└── scripts/
├── check-before-commit.sh # Kiểm tra security trước khi commit
├── setup-utm-network.sh # Cấu hình network UTM local
└── verify-cluster.sh # Kiểm tra cluster sau khi provision
Môi trường
| Env | Inventory | VM | Layout |
|---|---|---|---|
utm-local |
inventory/utm |
4 VM — 192.168.64.10/.11/.12/.13 | master / data-node / worker / obs-node tách riêng (xem sơ đồ trên) |
oracle |
inventory/oracle |
3 VM — 140.238.x.x (masked) | 1 master + 2 worker; stateful + observability stack gộp chạy trên master (data_nodes/obs_nodes cùng trỏ IP master) |
Ghi chú:
k3s_cluster(nhóm được K3s quản lý) chỉ gồmk3s_master+k3s_workers.data_nodesvàobs_nodeslà VM Docker Compose thuần, đứng ngoài K3s nhưng vẫn đượccommon/dockerrole provision baseline.
Quickstart
1. Yêu cầu
# macOS
brew install ansible helm kubectl
pip3 install ansible-lint
# Tạo SSH key cho cụm 4 VM UTM
ssh-keygen -t ed25519 -f ~/.ssh/utm_k3s -C "utm-k3s"
2. Cấu hình secrets
# Copy template và điền giá trị thật
cp secrets/vault.yml secrets/vault.yml.plain # chỉ để tham khảo
# Chỉnh sửa secrets/vault.yml, thay tất cả CHANGE_ME
# Sau đó encrypt:
ansible-vault encrypt secrets/vault.yml
ansible-vault encrypt roles/stateful_stack/vars/vault.yml
# Lưu vault password vào file local (không commit)
echo "your-vault-password" > ~/.vault_pass
chmod 600 ~/.vault_pass
3. Provision 4 VM từ đầu (chạy 1 lần)
# Kiểm tra kết nối SSH tới cả 4 VM
ansible all -i inventory/utm -m ping
# Provision toàn bộ — chạy tuần tự 10 bước (OS baseline → K3s → stateful → obs → ingress → monitoring)
ansible-playbook -i inventory/utm playbooks/site.yml \
--vault-password-file ~/.vault_pass
# Có thể chạy riêng từng bước bằng --tags, xem comment đầu playbooks/site.yml
# vd: chỉ deploy lại observability stack lên obs-node
ansible-playbook -i inventory/utm playbooks/site.yml --tags obs \
--vault-password-file ~/.vault_pass
4. Bootstrap ArgoCD (1 lần, sau khi K3s đã chạy)
ansible-playbook -i inventory/utm playbooks/argocd.yml
Sau bước này, mọi deploy/update app đi qua repo football-booking-gitops —
xem ADR-0002. Ansible
không tự helm upgrade app nữa; deploy.yml/deploy_service.yml chỉ còn
dùng cho trường hợp khẩn cấp.
5. Kiểm tra cluster
export KUBECONFIG=~/.kube/football-k3s.yaml
kubectl get nodes # 2 node: k3s-master + k3s-worker
kubectl get pods -n football-booking # app pods (do ArgoCD sync)
kubectl get pods -n argocd
kubectl get pods -n monitoring
./scripts/verify-cluster.sh
Truy cập nhanh:
| Service | Cách truy cập |
|---|---|
| Grafana | kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80 |
| Kubernetes Dashboard | kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard 8443:443 |
| ArgoCD UI | https://argocd.football-booking.local (qua Traefik Ingress) hoặc kubectl port-forward -n argocd svc/argocd-server 8080:443 |
| SigNoz (APM) | http://192.168.64.13:3301 |
| Kafka UI | http://192.168.64.13:9095 |
| Traefik Dashboard | kubectl port-forward -n kube-system deploy/traefik 9000:9000 |
Đầy đủ hướng dẫn (kèm token/credential) được in ra cuối bước --tags monitoring của site.yml.
CI/CD với GitHub Actions
Deploy tự động khi push code vào app repo (backend/chatbot/go-service):
git push (app repo)
→ GitHub Actions CI: build Docker image → push GHCR
→ Gọi reusable workflow từ repo này (cd-backend.yml / cd-chatbot.yml)
→ Commit image tag mới vào football-booking-gitops
→ ArgoCD phát hiện thay đổi → tự sync vào K3s (rollback tự động nếu health check fail)
Repo này tự chạy CI riêng (ci.yml): Ansible Lint (non-blocking, ~139 finding
tồn đọng chưa dọn) + Molecule test cho từng role.
Setup chi tiết: .github/workflows/README.md
Deploy thủ công (legacy/emergency — không phải đường chính, xem ADR-0002):
ansible-playbook -i inventory/oracle playbooks/deploy_service.yml \
-e "service=user-service" \
-e "image_tag=abc1234" \
--vault-password-file ~/.vault_pass
Security
# Chạy trước mỗi lần git commit
./scripts/check-before-commit.sh
Script kiểm tra:
- Vault files đã encrypt chưa
- Không có private key content trong tracked files
- Không có hardcoded passwords
- Không có kubeconfig hoặc .vault_pass bị staged
Quan trọng:
secrets/vault.ymlphải luôn ở dạng encrypted ($ANSIBLE_VAULT;1.1;AES256) trước khi commit. Xem hướng dẫn chi tiết trongdocs/ARCHITECTURE.md.
Tài liệu
| File | Nội dung |
|---|---|
docs/adr/ |
Architecture Decision Records — quyết định hạ tầng đã chốt + lý do (đọc trước khi đổi CNI, ingress, secret management, ranh giới với repo khác...) |
docs/ARCHITECTURE.md |
Architecture guide (English) |
docs/ARCHITECTURE.vi.md |
Kiến trúc chi tiết, vai trò từng component (Tiếng Việt) |
docs/READING_GUIDE.md |
Sequential reading & debug map (English) |
docs/READING_GUIDE.vi.md |
Hướng dẫn đọc tuần tự + debug map (Tiếng Việt) |
.github/workflows/README.md |
Setup GitHub Actions CI/CD |
Related Repositories
Xem đầy đủ + mô tả trong SERVICES.md. Tóm tắt:
| Repo | Mô tả |
|---|---|
football-booking-backend-k3s |
NestJS microservices: api-gateway, user, field, booking, payment, notification |
football-booking-chatbot-service-k3s |
Python/FastAPI chatbot — Groq SDK + RAG (Pinecone) |
football-booking-go-service-k3s |
4 Go service (document-worker, availability-engine, analytics-pipeline, webhook-receiver) giao tiếp qua Kafka |
football-booking-gitops |
GitOps source of truth — ArgoCD Applications/ApplicationSets + Helm charts thật deploy vào cluster |
football-booking-terraform |
Config-as-code cho resource đã có REST API riêng (Postgres role, Keycloak realm, Kafka topic, Grafana dashboard, MinIO bucket, K8s NetworkPolicy...) |