UnoPim(Laravel 12)部署於 AWS Lightsail 的完整效能調校指南, 涵蓋作業系統、PHP-FPM、Nginx、MySQL、Redis、Octane 至 CloudFront 七個層級的參數設定與實戰說明。
本手冊說明 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。
本手冊的所有指令都假設你跑在 Ubuntu 24.04 LTS 上、使用 非 Bitnami 的 OS-only blueprint。 如果你選了 Lightsail 的 Bitnami LAMP/LEMP image,路徑會完全不一樣(PHP 在 /opt/bitnami/...), 本文的指令不能照抄。Bitnami 還會在重開機時把你的 PHP-FPM 設定改回去(這是 Lightsail 圈內最有名的雷之一,下面會講)。
UnoPim 是 Webkul 在 2024 年釋出的開源 PIM(Product Information Management, 商品資訊管理系統),架構在 Laravel 12 + Vue.js 之上。它定位為 Akeneo PIM Community Edition 的替代方案。Akeneo 從 2023 年起 CE 版已停止新增功能, 預定 2026 年 9 月停止支援。
| 組件 | 版本需求 | 角色 |
|---|---|---|
| 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 |
根據 Webkul 自己的文件、Packagist 上的 composer.json 以及 GitHub issue 區的記錄,
UnoPim 在「composer create-project + php artisan serve」
這條最簡單的安裝路徑完成後,預設狀態為「開發環境」設定。以下五個地方的預設值在正式環境中會造成明顯的效能問題:
這五件事如果你都不做,一台 4 GB Lightsail 大概每秒只能處理 5-8 個 request。 完成調校後,同一台機器可處理 80-150 RPS,差距達 15-20 倍。本手冊逐章說明各項調校的具體步驟。
下表依使用情境列出 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 限制會殺死你 |
如果你的用戶主要在台灣 / 香港 / 中國,選 Tokyo (ap-northeast-1) 或 Singapore (ap-southeast-1)。建議優先東京,因為網路延遲穩定且海纜不易斷。 日本機房有時候新規格會比較晚開放,但對於 PIM 後台系統來說,延遲比規格更重要。
LAMP/LEMP/Bitnami 預配 image 不適用於本手冊。Bitnami 將 PHP / MySQL / Nginx 安裝於
/opt/bitnami/,並在每次開機時以 memory.conf 覆寫 PHP-FPM 設定,
這是 AWS re:Post 上反覆記錄的問題。從 Ubuntu 24.04 OS-only image 開始,所有設定由操作者完整掌控,升級與除錯路徑較為清晰。
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。
Lightsail instance 重開後 public IP 會變更。在 Networking 頁籤建立 Static IP 並 attach, attach 至運行中的 instance 時不另收費。建議同時啟用每日自動 snapshot,月費增加約 $2。
Lightsail 月費看起來很便宜,但有些東西不在 $24/月之內:
以下步驟將 UnoPim 安裝至已建立的 Lightsail instance(Ubuntu 24.04 OS-only)。完整流程約需 30-45 分鐘。每段指令前有說明,建議確認理解後再執行。
在 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)。
# 更新 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
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
# 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
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
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;
# 匯入 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
# 建立 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 並進行完整效能調校。
調校 PHP / Nginx / MySQL 之前,先處理作業系統層。Lightsail 預設沒有 swap,記憶體耗盡時 OOM killer 會終止 process,MySQL 或 Elasticsearch 通常最先受影響。
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
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
高並發環境下,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
如果你發現 instance「跑一陣子就變慢」,到 Lightsail console 看「Burst capacity」這個圖表。 當 capacity 從 100% 慢慢降到 0%,CPU 就被降頻了。Lightsail 的 burst credit 是以 vCPU baseline 為基準累積的, 最便宜的 plan baseline 約 5%;當你長期超過這條線、burst credit 用完,CPU 就會降到 baseline 速度,造成 traffic 累積與 server 無回應。 解決方法只有兩個:升級規格、或減少 CPU 使用(這正是接下來要做的事)。
OPcache 是 PHP 內建的 opcode 快取。它將 PHP 編譯後的 bytecode 存放於記憶體,後續 request 直接讀取,不需重新 parse。依官方文件,啟用 OPcache 可大幅降低每個 request 的 CPU 消耗與回應時間,是 Laravel 正式環境的基本配置。
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 可阻止 OPcache 在每個 request 檢查檔案修改時間,在高並發下可降低 I/O 消耗。
但是 當你部署新版本時,必須手動清空 OPcache,否則 PHP 會繼續用舊的 bytecode。
清除方法是重啟 PHP-FPM:sudo systemctl reload php8.3-fpm,
或者在 deployment script 末端加上 php artisan optimize:clear && php artisan optimize。
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
PHP-FPM 的 worker pool 設定決定 server 同時處理的最大 request 數。worker 數量不足會導致請求排隊;設定過多則記憶體耗盡。下表為 4 GB Lightsail 的建議值。
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
這是 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
流量極低的站點(每分鐘數個 request),可將 pm = dynamic 改為 pm = ondemand,
並設定 pm.process_idle_timeout = 10s。無流量時 PHP-FPM 不保留常駐 worker,適合 demo 或開發環境。
正式環境建議使用 dynamic 模式,因 ondemand 的 cold start 約增加 100-200ms。
AWS 建議在 Lightsail 上以 Nginx/PHP-FPM 取代 Apache,因為分離的 process 模型效能較高。目標設定:Nginx 處理所有靜態檔案及 response 壓縮,PHP request 透過 Unix socket 轉交給 PHP-FPM。
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/*; }
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
sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d pim.yourdomain.com # 自動續約已經是 default 設好的,但驗證一下 sudo certbot renew --dry-run
Lightsail 上 Laravel 效能瓶頸通常來自資料庫,而非 PHP。MySQL 8 預設 innodb_buffer_pool_size 僅 128 MB,對 PIM 規模的資料量明顯不足。
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
服務運行一段時間後,查詢慢查詢 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
理想的 buffer pool hit rate > 99%。低於 95% 就要考慮加 RAM 或縮減資料量:
SHOW STATUS LIKE 'Innodb_buffer_pool%'; -- 命中率公式: -- hit_rate = (1 - reads / read_requests) * 100
將 Cache、Session、Queue、Broadcast 四個 driver 全部切換至 Redis,可將 file system I/O 降至接近零,是 Laravel 正式環境的標準配置。
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
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
Laravel 預設使用 predis(純 PHP 實作),phpredis(PHP C extension)速度約快 2-3 倍。STEP 03 已安裝 php8.3-redis,請在 .env 設定 REDIS_CLIENT=phpredis 以啟用。
Redis 有 16 個 logical database(0-15),我們把 cache、session、queue 分開存:
這樣 php artisan cache:clear 時不會把佇列裡的 job 也炸掉。
| DB Number | 用途 | .env 設定 |
|---|---|---|
| DB 0 | Session | REDIS_DB=0 |
| DB 1 | Cache (config / view / route) | REDIS_CACHE_DB=1 |
| DB 2 | Queue jobs | REDIS_QUEUE_DB=2 |
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
# 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,必須確保只在 config 檔案內呼叫 env() 函式;一旦 config 被快取,.env 檔不會再被載入,所有對 env 函式的呼叫都會回傳 null。
所以你的 controller / service 裡千萬不要寫 env('SOMETHING'),
應該寫 config('something.key'),並在 config/ 底下做 mapping。
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
Laravel Octane 搭配 Swoole 或 RoadRunner,將應用程式啟動後常駐記憶體,request 之間不重啟,消除傳統 PHP 的冷啟動成本。依 Laravel 官方 benchmark,API 密集型工作負載吞吐量可提升 5-10 倍。完成前述各章調校後再評估是否啟用 Octane。
UnoPim 的 composer.json 已經包含 laravel/octane: ^2.3,
代表它原生支援 Octane。只是預設沒啟用。
| 面向 | 傳統 PHP-FPM | Octane (Swoole) |
|---|---|---|
| 每次 request 啟動 | 重新 bootstrap Laravel(~50ms) | Bootstrap 一次,永久常駐 |
| RPS 上限 | 50-150 | 500-2000+ |
| 記憶體佔用 | 較低 | 每個 worker 持續吃 60-100 MB |
| 記憶體洩漏風險 | 無(每個 request 都全新環境) | 有;需要 watchdog 重啟 |
| 適合什麼站 | 後台、低-中流量、複雜邏輯 | API、高頻 read、SSR、PWA |
# 安裝 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 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; } }
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 把 Laravel 容器 (container) 在請求之間共用,這代表:
單例服務的狀態會在所有用戶之間洩漏。
例如你在 controller 裡寫了 $this->currentUser = auth()->user();,
下一個 request 來的時候 還可能 拿到上一個用戶的資料。
升級到 Octane 前,務必通讀官方的 Octane Caveats 章節,並在每個 service provider 裡
妥善處理 reset。對 UnoPim 來說,因為它是被動使用 Octane,問題比較少,
但仍建議先在 staging 環境跑兩週才上 production。
UnoPim 以 Elasticsearch 處理商品搜尋,這是其支援大規模 catalog 的核心機制。Elasticsearch 記憶體消耗較高,在 Lightsail 上必須明確限制 JVM heap 上限。
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"
catalog 規模增大時,可將 Elasticsearch 拆至第二台 Lightsail(同 region 內網互通免費)。
UnoPim 的 .env 中將 ELASTICSEARCH_HOST 改為對應的內部 IP 即可。
Redis vector search 是另一個選項,但 UnoPim 官方目前僅支援 Elasticsearch driver,
改用 Redis 需自行修改 driver 實作。
UnoPim 的商品匯入、匯出、AI agent、completeness 計算與 webhook 均為非同步任務。未啟動 queue worker 時,這些功能在 UI 操作後會報錯或持續等待,不會執行。
依 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 會持續堆積而不被處理。
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
每個 queue worker 約吃 60-90 MB 記憶體。在 4 GB 機器上,PHP-FPM 已經 佔用約 1.6 GB。若再開 8 個 worker(720 MB),剩餘記憶體不足以支撐 OS 與其他服務。4 個 worker 對中小流量站已足夠,每秒可處理 20-30 個 job。
--max-time=3600 強制 worker 每小時重啟一次,這是防止記憶體洩漏的標準做法。Laravel 的 ORM 與 Event 系統在長時間運行下會累積 reference,若不定期重啟,worker 記憶體消耗可能隨時間持續增長。
Lightsail 內建 CloudFront 分發功能,透過 Amazon CloudFront 基礎設施將內容分佈至全球邊緣節點以降低延遲。PIM 管理後台本身不需要 CDN 加速,但商品圖片與前端資產透過 CDN 傳遞速度可提升數倍。
/admin/* — 後台不快取/api/* — API 不快取/login, /logout# .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
不想被 AWS 綁,可以用 Cloudflare 的免費 plan: 把網域的 nameserver 指向 Cloudflare,DNS 設 A record 指 Lightsail 的 static IP, 勾選「Proxied」(橘色雲)即可。Cloudflare 的 Free plan 已經包含 DDoS protection、 無限頻寬、TLS、global CDN。對 PIM 這種低-中流量場景非常划算。
調校完成後,需要持續觀測以確認設定有效並及早發現問題。以下列出建議每週檢視一次的指標與工具。
在 Instance 頁面有 5 張預設圖表:CPU utilization、Burst capacity、Network traffic、 Status check failed、Memory utilization(需安裝 CloudWatch agent)。
Burst capacity 圖表最為關鍵:若過去 24 小時曾降至 0%,表示 CPU 發生降頻。處理方式為重新檢查 PHP-FPM 與 MySQL 設定,或升級 instance 規格。
sudo apt install -y htop atop iotop # 即時 process / CPU / 記憶體 htop # 歷史 process(系統會背景紀錄) atop # 磁碟 I/O 熱點 sudo iotop -o
Laravel 12 內建 /up 健康檢查端點。依官方文件,此 route 在應用程式正常啟動時回傳 200 HTTP response,可供 uptime monitor 或 load balancer 使用。建議接至 UptimeRobot(免費方案)或 BetterStack:
# 測試健康端點 curl https://pim.yourdomain.com/up # 應該回 200 + 一個簡單 HTML
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 沒做 |
以下整理自 r/aws、r/laravel、r/selfhosted、AWS re:Post 及多位部落客的實際部署經驗,記錄官方文件未涵蓋的常見問題與處理方式。
/opt/bitnami/php/etc/php-fpm.d/www.conf 結尾有一行
include=/opt/bitnami/php/etc/memory.conf。memory.conf 是 symlink,
每次開機時依 instance 規格自動重新生成,覆寫手動修改的參數。
處理方式為註解掉該行 include,或改用 OS-only image 重新安裝。
$this->app->scoped()),
或加入 config/octane.php 的 flush array 強制每次 request 重置。
PIM 或後台類系統建議先在 staging 環境運行一個月,確認無狀態洩漏後再部署至 production。
sudo apt install fail2ban 安裝後,預設規則即可防護 SSH brute force。
Nginx 可另建 jail:在 /etc/fail2ban/jail.d/nginx.conf 中設定規則,阻擋 404 風暴、惡意爬蟲與驗證失敗嘗試。同時建議在 Lightsail firewall 關閉所有未使用的 port,僅開放 80/443/22。
php artisan schedule:run 包含 completeness recalculation,商品數量多時此任務可能持續數小時。
處理方式:將此類任務改至週末凌晨執行、加 nice -n 19 降低 CPU 優先級,或移至第二台 Lightsail 執行批次作業。
下表整理自 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 |
公開站台前,依下列清單逐項確認。各項目均對應前述章節說明。
完成本手冊所有章節後,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 │
└─────────────────────────────────────────────────────────────┘
完成這 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 架構,無需從頭重新安裝。