Select Page

雲端現代化:如何將 WordPress 部署至 Cloud Run、Cloud SQL 與 Cloud Storage

在管理多個 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 DATABASEUSE 語句,以免匯入失敗或指向錯誤的資料庫。

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 負載平衡器

  1. 建立 Serverless NEG:讓負載平衡器找到 Cloud Run 。
  2. 設定前端 IP:保留一個靜態全域 IP。
  3. Google 管理憑證:在前端設定中新增網域,Google 會自動處理 SSL 簽發與續期 。
  4. 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

參考資料

NVIDIA Spark 如何啟用 VNC:使用 Vino 與 RealVNC 遠端桌面完整教學

在使用 nvidia spark 或 NVIDIA Jetson 類型的 AI 開發平台時,很多開發者會希望能夠遠端操作設備,而不是每次都連接螢幕、鍵盤與滑鼠。這時候 VNC(Virtual Network Computing) 就是一個非常方便的遠端桌面解決方案。

透過 vino VNC ServerRealVNC Viewer,你可以在 Windows、macOS 或 Linux 上遠端連線到 NVIDIA Spark 的桌面環境,像是直接操作本機一樣。VNC 可以透過網路傳輸桌面畫面與輸入操作,因此非常適合 AI 開發、邊緣設備管理與遠端維護。

本文將介紹如何在 nvidia spark / Ubuntu 系統 中啟用 VNC Server(vino),並透過 RealVNC Viewer 進行遠端連線。

為什麼 NVIDIA Spark 建議使用 VNC

在 AI 或嵌入式開發場景中,遠端桌面有幾個重要用途:

  • 遠端查看 GPU 程式的 GUI
  • 遠端操作桌面應用程式
  • headless(無螢幕)設備管理
  • 在不同電腦之間共享桌面

VNC 允許使用者透過網路存取 Linux 圖形桌面,而不需要實際連接顯示器。


NVIDIA Spark 啟用 VNC(vino)教學

以下步驟適用於 Ubuntu / GNOME 環境的 nvidia spark

1 安裝 VNC Server(vino)

首先在 NVIDIA Spark 中安裝 vino

sudo apt update
sudo apt install vino

vino 是 GNOME 桌面內建的 VNC Server,常用於 Ubuntu 遠端桌面。


2 啟用 VNC Server 自動啟動

建立 symbolic link,讓 vino 在登入後自動啟動:

cd /usr/lib/systemd/user/graphical-session.target.wants
sudo ln -s ../vino-server.service ./

這樣每次登入桌面時,VNC 服務就會自動啟動。


3 設定 VNC Server

關閉安全提示與加密:

gsettings set org.gnome.Vino prompt-enabled false
gsettings set org.gnome.Vino require-encryption false

這些設定可以避免某些 VNC Viewer 無法連線的問題。


4 設定 VNC 密碼

設定遠端登入密碼:

gsettings set org.gnome.Vino authentication-methods "['vnc']"
gsettings set org.gnome.Vino vnc-password $(echo -n 'yourpassword' | base64)

請將 yourpassword 替換為自己的密碼。


5 重新啟動系統

sudo reboot

重新啟動後,VNC Server 設定就會生效。


使用 RealVNC Viewer 連線 NVIDIA Spark

在另一台電腦安裝 RealVNC Viewer

接著:

  1. 查詢 NVIDIA Spark IP
ifconfig

ip a
  1. 開啟 RealVNC Viewer
  2. 輸入 IP 地址

例如:

192.168.1.50
  1. 輸入剛剛設定的 VNC 密碼

即可成功遠端操作 NVIDIA Spark 桌面。


VNC 常見問題

1 VNC 無法連線

可能原因:

  • vino 沒有啟動
  • GNOME 沒有登入
  • 防火牆阻擋

確認服務:

ps aux | grep vino

2 VNC 黑畫面

可能是:

  • 沒有登入桌面
  • 沒有顯示器(headless模式)

可設定 自動登入(Auto Login) 解決。


VNC、RealVNC、vino 的差異

工具功能
vinoLinux GNOME VNC Server
VNC遠端桌面協定
RealVNC Viewer常用 VNC 客戶端

通常搭配方式:

vino server + RealVNC viewer

結論

透過 VNC + vino + RealVNC,可以快速讓 nvidia spark 具備遠端桌面能力,對於 AI 開發、遠端管理或 headless 系統來說非常方便。

只需要簡單幾個步驟:

  1. 安裝 vino
  2. 啟用 VNC server
  3. 設定密碼
  4. 使用 RealVNC 連線

就能輕鬆遠端控制 NVIDIA Spark。

相關資料

https://developer.nvidia.com/embedded/learn/tutorials/vnc-setup

WordPress 效能翻倍:手把手教你將媒體資源遷移至 Google Cloud Storage (GCS)

當你的 WordPress 網站發展到多站點規模時,伺服器硬碟空間與流量往往成為瓶頸,我們透過 wp-content/uploads 遷移到 Google Cloud Storage (GCS) 並搭配 Cloud CDN,不僅能節省主機成本,還能大幅提升全球存取速度。

一、 為什麼選擇 GCS + CDN 而非 Filestore?

在 GCP 上有兩種主流方案:

  1. Filestore (NFS):適合需要完全同步 wp-content(含外掛與主題)的架構,但成本較高(1TB 起跳約 $160/月)。
  2. 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),建議分別執行同步以保持目錄整潔:

  • 同步站點 Agcloud storage rsync /mnt/webs/demo-1/wp-content/uploads gs://ai-websites-uploads/demo-1 -r
  • 同步站點 Bgcloud storage rsync /mnt/webs/blaupunkt/wp-content/uploads gs://ai-websites-uploads/blaupunkt -r

4. 權限排錯

如果您在同步時遇到「Permission Denied」,請檢查以下兩點:

  1. IAM 角色:確保服務帳戶擁有 「儲存空間物件管理員 (Storage Object Admin)」 角色。
  2. 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 設定檔。

PHP-FPM 被 OOM Killer 殺掉?完整排查與修復實戰紀錄(Ubuntu 16GB RAM 環境)

最近我的伺服器突然出現網站 502 錯誤,檢查後發現 php8.2-fpm 服務竟然被系統強制終止。透過一系列排查,最終確認是 Linux OOM Killer(Out Of Memory) 觸發導致。

這篇文章整理完整排查過程與解決方案,提供給遇到相同問題的人參考。


📌 問題現象

使用 systemctl status php8.2-fpm 發現:

Active: failed (Result: oom-kill)

進一步檢查 kernel log:

Out of memory: Killed process 7057 (php-fpm8.2)
anon-rss:283560kB
shmem-rss:119848kB

代表:

  • 系統記憶體耗盡
  • Linux 啟動 OOM Killer
  • 強制殺掉 php-fpm worker

🔍 進一步分析

查看當時 PHP worker 記憶體使用情況:

ps -o pid,rss,etime,cmd -C php-fpm8.2 --sort=-rss | head -n 20

當時結果顯示:

RSS 約 250MB ~ 325MB/每個 worker

而之前狀態顯示:

Processes active: 80
idle: 0

🔥 問題核心

如果每個 worker 約 300MB:

300MB × 80 = 24GB

但機器只有:

15GB RAM
0GB Swap

👉 記憶體一定會爆。


🧠 伺服器環境

Memory: 15GiB
Swap: 0

這代表:

  • 沒有 swap 作為緩衝
  • 一旦瞬間記憶體尖峰,直接 OOM

🛠 解決方案


✅ 1️⃣ 建立 8GB Swap(防止再次 OOM)

sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

確認:

swapon --show
free -h

結果:

Swap: 8.0GiB

建議調整 swappiness

echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl -p /etc/sysctl.d/99-swappiness.conf

✅ 2️⃣ 調整 PHP-FPM 設定(關鍵)

編輯:

/etc/php/8.2/fpm/pool.d/www.conf

修改為:

pm = dynamic
pm.max_children = 25
pm.start_servers = 6
pm.min_spare_servers = 4
pm.max_spare_servers = 10
pm.max_requests = 500

為什麼設 25?

假設每個 worker 約 150MB:

150MB × 25 ≈ 3.7GB

在 15GB RAM 環境下非常安全。


✅ 3️⃣ 開啟 Slow Log(抓出慢請求)

www.conf 加入:

request_slowlog_timeout = 10s
slowlog = /var/log/php8.2-fpm/www-slow.log
request_terminate_timeout = 60s

建立目錄:

sudo mkdir -p /var/log/php8.2-fpm
sudo chown -R www-data:www-data /var/log/php8.2-fpm
sudo systemctl restart php8.2-fpm

查看慢請求:

sudo tail -f /var/log/php8.2-fpm/www-slow.log

📊 調整後效果

重啟後查看:

ps -o pid,rss,etime,cmd -C php-fpm8.2 --sort=-rss | head -n 20

結果:

RSS 約 100MB ~ 170MB
ELAPSED 僅 20 秒左右

✔ 不再出現 300MB 以上肥 worker
✔ 不再出現 active 80
✔ 系統穩定運作


🎯 問題總結

本次 OOM 原因:

  1. pm.max_children 設定過高
  2. 每個 worker 記憶體使用偏高
  3. 沒有 swap 緩衝
  4. 慢請求導致 worker 堆積

最終解法:

  • 限制 max_children
  • 建立 swap
  • 開 slowlog 抓慢請求
  • 設定 max_requests 避免 memory leak

🚀 建議最佳實務

設定建議
Swap一定要有(至少 4GB)
max_children用 RAM / worker RSS 計算
max_requests300~1000
slowlog必開
request_terminate_timeout必設

📌 結論

OOM Killer 不是錯誤,它是保護機制。

真正的問題通常是:

  • PHP-FPM 設定不合理
  • 慢請求堆積
  • 記憶體容量與負載不匹配

只要正確限制 worker 數量並建立 swap,伺服器穩定度會大幅提升。


如果你也遇到 php-fpm 被 OOM kill 的問題,希望這篇實戰紀錄能幫助你快速排查與解決。

相關資訊