~/blog/wordpress-docker-compose-multiservice-deployment-guide.md
Laravel 與後端開發 · 2025 / 11 / 29 · 4 views

Docker Compose 實戰:一份 YAML 指揮 WordPress、Nginx、Redis、MariaDB 四重奏

Eric — 浪花科技創辦人 / AI 架構師
Eric
浪花科技創辦人 · AI 架構師
Docker Compose 實戰:一份 YAML 指揮 WordPress、Nginx、Redis、MariaDB 四重奏
目錄 table-of-contents.md

如果你還記得,我們之前聊過如何用 Docker 把 WordPress 包起來,解決了那句工程師最痛恨的魔咒:「在我電腦明明可以跑的啊?」。但說真的,那只是前菜。一個真正能打仗、扛得住流量的專業網站,絕對不是只有 WordPress 本體那麼單純。它更像一個交響樂團,需要網頁伺服器、資料庫、快取系統… 各司其職,完美協奏。

今天,我們就要來談談如何從一個單純的樂手,晉升為整個樂團的指揮家。我們要用的指揮棒,就是 Docker Compose。這篇文章會帶你從零開始,用一個 `docker-compose.yml` 檔案,一鍵啟動一個包含 Nginx、MariaDB、Redis 與 WordPress (PHP-FPM) 的高效能網站架構。別再手動一個一個安裝服務了,身為一個追求效率(跟懶惰)的工程師,自動化才是王道!

為什麼單一容器不夠?解構現代 WordPress 網站架構

在我們動手之前,先來個小小的精神喊話,或者說是工程師的囉嗦。為什麼我們需要這麼多服務?不能全部塞在一個 Docker 容器裡嗎?理論上可以,但那就像把小提琴手、鼓手、鋼琴師全塞在一個電話亭裡,不僅施展不開,還很容易因為一個人出錯(比如資料庫掛了)導致整個樂團(網站)跟著陪葬。

一個專業的 WordPress 網站架構,通常包含以下幾個核心角色:

  • 網頁伺服器 (Web Server) - Nginx: 樂團的門面擔當。它負責接收來自使用者的所有請求,把靜態檔案(如圖片、CSS、JS)直接回傳,並將動態請求(需要 PHP 處理的頁面)轉交給後端的 PHP-FPM。
  • PHP 處理器 (PHP-FPM): 樂團的主唱。這是 WordPress 的核心,負責執行所有 PHP 程式碼,從資料庫撈取資料,產生最終的 HTML 頁面。
  • 資料庫 (Database) - MariaDB: 樂團的靈魂與記憶庫。你的所有文章、頁面、使用者資料、設定… 全部都儲存在這裡。MariaDB 是 MySQL 的一個開源分支,完全相容且效能優異。
  • 物件快取 (Object Cache) - Redis: 樂團的加速器。每次頁面載入都去資料庫重新查詢太慢了。Redis 是一個基於記憶體的快取系統,可以將常用的查詢結果暫存在記憶體中,大幅減少資料庫的壓力,讓網站快到飛起。

透過 Docker Compose 多服務部署,我們可以將這四個角色分別放進獨立的容器中。它們各自獨立、互不干擾,卻又能透過 Docker 內建的虛擬網路高效溝通。這就是所謂的「關注點分離」,不僅維護方便,未來要針對某個服務(例如資料庫)單獨升級或擴展也輕而易舉。

Docker Compose 核心概念:服務、網路與卷宗

Docker Compose 的精髓全在一支名為 `docker-compose.yml` 的 YAML 格式設定檔。你可以把它想像成樂團的總樂譜,上面清楚標示了每個樂手(服務)該做什麼、用什麼樂器(映像檔)、以及他們之間如何溝通(網路)。

  • 服務 (Services): 樂譜中的每個獨立部分。在我們的例子中,`nginx`、`wordpress`、`db`、`redis` 就是四個獨立的服務。
  • 網路 (Networks): Docker Compose 會自動幫你建立一個專屬的內部虛擬網路,讓所有服務可以透過「服務名稱」直接溝通。例如,WordPress 容器可以直接用 `db` 這個主機名稱連到 MariaDB 容器,完全不用管它實際的 IP 是多少,超方便!而且這個網路是隔離的,你不用把資料庫的 3306 連接埠暴露在公網上,大大提升了安全性。
  • 卷宗 (Volumes): 容器本身是「無狀態」的,意思是只要容器一刪除,裡面的資料就灰飛煙滅了。這對程式碼來說沒問題,但對資料庫或使用者上傳的檔案來說可是天大的災難。卷宗 (Volume) 就是用來解決這個問題的,它能將主機的某個資料夾掛載到容器內部,讓資料可以永久保存,即使容器被銷毀重建,資料依然安然無恙。

實戰演練:打造你的 WordPress 高效能四重奏

理論說完了,來點實際的吧!跟著我一步一步把這個高效能架構建立起來。

Step 1: 建立專案結構

首先,在你的電腦上建立一個專案資料夾,結構如下:


my-wordpress-stack/
├── docker-compose.yml
├── nginx/
│   └── default.conf
└── wordpress/
    └── (這個資料夾會自動產生,用來存放 WordPress 核心檔案)

Step 2: 編寫指揮棒 `docker-compose.yml`

這就是我們整個架構的核心,請將以下內容貼到 `docker-compose.yml` 檔案中。我會逐段解釋每個設定的意義。


version: '3.8'

services:
  db:
    image: mariadb:10.6
    container_name: my_wp_db
    restart: unless-stopped
    volumes:
      - db_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: your_strong_root_password
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: your_strong_user_password
    networks:
      - wordpress_network

  redis:
    image: redis:6.2-alpine
    container_name: my_wp_redis
    restart: unless-stopped
    networks:
      - wordpress_network

  wordpress:
    image: wordpress:6.4-php8.1-fpm-alpine
    container_name: my_wp_app
    restart: unless-stopped
    depends_on:
      - db
      - redis
    volumes:
      - ./wordpress:/var/www/html
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: your_strong_user_password
      WORDPRESS_DB_NAME: wordpress
    networks:
      - wordpress_network

  nginx:
    image: nginx:1.25-alpine
    container_name: my_wp_nginx
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./wordpress:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - wordpress
    networks:
      - wordpress_network

volumes:
  db_data:

networks:
  wordpress_network:
    driver: bridge

稍微囉嗦一下各區塊的設定:

  • db 服務:使用 MariaDB 10.6 映像檔。volumes: - db_data:/var/lib/mysql 這行是關鍵,它建立了一個名為 `db_data` 的具名卷宗 (named volume),專門用來存放資料庫檔案。`environment` 則是用來設定資料庫的各種帳號密碼。
  • redis 服務:相對單純,就是啟動一個 Redis 容器。
  • wordpress 服務:我們用的是 `fpm-alpine` 版本的映像檔,它只包含 PHP-FPM,不含網頁伺服器,更輕量、更專業。depends_on 確保它會在 `db` 和 `redis` 啟動之後才啟動。volumes: - ./wordpress:/var/www/html 則是將我們主機的 `wordpress` 資料夾掛載進去,這樣我們就能直接在主機上修改佈景主題或外掛檔案了。
  • nginx 服務:負責對外。ports: - "8080:80" 把容器的 80 port 映射到我們主機的 8080 port,這樣我們就能透過 `http://localhost:8080` 訪問網站。它同時掛載了 WordPress 的檔案目錄和我們自訂的 Nginx 設定檔。

Step 3: Nginx 設定檔 (`nginx/default.conf`)

Nginx 需要知道如何處理 PHP 請求。將以下內容貼到 `nginx/default.conf`:


server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html index.htm;

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

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

這裡的魔法是 fastcgi_pass wordpress:9000;。它告訴 Nginx,把所有 PHP 請求都轉發給一個叫做 `wordpress` 的主機的 9000 port,而這個 `wordpress` 正是我們在 `docker-compose.yml` 裡定義的服務名稱!這就是 Docker Compose 內建 DNS 解析的威力。想了解更多 Nginx 的調校技巧,可以參考這篇 Nginx 效能調校聖經

Step 4: 啟動!`docker-compose up -d`

萬事俱備!在你的專案根目錄下,打開終端機,執行以下指令:

docker-compose up -d

-d 參數代表在背景執行。Docker Compose 會開始下載映像檔、建立網路、卷宗並依序啟動所有容器。你可以用 docker-compose ps 來查看所有容器的狀態。第一次啟動需要幾分鐘,請耐心等候。

啟動成功後,打開瀏覽器訪問 `http://localhost:8080`,你應該就能看到熟悉的 WordPress 安裝畫面了!

整合 Redis 物件快取:讓你的 WordPress 飛起來

我們的 Redis 容器已經在背景默默運作了,但 WordPress 還不知道它的存在。我們需要搭起他們之間的橋樑。

  1. 安裝 Redis 外掛: 進入 WordPress 後台,安裝並啟用「Redis Object Cache」這個外掛。
  2. 設定 `wp-config.php`: 啟用外掛後,它可能會提示 `wp-config.php` 無法寫入。沒關係,我們手動來。打開 `./wordpress/wp-config.php` 檔案 (如果還沒有,可以先完成 WordPress 安裝讓它自動生成),在 `/* That's all, stop editing! Happy publishing. */` 這行註解之前,加入以下設定:
    
    define('WP_REDIS_HOST', 'redis');
    define('WP_REDIS_PORT', 6379);
    define('WP_CACHE_KEY_PREFIX', 'my_wp_site:');
    define('WP_CACHE', true);
    
  3. 啟用物件快取: 回到後台的 Redis Object Cache 設定頁面,點擊「Enable Object Cache」。如果狀態顯示為「Connected」,恭喜你!你的 WordPress 已經裝上了 Redis 渦輪引擎!想深入了解 Redis 如何為 WordPress 加速,可以看看這篇 Redis 物件快取實戰教學

工程師的囉嗦時間:常見陷阱與最佳實踐

踩過坑的學長告訴你,光是能跑起來還不夠,有幾個細節能讓你的開發流程更順暢、更安全。

檔案權限問題

Docker for Mac/Windows 通常處理得比較好,但在 Linux 上,你可能會遇到 Nginx 或 WordPress 因為權限不足而無法寫入 `./wordpress` 資料夾的問題。這是因為容器內的使用者 ID (UID) 和主機上的使用者 ID 不匹配。一個常見的解法是,在啟動容器後,手動修改主機上資料夾的權限,例如 sudo chown -R www-data:www-data wordpress/,但這不是最優雅的方式。更進階的作法是在 Dockerfile 中去動態設定使用者 ID,但這對初學者來說有點複雜,我們先知道有這個問題即可。

環境變數管理

直接把資料庫密碼寫在 `docker-compose.yml` 裡不是個好習慣,尤其當你要把這個檔案交給同事或上傳到 Git 時。最佳實踐是使用 `.env` 檔案。你可以在 `docker-compose.yml` 同層目錄下建立一個 `.env` 檔案,內容如下:


DB_ROOT_PASSWORD=your_strong_root_password
DB_USER=wordpress
DB_PASSWORD=your_strong_user_password
DB_NAME=wordpress

然後修改 `docker-compose.yml`,用 ${VARIABLE_NAME} 的語法來讀取:


# ...
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
# ...

這樣一來,你的敏感資訊就跟設定檔分開了,記得把 `.env` 加入到 `.gitignore` 中!

結論:你已經是半個 DevOps 工程師了!

恭喜你!你不再只是一個會用 WordPress 的開發者,你已經掌握了如何用 Docker Compose 這個強大的工具,來編排和管理一個現代化的、高效能的、可複製的網站應用程式架構。從今天起,無論是建立一個新的本地開發環境,還是要將整個專案打包交給同事,都只是一行 `docker-compose up -d` 的事。

這套架構不僅適用於開發,稍作調整(例如加入 SSL 憑證管理、更完善的備份策略),它完全有能力成為你正式上線網站的基石。這就是容器化帶來的革命:它模糊了開發與維運的界線,讓開發者能更好地掌控自己的應用程式。


延伸閱讀

打造一個穩固又高效的網站架構是專案成功的基礎。如果你在部署過程中遇到任何問題,或是希望為你的企業導入更專業、更客製化的 DevOps 流程與網站架構,浪花科技的團隊擁有豐富的實戰經驗。我們不只是寫程式,更是你數位轉型路上的技術夥伴。

別讓複雜的技術問題拖慢你的腳步,立即聯繫我們,讓我們的專業團隊為你量身打造最適合的解決方案!

// FAQ

常見問題

用 Docker Compose 部署的 WordPress 高效能架構通常包含哪些服務?
一個專業架構通常由四個角色組成:Nginx 作為網頁伺服器負責接收請求並回傳靜態檔案、轉交動態請求;PHP-FPM 執行 WordPress 的 PHP 程式碼產生 HTML;MariaDB 作為資料庫儲存所有文章、使用者與設定;Redis 作為物件快取,把常用查詢結果暫存在記憶體以減輕資料庫壓力。各服務放在獨立容器中,互不干擾又能高效溝通。
為什麼不把 WordPress、資料庫、快取全部塞進同一個 Docker 容器?
把所有服務塞進單一容器違反「關注點分離」原則,不僅施展不開,還會因為單一服務出錯(例如資料庫掛掉)而導致整個網站一起停擺。將各服務拆成獨立容器後,維護更方便,未來要針對某個服務單獨升級或擴展也更容易。
Docker Compose 中的 Volume(卷宗)有什麼用,為什麼資料庫一定要用?
容器本身是無狀態的,一旦刪除容器,裡面的資料就會消失。Volume 可將主機的某個資料夾掛載到容器內部,讓資料永久保存,即使容器被銷毀重建資料依然存在。因此資料庫檔案(如掛載 db_data 到 /var/lib/mysql)與使用者上傳的檔案都必須使用 Volume,才不會在重建容器時遺失。
在 Docker Compose 中,WordPress 容器要怎麼連到 MariaDB 容器?
Docker Compose 會自動為同一份設定檔中的服務建立專屬的內部虛擬網路,讓服務之間可直接以「服務名稱」當主機名稱互相溝通。例如 WordPress 容器只要把資料庫主機設為 db:3306 即可連到名為 db 的 MariaDB 容器,不需知道實際 IP。這個內部網路是隔離的,也不必把資料庫的 3306 連接埠暴露到公網,更安全。
WordPress 的 Docker 映像檔為什麼選用 fpm-alpine 版本?
fpm-alpine 版本只包含 PHP-FPM 而不含內建網頁伺服器,更輕量也更專業,能與獨立的 Nginx 容器搭配,由 Nginx 專責處理 HTTP 請求、PHP-FPM 專責執行 PHP,符合各司其職的設計,並可透過 depends_on 確保它在 db 與 redis 啟動之後才啟動。
~/roamer-tech/newsletter // FREE
// newsletter

訂閱免費電子報

把 AI 自動化、企業系統設計與 WordPress / Laravel 開發的真實案例和可直接照做的技巧,整理成電子報寄給你。只寄精選內容、不灌垃圾信,一鍵就能退訂。

$
// final.exec()

準備好讓你的網站開始為你工作了嗎?