Skip to content

部署指南

本指南从零到生产部署 元点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
  • 网络:公网 IP、80/443 端口开放

域名

  • 一个你拥有的主域名,例如 example.com
  • DNS 能做通配符记录(*.example.com

可选但推荐

  • Let's Encrypt 免费 SSL 证书
  • 一个 SMTP 服务(发送系统邮件用,本文未覆盖配置细节)

2. DNS 配置

在你的 DNS 服务商(阿里云 / Cloudflare / Namecheap 等)添加以下记录,把它们全部指向服务器公网 IP:

类型名称说明
A@SERVER_IP根域名 example.com → 公共前台
AwwwSERVER_IPwww.example.com → 公共前台
AadminSERVER_IPadmin.example.com → 平台超管后台
A*SERVER_IP通配符 *.example.com → 租户后台

验证 DNS 生效(替换 example.com 为你的域名):

bash
dig admin.example.com +short      # 应返回 SERVER_IP
dig demo.example.com +short       # 应返回 SERVER_IP(通配符)
dig example.com +short            # 应返回 SERVER_IP

DNS 通常需要 5-30 分钟全球生效。可以先继续后面的步骤,到最后再验证域名可达。


3. Clone 代码 + 配置 .env

bash
# 推荐 /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

bash
cp server/.env.example server/.env

编辑 server/.env必须修改的字段:

bash
# 数据库连接(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

bash
cp docker/.env.docker docker/.env.docker.local
# 或直接编辑 docker/.env.docker

修改:

bash
MYSQL_ROOT_PASSWORD=<改成强密码>
MYSQL_PASSWORD=< server/.env PASSWORD 保持一>
REDIS_PASSWORD=<可选,留空或设置强密码>
SAAS_ROOT_DOMAIN=example.com

4. 前端构建

在宿主机上构建三个前端产物,nginx 会通过 bind mount 读取它们。

bash
# 安装 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/

验证三个目录都存在且非空:

bash
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.html

5. 启动 Docker 服务

bash
cd /opt/ydadmin-saas/docker
docker compose up -d

等待健康检查(约 30 秒):

bash
docker compose ps

所有服务应为 running 且 mysql / redis 显示 healthy

查看 nginx 启动日志确认配置生成正确:

bash
docker compose logs nginx | head -20

应该看到 [entrypoint] Substituting SAAS_ROOT_PLACEHOLDER -> example.com 和生成后的 config 前 30 行。


6. 数据库迁移

bash
cd /opt/ydadmin-saas/docker
docker compose exec php php think migrate:run

应输出 All Done.,~20 个迁移全部执行。


7. 初始化种子数据

bash
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 未定义,改用:

bash
docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" yd_admin' < /opt/ydadmin-saas/server/public/install/data/init.sql

验证种子:

bash
docker compose exec mysql sh -c 'mysql -uroot -p"$MYSQL_ROOT_PASSWORD" yd_admin -e "SELECT code, name FROM plans"'

应输出 free / basic / pro 三行。


8. 创建平台超管账号

bash
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 获取证书

bash
# 安装 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,并添加:

nginx
    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):

yaml
    volumes:
      - /etc/letsencrypt:/etc/letsencrypt:ro
      # ... 其他 volumes 保留
    ports:
      - "80:80"
      - "443:443"

重启:

bash
docker compose up -d

10.3 证书自动续期

bash
# 加到 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 添加:

bash
crontab -e

添加:

txt
# 元点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

验证:

bash
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/.envJWT_TENANT_SECRETJWT_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/storageserver/pluginsserver/plugin_packages
  • 插件发布:插件安装、升级、卸载优先通过平台后台执行;发布前跑红线测试。
  • 监控:至少监控容器存活、磁盘空间、队列堆积、MySQL/Redis 可用性和支付回调错误日志。

参考

基于 Apache-2.0 协议开源