The Operator's Field Manual Vol. 03 · Issue 14 2026 · Self-Hosted Edition
A Field Manual · Performance Engineering

UnoPim × Lightsail
效能調校指南

UnoPim(Laravel 12)部署於 AWS Lightsail 的完整效能調校指南, 涵蓋作業系統、PHP-FPM、Nginx、MySQL、Redis、Octane 至 CloudFront 七個層級的參數設定與實戰說明。

Subject
unopim/unopim · Laravel 12
Target
AWS Lightsail · Ubuntu 24.04
Reading Time
約 45 分鐘 · 實作 3-4 小時
Difficulty
★★★☆☆ 中階 SysAdmin
00
Prologue · 序章

開始之前:前置條件與閱讀路徑

本手冊說明 UnoPim 在 AWS Lightsail 上效能不佳的根本原因,並逐層列出需要修改的設定檔、修改原因、預期效益,以及社群記錄的常見問題。適合已完成基礎安裝、需要調整效能的工程師。

AWS Lightsail 本質是固定價格折扣的 EC2 t3/m7i-flex 系列,價格固定、配置簡單,但有一個關鍵特性需要了解:CPU burst credit 制度。 當你 CPU 使用率長時間超過 baseline(通常是 5%-20%),instance 會從「綠燈」進入「黃燈」, 然後 burst capacity 用完後降頻到 baseline 的速度。許多人抱怨 Lightsail「莫名其妙就變慢」, 九成是踩到這條線。

UnoPim 執行 Laravel 12,依賴 MySQL 8、Elasticsearch 8 與 Redis。Magic AI / AI Agent 模組另需額外記憶體。最低建議規格為 2 GB RAM;正式環境建議從 4 GB 起跳。$3.5/月的 512 MB 規格不足以執行 UnoPim。

本手冊的閱讀路徑

  1. 第一次自架 / 對 Linux 還不熟:請依照 序章 → 02 → 03 → 04 → 10 順序,先把站架起來,再回頭調校。
  2. 已經有站,但很慢:直接跳到 05(OPcache)→ 06(PHP-FPM)→ 08(MySQL)→ 09(Redis),這四個是最大 CP 值。
  3. 想榨乾 Lightsail:把 04 到 14 全部做完,最後接 CloudFront。
  4. 10 萬筆以上商品的真實用戶:直接跳到 11 章 Octane 與 17 章的選型討論。
⚠ Disclaimer · 免責聲明

本手冊的所有指令都假設你跑在 Ubuntu 24.04 LTS 上、使用 非 Bitnami 的 OS-only blueprint。 如果你選了 Lightsail 的 Bitnami LAMP/LEMP image,路徑會完全不一樣(PHP 在 /opt/bitnami/...), 本文的指令不能照抄。Bitnami 還會在重開機時把你的 PHP-FPM 設定改回去(這是 Lightsail 圈內最有名的雷之一,下面會講)。

01
Chapter One · 認識

UnoPim 是什麼?為什麼會慢?

UnoPim 是 Webkul 在 2024 年釋出的開源 PIM(Product Information Management, 商品資訊管理系統),架構在 Laravel 12 + Vue.js 之上。它定位為 Akeneo PIM Community Edition 的替代方案。Akeneo 從 2023 年起 CE 版已停止新增功能, 預定 2026 年 9 月停止支援。

UnoPim 的技術棧長這樣

組件 版本需求 角色
PHP 8.3+ 應用層執行環境,需要 calendar / curl / intl / mbstring / openssl / pdo_mysql / tokenizer 等延伸模組
Laravel 12.x 主框架,內建 Octane / Passport / Sanctum / AI 套件
MySQL 8.0.32+ 主資料庫;商品 / attribute / family / channel / locale 都在這
Elasticsearch 8.17+ 商品搜尋引擎;大型 catalog 建議安裝
Redis 建議 7.x Cache + Session + Queue(正式環境必要)
Node.js 20 LTS+ 編譯前端資產(Vite)
Composer 2.5+ PHP 套件管理
Web Server Nginx / Apache2 本手冊全程使用 Nginx

UnoPim「預設安裝就會很慢」的五個原因

根據 Webkul 自己的文件、Packagist 上的 composer.json 以及 GitHub issue 區的記錄, UnoPim 在「composer create-project + php artisan serve」 這條最簡單的安裝路徑完成後,預設狀態為「開發環境」設定。以下五個地方的預設值在正式環境中會造成明顯的效能問題:

  1. APP_DEBUG=true 預設開啟,每個 request 都會載入完整 stack trace 與 telescope-like 偵錯資訊。
  2. CACHE_DRIVER=file / SESSION_DRIVER=file:預設使用檔案系統。Lightsail 的 SSD 雖為 NVMe,存取速度仍比 Redis 慢兩個數量級。
  3. QUEUE_CONNECTION=sync,意思是 import / export / AI agent / webhook 全部「同步阻塞」執行;用戶按下匯入按鈕後,瀏覽器會卡到天荒地老。
  4. OPcache 沒設定,每個 PHP request 都要重新 parse compile 一次(Laravel 12 啟動約 800-1200 個檔案)。
  5. 沒有 config:cache / route:cache,Laravel 每個 request 都重新解析 60+ 個 config 檔。

這五件事如果你都不做,一台 4 GB Lightsail 大概每秒只能處理 5-8 個 request。 完成調校後,同一台機器可處理 80-150 RPS,差距達 15-20 倍。本手冊逐章說明各項調校的具體步驟。

10M+
UnoPim 官方宣稱
可承載商品數
15-20×
完整調校後
吞吐量提升倍率
2GB
推薦最低 RAM
(小流量站)
$24/mo
建議的 Lightsail
正式環境月費
02
Chapter Two · 選擇

Lightsail 規格選擇指南

下表依使用情境列出 UnoPim 的 Lightsail 規格建議,整理自 Laravel 部署實務、AWS re:Post 討論及 Webkul 官方文件。規格不足時,後續調校無法有效改善效能。

用途情境 規格 月費 備註
純測試 / 給老闆看 Demo 2 vCPU · 2 GB RAM · 60 GB SSD $12 商品 < 1000、無 Elasticsearch、無 Magic AI
正式小流量站
(1-5 萬商品)
2 vCPU · 4 GB RAM · 80 GB SSD $24 本手冊預設規格;Elasticsearch 跑同一台
中等流量
(5-20 萬商品)
2 vCPU · 8 GB RAM · 160 GB SSD $44 Elasticsearch 需要至少 1.5 GB heap
高負載
(20-100 萬商品)
4 vCPU · 16 GB RAM · 320 GB SSD $84 已接近 Lightsail 天花板,可考慮拆 Elasticsearch 出去
百萬商品以上 遷移到 EC2 Lightsail 沒有 auto-scaling,CPU burst 限制會殺死你
⚠ 重要 · Lightsail Region 的選擇

如果你的用戶主要在台灣 / 香港 / 中國,選 Tokyo (ap-northeast-1)Singapore (ap-southeast-1)。建議優先東京,因為網路延遲穩定且海纜不易斷。 日本機房有時候新規格會比較晚開放,但對於 PIM 後台系統來說,延遲比規格更重要。

建立 instance 前的三項設定

① Blueprint:選「OS Only · Ubuntu 24.04 LTS」

LAMP/LEMP/Bitnami 預配 image 不適用於本手冊。Bitnami 將 PHP / MySQL / Nginx 安裝於 /opt/bitnami/,並在每次開機時以 memory.conf 覆寫 PHP-FPM 設定, 這是 AWS re:Post 上反覆記錄的問題。從 Ubuntu 24.04 OS-only image 開始,所有設定由操作者完整掌控,升級與除錯路徑較為清晰。

② Plan:正式環境建議 4 GB 起跳

Elasticsearch 8 預設需要 1 GB JVM heap、MySQL 8 至少 512 MB、 PHP-FPM 4 個 worker × 80 MB = 320 MB、Redis 256 MB、OS 本身 300 MB, 合計約 2.4 GB。npm run build 編譯前端資產時另需 1-2 GB 瞬間記憶體, 2 GB 規格的 Lightsail 在此步驟容易觸發 OOM killed。

③ Networking:設定 Static IP 與 Snapshot

Lightsail instance 重開後 public IP 會變更。在 Networking 頁籤建立 Static IP 並 attach, attach 至運行中的 instance 時不另收費。建議同時啟用每日自動 snapshot,月費增加約 $2。

定價的隱藏成本

Lightsail 月費看起來很便宜,但有些東西不在 $24/月之內:

  • Data Transfer 超量:4 GB plan 含 4 TB 流出;超過後每 GB $0.09。
  • CloudFront CDN Distribution:免費版有 50 GB/月 + 200 萬 requests,超過後另計。
  • Managed Database:若拆 MySQL 出去用 Lightsail Managed Database,最便宜 $15/月。
  • Load Balancer:$18/月,只有要做 multi-instance 才需要。
  • Block Storage:若 80 GB 不夠用,額外掛 disk 每 GB/月 $0.10。
03
Chapter Three · 部署

從 0 開始:環境安裝逐步說明

以下步驟將 UnoPim 安裝至已建立的 Lightsail instance(Ubuntu 24.04 OS-only)。完整流程約需 30-45 分鐘。每段指令前有說明,建議確認理解後再執行。

STEP 01連線到你的 instance

在 Lightsail 的 Instance 頁面找到「Connect using SSH」按鈕,會打開瀏覽器版的 terminal。 但我們建議下載 SSH key(在 Account > SSH keys 下載 default key 的 .pem 檔), 然後在你自己的電腦上連線:

# 給金鑰正確權限(macOS / Linux)
chmod 400 ~/Downloads/LightsailDefaultKey-ap-northeast-1.pem

# 連線(把 IP 換成你的 static IP)
ssh -i ~/Downloads/LightsailDefaultKey-ap-northeast-1.pem ubuntu@13.230.XXX.XXX

# Windows 用戶請用 PuTTY 並把 .pem 轉成 .ppk,
# 或用 Windows Terminal + OpenSSH(內建於 Win10/11)。

STEP 02更新系統並裝基礎工具

# 更新 package list 與已安裝套件
sudo apt update && sudo apt upgrade -y

# 裝基本工具(curl / git / unzip / 等)
sudo apt install -y curl git unzip software-properties-common \
    ca-certificates gnupg lsb-release ufw htop tmux \
    build-essential pkg-config

STEP 03安裝 PHP 8.3 與所有延伸模組

Ubuntu 24.04 預設 PHP 版本為 8.3,符合 UnoPim 的需求,不需要加 ondrej PPA。 但若你跑 22.04,請先 sudo add-apt-repository ppa:ondrej/php

sudo apt install -y \
    php8.3 php8.3-fpm php8.3-cli \
    php8.3-mysql php8.3-pgsql \
    php8.3-curl php8.3-gd php8.3-mbstring \
    php8.3-xml php8.3-zip php8.3-bcmath \
    php8.3-intl php8.3-soap php8.3-readline \
    php8.3-tokenizer php8.3-imap php8.3-opcache \
    php8.3-redis php8.3-imagick \
    php8.3-calendar

# 確認版本
php -v
# 應該看到 PHP 8.3.x

# 確認 PHP-FPM 在跑
sudo systemctl status php8.3-fpm

STEP 04安裝 Composer 2 與 Node.js 20

# Composer
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
composer --version

# Node.js 20 LTS(透過 NodeSource)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v # 應該是 v20.x
npm -v

STEP 05安裝 MySQL 8、Redis 與 Nginx

sudo apt install -y mysql-server-8.0 redis-server nginx

# 啟動並設為開機自動執行
sudo systemctl enable --now mysql redis-server nginx

# 跑 MySQL 安全設定(設 root 密碼、移除測試資料庫等)
sudo mysql_secure_installation

STEP 06建立 UnoPim 的資料庫

sudo mysql -u root -p

# 進入 MySQL prompt 後依序執行:
CREATE DATABASE unopim CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'unopim'@'localhost' IDENTIFIED BY '你的安全密碼';
GRANT ALL PRIVILEGES ON unopim.* TO 'unopim'@'localhost';
FLUSH PRIVILEGES;
EXIT;

STEP 07安裝 Elasticsearch 8(選擇性,但建議)

# 匯入 Elastic 的 GPG key
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | \
    sudo gpg --dearmor -o /usr/share/keyrings/elastic.gpg

# 加入 Elastic 8.x 套件庫
echo "deb [signed-by=/usr/share/keyrings/elastic.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | \
    sudo tee /etc/apt/sources.list.d/elastic-8.x.list

sudo apt update && sudo apt install -y elasticsearch

# 把 ES heap 限制為 1 GB(Lightsail 4GB 規格建議)
sudo tee /etc/elasticsearch/jvm.options.d/heap.options << EOF
-Xms1g
-Xmx1g
EOF

sudo systemctl enable --now elasticsearch

STEP 08下載並安裝 UnoPim

# 建立 web 根目錄
sudo mkdir -p /var/www
cd /var/www

# 用 composer 建立專案(這一步要 5-15 分鐘)
sudo composer create-project unopim/unopim --no-interaction

# 改擁有者給 www-data,這是 PHP-FPM 跟 Nginx 的執行身份
sudo chown -R www-data:www-data /var/www/unopim
sudo chmod -R 755 /var/www/unopim
sudo chmod -R 775 /var/www/unopim/storage
sudo chmod -R 775 /var/www/unopim/bootstrap/cache

cd /var/www/unopim

# 編輯 .env 設好資料庫連線
sudo nano .env

.env 裡至少要設定:

APP_NAME=UnoPim
APP_ENV=production
APP_DEBUG=false
APP_URL=https://pim.yourdomain.com

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=unopim
DB_USERNAME=unopim
DB_PASSWORD=你剛剛設的密碼

# --- 這四行是效能關鍵 ---
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
BROADCAST_DRIVER=redis

REDIS_HOST=127.0.0.1
REDIS_PORT=6379

ELASTICSEARCH_HOST=127.0.0.1
ELASTICSEARCH_PORT=9200
# 跑安裝指令(建表、塞種子資料、建立管理員帳號)
sudo -u www-data php artisan unopim:install

# 建前端資產
sudo -u www-data npm install
sudo -u www-data npm run build
✓ 第一個檢查點

到這裡,如果你執行 sudo -u www-data php artisan serve --host 0.0.0.0, 應該可以從瀏覽器用 http://你的IP:8000/admin 看到登入頁面。 此為開發伺服器模式。後續章節將接上 Nginx 並進行完整效能調校。

04
Chapter Four · 系統層

作業系統層:Swap 與 Kernel

調校 PHP / Nginx / MySQL 之前,先處理作業系統層。Lightsail 預設沒有 swap,記憶體耗盡時 OOM killer 會終止 process,MySQL 或 Elasticsearch 通常最先受影響。

建立 4 GB Swap File

Swap 比實體記憶體慢一個數量級,但可避免 OOM 直接終止 process。依 Adrian Rubico 的 Lightsail 調校建議:RAM 小於 2 GB 時,swap 建議為 RAM 的兩倍;超過 2 GB 後,額外分配 2-4 GB 已足夠。

# 看當前有沒有 swap
free -h

# 建立一個 4GB 的 swap file
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 寫入 fstab 讓重開機後自動啟用
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

# 確認
free -h

調整 swappiness

Ubuntu 預設 vm.swappiness=60,表示記憶體使用率達 40% 時即開始使用 swap。對 server 而言此值偏高,建議調低至 10,優先使用實體記憶體,僅在記憶體不足時才使用 swap。

# 即時生效
sudo sysctl vm.swappiness=10

# 寫入永久設定
sudo tee -a /etc/sysctl.conf << EOF

# --- Performance tuning for Laravel ---
vm.swappiness=10
vm.vfs_cache_pressure=50
net.core.somaxconn=4096
net.ipv4.tcp_max_syn_backlog=4096
net.ipv4.ip_local_port_range=10000 65535
fs.file-max=2097152
EOF

sudo sysctl -p

提高 file descriptor 上限

高並發環境下,Nginx + PHP-FPM 容易耗盡預設的 1024 個 file descriptor。 我們需要把它調高。

sudo tee -a /etc/security/limits.conf << EOF
www-data soft nofile 65535
www-data hard nofile 65535
root     soft nofile 65535
root     hard nofile 65535
EOF

# 同時加進 systemd 限制
sudo mkdir -p /etc/systemd/system/php8.3-fpm.service.d
sudo tee /etc/systemd/system/php8.3-fpm.service.d/limits.conf << EOF
[Service]
LimitNOFILE=65535
EOF

sudo systemctl daemon-reload
⚠ 關於 Lightsail CPU Credit

如果你發現 instance「跑一陣子就變慢」,到 Lightsail console 看「Burst capacity」這個圖表。 當 capacity 從 100% 慢慢降到 0%,CPU 就被降頻了。Lightsail 的 burst credit 是以 vCPU baseline 為基準累積的, 最便宜的 plan baseline 約 5%;當你長期超過這條線、burst credit 用完,CPU 就會降到 baseline 速度,造成 traffic 累積與 server 無回應。 解決方法只有兩個:升級規格、或減少 CPU 使用(這正是接下來要做的事)。

05
Chapter Five · PHP 設定

PHP 8.3 + OPcache 設定

OPcache 是 PHP 內建的 opcode 快取。它將 PHP 編譯後的 bytecode 存放於記憶體,後續 request 直接讀取,不需重新 parse。依官方文件,啟用 OPcache 可大幅降低每個 request 的 CPU 消耗與回應時間,是 Laravel 正式環境的基本配置。

編輯 PHP-FPM 的 php.ini

sudo nano /etc/php/8.3/fpm/php.ini

找到並修改以下參數(其中 opcache.* 區段是重點):

; ===== 基本記憶體與執行時間 =====
memory_limit = 512M
upload_max_filesize = 128M
post_max_size = 128M
max_execution_time = 300
max_input_vars = 5000

; ===== OPcache(效能關鍵) =====
opcache.enable = 1
opcache.enable_cli = 1
opcache.memory_consumption = 256
opcache.interned_strings_buffer = 32
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0      ; 正式環境設 0
opcache.save_comments = 1             ; Laravel 註解驅動的功能需要
opcache.fast_shutdown = 1
opcache.jit_buffer_size = 128M
opcache.jit = tracing

; ===== Realpath cache =====
realpath_cache_size = 4096K
realpath_cache_ttl = 600
⚠ validate_timestamps = 0 的副作用

在正式環境設定 validate_timestamps=0 可阻止 OPcache 在每個 request 檢查檔案修改時間,在高並發下可降低 I/O 消耗。 但是 當你部署新版本時,必須手動清空 OPcache,否則 PHP 會繼續用舊的 bytecode。 清除方法是重啟 PHP-FPM:sudo systemctl reload php8.3-fpm, 或者在 deployment script 末端加上 php artisan optimize:clear && php artisan optimize

同步設定 CLI 版本的 php.ini

php artisan 跑的是 CLI 版的 PHP,與 PHP-FPM 是兩份獨立的設定檔。 把同樣的調整套到 CLI:

sudo nano /etc/php/8.3/cli/php.ini
# 同樣的 memory_limit、opcache.* 都加進去

# 重啟 PHP-FPM
sudo systemctl restart php8.3-fpm

# 確認 OPcache 真的啟用了
php -i | grep "opcache.enable"
# 應該看到 opcache.enable => On => On
3-5×
啟用 OPcache 後
平均 request 加速
256MB
建議 opcache
memory consumption
20K
max_accelerated_files
(Laravel 約 9000 檔)
JIT
PHP 8 啟用後
計算密集型 +15-20%
06
Chapter Six · 進程

PHP-FPM Pool 進程調校

PHP-FPM 的 worker pool 設定決定 server 同時處理的最大 request 數。worker 數量不足會導致請求排隊;設定過多則記憶體耗盡。下表為 4 GB Lightsail 的建議值。

編輯 www.conf

sudo nano /etc/php/8.3/fpm/pool.d/www.conf
; ===== Pool 進程管理(4GB Lightsail 建議值) =====
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data

; 動態進程管理 — 對於變動流量最合適
pm = dynamic
pm.max_children = 20             ; 同時最多 20 個 worker
pm.start_servers = 4             ; 開機時起 4 個
pm.min_spare_servers = 2         ; 閒置時保留 2 個
pm.max_spare_servers = 8         ; 閒置時最多 8 個
pm.max_requests = 500           ; 處理 500 個 request 後重啟(防記憶體洩漏)

; ===== 慢請求 log =====
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log

; ===== 狀態端點(給 monitoring 用) =====
pm.status_path = /fpm-status
ping.path = /fpm-ping

那個 pm.max_children 的數字怎麼算?

這是 PHP-FPM 調校最常被問的問題。公式很簡單:

pm.max_children = (總記憶體 - 系統保留 - MySQL - Redis - ES) ÷ 單個 worker 平均記憶體

4 GB Lightsail 範例:
  總記憶體 = 4096 MB
  系統保留 = 400 MB
  MySQL 8 = 800 MB
  Redis = 256 MB
  Elasticsearch = 1024 MB
  → 給 PHP-FPM = 1616 MB
  單個 Laravel worker ≈ 80 MB
  → pm.max_children ≈ 1616 / 80 = 20

想知道你的 worker 平均吃多少記憶體?跑這條:

ps -ylC php-fpm8.3 --sort:rss | awk '!/RSS/ { s+=$8 } END { printf "%d KB total / %d procs\n", s, NR-1 }'

重啟並確認

sudo mkdir -p /var/log/php-fpm
sudo chown www-data:www-data /var/log/php-fpm

sudo php-fpm8.3 -t   # 檢查設定有沒有錯
sudo systemctl restart php8.3-fpm
sudo systemctl status php8.3-fpm
✓ 低流量站的 ondemand 模式

流量極低的站點(每分鐘數個 request),可將 pm = dynamic 改為 pm = ondemand, 並設定 pm.process_idle_timeout = 10s。無流量時 PHP-FPM 不保留常駐 worker,適合 demo 或開發環境。 正式環境建議使用 dynamic 模式,因 ondemand 的 cold start 約增加 100-200ms。

07
Chapter Seven · 反向代理

Nginx 設定優化

AWS 建議在 Lightsail 上以 Nginx/PHP-FPM 取代 Apache,因為分離的 process 模型效能較高。目標設定:Nginx 處理所有靜態檔案及 response 壓縮,PHP request 透過 Unix socket 轉交給 PHP-FPM。

編輯 nginx.conf 的全域參數

sudo nano /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
    use epoll;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    keepalive_requests 1000;
    types_hash_max_size 2048;
    server_tokens off;

    client_max_body_size 128M;
    client_body_buffer_size 256k;
    client_body_timeout 60;

    # ===== Gzip 壓縮 =====
    gzip on;
    gzip_vary on;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_types text/plain text/css text/xml application/json
               application/javascript application/xml+rss
               application/atom+xml image/svg+xml;

    # ===== FastCGI 快取 =====
    fastcgi_cache_path /var/cache/nginx levels=1:2
        keys_zone=UNOPIM_CACHE:100m max_size=1g inactive=60m
        use_temp_path=off;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

UnoPim 專屬的 Server Block

sudo nano /etc/nginx/sites-available/unopim
server {
    listen 80;
    listen [::]:80;
    server_name pim.yourdomain.com;
    root /var/www/unopim/public;
    index index.php;

    charset utf-8;

    # Laravel 標準 rewrite
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # 靜態資產:1 年快取 + immutable
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|webp|avif)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # PHP 處理
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        fastcgi_read_timeout 300;
    }

    # 鎖死敏感檔
    location ~ /\.(?!well-known).* { deny all; }
    location ~ /\.env { deny all; }

    # 日誌
    access_log /var/log/nginx/unopim_access.log;
    error_log /var/log/nginx/unopim_error.log;
}
# 啟用站點、移除預設、測試、reload
sudo ln -sf /etc/nginx/sites-available/unopim /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

裝 SSL(Let's Encrypt)

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d pim.yourdomain.com

# 自動續約已經是 default 設好的,但驗證一下
sudo certbot renew --dry-run
08
Chapter Eight · 資料庫

MySQL 8 緩衝池與索引

Lightsail 上 Laravel 效能瓶頸通常來自資料庫,而非 PHP。MySQL 8 預設 innodb_buffer_pool_size 僅 128 MB,對 PIM 規模的資料量明顯不足。

建立 UnoPim 專屬的 MySQL 設定檔

sudo nano /etc/mysql/mysql.conf.d/unopim.cnf
[mysqld]

# ===== InnoDB 緩衝池(最重要) =====
# 4GB 機器:分 1.5GB 給 InnoDB
innodb_buffer_pool_size = 1536M
innodb_buffer_pool_instances = 2
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000

# ===== 連線設定 =====
max_connections = 150
thread_cache_size = 32
wait_timeout = 600
interactive_timeout = 600

# ===== Query Cache(MySQL 8 已移除,這裡不用設) =====

# ===== 暫存檔與排序 =====
tmp_table_size = 64M
max_heap_table_size = 64M
sort_buffer_size = 2M
join_buffer_size = 2M

# ===== 慢查詢 log =====
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1

# ===== 字元編碼 =====
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
sudo mkdir -p /var/log/mysql
sudo chown mysql:mysql /var/log/mysql

sudo systemctl restart mysql
sudo systemctl status mysql

找出 UnoPim 的慢查詢

服務運行一段時間後,查詢慢查詢 log 以定位效能問題:

# 用內建 mysqldumpslow 看 top 10 慢 query
sudo mysqldumpslow -t 10 /var/log/mysql/slow.log

# 進階:用 pt-query-digest(Percona Toolkit)
sudo apt install -y percona-toolkit
sudo pt-query-digest /var/log/mysql/slow.log | less
✓ Pro Tip · 看 InnoDB 緩衝池命中率

理想的 buffer pool hit rate > 99%。低於 95% 就要考慮加 RAM 或縮減資料量:

SHOW STATUS LIKE 'Innodb_buffer_pool%';

-- 命中率公式:
-- hit_rate = (1 - reads / read_requests) * 100
09
Chapter Nine · 快取層

Redis:快取、Session 與 Queue

Cache、Session、Queue、Broadcast 四個 driver 全部切換至 Redis,可將 file system I/O 降至接近零,是 Laravel 正式環境的標準配置。

設定 Redis

sudo nano /etc/redis/redis.conf
# ===== 網路 =====
bind 127.0.0.1            # 只接受 localhost 連線
port 6379
protected-mode yes

# ===== 認證(強烈建議設密碼) =====
requirepass 你的安全 redis 密碼

# ===== 記憶體管理 =====
maxmemory 512mb
maxmemory-policy allkeys-lru
                # 當記憶體滿時,刪除最少用到的 key

# ===== 持久化(cache 不需要太頻繁存檔) =====
save 900 1
save 300 10
save 60 10000

# ===== AOF(Queue 數據用) =====
appendonly yes
appendfsync everysec

# ===== 性能 =====
tcp-keepalive 300
timeout 0
tcp-backlog 511
sudo systemctl restart redis-server

# 測試連線
redis-cli -a 你的密碼 ping
# 應該回應 PONG

更新 UnoPim 的 .env 連 Redis

CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
BROADCAST_DRIVER=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=你的密碼
REDIS_PORT=6379
REDIS_DB=0
REDIS_CACHE_DB=1
REDIS_CLIENT=phpredis
✓ phpredis 與 predis 的選擇

Laravel 預設使用 predis(純 PHP 實作),phpredis(PHP C extension)速度約快 2-3 倍。STEP 03 已安裝 php8.3-redis,請在 .env 設定 REDIS_CLIENT=phpredis 以啟用。

分離 Cache 與 Queue 的 DB

Redis 有 16 個 logical database(0-15),我們把 cache、session、queue 分開存: 這樣 php artisan cache:clear 時不會把佇列裡的 job 也炸掉。

DB Number用途.env 設定
DB 0SessionREDIS_DB=0
DB 1Cache (config / view / route)REDIS_CACHE_DB=1
DB 2Queue jobsREDIS_QUEUE_DB=2
10
Chapter Ten · Artisan

Laravel 內建優化指令

Laravel 提供 optimize 指令,一次快取 config、events、routes、views 至單一編譯檔案。依官方文件,此指令應作為每次部署流程的一部分執行。

正式環境的部署指令清單

cd /var/www/unopim

# ===== 部署前清空舊快取 =====
sudo -u www-data php artisan optimize:clear

# ===== 啟用 production mode =====
sudo -u www-data php artisan optimize

# 上面這條相當於以下個別指令:
sudo -u www-data php artisan config:cache
sudo -u www-data php artisan route:cache
sudo -u www-data php artisan view:cache
sudo -u www-data php artisan event:cache

# ===== 重啟 PHP-FPM 以套用新 bytecode =====
sudo systemctl reload php8.3-fpm

UnoPim 額外的優化指令

# Composer 的 autoloader 優化(重要!)
sudo -u www-data composer install --no-dev --optimize-autoloader

# 建立 storage symlink(首次部署需要)
sudo -u www-data php artisan storage:link

# 重新建立 Elasticsearch 索引(如果有跑 ES)
sudo -u www-data php artisan search:reindex
⚠ config:cache 之後 env() 會壞掉

如果你執行 config:cache,必須確保只在 config 檔案內呼叫 env() 函式;一旦 config 被快取,.env 檔不會再被載入,所有對 env 函式的呼叫都會回傳 null。 所以你的 controller / service 裡千萬不要寫 env('SOMETHING'), 應該寫 config('something.key'),並在 config/ 底下做 mapping。

建立一個 deploy.sh 自動化腳本

sudo nano /var/www/unopim/deploy.sh
#!/bin/bash
# /var/www/unopim/deploy.sh

set -e

cd /var/www/unopim

echo "→ 進入 maintenance mode"
sudo -u www-data php artisan down

echo "→ 拉新 code"
sudo -u www-data git pull origin main

echo "→ 更新 composer"
sudo -u www-data composer install --no-dev --optimize-autoloader

echo "→ 跑 migration"
sudo -u www-data php artisan migrate --force

echo "→ 編譯前端"
sudo -u www-data npm install
sudo -u www-data npm run build

echo "→ 重建快取"
sudo -u www-data php artisan optimize:clear
sudo -u www-data php artisan optimize

echo "→ 重啟 services"
sudo systemctl reload php8.3-fpm
sudo supervisorctl restart unopim-worker:*

echo "→ 解除 maintenance mode"
sudo -u www-data php artisan up

echo "✓ Deploy 完成"
sudo chmod +x /var/www/unopim/deploy.sh

# 之後每次部署只需跑:
sudo /var/www/unopim/deploy.sh
11
Chapter Eleven · 常駐模式

Laravel Octane 常駐模式(進階)

Laravel Octane 搭配 Swoole 或 RoadRunner,將應用程式啟動後常駐記憶體,request 之間不重啟,消除傳統 PHP 的冷啟動成本。依 Laravel 官方 benchmark,API 密集型工作負載吞吐量可提升 5-10 倍。完成前述各章調校後再評估是否啟用 Octane。

UnoPim 的 composer.json 已經包含 laravel/octane: ^2.3, 代表它原生支援 Octane。只是預設沒啟用。

Octane 的權衡

面向傳統 PHP-FPMOctane (Swoole)
每次 request 啟動重新 bootstrap Laravel(~50ms)Bootstrap 一次,永久常駐
RPS 上限50-150500-2000+
記憶體佔用較低每個 worker 持續吃 60-100 MB
記憶體洩漏風險無(每個 request 都全新環境)有;需要 watchdog 重啟
適合什麼站後台、低-中流量、複雜邏輯API、高頻 read、SSR、PWA

安裝 Swoole 並啟用 Octane

# 安裝 Swoole PHP extension
sudo apt install -y php8.3-swoole

# 確認 Swoole 已載入
php -m | grep swoole

# 在 UnoPim 啟用 Octane
cd /var/www/unopim
sudo -u www-data php artisan octane:install --server=swoole

# 啟動 Octane(測試用)
sudo -u www-data php artisan octane:start \
    --server=swoole \
    --host=127.0.0.1 \
    --port=8000 \
    --workers=4 \
    --task-workers=2 \
    --max-requests=500

讓 Nginx 把 PHP request 導到 Octane

修改你剛剛建立的 Nginx server block,把 PHP location 改成 reverse proxy:

upstream octane {
    server 127.0.0.1:8000;
}

server {
    # ... 同前 ...

    location / {
        try_files $uri $uri/ @octane;
    }

    location @octane {
        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 $scheme;
        proxy_pass http://octane;
        proxy_http_version 1.1;
        proxy_buffering off;
    }
}

用 Supervisor 守護 Octane 進程

sudo apt install -y supervisor
sudo nano /etc/supervisor/conf.d/octane.conf
[program:unopim-octane]
process_name=%(program_name)s
command=php /var/www/unopim/artisan octane:start --server=swoole --host=127.0.0.1 --port=8000 --workers=4
autostart=true
autorestart=true
user=www-data
redirect_stderr=true
stdout_logfile=/var/log/octane.log
stopwaitsecs=60
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start unopim-octane
⚠ Octane 的陷阱:Stateful Container

Octane 把 Laravel 容器 (container) 在請求之間共用,這代表: 單例服務的狀態會在所有用戶之間洩漏。 例如你在 controller 裡寫了 $this->currentUser = auth()->user();, 下一個 request 來的時候 還可能 拿到上一個用戶的資料。 升級到 Octane 前,務必通讀官方的 Octane Caveats 章節,並在每個 service provider 裡 妥善處理 reset。對 UnoPim 來說,因為它是被動使用 Octane,問題比較少, 但仍建議先在 staging 環境跑兩週才上 production。

12
Chapter Twelve · 搜尋

Elasticsearch 設定與記憶體

UnoPim 以 Elasticsearch 處理商品搜尋,這是其支援大規模 catalog 的核心機制。Elasticsearch 記憶體消耗較高,在 Lightsail 上必須明確限制 JVM heap 上限。

限制 JVM Heap Size

Elasticsearch 預設會佔用系統一半的記憶體。在 4 GB 的 Lightsail 上若不限制,會直接觸發 OOM。 我們已經在 STEP 07 把 heap 限制為 1 GB,這裡再確認一下:

cat /etc/elasticsearch/jvm.options.d/heap.options
# 應該看到:
# -Xms1g
# -Xmx1g

# Heap 與 max 必須一樣,否則 JVM 動態調整很消耗

關閉不必要的功能

sudo nano /etc/elasticsearch/elasticsearch.yml
cluster.name: unopim-cluster
node.name: unopim-node-1

# 單機 cluster:禁止它去找其他 node
discovery.type: single-node

# 只接受本機連線
network.host: 127.0.0.1
http.port: 9200

# 關閉安全模組(內部網路才這樣設!)
xpack.security.enabled: false
xpack.security.enrollment.enabled: false

# 關閉 ML(除非你要用)
xpack.ml.enabled: false

# Bootstrap memory lock(防止被 swap)
bootstrap.memory_lock: true
# 加上 systemd 的 mlockall 權限
sudo mkdir -p /etc/systemd/system/elasticsearch.service.d
sudo tee /etc/systemd/system/elasticsearch.service.d/override.conf << EOF
[Service]
LimitMEMLOCK=infinity
EOF

sudo systemctl daemon-reload
sudo systemctl restart elasticsearch

# 確認跑得起來
curl -X GET "localhost:9200/?pretty"
✓ 商品超過 100 萬時的建議

catalog 規模增大時,可將 Elasticsearch 拆至第二台 Lightsail(同 region 內網互通免費)。 UnoPim 的 .env 中將 ELASTICSEARCH_HOST 改為對應的內部 IP 即可。 Redis vector search 是另一個選項,但 UnoPim 官方目前僅支援 Elasticsearch driver, 改用 Redis 需自行修改 driver 實作。

13
Chapter Thirteen · 佇列

Queue Worker + Supervisor

UnoPim 的商品匯入、匯出、AI agent、completeness 計算與 webhook 均為非同步任務。未啟動 queue worker 時,這些功能在 UI 操作後會報錯或持續等待,不會執行。

UnoPim 官方建議的 Queue 設定

依 UnoPim 官方文件,執行 imports/exports、AI agent tasks、completeness jobs 與 webhook deliveries 均需啟動 queue worker,指令為 php artisan queue:work --queue=webhooks,system,default,completeness。webhooks queue 必須包含在內,否則 webhook event 會持續堆積而不被處理。

用 Supervisor 跑 4 個 worker

sudo nano /etc/supervisor/conf.d/unopim-worker.conf
[program:unopim-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/unopim/artisan queue:work redis \
    --queue=webhooks,system,default,completeness \
    --sleep=3 --tries=3 --max-time=3600 --max-jobs=1000

autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=4
redirect_stderr=true
stdout_logfile=/var/log/unopim-worker.log
stopwaitsecs=3600
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start unopim-worker:*

# 確認 4 個 process 都在跑
sudo supervisorctl status

為什麼 numprocs=4 而不是更多?

每個 queue worker 約吃 60-90 MB 記憶體。在 4 GB 機器上,PHP-FPM 已經 佔用約 1.6 GB。若再開 8 個 worker(720 MB),剩餘記憶體不足以支撐 OS 與其他服務。4 個 worker 對中小流量站已足夠,每秒可處理 20-30 個 job。

⚠ 別忘了 max-time

--max-time=3600 強制 worker 每小時重啟一次,這是防止記憶體洩漏的標準做法。Laravel 的 ORM 與 Event 系統在長時間運行下會累積 reference,若不定期重啟,worker 記憶體消耗可能隨時間持續增長。

14
Chapter Fourteen · 邊緣

CloudFront CDN 全站加速

Lightsail 內建 CloudFront 分發功能,透過 Amazon CloudFront 基礎設施將內容分佈至全球邊緣節點以降低延遲。PIM 管理後台本身不需要 CDN 加速,但商品圖片與前端資產透過 CDN 傳遞速度可提升數倍。

建立 Distribution

  1. 到 Lightsail Console → Networking 頁籤 → Create distribution
  2. Origin:選你的 Lightsail instance
  3. Caching behavior:選 Custom(manual configuration)
  4. 把以下 path 設為「不要快取」:
    • /admin/* — 後台不快取
    • /api/* — API 不快取
    • /login, /logout
  5. 其他 path 可以放心快取
  6. 選擇最便宜的 plan($2.5/月 含 50 GB / 200 萬 requests)
  7. 點 Create

更新 UnoPim 的 ASSET_URL

# .env 增加:
ASSET_URL=https://d12345abcde.cloudfront.net
APP_URL=https://pim.yourdomain.com

然後讓 Laravel 知道:所有 asset() helper 輸出的 URL 走 CloudFront、 但 url()(含路由)仍走自家 domain。

sudo -u www-data php artisan optimize:clear
sudo -u www-data php artisan optimize

Cloudflare 作為替代

不想被 AWS 綁,可以用 Cloudflare 的免費 plan: 把網域的 nameserver 指向 Cloudflare,DNS 設 A record 指 Lightsail 的 static IP, 勾選「Proxied」(橘色雲)即可。Cloudflare 的 Free plan 已經包含 DDoS protection、 無限頻寬、TLS、global CDN。對 PIM 這種低-中流量場景非常划算。

15
Chapter Fifteen · 觀測

監控、Log 與除錯

調校完成後,需要持續觀測以確認設定有效並及早發現問題。以下列出建議每週檢視一次的指標與工具。

Lightsail 內建 metrics

在 Instance 頁面有 5 張預設圖表:CPU utilization、Burst capacity、Network traffic、 Status check failed、Memory utilization(需安裝 CloudWatch agent)。

Burst capacity 圖表最為關鍵:若過去 24 小時曾降至 0%,表示 CPU 發生降頻。處理方式為重新檢查 PHP-FPM 與 MySQL 設定,或升級 instance 規格。

裝 htop + atop 看即時狀態

sudo apt install -y htop atop iotop

# 即時 process / CPU / 記憶體
htop

# 歷史 process(系統會背景紀錄)
atop

# 磁碟 I/O 熱點
sudo iotop -o

Laravel 自帶的健康檢查

Laravel 12 內建 /up 健康檢查端點。依官方文件,此 route 在應用程式正常啟動時回傳 200 HTTP response,可供 uptime monitor 或 load balancer 使用。建議接至 UptimeRobot(免費方案)或 BetterStack:

# 測試健康端點
curl https://pim.yourdomain.com/up
# 應該回 200 + 一個簡單 HTML

啟用 Laravel Horizon(Queue 監控)

cd /var/www/unopim
sudo -u www-data composer require laravel/horizon
sudo -u www-data php artisan horizon:install

# 修改 supervisor 跑 horizon 取代原本 queue worker
sudo nano /etc/supervisor/conf.d/horizon.conf

安裝完成後,https://pim.yourdomain.com/horizon 提供 Redis-backed queue 的即時吞吐量指標與 worker scaling 設定,適合用於監控 queue 健康狀態與流量突增時的問題排查。

排錯三步驟

症狀先查常見原因
網頁 500 error tail -f /var/www/unopim/storage/logs/laravel.log 權限錯、.env 沒設好、migration 沒跑
網頁很慢 htop 看 CPU;mysqldumpslow 慢查詢、OPcache 沒開、burst credit 用完
502 Bad Gateway sudo systemctl status php8.3-fpm PHP-FPM 掛了、socket 路徑錯、記憶體爆掉
匯入卡住 sudo supervisorctl status queue worker 沒跑、Redis 沒連到
搜尋沒結果 curl localhost:9200/_cat/indices Elasticsearch 掛了、reindex 沒做
16
Chapter Sixteen · 江湖

Reddit / 社群實戰祕技

以下整理自 r/aws、r/laravel、r/selfhosted、AWS re:Post 及多位部落客的實際部署經驗,記錄官方文件未涵蓋的常見問題與處理方式。

AWS re:Post · Laravel community ▲ 247 July 2022
我用 1 vCPU / 512MB 跟 1 vCPU / 2GB 兩台分別架 Laravel。前面 1 小時都很快, 每頁 400ms 載完,然後突然就變成 120 秒以上。CPU 使用率明明很低、記憶體只有 400MB, 但每個頁面要「boot 兩分鐘」才開始載入。
解法 這是 burst credit 耗盡的典型症狀。AWS 工程師建議改用 Nginx + PHP-FPM、記憶體不低於 1 GB、並建立 swap。根本解法為升級至至少 2 GB / 2 vCPU 規格。
r/aws · Bitnami 用戶慘案 ▲ 189 March 2024
我改了 php-fpm.d/www.conf 的 pm.max_children,重啟伺服器後設定居然被還原了。 我是不是瘋了?
Bitnami 設定被覆寫的原因 Bitnami 的 /opt/bitnami/php/etc/php-fpm.d/www.conf 結尾有一行 include=/opt/bitnami/php/etc/memory.conf。memory.conf 是 symlink, 每次開機時依 instance 規格自動重新生成,覆寫手動修改的參數。 處理方式為註解掉該行 include,或改用 OS-only image 重新安裝。
r/laravel · Octane 上線後遺症 ▲ 312 October 2025
升了 Octane 之後 RPS 從 80 飆到 600,老闆超開心。結果三天後有個用戶 A 在後台看到用戶 B 的訂單 — 我整個冷汗。
Octane Stateful Container 問題 Octane 的 Laravel container 在 request 之間常駐。任何 singleton service 或在 ServiceProvider 裡 bind 的 instance,均不會在 request 結束時自動 reset。 處理方式:將相關 service 改為 scoped(在 boot 裡使用 $this->app->scoped()), 或加入 config/octane.phpflush array 強制每次 request 重置。 PIM 或後台類系統建議先在 staging 環境運行一個月,確認無狀態洩漏後再部署至 production。
Blogger experience · 2024 ▲ 156 Adrian Rubico
我的 Lightsail 一直 CPU 飆高、network 也飆高。後來發現是 fail2ban 沒裝, scrapers 跟 SSH brute force 把我網路頻寬都吃光, 導致我「以為的 Lightsail 效能不夠」其實是被攻擊。
建議安裝 fail2ban sudo apt install fail2ban 安裝後,預設規則即可防護 SSH brute force。 Nginx 可另建 jail:在 /etc/fail2ban/jail.d/nginx.conf 中設定規則,阻擋 404 風暴、惡意爬蟲與驗證失敗嘗試。同時建議在 Lightsail firewall 關閉所有未使用的 port,僅開放 80/443/22。
r/selfhosted · PIM 選型討論 ▲ 88 2025
我們團隊評估了 Akeneo CE、Pimcore 跟 UnoPim 三家。最後選 UnoPim, 因為它的學習曲線最平。但有個坑:Magic AI / AI agent 功能是吃 token 的、要接 OpenAI key, 免費版能用但要自己付 LLM 費用。
UnoPim 授權與 AI 功能費用說明 UnoPim 本體採 MIT 授權,自架免費。「Magic AI」、「AI Agent Chat」等 LLM 功能會呼叫外部 API(OpenAI / Anthropic / Gemini 等),費用由使用者自行負擔。 不需要此功能時,可在 admin 介面關閉 AI module,或改接本地執行的 Ollama(需自行修改 driver)。
Reddit thread · CloudFront 隱形成本 ▲ 134 2025
我們用了 Lightsail + CloudFront 一個月。Lightsail 帳單是 $24, CloudFront 居然是 $63 — 後來才發現我們沒設 cache 規則,所有 request 都打回 origin。
CloudFront cache 命中率設定 建立 distribution 後需設定 cache behavior 與 TTL。PIM 場景中商品圖片更新頻率低,靜態資產 TTL 建議設 1 年(31536000 秒),HTML 設 5 分鐘至 1 小時。每月檢視一次 Cache Hit Rate,理想值 > 85%。低於 70% 時,檢查 cache key 是否包含過多 query string 參數。
AWS re:Post · 神秘的 burst ▲ 421 2024
我的 Lightsail 平常都很快,但每月特定一兩天會突然變慢,三天後又恢復 — 我以為是被駭客攻擊, 花了兩週查 log 找不到原因。
排程任務的 CPU 影響 每月對帳、重建索引、全站匯出等批次 cron job 是常見的 CPU 高峰來源。UnoPim 預設的 php artisan schedule:run 包含 completeness recalculation,商品數量多時此任務可能持續數小時。 處理方式:將此類任務改至週末凌晨執行、加 nice -n 19 降低 CPU 優先級,或移至第二台 Lightsail 執行批次作業。
Pro Sysadmin · Vincent Voyer ▲ 209 2024
Laravel + Lightsail 的真正瓶頸幾乎都不是 PHP — 是 MySQL。 innodb_buffer_pool_size 設好佔半個機器記憶體,效能會比換 Octane 還明顯。
效能改善的建議優先順序 依實務經驗,從效益明顯到實作複雜依序為:(1) MySQL innodb_buffer_pool_size (2) Redis 取代 file cache (3) OPcache (4) Nginx gzip + asset cache (5) artisan optimize (6) Octane (7) CloudFront。 完成前四項通常可獲得約 80% 的效能改善。Octane 效益顯著但需處理 stateful container 風險,建議最後評估。
17
Chapter Seventeen · 選型

UnoPim vs Akeneo vs Pimcore

下表整理自 AtroPIM 開源 PIM 比較研究、Akeneo 官方公告及 GitHub 活躍度數據,列出 UnoPim 與主要開源 PIM 的技術差異。

面向 UnoPim Akeneo CE Pimcore AtroPIM
框架 Laravel 12 (PHP) Symfony (PHP) Symfony (PHP) AtroCore (PHP)
授權 MIT OSL v3 GPL v3 GPL v3
學習曲線 簡單 中等 中等
商品上限(實測) 10M+(官方宣稱) 1M+ 10M+ 5M+
AI 內建支援 ✓ Magic AI / Agent 需付費版 需付費版 部分
DAM 整合 ✓ Unopim DAM 模組 需額外模組 ✓ 內建 需擴充
多語系
欄位級權限 需設定
長期支援 活躍開發中 2023 起停加新功能
2026/9 EOL
穩定 活躍
社群規模 (GitHub) ~10K stars ~3K stars ~12K stars ~500 stars

各產品適用情境

  • UnoPim:小到中型 B2B / B2C,1 萬到百萬商品,需要 AI 內建、團隊熟悉 Laravel。
  • Akeneo CE2026 後不建議新案使用,現存案場可繼續、新案請評估遷移。
  • Pimcore:需要 PIM + DAM + MDM + CMS 一站式、有專屬開發團隊、預算充足。
  • AtroPIM:需要 API-first、欄位級權限、複雜資料模型的製造業 / 經銷商。
18
Chapter Eighteen · 上線

部署檢查清單

公開站台前,依下列清單逐項確認。各項目均對應前述章節說明。

上線前 · Pre-Launch Checklist

A · 基礎建設
Lightsail 規格至少 2 vCPU / 4 GB RAM / 80 GB SSD
Static IP 已 attach 到 instance
Daily snapshot 已啟用
Firewall 只開 80 / 443 / 22
fail2ban 已安裝並啟動
SSH 已關閉 password 登入,只允許 key
B · 作業系統
4 GB Swap file 已建立並寫進 /etc/fstab
vm.swappiness=10 已寫進 /etc/sysctl.conf
ulimit / file descriptor 提高到 65535
時區設成 Asia/Taipei(或符合用戶所在地)
unattended-upgrades 已啟用安全更新
C · PHP 與應用層
PHP 8.3 + 所有 UnoPim 需要的 ext 模組
OPcache 啟用且 validate_timestamps=0
OPcache JIT 啟用
PHP-FPM pm.max_children 已依公式計算
composer install --no-dev --optimize-autoloader
artisan optimize 已執行
D · 資料庫與快取
MySQL innodb_buffer_pool_size 至少 1.5 GB
MySQL slow_query_log 已開啟
Redis 已設密碼、bind 127.0.0.1
Redis maxmemory + allkeys-lru policy
Elasticsearch heap 限制為 1 GB
UnoPim .env: CACHE/SESSION/QUEUE 全部設成 redis
E · Nginx 與 SSL
Nginx server_tokens off
Gzip 壓縮已啟用
靜態資產 Cache-Control: 1 year + immutable
Let's Encrypt SSL 已安裝
certbot renew --dry-run 通過
HTTP 自動 redirect 到 HTTPS
.env 與 .git 被 deny 規則擋住
F · 佇列與背景任務
Supervisor 已安裝
queue:work --queue=webhooks,system,default,completeness
numprocs 至少 2-4 個 worker
cron job 跑 schedule:run
Laravel Horizon 已安裝(建議)
G · 監控與備份
UptimeRobot / BetterStack 監控 /up 端點
每日 MySQL dump 自動備份到 S3 / 外部儲存
storage/app/public 也有定期備份
Laravel log 有 rotate(避免 1 個檔案上百 GB)
設好 admin email 告警
H · 應用層設定
APP_DEBUG=false
APP_ENV=production
已建立管理員帳號(並換掉預設密碼 admin123)
時區、語系、貨幣已在 admin 後台設定
Magic AI 不用就關掉,省 token 費用
已測試匯入、匯出、API 三個關鍵 flow
Appendix · 全景圖

完整服務架構圖

完成本手冊所有章節後,Lightsail 4 GB 機器上的服務佈局如下圖所示。所有服務透過 localhost 互通,記憶體分配標示於圖中。

                        ┌──────────────────────────┐
                        │   CloudFront / Cloudflare │
                        │   (Static asset cache)    │
                        └────────────┬─────────────┘
                                     │ HTTPS
                                     ▼
   ┌─────────────────────────────────────────────────────────────┐
   │              AWS LIGHTSAIL · ap-northeast-1                 │
   │              4 GB RAM · 2 vCPU · 80 GB SSD                  │
   │  ┌──────────────────────────────────────────────────────┐   │
   │  │  Nginx 1.24                              :80 / :443  │   │
   │  │  ─ gzip · http2 · static cache 1yr                   │   │
   │  └────────────────────┬─────────────────────────────────┘   │
   │                       │ Unix socket                         │
   │                       ▼                                     │
   │  ┌──────────────────────────────────────────────────────┐   │
   │  │  PHP-FPM 8.3 · pm=dynamic · 20 workers   ~1600 MB    │   │
   │  │  ─ OPcache 256 MB · JIT enabled                      │   │
   │  │  ─ Laravel 12 · UnoPim                               │   │
   │  └────┬──────────────┬──────────────┬───────────────────┘   │
   │       │              │              │                       │
   │       ▼              ▼              ▼                       │
   │  ┌─────────┐   ┌─────────┐   ┌──────────────┐               │
   │  │ MySQL 8 │   │ Redis 7 │   │ Elasticsearch │              │
   │  │ ~800 MB │   │ ~256 MB │   │   ~1024 MB    │              │
   │  └─────────┘   └────┬────┘   └──────────────┘               │
   │                     │                                       │
   │                     ▼                                       │
   │  ┌──────────────────────────────────────────────────────┐   │
   │  │  Supervisor                                          │   │
   │  │  ─ queue:work × 4   (webhooks,system,default,...)    │   │
   │  │  ─ schedule:run  (cron-based, 1/min)                 │   │
   │  └──────────────────────────────────────────────────────┘   │
   │                                                             │
   │  Swap: 4 GB · vm.swappiness=10                              │
   └─────────────────────────────────────────────────────────────┘
— 圖. 一台 4 GB Lightsail 完整調校後的服務佈局 —

結語

完成這 18 章後,UnoPim 在 $24/月的 Lightsail 上可承載 5-10 萬商品、每秒 80-150 個 request、p95 延遲約 200-400ms。此效能水準符合中型 PIM 正式環境需求。

所有調校設定均有時效性。Laravel 升版、UnoPim 新功能、AWS burst 政策調整,以及 MySQL 版本更新,都可能影響現有設定的有效性。建議每半年重新檢視本手冊各章節,尤其 PHP 升至 8.4 後,OPcache 與 JIT 相關參數可能需要調整。

當 Lightsail 頂規仍無法滿足需求時,可從 Lightsail 直接 export snapshot 至 EC2 AMI,再建立 EC2 + Auto Scaling Group 架構,無需從頭重新安裝。