# 生产部署(Docker / 腾讯云 CVM) 单机 Docker Compose 方案:`Nginx + API + PostgreSQL + MinIO`,适合腾讯云轻量/ CVM。 ## 架构 ``` Internet ──:80/443──▶ 边缘网关(Caddy 推荐 / 或 CLB) │ ▼ web(nginx) :HTTP_PORT(默认 18080 → 容器 80) ├── / 前端 SPA ├── /api/* → api:8080(含 WebSocket) ├── /health → api └── /files/* → minio:9000/kefu(图片公共读) ``` **两层分工(不要混为一谈):** | 层级 | 组件 | 职责 | |------|------|------| | 边缘 | 宿主机 Caddy(或 CLB) | 占 80/443、多域名、自动 HTTPS | | 应用内 | compose 里的 `web`(Nginx) | SPA + `/api` + `/files` 路径反代 | 若机器上已有 `caddy` 占用 80/443(例如旧站 `kefu_sys`),**不要**让本项目 `HTTP_PORT=80`,应改用 `18080` 并由 Caddy 反代。 ## 服务器准备(腾讯云) 1. 购买 **CVM / 轻量应用服务器**(建议 2 核 4G 起,系统 Ubuntu 22.04) 2. **安全组**放行:`22`、`80`(有证书再放 `443`)。应用内端口(如 `18080`)**不必**对公网开放 3. 安装 Docker: ```bash # Ubuntu 示例 curl -fsSL https://get.docker.com | sh sudo usermod -aG docker "$USER" # 重新登录后 docker 无需 sudo docker compose version ``` 4. **(强烈建议)Docker 镜像加速** —— 加速拉 `golang` / `node` / `nginx` / `postgres` 等基础镜像。 腾讯云 CVM 可写(其它云可换成对应镜像地址): ```bash sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<'EOF' { "registry-mirrors": [ "https://mirror.ccs.tencentyun.com" ] } EOF sudo systemctl daemon-reload sudo systemctl restart docker docker info | grep -A5 'Registry Mirrors' ``` 说明:Dockerfile 内已默认使用 **腾讯云 Alpine 源、goproxy.cn、npmmirror**,加速构建阶段的 `apk` / `go mod` / `npm ci`。 基础镜像本身仍依赖上面的 Docker Hub 镜像加速。 5. 上传代码到服务器,例如: ```bash git clone <你的仓库地址> kefu_cloud cd kefu_cloud ``` 6. 部署前检查端口占用: ```bash sudo ss -tlnp | grep -E ':80|:443|:18080' docker ps --format 'table {{.Names}}\t{{.Ports}}' ``` ## 一键部署 ```bash chmod +x deploy/scripts/*.sh # 首次会生成 deploy/.env.prod 并提示你改域名/公网地址 ./deploy/scripts/deploy.sh # 需要演示账号时(kefu_admin / kefu_admin123 等) ./deploy/scripts/deploy.sh --seed ``` ### 必改配置(`deploy/.env.prod`) | 变量 | 示例 | 说明 | |------|------|------| | `APP_BASE_URL` | `https://kefu.xx.com` | **浏览器**访问根地址(经 Caddy 时写 https 域名,不要写 `:18080`) | | `STORAGE_PUBLIC_BASE_URL` | `https://kefu.xx.com/files` | **必须**与应用内 Nginx `/files` 一致 | | `HTTP_PORT` | `18080` | 宿主机映射端口;已有 Caddy 占 80 时用非 80 端口 | | `DB_PASSWORD` / `JWT_SECRET` / `STORAGE_*` | 强随机 | 脚本可自动生成密钥 | 脚本会校验占位符是否改掉,并在 `HTTP_PORT` 已被占用时告警/退出。 ### 常用命令 ```bash ./deploy/scripts/deploy.sh --status # 状态 ./deploy/scripts/deploy.sh --down # 停容器(保留卷) ./deploy/scripts/deploy.sh --seed-only # 仅跑种子 ./deploy/scripts/backup-db.sh # 备份数据库到 ./backups docker compose -f docker-compose.prod.yml --env-file deploy/.env.prod logs -f api docker compose -f docker-compose.prod.yml --env-file deploy/.env.prod logs -f web ``` ## 与现有 Caddy 共存(同机已有 kefu_sys 等) 典型现状: ```text caddy 0.0.0.0:80→80, 0.0.0.0:443→443 kefu_sys 仅容器内 8080(由 Caddy 反代) ``` 推荐拓扑: ```text Internet └── Caddy :80/:443 ├── 旧域名 → kefu_sys └── 新域名 → 127.0.0.1:18080 → kefu-cloud-web(Nginx) ``` ### 步骤 1. `.env.prod` 设置(示例): ```bash HTTP_PORT=18080 APP_BASE_URL=https://kefu.example.com STORAGE_PUBLIC_BASE_URL=https://kefu.example.com/files ``` 2. 部署本项目:`./deploy/scripts/deploy.sh`(先确认本机 `curl http://127.0.0.1:18080/healthz` 返回 ok) 3. 将 `deploy/caddy/Caddyfile.example` 中的站点段合并进现有 Caddy 配置,把域名改成真实域名后 reload: ```bash # 示例(按你实际 Caddy 部署方式调整) # docker exec caddy caddy validate --config /etc/caddy/Caddyfile # docker exec caddy caddy reload --config /etc/caddy/Caddyfile ``` 4. 浏览器访问 `https://kefu.example.com`,登录页 `/login`,健康检查 `/health`。 ### 为何边缘用 Caddy、应用内仍用 Nginx - **Caddy**:你已在用、自动 HTTPS、多站点配置简单,继续当 80/443 入口。 - **Nginx(web 容器)**:SPA、`/api` WebSocket、`/files`→MinIO 已写好,无需换成 Caddy。 - **不要**再在宿主机起一个 Nginx 抢 80/443,也**不要**让本 compose 默认绑 80。 ## HTTPS 其它方式 任选其一(与上一节「Caddy 共存」二选一即可): 1. **CLB / CDN** 终结 HTTPS,回源 CVM `HTTP_PORT`(或仍回源 Caddy) 2. 本机 **Caddy** 反代到 `127.0.0.1:${HTTP_PORT}`(推荐,见上) 3. 独占机器时 `HTTP_PORT=80`,另用 Nginx + Let’s Encrypt 做 443 配置 HTTPS 后,请同步把 `APP_BASE_URL`、`STORAGE_PUBLIC_BASE_URL` 改成 `https://…`。 ## 使用腾讯云 COS 替代 MinIO(可选) 1. 创建 COS 桶,开启公共读或使用 CDN 域名 2. 在 `.env.prod` 配置 S3 兼容参数(见 `.env.prod.example` 注释) 3. 从 `docker-compose.prod.yml` 中去掉 `minio` / `minio-init` 依赖,并修改 Nginx,去掉 `/files` 反代(图片直链 COS) 当前一键脚本默认 **内置 MinIO**,零额外云产品即可跑通。 ## 默认种子账号(仅 --seed 后) | 角色 | 用户名 | 密码 | |------|--------|------| | 平台管理员 | `platform_admin` | `kefu_admin123` | | 租户管理员 | `kefu_admin` | `kefu_admin123` | | 坐席 | `agent1` 等 | `kefu_admin123` | **上线后立即修改密码或关闭 seed 账号。** ## 目录说明 | 路径 | 说明 | |------|------| | `docker-compose.prod.yml` | 生产编排 | | `deploy/docker/Dockerfile.api` | Go API 镜像 | | `deploy/docker/Dockerfile.web` | 前端构建 + Nginx | | `deploy/docker/nginx.conf` | 反代与 SPA | | `deploy/caddy/Caddyfile.example` | 边缘 Caddy 站点示例(与 80 共存) | | `deploy/.env.prod.example` | 环境变量模板 | | `deploy/scripts/deploy.sh` | 一键部署 | | `deploy/scripts/backup-db.sh` | 数据库备份 | ## 故障排查 | 现象 | 处理 | |------|------| | 端口被占用 / bind 失败 | `ss -tlnp` 查占用;改 `HTTP_PORT` 或停冲突容器 | | 页面 502 | `docker compose … logs api`,检查 DB 是否 healthy;Caddy 是否指到正确端口 | | 图片打不开 | 检查 `STORAGE_PUBLIC_BASE_URL` 是否为 `…/files`,MinIO 桶是否 public download | | WebSocket 断 | 确认走同源 `/api/ws`;Caddy 用示例中的 `flush_interval`;CLB 需开启 WebSocket | | 构建 Go 失败 | 服务器内存不足时加 swap,或本机 build 后 `docker save` 上传 | | 只能本机 18080 访问、域名不通 | Caddy 未 reload / 域名 DNS 未指向本机 / 安全组未放 80/443 | ## 升级 ```bash git pull ./deploy/scripts/backup-db.sh # 升级前建议备份 ./deploy/scripts/deploy.sh # 重新 build 并滚动启动 ```