❤️ AZDIGI chính thức cập nhật hệ thống blog mới hoàn chỉnh. Tuy nhiên có thể một số bài viết bị sai lệch hình ảnh, hãy ấn nút Báo cáo bài viết ở cuối bài để AZDIGI cập nhật trong thời gian nhanh nhất. Chân thành cám ơn.

Bài 5, bạn đã học cách dùng Docker Volume để lưu trữ dữ liệu bền vững và Docker Network để kết nối các container với nhau. Cuối bài, bạn đã triển khai thành công WordPress + MySQL bằng cách chạy thủ công từng lệnh docker run dài ngoằng.

Docker Compose cơ bản
Minh họa: Docker Compose quản lý nhiều container

Vấn đề là: mỗi lần muốn chạy lại hệ thống đó, bạn phải nhớ đúng thứ tự, đúng tham số, đúng tên volume/network. Quên một flag là lỗi. Sai một chỗ là debug cả buổi.

Trong bài này, mình sẽ giới thiệu Docker Compose : công cụ giúp bạn gói toàn bộ hệ thống multi-container vào một file YAML duy nhất, rồi chạy lên chỉ với một lệnh. Không cần nhớ gì cả , tất cả đã nằm trong file.

Vấn đề: Chạy nhiều container bằng docker run

Hãy nhìn lại những gì bạn phải làm ở Bài 5 để chạy WordPress + MySQL:

# Bước 1: Tạo network
docker network create wp-network

# Bước 2: Tạo volume cho MySQL docker volume create wp-db-data

# Bước 3: Tạo volume cho WordPress docker volume create wp-content

# Bước 4: Chạy MySQL docker run -d \ --name wp-mysql \ --network wp-network \ -e MYSQL_ROOT_PASSWORD=rootpass123 \ -e MYSQL_DATABASE=wordpress \ -e MYSQL_USER=wpuser \ -e MYSQL_PASSWORD=wppass123 \ -v wp-db-data:/var/lib/mysql \ mysql:8.0

# Bước 5: Chạy WordPress docker run -d \ --name wp-app \ --network wp-network \ -p 8080:80 \ -e WORDPRESS_DB_HOST=wp-mysql \ -e WORDPRESS_DB_USER=wpuser \ -e WORDPRESS_DB_PASSWORD=wppass123 \ -e WORDPRESS_DB_NAME=wordpress \ -v wp-content:/var/www/html/wp-content \ wordpress:latest

5 lệnh, hàng chục tham số, phải chạy đúng thứ tự. Và đây mới chỉ là 2 container. Thực tế bạn có thể cần thêm phpMyAdmin, Redis, Nginx reverse proxy, lúc đó sẽ là 10+ lệnh.

Bây giờ, cùng nội dung trên nhưng với Docker Compose:

services:
  mysql:
    image: mysql:8.0
    volumes:
      - wp-db-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: rootpass123
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: wppass123

wordpress: image: wordpress:latest ports: - "8080:80" volumes: - wp-content:/var/www/html/wp-content environment: WORDPRESS_DB_HOST: mysql WORDPRESS_DB_USER: wpuser WORDPRESS_DB_PASSWORD: wppass123 WORDPRESS_DB_NAME: wordpress depends_on: - mysql

volumes: wp-db-data: wp-content:

Lưu vào file docker-compose.yml, rồi chạy:

docker compose up -d

Một lệnh duy nhất. Docker Compose tự tạo network, tạo volume, chạy từng container đúng thứ tự. Muốn dừng và xoá tất cả? Cũng một lệnh:

docker compose down

Đây chính là lý do Docker Compose tồn tại, biến một đống lệnh dài thành một file config dễ đọc, dễ chia sẻ, dễ version control bằng Git.

Docker Compose là gì?

Docker Compose là công cụ cho phép bạn định nghĩa và chạy ứng dụng multi-container bằng một file YAML. Thay vì gõ từng lệnh docker run, bạn mô tả toàn bộ hệ thống (services, volumes, networks) trong file docker-compose.yml, rồi dùng một lệnh để quản lý tất cả.

Đã cài sẵn từ Bài 2

Nếu bạn đã cài Docker theo hướng dẫn ở Bài 2 (cài từ repo chính thức), Docker Compose đã được cài sẵn dưới dạng plugin (docker-compose-plugin). Không cần cài thêm gì.

Kiểm tra nhanh:

docker compose version
Docker Compose version v2.32.4

Nếu bạn thấy output tương tự thì đã sẵn sàng.

docker compose (v2) vs docker-compose (v1)

Bạn có thể thấy trên mạng nhiều hướng dẫn dùng lệnh docker-compose (có dấu gạch ngang). Đó là Compose v1: phiên bản cũ, viết bằng Python, đã ngừng hỗ trợ từ tháng 7/2023.

Phiên bản hiện tại là Compose v2, được tích hợp trực tiếp vào Docker CLI dưới dạng plugin. Lệnh dùng là docker compose (có dấu cách, không có gạch ngang).

Trong toàn bộ serie này, mình chỉ dùng docker compose (v2). Nếu bạn gặp hướng dẫn dùng docker-compose, hãy thay bằng docker compose: cú pháp giống hệt nhau.

Cấu trúc file docker-compose.yml

File docker-compose.yml sử dụng định dạng YAML. Nếu bạn chưa quen YAML, chỉ cần nhớ: thụt lề bằng dấu cách (space), không dùng tab. Mỗi level thụt 2 spaces.

Dưới đây là cấu trúc tổng quan của một file docker-compose.yml với đầy đủ các key phổ biến:

services:
  ten-service-1:
    image: image-name:tag
    build: ./path-to-dockerfile
    container_name: ten-container
    ports:
      - "host-port:container-port"
    volumes:
      - volume-name:/path/in/container
      - ./local-path:/path/in/container
    environment:
      KEY: value
    env_file:
      - .env
    networks:
      - ten-network
    depends_on:
      - ten-service-khac
    restart: unless-stopped

ten-service-2: image: another-image:tag # ...

volumes: volume-name:

networks: ten-network:

Bây giờ mình sẽ giải thích từng key chi tiết.

version (deprecated)

Trước đây, file docker-compose.yml bắt đầu bằng dòng version: "3.8" để chỉ định phiên bản Compose file format. Tuy nhiên, từ Docker Compose v2, key này không còn cần thiết và đã bị đánh dấu deprecated.

Nếu bạn thêm version, Compose sẽ hiện warning:

WARN[0000] /path/docker-compose.yml: the attribute `version` is obsolete

Trong serie này, mình sẽ không dùng key version. Nếu bạn đọc được file cũ có version: "3" hay version: "3.8", cứ bỏ đi, không ảnh hưởng gì.

services

Đây là key quan trọng nhất: nơi bạn định nghĩa các container sẽ chạy. Mỗi service tương đương với một container.

services:
  web:        # service thứ nhất — sẽ tạo container cho web server
    image: nginx:latest
database:   # service thứ hai — sẽ tạo container cho database
    image: mysql:8.0

Tên service (web, database) do bạn tự đặt. Docker Compose sẽ dùng tên này làm hostname trong network nội bộ, giống cách bạn dùng tên container để gọi nhau ở Bài 5.

image và build

Mỗi service cần biết chạy từ image nào. Có 2 cách:

Dùng image có sẵn từ Docker Hub:

services:
  web:
    image: nginx:latest

Build image từ Dockerfile:

services:
  app:
    build: ./app          # thư mục chứa Dockerfile
    # hoặc chi tiết hơn:
    build:
      context: ./app      # build context
      dockerfile: Dockerfile.prod   # tên Dockerfile (nếu không phải mặc định)

Bạn cũng có thể dùng cả image lẫn build: khi đó Compose sẽ build image và đặt tên theo giá trị image:

services:
  app:
    build: ./app
    image: my-app:latest   # image được build sẽ có tên my-app:latest

ports

Map port từ host vào container, giống flag -p trong docker run:

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"         # host:container — truy cập port 8080 trên host → port 80 trong container
      - "443:443"         # có thể map nhiều port

Lưu ý: Luôn bọc giá trị port trong dấu ngoặc kép ("8080:80"). Nếu không, YAML có thể hiểu sai kiểu dữ liệu (ví dụ 80:80 sẽ bị parse thành số thay vì chuỗi).

volumes

Mount dữ liệu vào container, giống flag -v trong docker run. Hỗ trợ cả named volume và bind mount:

services:
  database:
    image: mysql:8.0
    volumes:
      - db-data:/var/lib/mysql          # Named volume — Docker quản lý
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql   # Bind mount — file từ host
      - ./config:/etc/mysql/conf.d:ro   # Bind mount read-only
volumes:
  db-data:     # Khai báo named volume ở cuối file

Quan trọng: Nếu bạn dùng named volume (ví dụ db-data), phải khai báo nó trong section volumes: ở cuối file. Nếu không, Compose sẽ báo lỗi.

environment và env_file

Truyền biến môi trường vào container, giống flag -e trong docker run:

Cách 1: Khai báo trực tiếp trong file

services:
  database:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass123
      MYSQL_DATABASE: myapp
      # hoặc dạng list:
      # - MYSQL_ROOT_PASSWORD=rootpass123
      # - MYSQL_DATABASE=myapp

Cách 2: Dùng file .env riêng

services:
  database:
    image: mysql:8.0
    env_file:
      - .env              # đọc biến từ file .env
      - ./config/db.env   # có thể dùng nhiều file

File .env có format đơn giản:

MYSQL_ROOT_PASSWORD=rootpass123
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass456

Tip: Dùng env_file khi có nhiều biến môi trường hoặc khi bạn muốn tách secret ra khỏi file docker-compose.yml. Thêm .env vào .gitignore để không push password lên Git.

networks

Mặc định, Docker Compose tự tạo một network cho toàn bộ project. Tất cả services trong cùng file docker-compose.yml đều có thể gọi nhau bằng tên service, không cần cấu hình network thủ công như ở Bài 5.

Tuy nhiên, nếu bạn muốn kiểm soát network (ví dụ tách frontend và backend vào 2 network khác nhau), bạn có thể khai báo tường minh:

services:
  web:
    image: nginx:latest
    networks:
      - frontend

app: image: node:18 networks: - frontend - backend

database: image: mysql:8.0 networks: - backend

networks: frontend: backend:

Trong ví dụ trên, web chỉ nói chuyện được với app (qua network frontend), database chỉ nói chuyện được với app (qua network backend). web không thể truy cập trực tiếp database: đây là cách tăng bảo mật cho hệ thống.

Nhưng với các project đơn giản (2-3 services), bạn không cần khai báo network: để Compose tự xử lý là đủ.

depends_on

Chỉ định thứ tự khởi động giữa các services:

services:
  wordpress:
    image: wordpress:latest
    depends_on:
      - mysql       # WordPress sẽ khởi động SAU mysql
mysql:
    image: mysql:8.0

Lưu ý quan trọng: depends_on chỉ đảm bảo container MySQL được start trước WordPress. Nó không đợi MySQL sẵn sàng nhận kết nối. MySQL có thể mất 10-20 giây để khởi tạo database, trong thời gian đó WordPress có thể đã chạy và kết nối thất bại.

Để đợi service thực sự sẵn sàng, bạn có thể dùng healthcheck:

services:
  wordpress:
    image: wordpress:latest
    depends_on:
      mysql:
        condition: service_healthy
mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass123
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 3s
      retries: 10

Với cách này, WordPress sẽ chỉ khởi động khi MySQL đã trả lời ping thành công, an toàn hơn nhiều.

restart

Chính sách tự khởi động lại container khi nó bị crash hoặc khi server reboot:

services:
  web:
    image: nginx:latest
    restart: unless-stopped

Có 4 giá trị:

  • no: không tự restart (mặc định)
  • always: luôn restart, kể cả khi bạn stop thủ công (sau khi Docker daemon restart, container sẽ tự chạy lại)
  • on-failure: chỉ restart khi exit code khác 0 (container bị crash)
  • unless-stopped: giống always, nhưng nếu bạn đã stop thủ công thì không tự restart khi Docker daemon restart

Khuyến nghị: Dùng unless-stopped cho hầu hết các service trên VPS. Container sẽ tự chạy lại sau khi server reboot, nhưng vẫn tôn trọng khi bạn stop thủ công.

container_name

Mặc định, Docker Compose đặt tên container theo format: tên-thư-mục_tên-service_1. Ví dụ nếu file docker-compose.yml nằm trong thư mục myproject, service web sẽ có tên container là myproject-web-1.

Nếu bạn muốn đặt tên cụ thể:

services:
  web:
    image: nginx:latest
    container_name: my-nginx

Lưu ý: Khi dùng container_name, bạn không thể scale service đó (chạy nhiều instance) vì tên container phải là duy nhất. Chỉ dùng khi bạn chắc chắn chỉ cần 1 instance.

Các lệnh Docker Compose quan trọng

Tất cả lệnh Docker Compose cần được chạy trong thư mục chứa file docker-compose.yml (hoặc dùng flag -f để chỉ đường dẫn).

docker compose up -d

Khởi động toàn bộ services được định nghĩa trong file:

# Khởi động tất cả services ở chế độ detach (chạy nền)
docker compose up -d

# Chỉ khởi động 1 service cụ thể docker compose up -d web

# Build lại image trước khi khởi động (nếu dùng build) docker compose up -d --build

Flag -d (detach) để chạy ở background. Nếu bỏ -d, log của tất cả services sẽ hiển thị trực tiếp trên terminal, hữu ích khi debug.

docker compose down

Dừng và xoá tất cả containers, networks được tạo bởi up:

# Dừng + xoá containers và networks
docker compose down

# Dừng + xoá cả volumes (CẨN THẬN — mất data!) docker compose down -v

# Dừng + xoá cả images docker compose down --rmi all

Lưu ý: docker compose down mặc định không xoá volumes. Data trong named volumes vẫn được giữ lại. Chỉ dùng -v khi bạn thực sự muốn xoá toàn bộ dữ liệu.

docker compose ps

Xem trạng thái các services:

docker compose ps
NAME            IMAGE          COMMAND                  SERVICE    STATUS          PORTS
myproject-web-1 nginx:latest   "/docker-entrypoint.…"   web        Up 2 minutes    0.0.0.0:8080->80/tcp
myproject-db-1  mysql:8.0      "docker-entrypoint.s…"   database   Up 2 minutes    3306/tcp

docker compose logs

Xem log của services:

# Log tất cả services
docker compose logs

# Log 1 service cụ thể docker compose logs web

# Follow log realtime (giống tail -f) docker compose logs -f web

# Chỉ xem 50 dòng cuối docker compose logs --tail 50 web

docker compose exec

Exec vào một service đang chạy, giống docker exec:

# Mở shell vào service web
docker compose exec web bash
# Chạy 1 lệnh trong service database
docker compose exec database mysql -u root -p

Ở đây bạn dùng tên service (web, database) chứ không phải tên container.

docker compose pull / build

# Pull phiên bản mới nhất của tất cả images
docker compose pull

# Build lại images (cho services dùng build) docker compose build

# Build không dùng cache docker compose build --no-cache

Quy trình cập nhật image thường là: docker compose pulldocker compose up -d. Compose sẽ tự detect image mới và recreate container.

docker compose restart / stop / start

# Restart tất cả services
docker compose restart

# Restart 1 service docker compose restart web

# Stop tất cả (giữ container, không xoá) docker compose stop

# Start lại (sau khi stop) docker compose start

Khác biệt giữa downstop: stop chỉ dừng container (giữ nguyên), start chạy lại. down dừng VÀ xoá container + network. Dùng stop/start khi muốn tạm dừng, dùng down/up khi muốn dọn sạch và tạo lại.

Thực hành 1: Nginx + Custom HTML

Bắt đầu với ví dụ đơn giản nhất, chạy Nginx phục vụ một trang HTML tĩnh bằng Docker Compose.

Tạo cấu trúc project

mkdir -p ~/docker-compose-demo/html
cd ~/docker-compose-demo

Tạo file HTML

cat > html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>Docker Compose Demo</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 50px; background: #f0f0f0; }
        .container { background: white; padding: 40px; border-radius: 10px; display: inline-block; }
        h1 { color: #0db7ed; }
    </style>
</head>
<body>
    <div class="container">
        <h1>🐳 Docker Compose hoạt động!</h1>
        <p>Trang này được serve bởi Nginx chạy qua Docker Compose.</p>
    </div>
</body>
</html>
EOF

Tạo file docker-compose.yml

cat > docker-compose.yml << 'EOF'
services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped
EOF

File này rất ngắn gọn, chỉ 1 service duy nhất:

  • image: nginx:latest: dùng image Nginx chính thức
  • ports: "8080:80": map port 8080 trên host vào port 80 của Nginx
  • volumes: ./html:/usr/share/nginx/html:ro: bind mount thư mục html vào nơi Nginx đọc file, :ro nghĩa là read-only (Nginx không cần ghi vào đây)
  • restart: unless-stopped: tự restart nếu container crash hoặc server reboot

Khởi động và kiểm tra

# Khởi động
docker compose up -d
[+] Running 2/2
 ✔ Network docker-compose-demo_default  Created
 ✔ Container docker-compose-demo-web-1  Started

Compose tự tạo một network docker-compose-demo_default (tên thư mục + _default) và khởi động container.

# Kiểm tra trạng thái
docker compose ps
NAME                          IMAGE          COMMAND                  SERVICE   STATUS         PORTS
docker-compose-demo-web-1     nginx:latest   "/docker-entrypoint.…"   web       Up 5 seconds   0.0.0.0:8080->80/tcp
# Test bằng curl
curl http://localhost:8080

Bạn sẽ thấy nội dung HTML hiển thị. Nếu truy cập bằng trình duyệt tại http://your-server-ip:8080, sẽ thấy trang "Docker Compose hoạt động!".

# Xem log
docker compose logs web
web-1  | /docker-entrypoint.sh: Configuration complete; ready for start up
web-1  | 172.18.0.1 - - [14/Mar/2026:10:00:00 +0000] "GET / HTTP/1.1" 200 512 "-" "curl/8.5.0"

Cập nhật nội dung

Vì dùng bind mount, bạn có thể sửa file HTML trực tiếp mà không cần restart container:

echo '<h1>Noi dung da cap nhat!</h1>' > html/index.html
curl http://localhost:8080
# Output: <h1>Noi dung da cap nhat!</h1>

Thay đổi có hiệu lực ngay lập tức, không cần docker compose restart.

Dọn dẹp

# Dừng và xoá container + network
docker compose down
[+] Running 2/2
 ✔ Container docker-compose-demo-web-1  Removed
 ✔ Network docker-compose-demo_default  Removed

Sạch sẽ. Container và network đã bị xoá. Thư mục html/ trên host vẫn còn nguyên vì đó là bind mount.

Thực hành 2: Nginx + PHP-FPM

Bây giờ nâng level lên, chạy hệ thống 2 services: Nginx làm web server và PHP-FPM xử lý code PHP. Đây là combo rất phổ biến trên VPS.

Tạo cấu trúc project

mkdir -p ~/nginx-php/{src,nginx}
cd ~/nginx-php

Cấu trúc thư mục sẽ như sau:

nginx-php/
├── docker-compose.yml
├── nginx/
│   └── default.conf      # Config Nginx
└── src/
    └── index.php          # Code PHP

Tạo file PHP

cat > src/index.php << 'EOF'
<?php
echo "<h1>🐳 Nginx + PHP-FPM với Docker Compose</h1>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Server: " . $_SERVER['SERVER_SOFTWARE'] . "</p>";
echo "<p>Hostname (container): " . gethostname() . "</p>";
echo "<p>Thời gian: " . date('Y-m-d H:i:s') . "</p>";
?>
EOF

Tạo config Nginx

cat > nginx/default.conf << 'EOF'
server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;

location / { try_files $uri $uri/ =404; }

location ~ \.php$ { fastcgi_pass php:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } } EOF

Điểm quan trọng: fastcgi_pass php:9000;: Nginx chuyển request PHP đến service có tên php ở port 9000. Tên php ở đây chính là tên service trong file docker-compose.yml. Docker Compose tự tạo DNS record, nên Nginx có thể resolve tên php thành IP đúng.

Tạo file docker-compose.yml

cat > docker-compose.yml << 'EOF'
services:
  nginx:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html:ro
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - php
    restart: unless-stopped
php:
    image: php:8.3-fpm
    volumes:
      - ./src:/var/www/html
    restart: unless-stopped
EOF

Phân tích từng phần:

Service nginx:

  • ports: "8080:80": Nginx nhận request từ bên ngoài qua port 8080
  • ./src:/var/www/html:ro: mount thư mục source code vào Nginx (read-only vì Nginx chỉ đọc file tĩnh)
  • ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro: mount file config Nginx
  • depends_on: php: đợi PHP-FPM start trước

Service php:

  • image: php:8.3-fpm: dùng image PHP-FPM 8.3
  • ./src:/var/www/html: cùng thư mục source code, nhưng không có :ro vì PHP có thể cần ghi file (upload, cache, session...)
  • Không có ports: PHP-FPM chỉ giao tiếp nội bộ với Nginx, không cần expose ra ngoài

Shared volume: Cả 2 services đều mount ./src vào /var/www/html. Đây là cách để Nginx và PHP-FPM cùng truy cập được source code. Nginx phục vụ file tĩnh (CSS, JS, ảnh), còn PHP-FPM xử lý file .php.

Khởi động và kiểm tra

docker compose up -d
[+] Running 3/3
 ✔ Network nginx-php_default    Created
 ✔ Container nginx-php-php-1    Started
 ✔ Container nginx-php-nginx-1  Started

Compose tự tạo network, khởi động PHP-FPM trước (vì depends_on), rồi mới khởi động Nginx.

# Kiểm tra trạng thái
docker compose ps
NAME                  IMAGE          COMMAND                  SERVICE   STATUS         PORTS
nginx-php-nginx-1     nginx:latest   "/docker-entrypoint.…"   nginx     Up 3 seconds   0.0.0.0:8080->80/tcp
nginx-php-php-1       php:8.3-fpm    "docker-php-entrypoi…"   php       Up 3 seconds   9000/tcp
# Test
curl http://localhost:8080

Bạn sẽ thấy output dạng:

<h1>🐳 Nginx + PHP-FPM với Docker Compose</h1>
<p>PHP Version: 8.3.15</p>
<p>Server: nginx/1.27.3</p>
<p>Hostname (container): 7a3b2c1d4e5f</p>
<p>Thời gian: 2026-03-14 10:00:00</p>

Nginx đã chuyển request PHP sang PHP-FPM thành công. Hai container giao tiếp qua network nội bộ mà Docker Compose tự tạo.

Thêm file PHP mới

Thử thêm file mới vào thư mục src/:

cat > src/info.php << 'EOF'
<?php phpinfo(); ?>
EOF
curl http://localhost:8080/info.php | head -5

Trang phpinfo() hiện ra ngay, không cần restart gì cả, nhờ bind mount.

Xem log của từng service

# Log Nginx — access log
docker compose logs nginx

# Log PHP-FPM — error log, slow log docker compose logs php

# Follow log cả 2 services cùng lúc docker compose logs -f

Exec vào container

# Exec vào PHP-FPM để kiểm tra
docker compose exec php bash
# Bên trong container:
php -v
ls /var/www/html/
exit

Dọn dẹp

docker compose down

📚 Serie Docker từ A đến Z

  1. Bài 1: Docker là gì? Tại sao nên dùng Docker trên VPS
  2. Bài 2: Cài đặt Docker và Docker Compose trên VPS Ubuntu
  3. Bài 3: Làm quen với Docker – Các lệnh cơ bản cần biết
  4. Bài 4: Docker Image & Dockerfile – Tự tạo Image riêng
  5. Bài 5: Docker Volume & Network – Quản lý dữ liệu và mạng
  6. Bài 6: Docker Compose là gì? Cài đặt và cú pháp cơ bản (đang đọc)
  7. Bài 7: Deploy WordPress + MySQL + phpMyAdmin bằng Docker Compose
  8. Bài 8: Deploy LEMP Stack (Nginx + PHP-FPM + MariaDB) bằng Docker Compose
  9. Bài 9: Biến môi trường & file .env trong Docker Compose
  10. Bài 10: Reverse Proxy với Nginx Proxy Manager + SSL tự động
  11. Bài 11: Deploy ứng dụng Node.js / Python với Docker Compose
  12. Bài 12: Backup & Restore dữ liệu Docker Volume
  13. Bài 13: Monitoring Docker với Portainer, Uptime Kuma và cAdvisor
  14. Bài 14: Docker Logging – Quản lý log hiệu quả
  15. Bài 15: Bảo mật Docker trên VPS
  16. Bài 16: CI/CD đơn giản – Auto deploy với Webhook + Docker Compose
  17. Bài 17: Docker Compose trong thực tế – Tổng hợp project mẫu

Tổng kết

Trong bài này, bạn đã học được:

Docker Compose là gì:

  • Công cụ define + run multi-container apps bằng file YAML
  • Đã cài sẵn khi cài Docker (docker-compose-plugin)
  • Dùng docker compose (v2, có dấu cách): không dùng docker-compose (v1, đã deprecated)

Cấu trúc docker-compose.yml:

  • services: định nghĩa các container (image, ports, volumes, environment, depends_on, restart)
  • volumes: khai báo named volumes
  • networks: tuỳ chọn, mặc định Compose tự tạo network
  • Không cần key version nữa (deprecated)

Các lệnh quan trọng:

  • docker compose up -d: khởi động
  • docker compose down: dừng + xoá
  • docker compose ps: xem trạng thái
  • docker compose logs: xem log
  • docker compose exec: exec vào service
  • docker compose pull + up -d: cập nhật image

Thực hành:

  • Chạy Nginx + custom HTML (1 service, bind mount)
  • Chạy Nginx + PHP-FPM (2 services, shared volume, custom nginx config)

Docker Compose thay đổi hoàn toàn cách bạn làm việc với Docker. Thay vì nhớ hàng chục lệnh dài, bạn chỉ cần viết một file YAML và chạy docker compose up -d. File đó còn đóng vai trò như documentation cho hệ thống, bất kỳ ai đọc file cũng hiểu hệ thống cần những gì.

Bài 7, mình sẽ áp dụng Docker Compose để triển khai một hệ thống thực tế hoàn chỉnh: WordPress + MySQL + phpMyAdmin. Đây sẽ là project gần với production nhất trong serie, bạn sẽ thấy sức mạnh thực sự của Docker Compose khi quản lý 3 services cùng lúc chỉ với một file config.

👈 Bài trước: Docker Volume & Network – Quản lý dữ liệu và mạng

👉 Bài tiếp: Deploy WordPress + MySQL + phpMyAdmin bằng Docker Compose

Chia sẻ:
Bài viết đã được kiểm duyệt bởi AZDIGI Team

Về tác giả

Thạch Phạm

Thạch Phạm

Đồng sáng lập và Giám đốc điều hành của AZDIGI. Có hơn 15 năm kinh nghiệm trong phổ biến kiến thức liên quan đến WordPress tại thachpham.com, phát triển website và phát triển hệ thống.

Hơn 10 năm phục vụ 80.000+ khách hàng

Bắt đầu dự án web của bạn với AZDIGI