267 lines
11 KiB
Markdown
267 lines
11 KiB
Markdown
# 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*
|