Files
larablog/docs/ops/deploy.md
T
gouki 3cec4c5e18
CI / PHPUnit (PHP 8.3) (push) Failing after 4s
CI / PHPUnit (PHP 8.2) (push) Failing after 1m9s
CI / Deploy (manual gate) (push) Skipped
wip: article AI polish, category SEO fields, cover generator, membership plan seeder
2026-09-07 18:48:37 +00:00

6.7 KiB
Raw Blame History

部署(生产)

Web 由 nginx + php-fpm(或同等 PHP-FPM)提供;不要把 HTTP 交给 PM2。PM2 只跑队列、调度、Workerman。

本地安装见 install.md,迁旧站见 import.md

架构

浏览器 → nginx → php-fpm → Laravel (public/index.php)
                 ↘ /attachments-local 仅开发;生产走 S3/R2 302

PM2
  larablog-queue         queue:work 只消费 default
  larablog-schedule      schedule:work
  larablog-ai-workerman  workerman:ai 消费 ai-content / ai-moderation

queue:ai 是开发用的短生命周期 queue:work 包装,不会起 Workerman。生产用 workerman:ai 单独的 queue:work --queue=ai-content,ai-moderation,不要和 Workerman 同时抢同一批 AI Job。

服务器

建议
PHP 8.2 / 8.3 FPM,扩展同安装文档,外加 redis
Workerman CLI PHP 需 pcntlposix(与 FPM 不是同一 php.ini 时要两边都查)
数据库 MySQL 8 / MariaDB 10.6+(不要用 SQLite 当生产)
Redis 队列 + 缓存;多站点共用时设前缀
附件 S3 兼容(Cloudflare R2 / 腾讯 COS / 阿里 OSS / MinIO
进程 Node 的 PM2,或 systemd;配置见仓库根目录 ecosystem.config.cjs
php -m | grep -E 'pcntl|posix|redis|gd'
php-fpm -m | grep -E 'redis|gd'

.env(生产要点)

APP_ENV=production
APP_DEBUG=false
APP_URL=https://www.example.com
APP_LOCALE=zh_CN

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=larablog
DB_USERNAME=larablog
DB_PASSWORD=

QUEUE_CONNECTION=redis
CACHE_STORE=redis
SESSION_DRIVER=database
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PREFIX=larablog_
CACHE_PREFIX=larablog_cache_

ATTACHMENTS_DRIVER=s3
ATTACHMENTS_DISK=attachments
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=auto
AWS_BUCKET=
AWS_URL=https://cdn.example.com
AWS_ENDPOINT=https://xxx.r2.cloudflarestorage.com
AWS_USE_PATH_STYLE_ENDPOINT=true

AI_PROVIDER=openai_compatible
AI_API_BASE_URL=
AI_API_KEY=
AI_MODEL=

APP_URL 必须是对外 origin(含 https),否则附件 URL、OG、RSS 会错。
REDIS_PREFIX / CACHE_PREFIX 避免和同机其他 Laravel 抢键。

首次发布

代码放到目标目录后(示例 /var/www/larablog):

cd /var/www/larablog
composer install --no-dev --optimize-autoloader
cp .env.example .env
# 编辑 .env:生产库、Redis、S3、APP_URL、AI_*
php artisan key:generate --force

php artisan migrate --force
php artisan plugins:sync
php artisan migrate --force
php artisan themes:publish

# 新站演示数据(迁 sablog 则跳过,改走 import.md
# php artisan db:seed --force

php artisan db:seed --class=Database\\Seeders\\AiSettingsSeeder --force

php artisan config:cache
php artisan route:cache
php artisan view:cache

chmod -R ug+rwx storage bootstrap/cache
chown -R www-data:www-data storage bootstrap/cache

FPM 用户以发行版为准(www-data / nginx / php-fpm)。

然后配 nginx、起 PM2。

nginx

示例:deploy/nginx.conf。生产至少保证:

  • root 指向 .../public,不是仓库根
  • try_filesindex.php
  • PHP 走 php-fpm socket(版本与套接字路径改成你机器上的)
  • 限制隐藏文件;按需 client_max_body_size(后台传附件)

HTTPS 用发行版 certbot / 已有证书终止 TLS。HTTP 仅作跳转。

生产 ATTACHMENTS_DRIVER=s3 时,不必再配 location /attachments-local/。正文与 /attachment.php?id= 会 302 到对象存储。

附件

ATTACHMENTS_DRIVER 行为
local 文件在 storage/app/attachmentsURL /attachments-local/...(开发)
s3 Flysystem S3AWS_* 指向兼容端点

不要把附件长期放在 public/ 或应用盘当生产方案。导入旧附件见 import.md

封面模板字需要中文时,设 LARABLOG_COVER_FONT 为服务器上的 TTF/OTF 绝对路径。

PM2

ecosystem.config.cjscwd 是仓库根。若部署路径不是开发机路径,把该文件里的约定理解成「在项目根执行 pm2 start ecosystem.config.cjs」。

cd /var/www/larablog
# 如 PHP 不在 PATHPHP_BINARY=/usr/bin/php pm2 start ecosystem.config.cjs
pm2 start ecosystem.config.cjs
pm2 save
pm2 startup
pm2 status
name 命令 日志
larablog-queue queue:work redis --queue=default ... storage/logs/pm2-queue.*.log
larablog-schedule schedule:work storage/logs/pm2-schedule.*.log
larablog-ai-workerman workerman:ai start storage/logs/pm2-ai.*.log

Workerman 自己的 pid / 日志 / 状态文件在 storage/logs/workerman-ai.*,不要写到仓库根。若根目录已有 workerman.logworkerman.artisan.status*:先停进程再删。

没有 pcntl/posix 时不要起 larablog-ai-workerman,可从 ecosystem 里去掉该 app,改用:

php artisan queue:work redis --queue=ai-content,ai-moderation --sleep=1 --tries=3

调度任务(routes/console.php):Redis 时每天 cache:prune-stale-tags;每周清理 7 天前的失败队列。

插件

php artisan plugins:sync
# 后台启用,或:
php artisan plugins:sync --enable=larablog/payment,larablog/membership
php artisan migrate --force
php artisan config:cache

付费内容依赖支付插件。Stub 支付仅打通下单,不是微信/支付宝。

日常更新

cd /var/www/larablog
git pull
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan plugins:sync
php artisan themes:publish
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan filament:upgrade
pm2 reload ecosystem.config.cjs

维护窗口:

php artisan down
# …发布…
php artisan up

.env 后必须再 config:cache

日志与排障

文件 来源
storage/logs/laravel.log 应用
storage/logs/pm2-*.log PM2 stdout/err
storage/logs/workerman-ai.log Workerman
storage/logs/workerman-ai.status Workerman 状态(workerman:ai status
php artisan workerman:ai status
pm2 logs larablog-ai-workerman --lines 100

队列堆积:Redis 里看 REDIS_PREFIX 下的队列键;确认只开了一种 AI 消费者。

发布后自检

  • https://站点/、一篇 /show-{id}.shtml/rss.xml/sitemap.xml
  • /admin 可登录
  • 上传一张附件,前台 /attachment.php?id= 能跳到对象存储
  • 后台触发一次 AI 润色,对应队列被消费(stub 或真实 API)
  • APP_DEBUG=false.env 不能从 Web 读到

CI

.github/workflows/ci.ymlPHP 8.2/8.3 PHPUnitsqlite memory)。deploy job 只是占位,需绑定 GitHub production environment 并改成你的 rsync/ssh + 上文更新步骤。