# 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` ```yaml 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` ```yaml alerting: alertmanagers: - static_configs: - targets: [] # EMPTY — no alerts will fire ``` **Impact**: Toàn bộ `alert-rules.yml` và `rules/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` ```bash 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` ```yaml - 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.yml` và `deploy-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` ```yaml 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` ```yaml 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` ```yaml - "--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` ```yaml 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. ```yaml 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` ```yaml 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` ```yaml 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](https://external-secrets.io) 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*