跳到主內容

用Docker打造最佳效能的Wordpress環境

要做的事:Docker → WordPress 可用流程

1. 建立 Docker 架構

  • 撰寫 docker-compose.yml,服務包含:

    • nginx:前端 Web server(只負責容器內請求,對外由主機 Proxy)

    • php (php-fpm):執行 WordPress/PHP

    • mariadb:資料庫,儲存 WordPress 資料

    • redis:Object Cache(提供效能優化)

    • wp-cli:命令列管理工具

    • cron:跑 WordPress 排程(wp-cron)

  • 設定:

    • Nginx 靜態檔案快取、購物流程繞過快取。

    • PHP FPM(memory_limit、pm.max_children 等)依你的 4C/4G RAM 調整。

    • MariaDB 用 volume 儲存資料、utf8mb4 編碼。

    • Redis 用獨立 volume,內建設定。


2. 主機層 Proxy (Ubuntu Nginx)

  • 主機 Nginx 反向代理 → 127.0.0.1:8080(容器內 nginx)。

  • 加入 SSL (Let’s Encrypt Certbot),伺服器區塊 server_name mysite.com;

  • 解決 502 問題:確保容器內 nginx 配置正確、port 綁在 127.0.0.1:8080。

  • 調整 proxy header,讓 WordPress 正確認定 HTTPS(解決 CSS/JS 不載入問題):

proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port  443;

3. WordPress 部署

  • 使用 wp-cli 下載 WordPress 核心:

docker compose exec wp-cli wp core download --allow-root
  • 產生 wp-config.php,連線資訊:

    • DB 名稱:wp

    • 帳號:wpuser

    • 密碼:change_me

    • Host:db:3306 ← 注意 Docker 要用服務名稱,不是 localhost

  • 透過安裝頁完成初始化(語言、站名、管理員帳密)。


4. 修正容器問題

  • Nginx

    • 修掉 fastcgi_cache_path 放錯區塊(要在 http 層級,不是 server)。

    • 最小化設定檔 → 先跑起站再逐步加快取規則。

  • cron

    • 原本 Restarting → 新增 cron/wp-cron.sh 腳本,正確執行排程。

  • wp-config.php

    • 修正 Redis 設定錯誤(define('WP_REDIS_HOST', redis );define('WP_REDIS_HOST', 'redis');

    • 加入 HTTPS 判斷(反向代理環境必須):

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

5. Redis 整合

  • 確認 Redis 容器正常:docker compose exec redis redis-cli ping → PONG

  • wp-config.php 正確寫入:

define( 'WP_REDIS_HOST', 'redis' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_DATABASE', 1 );
  • 重啟 PHP 讓 opcache 重載:docker compose restart php

  • 使用 wp-cli 啟用 Redis drop-in,確認狀態:

docker compose exec wp-cli wp redis enable --allow-root
docker compose exec wp-cli wp redis status --allow-root

6. WordPress 網站成功啟動

  • 首頁、安裝頁能正常載入 CSS/JS(不再 502、不再純文字)。

  • 安裝流程完成,能登入後台。

  • Redis Object Cache 狀態正常,顯示連到 redis:6379

  • 主機 Proxy + 容器 Nginx + PHP-FPM + DB 都在健康狀態。

📂 專案目錄結構

wordpress-perf/
├── docker-compose.yml
├── .env
├── nginx/
│   └── conf.d/
│       ├── wordpress.conf
│       └── 00-cache.conf
├── cron/
│   └── wp-cron.sh
└── README.md

⚙️ 檔案內容

1. .env

HTTP_PORT=8080

DB_NAME=wp
DB_USER=wpuser
DB_PASSWORD=change_me
DB_ROOT_PASSWORD=change_root

# PHP 記憶體/子程序調整(4C/4G RAM)
PHP_MEMORY_LIMIT=256M
PHP_MAX_CHILDREN=10
PHP_MAX_REQUESTS=400

2. docker-compose.yml

services:
  nginx:
    image: nginx:1.25-alpine
    restart: always
    ports:
      - "127.0.0.1:${HTTP_PORT:-8080}:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - wp_data:/var/www/html:ro
      - cache_data:/var/cache/nginx
    depends_on:
      - php

  php:
    build:
      context: .
      dockerfile_inline: |
        FROM wordpress:php8.3-fpm-alpine
        RUN docker-php-ext-install mysqli pdo pdo_mysql
        COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
    restart: always
    environment:
      WORDPRESS_DB_NAME: ${DB_NAME}
      WORDPRESS_DB_USER: ${DB_USER}
      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
      WORDPRESS_DB_HOST: db:3306
      PHP_MEMORY_LIMIT: ${PHP_MEMORY_LIMIT}
    volumes:
      - wp_data:/var/www/html

  db:
    image: mariadb:10.11
    restart: always
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql

  redis:
    image: redis:7-alpine
    restart: always
    volumes:
      - redis_data:/data

  wp-cli:
    image: wordpress:cli-php8.3
    depends_on:
      - php
      - db
    volumes:
      - wp_data:/var/www/html
    entrypoint: wp

  cron:
    image: wordpress:cli-php8.3
    depends_on:
      - php
    volumes:
      - wp_data:/var/www/html
      - ./cron:/cron
    entrypoint: /cron/wp-cron.sh

volumes:
  wp_data:
  db_data:
  redis_data:
  cache_data:

4. nginx/conf.d/wordpress.conf

server {
  listen 80 default_server;
  server_name _;
  root /var/www/html;
  index index.php index.html;

  # 跳過快取條件
  set $skip_cache 0;
  if ($request_method = POST) { set $skip_cache 1; }
  if ($query_string != "")   { set $skip_cache 1; }
  if ($http_cookie ~* "wordpress_logged_in_|woocommerce_items_in_cart|woocommerce_cart_hash|wp_woocommerce_session_") { set $skip_cache 1; }
  if ($request_uri ~* "^/(wp-admin/|wp-login\.php|cart|checkout|my-account|wc-api/|wp-json/)") { set $skip_cache 1; }
  if ($request_uri ~* "add-to-cart=") { set $skip_cache 1; }
  if ($request_uri ~* "admin-ajax\.php") { set $skip_cache 1; }

  location / {
    try_files $uri $uri/ /index.php?$args;
  }

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

    fastcgi_buffers 16 32k;
    fastcgi_buffer_size 32k;
    fastcgi_read_timeout 120s;

    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;
    fastcgi_cache PHP_MICRO;
    fastcgi_cache_valid 200 301 302 5s;

    add_header X-Cache $upstream_cache_status;
    fastcgi_param HTTPS on;
    fastcgi_param SERVER_PORT 443;
    fastcgi_param HTTP_X_FORWARDED_PROTO https;
  }

  location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|webp|ico|ttf|otf|woff|woff2)$ {
    expires 30d;
    access_log off;
  }

  client_max_body_size 64m;
}

5. cron/wp-cron.sh

#!/bin/sh
set -e
# 每 1 分鐘跑一次 wp-cron
while true; do
  wp --path=/var/www/html --allow-root cron event run --due-now >/dev/null 2>&1 || true
  sleep 60
done

6. README.md(流程文件)

# WordPress on Docker (WooCommerce Ready)

## Services
- Nginx 1.25 (reverse proxy inside container)
- PHP-FPM 8.3 (with mysqli, pdo_mysql)
- MariaDB 10.11
- Redis 7 (object cache)
- WP-CLI
- Cron container for wp-cron

## Usage
```bash
docker compose up -d

WordPress setup

docker compose exec wp-cli wp core download --allow-root
docker compose exec wp-cli wp config create \
  --dbname=$DB_NAME --dbuser=$DB_USER --dbpass=$DB_PASSWORD \
  --dbhost=db:3306 --skip-check --allow-root
docker compose exec wp-cli wp core install \
  --url="https://mysite.com" \
  --title="MySite Shop" \
  --admin_user="admin" \
  --admin_password="ChangeMe123!" \
  --admin_email="you@example.com" \
  --allow-root

Redis setup

docker compose exec wp-cli wp config set WP_REDIS_HOST 'redis' --allow-root
docker compose exec wp-cli wp config set WP_REDIS_PORT 6379 --raw --allow-root
docker compose exec wp-cli wp config set WP_REDIS_DATABASE 1 --raw --allow-root
docker compose restart php
docker compose exec wp-cli wp redis enable --allow-root

Proxy setup (Host machine)

Nginx reverse proxy:

server {
    listen 443 ssl http2;
    server_name mysite.com;

    ssl_certificate     /etc/letsencrypt/live/mysite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}

Result

  • WordPress 正常啟動 (https://mysite.com)

  • Redis Object Cache 啟用

  • Nginx + PHP-FPM + MariaDB + Redis 健康運作