Docker Compose: Chạy Ứng Dụng Nhiều Container

K
Kai··4 min read

Một ứng dụng thật hiếm khi chỉ có một container. Thường là web + database + cache, mỗi thứ một container. Chạy tay từng docker run với đủ cờ -p, -v, --network, -e... rất rườm rà và dễ sai. Docker Compose giải quyết: khai báo toàn bộ trong một file, chạy bằng một lệnh.

Compose là gì

Compose cho phép mô tả các container (gọi là service), mạng và volume của một ứng dụng trong một file YAML (compose.yaml). Sau đó docker compose up dựng tất cả; docker compose down dọn tất cả. Toàn bộ cấu hình nằm trong file, đưa vào Git được, và tái lập y hệt ở máy khác.

Compose nay là một lệnh con tích hợp: docker compose (có dấu cách). Bản cũ là docker-compose (có gạch nối) chạy riêng — nếu thấy tài liệu cũ dùng dạng gạch nối thì hiểu là cùng công cụ.

Một compose file tối thiểu

Tạo compose.yaml cho một web server và một cache Redis:

services:
  web:
    image: nginx:alpine
    ports:
      - "8088:80"
    depends_on:
      - cache
  cache:
    image: redis:alpine

Đọc file: hai service webcache. web dùng image nginx, publish cổng 8088→80, và depends_on: cache (khởi động cache trước). cache dùng image redis.

Chạy cả hai:

docker compose up -d
 Container ..._cache-1  Started
 Container ..._web-1    Started

Xem trạng thái:

docker compose ps
SERVICE   STATUS
cache     Up
web       Up

curl http://localhost:8088 trả về trang nginx (HTTP 200). Chỉ một lệnh, cả hai container đã chạy với đúng cấu hình.

Điểm hay nhất: service gọi nhau bằng tên

Compose tự tạo một user-defined network cho ứng dụng (nhớ Bài 7: user-defined bridge có DNS nội bộ). Mọi service trong cùng compose file nằm chung mạng đó và gọi nhau bằng tên service.

docker compose exec web ping -c 1 cache
PING cache (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.160 ms

web gọi được cache chỉ bằng cái tên cache — không cần biết IP. Đây là lý do thực dụng nhất để dùng Compose: trong code của web, bạn cấu hình host của Redis là cache, host của database là db... đúng tên service. Không phải dò IP, không phải --link lỗi thời.

   Compose project
   ┌──────────────────────────────────────────┐
   │   mạng riêng (Compose tự tạo)              │
   │                                            │
   │   [web]  ──gọi "cache"──►  [cache]         │
   │     │                                      │
   │   :8088 (publish ra host)                  │
   └──────────────────────────────────────────┘

Compose file đầy đủ hơn

Ví dụ thực tế: app của bạn (build từ Dockerfile ở Bài 5) + PostgreSQL có volume giữ dữ liệu.

services:
  app:
    build: .                    # build từ Dockerfile trong thư mục hiện tại
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: "postgres://postgres:secret@db:5432/appdb"
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
    volumes:
      - pgdata:/var/lib/postgresql/data   # volume giữ dữ liệu (Bài 6)

volumes:
  pgdata:

Vài điểm:

  • build: . thay cho image: — Compose tự build image từ Dockerfile. Dùng image: khi lấy image có sẵn, build: khi tự dựng.
  • environment truyền biến môi trường vào container. Để ý DATABASE_URL trỏ host là db — đúng tên service, nhờ DNS nội bộ.
  • volumes ở mức service gắn volume; khối volumes: ở cuối khai báo named volume pgdata (Bài 6).
  • depends_on quyết định thứ tự khởi động, nhưng lưu ý: nó chỉ đợi container kia start, không đợi dịch vụ bên trong sẵn sàng nhận kết nối. Database có thể đang khởi động khi app đã chạy. Ứng dụng thật nên tự thử-lại kết nối, hoặc dùng healthcheck (condition: service_healthy).

Các lệnh Compose hay dùng

docker compose up -d           # dựng và chạy nền tất cả service
docker compose up -d --build   # build lại image trước khi chạy
docker compose ps              # trạng thái các service
docker compose logs -f         # xem log gộp của tất cả service
docker compose logs -f app     # log của một service
docker compose exec app sh     # mở shell trong service app
docker compose down            # dừng và xóa container + mạng
docker compose down -v         # xóa luôn cả volume (mất dữ liệu!)

🧹 Dọn dẹp

Compose dọn gọn ngay trong một lệnh — đây là một cái tiện nữa của nó:

docker compose down

Lệnh này xóa các container và mạng mà Compose đã tạo. Volume được giữ lại mặc định (để khỏi mất dữ liệu); thêm -v nếu muốn xóa cả volume:

docker compose down -v

Tổng kết

Docker Compose mô tả cả ứng dụng nhiều container trong một file YAML và quản lý bằng up/down. Nó tự tạo mạng riêng nên các service gọi nhau bằng tên — giải quyết gọn việc kết nối mà Bài 7 phải làm thủ công. Compose là công cụ chuẩn để chạy ứng dụng nhiều thành phần trên một máy, cả lúc dev lẫn trên server nhỏ.

Image của ta đến giờ chạy tốt nhưng có thể còn to và chưa tối ưu. Bài 9 học cách làm image nhỏ và an toàn hơn bằng multi-stage build và vài best practice — trước khi bước sang Docker Swarm để chạy trên nhiều máy.