部署指南
本指南从零到生产部署 元点Saas 到一台 Linux 服务器。预计耗时 30-60 分钟(不含 DNS 生效等待)。
1. 前置条件
服务器
- Linux 发行版:Ubuntu 22.04+ / Debian 11+ / CentOS 8+ / Rocky Linux 9+
- 配置:最低 2 核 4GB RAM + 40GB SSD(支持 50 租户;100+ 租户建议 4 核 8GB)
- 软件:
- Docker 24+ (
curl -fsSL https://get.docker.com | sh) - Docker Compose v2+(已包含在 Docker 24 中,
docker compose version) - git
- Docker 24+ (
- 网络:公网 IP、80/443 端口开放
域名
- 一个你拥有的主域名,例如
example.com - DNS 能做通配符记录(
*.example.com)
可选但推荐
- Let's Encrypt 免费 SSL 证书
- 一个 SMTP 服务(发送系统邮件用,本文未覆盖配置细节)
2. DNS 配置
在你的 DNS 服务商(阿里云 / Cloudflare / Namecheap 等)添加以下记录,把它们全部指向服务器公网 IP:
| 类型 | 名称 | 值 | 说明 |
|---|---|---|---|
| A | @ | SERVER_IP | 根域名 example.com → 公共前台 |
| A | www | SERVER_IP | www.example.com → 公共前台 |
| A | admin | SERVER_IP | admin.example.com → 平台超管后台 |
| A | * | SERVER_IP | 通配符 *.example.com → 租户后台 |
验证 DNS 生效(替换 example.com 为你的域名):
dig admin.example.com +short # 应返回 SERVER_IP
dig demo.example.com +short # 应返回 SERVER_IP(通配符)
dig example.com +short # 应返回 SERVER_IPDNS 通常需要 5-30 分钟全球生效。可以先继续后面的步骤,到最后再验证域名可达。
3. Clone 代码 + 配置 .env
# 推荐 /opt 或 /srv 作为部署目录
sudo mkdir -p /opt/ydadmin-saas
sudo chown $USER:$USER /opt/ydadmin-saas
cd /opt/ydadmin-saas
# Clone(任选一个镜像)
git clone --depth=1 https://github.com/yuandianxitong/ydadmin-saas.git .
# 或:git clone --depth=1 https://gitee.com/yuandianxitong/ydadmin-saas.git .3.1 server/.env
cp server/.env.example server/.env编辑 server/.env,必须修改的字段:
# 数据库连接(docker 内的 mysql 服务)
HOSTNAME = mysql
HOSTPORT = 3306
DATABASE = yd_admin
USERNAME = ydadmin
PASSWORD = <改成强密码>
# Redis(docker 内的 redis 服务)
CACHE_HOST = redis
CACHE_PORT = 6379
CACHE_PASSWORD =
# 两套独立的 JWT 密钥(必须 32 字节以上随机串)
# 生成命令:openssl rand -base64 48
JWT_TENANT_SECRET = <openssl rand -base64 48 输出>
JWT_PLATFORM_SECRET = <openssl rand -base64 48 输出,必须和上面不同>
JWT_TENANT_ISSUER = ydadmin-saas-tenant
JWT_PLATFORM_ISSUER = ydadmin-saas-platform
# SaaS 域名配置
SAAS_ROOT_DOMAIN = example.com
SAAS_PLATFORM_DOMAIN = admin.example.com
SAAS_GRACE_DAYS = 7
# 支付回调 base URL(部署 HTTPS 后改成 https://)
SAAS_PAY_NOTIFY_BASE_URL = http://admin.example.com
# SaaS 级支付(生产环境启用,开发可留空)
SAAS_PAY_WECHAT_ENABLED = false
SAAS_PAY_ALIPAY_ENABLED = false⚠️ .env 注释里绝对不要写半角括号 ( ) —— ThinkPHP 的 env 解析器有已知 bug 会崩溃。用 、 CJK 逗号或其他符号替代。
3.2 docker/.env.docker
cp docker/.env.docker docker/.env.docker.local
# 或直接编辑 docker/.env.docker修改:
MYSQL_ROOT_PASSWORD=<改成强密码>
MYSQL_PASSWORD=<和 server/.env 的 PASSWORD 保持一致>
REDIS_PASSWORD=<可选,留空或设置强密码>
SAAS_ROOT_DOMAIN=example.com4. 前端构建
在宿主机上构建三个前端产物,nginx 会通过 bind mount 读取它们。
# 安装 Node.js 18+(如未安装)
# curl -fsSL https://deb.nodesource.com/setup_20.x | sudo bash -
# sudo apt-get install -y nodejs
npm install -g pnpm
# 构建 platform
cd /opt/ydadmin-saas/platform
pnpm install
pnpm build
# 产物在 platform/dist/
# 构建 tenant
cd /opt/ydadmin-saas/tenant
pnpm install
pnpm build
# 产物在 tenant/dist/
# 构建 pc
cd /opt/ydadmin-saas/pc
pnpm install
pnpm build
# Nuxt 3 产物在 pc/.output/public/验证三个目录都存在且非空:
ls -la /opt/ydadmin-saas/platform/dist/index.html
ls -la /opt/ydadmin-saas/tenant/dist/index.html
ls -la /opt/ydadmin-saas/pc/.output/public/index.html5. 启动 Docker 服务
cd /opt/ydadmin-saas/docker
docker compose up -d等待健康检查(约 30 秒):
docker compose ps所有服务应为 running 且 mysql / redis 显示 healthy。
查看 nginx 启动日志确认配置生成正确:
docker compose logs nginx | head -20应该看到 [entrypoint] Substituting SAAS_ROOT_PLACEHOLDER -> example.com 和生成后的 config 前 30 行。
6. 数据库迁移
cd /opt/ydadmin-saas/docker
docker compose exec php php think migrate:run应输出 All Done.,~20 个迁移全部执行。
7. 初始化种子数据
docker compose exec -T mysql mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" yd_admin < /opt/ydadmin-saas/server/public/install/data/init.sql⚠️ 如果 -T 选项不被支持或 $MYSQL_ROOT_PASSWORD 未定义,改用:
docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" yd_admin' < /opt/ydadmin-saas/server/public/install/data/init.sql验证种子:
docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" yd_admin -e "SELECT code, name FROM plans"'应输出 free / basic / pro 三行。
8. 创建平台超管账号
cd /opt/ydadmin-saas/docker
docker compose exec php php think saas:create-platform-admin admin@example.com 'YourStrongPassword!'应输出 平台超管创建成功。
9. 访问平台后台 + 创建首个租户
打开浏览器访问:
http://admin.example.com/用上一步创建的账号登录。左侧菜单 → 租户管理 → 新建租户:
- 租户 Code:
demo(将作为子域名,访问demo.example.com) - 租户名称:Demo Company
- 套餐:basic
- 试用天数:30
创建成功后,可以访问:
http://demo.example.com/用租户自动生成的初始管理员账号登录(创建租户时会显示一次)。
10. HTTPS 配置(强烈推荐生产环境启用)
10.1 使用 Certbot 获取证书
# 安装 Certbot
sudo apt-get install certbot python3-certbot-nginx
# 先停止 docker nginx 释放 80 端口
cd /opt/ydadmin-saas/docker
docker compose stop nginx
# standalone 模式获取证书(包含通配符需要 DNS 验证)
sudo certbot certonly --standalone \
-d example.com \
-d www.example.com \
-d admin.example.com
# 通配符证书需要 DNS-01 验证(阿里云 / Cloudflare / DNSPod 有相应插件)
sudo certbot certonly --manual --preferred-challenges=dns \
-d '*.example.com'证书文件位于 /etc/letsencrypt/live/example.com/。
10.2 更新 Nginx 配置添加 443 server block
编辑 docker/nginx/default.conf,把每个 listen 80; 的 server block 复制一份改成 443,并添加:
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 可选:80 → 443 强制跳转
if ($scheme = http) {
return 301 https://$host$request_uri;
}把 Let's Encrypt 证书目录挂进 nginx 容器(编辑 docker-compose.yml 的 nginx volumes):
volumes:
- /etc/letsencrypt:/etc/letsencrypt:ro
# ... 其他 volumes 保留
ports:
- "80:80"
- "443:443"重启:
docker compose up -d10.3 证书自动续期
# 加到 crontab
(crontab -l; echo "0 3 * * * certbot renew --quiet --deploy-hook 'cd /opt/ydadmin-saas/docker && docker compose restart nginx'") | crontab -11. Cron 任务配置
元点Saas 至少依赖两个 cron 任务持续运行。在宿主机 crontab 添加:
crontab -e添加:
# 元点Saas 订阅生命周期扫描(每小时)
# 处理 grace / frozen 状态转移 + 每日用量快照
0 * * * * cd /opt/ydadmin-saas/docker && docker compose exec -T php php think saas:tenant-lifecycle >> /var/log/ydadmin-saas-lifecycle.log 2>&1
# 元点Saas 过期订单清理(每 5 分钟)
# 取消超过 expired_at + 60s 的待支付订单
*/5 * * * * cd /opt/ydadmin-saas/docker && docker compose exec -T php php think saas:order-cleanup >> /var/log/ydadmin-saas-order-cleanup.log 2>&1验证:
crontab -l
tail -f /var/log/ydadmin-saas-lifecycle.log # 1 小时后应看到输出插件菜单校准、移动端构建清理、插件迁移回填等低频维护命令见 运维命令。
12. 常见问题排查
12.1 子域名访问 404 / 无响应
- 检查 DNS 是否生效:
dig demo.example.com +short - 检查 nginx 配置生成正确:
docker compose logs nginx | grep server_name - 检查 server_name 匹配:容器里
docker compose exec nginx cat /etc/nginx/conf.d/default.conf确认SAAS_ROOT_PLACEHOLDER已被替换 - 检查浏览器 DNS 缓存:换一个设备或清缓存再试
12.2 502 Bad Gateway
- php 容器没起来:
docker compose ps | grep php - php 配置错误:
docker compose logs php | tail -30 - nginx 配置错误:
docker compose exec nginx nginx -t
12.3 登录后 401 Unauthorized
- JWT 密钥没配置:检查
server/.env中JWT_TENANT_SECRET和JWT_PLATFORM_SECRET都填了 32 字节以上的随机串 - 两套密钥配成同一个:必须不同,否则跨 scope 的 token 会意外生效
12.4 支付回调不工作
- SAAS_PAY_NOTIFY_BASE_URL 配错:必须是外网可达的 URL 前缀
- 签名校验失败:查看
docker compose logs php | grep notify - 订单金额不一致:M3B 的整数分比较会拒绝 1 分钱以上的差异
12.5 跨租户看到别人数据
- 如果出现这种情况立即停服检查!M1-M3 的 31 个红线测试专门防御这种场景
- 运行红线测试:
docker compose exec php ./vendor/bin/phpunit tests/RedLine - 检查
TenantContextMiddleware是否正确挂载到 tenantapi 应用 - 检查可疑 Repository 是否绕过了
$this->query()基类方法
12.6 配额超限无法上传
- 查看租户当前用量:
docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" yd_admin -e "SELECT id, name, storage_used_bytes, storage_limit_bytes FROM tenants"' - 手动扩容:
UPDATE tenants SET storage_limit_bytes = 10737418240 WHERE id = X; - 或者让租户升级套餐(platform 后台 → 套餐管理)
13. 生产建议
- HTTPS:生产环境必须启用 HTTPS,支付回调地址同步改为
https://admin.example.com。 - 队列守护:使用 Supervisor 或 systemd 常驻
php think queue:work --queue default --tries=3。 - 日志轮转:为 PHP、Nginx、队列和 cron 日志配置 logrotate。
- 备份:定期备份 MySQL、
server/public/storage、server/plugins、server/plugin_packages。 - 插件发布:插件安装、升级、卸载优先通过平台后台执行;发布前跑红线测试。
- 监控:至少监控容器存活、磁盘空间、队列堆积、MySQL/Redis 可用性和支付回调错误日志。
