Mục lục
- Mục tiêu bài học
- CI/CD recap cho người chưa có background
- CI/CD cho ML — 3 đặc thù so với phần mềm thông thường
- Workflow tổng thể của một ML pipeline
- GitHub Actions cơ bản — CI workflow
- Workflow train và eval
- GPU runner và self-hosted runner
- Secret management
- Build và deploy Docker image
- CML — Continuous Machine Learning
- PR-driven ML workflow
- Matrix strategy — train nhiều config song song
- Caching để tăng tốc
- Trigger pattern
- Chi phí và cảnh báo
- Common pitfalls
- Alternatives
- Tóm tắt
- Bài tiếp theo
Mục tiêu bài học
Sau bài này bạn sẽ:
- ✅ Hiểu tại sao CI/CD cho ML cần xử lý thêm 3 vấn đề mà CI/CD thông thường không có
- ✅ Viết được GitHub Actions workflow cho CI (lint + test) và tách riêng workflow train
- ✅ Tích hợp DVC để pull data và chạy pipeline trong Actions
- ✅ Dùng CML để comment metric report lên PR tự động
- ✅ Build và push Docker image lên GHCR khi tag release
- ✅ Biết khi nào nên dùng GPU runner, self-hosted runner
- ✅ Tránh được các pitfall phổ biến về chi phí, secret leak, và cache
CI/CD recap cho người chưa có background
CI (Continuous Integration): mỗi khi developer push code hoặc mở Pull Request, một pipeline tự động chạy — thường gồm lint, unit test, build. Mục đích là phát hiện lỗi sớm trước khi code merge vào nhánh chính.
CD (Continuous Delivery / Deployment): sau khi CI pass, tự động deploy lên môi trường staging hoặc production. Continuous Delivery dừng ở bước "sẵn sàng để deploy, cần human approve". Continuous Deployment đi thêm một bước: tự deploy không cần approve.
GitHub Actions là CI/CD platform tích hợp sẵn trong GitHub. Workflow được định nghĩa bằng file YAML đặt trong thư mục .github/workflows/. Mỗi workflow có thể có nhiều job, mỗi job chạy trên một máy ảo riêng (runner) và gồm nhiều step.
Cấu trúc cơ bản của một workflow file:
name: Tên workflow
on: # Trigger: khi nào chạy
push:
branches: [main]
jobs:
job-name:
runs-on: ubuntu-latest # loại runner
steps:
- name: Tên step
run: lệnh shell # hoặc uses: action@version
Actions có thể là lệnh shell thuần (run:) hoặc dùng action có sẵn từ Marketplace (uses:). Ví dụ actions/checkout@v4 clone repo về runner, actions/setup-python@v5 cài Python.
CI/CD cho ML — 3 đặc thù so với phần mềm thông thường
1. Artifact của CI/CD ML gồm cả model và data
CI/CD truyền thống deploy code (binary, container). CI/CD cho ML cần deploy code + model artifact + config, đôi khi cả data version. Model là một binary lớn không phù hợp lưu trong git — cần tool riêng như DVC (bài 43) hoặc MLflow Model Registry (bài 42).
2. Train rất chậm, không thể chạy mỗi PR
Build Docker image mất vài phút. Train một model thực tế có thể mất vài giờ đến vài ngày. Nếu trigger train mỗi lần push code, chi phí compute sẽ mất kiểm soát và queue sẽ bị tắc nghẽn.
Cách xử lý thực tế: tách workflow thành 2 loại —
- CI workflow: chạy mỗi push/PR — lint, unit test, validate data schema. Chạy nhanh (dưới 5 phút).
- Train workflow: chỉ trigger khi cần —
workflow_dispatch(bấm tay), schedule (cron), hoặc khi merge vào nhánh đặc biệt.
3. "Test pass" chưa đủ — cần so sánh metric với baseline
Code có thể compile và test pass, nhưng model mới có thể có accuracy thấp hơn model hiện tại ở production. Cần bước evaluation gate: so sánh metric của model mới với baseline, chỉ promote nếu vượt ngưỡng.
Ví dụ: val_accuracy >= 0.85 và val_accuracy >= current_production_accuracy - 0.02 (cho phép lệch 2% để tránh loại model tốt do random seed).
Workflow tổng thể của một ML pipeline
Nhìn từ trên cao, pipeline CI/CD cho ML điển hình gồm các bước sau:
Push / PR
│
▼
Lint + Unit test (chạy mọi PR)
│
▼ (nếu merge vào main hoặc trigger thủ công)
Validate data (schema, missing, distribution)
│
▼
Train (chậm — có thể chạy trên GPU runner)
│
▼
Evaluate — so sánh metric với baseline
│
▼ (nếu pass evaluation gate)
Register model vào Model Registry (MLflow, W&B)
│
▼
Build Docker image + push lên registry
│
▼
Deploy (Render hook, K8s rollout, ...)
Không nhất thiết phải implement hết ngay từ đầu. Phần lớn team bắt đầu với CI workflow đơn giản (lint + test), thêm train workflow sau khi có Model Registry, rồi tích hợp deploy tự động khi cần.
GitHub Actions cơ bản — CI workflow
File .github/workflows/ci.yml — chạy mỗi push và PR:
name: CI
on:
push:
branches: [main, dev]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip" # cache pip packages theo requirements
- run: pip install -r requirements.txt -r requirements-dev.txt
- run: ruff check . # lint
- run: pytest --cov=src tests/
Một vài điểm cần chú ý:
actions/checkout@v4: luôn pin version (v4) để tránh breaking change từ action author.cache: "pip"trongsetup-python: tự động cache~/.cache/piptheo nội dungrequirements*.txt. Giảm thời gian install từ ~2 phút xuống còn vài giây khi cache hit.- Tách
requirements.txt(production deps) vàrequirements-dev.txt(pytest, ruff, mypy, ...) để không đưa dev tool vào Docker image. ruff check .:ruff(Rust-based) nhanh hơnflake8/pylintđáng kể — phù hợp cho CI.
Thêm bước validate data schema
Nếu repo chứa script ingest/validate data, có thể thêm vào CI:
- name: Validate data schema
run: python scripts/validate_data.py --data-dir data/raw
Script này kiểm tra: cột có đúng không, kiểu dữ liệu có đúng không, không có toàn NaN trong cột quan trọng. Dùng pandera hoặc great_expectations để viết schema validation dễ maintain hơn assert thủ công.
Workflow train và eval
File .github/workflows/train.yml — tách riêng, chỉ trigger khi cần:
name: Train Model
on:
workflow_dispatch: # bấm tay từ UI hoặc gh CLI
schedule:
- cron: "0 2 * * 1" # mỗi thứ 2, 2h sáng UTC
jobs:
train:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r requirements.txt
# Pull data từ DVC remote (S3, GCS, ...)
- uses: iterative/setup-dvc@v1
- name: Pull data
run: |
dvc remote modify storage --local \
access_key_id ${{ secrets.AWS_KEY }}
dvc remote modify storage --local \
secret_access_key ${{ secrets.AWS_SECRET }}
dvc pull
# Chạy pipeline DVC (train + eval steps)
- name: Train
run: dvc repro
# Kiểm tra metric pass ngưỡng
- name: Check metrics
run: |
python scripts/check_metrics.py \
--metric val_accuracy \
--threshold 0.85
# Register model vào MLflow nếu pass
- name: Register model
run: |
python scripts/register_model.py \
--model-name iris-classifier \
--run-id $(cat run_id.txt)
env:
MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_URI }}
Ví dụ script check_metrics.py đơn giản:
import argparse
import json
import sys
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--metric", required=True)
parser.add_argument("--threshold", type=float, required=True)
args = parser.parse_args()
# DVC lưu metrics vào metrics.json sau khi chạy dvc repro
with open("metrics.json") as f:
metrics = json.load(f)
value = metrics[args.metric]
print(f"{args.metric} = {value:.4f} (threshold = {args.threshold})")
if value < args.threshold:
print(f"FAIL: {value:.4f} < {args.threshold}")
sys.exit(1)
print("PASS")
if __name__ == "__main__":
main()
Script exit code 1 làm job fail, GitHub Actions sẽ đánh dấu workflow failed và không chạy bước register model tiếp theo.
GPU runner và self-hosted runner
Runner mặc định (ubuntu-latest) là máy ảo CPU. Không có GPU.
GitHub-hosted GPU runner (paid)
GitHub ra mắt GPU runner từ 2024 cho GitHub Teams/Enterprise. Label: ubuntu-22.04-gpu. Specs: NVIDIA T4 GPU, 4 vCPU, 28 GB RAM. Giá tính theo phút, xem pricing tại docs.github.com.
jobs:
train-gpu:
runs-on: ubuntu-22.04-gpu
steps:
- uses: actions/checkout@v4
- run: nvidia-smi # kiểm tra GPU available
- run: python train.py --device cuda
Self-hosted runner
Nếu đã có GPU machine riêng (on-premise hoặc cloud VM), có thể register nó làm runner của repo:
- Repo Settings → Actions → Runners → New self-hosted runner.
- Chạy script cài đặt trên máy GPU.
- Runner tự kết nối về GitHub, nhận job khi có workflow trigger.
jobs:
train:
runs-on: [self-hosted, gpu] # label tự đặt khi setup runner
steps:
- uses: actions/checkout@v4
- run: python train.py --device cuda
Self-hosted runner phù hợp khi: cần GPU thường xuyên (cost thấp hơn cloud runner), cần access network nội bộ (data không ra ngoài internet), hoặc cần môi trường đặc biệt (CUDA version cụ thể).
Lưu ý bảo mật: không dùng self-hosted runner cho public repo — PR từ fork có thể trigger job và chạy code tùy ý trên máy của bạn.
Secret management
Workflow cần nhiều credential: AWS key để pull data từ S3, MLflow URI, API key deploy. Những thông tin này không được hard-code trong file yml.
GitHub Secrets
Thêm secret tại: Repo Settings → Secrets and variables → Actions → New repository secret.
Dùng trong workflow:
env:
MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_URI }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GitHub tự động mask giá trị secret trong log — nếu secret xuất hiện trong output, nó sẽ bị thay bằng ***. Tuy nhiên cơ chế mask không hoàn hảo: nếu secret bị encode (base64, URL encode) hoặc bị split qua nhiều dòng, có thể không mask được.
Quy tắc cứng
- Không
echo $SECRETra stdout — nếu cần debug, dùng hash:echo $SECRET | sha256sum. - Không dùng
set -x(trace all commands) khi có secret trong env. - Secret chỉ available cho jobs trong cùng repo, không truyền qua fork PR (theo mặc định).
Organization-level secrets
Nếu nhiều repo dùng chung credential (ví dụ: cùng AWS account, cùng MLflow server), dùng Organization Secrets thay vì add vào từng repo: Settings → Secrets → Organization secrets.
Environment secrets
GitHub hỗ trợ deployment environments (staging, production) với secret riêng và require-approval gate. Job phải được approve trước khi chạy và trước khi nhận secret của environment đó.
jobs:
deploy-prod:
environment: production # sẽ trigger approval gate nếu đã cấu hình
runs-on: ubuntu-latest
steps:
- run: deploy-to-prod.sh
env:
PROD_API_KEY: ${{ secrets.PROD_API_KEY }}
Build và deploy Docker image
File .github/workflows/release.yml — trigger khi push tag dạng v1.2.3:
name: Build & Deploy
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # token tự động, không cần tạo thủ công
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
cache-from: type=gha # dùng GitHub Actions cache cho Docker layer
cache-to: type=gha,mode=max
deploy:
needs: build # chỉ chạy sau khi build xong
runs-on: ubuntu-latest
steps:
- name: Trigger deploy hook
run: |
curl -X POST "${{ secrets.RENDER_DEPLOY_HOOK }}"
Giải thích một số chi tiết:
secrets.GITHUB_TOKEN: GitHub tự inject token này vào mọi workflow, có quyền push image lên GHCR (GitHub Container Registry) của repo. Không cần tạo Personal Access Token riêng.cache-from: type=gha: lưu Docker layer cache vào GitHub Actions cache storage, dùng lại ở lần build sau. Giảm build time đáng kể khi chỉ thay đổi code app (layers base image và pip install không rebuild).needs: build: jobdeploychỉ chạy khi jobbuildthành công. Nếu build fail, deploy không trigger.- Deploy hook của Render/Railway là URL dạng
https://api.render.com/deploy/srv-xxx?key=yyy— POST vào đó là đủ để trigger redeploy với image mới nhất.
Tag image nhiều tag cùng lúc
Thường muốn tag cả v1.2.3 lẫn latest:
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
CML — Continuous Machine Learning
CML (Continuous Machine Learning) là tool mã nguồn mở của Iterative (cùng nhóm làm DVC). CML cho phép workflow tự comment kết quả train lên PR dưới dạng report gồm text, bảng metric, và biểu đồ.
- uses: iterative/setup-cml@v2
- name: Generate and post report
run: |
echo "## Model Metrics" >> report.md
cat metrics.json | python -m json.tool >> report.md
echo "" >> report.md
echo "## Training Curve" >> report.md
cml asset publish plots/loss.png --md >> report.md
cml comment create report.md
env:
REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Kết quả: mỗi khi workflow train chạy xong, bot tự comment lên PR (hoặc commit) với nội dung như:
## Model Metrics
{
"val_accuracy": 0.8712,
"val_loss": 0.4231
}
## Training Curve
[biểu đồ loss curve được nhúng vào comment]
Reviewer có thể thấy ngay model mới đạt accuracy bao nhiêu so với run trước, không cần tự pull artifact về chạy. Đặc biệt hữu ích khi team có nhiều người review PR model thay đổi.
cml asset publish upload file ảnh lên GitHub và trả về markdown image link. cml comment create post comment lên PR/commit hiện tại.
So sánh với baseline tự động
Có thể mở rộng report để so sánh với baseline (metric của model đang ở production):
# scripts/generate_report.py
import json
with open("metrics.json") as f:
current = json.load(f)
with open("baseline_metrics.json") as f:
baseline = json.load(f)
delta = current["val_accuracy"] - baseline["val_accuracy"]
sign = "+" if delta >= 0 else ""
print(f"## Evaluation Report\n")
print(f"| Metric | Baseline | Current | Delta |")
print(f"|--------|----------|---------|-------|")
print(f"| val_accuracy | {baseline['val_accuracy']:.4f} | {current['val_accuracy']:.4f} | {sign}{delta:.4f} |")
PR-driven ML workflow
Pattern phổ biến trong team ML:
- Dev tạo nhánh, thay đổi code train / feature engineering / hyperparameter.
- Mở PR → CI chạy: lint, test, train mini (dùng 10% data để kiểm tra pipeline không bị lỗi, không để đánh giá chất lượng).
- CML bot comment metric của mini-run lên PR.
- Reviewer thấy pipeline không crash, code sạch → approve.
- Merge vào
main→ trigger full train workflow (toàn bộ data). - Full train xong → evaluation gate → nếu pass → register model → deploy.
Train mini trên PR có thể cấu hình qua environment variable:
- name: Train (mini on PR)
run: |
python train.py \
--max-samples 1000 \
--epochs 2
if: github.event_name == 'pull_request'
- name: Train (full on main)
run: python train.py
if: github.ref == 'refs/heads/main'
if: condition cho phép step chỉ chạy theo điều kiện — rất hữu ích để phân biệt hành vi trên PR và trên main.
Matrix strategy — train nhiều config song song
GitHub Actions cho phép chạy nhiều job song song với cấu hình khác nhau thông qua strategy.matrix:
jobs:
hyperparameter-search:
runs-on: ubuntu-latest
strategy:
matrix:
lr: [0.001, 0.0001]
batch_size: [32, 64]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install -r requirements.txt
- run: |
python train.py \
--lr ${{ matrix.lr }} \
--batch-size ${{ matrix.batch_size }} \
--run-name "lr=${{ matrix.lr }}_bs=${{ matrix.batch_size }}"
env:
MLFLOW_TRACKING_URI: ${{ secrets.MLFLOW_URI }}
Với 2×2 matrix, GitHub spin up 4 job chạy song song. Mỗi job nhận một tổ hợp (lr, batch_size) khác nhau. Kết quả 4 run được log lên MLflow để so sánh. Tổng thời gian gần bằng thời gian của 1 run thay vì 4 run tuần tự.
Chú ý: 4 job song song = 4 runner đồng thời. GitHub Free tier giới hạn concurrent jobs (20 jobs với public repo, ít hơn với private). Tổng phút cũng tính gộp: 4 job × 30 phút = 120 phút tiêu thụ.
Matrix với fail-fast
Mặc định, nếu một job trong matrix fail, GitHub hủy các job còn lại (fail-fast: true). Để chạy hết tất cả dù có job fail:
strategy:
fail-fast: false
matrix:
lr: [0.001, 0.0001]
batch_size: [32, 64]
Caching để tăng tốc
Runner GitHub Actions là máy ảo sạch mỗi lần chạy — không giữ lại gì giữa các run. Cache là cơ chế lưu thư mục vào storage, restore lại ở run tiếp theo nếu cache key khớp.
Pip cache
actions/setup-python@v5 với cache: "pip" tự xử lý cache ~/.cache/pip. Key tự động dựa trên hash của requirements*.txt. Không cần cấu hình thêm.
Docker layer cache
Dùng cache-from: type=gha trong docker/build-push-action như đã thấy ở bước 9. Quan trọng: sắp xếp Dockerfile để layers thay đổi ít (base image, pip install) đứng trước layers thay đổi nhiều (copy source code).
HuggingFace model cache
Nếu workflow download model từ HuggingFace Hub, cache lại để tránh download mỗi lần:
- uses: actions/cache@v4
with:
path: ~/.cache/huggingface
key: hf-${{ hashFiles('requirements.txt') }}
restore-keys: |
hf-
- run: python scripts/download_model.py
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
Key dùng hash của requirements.txt: khi đổi version transformers/torch thì cache miss → download lại. restore-keys: hf- là fallback: nếu không có exact match, dùng cache cũ nhất có prefix hf- (vẫn có ích hơn download từ đầu).
DVC cache
DVC remote (S3, GCS) bản thân đã là persistent storage. Không cần thêm Actions cache cho DVC artifacts — dvc pull chỉ download file chưa có ở local và đã thay đổi.
Giới hạn cache
GitHub Actions cache tối đa 10 GB per repo (GitHub Free). Cache entry hết hạn sau 7 ngày không dùng. Nếu hết quota, cache cũ nhất bị xóa tự động.
Trigger pattern
Tổng hợp các trigger phổ biến và use case tương ứng:
| Trigger | Cú pháp | Use case |
|---|---|---|
| Push to main | push: branches: [main] |
Deploy to production sau khi merge |
| Push to staging | push: branches: [staging] |
Deploy to staging environment |
| Pull request | pull_request: branches: [main] |
CI: lint, test, mini-train trên PR |
| Tag release | push: tags: ['v*'] |
Build Docker image, tạo GitHub Release |
| Cron schedule | schedule: - cron: "0 2 * * 1" |
Retrain định kỳ (hàng tuần, hàng ngày) |
| Manual dispatch | workflow_dispatch: |
Train thủ công, trigger từ UI hoặc gh workflow run |
| Manual với input | workflow_dispatch: inputs: ... |
Train với hyperparameter tùy chọn |
workflow_dispatch với input
on:
workflow_dispatch:
inputs:
model_name:
description: "Tên model để train"
required: true
default: "resnet50"
epochs:
description: "Số epoch"
required: false
default: "10"
type: number
jobs:
train:
runs-on: ubuntu-latest
steps:
- run: |
python train.py \
--model ${{ inputs.model_name }} \
--epochs ${{ inputs.epochs }}
Khi trigger từ GitHub UI, người dùng được hỏi giá trị cho các input trước khi workflow chạy. Trigger từ CLI: gh workflow run train.yml -f model_name=efficientnet -f epochs=20.
Chi phí và cảnh báo
GitHub Actions Free tier:
- Public repo: unlimited phút.
- Private repo: 2000 phút/tháng (Teams: 3000 phút, Enterprise: 50000 phút).
- Storage: 500 MB artifact + 500 MB cache (Free).
Nhân hệ số: Linux runner tính ×1, Windows runner ×2, macOS runner ×10. Phần lớn ML workflow chạy Linux nên tính ×1.
Train job 1 giờ × 30 lần/tháng = 1800 phút — gần hết quota Free tier. Vì vậy:
- Chỉ trigger train khi thực sự cần:
workflow_dispatchhoặc schedule, không trigger mỗi push. - Dùng
concurrency:để hủy run đang chờ nếu có run mới hơn:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Với cấu hình này, nếu bạn push 3 lần liên tiếp vào cùng một nhánh, chỉ run cuối cùng được thực thi — 2 run trước bị cancel.
GPU runner paid được tính theo phút ×GPU multiplier. Một T4 GPU runner (~$0.07/phút theo giá 2024) chạy 1 tiếng = ~$4.2. Cần theo dõi qua Settings → Billing → Actions.
Common pitfalls
Trigger train mỗi push
Nếu workflow train có on: push: branches: [main], mỗi merge vào main đều chạy train. Với team push 10 lần/ngày, quota hết trong vài ngày. Fix: đổi sang workflow_dispatch hoặc schedule.
Secret leak qua log
GitHub mask secret trong log, nhưng không hoàn toàn. Ví dụ nguy hiểm:
# NGUY HIỂM — có thể leak nếu value có ký tự đặc biệt
- run: echo "Connecting to ${{ secrets.DB_URL }}"
Fix: không log secret, dùng action official thay vì tự viết connection string ra log.
PR từ fork có thể trigger workflow với secret
Với pull_request event, PR từ fork không có access secret (GitHub chặn theo mặc định). Nhưng pull_request_target event chạy trong context của repo gốc, có access secret — dùng trigger này cẩn thận, dễ bị tấn công nếu cho phép PR từ fork chạy code tùy ý.
Cache không invalidate
Cache key không đổi dù requirements đã thay đổi. Thường xảy ra khi hard-code key: key: pip-cache. Fix: dùng hash: key: pip-${{ hashFiles('**/requirements*.txt') }}.
Self-hosted runner không được update
Runner software cần update định kỳ. Runner quá cũ có thể bị GitHub ngừng kết nối, hoặc có lỗ hổng bảo mật. Nên bật auto-update hoặc đặt lịch update thủ công.
Notify step không chạy khi job fail
Step cuối "Notify Slack" bị skip nếu job trước fail. Fix: thêm if: always():
- name: Notify Slack
if: always() # chạy dù step trước fail hay pass
uses: slackapi/slack-github-action@v1
with:
payload: |
{"text": "Workflow ${{ job.status }}"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Workflow chạy quá lâu không có timeout
Job mặc định có timeout 6 giờ. Nếu script train bị hang (ví dụ chờ input, deadlock), job sẽ chạy cho đến khi hết quota. Đặt timeout rõ ràng:
jobs:
train:
runs-on: ubuntu-latest
timeout-minutes: 120 # tối đa 2 tiếng
Alternatives
GitHub Actions phù hợp cho hầu hết team vì tích hợp sẵn với GitHub. Các lựa chọn khác:
| Tool | Phù hợp khi | Điểm cần lưu ý |
|---|---|---|
| GitLab CI | Repo ở GitLab, on-premise GitLab | Cú pháp YAML tương tự, native Kubernetes runner |
| CircleCI | Cần resource class linh hoạt (GPU, large memory) | Paid plan, cấu hình phức tạp hơn |
| Jenkins | Legacy enterprise, on-premise hoàn toàn | Cần tự quản lý infrastructure, Groovy DSL |
| Argo Workflows | Kubernetes-native, DAG phức tạp | Cần K8s cluster, learning curve cao hơn |
| Kubeflow Pipelines | Team lớn, pipeline ML phức tạp, K8s | Setup nặng, overkill cho team nhỏ |
| AWS CodePipeline | Stack AWS hoàn toàn (SageMaker, ECR, ...) | Lock-in AWS, integrate tốt với SageMaker |
| GCP Cloud Build | Stack GCP (Vertex AI, Artifact Registry) | Trigger từ Cloud Source Repos hoặc GitHub |
Với team nhỏ (<10 người) dùng GitHub, GitHub Actions là lựa chọn có ít overhead nhất để bắt đầu. Khi cần pipeline ML phức tạp hơn (branching, artifact dependency), chuyển sang Argo Workflows hoặc Kubeflow khi đã có K8s cluster.
Tóm tắt
- CI/CD cho ML có 3 đặc thù: artifact phức tạp hơn (code + model + data), train chậm nên không chạy mỗi PR, cần evaluation gate so sánh metric với baseline.
- Tách CI workflow (chạy mọi PR, nhanh) khỏi train workflow (trigger thủ công hoặc schedule).
- GitHub Actions workflow cơ bản:
checkout@v4→setup-python@v5vớicache: pip→ install → lint → test. - Train workflow tích hợp DVC pull data,
dvc reprochạy pipeline, script check metric, register model vào MLflow. - GPU runner: GitHub-hosted (
ubuntu-22.04-gpu, paid) hoặc self-hosted runner với label[self-hosted, gpu]. - Secret luôn dùng
${{ secrets.NAME }}, không echo ra log. - Build Docker image với
docker/build-push-action@v6, push lên GHCR, trigger deploy hook. - CML tự comment metric report + plot lên PR — reviewer thấy kết quả không cần pull artifact về.
- Matrix strategy spin up nhiều job song song để hyperparameter search.
- Đặt
timeout-minutes, dùngconcurrencyđể tránh tốn quota không cần thiết.
