生产化部署指南
本指南说明如何将 OAuth2 全栈系统(用户前端 + 管理后台 + 后端 API)部署到生产环境。
架构概览
Internet
│
┌──────┴──────┐
│ Nginx │ :80 → :443 (TLS)
│ (反向代理) │
└──────┬──────┘
┌────────────┼────────────┐
│ │ │
┌─────┴─────┐ ┌────┴────┐ ┌────┴────┐
│ Frontend │ │ Admin │ │ Backend │
│ (Vue SPA) │ │ (Vue) │ │ (C++) │
│ :80 │ │ :80 │ │ :5555 │
└───────────┘ └─────────┘ └────┬────┘
│
┌─────────┼─────────┐
│ │
┌─────┴─────┐ ┌───────┴───────┐
│ PostgreSQL│ │ Redis │
│ :5432 │ │ :6379 │
└───────────┘ └───────────────┘
路由规则(Nginx):
/api/*,/oauth2/*,/.well-known/*,/health→ Backend/admin/*→ Admin Console/*(其他) → User Frontend
前置条件
硬件要求
- CPU: 2 核心以上
- 内存: 4GB 以上(推荐 8GB)
- 磁盘: 20GB 以上可用空间
- 网络: 公网 IP,域名已解析到服务器
操作系统支持
- Ubuntu 20.04 / 22.04 / 24.04 LTS
- Debian 11 / 12
- CentOS Stream 8 / 9
- Rocky Linux 8 / 9
软件依赖安装
1. 安装 Docker
Ubuntu/Debian:
# 更新包索引
sudo apt update
# 安装必要依赖
sudo apt install -y ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# 设置 Docker 仓库
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装 Docker Engine
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 启动 Docker 服务
sudo systemctl start docker
sudo systemctl enable docker
# 验证安装
docker --version
docker compose version
CentOS/Rocky Linux:
# 安装必要依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加 Docker 仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 安装 Docker
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 启动 Docker 服务
sudo systemctl start docker
sudo systemctl enable docker
# 验证安装
docker --version
docker compose version
2. 配置 Docker 用户组(可选但推荐)
# 创建 docker 组(如果不存在)
sudo groupadd docker
# 将当前用户添加到 docker 组
sudo usermod -aG docker $USER
# 重新登录或运行以下命令使组权限生效
newgrp docker
# 验证:无需 sudo 运行 docker
docker ps
2.5. 配置 Docker 镜像加速器(中国大陆必需)
由于 Docker Hub 在中国大陆访问不稳定,拉取镜像会超时(典型错误:dial tcp registry-1.docker.io:443: i/o timeout),必须配置镜像加速器。
以下加速器地址经实测(2026-06)在阿里云服务器上验证可用:
创建或修改 Docker 配置文件:
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null << 'EOF'
{
"registry-mirrors": [
"https://docker.1panel.live",
"https://docker.awsl9527.cn",
"https://docker.xuanyuan.me"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
说明:配置多个加速器,Docker 会按顺序尝试,任一可用即拉取成功。
重启 Docker 服务使配置生效:
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl status docker
验证镜像加速器配置:
# 检查配置是否被加载(应显示上述 registry-mirrors 列表)
docker info | grep -A 5 "Registry Mirrors"
# 测试拉取镜像(本项目需要的全部镜像)
docker pull postgres:17-alpine
docker pull redis:7-alpine
docker pull nginx:stable-alpine
docker pull prom/prometheus:latest
docker pull ubuntu:22.04
如果某个加速器报错(如 502 或 i/o timeout),Docker 会自动尝试下一个;若全部失败,参考下方故障排除。
故障排除:
-
所有加速器均失败:访问 dongyubin/DockerHub 获取最新可用列表,替换
daemon.json中的地址后重启 Docker。 -
使用阿里云专属加速器(需要阿里云账号,最稳定):
- 登录 阿里云容器镜像服务 → 镜像工具 → 镜像加速 器
- 获取专属加速地址(形如
https://<your_code>.mirror.aliyuncs.com) - 将该地址置于
daemon.json的registry-mirrors数组首位
-
使用代理拉取(如果有可用的代理服务器):
# 为 Docker 守护进程配置代理sudo mkdir -p /etc/systemd/system/docker.service.dsudo tee /etc/systemd/system/docker.service.d/http-proxy.conf > /dev/null << EOF[Service]Environment="HTTP_PROXY=http://your-proxy:port"Environment="HTTPS_PROXY=http://your-proxy:port"Environment="NO_PROXY=localhost,127.0.0.1"EOFsudo systemctl daemon-reloadsudo systemctl restart docker
3. 安装 Git
Ubuntu/Debian:
sudo apt install -y git
CentOS/Rocky Linux:
sudo yum install -y git
4. 安装 OpenSSL(用于生成密钥)
Ubuntu/Debian:
sudo apt install -y openssl
CentOS/Rocky Linux:
sudo yum install -y openssl
5. 安装 Certbot(用于获取 Let's Encrypt 证书)
Ubuntu/Debian:
sudo apt install -y certbot
CentOS/Rocky Linux:
sudo yum install -y certbot
验证依赖安装
# 检查 Docker 版本(要求 24+)
docker --version
# 检查 Docker Compose 版本(要求 v2)
docker compose version
# 检查 Git
git --version
# 检查 OpenSSL
openssl version
# 检查 Certbot
certbot --version
域名和 DNS 配置
- 域名解析:确保您的域名(如
your-domain.example.com)的 A 记录指向服务器公网 IP - DNS 传播验证:
# 检查域名是否正确解析dig +short your-domain.example.comnslookup your-domain.example.com
- 防火墙配置:确保以下端口可访问:
80/tcp(HTTP)443/tcp(HTTPS)
防火墙配置
Ubuntu (UFW):
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
CentOS/Rocky Linux (firewalld):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
快速部署(5 步)
1. 克隆项目
git clone <repo-url>
cd fulla
2. 生成密钥
# 生成 JWT 签名密钥
chmod +x scripts/generate-jwt-keys.sh
./scripts/generate-jwt-keys.sh
# 生成 TLS 证书(开发用自签名,生产用 Let's Encrypt)
chmod +x scripts/generate-certs.sh
./scripts/generate-certs.sh
生产环境使用 Let's Encrypt:
# 安装 certbot
sudo apt install certbot
# 创建 SSL 证书目录
mkdir -p deploy/nginx/ssl/
# 获取证书(先停止 nginx)
sudo certbot certonly --standalone -d your-domain.com
# 复制证书
cp /etc/letsencrypt/live/your-domain.com/fullchain.pem deploy/nginx/ssl/
cp /etc/letsencrypt/live/your-domain.com/privkey.pem deploy/nginx/ssl/
3. 配置环境变量
# 检查模板文件是否存在
[ -f deploy/env/docker.env.example ] && echo "模板文件存在" || echo "错误:模板文件不存在"
cp deploy/env/docker.env.example .env.docker
编辑 .env.docker,设置强密码与 HTTPS 域名相关配置:
# 运行模式(生产强制校验 HTTPS issuer / 强密码;须配合 FULLA_ISSUER=https://)
FULLA_ENV=production
FULLA_ISSUER=https://your-domain.com
# JWT 签名密钥(生产必填;不设则每次重启 token 失效)
FULLA_JWT_KEY_PATH=/app/keys/signing.pem
POSTGRES_USER=fulla_user
POSTGRES_PASSWORD=<生成强密码>
POSTGRES_DB=fulla_db
REDIS_PASSWORD=<生成强密码>
FULLA_DB_HOST=fulla-postgres
FULLA_DB_PORT=5432
FULLA_DB_NAME=fulla_db
FULLA_DB_USER=fulla_user
FULLA_DB_PASSWORD=<与 POSTGRES_PASSWORD 相同>
FULLA_REDIS_HOST=fulla-redis
FULLA_REDIS_PORT=6379
FULLA_REDIS_PASSWORD=<与 REDIS_PASSWORD 相同>
# CORS / OAuth 回调(HTTPS 域名必填,否则浏览器请求被拦截)
FULLA_FRONTEND_URL=https://your-domain.com
FULLA_CORS_ALLOW_ORIGINS=https://your-domain.com
FULLA_VUE_REDIRECT_URI=https://your-domain.com/callback
FULLA_VUE_CLIENT_SECRET=<生成强密码>
FULLA_GOOGLE_REDIRECT_URI=https://your-domain.com/callback
# 错误详细度(生产建议 false,不暴露字段级校验错误)
DETAILED_VALIDATION_ERRORS=false
# 邮件服务(SMTP)— 生产环境必须配置
FULLA_SMTP_HOST=smtp.example.com
FULLA_SMTP_PORT=465
FULLA_SMTP_PASSWORD=<SMTP 授权码,非邮箱登录密码>
FULLA_SMTP_FROM_NAME=OAuth2 Platform
FULLA_SMTP_SSL=true
# 前端构建变量(Vite 构建期注入)
# VITE_API_BASE_URL 生产必须留空 → SPA 走相对路径(nginx 同源反代)
VITE_API_BASE_URL=
VITE_CLIENT_ID=vue-client
VITE_REDIRECT_URI=https://your-domain.com/callback
VITE_GITHUB_CLIENT_ID=
DOMAIN=your-domain.com
重要耦合:
FULLA_ENV=production与FULLA_ISSUER=https://...必须同时设置。仅设 production 而不配 HTTPS issuer 会导致后端启动校验失败(ConfigManager的 prod-mode 校验拒绝非 https issuer)。同理 DB/Redis 密码不能是默认的123456/password,否则 prod 校验也会拒绝启动。
生成强密码:
openssl rand -base64 32
邮件服务(SMTP)配置说明
后端邮件服务有两种模式(由 getEmailService() 根据环境变量自动选择):
| 模式 | 触发条件 | 行为 |
|---|---|---|
| Console 模式 | 未设置 FULLA_SMTP_HOST / USER / PASSWORD | 邮件内容只输出到后端日志,不真正发送 |
| SMTP 模式 | 上述三个变量均已设置且非空 | 通过 SMTP 真正发送邮件 |
生产环境必须配置 SMTP,否则邮箱验证、密码重置等功能的邮件不会真正发送给用户(只在服务器日志里)。
常见邮箱服务商配置参考:
| 服务商 | SMTP 主机 | 端口 | SSL | 凭据说明 |
|---|---|---|---|---|
| 163 邮箱 | smtp.163.com | 465 | true | 授权码(非登录密码) |
| QQ 邮箱 | smtp.qq.com | 465 | true | 授权码 |
| Gmail | smtp.gmail.com | 465 | true | 应用专用密码(需开两步验证) |
| 腾讯企业邮 | smtp.exmail.qq.com | 465 | true | 邮箱密码 |
| 阿里云企业邮 | smtp.qiye.aliyun.com | 465 | true | 邮箱密码 |
| SendGrid | smtp.sendgrid.net | 587 | false | 用户名 apikey,密码为 API Key |
获取授权码(以 163 为例):
- 登录 163 邮箱网页版
- 设置 → POP3/SMTP/IMAP
- 开启 SMTP 服务
- 按提示生成授权码(16 位字符串)
配置完成后重启后端生效:
docker compose -f deploy/docker/docker-compose.prod.yml --env-file .env.docker up -d fulla-backend
# 验证已切换到 SMTP 模式(应输出 "Email service: SMTP (...)")
docker compose -f deploy/docker/docker-compose.prod.yml logs fulla-backend | grep "Email service"
4. 启动服务
docker compose -f deploy/docker/docker-compose.prod.yml --env-file .env.docker up -d
5. 验证部署
# 检查所有容器状态
docker compose -f deploy/docker/docker-compose.prod.yml ps
# 检查后端健康
curl -k https://localhost/health
# 检查前端
curl -k https://localhost/
# 检查管理后台
curl -k https://localhost/admin/
服务详情
用户前端 (OAuth2Frontend)
| 项目 | 值 |
|---|---|
| 容器名 | fulla-frontend |
| 构建 | Dockerfile (target: frontend-runtime) |
| 基础镜像 | nginx:stable-alpine |
| 内部端口 | 80 |
| 访问路径 | https://your-domain.com/ |
| 功能 | 登录、注册、个人资料、安全设置、OAuth2 授权 |
管理后台 (OAuth2Admin)
| 项目 | 值 |
|---|---|
| 容器名 | fulla-admin |
| 构建 | frontends/admin/Dockerfile |
| 基础镜像 | nginx:alpine |
| 内部端口 | 80 |
| 访问路径 | https://your-domain.com/admin/ |
| 功能 | 应用管理、用户管理、角色/Scope/Token 管理 |
后端 API (fulla-server)
| 项目 | 值 |
|---|---|
| 容器名 | fulla-backend |
| 构建 | Dockerfile (target: backend-runtime) |
| 基础镜像 | ubuntu:22.04 (minimal) |
| 内部端口 | 5555 |
| 访问路径 | https://your-domain.com/api/*, /oauth2/* |
| 数据库迁移 | 启动时自动执行(FULLA_AUTO_MIGRATE=true) |
基础设施
| 服务 | 镜像 | 用途 |
|---|---|---|
| fulla-postgres | postgres:17-alpine | 主数据库 |
| fulla-redis | redis:7-alpine | Token 缓存 |
| oauth2-nginx | nginx:stable-alpine | TLS 终止 + 反向代理 |
| fulla-prometheus | prom/prometheus | 监控指标采集 |
配置说明
后端配置 (config.prod.json)
后端通过环境变量覆盖配置文件中的值(优先级:.env 文件 > 系统环境变量 > config.prod.json 默认值):
| 环境变量 | 用途 | 默认值 |
|---|---|---|
FULLA_ENV | 运行模式(production 启用 HTTPS issuer + 强密码严格校验) | development |
FULLA_ISSUER | JWT issuer(生产必须 https://) | http://localhost:5555 |
FULLA_JWT_KEY_PATH | JWT 签名密钥文件路径 | /app/keys/signing.pem |
FULLA_SIGNING_KEY | JWT 密钥 PEM 内容(与 JWT_KEY_PATH 二选一) | (可选) |
FULLA_DB_HOST | PostgreSQL 主机 | postgres |
FULLA_DB_PORT | PostgreSQL 端口 | 5432 |
FULLA_DB_NAME | 数据库名 | fulla_db_prod |
FULLA_DB_USER | 数据库用户 | fulla_user |
FULLA_DB_PASSWORD | 数据库密码 | (必须设置) |
FULLA_REDIS_HOST | Redis 主机 | redis |
FULLA_REDIS_PORT | Redis 端口 | 6379 |
FULLA_REDIS_PASSWORD | Redis 密码 | (必须设置) |
FULLA_LISTEN_PORT | 后端监听端口 | 5555 |
FULLA_FRONTEND_URL | 前端 URL(用于重定向等) | http://localhost:5173 |
FULLA_CORS_ALLOW_ORIGINS | CORS 允许的源(逗号分隔,覆盖 JSON 数组) | config 中的 localhost 列表 |
FULLA_VUE_REDIRECT_URI | vue-client OAuth 回调 URI | config 中的 localhost 值 |
FULLA_GOOGLE_REDIRECT_URI | Google OAuth 回调 URI | config 中的 localhost 值 |
FULLA_VUE_CLIENT_SECRET | vue-client 密钥 | 123456 |
FULLA_AUTO_MIGRATE | 自动执行数据库迁移 | true |
DETAILED_VALIDATION_ERRORS | 是否返回字段级校验错误(生产建议 false) | false |
FULLA_GITHUB_CLIENT_ID / FULLA_GITHUB_CLIENT_SECRET | GitHub OAuth(可选) | (空) |
FULLA_GOOGLE_CLIENT_ID / FULLA_GOOGLE_CLIENT_SECRET | Google OAuth(可选) | (空) |
FULLA_SMTP_HOST | SMTP 服务器主机(未设置则邮件走 Console 模式) | (可选) |
FULLA_SMTP_PORT | SMTP 端口 | 465 |
FULLA_SMTP_USER | SMTP 用户名(完整邮箱地址) | (可选) |
FULLA_SMTP_PASSWORD | SMTP 授权码(非邮箱登录密码) | (可选) |
FULLA_SMTP_FROM_NAME | 发件人显示名称 | OAuth2 Platform |
FULLA_SMTP_SSL | 是否启用 SSL | true |
邮件模式说明:仅当
FULLA_SMTP_HOST+FULLA_SMTP_USER+FULLA_SMTP_PASSWORD三项均非空时启用真实 SMTP 发送;否则邮件只输出到后端日志。详见上文"邮件服务(SMTP)配置说明"。CORS 数组覆盖:
FULLA_CORS_ALLOW_ORIGINS是逗号分隔的字符串(如https://a.com,https://b.com),后端启动时自动分割成 JSON 数组覆盖config.prod.json的custom_config.cors.allow_origins。CORS 校验代码要求该字段是数组,因此必须