Capstone phần 1: train, đăng ký model và deploy lên hạ tầng rẻ
Bài trước bạn đã chốt cách phục vụ tiết kiệm: container nhẹ, batch cho việc không cần realtime, autoscale theo tải thật. Bài này ghép mảnh đó với stack OSS bạn đã dựng ở Module 1 (MLflow, DVC, MinIO trên một VPS rẻ) thành một đường ống chạy được thật: train, đăng ký model, rồi mở một endpoint HTTP trả dự đoán.
Mục tiêu: một endpoint thật từ model thật
Capstone phần 1 không cần model hoàn hảo. Nó cần một đường ống mà người khác gọi vào được và bạn giải thích được từng bước. Cụ thể, cuối bài này bạn phải có:
- Một nhánh train chạy bằng một lệnh, ghi đầy đủ params, metrics, model vào MLflow.
- Model nằm trong Model Registry với tên cố định và ít nhất hai phiên bản.
- Một container phục vụ model đó, chạy trên đúng chiếc VPS rẻ bạn đã chọn, trả dự đoán qua HTTP.
Lý do phải làm đủ ba phần: thiếu registry thì không ai biết endpoint đang chạy model nào; thiếu endpoint thì pipeline chỉ là notebook đẹp; thiếu chi phí kiểm soát thì VPS 2GB của bạn sẽ OOM ngay lần đầu load model.
flowchart TD
Data["📦<br/>Dữ liệu (DVC + MinIO)"] --> Train["🏋️<br/>Script train"]
Train --> Track["📈<br/>MLflow Tracking"]
Track --> Reg["🗂️<br/>Model Registry"]
Reg --> Build["🐳<br/>Image serving nhẹ"]
Build --> Serve["🚀<br/>Endpoint trên VPS"]
Serve --> Client["🖥️<br/>Client gọi API"]
Đặc tả: pipeline cần có gì
| Hạng mục | Yêu cầu tối thiểu |
|---|---|
| Dữ liệu | Bộ nhỏ (vài chục nghìn dòng), phiên bản hóa bằng DVC, artifact đẩy về MinIO |
| Train | Một script, một lệnh, không hardcode đường dẫn tuyệt đối |
| Logging | mlflow.log_params, log_metrics, và log_model kèm signature + input_example |
| Registry | Tên model cố định, ít nhất 2 phiên bản, có mô tả ngắn cho từng bản |
| Serving | Container nhẹ, POST /invocations (hoặc /predict), GET /health |
| Chi phí | Chạy gọn trong 1 VPS 2 vCPU / 2GB RAM, không thêm dịch vụ trả tiền |
Ba biến môi trường tối thiểu khi train trong container, vì localhost trong container không phải VPS của bạn:
export MLFLOW_TRACKING_URI=http://mlflow:5000
export MLFLOW_S3_ENDPOINT_URL=http://minio:9000
export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=...
Bốn chặng làm việc trong 35 phút
- Chặng 1 (8 phút): chuẩn hóa script train, thêm
mlflow.start_run, log params và 2 metric chính (ví dụ accuracy, f1). - Chặng 2 (8 phút): log model kèm
signaturevàinput_example, đặtregistered_model_name="ten-model", chạy lại lần hai để có 2 phiên bản. - Chặng 3 (10 phút): dựng Dockerfile
python:3.11-slim, cài đúng thư viện cần, chạymlflow models servevới--env-manager local --workers 1. - Chặng 4 (9 phút): mở port, gọi
curlvào/healthvà endpoint dự đoán, ghi lại thời gian cold start và mức RAM tiêu thụ.
Tip: Chặng 2 là chỗ hay bị bỏ qua nhất. Nếu chỉ
log_artifactfile.pkl, model vào registry nhưng không có signature, và serving sẽ nhận request sai schema mà không báo lỗi rõ ràng.
Bài tập nhanh 1. Trước khi đóng Docker, hãy kiểm chứng model trong registry chạy được bằng CLI. Bạn viết lệnh nào, và vì sao nên dùng --env-manager local?
Gợi ý đáp án
mlflow models serve -m models:/ten-model/1 -p 5001 --env-manager local --workers 1
curl -X POST http://127.0.0.1:5001/invocations \
-H "Content-Type: application/json" \
-d '{"dataframe_split": {"columns": ["f1","f2","f3","f4"], "data": [[5.1,3.5,1.4,0.2]]}}'
--env-manager local bỏ qua bước tạo conda env (tốn vài phút và vài trăm MB trên VPS). --workers 1 tránh 4 worker cùng load model vào RAM, đây là nguyên nhân OOM phổ biến nhất trên VPS 2GB.
Nghiệm thu: dấu hiệu bạn đã xong
| Tiêu chí | Cách kiểm chứng | Đạt khi |
|---|---|---|
| Model có signature | Mở UI MLflow, tab model version | Hiện rõ schema input/output |
| Endpoint sống | curl -i http://vps:5001/health |
Trả 200 |
| Dự đoán hợp lệ | curl POST một dòng dữ liệu |
JSON có kết quả, không phải 500 |
| Restart không mất model | docker restart rồi gọi lại |
Vẫn trả kết quả, model load từ registry hoặc MinIO |
| Có baseline | Gọi lại phiên bản 1 | Bản cũ vẫn dùng được để so sánh |
Bài tập nhanh 2. Endpoint đang trỏ models:/ten-model/2, nay có phiên bản 3 tốt hơn. Bạn đổi mà không sửa code client bằng cách nào?
Gợi ý đáp án
Hai cách: (1) dùng alias, client trỏ models:/ten-model@champion, bạn chỉ chuyển alias sang version 3; (2) truyền MODEL_URI qua biến môi trường container và restart. Cả hai đều giữ nguyên URL client gọi, đúng tinh thần tách cấu hình khỏi code.
Ôn nhanh các điểm then chốt
MLflow _____ ghi lại tham số, metric và artifact của từng lần chạy huấn luyện. Để serving biết kiểu dữ liệu đầu vào, model cần được log kèm _____ và một input example. Registry cho phép trỏ tới một phiên bản cụ thể bằng URI dạng models:/_____. Container serving nên dựa trên image nền _____ để giữ dung lượng nhỏ và giảm RAM. Trước khi thay model đang chạy, luôn giữ lại một _____ để có đường lùi.
Ôn tập bằng thẻ ghi nhớ
Front: Sản phẩm tối thiểu của capstone phần 1 là gì? Back: Một nhánh train ghi model vào registry và một endpoint HTTP thật trả dự đoán.
Front: MLflow Tracking lưu những gì cho mỗi lần chạy? Back: Params, metrics, tags và artifacts.
Front: Model signature dùng để làm gì? Back: Khai báo kiểu dữ liệu vào ra để serving validate request thay vì lỗi âm thầm.
Front: URI trỏ tới một phiên bản model trong Registry? Back: models:/ten_model/so_phien_ban
Front: Vì sao artifact phải đẩy về MinIO? Back: Để model không phụ thuộc ổ đĩa container hay VPS, restart hoặc rebuild không mất.
Front: Ba biến môi trường cần có khi train trong container? Back: MLFLOW_TRACKING_URI, MLFLOW_S3_ENDPOINT_URL và cặp AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY.
Front: Vì sao chạy mlflow models serve trước khi đóng Docker? Back: Để tách lỗi model khỏi lỗi đóng gói image.
Front: Image nền nào phù hợp cho serving nhẹ? Back: python:3.11-slim, chỉ cài đúng thư viện inference cần dùng.
Front: Route health check trả gì khi model đã load xong? Back: HTTP 200 kèm trạng thái sẵn sàng.
Front: Vì sao phải đặt --workers 1 trên VPS 2GB? Back: Mỗi worker load một bản model vào RAM, mặc định 4 worker dễ gây OOM.
Front: Alias champion trong Registry giải quyết việc gì? Back: Trỏ một tên cố định tới phiên bản mới nhất, client không phải đổi URI.
Front: Baseline model để làm gì? Back: Là bản đang chạy production, dùng để so sánh và lùi khi bản mới lỗi.
Front: Dấu hiệu cold start quá lâu trên VPS rẻ? Back: Request đầu chậm vài chục giây hoặc bị kill, cần model nhỏ hơn hoặc giữ container warm.
Cạm bẫy khi lên endpoint đầu tiên
- Dùng
localhosttrongMLFLOW_TRACKING_URIkhi train bằng container. Trong container,localhostlà chính nó, không phải MLflow server. Dùng tên service trong docker network. - Log model bằng
log_artifactthay vìlog_model. Bạn có file nhưng mất signature, mất flavor, serving không biết cách nạp. - Cài cả Jupyter và MLflow đầy đủ vào image serving: image phình lên hơn 1GB, thời gian pull và khởi động tăng, RAM cạn.
- Quên ghim version thư viện trong
requirements.txt. Rebuild vài tuần sau ra môi trường khác, dự đoán lệch. - Mở endpoint ra internet mà không có giới hạn: thêm firewall chỉ cho IP tin cậy, hoặc đặt sau reverse proxy có API key. Đây là endpoint công khai đầu tiên của bạn, đừng để nó thành cửa mở.
- Không đo chi phí thật: ghi lại thời gian container sống, RAM đỉnh, dung lượng image. Con số này là đầu vào cho báo cáo chi phí ở capstone phần 2.
Đã có gì trong tay
Bạn vừa hoàn thành xương sống của toàn khóa: dữ liệu có phiên bản, một lệnh train ghi đầy đủ dấu vết, model vào registry có signature, và một endpoint thật trả dự đoán trên hạ tầng rẻ. Ba thứ này sẽ không đổi nữa, các bài sau chỉ bồi thêm lớp quan sát và tối ưu.
Bài tiếp theo là checkpoint của module: bạn sẽ kiểm tra lại toàn bộ stack theo danh sách, đo chi phí thực tế của cấu hình đang chạy, và xử lý các điểm chưa đạt trước khi bước sang monitoring.