ข้ามไปยังเนื้อหา

การ Dockerize Python App

ถ้าคุณเคย containerize Node/TypeScript app มาก่อน คุณรู้ pattern: copy package.json, รัน npm ci, copy source, build และรัน Python Dockerfile ตาม multi-stage logic เดียวกัน — แต่มี best practices เฉพาะ Python บางอย่างที่คุณต้องเข้าใจ

TypeScript
# ---- Node / TypeScript multi-stage Dockerfile ----
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
CMD ["node", "dist/main.js"]
Python
# ---- Python multi-stage Dockerfile ----
FROM python:3.12-slim AS builder
WORKDIR /app
# ติดตั้ง build tools แล้วสร้าง venv ภายใน image
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim AS runner
WORKDIR /app
# Copy pre-built venv จาก builder
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
# Copy application source
COPY src/ ./src/
# รันด้วย non-root user
RUN addgroup --system appgroup && adduser --system --ingroup appgroup appuser
USER appuser
EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]
# syntax=docker/dockerfile:1
# ---- Stage 1: Build / dependency installation ----
FROM python:3.12-slim AS builder
WORKDIR /app
# PYTHONDONTWRITEBYTECODE: ไม่เขียน .pyc files (ประหยัด space)
# PYTHONUNBUFFERED: log output ทันที (12-Factor: log ไปยัง stdout)
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# สร้าง virtual environment ภายใน image
# ทำให้เรา copy ทั้ง /app/.venv ไปยัง final stage ได้
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
# Copy dependency manifest ก่อน (Docker layer caching)
COPY pyproject.toml uv.lock* requirements.txt* ./
# ติดตั้ง dependencies — no cache เพื่อให้ image เล็ก
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# ---- Stage 2: Runtime image ----
FROM python:3.12-slim AS runner
WORKDIR /app
# Copy venv ที่มี dependencies ครบจาก builder
COPY --from=builder /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Copy application source (หลัง deps — cache hit ที่ดีกว่าเมื่อ rebuild)
COPY src/ ./src/
# สร้างและเปลี่ยนไปใช้ non-root user (security best practice)
RUN addgroup --system appgroup && \
adduser --system --ingroup appgroup --no-create-home appuser
USER appuser
# Expose port ที่ app รับฟัง
EXPOSE 8000
# CMD ใช้ exec form (ไม่มี shell wrapper — signals ส่งตรงถึง process)
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]

รันในเทอร์มินัลของคุณเพื่อ build และรัน:

Terminal window
docker build -t my-python-app .
docker run -p 8000:8000 my-python-app
# หรือด้วย docker compose:
# docker compose up --build

เหมือน .gitignore สำหรับ Docker สร้างเสมอเพื่อหลีกเลี่ยงการ copy venv, cache และ secrets ลงใน image:

.venv/
__pycache__/
*.pyc
*.pyo
.pytest_cache/
.mypy_cache/
.ruff_cache/
.env
.env.*
*.log
ทำไมจึงควรสร้าง virtual environment ภายใน Docker image แทนที่จะใช้ system Python?
PYTHONUNBUFFERED=1 รับประกันอะไรใน containerized Python app?
ใน multi-stage Dockerfile คุณนำ installed packages จาก builder stage เข้าสู่ runner stage อย่างไร?