# 22 — CI/CD ## Phân loại **Loại tài liệu:** Explanation với workflow reference. **Reader job:** hiểu pipeline CI, deploy và rollback hiện có, cùng khoảng trống giữa chúng. ## Workflow hiện có | Workflow | Trigger | Mục đích | |---|---|---| | `ci.yml` | mọi push và pull request | AI Ruff/pytest, ingestion pytest, web lint/build | | `deploy.yml` | selected paths trên `master`, manual | Build/deploy EC2 Compose và chạy smoke/observability checks | | `rollback.yml` | manual với `target_sha` | Reset/rebuild commit tốt trước đó và verify health | | `migrate-qdrant-snapshot.yml` | manual | Bridge snapshot một lần từ production sang practice cluster | ## CI flow ```mermaid flowchart LR P[push hoặc pull request] A[AI service: Ruff + pytest] I[Ingestion: pytest] W[Web: lint + build] P --> A P --> I P --> W ``` `ci.yml` dùng Python 3.12 và Node 20. AI dependencies được cài tương tự Dockerfile vì project chưa có Python lockfile. `tests/conftest.py` đặt provider mặc định về disabled, nên unit suite không cần Qdrant/AWS. Ingestion cài bằng `pip install -e "./ingestion[dev]"`. Web dùng `pnpm install --frozen-lockfile`. CI hiện không chạy: - frontend/browser tests vì chưa có test runner; - Helm lint/template; - real datastore integration; - dependency, secret hoặc image vulnerability scan; - live RAG evaluation. ## Deploy flow ```mermaid flowchart LR M[master path change] S[SSH production host] G[fetch + reset origin/master] B[Compose build/up] C[Caddy + migrations] H[health/readiness/web] R[real RAG smoke] O[Prometheus/Tempo/Grafana checks] M --> S --> G --> B --> C --> H --> R --> O ``` `deploy.yml` chỉ trigger tự động cho các path mà production images/config thực sự dùng: - `apps/ai-service/**`; - `apps/web/**`; - `packages/**`; - `ingestion/data/verified/drug_entities.json`; - `infra/docker/**`; - `.github/workflows/deploy.yml`. Docs-only changes không redeploy production. `workflow_dispatch` vẫn cho phép chạy thủ công. ## Quan hệ giữa CI và deploy CI và deploy là **hai workflow độc lập**. `deploy.yml` không có `workflow_run` dependency hoặc `needs` trỏ đến jobs trong `ci.yml`. Do đó: - pull request có feedback Ruff/pytest/lint/build; - nhưng một CI run đỏ không tự động ngăn deploy workflow được trigger bởi push lên `master`; - branch protection/required checks có thể giảm rủi ro, nhưng trạng thái đó không thể xác minh chỉ từ repository. Đây là khoảng trống khác với “không có CI”: CI đã tồn tại, nhưng chưa phải mechanical precondition của deploy. ## Verification sau deploy `set -e` làm mỗi assertion sau đây fatal: 1. Caddy config valid và reload được. 2. Migrations chạy trong ai-service container. 3. AI `/health` và `/ready` trả thành công. 4. Web trả thành công. 5. Condition→drug query chạy trên corpus/provider thật. 6. Response là `answerable` và có citation section `chi_dinh`. 7. Prometheus ready. 8. Tempo ready với retry. 9. Grafana health, Prometheus/Tempo datasources và dashboard tồn tại. 10. Public Grafana login route truy cập được. 11. Một request có correlation ID trả `X-Trace-ID` đúng định dạng. 12. `duocthu_requests_total` query được và đúng trace có trong Tempo. Đây là post-deploy verification mạnh, nhưng chỉ smoke một nhánh RAG; nó không thay thế full evaluation. ## Rollback `rollback.yml` nhận `target_sha`, verify commit, reset production checkout, rebuild app/observability tier, chạy migrations rồi health checks. Deploy fail không tự gọi rollback workflow. Migrations không có down scripts. Các migration hiện hành idempotent, nhưng một migration tương lai không tương thích ngược có thể làm code rollback không đủ để khôi phục dịch vụ. ## Qdrant migration workflow `migrate-qdrant-snapshot.yml` tạo snapshot hai collection: - `duocthu_v1`; - `duocthu_v1__manifest`. Nó tải snapshot về runner và upload artifact giữ một ngày. Comment của workflow xác định đây là bridge một lần, không phải regular deployment path. Sau khi migration practice cluster đóng, workflow nên được xóa hoặc archive để giảm credential surface. ## Trade-off hiện tại | Thuộc tính | Hệ quả | |---|---| | Build trên production host | Build failure xảy ra sau khi checkout đã chuyển SHA | | Images không có immutable release tag | Rollback phải rebuild từ commit cũ | | CI/deploy độc lập | Red CI không tự động chặn deploy | | Deploy in-place | Có thể có gián đoạn ngắn khi service rebuild/restart | | Stateful services không nằm trong deploy `up` list | Code deploy không restart PostgreSQL/Qdrant | | Post-deploy smoke dùng provider thật | Bắt được lỗi integration nhưng tốn thời gian/cost và chỉ phủ một flow | ## Target GitOps chưa hoạt động `infra/ci/github-actions/README.md` mô tả các workflow tách nhỏ và `bump-image-tag.yml` cho GitOps. Những file được hứa trong đó chưa tồn tại. CI thực tế là workflow hợp nhất `ci.yml`; image registry/promotion và ArgoCD update loop vẫn là target state. ## Ưu tiên tiếp theo 1. Làm green required checks thành điều kiện cơ học trước production deploy. 2. Build/tag/push immutable images trong CI và deploy theo tag/digest. 3. Thêm frontend tests, Helm render/lint và migration tests. 4. Thêm evaluation regression gate tách khỏi live post-deploy smoke. 5. Xóa workflow migration một lần sau khi hoàn thành nhiệm vụ. ## Liên quan - [How to deploy and rollback](how-to/deploy-and-rollback.md) - [Testing](18-testing.md) - [Deployment](20-deployment.md) - [Kubernetes and ArgoCD](21-kubernetes-and-argocd.md) - [Production operations](24-production-operations.md)