Dockerize Django REST Framework: Từ Image ‘Cồng Kềnh’ Đến Bản Production Tinh Gọn

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

Dockerize ứng dụng Django: Đừng chỉ dừng lại ở việc ‘chạy được’

Cảnh tượng thường thấy: Bạn build xong Docker image cho Django và tá hỏa khi thấy nó nặng hơn 1GB. Deploy lên server thì chậm, còn RAM thì cứ báo đỏ liên tục vì gánh thêm hàng tá công cụ build dư thừa.

Mình từng rơi vào tình cảnh tương tự khi quản lý hệ thống hơn 30 container cho một sàn e-commerce. Lúc đó, việc tối ưu không còn là sở thích mà là yêu cầu sống còn để tiết kiệm chi phí cloud. Bằng cách áp dụng Multi-stage build, mình đã ép cân image từ 900MB xuống còn 180MB. Thời gian CI/CD cũng nhờ đó mà giảm từ 5 phút xuống còn chưa đầy 45 giây.

Bài viết này sẽ chia sẻ bộ khung chuẩn chỉnh để bạn đưa dự án Django REST Framework (DRF) lên production một cách chuyên nghiệp nhất.

Quick Start: Chạy ngay trong 5 phút

Nếu bạn đang vội, hãy tạo file Dockerfiledocker-compose.yml tại thư mục gốc theo cấu trúc dưới đây để thấy ngay thành quả.

. 
├── core/ (Django project)
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── .env

Kích hoạt toàn bộ stack bằng một lệnh duy nhất:

docker-compose up --build

Sau khi thấy app chạy mượt, hãy cùng mình mổ xẻ cách tối ưu từng chút một để biến nó thành một cỗ máy thực thụ.

1. Tuyệt chiêu Multi-stage Build: Ép cân cho Image

Tại sao image Python thường nặng? Đó là vì các thư viện như psycopg2 hay Pillow yêu cầu gccmusl-dev để biên dịch. Những công cụ này rất nặng nhưng lại hoàn toàn vô dụng khi app đã chạy.

# Stage 1: Builder - Nơi 'nấu nướng' các thư viện
FROM python:3.11-slim as builder

WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN apt-get update && apt-get install -y \
    build-essential libpq-dev --no-install-recommends

COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt

# Stage 2: Final - Chỉ giữ lại những gì cần thiết để chạy
FROM python:3.11-slim

WORKDIR /app
# Tạo user non-root để đảm bảo bảo mật
RUN addgroup --system app && adduser --system --group app

RUN apt-get update && apt-get install -y libpq-dev --no-install-recommends \
    && rm -rf /var/lib/apt/lists/*

COPY --from=builder /app/wheels /wheels
RUN pip install --no-cache /wheels/*

COPY . .
RUN chown -R app:app /app
USER app

CMD ["gunicorn", "core.wsgi:application", "--bind", "0.0.0.0:8000"]

Bằng cách này, toàn bộ bộ build (gcc, headers) bị bỏ lại ở Stage 1. Image cuối cùng của bạn sẽ cực kỳ tinh gọn và bảo mật hơn vì không chạy dưới quyền root.

2. Docker Compose: Gắn kết Web, Celery và Redis

Một hệ thống DRF thực tế luôn cần background worker để xử lý tác vụ nặng. Điểm mấu chốt là dùng chung một Dockerfile cho cả Web app và Celery worker. Điều này giúp đảm bảo môi trường đồng nhất 100% giữa các service.

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    env_file: .env

  redis:
    image: redis:7-alpine

  web:
    build: .
    command: gunicorn core.wsgi:application --bind 0.0.0.0:8000
    volumes: [ ".:/app" ]
    ports: [ "8000:8000" ]
    env_file: .env
    depends_on: [ db, redis ]

  worker:
    build: .
    command: celery -A core worker --loglevel=info
    env_file: .env
    depends_on: [ db, redis ]

volumes:
  postgres_data:

Lưu ý nhỏ: depends_on chỉ đảm bảo container DB khởi động trước, chứ không đợi DB sẵn sàng nhận kết nối. Ở môi trường production, bạn nên dùng thêm script wait-for-it.

3. Xử lý Entrypoint và Migration tự động

Đừng bao giờ chạy migration thủ công trên server. Hãy để Docker tự làm việc đó mỗi khi khởi động thông qua file entrypoint.sh.

#!/bin/sh

# Kiểm tra DB đã sẵn sàng chưa trước khi chạy migration
python manage.py migrate --noinput
python manage.py collectstatic --noinput

exec "$@"

Đừng quên lệnh chmod +x entrypoint.sh. Nếu thiếu bước này, container của bạn sẽ báo lỗi ‘Permission denied’ ngay khi khởi động.

4. Kinh nghiệm thực chiến để tránh ‘ăn hành’

Triệt tiêu file rác với .dockerignore

Mỗi file bạn copy vào image đều làm tăng dung lượng và rủi ro bảo mật. Hãy tạo .dockerignore để loại bỏ .git, __pycache__ và các file môi trường .env không cần thiết.

Giới hạn tài nguyên (Resource Limits)

Một bug rò rỉ bộ nhớ (memory leak) trong Django có thể kéo sập toàn bộ VPS. Hãy luôn giới hạn RAM cho từng service trong file compose. Ví dụ: memory: 512M là con số hợp lý cho một instance Django tầm trung.

Quản lý Log thông minh

Mặc định, Docker lưu log dưới dạng JSON và nó sẽ phình to cho đến khi đầy ổ cứng. Hãy cấu hình max-size: "10m" để Docker tự động xoay vòng log, tránh tình trạng server chết đứng vào lúc nửa đêm.

Tổng kết

Dockerize Django chuẩn không chỉ là viết Dockerfile cho chạy được. Đó là nghệ thuật cân bằng giữa dung lượng image, tốc độ build và tính bảo mật.

Với bộ khung Multi-stage và cách tổ chức Docker Compose như trên, bạn đã có một nền tảng vững chắc để scale app lên hàng chục container mà không sợ rối. Nếu dự án lớn hơn, hãy cân nhắc thêm Nginx để xử lý file tĩnh và SSL. Chúc các bạn deploy thành công!

Share: