by Rain Chu | 3 月 12, 2026 | Docker, google, wordpress
在管理多個 WordPress 專案時,傳統 VM 加架構往往面臨擴展性與維護成本的挑戰。透過 Google Cloud Run (Serverless)、Cloud SQL (代管資料庫) 與 Cloud Storage (雲端儲存) 的組合,我們可以建立一個自動縮放、安全且高效率的網站環境。
一、 架構預覽
- 計算節點:Google Cloud Run (Docker 容器化運行)。
- 資料庫:Google Cloud SQL (MySQL 8.0)。
- 靜態檔案:Google Cloud Storage (GCS)。
- 流量分配:Google Cloud Load Balancing (HTTPS 負載平衡器)。
二、 準備 Docker 鏡像與環境排除
在打包之前,請務必設定 .dockerignore 以優化鏡像體積並保護敏感資訊 。
my-wp-site/
├── Dockerfile # 自動化打包腳本
├── wp-config.php # 修改為讀取環境變數的版本
├── .dockerignore # 排除不需要打包的檔案 (如 .git, local backups)
└── wp-content/
├── plugins/ # 放置您自定義的外掛
└── themes/ # 放置您自定義的主題
建立 標準化 Dockerfile 範本
# 使用官方 PHP-Apache 映像檔,穩定且相容性高
FROM wordpress:php8.2-apache
# 1. 設定環境變數 (Cloud Run 預設監聽 8080,但官方 WP 鏡像預設是 80)
# 這裡我們讓 Apache 監聽 Cloud Run 指定的 PORT
RUN sed -i 's/Listen 80/Listen ${PORT}/g' /etc/apache2/ports.conf
RUN sed -i 's/:80/:${PORT}/g' /etc/apache2/sites-available/000-default.conf
# 2. 安裝必要的系統套件 (如有需要自訂 PHP 擴展可在這加)
RUN apt-get update && apt-get install -y \
libpng-dev \
libjpeg-dev \
&& docker-php-ext-configure gd --with-jpeg \
&& docker-php-ext-install gd
# 3. 複製現有的自定義檔案進入容器
# 建議只複製 plugins 和 themes,核心檔案由官方鏡像提供
COPY ./wp-content/plugins/ /var/www/html/wp-content/plugins/
COPY ./wp-content/themes/ /var/www/html/wp-content/themes/
COPY ./wp-config.php /var/www/html/wp-config.php
# 4. 設定正確的檔案權限 (對 WordPress 運行至關重要)
RUN chown -R www-data:www-data /var/www/html
# 5. 設定預設環境變數 (可在部署時被 gcloud 指令覆蓋)
ENV PORT=8080
ENV DB_HOST=127.0.0.1
ENV DB_USER=root
ENV DB_PASSWORD=password
# 暴露埠號
EXPOSE 8080
1. 建立 .dockerignore
Plaintext
.git
.gitignore
.dockerignore
Dockerfile
*.sql
*.zip
.vscode/
wp-config-sample.php
2. 打包與推送鏡像
PowerShell
# 編譯鏡像
docker build -t asia-east1-docker.pkg.dev/[PROJECT_ID]/wp-repo/[docker_name]:latest .
# 推送到 Artifact Registry
docker push asia-east1-docker.pkg.dev/[PROJECT_ID]/wp-repo/[docker_name]:latest
三、 資料庫遷移與設定
1. 匯入 SQL 腳本
將 .sql 檔案上傳至 Google Cloud Storage (GCS) 後執行匯入 。
注意:請確保 SQL 檔案中不含 CREATE DATABASE 或 USE 語句,以免匯入失敗或指向錯誤的資料庫。
PowerShell
gcloud sql import sql [INSTANCE_NAME] gs://[BUCKET_NAME]/[docker_name].sql --database=[docker_name]_db
2. 設定 wp-config.php 智慧判斷
為了同時支援本地開發與雲端環境,建議在 wp-config.php 加入連線判斷邏輯 :
PHP
// 偵測是否在 Cloud Run 環境 (透過 Unix Socket 連線)
if (getenv('INSTANCE_CONNECTION_NAME')) {
define( 'DB_HOST', ':/cloudsql/' . getenv('INSTANCE_CONNECTION_NAME') );
} else {
define( 'DB_HOST', getenv('DB_HOST') ?: '127.0.0.1' );
}
// 負載平衡器 HTTPS 辨識
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
四、 部署至 Cloud Run
部署時需指定 Cloud SQL 連線名稱,這會自動建立加密隧道 。
PowerShell
gcloud run deploy [docker_name] `
--image asia-east1-docker.pkg.dev/[PROJECT_ID]/wp-repo/[docker_name]:latest `
--region asia-east1 `
--allow-unauthenticated `
--add-cloudsql-instances [PROJECT_ID]:asia-east1:[INSTANCE_NAME] `
--set-env-vars="INSTANCE_CONNECTION_NAME=[PROJECT_ID]:asia-east1:[INSTANCE_NAME],DB_NAME=[docker_name]_db,DB_USER=root,DB_PASSWORD=[PASSWORD]"
五、 設定負載平衡器 (GCLB) 與自訂網域
為了使用自有的網域(如 blog.rain.tips),建議使用 HTTPS 負載平衡器 。
- 建立 Serverless NEG:讓負載平衡器找到 Cloud Run 。
- 設定前端 IP:保留一個靜態全域 IP。
- Google 管理憑證:在前端設定中新增網域,Google 會自動處理 SSL 簽發與續期 。
- DNS 設定:將您的網域 A 紀錄 指向負載平衡器的靜態 IP 。
六、 故障排除 (Troubleshooting)
- Error establishing a database connection:
- 檢查 Cloud Run 服務帳戶是否擁有 「Cloud SQL Client」 角色 。
- 確認
DB_HOST 在雲端環境是否正確指向 :/cloudsql/... 。
- 503 Service Unavailable:
- 確認 Cloud Run 服務已設定為 「允許未經驗證的叫用」 。
- 檢查負載平衡器的憑證是否已變為綠色的 Active 狀態 。
- IPv6 連線問題:
- 若使用 Nginx 反向代理遇到
Network is unreachable,請強制 Nginx 優先使用 IPv4 或修改系統 /etc/hosts 。
參考資料
by Rain Chu | 3 月 3, 2026 | wordpress
當你的 WordPress 網站發展到多站點規模時,伺服器硬碟空間與流量往往成為瓶頸,我們透過 wp-content/uploads 遷移到 Google Cloud Storage (GCS) 並搭配 Cloud CDN,不僅能節省主機成本,還能大幅提升全球存取速度。
一、 為什麼選擇 GCS + CDN 而非 Filestore?
在 GCP 上有兩種主流方案:
- Filestore (NFS):適合需要完全同步
wp-content(含外掛與主題)的架構,但成本較高(1TB 起跳約 $160/月)。
- GCS + Cloud CDN:按量計費,且能透過邊緣節點加速圖片,是多數 WordPress 站長的性價比首選。
二、 核心架構建立:GCS 與權限設定
1. 建立儲存桶與開放權限
在 GCS 建立儲存桶後,務必處理 CORS 與公開存取問題,否則圖片會出現 AccessDenied。
- 公開讀取:在儲存桶權限中加入
allUsers 為「儲存空間物件檢視者」。
- 解決字體 CORS 報錯:若使用 Elementor,必須透過
gcloud 指令設定 CORS 規則,允許您的網域存取字體檔 (.woff2)。
2. 服務帳戶 (IAM) 設定
為您的 WordPress 建立專屬服務帳戶,並指派 「儲存空間管理員 (Storage Admin)」 角色。這能確保 WP-Stateless 等外掛有權限執行同步與驗證儲存桶狀態。
三、 WordPress 插件配置:WP-Stateless
為了讓多站點井然有序,建議在 WP-Stateless 設定中自定義路徑:
- Mode:選擇
Stateless(上傳後刪除本地檔案,釋放 VM 空間)。
- Folder:手動輸入站點名稱,例如
demo-1/%date_year/date_month%。
- Service Account JSON:貼入從 GCP 下載的 JSON 金鑰以完成對接。
四、 Nginx 層級的自動化重定向
為了確保舊文章不破圖,且能自動導向 GCS,我們在 Nginx 加入了變數化的重定向規則。
最佳實踐:排除 Elementor 以避開錯誤
Elementor 的 CSS 與字體檔案若放在 GCS 容易產生 CORS 阻擋或 404,建議將其留在本地 SSD:
Nginx
# 1. 優先處理 Elementor (留在本地)
location ^~ /xxx/wp-content/uploads/elementor/ {
include /etc/nginx/sites-available/gcp_web_pool.conf;
}
# 2. 其他媒體檔案重定向至 GCS
location ~* ^/xxx/wp-content/uploads/(.*)$ {
add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com' always;
return 301 https://storage.googleapis.com/your-bucket/sites-name/$1;
}
注意:將重定向規則放在單獨的 location 塊中,可以有效避免嵌套導致的 504 Gateway Timeout。
五、 舊檔案遷移與同步
對於已經存在 VM 上的數 GB 舊圖片,使用 gcloud storage rsync 是最快的方法:
Bash
# 同步指令 (遞迴處理所有子目錄)
gcloud storage rsync /var/www/html/wp-content/uploads gs://your-bucket/demo-1 -r
傳輸完成後,別忘了使用 WP-CLI 批次取代資料庫中的網址:
Bash
# 加入 --skip-plugins 避免 cron 相關的 PHP Fatal error
wp search-replace 'https://old-url.com/uploads' 'https://storage.googleapis.com/bucket/demo-1' --all-tables --skip-plugins --skip-themes
WordPress 多站點環境設計的同步教學:
1. 基本同步指令
如果您要將 VM 上的 uploads 資料夾同步到 GCS,請執行:
Bash
gcloud storage rsync /var/www/html/wp-content/uploads gs://[您的儲存桶名稱]/[站點目錄] --recursive
- 參數說明:
/var/www/...:來源路徑(本地 VM)。
gs://...:目標路徑(GCS 儲存桶)。
--recursive (或 -r):遞迴處理所有子目錄。
2. 進階同步技巧
A. 鏡像同步(刪除 GCS 上多餘的檔案)
如果您希望 GCS 的內容與本地完全一致(本地刪除,雲端也跟著刪除),請加上 --delete-unmatched-destination-objects:
Bash
gcloud storage rsync [來源] [目標] -r --delete-unmatched-destination-objects
警告:此操作不可逆,請確認目標路徑無誤再執行。
B. 僅同步特定類型的檔案
如果您只想同步圖片,排除備份檔(如 .zip 或 .sql):
Bash
gcloud storage rsync [來源] [目標] -r --include-regex=".*\\.(jpg|jpeg|png|gif)$"
C. 大量檔案平行加速
當您有數萬張圖片需要同步時,使用多執行緒可以大幅縮短時間:
Bash
gcloud storage rsync [來源] [目標] -r --parallel
3. 多站點組織化同步建議
既然您有多個站點(如 demo-1, blaupunkt),建議分別執行同步以保持目錄整潔:
- 同步站點 A:
gcloud storage rsync /mnt/webs/demo-1/wp-content/uploads gs://ai-websites-uploads/demo-1 -r
- 同步站點 B:
gcloud storage rsync /mnt/webs/blaupunkt/wp-content/uploads gs://ai-websites-uploads/blaupunkt -r
4. 權限排錯
如果您在同步時遇到「Permission Denied」,請檢查以下兩點:
- IAM 角色:確保服務帳戶擁有 「儲存空間物件管理員 (Storage Object Admin)」 角色。
- VM 存取範圍:確認 VM 的 Cloud API access scopes 已設定為「Allow full access to all Cloud APIs」。
下一步驗證
同步完成後,您可以執行以下指令查看 GCS 上的檔案數量,確保與本地一致:
Bash
gcloud storage ls -r gs://[您的儲存桶]/[站點目錄] | wc -l
七、 常見疑難排解 (FAQ)
- Q: 出現 403 Forbidden? A: 檢查 IAM 角色是否為
Storage Admin,並確認儲存桶是否已加入 allUsers。
- Q: Nginx 出現 504 Timeout? A: 檢查是否有嵌套迴圈。建議將
301 return 移出 include 所在的區塊。
- Q: 字體無法載入? A: 這是 CORS 問題。請檢查 GCS 儲存桶是否已套用包含您的網域的 JSON CORS 設定檔。
by Rain Chu | 12 月 31, 2025 | wordpress
目前正在處理的 request
編輯 php-fpm pool(www)
啟用下面的這幾行
重啟服務
檢查服務
curl 127.0.0.1/fpm-status?full
會看到的資訊
慢查詢-外掛定位
查詢
tail -f /var/log/php-fpm/slow.log
# 或是
tail -n 50 /var/log/php-fpm/slow.log
針對慢查詢,精準的找出問題
慢站點 Top:
sudo awk -F'= ' '/^script_filename/ {print $2}' /var/log/www-slow.log \
| awk -F'/' '{print $4}' | sort | uniq -c | sort -nr | head -20
入口 Top:
sudo awk -F'= ' '/^script_filename/ {print $2}' /var/log/www-slow.log \
| awk -F'/' '{print $NF}' | sort | uniq -c | sort -nr | head -20
外掛 Top:
多站的正確架構
每站一個 pool:
Nginx 的對應:
by rainchu | 12 月 19, 2024 | Nginx, SERVER, wordpress
大前提你要先安裝有支援 webp, avif 的外掛,例如EWWW, Convert Media等等
在 Nginx 配置中加 map 指令:
- 在 Nginx 的主配置文件,通常在
/etc/nginx/nginx.conf。
- 在
http {} 内、server {} 之前,加入以下内容:
map $http_accept $avif_suffix {
default "";
"~*avif" ".avif";
}
map $http_accept $webp_suffix {
default "";
"~*webp" ".webp";
}
- 配置客户端的
Accept ,來定義 $avif_suffix 和 $webp_suffix 變量,確認是否支持 AVIF 或 WebP 格式。
Nginx 讀取 AVIF 和 WebP 的 MIME Type:
找到 /etc/nginx/ 的 mime.types
image/avif avif;
image/webp webp;
在站點配置中設定重導向:
- 找到
/etc/nginx/sites-available/yourdomain。
- 在
server {} ,加入以下内容:
location ~* ^/wp-content/.+\.(jpe?g|png|gif)$ {
add_header Vary Accept;
add_header Cache-Control "private";
expires 365d;
try_files $uri$avif_suffix $uri$webp_suffix $uri =404;
}
作用是:
- 匹配所有請求的
.jpg、.jpeg、.png 和 .gif 文件。
-
Vary: Accept ,告訴缓存系統按照 Accept 來區分不同的響應。
- 缓存控制和過期時間。
- 使用
try_files 指令,會先試提供的 .avif 文件,如果不存在,則提供 .webp 文件,若仍不存在,則提供原始文件。
測試並且重新加載 Nginx 配置:
sudo nginx -t
sudo systemctl reload nginx
這些配置假設您的 .avif 和 .webp 文件與原始圖片文件位於相同的目錄,並具有相同的文件名,只是副檔名不同(例如,image.jpg 對應 image.jpg.avif 和 image.jpg.webp)。
此外,請確保您的伺服器上已經存在對應的 .avif 和 .webp 文件,否則 Nginx 將返回 404 錯誤。
有關更多詳細資訊,您可以參考以下資源:
網站圖片無縫兼容 WebP/AVIF
在 Nginx 上提供 WebP 和 AVIF 圖片
圖片優化新選擇!WebP、AVIF 如何提升網站速度
by Rain Chu | 10 月 19, 2024 | Nginx, SERVER
如果你也是 wordpress 愛用 WP Rocket 的使用者,並且WEB SERVER是用 NGINX 架設的,那肯定要參考這個 GitHub 項目,來作為你 Nginx 以及伺服器上面的最佳設定
安裝
先從 github 中下載下來
cd /etc/nginx
git clone https://github.com/satellitewp/rocket-nginx.git
並且利用 PHP 產生預設的配置檔
cd rocket-nginx
cp rocket-nginx.ini.disabled rocket-nginx.ini
php rocket-parser.php
設定 Nginx
找到 nginx 的設定檔,通常在 /etc/nginx/sites-available ,並且加入設定
server {
...
# Rocket-Nginx configuration
include rocket-nginx/conf.d/default.conf;
...
}
項目位置
GitHub Rocket-nginx
近期留言