Files
pos-system/docs/audit/devops.md

11 KiB

DevOps Audit Report — GoodGo POS Platform

Auditor: DevOps Engineer (TechBi Company) Date: 2026-03-20 Scope: Docker configs, CI/CD pipelines, Kubernetes manifests, health checks, observability Branch: master


Executive Summary

Infrastructure cơ bản đã được triển khai tốt: multi-stage Docker builds, non-root container users, health probes trên tất cả K8s deployments, và CI/CD pipeline có environment approval cho production. Tuy nhiên có 2 vấn đề critical cần fix ngay trước khi go-live: production K8s manifests hardcode :latest image tag, và Alertmanager chưa được cấu hình dẫn đến toàn bộ alert rules không có đích nhận.


Critical Issues

CRIT-1: Production K8s manifests hardcode :latest image tag

File: deployments/production/kubernetes/iam-service.yaml:45

image: goodgo/iam-service-net:latest  # WRONG

Impact: Nếu ai chạy kubectl apply -f trực tiếp (không qua pipeline), pod sẽ pull :latest — không reproducible, không rollback được. Mâu thuẫn hoàn toàn với nguyên tắc "NEVER use :latest tag in production" trong CLAUDE.md và logic pipeline deploy-production.yml:327.

Affected files: Cần kiểm tra tất cả files trong deployments/production/kubernetes/ — khả năng cao tất cả service manifests đều bị.

Fix: Dùng placeholder IMAGE_TAG và sed-replace trong pipeline, hoặc dùng Kustomize/Helm để inject SHA tag.


CRIT-2: Alertmanager không được cấu hình — toàn bộ alert rules im lặng

File: infra/observability/prometheus/prometheus.yml:29

alerting:
  alertmanagers:
    - static_configs:
        - targets: []   # EMPTY — no alerts will fire

Impact: Toàn bộ alert-rules.ymlrules/service-alerts.yml định nghĩa đúng (ServiceDown, HighErrorRate, HighLatencyP95...) nhưng không có receiver nào. Production incident xảy ra sẽ không có ai được notify.

Fix: Deploy Alertmanager và uncomment targets: ['alertmanager:9093']. Configure receivers (Slack/PagerDuty/email) cho ít nhất kênh critical.


CRIT-3: docker-build.yml push :latest tag lên Docker Hub từ branch main

File: .github/workflows/docker-build.yml:99-103

if [ "$BRANCH" = "main" ]; then
  echo "tags=${IMAGE}:latest,${IMAGE}:${SHA}" >> $GITHUB_OUTPUT

Impact: :latest tag bị overwrite mỗi lần push lên main. Mâu thuẫn với deploy-production.yml (dùng SHA). Tạo race condition nếu pipeline chạy song song. Tag :latest trên Docker Hub không ổn định.

Fix: Bỏ :latest khỏi production tags. Chỉ dùng ${SHA} + production mutable tag nếu cần.


Warnings

WARN-1: redis-exporter được Prometheus scrape nhưng không tồn tại trong docker-compose

File: infra/observability/prometheus/prometheus.yml:132

- job_name: 'redis'
  static_configs:
    - targets: ['redis-exporter:9121']  # service không tồn tại

deployments/local/docker-compose.yml không có service redis-exporter. Prometheus sẽ báo target missing liên tục. Cần thêm oliver006/redis_exporter vào compose hoặc xóa scrape job này.


WARN-2: Nhiều microservices trong docker-compose nhưng không có trong CI/CD pipeline

Missing từ deploy-staging.ymldeploy-production.yml:

  • chat-service-net, social-service-net, mining-service-net, mission-service-net
  • promotion-service-net, booking-service-net (staging), membership-service-net
  • Toàn bộ 5 ads services (ads-manager, ads-serving, ads-billing, ads-tracking, ads-analytics)
  • mkt-facebook, mkt-whatsapp, mkt-x, mkt-zalo

15+ services không có automated deployment. Phải deploy thủ công.


WARN-3: pr-checks.yml chỉ kiểm tra Node.js — không có .NET build/test

File: .github/workflows/pr-checks.yml

PR checks chỉ chạy pnpm lint, pnpm typecheck, pnpm build. Không có step nào build hoặc test .NET services. Broken .NET code có thể merge vào branch mà không bị phát hiện.

Fix: Thêm matrix build cho .NET services, hoặc ít nhất dotnet build cho các service thay đổi trong PR.


WARN-4: Redis deploy trong K8s là single instance — SPOF

File: deployments/staging/kubernetes/redis.yaml:14

replicas: 1

Redis là SignalR backplane cho chat-service, fnb-engine, mining-service. Redis down = toàn bộ real-time features down. Cần Redis Sentinel (HA) hoặc migrate sang Redis Cluster.


WARN-5: Không có Kubernetes NetworkPolicy

Không tìm thấy NetworkPolicy manifest nào trong deployments/staging/kubernetes/ hay deployments/production/kubernetes/. Theo principle least-privilege, mặc định tất cả pods có thể communicate với nhau (và ra internet nếu không có egress restriction).

Fix: Thêm default-deny NetworkPolicy + whitelist explicit inter-service communication.


WARN-6: MinIO dùng credentials mặc định trong local dev

File: deployments/local/docker-compose.yml:78-79

MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin123

Credentials mặc định well-known. Dù là local dev, nên dùng .env file để tránh habit bad practice. Thêm vào .gitignore nếu chưa có.


WARN-7: Distributed tracing (Jaeger) bị comment out

File: deployments/local/docker-compose.yml:1230-1241

Jaeger config được comment out. Không có distributed tracing cho request correlation cross-service. Với 26+ microservices, đây là gap observability nghiêm trọng khi debug production issues.

Fix: Enable Jaeger hoặc dùng Grafana Tempo (đã có Loki/Grafana stack).


WARN-8: Traefik Dashboard exposed không có authentication

File: deployments/local/docker-compose.yml:121

- "--api.insecure=true"

Traefik dashboard (http://localhost:8080) accessible không cần auth. OK cho local dev nhưng cần đảm bảo api.insecure=false ở staging/production (K8s ingress không expose dashboard port nên staging OK, nhưng cần verify production Traefik config).


WARN-9: storage-service naming inconsistency

Trong docker-compose.yml service được gọi là storage-service (line 152) nhưng build context là ../../services/storage-service-net. Trong Traefik routes và K8s manifests cũng không nhất quán. Cần chuẩn hóa.


WARN-10: Jwt__RequireHttpsMetadata=false trong staging configmap

File: deployments/staging/kubernetes/configmap.yaml:21

Jwt__RequireHttpsMetadata: "false"

Staging đã có TLS (cert-manager + letsencrypt), nhưng JWT validation không require HTTPS. Nếu token bị intercept qua HTTP (misconfigured client), không bị phát hiện.


Improvements

IMP-1: Áp dụng GitOps với ArgoCD

Hiện tại CI/CD dùng kubectl apply trực tiếp từ GitHub Actions. Recommend deploy ArgoCD và dùng GitOps: Git là source of truth, ArgoCD tự sync K8s state. Lợi ích: drift detection, automatic rollback, audit trail, không cần expose KUBECONFIG trong GitHub Secrets.


IMP-2: Thêm PodDisruptionBudget cho production services

Hiện chưa có PDB. Khi node drain/upgrade, tất cả replicas của một service có thể bị terminate cùng lúc. Với maxUnavailable: 0 trong RollingUpdate — đây là bảo vệ khi rolling update, nhưng không bảo vệ khi voluntary disruption.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: iam-service-pdb
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: iam-service

IMP-3: Pin tất cả Docker image versions trong docker-compose

Hiện tại minio/minio:latest (line 74), grafana/grafana:10.3.1 (OK), prom/prometheus:v2.51.0 (OK).

Cần pin MinIO: minio/minio:RELEASE.2024-03-15T01-07-19Z hoặc version cụ thể.


IMP-4: Thêm health check endpoint /health/ready cho web-client-tpos-net

File: deployments/local/docker-compose.yml:1375

test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"]

Blazor WASM app chỉ có /health (basic), không có readiness probe riêng. K8s sẽ gửi traffic trước khi app thực sự ready.


IMP-5: Thêm image vulnerability scanning vào CI pipeline

Không có bước nào trong .github/workflows/ scan Docker images cho CVE. Recommend thêm aquasecurity/trivy-action sau build step.


IMP-6: Migrate Grafana credentials sang K8s Secret

File: deployments/local/docker-compose.yml:1278

GF_SECURITY_ADMIN_PASSWORD=admin

Password admin là default. Cần rotate và inject qua Secret, không hardcode trong compose file.


IMP-7: Bổ sung scrape targets còn thiếu trong Prometheus

prometheus.yml chỉ scrape 8 services trong khi docker-compose có 20+. Missing: promotion-service, booking-service, membership-service, social-service, mining-service, mission-service, fnb-engine, wallet-service, ads-*.


IMP-8: Thêm Kubernetes Secrets External Operator cho production

secrets.yaml.example note "Option 3: Use sealed-secrets or external-secrets operator (recommended for production)" nhưng chưa implement. Nên dùng External Secrets Operator với HashiCorp Vault hoặc AWS Secrets Manager.


Action Items (Prioritized)

Priority Issue File Effort
P0 Fix production K8s :latest image tags deployments/production/kubernetes/*.yaml 2h
P0 Configure Alertmanager + receivers infra/observability/prometheus/prometheus.yml 4h
P0 Remove :latest push từ docker-build.yml .github/workflows/docker-build.yml:100 30m
P1 Thêm redis-exporter vào docker-compose deployments/local/docker-compose.yml 1h
P1 Thêm .NET build/test vào pr-checks.yml .github/workflows/pr-checks.yml 2h
P1 Thêm NetworkPolicy manifests deployments/*/kubernetes/ 4h
P1 Mở rộng CI/CD pipelines cho missing services .github/workflows/deploy-*.yml 8h
P2 Deploy Alertmanager + PagerDuty/Slack infra/observability/ 4h
P2 Redis HA (Sentinel) cho staging/production deployments/*/kubernetes/redis.yaml 4h
P2 Enable distributed tracing (Jaeger/Tempo) deployments/local/docker-compose.yml 2h
P2 Thêm PodDisruptionBudget cho production deployments/production/kubernetes/ 2h
P2 Image vulnerability scanning trong CI .github/workflows/docker-build.yml 1h
P3 Pin MinIO image version deployments/local/docker-compose.yml:74 15m
P3 Rotate Grafana default password deployments/local/docker-compose.yml:1278 30m
P3 ArgoCD GitOps adoption New infra 2-3 days
P3 External Secrets Operator deployments/production/kubernetes/ 1 day

Report generated by DevOps Engineer agent (TEC-223) — 2026-03-20