WordPress Zero-Downtime Deployment: Quy Trình Cập Nhật Website Production Không Gián Đoạn

WordPress zero-downtime deployment là quy trình cập nhật WordPress production sao cho người dùng gần như không thấy gián đoạn, đội vận hành có thể rollback nhanh, còn nội dung/database không bị mất hoặc lệch phiên bản. Với website có traffic thật, cách “upload đè file bằng FTP rồi cầu nguyện” là một rủi ro lớn: chỉ cần plugin lỗi, cache sai, migration database fail hoặc theme thiếu file là site có thể trắng trang.

Bài này đi theo hướng thực chiến cho sysadmin/DevOps quản trị WordPress: có staging, backup, Git deploy, WP-CLI, kiểm thử, bật/tắt maintenance đúng lúc và checklist nghiệm thu. Mục tiêu không phải làm pipeline phức tạp cho đẹp, mà là biến mỗi lần cập nhật plugin/theme/core thành một thao tác có thể đoán trước và có đường lùi.

1. Zero-downtime trong WordPress nghĩa là gì?

Zero-downtime không có nghĩa là không bao giờ có request lỗi. Trong thực tế, mục tiêu là:

  • Không để người dùng thấy trang bảo trì kéo dài.
  • Không có giai đoạn file PHP bị thiếu một nửa vì upload chưa xong.
  • Không làm mất upload, order, comment hoặc bài viết mới.
  • Có thể rollback trong vài giây đến vài phút.
  • Có log để biết bản nào được deploy, ai deploy và deploy lúc nào.

Điểm khó của WordPress nằm ở chỗ code, database và nội dung media thường sống chung. Vì vậy quy trình deploy phải tách rõ phần nào được quản lý bằng Git, phần nào là dữ liệu runtime.

2. Kiến trúc thư mục khuyến nghị

Một mẫu đơn giản nhưng hiệu quả là dùng thư mục release và symlink:

/var/www/example.com/
├── current -> releases/20260610-1900
├── releases/
│   ├── 20260608-1900/
│   └── 20260610-1900/
├── shared/
│   ├── wp-content/uploads/
│   ├── wp-config.php
│   └── cache/
└── logs/

Mỗi lần deploy, bạn build một release mới trong releases/, kiểm tra xong mới đổi symlink current. Thư mục upload và cấu hình nhạy cảm nằm trong shared để không bị ghi đè.

3. Những thứ nên và không nên đưa vào Git

Nên đưa vào Git:

  • Theme tự phát triển.
  • Plugin custom.
  • File cấu hình mẫu, script deploy, composer.json nếu dùng Composer.
  • Mu-plugin bắt buộc cho production.

Không nên đưa vào Git:

  • wp-content/uploads vì đây là dữ liệu người dùng.
  • wp-config.php chứa credential.
  • Cache, log, backup dump.
  • Plugin premium nếu license không cho phép lưu trong repo chung.

4. Backup trước deploy: không có backup thì không gọi là quy trình

Trước khi chạm vào production, tối thiểu cần backup database và ghi lại version hiện tại.

cd /var/www/example.com/current
wp core version
wp plugin list --format=table
wp theme list --format=table
wp db export /var/backups/wp/example-$(date +%F-%H%M).sql --single-transaction

Giải thích:

  • wp core version giúp biết WordPress đang ở bản nào.
  • wp plugin listwp theme list là snapshot nhanh để rollback logic nếu cần.
  • --single-transaction phù hợp với InnoDB để giảm lock trong lúc export.

Output mẫu:

Success: Exported to '/var/backups/wp/example-2026-06-10-1855.sql'.

5. Staging phải giống production ở những điểm quan trọng

Staging không cần cùng kích thước server, nhưng nên giống production ở PHP version, web server, extension PHP, object cache, database engine và plugin chính. Nếu production chạy Redis object cache mà staging không có Redis, nhiều lỗi cache sẽ không xuất hiện trước khi deploy.

php -v
php -m | egrep 'redis|imagick|mysqli|curl|mbstring|zip|intl'
wp --info
wp option get home
wp option get siteurl

Khi clone database production về staging, hãy thay URL và chặn email thật:

wp search-replace 'https://example.com' 'https://staging.example.com' --skip-columns=guid
wp option update blog_public 0

6. Quy trình deploy theo release

Ví dụ script deploy tối giản:

#!/usr/bin/env bash
set -euo pipefail
APP=/var/www/example.com
REL=$(date +%Y%m%d-%H%M%S)
RELEASE=$APP/releases/$REL

git clone --depth=1 git@github.com:company/wp-site.git "$RELEASE"
ln -sfn $APP/shared/wp-config.php $RELEASE/wp-config.php
rm -rf $RELEASE/wp-content/uploads
ln -sfn $APP/shared/wp-content/uploads $RELEASE/wp-content/uploads

cd "$RELEASE"
composer install --no-dev --optimize-autoloader || true
wp core verify-checksums --allow-root || true
wp plugin list --allow-root

ln -sfn "$RELEASE" $APP/current
systemctl reload php8.3-fpm

Điểm quan trọng là symlink chỉ đổi sau khi release mới đã chuẩn bị xong. Người dùng không gặp trạng thái thư mục WordPress bị upload dở.

7. Database migration: phần nguy hiểm nhất

Nhiều plugin chạy migration database khi được cập nhật. Đây là điểm khiến rollback không còn đơn giản vì code có thể lùi nhưng database đã đổi. Với website quan trọng, hãy kiểm tra plugin changelog trước khi update và test migration trên staging.

wp plugin update --dry-run --all
wp plugin update woocommerce --dry-run
wp db check
wp db optimize

Nếu update liên quan WooCommerce, membership, LMS hoặc plugin custom có bảng riêng, cần lên lịch maintenance window ngắn và backup ngay trước thời điểm deploy.

8. Dùng maintenance mode đúng cách

Với deploy chỉ đổi file theme/plugin không migration database, có thể không cần maintenance mode. Nhưng nếu có update core/plugin lớn, nên bật maintenance trong thời gian rất ngắn:

wp maintenance-mode activate
# chạy migration/update đã test
wp plugin update plugin-name
wp core update-db
wp maintenance-mode deactivate

Không nên bật maintenance rồi để đó trong toàn bộ quá trình build. Hãy build release trước, chỉ bật maintenance khi chạy bước ảnh hưởng database.

9. Cache: nguyên nhân phổ biến khiến deploy “đúng mà sai”

Sau khi đổi release, cần làm sạch các lớp cache liên quan:

wp cache flush
wp transient delete --all
systemctl reload nginx
systemctl reload php8.3-fpm

Nếu dùng Cloudflare hoặc CDN, purge theo URL quan trọng thay vì xóa toàn bộ cache nếu traffic lớn. Nếu dùng Redis object cache, xác nhận drop-in vẫn hoạt động:

wp redis status || true
wp cache type

10. Smoke test sau deploy

Không nên chỉ mở homepage bằng mắt. Hãy kiểm tra các URL đại diện:

curl -I https://example.com/
curl -I https://example.com/wp-login.php
curl -I https://example.com/category/news/
curl -s https://example.com/ | grep -i '<title>'

Nếu là WooCommerce hoặc site có form, cần kiểm tra thêm cart, checkout sandbox, contact form, cron và REST API:

curl -s https://example.com/wp-json/ | jq '.name'
wp cron event list --fields=hook,next_run_relative --format=table

11. Rollback trong vài giây bằng symlink

Nếu release mới lỗi ở tầng code, rollback rất nhanh:

APP=/var/www/example.com
PREV=$(ls -1dt $APP/releases/* | sed -n '2p')
ln -sfn "$PREV" $APP/current
systemctl reload php8.3-fpm
wp --path=$APP/current cache flush

Lưu ý: rollback code không tự rollback database. Nếu migration đã thay đổi schema hoặc dữ liệu, cần kế hoạch riêng: restore backup, migration ngược, hoặc maintenance window có thông báo rõ.

12. Lỗi thường gặp và cách xử lý

Trang trắng sau deploy

tail -100 /var/log/nginx/error.log
tail -100 /var/log/php8.3-fpm.log
wp --path=/var/www/example.com/current plugin list

Thường do fatal error PHP, thiếu extension hoặc plugin không tương thích PHP version.

Upload ảnh lỗi sau deploy

ls -ld /var/www/example.com/shared/wp-content/uploads
sudo -u www-data test -w /var/www/example.com/shared/wp-content/uploads && echo OK

Kiểm tra symlink uploads và quyền ghi của user web server.

CSS/JS cũ vẫn hiển thị

Xóa cache plugin, object cache, CDN và kiểm tra version asset trong theme. Nếu dùng build tool, hãy đảm bảo file manifest mới được deploy cùng release.

13. Checklist nghiệm thu production

  • Đã backup database và lưu được file backup.
  • Release mới nằm trong thư mục riêng, không upload đè trực tiếp production.
  • wp-config.php và uploads dùng shared path đúng.
  • Staging đã test plugin/theme/core update.
  • Maintenance mode chỉ bật trong bước cần thiết.
  • Đã flush cache PHP/WordPress/CDN phù hợp.
  • Homepage, login, REST API, category, form quan trọng trả HTTP 200.
  • Có lệnh rollback đã kiểm tra trước.
  • Log deploy ghi release ID, người thực hiện và thời gian.

14. Bài tập lab

  • Tạo một WordPress lab bằng Docker hoặc VPS nhỏ.
  • Chuyển cấu trúc sang current/releases/shared.
  • Deploy một thay đổi nhỏ trong theme bằng symlink release.
  • Cố tình tạo lỗi PHP trong release mới, sau đó rollback về release cũ.
  • Viết runbook một trang gồm: backup, deploy, smoke test, rollback.

Kết luận: WordPress zero-downtime deployment không cần bắt đầu bằng hệ thống CI/CD đồ sộ. Chỉ cần tách dữ liệu runtime khỏi code, build release riêng, đổi symlink đúng thời điểm, kiểm thử bằng WP-CLI/curl và luôn có rollback. Khi quy trình này ổn định, bạn có thể nâng cấp dần lên GitHub Actions, GitLab CI hoặc deployment runner nội bộ.

Tác giả: Mạnh Hoàng

Tôi là Hoàng Mạnh, người sáng lập blog SysadminSkills.com. Tôi viết về quản trị hệ thống, bảo mật máy chủ, DevOps và cách ứng dụng AI để tự động hóa công việc IT. Blog này là nơi tôi chia sẻ những gì đã học được từ thực tế – đơn giản, ngắn gọn và áp dụng được ngay.