Sổ tay kỹ thuật

Dev trong container (compose watch, bind mount, debug Python)

how-to#docker#docker-compose#python· cập nhật 2026-11-08

Dùng khi muốn sửa code trên máy và chạy trong container với auto-sync bằng compose watch hoặc bind mount, kèm gắn debugger debugpy từ VS Code.

Sửa trang này
Mục lục

Tóm tắt: Có 2 cách đưa code vào container khi dev: bind mount (volumes: ./app:/srv/app) hoặc docker compose watch với khối develop.watch (action sync, sync+restart, rebuild). Debug Python bằng debugpy mở cổng 5678 trong container rồi attach từ VS Code. Stack nhiều service xem Docker Compose – pattern hay dùng.

Luồng hoạt động

flowchart LR A["Sửa code trên máy"] --> B{"Cách đưa code vào"} B -->|"bind mount"| C["Container thấy file ngay"] B -->|"compose watch"| D["sync / sync+restart / rebuild"] C --> E["App reload"] D --> E E --> F["debugpy :5678"] F --> G["VS Code attach"]

Cấu hình

Chọn bind mount hay watch

Tiêu chí Bind mount compose watch
Cấu hình volumes: - ./app:/srv/app develop.watch trong compose
Đổi dependency (pyproject.toml) Phải build lại tay action: rebuild tự làm
Thư mục sinh ra (.venv, node_modules) Dễ đè lên thư mục trong image Chỉ sync path khai báo, có ignore
Quyền file (UID/GID) Hay lệch giữa host và container File sync vào container
Phù hợp Code thay đổi liên tục, app tự reload Cần phân biệt sửa code / sửa config / đổi dependency

Compose cho dev

compose.yaml
name: netapp
 
services:
  api:
    build: .
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
    ports:
      - "127.0.0.1:8000:8000"
    environment:
      PYTHONUNBUFFERED: "1"
    develop:
      watch:
        - action: sync
          path: ./app
          target: /srv/app
          ignore:
            - __pycache__/
        - action: sync+restart
          path: ./config.yaml
          target: /srv/config.yaml
        - action: rebuild
          path: ./pyproject.toml
docker compose watch                 # build, up, rồi theo dõi file
docker compose up --watch            # tương đương, kèm log của service
docker compose up -d --build && docker compose watch --no-up   # tách up và watch

Ý nghĩa các action:

  • sync: copy file thay đổi vào target trong container, app tự reload (vd uvicorn --reload).
  • sync+restart: copy rồi restart container, dùng cho file config chỉ đọc lúc khởi động.
  • rebuild: build lại image và recreate container, dùng khi đổi dependency hoặc Dockerfile.
  • path tương đối so với thư mục chứa compose file; ignore nhận pattern kiểu gitignore.

Bind mount (cách đơn giản)

compose.override.yaml
services:
  api:
    volumes:
      - ./app:/srv/app
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload

Chỉ mount thư mục code, không mount cả thư mục project (tránh đè .venv hoặc node_modules đã cài trong image). compose.override.yaml được docker compose up tự đọc, xem phần override ở Docker Compose – pattern hay dùng.

Debug Python bằng debugpy

Thêm debugpy vào dev dependency, rồi dùng file override riêng cho debug:

compose.debug.yaml
services:
  api:
    command: >
      python -m debugpy --listen 0.0.0.0:5678
      -m uvicorn app.main:app --host 0.0.0.0 --port 8000
    ports:
      - "127.0.0.1:5678:5678"
docker compose -f compose.yaml -f compose.debug.yaml up -d
.vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach api (compose)",
      "type": "debugpy",
      "request": "attach",
      "connect": { "host": "localhost", "port": 5678 },
      "pathMappings": [
        { "localRoot": "${workspaceFolder}/app", "remoteRoot": "/srv/app" }
      ]
    }
  ]
}
  • Thêm --wait-for-client vào lệnh debugpy nếu cần dừng ngay từ lúc khởi động cho tới khi debugger attach.
  • Bỏ --reload khi debug: reloader của uvicorn chạy app ở tiến trình con, debugger hay mất kết nối.
  • Chỉ publish cổng debug về 127.0.0.1: debugpy cho phép thực thi code tuỳ ý, không được mở ra mạng.
  • pathMappings sai thì breakpoint hiện xám (unverified), vì VS Code không khớp file local với file trong container.

Lệnh

Mục đích Lệnh
Xem file compose sau merge docker compose -f compose.yaml -f compose.debug.yaml config
Vào shell service đang chạy docker compose exec api sh
Chạy lệnh với user khác docker compose exec -u root api sh
Chạy test một lần docker compose run --rm api pytest -q
Chạy một lần nhưng publish cổng của service docker compose run --rm --service-ports api sh
Log theo dõi docker compose logs -f --tail 100 api
Build lại không cache docker compose build --no-cache api
Dọn container và network docker compose down

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
Sửa file mà app không đổi App không có reload, hoặc path watch/mount sai Thêm --reload (dev) hoặc dùng sync+restart; kiểm tra path/target
watch báo không có gì để theo dõi Service thiếu khối develop.watch Khai báo develop.watch cho service cần sync
.venv/node_modules trong image bị biến mất Bind mount cả project đè lên thư mục trong image Chỉ mount thư mục code, hoặc dùng watch + ignore
File tạo trong container thuộc root trên host Container chạy root, ghi vào bind mount Chạy container bằng user: trùng UID host, hoặc dùng watch
Breakpoint không dừng pathMappings sai hoặc app chạy ở tiến trình con do reload Sửa mapping, bỏ --reload khi debug
Connection refused khi attach cổng 5678 Chưa publish cổng hoặc debugpy chưa chạy Dùng compose.debug.yaml, kiểm tra docker compose ps và log
Đổi dependency mà container vẫn thiếu package Image chưa build lại action: rebuild hoặc docker compose up -d --build

Tham khảo