Danh sách bài viết

Bài 33: Dockerfile cho ứng dụng AI Python — best practices

AI image Python khác image web thông thường chủ yếu vì size: PyTorch một mình đã có thể chiếm 1–3 GB, model weight thêm vài trăm MB đến vài GB nữa. Bài này trình bày cách chọn base image, cài PyTorch đúng variant (CPU/GPU), pin version, tận dụng BuildKit cache, bảo mật bằng non-root user và thêm healthcheck — tất cả trên Docker Engine 24+ với BuildKit mặc định.

27/05/2026
0 lượt xem
1

Mục tiêu bài học

Sau bài này bạn sẽ:

  • Biết chọn base image phù hợp cho từng loại workload (CPU inference, GPU training, dev).
  • Viết được Dockerfile hoàn chỉnh cho FastAPI AI service với non-root user và healthcheck.
  • Cài PyTorch đúng variant để tránh kéo CUDA libs không cần thiết (~3 GB thừa).
  • Tận dụng BuildKit cache mount để tăng tốc build lần 2 trở đi.
  • Hiểu thứ tự layer và lý do tại sao app code phải ở cuối.
  • Nhận biết các pitfall phổ biến gây image lớn hoặc rebuild chậm.
2

Bối cảnh: AI image khác image web ở điểm nào

Image FastAPI thuần phục vụ CRUD API thường có size dưới 200 MB. Image AI cho cùng framework đó có thể dễ dàng vượt 2–5 GB vì phụ thuộc thêm:

  • PyTorch 2.x — wheel mặc định (có CUDA) nặng khoảng 700 MB–2.5 GB tùy CUDA version. Wheel CPU-only khoảng 150–250 MB.
  • Transformers 4.x + tokenizers — thêm khoảng 100–200 MB.
  • CUDA runtime libs — nếu dùng GPU, cần thêm vài trăm MB đến hơn 1 GB tùy base image.
  • Model weight — BERT base: ~440 MB, GPT-2 small: ~500 MB, LLaMA-3 8B (fp16): ~16 GB.

Vì vậy, với AI image, 4 vấn đề cần quan tâm hơn image thông thường:

  • Build timepip install torch một mình mất 2–5 phút download. Cache BuildKit giúp lần sau dưới 10 giây.
  • Image size — ảnh hưởng đến pull time khi deploy, storage cost, và khởi động container trên node mới.
  • Runtime startup — load model PyTorch vào RAM/VRAM khi container start, có thể mất 10–60 giây. Healthcheck cần tính đến điều này.
  • Security — container không nên chạy root, đặc biệt khi expose API.

Bài này tập trung CPU inference (FastAPI + PyTorch + Transformers). GPU workflow sẽ được đề cập ở phần cài PyTorch.

3

Chọn base image

Quyết định base image ảnh hưởng trực tiếp đến size cuối và những gì cần cài thêm. Bảng dưới so sánh các lựa chọn phổ biến:

Base image Size xấp xỉ Use case Trade-off
python:3.11-slim ~125 MB CPU inference, FastAPI, API server Thiếu nhiều system lib — phải cài riêng (build-essential, libgomp1...)
python:3.11 ~900 MB Dev / debug Có sẵn nhiều tool, nhưng nặng hơn 7x so với slim
nvidia/cuda:12.1-runtime-ubuntu22.04 ~2 GB GPU runtime — cần cài Python riêng Linh hoạt, nhưng phải tự cài Python và pip
pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime ~6 GB PyTorch + GPU sẵn Start nhanh, nhưng rất lớn và lock PyTorch version
tensorflow/tensorflow:2.15.0-gpu ~7 GB TensorFlow + GPU sẵn Tương tự PyTorch image, lock TF version
mambaorg/micromamba ~30 MB Conda env nhẹ Cần cài thêm mọi thứ, phù hợp nếu đã quen conda workflow

Khuyến nghị cho CPU inference: python:3.11.7-slim. Pinned minor version để build reproducible. Slim thiếu một số lib nhưng dễ kiểm soát và image cuối nhỏ hơn nhiều.

Khuyến nghị cho GPU inference/training: nvidia/cuda:12.1-runtime-ubuntu22.04 + tự cài Python, hoặc pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime nếu không quan trọng size. Tránh dùng -devel variant của CUDA image (có thêm compiler, nặng hơn nhiều) trừ khi cần compile extension.

Một lưu ý về tag: python:3.11python:3.11-slim là floating tag — Docker sẽ pull version minor mới nhất mỗi lần build nếu cache miss. Dùng python:3.11.7-slim để cố định minor version.

4

Dockerfile FastAPI inference đầy đủ (CPU)

Dưới đây là Dockerfile hoàn chỉnh cho một FastAPI service chạy inference CPU với PyTorch + Transformers:

# syntax=docker/dockerfile:1.7
FROM python:3.11.7-slim AS base

# --- ENV Python ---
# PYTHONDONTWRITEBYTECODE=1  : không tạo file .pyc (giảm size nhỏ)
# PYTHONUNBUFFERED=1         : stdout/stderr không buffer (log realtime)
# PIP_NO_CACHE_DIR=1         : pip không lưu cache vào image (dùng BuildKit mount thay thế)
# PIP_DISABLE_PIP_VERSION_CHECK=1 : tắt cảnh báo upgrade pip mỗi lần chạy
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# --- System deps ---
# build-essential : gcc, g++ cho một số wheel cần compile (ví dụ sentencepiece)
# libgomp1        : OpenMP runtime, PyTorch dùng cho CPU parallelism
# curl            : dùng cho healthcheck (CMD curl -f ...)
# --no-install-recommends : không cài extra package gợi ý (tiết kiệm ~50–100 MB)
# rm -rf /var/lib/apt/lists/* : xóa apt cache ra khỏi layer (tiết kiệm ~30–50 MB)
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libgomp1 \
    curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# --- Python dependencies ---
# Copy requirements trước COPY . . để cache layer này
# Chỉ khi requirements.txt thay đổi mới rebuild pip install
COPY requirements.txt .
RUN pip install -r requirements.txt

# --- Non-root user ---
# Tạo user trước COPY app code để --chown không cần root ở bước sau
RUN useradd -m -u 1000 appuser

# --- App code ---
COPY --chown=appuser:appuser . .

USER appuser

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Giải thích thêm một số điểm:

Dòng # syntax=docker/dockerfile:1.7

Khai báo frontend syntax version cho BuildKit. Cần có dòng này ở đầu file nếu muốn dùng RUN --mount=type=cache (xem mục 8). Docker Engine 24+ đã bật BuildKit mặc định, nhưng khai báo explicit đảm bảo tính tương thích.

Dòng AS base

Đặt tên stage base. Hiện tại chỉ có 1 stage, nhưng tên này sẽ hữu ích khi bài 34 chuyển sang multi-stage build — stage khác có thể COPY --from=base.

Thứ tự useraddCOPY

useradd đặt trước COPY --chown=appuser:appuser vì chown cần user đã tồn tại. USER appuser đặt sau cùng vì các bước trước (apt-get, pip install) cần quyền root.

Port 8000 thay vì 80

Port nhỏ hơn 1024 cần quyền root để bind. Khi chạy với non-root user, dùng port >= 1024. 8000 là convention của uvicorn/FastAPI.

5

Cài PyTorch — CPU vs GPU

pip install torch mà không chỉ định index-url sẽ kéo wheel mặc định từ PyPI — wheel đó bundle sẵn CUDA libs. Trên PyTorch 2.3.0, wheel mặc định (CUDA 12.1) nặng khoảng 2.5 GB. Nếu container chạy trên CPU-only host, toàn bộ CUDA libs trong đó là thừa.

CPU-only

RUN pip install torch==2.3.0 --index-url https://download.pytorch.org/whl/cpu

Wheel CPU-only của PyTorch 2.3.0 khoảng 190–250 MB — nhỏ hơn 10x so với wheel CUDA. Chỉ thêm --index-url để trỏ sang PyTorch wheel index, không phải PyPI.

Hoặc trong requirements.txt:

--extra-index-url https://download.pytorch.org/whl/cpu
torch==2.3.0+cpu

Variant +cpu là suffixed version string — pip nhận diện là build tag, chỉ match wheel có đúng build tag đó.

GPU

Cách đơn giản nhất là dùng official PyTorch image làm base:

FROM pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime

# PyTorch + CUDA runtime đã có sẵn trong base image
# Chỉ cần cài thêm các package khác
COPY requirements-gpu.txt .
RUN pip install -r requirements-gpu.txt

Trong requirements-gpu.txt không cần dòng torch vì đã có trong image. Chỉ liệt kê transformers, fastapi, uvicorn...

Cách khác — dùng nvidia/cuda base và cài PyTorch CUDA wheel:

FROM nvidia/cuda:12.1.1-runtime-ubuntu22.04

RUN apt-get update && apt-get install -y --no-install-recommends \
    python3.11 python3-pip \
    && rm -rf /var/lib/apt/lists/*

RUN pip install torch==2.3.0 --index-url https://download.pytorch.org/whl/cu121

CUDA wheel index: https://download.pytorch.org/whl/cu121 (cho CUDA 12.1), cu118 (CUDA 11.8)... Kiểm tra CUDA version tương thích tại pytorch.org/get-started/locally.

Khi chạy container GPU, cần --gpus all (hoặc chỉ định GPU cụ thể) và NVIDIA Container Toolkit trên host:

docker run --gpus all my-gpu-app
6

Cài model weight: 3 cách

Model weight không phải code — chúng không thay đổi khi sửa app logic. Cách handle weight ảnh hưởng lớn đến image size và startup time.

Cách 1 — Bake vào image

# Download model lúc build — weight nằm trong image layer
RUN python -c "from transformers import AutoModel, AutoTokenizer; \
    AutoModel.from_pretrained('bert-base-uncased'); \
    AutoTokenizer.from_pretrained('bert-base-uncased')"

Transformers cache vào ~/.cache/huggingface theo mặc định. Có thể đổi cache dir:

ENV TRANSFORMERS_CACHE=/app/models
RUN python -c "from transformers import AutoModel; \
    AutoModel.from_pretrained('bert-base-uncased')"

Ưu điểm: Container start là model sẵn sàng — không cần network lúc runtime, startup nhanh (chỉ load vào RAM, không download).

Nhược điểm: Image nặng thêm kích thước của weight. BERT base ~440 MB, GPT-2 ~500 MB. Mỗi lần model version thay đổi cần rebuild image và push toàn bộ layer mới.

Cách 2 — Download lúc startup

Không cài weight trong Dockerfile. App code tự download khi start:

# main.py — download model khi app khởi động
from transformers import AutoModel
from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Chạy khi startup
    app.state.model = AutoModel.from_pretrained("bert-base-uncased")
    yield
    # Chạy khi shutdown
    del app.state.model

app = FastAPI(lifespan=lifespan)

Ưu điểm: Image nhỏ — chỉ code và dependencies.

Nhược điểm: Startup chậm (download + load), cần network lúc container start. Nếu Hugging Face Hub unreachable → container fail. Không phù hợp môi trường production air-gapped.

Cách 3 — Mount volume

# Chuẩn bị sẵn model ở host (hoặc cloud storage được mount)
docker run \
    -v /data/models:/app/models \
    -e TRANSFORMERS_CACHE=/app/models \
    my-app

Hoặc trong Docker Compose:

services:
  api:
    image: my-app
    volumes:
      - model-cache:/app/models
    environment:
      - TRANSFORMERS_CACHE=/app/models

volumes:
  model-cache:

Ưu điểm: Image nhỏ. Nhiều container dùng chung 1 volume → không download lại. Cập nhật model chỉ cần thay file trong volume.

Nhược điểm: Phải provision và manage volume. Trên cloud cần thêm bước mount (EFS trên AWS, NFS, hoặc object storage mount).

Khuyến nghị thực tế: Cách 1 (bake) dùng khi model nhỏ (<1 GB) và version ổn định. Cách 3 (volume) dùng khi model lớn hoặc cần share giữa nhiều replica. Cách 2 dùng cho prototype/dev.

7

Pin version trong requirements.txt

Reproducibility là yêu cầu cơ bản của production. Dùng == để pin exact version, không phải >= hay ~=:

--extra-index-url https://download.pytorch.org/whl/cpu

fastapi==0.115.0
uvicorn[standard]==0.30.6
pydantic==2.9.2
torch==2.3.0+cpu
transformers==4.44.2
tokenizers==0.19.1
numpy==1.26.4
httpx==0.27.2

Một số điểm cần chú ý:

  • torch==2.3.0+cpu: build tag +cpu phân biệt CPU wheel và CUDA wheel. PyPI không có tag này — chỉ có ở PyTorch wheel index.
  • tokenizers==0.19.1: Transformers phụ thuộc tokenizers. Pin cả tokenizers để tránh tình huống Transformers 4.44.2 work với tokenizers 0.19 nhưng fail với 0.20.
  • uvicorn[standard]: bracket syntax cài thêm extras (websockets, httptools). Vẫn pin version chính.

Cách tạo requirements.txt từ environment hiện tại:

pip freeze > requirements.txt

Lưu ý: pip freeze bao gồm tất cả package kể cả transitive deps. Thực tế nên dùng pip-tools hoặc uv để phân biệt direct và transitive deps, nhưng với project nhỏ pip freeze là đủ.

8

BuildKit cache mount — pip nhanh hơn giữa các build

Docker Engine 24+ bật BuildKit mặc định. BuildKit cung cấp RUN --mount=type=cache để persist cache giữa các build mà không đưa cache vào image layer cuối.

Thay vì:

RUN pip install -r requirements.txt

Dùng:

# syntax=docker/dockerfile:1.7
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

Cách hoạt động: BuildKit mount thư mục cache pip vào quá trình build — pip download wheel vào đó và reuse ở build sau. Khi build hoàn tất, thư mục cache không được copy vào image cuối (PIP_NO_CACHE_DIR=0 vẫn có thể dùng khi kết hợp cache mount).

Kết quả thực tế:

  • Build lần đầu: chạy bình thường, download tất cả wheel.
  • Build lần 2 (thay đổi app code, requirements.txt không đổi): pip install gần như tức thì — tất cả wheel đã có trong cache.
  • Build lần 2 (thêm 1 package vào requirements.txt): chỉ download package mới, các package cũ lấy từ cache.

Lưu ý: cache mount là máy-local. Trên CI/CD (GitHub Actions, GitLab CI), cache BuildKit cần cấu hình thêm cache backend (S3, GCS, registry) để persist giữa các run khác nhau.

Ví dụ Dockerfile đầy đủ với cache mount:

# syntax=docker/dockerfile:1.7
FROM python:3.11.7-slim AS base

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential libgomp1 curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt .

RUN --mount=type=cache,target=/root/.cache/pip \
    pip install -r requirements.txt

RUN useradd -m -u 1000 appuser
COPY --chown=appuser:appuser . .
USER appuser

EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Khi dùng cache mount thì bỏ PIP_NO_CACHE_DIR=1 vì pip cần ghi vào cache dir đó. Thay vào đó chỉ giữ PIP_DISABLE_PIP_VERSION_CHECK=1.

9

Layer ordering tối ưu

Docker build cache hoạt động theo nguyên tắc: khi một layer thay đổi, tất cả layer sau đó bị invalidate. Vì vậy, sắp xếp layer theo nguyên tắc: ít thay đổi nhất lên đầu, hay thay đổi nhất xuống cuối.

Thứ tự đúng cho AI service:

  1. FROM — base image, gần như không đổi
  2. ENV — config Python, rất hiếm thay đổi
  3. System deps (apt-get) — thay đổi khi cần system lib mới, không thường xuyên
  4. COPY requirements.txt + pip install — thay đổi khi thêm/nâng version package
  5. Download model weight — nếu bake vào image, thay đổi khi đổi model
  6. COPY app code — thay đổi thường xuyên nhất
  7. USER / EXPOSE / CMD — hiếm thay đổi

Sai — COPY trước pip install

# Mỗi lần sửa main.py → pip install chạy lại → chậm
COPY . .
RUN pip install -r requirements.txt

Đúng — tách COPY requirements.txt riêng

# Chỉ khi requirements.txt thay đổi mới rebuild pip
COPY requirements.txt .
RUN pip install -r requirements.txt
# Layer pip install được cache — thay đổi main.py không ảnh hưởng
COPY . .

Lý do tách COPY requirements.txt . thành layer riêng: file này thay đổi ít hơn app code rất nhiều. Hầu hết các lần push code, chỉ .py thay đổi — pip install layer vẫn được dùng từ cache.

10

Non-root user và permissions

Container chạy root theo mặc định. Nếu có lỗ hổng trong app cho phép attacker thực thi lệnh trong container, họ có quyền root bên trong container đó. Chạy non-root là defense-in-depth cơ bản.

Pattern chuẩn:

# Tạo user với UID cố định
# -m : tạo home dir /home/appuser
# -u 1000 : UID 1000 (conventional cho user đầu tiên non-root)
RUN useradd -m -u 1000 appuser

WORKDIR /app

# Copy với chown để appuser có quyền đọc file
COPY --chown=appuser:appuser . .

# Chuyển sang non-root user — các CMD sau chạy với quyền này
USER appuser

Một số điểm cần lưu ý khi dùng non-root user:

Port >= 1024

Linux không cho phép process non-root bind port < 1024 theo mặc định. Uvicorn/FastAPI dùng 8000 — không vấn đề. Nếu muốn expose port 80 bên ngoài, dùng reverse proxy (nginx, Traefik) hoặc Docker port mapping -p 80:8000 thay vì chạy app trực tiếp trên port 80.

Ghi file

Nếu app cần ghi vào thư mục (log, temp file, model cache), thư mục đó phải thuộc về appuser hoặc được chmod phù hợp trước khi USER appuser:

RUN useradd -m -u 1000 appuser && \
    mkdir -p /app/models /app/logs && \
    chown -R appuser:appuser /app

USER appuser

Kết hợp với volume

Khi mount volume từ host, file trong volume thuộc về UID của host. Nếu host tạo thư mục với UID 0 (root) và container app chạy UID 1000, app không thể ghi vào đó. Cần đảm bảo UID match hoặc dùng permission phù hợp trên host directory.

11

Healthcheck

Docker healthcheck cho phép Docker (và các orchestrator như Kubernetes) biết container có thực sự sẵn sàng phục vụ request hay không — không chỉ là "process đang chạy".

HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:8000/health || exit 1

Giải thích các flag:

  • --interval=30s: Docker check mỗi 30 giây sau khi container đã healthy.
  • --timeout=10s: Nếu command không trả về trong 10 giây → coi là fail.
  • --start-period=60s: Quan trọng với AI app. Trong 60 giây đầu, fail không tính vào retries — cho phép model load vào RAM trước khi bắt đầu health check thực sự. Điều chỉnh theo thực tế startup time của app.
  • --retries=3: 3 lần fail liên tiếp mới đánh dấu container unhealthy.

Endpoint /health trong FastAPI:

from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
async def health():
    # Có thể thêm check: model loaded, GPU available...
    return {"status": "ok"}

Kiểm tra health status của container đang chạy:

docker inspect --format='{{.State.Health.Status}}' <container_id>
# starting | healthy | unhealthy

Nếu không muốn bake healthcheck vào image (để linh hoạt thay đổi endpoint), có thể khai báo trong docker-compose.yml thay thế:

services:
  api:
    image: my-app
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      start_period: 60s
      retries: 3
12

Đo image size

Ba cách để hiểu image size và phân bổ:

docker images

docker images my-app
# REPOSITORY   TAG     IMAGE ID       CREATED          SIZE
# my-app       latest  a1b2c3d4e5f6   2 minutes ago    1.23GB

Size ở đây là uncompressed size. Registry push size nhỏ hơn vì layer được nén.

docker history

docker history my-app --no-trunc
# IMAGE          CREATED         CREATED BY                                      SIZE
# a1b2c3d4e5f6   2 min ago       CMD ["uvicorn" "main:app" ...]                  0B
# <missing>      2 min ago       COPY . . # buildkit                             45.2kB
# <missing>      2 min ago       RUN pip install -r requirements.txt             876MB
# ...

Xem từng layer đóng góp bao nhiêu vào tổng size. Phát hiện layer nào "bất thường" lớn.

dive

dive là tool external (open source) cung cấp terminal UI để inspect từng layer, xem file nào nằm trong layer đó, và tính "wasted space" (file được thêm vào rồi xóa ở layer sau nhưng vẫn chiếm size). Cài qua brew install dive (macOS) hoặc apt install dive:

dive my-app

dive hữu ích để phát hiện các trường hợp như: apt cache không được cleanup đúng cách, file tạm được copy vào rồi xóa nhưng vẫn nằm trong layer.

13

Common pitfalls

1. Cài torch mặc định khi chạy CPU → +3 GB thừa

pip install torch==2.3.0 kéo wheel CUDA ~2.5 GB dù container chạy trên CPU-only host. Luôn thêm --index-url https://download.pytorch.org/whl/cpu cho CPU workload.

2. COPY . . trước pip install

Mỗi lần sửa bất kỳ file Python nào, layer COPY . . bị invalidate → pip install phải chạy lại từ đầu. Tách COPY requirements.txt . thành bước riêng trước pip install.

3. Không cleanup apt cache

# Sai — apt cache vẫn nằm trong layer
RUN apt-get update && apt-get install -y build-essential

# Đúng — cleanup trong cùng RUN command (khác RUN sẽ tạo layer mới không xóa được)
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

Quan trọng: rm -rf /var/lib/apt/lists/* phải ở trong cùng RUN với apt-get. Nếu là RUN riêng, Docker tạo layer mới — layer trước vẫn chứa cache và không thể xóa.

4. Pin major Python version thay vì minor

python:3.11-slim là floating tag — Docker pull phiên bản minor mới nhất mỗi khi cache miss. Giữa các lần build cách nhau vài tháng, có thể nhận 3.11.7 lần này và 3.11.9 lần sau. Dùng python:3.11.7-slim để lock minor version.

5. Build local mà không test trên CI

Máy local có thể có layer cache sẵn, khác biệt env, hoặc dùng local model đã download trước. CI build từ scratch — các vấn đề thực sự chỉ xuất hiện ở đó. Build image trên CI từ đầu (không có cache) ít nhất 1 lần trước khi ship.

6. Quên EXPOSE hoặc -p khi run

EXPOSE trong Dockerfile chỉ là documentation — không thực sự publish port. Container vẫn cần -p 8000:8000 khi run (hoặc ports: trong Compose). Thiếu EXPOSE không gây lỗi nhưng gây confusion khi đọc Dockerfile.

7. TORCH_HOME không được mount → mỗi container download lại model

Nếu set ENV TORCH_HOME=/cache nhưng không mount volume cho /cache, mỗi container restart sẽ phải download lại model weight. Đảm bảo volume được mount hoặc bake weight vào image nếu muốn persistent.

8. Dùng latest tag cho model/package

Không tồn tại transformers==latest nhưng code kiểu AutoModel.from_pretrained("bert-base-uncased") không chỉ định revision sẽ pull main branch của model repo — có thể thay đổi bất ngờ. Dùng revision="sha_hoặc_tag" nếu cần reproducibility hoàn toàn.

14

Tóm tắt

  • Base image cho CPU inference: python:3.11.7-slim. Pin minor version để reproducible.
  • PyTorch CPU wheel: thêm --index-url https://download.pytorch.org/whl/cpu để tránh kéo CUDA libs ~3 GB thừa.
  • Layer ordering: FROM → ENV → system deps → requirements.txt + pip → model weight (nếu bake) → app code. App code luôn cuối cùng.
  • BuildKit cache mount (RUN --mount=type=cache,target=/root/.cache/pip): pip nhanh hơn nhiều ở build thứ 2 trở đi.
  • Non-root user: useradd -m -u 1000 appuser + COPY --chown + USER appuser. Port >= 1024 khi non-root.
  • Healthcheck: --start-period=60s đủ thời gian cho model load trước khi Docker bắt đầu đánh giá health.
  • Pitfall quan trọng nhất: torch default wheel kéo CUDA, COPY . . trước pip, apt cache không cleanup trong cùng RUN.
15

Bài tiếp theo

Bài 34: Multi-stage build — giảm kích thước image — dùng nhiều stage trong một Dockerfile để tách môi trường build (có compiler, dev tools) khỏi image runtime (chỉ cần artifacts đã compile).