Lập trình

Docker Compose cho môi trường phát triển nhất quán

“Máy em chạy được” thường là dấu hiệu môi trường phát triển chưa được mô tả đủ rõ. Khác phiên bản runtime, database, extension hoặc biến môi trường có thể khiến onboarding kéo dài và lỗi chỉ xuất hiện trên máy của một vài thành viên.

Docker Compose cho môi trường phát triển nhất quán

“Máy em chạy được” thường là dấu hiệu môi trường phát triển chưa được mô tả đủ rõ. Khác phiên bản runtime, database, extension hoặc biến môi trường có thể khiến onboarding kéo dài và lỗi chỉ xuất hiện trên máy của một vài thành viên.

Docker Compose giúp mô tả ứng dụng nhiều service trong một file có thể version control. Mục tiêu không phải container hóa mọi thứ bằng mọi giá, mà là tạo một quy trình dựng, chạy, reset và kiểm tra giống nhau cho cả nhóm.

Bắt đầu từ các service thực sự cần thiết

Một ứng dụng web thường cần app, database, cache và đôi khi có worker. Ví dụ:

services:
  app:
    build:
      context: .
      target: development
    ports:
      - "8080:8080"
    env_file:
      - .env.docker
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:17
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - db-data:/var/lib/postgresql/data
    secrets:
      - db_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:8-alpine

volumes:
  db-data:

secrets:
  db_password:
    file: ./.secrets/db_password

Hãy pin major version hoặc version cụ thể theo chính sách dự án. Dùng tag latest làm môi trường thay đổi ngoài ý muốn và khó tái hiện lỗi.

depends_on không đồng nghĩa service đã sẵn sàng

Container database có thể đã chạy nhưng vẫn đang khởi tạo. Compose chỉ chờ healthcheck khi dependency dùng điều kiện service_healthy. Vì vậy, healthcheck cần kiểm tra khả năng phục vụ thật, không chỉ kiểm tra process tồn tại.

Dù có healthcheck, ứng dụng vẫn nên có retry kết nối ngắn hạn. Database có thể restart sau khi toàn bộ stack đã chạy.

Phân biệt bind mount và named volume

LoạiPhù hợpLưu ý
Bind mountSource code cần sửa trực tiếpPhụ thuộc filesystem và quyền của host
Named volumeDữ liệu database, cache dependencyDocker quản lý, dễ giữ qua lần restart
tmpfsDữ liệu tạm, test nhanhMất khi container dừng

Không mount cả thư mục dự án một cách máy móc. Với Node.js, mount host node_modules giữa các hệ điều hành có thể gây lỗi binary. Có thể dùng named volume riêng cho dependency hoặc build dependency trong image.

Tăng tốc vòng lặp phát triển với Compose Watch

Compose Watch cho phép đồng bộ source, rebuild khi file dependency thay đổi hoặc restart service theo rule:

services:
  app:
    build: .
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: ./package.json
docker compose watch

Watch hữu ích khi bind mount chậm hoặc dự án cần kiểm soát rõ file nào được đồng bộ. Hãy đưa thư mục build, dependency và secret vào ignore phù hợp.

Tách cấu hình chung và cấu hình phát triển

Giữ compose.yaml mô tả nền tảng chung, còn compose.override.yaml thêm port debug, bind mount hoặc tool chỉ dùng local. Cách này giảm nguy cơ cấu hình phát triển lọt vào production.

docker compose config

Lệnh trên render cấu hình sau khi merge và thay biến. Đây là bước kiểm tra rất đáng làm trong CI vì nó phát hiện YAML sai, biến thiếu và cấu hình override ngoài dự kiến.

Không đưa secret vào Git

File .env tiện cho cấu hình không nhạy cảm trong local, nhưng secret thật không nên commit hoặc ghi trực tiếp trong compose file. Có thể dùng Compose secrets, file local bị ignore hoặc secret manager của môi trường triển khai.

  • Commit .env.example với tên biến và giá trị giả.
  • Đặt quyền file phù hợp cho secret local.
  • Không ghi secret vào Dockerfile vì layer image có thể lưu lại.
  • Không dùng chung credential production cho local.

Tạo image phát triển có cache tốt

Copy manifest dependency trước source code để tận dụng build cache:

FROM node:22-alpine AS development
WORKDIR /app

COPY package.json package-lock.json ./
RUN npm ci

COPY . .
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]

Thêm .dockerignore để loại .git, log, output build, dependency host và file secret khỏi build context.

Chuẩn hóa các lệnh thường dùng

docker compose up --build
docker compose exec app npm test
docker compose logs -f app
docker compose down
docker compose down -v

down -v xóa named volume, nghĩa là mất dữ liệu local. Nên đặt lệnh reset dữ liệu trong script có tên rõ ràng thay vì yêu cầu thành viên nhớ nhiều cờ.

Đừng dùng Compose như bản sao production tuyệt đối

Compose rất tốt cho local, CI và môi trường đơn host. Production có thể dùng nền tảng orchestration hoặc dịch vụ managed khác. Điều cần giữ nhất quán là version runtime, dependency, biến cấu hình, migration và health contract, không nhất thiết là mọi chi tiết hạ tầng.

Checklist cho repository

  • compose.yaml, Dockerfile và lockfile được commit.
  • Image được pin version phù hợp; không phụ thuộc latest.
  • Service có healthcheck thực tế và app có retry kết nối.
  • Source, dependency và dữ liệu được mount bằng loại volume phù hợp.
  • Secret không nằm trong Git hoặc image layer.
  • Có lệnh một bước để start, test, xem log và reset.
  • docker compose config được kiểm tra trong CI.

Nguồn tham khảo

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.