- 1. Tại sao Docker cho WordPress?
- 2. Kiến trúc stack WordPress + Docker
- 3. Cấu trúc thư mục dự án
- 4. File docker-compose.yml — Trái tim của stack
- Giải thích từng phần
- 5. Cấu hình Nginx — Xử lý static files và proxy PHP
- Điểm quan trọng
- 6. Khởi động stack — Chạy ngay trong 30 giây
- 7. Quản lý WordPress trong Docker — Các lệnh thường dùng
- 8. Cài WP-CLI — Quản lý WordPress từ command line
- 9. Tối ưu cho production
- 9.1. Sử dụng .env cho secrets
- 9.2. SSL/TLS với Let’s Encrypt
- 9.3. PHP OPcache và tuning
- 9.4. Backup tự động với cron
- 10. So sánh: Docker vs LEMP truyền thống
- 11. Troubleshooting — Các lỗi thường gặp
- 12. Từ Docker đến Kubernetes — Bước tiếp theo
- 13. Kết luận
1. Tại sao Docker cho WordPress?
Trước khi đụng tay vào code, hãy hiểu Docker giải quyết vấn đề gì:
| Vấn đề truyền thống | Cách Docker giải quyết | Lợi ích thực tế |
|---|---|---|
| “Trên máy tôi chạy được” — môi trường dev khác production | Container chứa toàn bộ dependencies: PHP version, extensions, Nginx config | Chạy giống hệt trên laptop, staging server, và production |
| Cài đặt thủ công tốn thời gian, dễ lỗi | Một file docker-compose.yml định nghĩa toàn bộ stack | docker compose up là xong, mất chưa đến 1 phút |
| Khó quản lý nhiều project với PHP/MySQL version khác nhau | Mỗi project có container riêng, cô lập hoàn toàn | Project A dùng PHP 7.4, project B dùng PHP 8.3 — không xung đột |
| Backup/restore database phức tạp | Volume Docker lưu data riêng biệt, dễ backup bằng docker exec | Backup chỉ là một file SQL, restore chỉ là một lệnh |
| Scaling khó khăn | Chạy nhiều container WordPress phía sau load balancer | Horizontal scaling dễ dàng với Docker Swarm hoặc Kubernetes |
2. Kiến trúc stack WordPress + Docker
Chúng ta sẽ xây dựng stack gồm 3 container:
- wordpress: Image
wordpress:latest— chạy PHP-FPM, chứa mã nguồn WordPress - db: Image
mysql:8.0— database MySQL cho WordPress - nginx: Image
nginx:alpine— reverse proxy, xử lý static files, chuyển PHP requests sang WordPress container
Client →
Nginx (port 80) →
WordPress PHP-FPM (port 9000) →
MySQL (port 3306)
3. Cấu trúc thư mục dự án
Tạo thư mục dự án với cấu trúc sau:
wordpress-docker/
|-- -- docker-compose.yml # Định nghĩa toàn bộ stack
|-- -- nginx/
| `-- -- default.conf # Cấu hình Nginx
|-- -- wordpress/ # Mã nguồn WordPress (mount từ container)
`-- -- db_data/ # Data MySQL (Docker volume)4. File docker-compose.yml — Trái tim của stack
Đây là file quan trọng nhất. Nó định nghĩa 3 services, network chung, và volumes để lưu data:
version: '3.8'
services:
db:
image: mysql:8.0
container_name: wp_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword123
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpresspass
volumes:
- db_data:/var/lib/mysql
networks:
- wp_network
command: --default-authentication-plugin=mysql_native_password
wordpress:
image: wordpress:php8.3-fpm
container_name: wp_app
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpresspass
volumes:
- ./wordpress:/var/www/html
networks:
- wp_network
depends_on:
- db
nginx:
image: nginx:alpine
container_name: wp_nginx
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./wordpress:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
- wp_network
depends_on:
- wordpress
volumes:
db_data:
networks:
wp_network:
driver: bridgeGiải thích từng phần:
restart: unless-stopped— Container tự khởi động lại khi server reboot, trừ khi bạn chủ động stop.volumes—db_datalà named volume, Docker quản lý và data tồn tại ngay cả khi xóa container../wordpresslà bind mount, cho phép bạn chỉnh sửa file từ host.networks— Các container trong cùng network có thể gọi nhau bằng tên service. WordPress kết nối MySQL quadb:3306.depends_on— Đảm bảo MySQL khởi động trước WordPress, tránh lỗi connection refused.
5. Cấu hình Nginx — Xử lý static files và proxy PHP
Tạo file nginx/default.conf:
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 6M;
add_header Cache-Control "public, immutable";
}
location ~ /\.ht {
deny all;
}
}Điểm quan trọng:
fastcgi_pass wordpress:9000— Nginx gửi PHP requests đến WordPress container qua port 9000 (PHP-FPM). Docker DNS tự động resolvewordpressthành IP của container.try_files— Hỗ trợ WordPress permalink. Nếu file không tồn tại, chuyển vềindex.php.expires 6M— Cache static assets 6 tháng, giảm tải server.
6. Khởi động stack — Chạy ngay trong 30 giây
Mở terminal, cd vào thư mục dự án, chạy:
# 1. Tạo thư mục
mkdir -p wordpress-docker/nginx wordpress-docker/wordpress
# 2. Tạo file docker-compose.yml và nginx/default.conf (copy từ trên)
# 3. Khởi động
cd wordpress-docker
docker compose up -d
# 4. Kiểm tra container đang chạy
docker compose ps
# 5. Xem logs nếu cần
docker compose logs -fSau khi chạy, truy cập http://localhost và bạn sẽ thấy màn hình cài đặt WordPress. Hoàn tất setup như bình thường.
7. Quản lý WordPress trong Docker — Các lệnh thường dùng
| Thao tác | Lệnh |
|---|---|
| Xem danh sách container | docker compose ps |
| Xem logs real-time | docker compose logs -f |
| Truy cập shell WordPress container | docker exec -it wp_app bash |
| Chạy WP-CLI | docker exec -it wp_app wp --allow-root [command] |
| Backup database | docker exec wp_db mysqldump -u wordpress -pwordpresspass wordpress > backup.sql |
| Restore database | docker exec -i wp_db mysql -u wordpress -pwordpresspass wordpress < backup.sql |
| Restart một service | docker compose restart nginx |
| Rebuild sau khi sửa config | docker compose up -d --build |
| Dừng toàn bộ stack | docker compose down |
| Dừng và xóa cả volume (cẩn thận) | docker compose down -v |
8. Cài WP-CLI — Quản lý WordPress từ command line
Thay vì vào wp-admin, bạn có thể cài plugin, update core, tạo user bằng WP-CLI:
# Cài WP-CLI vào WordPress container
docker exec -it wp_app bash -c "
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
mv wp-cli.phar /usr/local/bin/wp
"
# Kiểm tra WP-CLI
docker exec -it wp_app wp --allow-root --info
# Cài plugin
docker exec -it wp_app wp --allow-root plugin install wordpress-seo --activate
# Tạo user admin
docker exec -it wp_app wp --allow-root user create admin [email protected] --role=administrator --user_pass=admin123
# Update WordPress core
docker exec -it wp_app wp --allow-root core update9. Tối ưu cho production
Development và production khác nhau ở một số điểm quan trọng:
9.1. Sử dụng .env cho secrets
Không hardcode password trong docker-compose.yml. Tạo file .env:
MYSQL_ROOT_PASSWORD=your_secure_root_password
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=your_secure_db_password
WORDPRESS_DB_PASSWORD=your_secure_db_passwordSửa docker-compose.yml:
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}Thêm .env vào .gitignore để không push lên Git.
9.2. SSL/TLS với Let’s Encrypt
Thêm container certbot để tự động renew SSL:
certbot:
image: certbot/certbot
container_name: wp_certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
nginx:
# ... existing config
volumes:
- ./wordpress:/var/www/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbotSửa nginx/default.conf để redirect HTTP to HTTPS và dùng certificate:
server {
listen 80;
server_name yourdomain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
root /var/www/html;
index index.php;
# ... rest of config same as before
}9.3. PHP OPcache và tuning
Tạo file php.ini custom và mount vào WordPress container:
; php.ini
memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 8
opcache.max_accelerated_files = 4000
opcache.revalidate_freq = 2
opcache.fast_shutdown = 1Thêm vào docker-compose.yml:
wordpress:
# ...
volumes:
- ./wordpress:/var/www/html
- ./php.ini:/usr/local/etc/php/conf.d/custom.ini9.4. Backup tự động với cron
Tạo script backup hàng ngày:
#!/bin/bash
# backup.sh
DATE=$(date +%Y%m%d_%H%M%S)
docker exec wp_db mysqldump -u wordpress -pwordpresspass wordpress | gzip > ./backups/wordpress_$DATE.sql.gz
find ./backups -name "*.sql.gz" -mtime +7 -deleteThêm vào crontab:
0 2 * * * /path/to/wordpress-docker/backup.sh10. So sánh: Docker vs LEMP truyền thống
| Tiêu chí | LEMP thủ công | Docker |
|---|---|---|
| Thời gian setup | 30-60 phút (cài từng thành phần, config từng file) | 1-2 phút (docker compose up -d) |
| Tính tái lặp | Thấp — mỗi server có thể khác nhau do cấu hình tay | Cao — cùng một docker-compose.yml chạy giống hệt mọi nơi |
| Quản lý version | Khó — downgrade PHP/MySQL phức tạp | Dễ — đổi tag image, restart container |
| Multi-project | Khó — xung đột port, PHP version | Dễ — mỗi project một stack container riêng |
| Resource usage | Thấp hơn (chạy trực tiếp trên host) | Cao hơn một chút (overhead container ~1-5%) |
| Learning curve | Thấp — chỉ cần biết Linux + Nginx + MySQL | Trung bình — cần hiểu container, volume, network |
| Production-ready | Cần nhiều công sức tối ưu thủ công | Dễ scale, dễ backup, dễ CI/CD |
11. Troubleshooting — Các lỗi thường gặp
| Lỗi | Nguyên nhân | Cách fix |
|---|---|---|
Error establishing database connection | WordPress chưa kết nối được MySQL | Đợi thêm vài giây cho MySQL khởi động xong, hoặc chạy docker compose restart wordpress |
502 Bad Gateway | Nginx không kết nối được PHP-FPM | Kiểm tra fastcgi_pass wordpress:9000 trong nginx config. Đảm bảo WordPress container đang chạy. |
| Port 80 đã được sử dụng | Apache/Nginx khác đang chạy trên host | Dừng service đang chiếm port: sudo systemctl stop nginx hoặc đổi port mapping thành 8080:80 |
| Permission denied khi sửa file WordPress | File trong container thuộc về user www-data | Chạy sudo chown -R $USER:$USER ./wordpress trên host |
Database bị mất sau khi docker compose down | Dùng -v flag hoặc không dùng named volume | Đảm bảo dùng named volume db_data, không chạy down -v trừ khi thực sự muốn xóa data |
12. Từ Docker đến Kubernetes — Bước tiếp theo
Khi site lớn lên, bạn có thể cần:
- Docker Swarm — orchestration đơn giản, tích hợp sẵn trong Docker. Phù hợp cho 1-3 server.
- Kubernetes — orchestration mạnh mẽ, auto-scaling, self-healing. Phù hợp cho hệ thống lớn, multi-cloud.
- Docker + Cloud — AWS ECS, Google Cloud Run, Azure Container Instances đều hỗ trợ chạy container WordPress mà không cần quản lý server.
Nhưng đừng vội. Với hầu hết các WordPress site, một server đơn với Docker Compose đã quá đủ. Chỉ nghĩ đến Kubernetes khi bạn có thực sự cần scale ngang (horizontal scaling) hoặc chạy trên nhiều availability zone.
13. Kết luận
Docker không phải công nghệ “thần thánh” — nó chỉ là công cụ. Nhưng với WordPress, Docker giải quyết đúng những vấn đề đau đầu nhất: môi trường không đồng nhất, setup lặp đi lặp lại, backup/restore phức tạp.
Với chỉ 2 file — docker-compose.yml và nginx/default.conf — bạn đã có một stack WordPress production-ready, có thể chạy trên bất kỳ máy nào có Docker, từ laptop cá nhân đến cloud VPS.
Hãy thử copy config ở trên, chạy docker compose up -d, và trải nghiệm cảm giác “mọi thứ chỉ hoạt động” — đó chính là lý do Docker đã trở thành tiêu chuẩn công nghiệp cho việc triển khai ứng dụng.

Bình luận