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

229 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署(生产)
Web 由 **nginx + php-fpm**(或同等 PHP-FPM)提供;**不要**把 HTTP 交给 PM2。PM2 只跑队列、调度、Workerman。
本地安装见 [install.md](./install.md),迁旧站见 [import.md](./import.md)。
## 架构
```text
浏览器 → 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 需 `pcntl``posix`(与 FPM 不是同一 php.ini 时要两边都查) |
| 数据库 | MySQL 8 / MariaDB 10.6+(不要用 SQLite 当生产) |
| Redis | 队列 + 缓存;多站点共用时设前缀 |
| 附件 | S3 兼容(Cloudflare R2 / 腾讯 COS / 阿里 OSS / MinIO |
| 进程 | Node 的 PM2,或 systemd;配置见仓库根目录 `ecosystem.config.cjs` |
```bash
php -m | grep -E 'pcntl|posix|redis|gd'
php-fpm -m | grep -E 'redis|gd'
```
## `.env`(生产要点)
```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`):
```bash
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_files``index.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/attachments`URL `/attachments-local/...`(开发) |
| `s3` | Flysystem S3`AWS_*` 指向兼容端点 |
不要把附件长期放在 `public/` 或应用盘当生产方案。导入旧附件见 [import.md](./import.md)。
封面模板字需要中文时,设 `LARABLOG_COVER_FONT` 为服务器上的 TTF/OTF 绝对路径。
## PM2
`ecosystem.config.cjs``cwd` 是仓库根。若部署路径不是开发机路径,把该文件里的约定理解成「在项目根执行 `pm2 start ecosystem.config.cjs`」。
```bash
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.log``workerman.artisan.status*`:先停进程再删。
没有 `pcntl`/`posix` 时不要起 `larablog-ai-workerman`,可从 ecosystem 里去掉该 app,改用:
```bash
php artisan queue:work redis --queue=ai-content,ai-moderation --sleep=1 --tries=3
```
调度任务(`routes/console.php`):Redis 时每天 `cache:prune-stale-tags`;每周清理 7 天前的失败队列。
## 插件
```bash
php artisan plugins:sync
# 后台启用,或:
php artisan plugins:sync --enable=larablog/payment,larablog/membership
php artisan migrate --force
php artisan config:cache
```
付费内容依赖支付插件。Stub 支付仅打通下单,不是微信/支付宝。
## 日常更新
```bash
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
```
维护窗口:
```bash
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` |
```bash
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.yml`PHP 8.2/8.3 PHPUnitsqlite memory)。`deploy` job 只是占位,需绑定 GitHub `production` environment 并改成你的 rsync/ssh + 上文更新步骤。