Docker Compose từ A-Z: chạy cả stack bằng một file

Chia sẻ bài viết

Mục lục
Docker Compose từ A-Z: chạy cả stack bằng một file

Trả lời nhanh: Docker Compose là công cụ khai cả cụm dịch vụ trong một file YAML, app, database, cache, automation, rồi vận hành bằng lệnh ngắn: docker compose up -d dựng tất cả đúng thứ tự, docker compose down hạ gọn gàng. Giá trị lớn nhất: hạ tầng thành văn bản, commit vào git, chuyển máy mới chỉ việc chép file và up lại, và AI agent đọc hiểu để tự vận hành giúp bạn.

Chạy lẻ từng docker run với chuỗi flag dài dằng dặc chỉ vui được hai container đầu. Đến container thứ ba, khi app cần database, database cần đợi sẵn sàng, cả đám cần chung mạng, bạn cần Compose. Bài này đi từ khái niệm đến một file mẫu chạy trọn vibe stack: web app + Postgres + Redis + n8n trên một VPS.

Tóm tắt nhanh
  • Compose = hạ tầng thành văn bản: services, volumes, networks trong một YAML
  • up -d / down / logs -f / ps, bốn lệnh vận hành cả cụm
  • Volume có tên cho dữ liệu bền; mạng nội bộ tự có, service gọi nhau bằng tên
  • File mẫu vibe stack cuối bài: app + Postgres + Redis + n8n, dán là chạy

Từ docker run đến compose: cùng nội dung, khác đẳng cấp quản lý

Một lệnh docker run dài chính là một service trong compose, viết lại thành cấu trúc:

services:
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_PASSWORD: doi-mat-khau-nay
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "127.0.0.1:5432:5432"
volumes:
  pgdata:

docker compose up -d: chạy. Điểm ăn tiền không phải cú pháp mà là chỗ để: cấu hình giờ nằm trong file cạnh dự án, ai (kể cả agent AI, kể cả bạn-của-6-tháng-sau) nhìn vào cũng biết hệ thống gồm gì.

Bốn khái niệm gánh cả framework

  • services: mỗi dịch vụ một khối, image dùng gì, env nào, mở port đâu, restart: unless-stopped để sống qua reboot.
  • volumes có tên: dữ liệu bền qua đời container, mọi thứ là database/cấu hình phải nằm trong volume, không là mất khi recreate.
  • mạng nội bộ: compose tự tạo network riêng, service gọi nhau bằng tên: app kết nối chuỗi postgres://db:5432, khỏi IP khỏi port ngoài.
  • depends_on: khai thứ tự dựng; kèm healthcheck khi app đòi database thật sự sẵn sàng chứ không chỉ đã bật.

File mẫu: vibe stack hoàn chỉnh trên một VPS

services:
  app:
    build: ./app              # Dockerfile cua du an (bai Docker build cham co mau)
    restart: unless-stopped
    environment:
      DATABASE_URL: postgres://postgres:doi-mk@db:5432/shop
      REDIS_URL: redis://cache:6379
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      db: { condition: service_healthy }

  db:
    image: postgres:16
    restart: unless-stopped
    environment: { POSTGRES_PASSWORD: doi-mk, POSTGRES_DB: shop }
    volumes: [ pgdata:/var/lib/postgresql/data ]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10

  cache:
    image: redis:7-alpine
    restart: unless-stopped
    volumes: [ redisdata:/data ]

  n8n:
    image: docker.n8n.io/n8nio/n8n
    restart: unless-stopped
    environment:
      - WEBHOOK_URL=https://n8n.ten-mien.vn/
      - GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
    volumes: [ n8ndata:/home/node/.n8n ]
    ports:
      - "127.0.0.1:5678:5678"

volumes: { pgdata: {}, redisdata: {}, n8ndata: {} }

Caddy đứng ngoài trỏ tên miền vào 3000 và 5678 (mẫu ở bài cài n8n). Một file này + một VPS = sản phẩm, database, cache, automation, cả startup trong 40 dòng YAML.

Vận hành hằng ngày và bẫy cần né

  • docker compose logs -f app: dò lỗi một service; ps: trạng thái cả cụm; pull && up -d: nâng cấp image (backup volume trước với database).
  • Bẫy đổi env không ăn: sửa environment phải up -d lại (recreate), restart suông không đọc env mới.
  • Bẫy down -v: chữ -v xóa luôn volume, dữ liệu bay màu. Gõ down trần trụi là đủ hạ cụm.
  • Secrets: mật khẩu để file .env cạnh compose (tự động đọc), file đó vào .gitignore, YAML sạch, git sạch.

Cả cụm này sống trên một ổ đĩa: Postgres fsync, Redis snapshot, n8n ghi execution, app ghi log, cộng dồn là dòng I/O liên tục. Đây chính là kịch bản bài chia tài nguyên nhiều service phân tích sâu, và là nơi ổ NVMe enterprise trả lương chăm chỉ nhất.

Mười lệnh vận hành hằng ngày

docker compose up -d              # dung ca cum, chay nen
docker compose ps                 # xem trang thai tung service
docker compose logs -f app        # xem log mot service, theo doi truc tiep
docker compose logs --tail=100    # xem 100 dong cuoi cua tat ca
docker compose restart app        # khoi dong lai mot service
docker compose exec db psql -U postgres   # vao trong container dang chay
docker compose pull && docker compose up -d   # cap nhat image roi dung lai
docker compose down               # ha cum, GIU volume
docker compose down -v            # ha cum, XOA CA VOLUME, can than
docker compose config             # kiem cu phap va xem file sau khi gop bien

Lệnh cuối đáng dùng trước mỗi lần sửa tệp: nó in ra cấu hình cuối cùng sau khi thay biến môi trường, nên bạn thấy đúng thứ sẽ chạy chứ không phải thứ mình nghĩ.

Ba thứ nên có trong mọi tệp compose chạy thật

MụcVì sao cầnCách khai
healthcheckĐể depends_on chờ dịch vụ sẵn sàng thật, không phải chỉ chờ container khởi độngtest: CMD-SHELL pg_isready -U postgres, kèm interval và retries
Giới hạn logLog mặc định không giới hạn, chạy vài tháng là đầy ổKhối logging với driver: json-file và tùy chọn max-size: 10m, max-file: 3
Giới hạn tài nguyênMột container rò bộ nhớ không kéo sập cả máyKhai mem_limit: 512m cho từng service

Về depends_on: khai suông chỉ bảo đảm thứ tự khởi động, không bảo đảm dịch vụ đã sẵn sàng nhận kết nối. Muốn chờ thật thì phải kèm condition: service_healthy và dịch vụ kia phải có healthcheck. Đây là nguyên nhân số một của lỗi ứng dụng không nối được cơ sở dữ liệu ở lần khởi động đầu.

Bảo mật: ba lỗi hay gặp trong tệp compose

  • Mở cổng cơ sở dữ liệu ra ngoài. Viết "5432:5432" là phơi ra mọi nơi. Viết "127.0.0.1:5432:5432" là chỉ mở cho chính máy đó. Các dịch vụ trong cùng cụm gọi nhau bằng tên, không cần mở cổng ra ngoài chút nào.
  • Mật khẩu viết thẳng trong tệp rồi đẩy lên kho mã nguồn. Đưa vào tệp .env cạnh tệp compose, và thêm .env vào .gitignore. Compose tự đọc tệp đó.
  • Không cố định phiên bản image. Dùng postgres:16 chứ đừng dùng postgres:latest. Một sáng đẹp trời image mới đổi phiên bản lớn là dữ liệu không mở được, và bạn sẽ mất cả ngày để hiểu chuyện gì xảy ra.

Sao lưu dữ liệu trong volume

# Sao luu co so du lieu, cach dung nhat
docker compose exec -T db pg_dump -U postgres tendb | gzip > backup-$(date +%F).sql.gz

# Sao luu ca volume theo kieu tho
docker run --rm -v ten_volume:/data -v $(pwd):/backup alpine \
  tar czf /backup/volume-$(date +%F).tar.gz -C /data .

Cách đầu dùng cho cơ sở dữ liệu vì nó lấy bản nhất quán. Cách hai dùng cho volume chứa tệp tĩnh. Đặt cả hai vào một tệp kịch bản chạy theo lịch, và nhớ chép bản sao sang máy khác, giữ trên cùng máy chủ là không có tác dụng khi máy chủ hỏng.

Câu hỏi thường gặp

docker compose và docker-compose khác gì nhau?

docker compose (hai từ) là plugin hiện hành đi cùng Docker; docker-compose gạch ngang là bản cũ độc lập. File YAML gần như tương thích, cứ dùng bản hai từ, tài liệu cũ đọc vẫn hiểu được.

Compose có dùng cho production được không?

Với quy mô một-vài VPS: hoàn toàn, restart policy + healthcheck + backup volume là đủ nghiêm túc. Kubernetes chỉ cần đến khi bạn có nhiều máy và đội vận hành riêng; đừng vác dao mổ trâu giết gà.

Sửa file compose xong có phải down rồi up không?

Không cần down: docker compose up -d tự so khác biệt và chỉ recreate service thay đổi, các service khác chạy tiếp không gián đoạn.

Nhiều dự án compose trên một VPS có đụng nhau không?

Không, mỗi thư mục dự án là một 'project' tách biệt: mạng riêng, tên container riêng theo prefix thư mục. Chỉ cần tránh đụng port công khai (mỗi dự án một port 127.0.0.1 khác nhau, Caddy phân phối tên miền).

Hạ tầng NVMe Enterprise của TND
VPS ổ Enterprise U.2 NVMe, CPU xung cao, đặt tại Việt Nam
File mẫu trong bài dựng cả startup trên một máy, máy đó nên là TND VPS NVMe: Postgres, Redis, n8n cùng fsync trên Enterprise U.2 NVMe RAID 10 không giành nhau, Xeon Platinum xung cao cho app phản hồi lẹ. Gói 6-8 GB RAM là cả cụm thở khỏe, bàn giao 5 phút, backup máy chủ hàng tuần đỡ lưng cho volume của bạn.
Xem bảng giá VPS NVMe Xeon Platinum

Bài viết liên quan