❤️ 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.

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ốngalways, 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 pull → docker 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 down và stop: 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ứcports: "8080:80": map port 8080 trên host vào port 80 của Nginxvolumes: ./html:/usr/share/nginx/html:ro: bind mount thư mụchtmlvào nơi Nginx đọc file,:ronghĩ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 Nginxdepends_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
- Bài 1: Docker là gì? Tại sao nên dùng Docker trên VPS
- Bài 2: Cài đặt Docker và Docker Compose trên VPS Ubuntu
- Bài 3: Làm quen với Docker – Các lệnh cơ bản cần biết
- Bài 4: Docker Image & Dockerfile – Tự tạo Image riêng
- Bài 5: Docker Volume & Network – Quản lý dữ liệu và mạng
- Bài 6: Docker Compose là gì? Cài đặt và cú pháp cơ bản (đang đọc)
- Bài 7: Deploy WordPress + MySQL + phpMyAdmin bằng Docker Compose
- Bài 8: Deploy LEMP Stack (Nginx + PHP-FPM + MariaDB) bằng Docker Compose
- Bài 9: Biến môi trường & file .env trong Docker Compose
- Bài 10: Reverse Proxy với Nginx Proxy Manager + SSL tự động
- Bài 11: Deploy ứng dụng Node.js / Python với Docker Compose
- Bài 12: Backup & Restore dữ liệu Docker Volume
- Bài 13: Monitoring Docker với Portainer, Uptime Kuma và cAdvisor
- Bài 14: Docker Logging – Quản lý log hiệu quả
- Bài 15: Bảo mật Docker trên VPS
- Bài 16: CI/CD đơn giản – Auto deploy với Webhook + Docker Compose
- 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ùngdocker-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 volumesnetworks: tuỳ chọn, mặc định Compose tự tạo network- Không cần key
versionnữa (deprecated)
Các lệnh quan trọng:
docker compose up -d: khởi độngdocker compose down: dừng + xoádocker compose ps: xem trạng tháidocker compose logs: xem logdocker compose exec: exec vào servicedocker 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
Có thể bạn cần xem thêm
Về tác giả
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.