提交
This commit is contained in:
1 parent
09afdb1d65
commit
afd517ce10
215 files changed
+32936
-23862
No files matched your search
@@ -0,0 +1,80 @@
|
||||
# ============================================
|
||||
# 中科兴皖 Docker 部署环境变量
|
||||
# 复制为 .env 后修改密码与密钥,勿提交 .env 到 git
|
||||
# ============================================
|
||||
|
||||
# Compose Profile:full=全栈自带中间件 | app=仅应用(连接甲方已有 MySQL/Redis/MinIO)
|
||||
# 若已有 MySQL/Redis 但需自带 MinIO:COMPOSE_PROFILES=app,storage
|
||||
COMPOSE_PROFILES=full
|
||||
|
||||
# 对外 HTTP 端口(Nginx)
|
||||
HTTP_PORT=8112
|
||||
|
||||
# 应用镜像(交付包 docker load 后须与 images/zkxw-app-images.tar 内 tag 一致)
|
||||
ZKXW_BACKEND_IMAGE=zkxw-backend:latest
|
||||
ZKXW_WEB_IMAGE=zkxw-web:latest
|
||||
|
||||
# ---------- MySQL(profile=full 时使用,服务名 mysql)----------
|
||||
MYSQL_ROOT_PASSWORD=change-me-mysql-root
|
||||
|
||||
# profile=app 时改为甲方数据库地址,例如:
|
||||
# DATABASE_URL=mysql+pymysql://user:pass@10.0.0.10:3306/zkxw
|
||||
DATABASE_URL=mysql+pymysql://root:change-me-mysql-root@mysql:3306/zkxw
|
||||
|
||||
# ---------- 应用 ----------
|
||||
APP_NAME=中科兴皖
|
||||
DEBUG=false
|
||||
API_V1_PREFIX=/api
|
||||
|
||||
SECRET_KEY=change-me-jwt-secret-use-long-random-string
|
||||
ALGORITHM=HS256
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES=43200
|
||||
|
||||
DEFAULT_TENANT_NO=zkxw
|
||||
WEB_AUTH_DEFAULT_ROLE=super_admin
|
||||
|
||||
# ---------- Redis(profile=full 时使用,服务名 redis)----------
|
||||
# profile=app 时改为甲方 Redis 地址
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_DB=0
|
||||
REDIS_PASSWORD=
|
||||
REDIS_TIMEOUT_SEC=30
|
||||
REDIS_SSL_ENABLED=false
|
||||
|
||||
# ---------- MinIO(profile=full 或 storage 时使用,服务名 minio:9000)----------
|
||||
STORAGE_TYPE=minio
|
||||
MINIO_ENDPOINT=minio:9000
|
||||
MINIO_ACCESS_KEY=zkxw_minio
|
||||
MINIO_SECRET_KEY=change-me-minio-secret
|
||||
MINIO_BUCKET=zkxw
|
||||
MINIO_SECURE=false
|
||||
# 浏览器访问附件前缀(相对路径;由 zkxw-web 镜像内 Nginx 反代到 minio:9000,勿改为 :9000)
|
||||
MINIO_BASE_URL=/minio/zkxw
|
||||
|
||||
# profile=app 且使用甲方 MinIO 时示例:
|
||||
# MINIO_ENDPOINT=10.0.0.12:9000
|
||||
# MINIO_BASE_URL=http://10.0.0.12:9000/zkxw
|
||||
# 并需同步修改 web-admin/.env.docker 中 VITE_MINIO_BASE_URL 后重新 build web 镜像
|
||||
|
||||
# ---------- API 加密(Docker 交付默认关闭;若启用须与 web-admin/.env.docker 一致)----------
|
||||
API_ENCRYPT_ENABLED=false
|
||||
API_ENCRYPT_FAIL_OPEN=false
|
||||
# API_ENCRYPT_FIXED_SECRET=
|
||||
# API_ENCRYPT_TS_SKEW_SEC=300
|
||||
# API_ENCRYPT_NONCE_TTL_SEC=300
|
||||
|
||||
# 走 Nginx 同域 /api 反代,一般无需额外 CORS
|
||||
# BACKEND_CORS_ORIGINS=["http://your-host:8112"]
|
||||
|
||||
# 登录 RSA(多实例须配置同一私钥;留空则容器启动时自动生成,仅适合单容器)
|
||||
# LOGIN_RSA_PRIVATE_KEY_PEM=
|
||||
|
||||
# 全量 API 请求日志(需 sys_api_request_log 表,否则可关闭)
|
||||
API_REQUEST_LOG_ENABLED=false
|
||||
|
||||
# ---------- 微信支付(可选,不配则支付相关功能不可用)----------
|
||||
# WXPAY_MCHID=
|
||||
# WXPAY_MINI_APPID=
|
||||
# WXPAY_API_KEY=
|
||||
# WXPAY_NOTIFY_URL=https://your-domain.com/api/payment/notify
|
||||
@@ -0,0 +1 @@
|
||||
.env
|
||||
@@ -0,0 +1,430 @@
|
||||
# 中科兴皖 Docker 部署说明
|
||||
|
||||
> 适用对象:甲方运维人员
|
||||
> 部署方式:**预构建应用镜像 + compose**(交付包**不含**前后端源码;基础镜像 MySQL/Redis/MinIO 由甲方联网拉取)
|
||||
> 对外端口:**8112**
|
||||
|
||||
---
|
||||
|
||||
## 1. 环境要求
|
||||
|
||||
| 项 | 要求 |
|
||||
|----|------|
|
||||
| 操作系统 | Linux x86_64(推荐 Ubuntu 22.04 / CentOS 7+) |
|
||||
| Docker | ≥ 24.0 |
|
||||
| Docker Compose | v2(`docker compose` 子命令) |
|
||||
| 网络 | 可访问 Docker Hub 或已配置国内镜像加速 |
|
||||
| 硬件 | 建议 4 核 CPU、8 GB 内存、磁盘 ≥ 50 GB |
|
||||
| 端口 | 宿主机 **8112** 未被占用 |
|
||||
|
||||
验证:
|
||||
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
docker info | grep -A2 "Registry Mirrors" # 建议已配置国内加速
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.1 Docker 镜像加速(国内强烈建议)
|
||||
|
||||
**要配。** 与 pip / npm 镜像不是一回事:
|
||||
|
||||
| 类型 | 作用 | 本项目中谁需要 |
|
||||
|------|------|----------------|
|
||||
| **Docker Registry 镜像** | 加速 `docker pull` / 构建时拉 `python:3.9-slim` 等 | **发版方打包** + **甲方首次 `compose up`** |
|
||||
| **PyPI / npm 镜像** | 构建镜像时 `pip install`、`pnpm install` | 仅发版方打包(已写在 Dockerfile 里) |
|
||||
|
||||
甲方 `docker load` 只导入你们提供的 `zkxw-backend` / `zkxw-web`;首次启动仍会 **pull** 例如:
|
||||
|
||||
- `mysql:5.7.36`、`redis:7-alpine`、`minio/minio`、`minio/mc`
|
||||
|
||||
发版方在 Mac 上 `docker build` 时也要拉 `python:3.9-slim`、`node:20-alpine`、`nginx:1.27-alpine`。
|
||||
|
||||
### macOS(Docker Desktop)
|
||||
|
||||
**Settings → Docker Engine**,在 JSON 中增加(示例,任选可用加速地址):
|
||||
|
||||
```json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.1ms.run",
|
||||
"https://docker.m.daocloud.io"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
保存后 **Apply & Restart**。你本机若 `docker info` 已显示 `Registry Mirrors: https://docker.1ms.run/`,说明已配置,无需重复。
|
||||
|
||||
### Linux 云主机(发版 / 甲方)
|
||||
|
||||
编辑 `/etc/docker/daemon.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://docker.1ms.run",
|
||||
"https://docker.m.daocloud.io"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
然后:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
### 说明
|
||||
|
||||
- 镜像加速由 Docker 守护进程配置,**写进 Dockerfile 无效**。
|
||||
- 公司若已有 **阿里云 ACR 个人版加速地址**,优先用官方提供的专属 URL。
|
||||
- 应用镜像 `zkxw-*` 来自交付包 `docker load`,**不经过** Hub 拉取。
|
||||
- 若 `docker.1ms.run` 拉取 `node`/`nginx` 报 **not found**,前端构建 Dockerfile 已默认改用 `docker.m.daocloud.io/library/...`;或在 Docker Engine 中把 DaoCloud 放在 `registry-mirrors` 第一条。
|
||||
|
||||
---
|
||||
|
||||
## 2. 交付方打包(我方,含源码仓库)
|
||||
|
||||
在**己方**开发机或 CI(有完整 Git 仓库)执行:
|
||||
|
||||
```bash
|
||||
./scripts/package-docker-deploy.sh # 默认日期版本
|
||||
./scripts/package-docker-deploy.sh v1.0.0 # 指定版本号
|
||||
```
|
||||
|
||||
`package-docker-deploy.sh` 固定构建 **`linux/amd64`** 镜像(适配甲方 Linux x86 云主机)。仅在对方确认为 ARM 云主机时,发版前改脚本中的 `DOCKER_PLATFORM` 为 `linux/arm64`。
|
||||
|
||||
脚本会:
|
||||
|
||||
1. 本地 `docker build` 生成 `zkxw-backend:<版本>`、`zkxw-web:<版本>`
|
||||
2. `docker save` 导出为 `images/zkxw-app-images.tar`
|
||||
3. 打包 compose、`.env.example`、`docs/sql/zkxw.sql`(**不含** `backend/`、`web-admin/` 源码)
|
||||
|
||||
产物:`dist/zkxw-docker-deploy-<版本>.tar.gz`
|
||||
|
||||
仅重新构建镜像、不重新打交付包时:
|
||||
|
||||
```bash
|
||||
./scripts/build-docker-images.sh v1.0.0
|
||||
```
|
||||
|
||||
发版方在仓库内从源码本地调试构建:
|
||||
|
||||
```bash
|
||||
cd deploy/docker
|
||||
docker compose -f docker-compose.yml -f docker-compose.build.yml build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
**交付前自检:**
|
||||
|
||||
```bash
|
||||
tar -xzf dist/zkxw-docker-deploy-*.tar.gz -C /tmp
|
||||
cd /tmp/zkxw-docker-deploy-*
|
||||
docker load -i images/zkxw-app-images.tar
|
||||
cd deploy/docker && cp .env.example .env
|
||||
docker compose up -d
|
||||
curl -s http://127.0.0.1:8112/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 交付包目录说明(甲方收到的内容)
|
||||
|
||||
```text
|
||||
zkxw-docker-deploy-<版本>/
|
||||
├── images/
|
||||
│ └── zkxw-app-images.tar # 预构建前后端镜像(无源码)
|
||||
├── deploy/docker/
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── .env.example # 含 ZKXW_*_IMAGE 版本号
|
||||
│ └── mysql/init/
|
||||
├── docs/sql/zkxw.sql # 首次部署自动建库用
|
||||
├── 部署说明.md
|
||||
└── VERSION.txt
|
||||
```
|
||||
|
||||
**不包含**:`backend/`、`web-admin/`、Dockerfile、`.vue` / `.py` 业务源码。
|
||||
|
||||
数据库脚本 `zkxw.sql` 仅为表结构与基线数据,非应用源代码。
|
||||
|
||||
Nginx 反代配置已打入 `zkxw-web` 镜像,无需单独配置。
|
||||
|
||||
---
|
||||
|
||||
## 4. 甲方服务器上线步骤
|
||||
|
||||
```bash
|
||||
# 1) 安装 Docker + Compose(若未安装)
|
||||
docker --version && docker compose version
|
||||
|
||||
# 2) 上传并解压交付包
|
||||
mkdir -p /opt/zkxw && cd /opt/zkxw
|
||||
tar -xzf zkxw-docker-deploy-*.tar.gz
|
||||
cd zkxw-docker-deploy-*
|
||||
|
||||
# 3) 导入应用镜像(前后端,无源码)
|
||||
docker load -i images/zkxw-app-images.tar
|
||||
docker images | grep zkxw-
|
||||
|
||||
# 4) 配置并启动
|
||||
cd deploy/docker
|
||||
cp .env.example .env
|
||||
vi .env # 改密码;确认 ZKXW_BACKEND_IMAGE / ZKXW_WEB_IMAGE 与 load 的 tag 一致
|
||||
|
||||
# 5) 启动(会拉取 mysql:5.7.36、redis、minio 等基础镜像,无需 --build)
|
||||
docker compose up -d
|
||||
|
||||
# 6) 查看进度(首次 MySQL 导入约 1~3 分钟)
|
||||
docker compose ps
|
||||
docker compose logs -f mysql
|
||||
docker compose logs -f backend
|
||||
|
||||
# 7) 验收
|
||||
curl -s http://127.0.0.1:8112/health
|
||||
# 浏览器打开 http://<服务器IP>:8112/ 账号 admin / 111111
|
||||
```
|
||||
|
||||
防火墙 / 安全组放行 **TCP 8112**。
|
||||
|
||||
| 地址 | 说明 |
|
||||
|------|------|
|
||||
| `http://<服务器IP>:8112/` | Web 管理端 |
|
||||
| `http://<服务器IP>:8112/health` | 健康检查 |
|
||||
| `http://<服务器IP>:8112/docs` | Swagger |
|
||||
|
||||
默认账号:`admin` / `111111`(首次登录后请修改)。
|
||||
|
||||
默认 `COMPOSE_PROFILES=full`:自带 MySQL、Redis、MinIO,首次启动自动导入 `docs/sql/zkxw.sql`。
|
||||
|
||||
**Mac(M 芯片)本地试跑**:`mysql:5.7.36` 无 arm64 包,compose 已统一 `platform: linux/amd64`(模拟运行,会慢一些)。**不必**为 Mac 重新打 `zkxw-app-images.tar`。
|
||||
|
||||
---
|
||||
|
||||
## 5. 服务架构
|
||||
|
||||
```text
|
||||
浏览器 :8112
|
||||
└── web(Nginx + 前端静态)
|
||||
├── /api/* → backend:8111(FastAPI)
|
||||
└── /minio/zkxw/* → minio:9000(附件,仅容器内网络)
|
||||
|
||||
backend → mysql:3306(业务库 zkxw)
|
||||
→ redis:6379(会话)
|
||||
→ minio:9000(对象存储)
|
||||
|
||||
minio-init(首次/每次 up 时执行一次):自动创建 `MINIO_BUCKET`(默认 zkxw),并设置 **匿名可读(download)**,供 Nginx `/minio/zkxw/` 反代访问附件;上传仍由后端 API 鉴权后写入。
|
||||
```
|
||||
|
||||
**重要**:MySQL、Redis、MinIO **不映射宿主机端口**,只在 Docker 内部网络通信,**不会**与甲方已有 3306/6379/9000 服务冲突。对外仅开放 **8112**。
|
||||
|
||||
**API 传输**:Docker 交付默认**关闭**前后端 API 加解密(`API_ENCRYPT_ENABLED=false` / `VITE_API_ENCRYPT_ENABLED=false`),请求与响应为明文 JSON。若需启用,须同时改 `.env` 与 `web-admin/.env.docker` 后重建 `backend`、`web` 镜像。
|
||||
|
||||
---
|
||||
|
||||
## 6. 两种部署 Profile
|
||||
|
||||
通过 `.env` 中 `COMPOSE_PROFILES` 切换。
|
||||
|
||||
### 6.1 `full`(默认,全栈)
|
||||
|
||||
```env
|
||||
COMPOSE_PROFILES=full
|
||||
```
|
||||
|
||||
启动:MySQL + Redis + MinIO + 后端 + 前端。
|
||||
|
||||
`.env` 中使用 Docker 服务名:
|
||||
|
||||
```env
|
||||
DATABASE_URL=mysql+pymysql://root:<密码>@mysql:3306/zkxw
|
||||
REDIS_HOST=redis
|
||||
MINIO_ENDPOINT=minio:9000
|
||||
MINIO_BASE_URL=/minio/zkxw
|
||||
```
|
||||
|
||||
### 6.2 `app`(仅应用,连接甲方已有中间件)
|
||||
|
||||
甲方已有 MySQL、Redis、MinIO 时使用:
|
||||
|
||||
```env
|
||||
COMPOSE_PROFILES=app
|
||||
|
||||
DATABASE_URL=mysql+pymysql://user:pass@10.0.0.10:3306/zkxw
|
||||
REDIS_HOST=10.0.0.11
|
||||
REDIS_PORT=6379
|
||||
MINIO_ENDPOINT=10.0.0.12:9000
|
||||
MINIO_ACCESS_KEY=...
|
||||
MINIO_SECRET_KEY=...
|
||||
MINIO_BUCKET=zkxw
|
||||
MINIO_BASE_URL=http://10.0.0.12:9000/zkxw
|
||||
```
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
**注意**:若 MinIO 不在 Docker 网络内,需同步修改 `web-admin/.env.docker` 中的 `VITE_MINIO_BASE_URL` 并重新构建 web 镜像;或保留 MinIO 容器见下节。
|
||||
|
||||
### 6.3 `app,storage`(外部库 + 自带 MinIO)
|
||||
|
||||
已有 MySQL/Redis,但希望 MinIO 仍由本 compose 提供:
|
||||
|
||||
```env
|
||||
COMPOSE_PROFILES=app,storage
|
||||
```
|
||||
|
||||
`.env` 中 `DATABASE_URL`、`REDIS_HOST` 指向甲方地址;`MINIO_ENDPOINT` 仍用 `minio:9000`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 常用运维命令
|
||||
|
||||
```bash
|
||||
cd deploy/docker
|
||||
|
||||
# 停止
|
||||
docker compose down
|
||||
|
||||
# 重启
|
||||
docker compose restart
|
||||
|
||||
# 仅重建后端
|
||||
docker compose up -d --build backend
|
||||
|
||||
# 仅重建前端
|
||||
docker compose up -d --build web
|
||||
|
||||
# 查看日志
|
||||
docker compose logs -f backend
|
||||
docker compose logs -f web
|
||||
|
||||
# 进入后端容器
|
||||
docker compose exec backend bash
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据持久化与备份
|
||||
|
||||
Docker 卷(`docker volume ls | grep zkxw`):
|
||||
|
||||
| 卷名 | 内容 |
|
||||
|------|------|
|
||||
| `zkxw_mysql_data` | MySQL 数据 |
|
||||
| `zkxw_redis_data` | Redis 持久化 |
|
||||
| `zkxw_minio_data` | 附件对象 |
|
||||
| `zkxw_backend_uploads` | 本地存储模式下的上传目录 |
|
||||
|
||||
MySQL 备份示例:
|
||||
|
||||
```bash
|
||||
docker compose exec mysql mysqldump -uroot -p zkxw > zkxw_backup.sql
|
||||
```
|
||||
|
||||
**警告**:`docker compose down -v` 会删除所有卷,导致数据丢失。
|
||||
|
||||
---
|
||||
|
||||
## 9. 升级发版
|
||||
|
||||
```bash
|
||||
cd /opt/zkxw/zkxw-docker-deploy-<新版本>
|
||||
|
||||
# 1) 备份数据库与 MinIO 卷
|
||||
# 2) 导入新应用镜像
|
||||
docker load -i images/zkxw-app-images.tar
|
||||
|
||||
# 3) 更新 deploy/docker/.env 中 ZKXW_BACKEND_IMAGE、ZKXW_WEB_IMAGE
|
||||
cd deploy/docker
|
||||
docker compose up -d
|
||||
|
||||
# 4) 验证
|
||||
curl -s http://127.0.0.1:8112/health
|
||||
```
|
||||
|
||||
若仅有数据库结构变更,由交付方提供增量 SQL,**不要**删除已有 `mysql_data` 卷。
|
||||
|
||||
---
|
||||
|
||||
## 10. 环境变量要点
|
||||
|
||||
| 变量 | 说明 |
|
||||
|------|------|
|
||||
| `HTTP_PORT` | 对外端口,默认 8112 |
|
||||
| `MYSQL_ROOT_PASSWORD` | MySQL root 密码(full profile) |
|
||||
| `DATABASE_URL` | 须与 MySQL 账号密码一致 |
|
||||
| `SECRET_KEY` | JWT 密钥,生产必须改为长随机串 |
|
||||
| `API_ENCRYPT_ENABLED` | Docker 交付默认 `false`(前后端 API 不加密) |
|
||||
| `MINIO_*` | 对象存储;bucket 由 minio-init 自动创建并设为匿名下载(供 `/minio/zkxw/` 访问) |
|
||||
|
||||
修改 `.env` 后:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
docker compose restart backend
|
||||
```
|
||||
|
||||
修改 `web-admin/.env.docker` 后须重建 web:
|
||||
|
||||
```bash
|
||||
docker compose up -d --build web
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. 常见问题
|
||||
|
||||
### 8112 无法访问
|
||||
|
||||
- 检查防火墙 / 安全组是否放行 8112
|
||||
- `docker compose ps` 确认 `web` 为 running
|
||||
- `docker compose logs web`
|
||||
|
||||
### `/api` 返回 502
|
||||
|
||||
- 后端未就绪:`docker compose logs backend`
|
||||
- MySQL 初始化未完成(首次启动约 1~3 分钟):`docker compose logs mysql`
|
||||
|
||||
### 数据库初始化失败需重来
|
||||
|
||||
仅**首次部署失败**且可接受清空数据时:
|
||||
|
||||
```bash
|
||||
docker compose down -v
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 附件无法访问
|
||||
|
||||
- 确认 `STORAGE_TYPE=minio`
|
||||
- `docker compose logs minio-init`(应看到 bucket 创建与 `anonymous set download` 成功)
|
||||
- 浏览器 Network 中附件 URL 应为 `/minio/zkxw/...`
|
||||
- 若 bucket 在加入匿名策略前已存在,可手动执行:`docker compose run --rm minio-init` 或删除 `minio_data` 卷后重新 `up`(会清空附件)
|
||||
|
||||
### 拉取镜像慢或失败
|
||||
|
||||
见上文 **§1.1 Docker 镜像加速**,配置 `registry-mirrors` 后重试 `docker compose up -d`。
|
||||
|
||||
### 发版方打包时 pip / pnpm 很慢或超时
|
||||
|
||||
- 已默认 **阿里云 PyPI** + **npmmirror**,并启用 BuildKit 缓存。
|
||||
- **在 Mac(M 芯片)上交叉构建 `linux/amd64` 很慢**(后端 pip 常见 15~40 分钟),日志会长时间不动,**不要误按 Ctrl+C**。
|
||||
- **推荐**:把仓库放到 **Linux x86 云主机**(与甲方同架构),在该机器执行 `./scripts/package-docker-deploy.sh`,通常 5~15 分钟完成。
|
||||
|
||||
默认已使用阿里云 PyPI(`mirrors.aliyun.com`)。若需改用清华源:
|
||||
|
||||
```bash
|
||||
PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \
|
||||
PIP_TRUSTED_HOST=pypi.tuna.tsinghua.edu.cn \
|
||||
./scripts/package-docker-deploy.sh v1.0.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. 与手工部署的关系
|
||||
|
||||
本方案与 `测试环境部署/README.md` 中「Nginx 8112 + 同域 `/api` 反代」拓扑一致。应用以**预构建镜像**交付,甲方执行 `docker load` + `docker compose up -d` 即可,无需源码与 `pnpm`/`pip` 构建环境。
|
||||
@@ -0,0 +1,14 @@
|
||||
# 仅开发/发版机构建镜像时使用,不随交付包发给甲方
|
||||
# 用法(项目根或 deploy/docker):
|
||||
# docker compose -f docker-compose.yml -f docker-compose.build.yml build
|
||||
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: ../../backend
|
||||
dockerfile: Dockerfile
|
||||
|
||||
web:
|
||||
build:
|
||||
context: ../..
|
||||
dockerfile: deploy/docker/web-admin/Dockerfile
|
||||
@@ -0,0 +1,138 @@
|
||||
# 中科兴皖 Docker Compose 部署(甲方交付版:使用预构建镜像,不含源码)
|
||||
#
|
||||
# 启动(在 deploy/docker 目录):
|
||||
# docker load -i ../../images/zkxw-app-images.tar
|
||||
# cp .env.example .env # 修改密码与镜像 tag
|
||||
# docker compose up -d
|
||||
#
|
||||
# 发版方本地从源码构建镜像:见 scripts/build-docker-images.sh
|
||||
# Profile 说明见 README.md
|
||||
|
||||
name: zkxw
|
||||
|
||||
services:
|
||||
mysql:
|
||||
profiles: [full]
|
||||
image: mysql:5.7.36
|
||||
# MySQL 5.7 无 arm64 镜像;Mac(M 芯片) 与甲方 x86 云主机均用 amd64(Mac 上由 Docker 模拟)
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
command: >
|
||||
--character-set-server=utf8mb4
|
||||
--collation-server=utf8mb4_unicode_ci
|
||||
--explicit_defaults_for_timestamp=1
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
|
||||
MYSQL_DATABASE: zkxw
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./mysql/init/01-init-db.sql:/docker-entrypoint-initdb.d/01-init-db.sql:ro
|
||||
- ../../docs/sql/zkxw.sql:/docker-entrypoint-initdb.d/02-zkxw.sql:ro
|
||||
healthcheck:
|
||||
test: ['CMD', 'mysqladmin', 'ping', '-h', '127.0.0.1', '-p${MYSQL_ROOT_PASSWORD}']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 60s
|
||||
|
||||
redis:
|
||||
profiles: [full]
|
||||
image: redis:7-alpine
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
command: redis-server --appendonly yes
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
|
||||
minio:
|
||||
profiles: [full, storage]
|
||||
image: minio/minio:latest
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
command: server /data --console-address ":9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
|
||||
minio-init:
|
||||
profiles: [full, storage]
|
||||
image: minio/mc:latest
|
||||
platform: linux/amd64
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_started
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
|
||||
mc alias set local http://minio:9000 $${MINIO_ACCESS_KEY} $${MINIO_SECRET_KEY} && break
|
||||
sleep 2
|
||||
done &&
|
||||
mc mb --ignore-existing local/$${MINIO_BUCKET} &&
|
||||
mc anonymous set download local/$${MINIO_BUCKET} &&
|
||||
exit 0
|
||||
"
|
||||
environment:
|
||||
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
|
||||
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
|
||||
MINIO_BUCKET: ${MINIO_BUCKET}
|
||||
restart: 'no'
|
||||
|
||||
backend:
|
||||
profiles: [app, full]
|
||||
image: ${ZKXW_BACKEND_IMAGE:-zkxw-backend:latest}
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
required: false
|
||||
redis:
|
||||
condition: service_healthy
|
||||
required: false
|
||||
minio-init:
|
||||
condition: service_completed_successfully
|
||||
required: false
|
||||
volumes:
|
||||
- backend_uploads:/app/uploads
|
||||
healthcheck:
|
||||
test: ['CMD', 'python', '-c', "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8111/health')"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
web:
|
||||
profiles: [app, full]
|
||||
image: ${ZKXW_WEB_IMAGE:-zkxw-web:latest}
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- '${HTTP_PORT:-8112}:80'
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_started
|
||||
required: false
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1/']
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 15s
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
redis_data:
|
||||
minio_data:
|
||||
backend_uploads:
|
||||
@@ -0,0 +1,2 @@
|
||||
# 确保 init 脚本在 zkxw.sql 之前执行;库名由 compose 中 MYSQL_DATABASE 创建
|
||||
USE zkxw;
|
||||
@@ -0,0 +1,95 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
location = /health {
|
||||
proxy_pass http://backend:8111/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_read_timeout 30s;
|
||||
}
|
||||
|
||||
location ^~ /docs {
|
||||
proxy_pass http://backend:8111;
|
||||
proxy_http_version 1.1;
|
||||
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;
|
||||
}
|
||||
|
||||
location = /openapi.json {
|
||||
proxy_pass http://backend:8111/openapi.json;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
location ^~ /api/ {
|
||||
proxy_pass http://backend:8111;
|
||||
proxy_http_version 1.1;
|
||||
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;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_send_timeout 120s;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
location ^~ /minio/zkxw/ {
|
||||
proxy_pass http://minio:9000/zkxw/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_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;
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_connect_timeout 10s;
|
||||
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "DNT,User-Agent,Keep-Alive,Content-Type,Authorization,Range,If-Modified-Since,Cache-Control" always;
|
||||
|
||||
if ($request_method = OPTIONS) {
|
||||
add_header Content-Length 0;
|
||||
add_header Content-Type text/plain;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable" always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
|
||||
add_header Pragma "no-cache" always;
|
||||
add_header Expires "0" always;
|
||||
}
|
||||
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# 基础镜像走 DaoCloud(避免 docker.1ms.run 等加速站缺 node/nginx manifest)
|
||||
ARG NODE_IMAGE=docker.m.daocloud.io/library/node:20-alpine
|
||||
ARG NGINX_IMAGE=docker.m.daocloud.io/library/nginx:1.27-alpine
|
||||
|
||||
FROM ${NODE_IMAGE} AS builder
|
||||
|
||||
ARG NPM_REGISTRY=https://registry.npmmirror.com
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY web-admin/package.json web-admin/pnpm-lock.yaml ./
|
||||
RUN pnpm config set registry "${NPM_REGISTRY}" \
|
||||
&& pnpm install --frozen-lockfile
|
||||
|
||||
COPY web-admin/ .
|
||||
RUN pnpm exec vite build --mode docker
|
||||
|
||||
FROM ${NGINX_IMAGE}
|
||||
|
||||
COPY deploy/docker/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget -qO- http://127.0.0.1/ || exit 1
|
||||
Reference in new issue
Block a user