feat: 双环境自动化部署(生产 + 测试)

- 新增 docker-compose.prod.yml / docker-compose.test.yml 双环境配置
- 新增 docker-compose.gateway.yml Nginx 网关按域名路由
- 新增 Dockerfile.website 官网镜像构建
- 新增 gateway.conf 支持 8 个域名路由
- 更新 .gitlab-ci.yml 支持 prod/test 双分支自动部署
- 更新 Makefile 新增 prod-/test-/gateway- 快捷命令
- 新增 scripts/setup-server.sh 服务器一键初始化脚本
- 修复 Nginx 配置服务名不匹配问题
- 修复前端 Dockerfile 缺少 packages/ 导致构建失败
- 删除旧的 docker-compose.yml

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 19:12:32 +08:00
parent 0bbbe20ec7
commit bcf16fe53d
10 changed files with 786 additions and 169 deletions
+47 -7
View File
@@ -6,12 +6,13 @@ stages:
variables:
NODE_ENV: production
# prod 分支触发
# prod 和 test 分支触发
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: never
- if: $CI_COMMIT_BRANCH == "prod"
- if: $CI_COMMIT_BRANCH == "test"
# pnpm store 缓存
cache:
@@ -86,6 +87,20 @@ build_platform:
- apps/platform-admin/dist/
expire_in: 1 hour
build_website:
stage: build
tags:
- rent-deploy
needs: [install_dependencies]
script:
- npm install -g pnpm
- pnpm config set store-dir .pnpm-store
- pnpm --filter @rent/official-website build
artifacts:
paths:
- apps/official-website/out/
expire_in: 1 hour
# ==================== DEPLOY ====================
deploy_production:
stage: deploy
@@ -95,17 +110,42 @@ deploy_production:
- build_server
- build_merchant
- build_platform
- build_website
script:
- echo "部署到生产环境..."
- cd deploy/docker
- docker-compose down --remove-orphans
- docker-compose build --parallel
- docker-compose up -d
- docker-compose -f docker-compose.prod.yml down --remove-orphans
- docker-compose -f docker-compose.prod.yml build --parallel
- docker-compose -f docker-compose.prod.yml up -d
- docker image prune -f
- echo "等待服务启动..."
- sleep 10
- docker-compose ps
- docker-compose -f docker-compose.prod.yml ps
environment:
name: production
only:
- prod
rules:
- if: $CI_COMMIT_BRANCH == "prod"
deploy_test:
stage: deploy
tags:
- rent-deploy
needs:
- build_server
- build_merchant
- build_platform
- build_website
script:
- echo "部署到测试环境..."
- cd deploy/docker
- docker-compose -f docker-compose.test.yml down --remove-orphans
- docker-compose -f docker-compose.test.yml build --parallel
- docker-compose -f docker-compose.test.yml up -d
- docker image prune -f
- echo "等待服务启动..."
- sleep 10
- docker-compose -f docker-compose.test.yml ps
environment:
name: test
rules:
- if: $CI_COMMIT_BRANCH == "test"
+80 -23
View File
@@ -1,43 +1,100 @@
.PHONY: help install build up down restart logs ps deploy clean
.PHONY: help install \
prod-build prod-up prod-down prod-restart prod-logs prod-logs-server prod-ps prod-deploy \
test-build test-up test-down test-restart test-logs test-logs-server test-ps test-deploy \
gateway-up gateway-down gateway-reload \
init deploy clean
DOCKER_COMPOSE = docker-compose -f deploy/docker/docker-compose.yml
DOCKER_PROD = docker-compose -f deploy/docker/docker-compose.prod.yml
DOCKER_TEST = docker-compose -f deploy/docker/docker-compose.test.yml
DOCKER_GATEWAY = docker-compose -f deploy/docker/docker-compose.gateway.yml
help: ## 显示帮助信息
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}'
install: ## 安装依赖
npm install -g pnpm
pnpm install
build: ## 构建所有 Docker 镜像
$(DOCKER_COMPOSE) build --parallel
# ===== 生产环境 =====
prod-build: ## 构建生产环境镜像
$(DOCKER_PROD) build --parallel
up: ## 启动所有服务
$(DOCKER_COMPOSE) up -d
prod-up: ## 启动生产环境
$(DOCKER_PROD) up -d
down: ## 停止所有服务
$(DOCKER_COMPOSE) down
prod-down: ## 停止生产环境
$(DOCKER_PROD) down
restart: ## 重启所有服务
$(DOCKER_COMPOSE) restart
prod-restart: ## 重启生产环境
$(DOCKER_PROD) restart
logs: ## 查看所有服务日志
$(DOCKER_COMPOSE) logs -f --tail=100
prod-logs: ## 查看生产环境日志
$(DOCKER_PROD) logs -f --tail=100
logs-server: ## 查看后端服务日志
$(DOCKER_COMPOSE) logs -f --tail=100 server
prod-logs-server: ## 查看生产环境后端日志
$(DOCKER_PROD) logs -f --tail=100 server
ps: ## 查看运行中的容器
$(DOCKER_COMPOSE) ps
prod-ps: ## 查看生产环境容器
$(DOCKER_PROD) ps
deploy: down build up ## 完整部署(停止 → 构建 → 启动)
prod-deploy: prod-down prod-build prod-up ## 完整部署生产环境
@echo ""
@echo "部署完成!"
@echo " 商家后台: http://localhost:8081"
@echo " 平台后台: http://localhost:8082"
@echo " API 服务: http://localhost:3000"
@echo "生产环境部署完成!"
@echo " API: http://api.pinzhuhui.com"
@echo " 官网: http://www.pinzhuhui.com"
@echo " 商家后台: http://merchant.pinzhuhui.com"
@echo " 平台后台: http://platform-admin.pinzhuhui.com"
# ===== 测试环境 =====
test-build: ## 构建测试环境镜像
$(DOCKER_TEST) build --parallel
test-up: ## 启动测试环境
$(DOCKER_TEST) up -d
test-down: ## 停止测试环境
$(DOCKER_TEST) down
test-restart: ## 重启测试环境
$(DOCKER_TEST) restart
test-logs: ## 查看测试环境日志
$(DOCKER_TEST) logs -f --tail=100
test-logs-server: ## 查看测试环境后端日志
$(DOCKER_TEST) logs -f --tail=100 server
test-ps: ## 查看测试环境容器
$(DOCKER_TEST) ps
test-deploy: test-down test-build test-up ## 完整部署测试环境
@echo ""
@echo "测试环境部署完成!"
@echo " API: http://api-test.pinzhuhui.com"
@echo " 官网: http://test.pinzhuhui.com"
@echo " 商家后台: http://merchant-test.pinzhuhui.com"
@echo " 平台后台: http://platform-admin-test.pinzhuhui.com"
# ===== 网关 =====
gateway-up: ## 启动网关
$(DOCKER_GATEWAY) up -d
gateway-down: ## 停止网关
$(DOCKER_GATEWAY) down
gateway-reload: ## 重载网关配置
docker exec rent-gateway nginx -s reload
# ===== 快捷命令 =====
init: gateway-up ## 首次初始化(启动网关 + 创建网络)
@echo "网络已创建,网关已启动。"
@echo "接下来运行 make prod-deploy 或 make test-deploy"
deploy: prod-deploy ## 别名:部署生产环境
clean: ## 清理所有容器、数据卷和悬空镜像
$(DOCKER_COMPOSE) down -v
$(DOCKER_PROD) down -v
$(DOCKER_TEST) down -v
$(DOCKER_GATEWAY) down
docker image prune -f
+175 -119
View File
@@ -1,102 +1,139 @@
# 部署指南
## 架构概览
```
Internet → Gateway Nginx (80/443)
├─ rent-prod 网络
│ ├─ rent-prod-mysql (3306)
│ ├─ rent-prod-redis (6379)
│ ├─ rent-prod-server (3000)
│ ├─ rent-prod-merchant (8081→80)
│ ├─ rent-prod-platform (8082→80)
│ └─ rent-prod-website (8083→80)
└─ rent-test 网络
├─ rent-test-mysql (3307→3306)
├─ rent-test-redis (6380→6379)
├─ rent-test-server (3001→3000)
├─ rent-test-merchant (9081→80)
├─ rent-test-platform (9082→80)
└─ rent-test-website (9083→80)
```
## 域名规划
| 环境 | 用途 | 域名 |
|------|------|------|
| 生产 | API | api.pinzhuhui.com |
| 生产 | 官网 | www.pinzhuhui.com |
| 生产 | 商家后台 | merchant.pinzhuhui.com |
| 生产 | 平台后台 | platform-admin.pinzhuhui.com |
| 测试 | API | api-test.pinzhuhui.com |
| 测试 | 官网 | test.pinzhuhui.com |
| 测试 | 商家后台 | merchant-test.pinzhuhui.com |
| 测试 | 平台后台 | platform-admin-test.pinzhuhui.com |
## 目录结构
```
```text
deploy/
├── docker/
│ ├── docker-compose.yml # Docker Compose 编排文件
│ ├── Dockerfile.server # 后端服务镜像
│ ├── Dockerfile.merchant # 商家后台镜像
│ ├── Dockerfile.platform # 平台后台镜像
── .env.example # 环境变量模板
└── nginx/
├── api.conf # API 反向代理配置
├── merchant.conf # 商家后台 Nginx 配置
└── platform.conf # 平台后台 Nginx 配置
```
## 服务端口
| 服务 | 端口 | 说明 |
|------|------|------|
| MySQL | 3306 | 数据库 |
| Redis | 6379 | 缓存 |
| Server | 3000 | 后端 API |
| Merchant Admin | 8081 | 商家管理后台 |
| Platform Admin | 8082 | 平台管理后台 |
---
## 一、快速部署(手动)
### 1. 配置环境变量
```bash
cd deploy/docker
cp .env.example .env
# 编辑 .env,填入真实的密码和密钥
vi .env
```
### 2. 构建并启动
```bash
# 方式一:使用 Makefile(推荐)
make deploy
# 方式二:直接使用 docker-compose
docker-compose -f deploy/docker/docker-compose.yml up -d --build
```
### 3. 常用命令
```bash
make help # 查看所有命令
make logs # 查看日志
make logs-server # 查看后端日志
make ps # 查看容器状态
make restart # 重启服务
make down # 停止服务
make clean # 清理所有数据(包括数据库)
│ ├── docker-compose.prod.yml # 生产环境
│ ├── docker-compose.test.yml # 测试环境
│ ├── docker-compose.gateway.yml # Gateway 网关
│ ├── Dockerfile.server # 后端镜像
── Dockerfile.merchant # 商家后台镜像
│ ├── Dockerfile.platform # 平台后台镜像
├── Dockerfile.website # 官网镜像
├── .env.example # 环境变量模板
└── .env # 实际配置(不入仓库)
├── nginx/
│ ├── gateway/gateway.conf # Gateway 域名路由
│ ├── api.conf # 后端 Nginx 配置
│ ├── merchant.conf # 商家后台 Nginx 配置
│ └── platform.conf # 平台后台 Nginx 配置
scripts/
│ └── setup-server.sh # 首次初始化脚本
```
---
## 二、GitLab CI/CD 自动化部署
## 一、首次部署(服务器初始化)
### 第 1 步:运行初始化脚本
```bash
# 将代码克隆到服务器
git clone <你的GitLab仓库地址> ~/rent-platform
cd ~/rent-platform
# 运行初始化(安装 Docker、创建网络、启动网关)
bash scripts/setup-server.sh
```
脚本会自动完成:
- 安装 Docker、Docker Compose、Git、pnpm
- 创建 `.env` 文件并提示你填写密码
- 创建 Docker 网络(rent-prod、rent-test
- 启动 Gateway Nginx
### 第 2 步:配置 DNS
在你的域名管理面板添加 A 记录,将以下域名指向服务器 IP:
```text
# 生产环境
api.pinzhuhui.com → 服务器IP
www.pinzhuhui.com → 服务器IP
merchant.pinzhuhui.com → 服务器IP
platform-admin.pinzhuhui.com → 服务器IP
# 测试环境
api-test.pinzhuhui.com → 服务器IP
test.pinzhuhui.com → 服务器IP
merchant-test.pinzhuhui.com → 服务器IP
platform-admin-test.pinzhuhui.com → 服务器IP
```
### 第 3 步:部署服务
```bash
# 部署生产环境
make prod-deploy
# 部署测试环境
make test-deploy
```
---
## 二、GitLab CI/CD 自动化
### 前提条件
- 服务器已安装 Docker 和 Docker Compose
- 服务器已部署 GitLab 实例
- 项目代码已推送到 GitLab 仓库
- 服务器已运行初始化脚本
- 代码已推送到 GitLab 仓库
### 第 1 步:安装 GitLab Runner
```bash
# 添加 GitLab Runner 仓库
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
# 安装
sudo apt install gitlab-runner
```
### 第 2 步:注册 Runner
1. GitLab 中打开项目**Settings → CI/CD → Runners**
2. 复制 registration token
3. 在服务器上执行注册:
1. GitLab → **Settings → CI/CD → Runners** → 复制 token
2. 在服务器执行:
```bash
sudo gitlab-runner register
```
按提示填写:
| 项目 | 值 |
|------|------|
| GitLab URL | `http://你的GitLab域名/` |
| Token | 从 Settings 页面复制的 token |
| GitLab URL | `http://你的GitLab地址/` |
| Token | 从页面复制 |
| Description | `rent-deploy-runner` |
| Tags | `rent-deploy` |
| Executor | `shell` |
@@ -104,91 +141,110 @@ sudo gitlab-runner register
### 第 3 步:配置 Runner 权限
```bash
# 让 gitlab-runner 用户可以执行 docker 命令
sudo usermod -aG docker gitlab-runner
# 全局安装 pnpm
sudo -u gitlab-runner npm install -g pnpm
# 验证
sudo -u gitlab-runner docker ps
sudo -u gitlab-runner pnpm --version
```
### 第 4 步:配置 CI/CD 变量
GitLab 中打开 **Settings → CI/CD → Variables**,添加
GitLab **Settings → CI/CD → Variables**
| 变量名 | 说明 | 勾选 Masked |
|--------|------|-------------|
| `PROD_DB_PASSWORD` | MySQL root 密码 | ✅ |
| `PROD_JWT_SECRET` | JWT 签名密钥 | ✅ |
| 变量名 | 说明 | Masked |
|--------|------|--------|
| `PROD_DB_PASSWORD` | 生产数据库密码 | ✅ |
| `PROD_JWT_SECRET` | 生产 JWT 密钥 | ✅ |
| `TEST_DB_PASSWORD` | 测试数据库密码 | ✅ |
| `TEST_JWT_SECRET` | 测试 JWT 密钥 | ✅ |
### 第 5 步:创建环境变量文件
### 第 5 步:推送代码触发部署
```bash
# 在服务器项目目录下创建 .env(只需一次)
cd /path/to/repo/deploy/docker
cp .env.example .env
vi .env
# 填入 PROD_DB_PASSWORD 和 PROD_JWT_SECRET 的真实值
```
# 推送 test 分支 → 自动部署测试环境
git push origin test
### 第 6 步:推送代码触发部署
```bash
# 推送 prod 分支 → 自动部署生产环境
git push origin prod
```
推送后 GitLab 会自动运行流水线:
1. **install** 阶段 — 安装依赖
2. **build** 阶段 — 并行构建 server / merchant / platform
3. **deploy** 阶段 — 停止旧服务 → 构建镜像 → 启动新服务
可以在 GitLab → **CI/CD → Pipelines** 页面查看实时日志。
流水线:install → build(并行构建 4 个应用)→ deploy
---
## 三、故障排查
## 三、常用命令
```bash
make help # 查看所有命令
# 生产环境
make prod-deploy # 完整部署(停止→构建→启动)
make prod-logs # 查看日志
make prod-logs-server # 查看后端日志
make prod-ps # 查看容器状态
make prod-restart # 重启
# 测试环境
make test-deploy # 完整部署
make test-logs # 查看日志
make test-ps # 查看容器状态
make test-restart # 重启
# 网关
make gateway-up # 启动网关
make gateway-reload # 重载网关配置
# 其他
make clean # 清理所有环境(包括数据)
```
---
## 四、故障排查
### 查看服务状态
```bash
make ps
make prod-ps # 生产环境
make test-ps # 测试环境
```
### 查看日志
```bash
# 所有服务
make logs
make prod-logs # 生产环境所有日志
make prod-logs-server # 生产环境后端日志
make test-logs # 测试环境所有日志
# 仅后端
make logs-server
# 特定容器
docker logs rent-mysql
docker logs rent-redis
docker logs rent-server
# 直接查看某个容器
docker logs rent-prod-server
docker logs rent-test-mysql
```
### 数据库连接失败
### 数据库连接
```bash
# 检查 MySQL 是否就绪
docker exec rent-mysql mysqladmin ping -h localhost -u root -p
# 生产环境
docker exec -it rent-prod-mysql mysql -u root -p rent_platform
# 进入 MySQL 手动检查
docker exec -it rent-mysql mysql -u root -p rent_platform
# 测试环境
docker exec -it rent-test-mysql mysql -u root -p rent_platform
```
### 重建个服务
### 重建个服务
```bash
# 只重建后端
docker-compose -f deploy/docker/docker-compose.yml up -d --build server
# 只重建生产环境后端
docker-compose -f deploy/docker/docker-compose.prod.yml up -d --build server
# 只重建商家后台
docker-compose -f deploy/docker/docker-compose.yml up -d --build merchant-admin
# 只重建测试环境商家后台
docker-compose -f deploy/docker/docker-compose.test.yml up -d --build merchant-admin
```
### 网关问题
```bash
# 检查网关状态
docker logs rent-gateway
# 重载网关配置(修改了 gateway.conf 后)
make gateway-reload
```
+6 -8
View File
@@ -1,9 +1,7 @@
# ===== 数据库 =====
DB_PASSWORD=change_me_in_production
# ===== 生产环境 =====
PROD_DB_PASSWORD=change_me_in_production
PROD_JWT_SECRET=change_me_in_production
# ===== JWT =====
JWT_SECRET=change_me_in_production
# ===== 服务配置 =====
NODE_ENV=production
PORT=3000
# ===== 测试环境 =====
TEST_DB_PASSWORD=change_me_in_test
TEST_JWT_SECRET=change_me_in_test
+29
View File
@@ -0,0 +1,29 @@
# 官网 - Next.js 静态导出
FROM node:18-alpine AS website-builder
WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY packages/ ./packages/
COPY apps/official-website/ ./apps/official-website/
RUN npm install -g pnpm && pnpm install --frozen-lockfile
RUN cd apps/official-website && pnpm run build
FROM nginx:alpine AS website
COPY --from=website-builder /app/apps/official-website/out /usr/share/nginx/html
RUN echo 'server { \
listen 80; \
server_name _; \
root /usr/share/nginx/html; \
index index.html; \
location / { \
try_files $uri $uri.html $uri/ =404; \
} \
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { \
expires 30d; \
add_header Cache-Control "public, immutable"; \
} \
gzip on; \
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript; \
gzip_min_length 1024; \
}' > /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+21
View File
@@ -0,0 +1,21 @@
version: '3.8'
services:
gateway:
image: nginx:alpine
container_name: rent-gateway
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ../nginx/gateway/gateway.conf:/etc/nginx/nginx.conf:ro
networks:
- rent-prod
- rent-test
networks:
rent-prod:
external: true
rent-test:
external: true
@@ -3,17 +3,17 @@ version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: rent-mysql
container_name: rent-prod-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-rent123456}
MYSQL_ROOT_PASSWORD: ${PROD_DB_PASSWORD:-rent123456}
MYSQL_DATABASE: rent_platform
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_unicode_ci
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- mysql_prod_data:/var/lib/mysql
- ../../database/migrations:/docker-entrypoint-initdb.d
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
@@ -21,27 +21,31 @@ services:
interval: 10s
timeout: 5s
retries: 5
networks:
- rent-prod
redis:
image: redis:7-alpine
container_name: rent-redis
container_name: rent-prod-redis
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
- redis_prod_data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- rent-prod
server:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.server
container_name: rent-server
container_name: rent-prod-server
restart: always
ports:
- "3000:3000"
@@ -51,39 +55,60 @@ services:
DB_HOST: mysql
DB_PORT: 3306
DB_USERNAME: root
DB_PASSWORD: ${DB_PASSWORD:-rent123456}
DB_PASSWORD: ${PROD_DB_PASSWORD:-rent123456}
DB_DATABASE: rent_platform
REDIS_HOST: redis
REDIS_PORT: 6379
JWT_SECRET: ${JWT_SECRET:-change_this_in_production}
JWT_SECRET: ${PROD_JWT_SECRET:-change_this_in_production}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- rent-prod
merchant-admin:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.merchant
container_name: rent-merchant-admin
container_name: rent-prod-merchant
restart: always
ports:
- "8081:80"
depends_on:
- server
networks:
- rent-prod
platform-admin:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.platform
container_name: rent-platform-admin
container_name: rent-prod-platform
restart: always
ports:
- "8082:80"
depends_on:
- server
networks:
- rent-prod
website:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.website
container_name: rent-prod-website
restart: always
ports:
- "8083:80"
networks:
- rent-prod
volumes:
mysql_data:
redis_data:
mysql_prod_data:
redis_prod_data:
networks:
rent-prod:
name: rent-prod
+114
View File
@@ -0,0 +1,114 @@
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: rent-test-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${TEST_DB_PASSWORD:-rent123456}
MYSQL_DATABASE: rent_platform
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_unicode_ci
ports:
- "3307:3306"
volumes:
- mysql_test_data:/var/lib/mysql
- ../../database/migrations:/docker-entrypoint-initdb.d
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- rent-test
redis:
image: redis:7-alpine
container_name: rent-test-redis
restart: always
ports:
- "6380:6379"
volumes:
- redis_test_data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- rent-test
server:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.server
container_name: rent-test-server
restart: always
ports:
- "3001:3000"
environment:
NODE_ENV: test
PORT: 3000
DB_HOST: mysql
DB_PORT: 3306
DB_USERNAME: root
DB_PASSWORD: ${TEST_DB_PASSWORD:-rent123456}
DB_DATABASE: rent_platform
REDIS_HOST: redis
REDIS_PORT: 6379
JWT_SECRET: ${TEST_JWT_SECRET:-change_this_in_test}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- rent-test
merchant-admin:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.merchant
container_name: rent-test-merchant
restart: always
ports:
- "9081:80"
depends_on:
- server
networks:
- rent-test
platform-admin:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.platform
container_name: rent-test-platform
restart: always
ports:
- "9082:80"
depends_on:
- server
networks:
- rent-test
website:
build:
context: ../..
dockerfile: deploy/docker/Dockerfile.website
container_name: rent-test-website
restart: always
ports:
- "9083:80"
networks:
- rent-test
volumes:
mysql_test_data:
redis_test_data:
networks:
rent-test:
name: rent-test
+170
View File
@@ -0,0 +1,170 @@
worker_processes auto;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
gzip_min_length 1024;
# 速率限制
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
# ===== 生产环境 Upstream =====
upstream prod_api {
server rent-prod-server:3000;
}
upstream prod_merchant {
server rent-prod-merchant:80;
}
upstream prod_platform {
server rent-prod-platform:80;
}
upstream prod_website {
server rent-prod-website:80;
}
# ===== 测试环境 Upstream =====
upstream test_api {
server rent-test-server:3000;
}
upstream test_merchant {
server rent-test-merchant:80;
}
upstream test_platform {
server rent-test-platform:80;
}
upstream test_website {
server rent-test-website:80;
}
# ===== 生产环境 =====
server {
listen 80;
server_name api.pinzhuhui.com;
location / {
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://prod_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 50m;
}
}
server {
listen 80;
server_name www.pinzhuhui.com;
location / {
proxy_pass http://prod_website;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name merchant.pinzhuhui.com;
location / {
proxy_pass http://prod_merchant;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name platform-admin.pinzhuhui.com;
location / {
proxy_pass http://prod_platform;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# ===== 测试环境 =====
server {
listen 80;
server_name api-test.pinzhuhui.com;
location / {
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://test_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 50m;
}
}
server {
listen 80;
server_name test.pinzhuhui.com;
location / {
proxy_pass http://test_website;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name merchant-test.pinzhuhui.com;
location / {
proxy_pass http://test_merchant;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name platform-admin-test.pinzhuhui.com;
location / {
proxy_pass http://test_platform;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# 兜底:未知域名返回 444
server {
listen 80 default_server;
return 444;
}
}
+107
View File
@@ -0,0 +1,107 @@
#!/bin/bash
set -e
echo "============================================"
echo " 品居会 - 服务器首次初始化"
echo "============================================"
echo ""
# ===== 1. 系统更新 =====
echo ">>> 更新系统包..."
sudo apt update && sudo apt upgrade -y
# ===== 2. 安装 Docker =====
if ! command -v docker &> /dev/null; then
echo ">>> 安装 Docker..."
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
echo "Docker 已安装。可能需要重新登录以生效。"
else
echo ">>> Docker 已安装,跳过。"
fi
# ===== 3. 验证 Docker Compose =====
if ! docker compose version &> /dev/null; then
echo ">>> 安装 Docker Compose 插件..."
sudo apt install -y docker-compose-plugin
else
echo ">>> Docker Compose 已安装,跳过。"
fi
# ===== 4. 安装 Git =====
if ! command -v git &> /dev/null; then
echo ">>> 安装 Git..."
sudo apt install -y git
else
echo ">>> Git 已安装,跳过。"
fi
# ===== 5. 安装 pnpm =====
if ! command -v pnpm &> /dev/null; then
echo ">>> 安装 pnpm..."
npm install -g pnpm 2>/dev/null || { echo "需要先安装 Node.js"; exit 1; }
else
echo ">>> pnpm 已安装,跳过。"
fi
# ===== 6. 配置环境变量 =====
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ENV_FILE="$PROJECT_DIR/deploy/docker/.env"
if [ ! -f "$ENV_FILE" ]; then
echo ">>> 创建 .env 文件..."
cp "$PROJECT_DIR/deploy/docker/.env.example" "$ENV_FILE"
echo ""
echo " *** 重要 ***"
echo " 请编辑 $ENV_FILE 填入真实密码和密钥。"
echo " 必填变量:"
echo " - PROD_DB_PASSWORD 生产环境数据库密码"
echo " - PROD_JWT_SECRET 生产环境 JWT 密钥"
echo " - TEST_DB_PASSWORD 测试环境数据库密码"
echo " - TEST_JWT_SECRET 测试环境 JWT 密钥"
echo ""
read -p " 按 Enter 打开编辑器,或 Ctrl+C 退出手动编辑..."
${EDITOR:-vi} "$ENV_FILE"
else
echo ">>> .env 文件已存在,跳过。"
fi
# ===== 7. 创建 Docker 网络 =====
echo ">>> 创建 Docker 网络..."
docker network create rent-prod 2>/dev/null || echo " rent-prod 网络已存在。"
docker network create rent-test 2>/dev/null || echo " rent-test 网络已存在。"
# ===== 8. 启动网关 =====
echo ">>> 启动 Gateway Nginx..."
cd "$PROJECT_DIR"
docker compose -f deploy/docker/docker-compose.gateway.yml up -d
echo ""
echo "============================================"
echo " 初始化完成!"
echo "============================================"
echo ""
echo "后续步骤:"
echo " 1. 确认 .env 中的密码已正确填写"
echo " 2. 部署生产环境:make prod-deploy"
echo " 3. 部署测试环境:make test-deploy"
echo ""
echo " 配置以下 DNS 记录指向本服务器 IP:"
echo ""
echo " 生产环境:"
echo " api.pinzhuhui.com"
echo " www.pinzhuhui.com"
echo " merchant.pinzhuhui.com"
echo " platform-admin.pinzhuhui.com"
echo ""
echo " 测试环境:"
echo " api-test.pinzhuhui.com"
echo " test.pinzhuhui.com"
echo " merchant-test.pinzhuhui.com"
echo " platform-admin-test.pinzhuhui.com"
echo ""
echo " 4. (可选)配置 SSL 证书:"
echo " sudo apt install certbot"
echo " sudo certbot certonly --standalone -d api.pinzhuhui.com -d www.pinzhuhui.com ..."
echo ""