Deployment Log — K3s Infrastructure on UTM VMs
Mục đích: Ghi lại toàn bộ quá trình thiết lập hạ tầng K3s trên 3 UTM VMs (Apple Silicon / arm64),
bao gồm tất cả lỗi phát sinh và cách khắc phục từng bước.
Dùng làm tài liệu tham chiếu khi cần rebuild hoặc debug môi trường.
1. Tổng quan kiến trúc
| Thành phần | Vị trí | Mô tả |
|---|---|---|
| fb-k3s-master | 192.168.64.10 |
K3s control-plane + Docker Compose host cho stateful services |
| fb-k3s-worker-1 | 192.168.64.11 |
K3s worker node |
| fb-k3s-worker-2 | 192.168.64.12 |
K3s worker node |
Stateful services (Docker Compose trên master): PostgreSQL, MongoDB, Redis, Kafka/ZooKeeper, Keycloak, OmniRoute, ClickHouse, SigNoz, LangFuse, MinIO
Stateless services (K3s/Helm trên cluster): 6 backend NestJS microservices + chatbot service
2. Chuẩn bị VMs trên UTM
2.1 Cấu hình từng VM
Tạo 3 VMs Ubuntu Server 24.04 LTS (ARM64) trên UTM với cấu hình:
| VM | Hostname | vCPU | RAM | Disk | IP tĩnh |
|---|---|---|---|---|---|
| Master | fb-k3s-master |
4 | 8 GB | 80 GB | 192.168.64.10/24 |
| Worker 1 | fb-k3s-worker-1 |
2 | 4 GB | 40 GB | 192.168.64.11/24 |
| Worker 2 | fb-k3s-worker-2 |
2 | 4 GB | 40 GB | 192.168.64.12/24 |
Network adapter: Shared Network (NAT) — dùng dải 192.168.64.x của UTM
Lý do chọn .10/.11/.12: Tránh conflict với DHCP của UTM (thường cấp từ .2–.9)
2.2 Cấu hình IP tĩnh (Netplan)
Sau khi cài Ubuntu, cấu hình IP tĩnh cho mỗi VM qua Netplan. Interface thường là enp0s1 trên UTM ARM64.
# /etc/netplan/00-installer-config.yaml (ví dụ cho master)
network:
version: 2
ethernets:
enp0s1:
dhcp4: false
addresses:
- 192.168.64.10/24
routes:
- to: default
via: 192.168.64.1
nameservers:
addresses: [8.8.8.8, 8.8.4.4]
sudo netplan apply
2.3 Cấu hình SSH và sudo passwordless
Từ Mac, copy SSH public key vào từng VM:
ssh-copy-id -i ~/.ssh/utm_k3s.pub ubuntu@192.168.64.10
ssh-copy-id -i ~/.ssh/utm_k3s.pub ubuntu@192.168.64.11
ssh-copy-id -i ~/.ssh/utm_k3s.pub ubuntu@192.168.64.12
Trên mỗi VM, cấu hình sudo không cần password (bắt buộc cho Ansible):
echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/ubuntu-nopasswd
Kiểm tra: ansible -i inventory/utm all -m shell -a "sudo whoami" → phải trả về root trên cả 3 hosts.
2.4 Tạo vault password file
echo "your-vault-password" > ~/.vault_pass
chmod 600 ~/.vault_pass
3. Ansible Vault — Secrets
Tất cả secrets được lưu trong secrets/vault.yml (encrypted bằng AES-256).
Các biến đã khởi tạo:
| Biến Vault | Mô tả | Format yêu cầu |
|---|---|---|
vault_db_admin_password |
PostgreSQL admin password | string |
vault_redis_password |
Redis main password | string |
vault_chatbot_redis_password |
Redis chatbot session store | string |
vault_chatbot_db_password |
PostgreSQL chatbot user | string |
vault_keycloak_admin_password |
Keycloak admin | string |
vault_keycloak_client_secret |
Keycloak client secret | hex string |
vault_clickhouse_password |
ClickHouse default user | alphanumeric only (dùng trong URL) |
vault_jwt_secret |
JWT signing key | min 32 chars |
vault_jwt_encryption_key |
JWT encryption key | đúng 32 chars |
vault_internal_service_secret |
X-Internal-Service header | hex string |
vault_minio_root_password |
MinIO admin password | string |
vault_langfuse_nextauth_secret |
NextAuth secret | min 32 chars |
vault_langfuse_salt |
LangFuse password salt | string |
vault_langfuse_encryption_key |
LangFuse encryption key | đúng 64 hex chars (openssl rand -hex 32) |
vault_langfuse_redis_auth |
LangFuse Redis password | string |
vault_langfuse_init_password |
LangFuse admin password | string |
vault_langfuse_public_key |
LangFuse project public key | pk-lf-... |
vault_langfuse_secret_key |
LangFuse project secret key | sk-lf-... |
vault_groq_api_key |
Groq API key | từ console.groq.com |
vault_pinecone_api_key |
Pinecone API key | từ app.pinecone.io |
Generate secrets:
openssl rand -hex 32 # 64-char hex (langfuse_encryption_key, jwt keys)
openssl rand -base64 32 # base64 random string (passwords)
4. Các lần chạy Playbook và lỗi gặp phải
Nguyên tắc: Mọi fix đều phải thực hiện trong source (playbooks/roles/docker-compose), KHÔNG sửa trực tiếp trên VM.
Lần 1 — Lỗi callback plugin và deprecated apt modules
Lệnh chạy:
ansible-playbook -i inventory/utm playbooks/site.yml --vault-password-file ~/.vault_pass
Lỗi 1: community.general.yaml callback plugin ERROR
ERROR! Invalid callback requested: community.general.yaml
Nguyên nhân: ansible.cfg dùng stdout_callback = yaml nhưng plugin community.general.yaml bị xóa khỏi phiên bản mới của community.general.
Fix — ansible.cfg:
# Trước:
stdout_callback = yaml
# Sau:
stdout_callback = default
result_format = yaml
Lỗi 2: apt_key / apt_repository deprecation trong Docker role
[DEPRECATION WARNING]: Module 'apt_key' is deprecated
Nguyên nhân: Ansible khuyến cáo không dùng apt_key từ Ubuntu 22.04+. Cần dùng gpg --dearmor theo phương pháp mới.
Fix — roles/docker/tasks/main.yml: Thay bằng shell commands:
- name: Add Docker GPG key
shell: |
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
args:
creates: /usr/share/keyrings/docker-archive-keyring.gpg
- name: Add Docker apt repository
shell: |
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
args:
creates: /etc/apt/sources.list.d/docker.list
Lần 2 — Lỗi sudo password và br_netfilter
Lỗi 3: Missing sudo password
Task failed: Missing sudo password
Nguyên nhân: Các VM chưa được cấu hình passwordless sudo.
Fix: SSH vào từng VM và chạy:
echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/ubuntu-nopasswd
(Đây là bước setup VM, không phải fix trong playbook)
Lỗi 4: net.bridge.bridge-nf-call-iptables: No such file or directory
sysctl: setting key "net.bridge.bridge-nf-call-iptables": No such file or directory
Nguyên nhân: Module kernel br_netfilter chưa được load, nên /proc/sys/net/bridge/... không tồn tại.
Fix — roles/common/tasks/main.yml: Thêm các tasks trước sysctl:
- name: Load br_netfilter kernel module
shell: modprobe br_netfilter
changed_when: false
- name: Persist br_netfilter on boot
lineinfile:
path: /etc/modules-load.d/k3s.conf
line: br_netfilter
create: yes
mode: "0644"
Lần 3 — Lỗi Ingress template và Helm timeout
Lỗi 5: Ingress rules unmarshal error
cannot unmarshal object into Go struct field IngressSpec.spec.rules of type []v1.IngressRule
Nguyên nhân: Template dùng {{- if .Values.ingress.host }} với dấu - trim whitespace, làm mất khoảng trắng cần thiết trong YAML list item, render thành -host: thay vì - host:.
Fix — helm/ingress/templates/ingress.yaml: Viết lại hoàn toàn template, đặt http: trước host: có điều kiện:
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ .Values.apiGateway.service }}
port:
number: {{ .Values.apiGateway.port }}
{{- if .Values.ingress.host }}
host: {{ .Values.ingress.host }}
{{- end }}
Lỗi 6: Helm chatbot deployment timeout
Error: context deadline exceeded (client timeout exceeded while waiting for Helm operation to complete)
Nguyên nhân: Image sangtrandev00/chatbot-service:latest chưa được push lên Docker Hub → pod ImagePullBackOff → Helm --wait timeout sau 120 giây.
Fix — roles/helm_apps/tasks/main.yml: Xóa --wait --timeout 120s khỏi cả 2 Helm deploy commands để không block playbook.
Lần 4 — Lỗi containers sau khi Docker Compose start
Lỗi 7: Keycloak restarting — JDBC H2 format error
URL format error; jdbc:h2 expected
Nguyên nhân: command: start --optimized yêu cầu image đã được build sẵn với JDBC drivers (pre-compiled). Image chuẩn của Keycloak 24 không có postgres driver pre-built, dùng H2 mặc định với --optimized.
Fix — docker-compose/stateful-stack.yml:
keycloak:
command: start # bỏ --optimized
environment:
KC_HEALTH_ENABLED: "true"
# ... các env KC_DB khác
Lỗi 8: OTel collector restarting — cluster 'cluster' not found
Requested cluster 'cluster' not found
Nguyên nhân: SigNoz OTel collector v0.102.5 cần ClickHouse có cluster tên cluster để tạo distributed tables (ReplicatedMergeTree). ClickHouse standalone chưa có cấu hình cluster.
Fix — docker-compose/config/clickhouse-cluster.xml (file mới):
Dùng Kafka ZooKeeper đang chạy sẵn (zookeeper:2181) thay vì cài thêm ZooKeeper riêng:
<clickhouse>
<zookeeper>
<node><host>zookeeper</host><port>2181</port></node>
</zookeeper>
<remote_servers replace="true">
<cluster>...</cluster> <!-- cho SigNoz OTel -->
<default>...</default> <!-- cho LangFuse v3 -->
</remote_servers>
<macros>
<shard>01</shard>
<replica>clickhouse</replica>
<cluster>cluster</cluster>
</macros>
</clickhouse>
Ghi chú: Kafka ZooKeeper và ClickHouse dùng namespace ZooKeeper khác nhau (
/kafkavà/clickhouse), không conflict.
Lỗi 9: LangFuse restarting — CLICKHOUSE_MIGRATION_URL not configured
CLICKHOUSE_MIGRATION_URL is not configured
Fix — docker-compose/observability-stack.yml: Thêm env var với đúng scheme:
CLICKHOUSE_MIGRATION_URL: clickhouse://default:${CLICKHOUSE_PASSWORD}@clickhouse:9000
Quan trọng: Phải dùng scheme
clickhouse://(native protocol, port 9000), KHÔNG phảihttp://(HTTP interface, port 8123). LangFuse dùng Go'sdatabase/sqldriver vớiclickhouse-go.
Lỗi 10: SigNoz query-service restarting — unable to open database file
unable to open database file: no such file or directory
Fix — docker-compose/observability-stack.yml: Thêm volume và env path:
query-service:
environment:
SIGNOZ_LOCAL_DB_PATH: /root/.signoz/signoz.db
volumes:
- signoz_data:/root/.signoz
Lỗi 11: arm64 vs amd64 — K3s pods exec format error
x86_64-binfmt-P: QEMU internal SIGSEGV
Nguyên nhân: Images sangtrandev00/*:latest được build cho amd64, UTM VMs chạy arm64 (Apple Silicon). QEMU user-space emulation không đủ ổn định cho Node.js.
Fix — roles/common/tasks/main.yml: Cài QEMU packages (partial fix, không đủ cho production):
- name: Install required packages
apt:
name:
- qemu-user-static
- binfmt-support
Kết luận: K3s app pods cần multi-arch images build với
docker buildx --platform linux/amd64,linux/arm64. Đây là việc của CI/CD pipeline.
Lần 5–6 — ClickHouse Keeper không start
Lỗi 12: ClickHouse Keeper config không load
Ban đầu thử dùng built-in ClickHouse Keeper (không cần ZooKeeper ngoài). Nhưng config trong config.d/cluster.xml không được load trước khi ClickHouse start.
Code: 225. DB::Exception: Can't create replicated table without ZooKeeper. (NO_ZOOKEEPER)
Fix (lúc đó): Chuyển sang dùng Kafka ZooKeeper (zookeeper:2181) thay vì built-in Keeper. Đơn giản hơn và ZooKeeper đã chạy sẵn trên cùng Docker network.
Cập nhật (Kafka KRaft migration): Kafka bỏ Zookeeper hoàn toàn (chuyển sang KRaft,
apache/kafka:4.3.1) nên ClickHouse không còn zookeeper nào để dùng ké. Đã thử lại built-in ClickHouse Keeper — lần này chạy được, verify bằngCREATE DATABASE ... ON CLUSTER clusterthành công (test vớiclickhouse/clickhouse-server:24.8-alpine, cùng version đang dùng). Khác biệt so với lần thất bại trước: filekeeper-embedded.xml(chứa<keeper_server>) vàcluster.xml(chứa<zookeeper>trỏlocalhost:9181) được mount cùng lúc vàoconfig.d/trước khi container start lần đầu — không có bước nào update config sau khi ClickHouse đã chạy. Nếu deploy lên VM thật vẫn gặp lại lỗiNO_ZOOKEEPER, khả năng cao là do thứ tự copy file/mount volume của Ansible task — kiểm traroles/obs_stack/tasks/main.ymlđảm bảo cả 2 file config được copy TRƯỚC bướcStart observability stack.
Thêm Ansible task restart ClickHouse khi config thay đổi — roles/stateful_stack/tasks/main.yml:
- name: Copy ClickHouse cluster config to master
copy: ...
register: clickhouse_config_result
- name: Restart ClickHouse if cluster config changed
shell: |
cd /home/ubuntu/stacks/football-booking
docker compose -f observability-stack.yml restart clickhouse
when: clickhouse_config_result.changed
Lý do cần task này: Docker Compose state: present không tự restart container khi nội dung bind-mounted config file thay đổi (chỉ restart khi container definition thay đổi).
Lần 7 — LangFuse cluster name conflict
Lỗi 13: LangFuse — cluster 'default' not found
error: failed to open database: code: 701, message: Requested cluster 'default' not found
CREATE TABLE schema_migrations ON CLUSTER default (...)
Nguyên nhân:
- SigNoz OTel cần cluster tên cluster
- LangFuse v3 hardcode cluster tên default trong migrations
- Config ban đầu chỉ có cluster cluster
Fix — docker-compose/config/clickhouse-cluster.xml: Định nghĩa cả hai cluster pointing đến cùng shard:
<remote_servers replace="true">
<cluster> <!-- SigNoz OTel -->
<shard><replica>...</replica></shard>
</cluster>
<default> <!-- LangFuse v3 -->
<shard><replica>...</replica></shard>
</default>
</remote_servers>
Lỗi 14: SigNoz query-service — ClickHouse authentication failed
Authentication failed: password is incorrect, or there is no user with such name
Nguyên nhân: ClickHouseUrl: tcp://clickhouse:9000/?database=signoz_traces không có credentials. ClickHouse yêu cầu password cho TCP connections.
Fix — docker-compose/observability-stack.yml:
ClickHouseUrl: "tcp://clickhouse:9000/?username=default&password=${CLICKHOUSE_PASSWORD}&database=signoz_traces"
Lần 8 — Secrets rotation: CHANGE_ME placeholders
Lỗi 15: LangFuse worker — ENCRYPTION_KEY too short
ZodError: ENCRYPTION_KEY must be 256 bits, 64 string characters in hex format
Nguyên nhân: vault_langfuse_encryption_key vẫn còn giá trị placeholder "CHANGE_ME_64_char_hex_string" (28 chars). LangFuse worker v3 dùng Zod v4 validation — phải đúng 64 hex chars.
Phát hiện thêm: Nhiều biến vault khác vẫn còn CHANGE_ME. Cần update toàn bộ.
Quy trình rotate secrets:
# 1. Generate secrets
openssl rand -hex 32 # langfuse_encryption_key (64 chars)
openssl rand -hex 16 # jwt_encryption_key (32 chars), keycloak_client_secret
openssl rand -base64 32 # passwords
# 2. Decrypt vault
ansible-vault decrypt secrets/vault.yml --vault-password-file ~/.vault_pass
# 3. Edit file và replace tất cả CHANGE_ME values
# 4. Re-encrypt
ansible-vault encrypt secrets/vault.yml --vault-password-file ~/.vault_pass
Lần 9 — PostgreSQL password rotation
Lỗi 16: Keycloak và LangFuse-web — PostgreSQL authentication failed
FATAL: password authentication failed for user "football_admin"
Nguyên nhân: Khi đổi vault_db_admin_password, .env được update với password mới. Docker Compose recreate PostgreSQL container với password mới trong env. Nhưng PostgreSQL chỉ apply POSTGRES_PASSWORD khi data directory chưa tồn tại — data directory đã có từ lần khởi tạo trước với password cũ.
Fix — roles/stateful_stack/tasks/main.yml: Thêm task sync password sau mỗi lần start PostgreSQL:
- name: Wait for PostgreSQL to be ready
shell: docker exec postgres pg_isready -U football_admin
retries: 10
delay: 3
register: pg_ready
until: pg_ready.rc == 0
changed_when: false
- name: Sync PostgreSQL admin password with vault (handles vault password rotation)
shell: |
docker exec postgres psql -U football_admin -d postgres -c \
"ALTER USER football_admin PASSWORD '{{ vault_db_admin_password }}';"
changed_when: false
Tại sao dùng
docker execthay vì PostgreSQL module? PostgreSQL trong Docker dùngtrustauth cho local unix socket connections.docker execkết nối qua unix socket không cần password cũ → idempotent.
Lần 10 — LangFuse-web OOM
Lỗi 17: LangFuse-web — JavaScript heap out of memory
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
Nguyên nhân: LangFuse v3 Next.js app cần nhiều memory hơn limit cũ (--max-old-space-size=512, container limit 768M). Heap sử dụng ~517MB trong khi limit là 512MB.
Fix — docker-compose/observability-stack.yml:
langfuse-web:
environment:
NODE_OPTIONS: "--max-old-space-size=1024" # tăng từ 512
deploy:
resources:
limits:
memory: 1.5G # tăng từ 768M
5. Trạng thái cuối — Tất cả services sau 10 lần chạy playbook
Docker Compose (master — 192.168.64.10)
| Container | Image | Port | Status |
|---|---|---|---|
postgres |
postgres:16-alpine | 5432 | ✅ healthy |
mongodb |
mongo:7 | 27017 | ✅ running |
redis |
redis:7-alpine | 6379 | ✅ healthy |
zookeeper |
cp-zookeeper:7.6.0 | internal | ✅ running |
kafka |
cp-kafka:7.6.0 | 9093 (external) | ✅ healthy |
keycloak |
keycloak:24.0.1 | 8080 | ✅ running |
omni-route |
omniroute:latest | 20128 | ✅ healthy |
clickhouse |
clickhouse-server:24.8-alpine | 8123, 9000 (localhost) | ✅ healthy |
otel-collector |
signoz-otel-collector:0.102.5 | 4317, 4318 | ✅ running |
signoz-query-service |
query-service:0.52.0 | internal | ✅ running |
signoz-frontend |
frontend:0.52.0 | 3301 | ✅ running |
langfuse-web |
langfuse:3 | 3100 | ✅ running |
langfuse-worker |
langfuse-worker:3 | internal | ✅ running |
langfuse-redis |
redis:7-alpine | internal | ✅ running |
langfuse-minio |
chainguard/minio | 9090-9091 (localhost) | ✅ running |
chatbot-redis |
redis:7-alpine | 6380 | ✅ healthy |
K3s Cluster
| Component | Status |
|---|---|
fb-k3s-master |
✅ Ready (control-plane) |
fb-k3s-worker-1 |
✅ Ready |
fb-k3s-worker-2 |
✅ Ready |
ingress-nginx-controller |
✅ Running |
| Backend services (6 pods) | ❌ QEMU SIGSEGV — cần arm64 images |
chatbot-service |
❌ ImagePullBackOff — image chưa push |
6. Vấn đề còn lại và hướng xử lý
K3s App Pods — arm64/amd64 mismatch
Vấn đề: Images được build cho linux/amd64. UTM VMs chạy Ubuntu ARM64. QEMU user-space emulation (qemu-user-static) không đủ ổn định cho Node.js production workloads — crash với SIGSEGV.
Giải pháp: Build multi-arch images trong CI/CD:
docker buildx build \
--platform linux/amd64,linux/arm64 \
--push \
-t sangtrandev00/api-gateway:latest .
Hoặc trên GitHub Actions:
- uses: docker/build-push-action@v5
with:
platforms: linux/amd64,linux/arm64
push: true
tags: sangtrandev00/api-gateway:latest
Groq và Pinecone API Keys
Còn CHANGE_ME trong vault. Cần điền trước khi chatbot hoạt động:
ansible-vault edit secrets/vault.yml --vault-password-file ~/.vault_pass
# Điền vault_groq_api_key và vault_pinecone_api_key
7. Lệnh thường dùng
Chạy toàn bộ playbook
ansible-playbook -i inventory/utm playbooks/site.yml --vault-password-file ~/.vault_pass
Kiểm tra nhanh tất cả containers
ssh -i ~/.ssh/utm_k3s ubuntu@192.168.64.10 "docker ps --format 'table {{.Names}}\t{{.Status}}' | sort"
Kiểm tra K3s pods
export KUBECONFIG=~/.kube/football-k3s.yaml
kubectl get pods -n football-booking
kubectl get nodes
Xem logs một container
ssh -i ~/.ssh/utm_k3s ubuntu@192.168.64.10 "docker logs <container_name> --tail=20"
Edit vault
ansible-vault edit secrets/vault.yml --vault-password-file ~/.vault_pass
Rotate secrets và re-apply
ansible-vault decrypt secrets/vault.yml --vault-password-file ~/.vault_pass
# Edit file
ansible-vault encrypt secrets/vault.yml --vault-password-file ~/.vault_pass
ansible-playbook -i inventory/utm playbooks/site.yml --vault-password-file ~/.vault_pass
Test Ansible connectivity
ansible -i inventory/utm all -m ping
ansible -i inventory/utm all -m shell -a "sudo whoami"
8. Các pattern quan trọng rút ra
Pattern 1: Docker Compose không tự restart khi bind-mount file thay đổi
Docker Compose chỉ recreate container khi container definition thay đổi (image, env vars, volumes list). Nếu chỉ nội dung file bind-mount thay đổi, phải restart thủ công hoặc dùng Ansible task.
- name: Copy config file
copy: ...
register: config_result
- name: Restart service if config changed
shell: docker compose -f stack.yml restart <service>
when: config_result.changed
Pattern 2: PostgreSQL password rotation
PostgreSQL chỉ apply POSTGRES_PASSWORD env var khi data directory trống. Để rotate password:
1. Update vault
2. Chạy playbook (update .env, restart container với env mới)
3. Ansible task ALTER USER sync password từ vault vào DB
Pattern 3: ClickHouse ZooKeeper sharing
Trong dev, Kafka ZooKeeper (zookeeper:2181) có thể được dùng cho ClickHouse. Cả hai dùng namespace ZooKeeper riêng (/kafka vs /clickhouse). Tiết kiệm tài nguyên, đơn giản hơn ClickHouse Keeper.
Pattern 4: LangFuse v3 cluster naming
LangFuse v3 hardcode ON CLUSTER default trong migrations. SigNoz cần cluster tên cluster. Định nghĩa cả hai trong ClickHouse:
<remote_servers replace="true">
<cluster>...</cluster>
<default>...</default>
</remote_servers>
Pattern 5: URL encoding cho ClickHouse connection strings
# ĐÚNG (native protocol):
clickhouse://default:password@clickhouse:9000
# SAI (HTTP scheme không được clickhouse-go hỗ trợ):
http://default:password@clickhouse:8123
# Nếu password có special chars, cần URL encode:
clickhouse://default:my%40password@clickhouse:9000
# → Tốt hơn: dùng alphanumeric-only passwords cho ClickHouse
Tài liệu này được tạo sau khi hoàn thành thiết lập infrastructure.
Ngày: 2026-06-20
Số lần chạy playbook: 10
Số lỗi gặp và fix: 17
CI/CD Fix Log — 2026-06-29
- Removed
_example-caller-*.ymlfrom.github/workflows/(moved todocs/workflow-examples/) - Fixed phantom GitHub Actions runs caused by
${{ github.sha }}expression inworkflow_callinput description - Set
GITOPS_TOKENsecret infootball-booking-chatbot-service-k3srepo — GitOps push step now succeeds