Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e0e4a103a | ||
|
|
2d0ae4d7d1 | ||
|
|
9cfd515b8f |
@@ -28,3 +28,9 @@ package-lock.json
|
|||||||
# local vendor for agent test env (not shipped)
|
# local vendor for agent test env (not shipped)
|
||||||
.vendor/
|
.vendor/
|
||||||
vendor_wheels/
|
vendor_wheels/
|
||||||
|
|
||||||
|
# 正式环境部署产物(证书私钥、备份)
|
||||||
|
deploy/tls/certs/
|
||||||
|
backups/
|
||||||
|
ALERT.log
|
||||||
|
backup.log
|
||||||
|
|||||||
@@ -16,3 +16,16 @@ APP_BOOTSTRAP_ADMIN_PASSWORD=change-me-in-local-env
|
|||||||
|
|
||||||
# 内网测试可暂时关闭登录失败锁定;正式环境不得开启
|
# 内网测试可暂时关闭登录失败锁定;正式环境不得开启
|
||||||
# APP_LOGIN_RATE_LIMIT_DISABLED=1
|
# APP_LOGIN_RATE_LIMIT_DISABLED=1
|
||||||
|
|
||||||
|
# ---- 同宿主机第二套环境(正式)必须设置的隔离参数 ----
|
||||||
|
# compose 项目名(默认 deploy;正式环境改为 caiwuzongzhang-prod)
|
||||||
|
# COMPOSE_PROJECT_NAME=caiwuzongzhang-prod
|
||||||
|
# 应用容器名与宿主机端口映射(正式默认只绑本机回环,由反代对外)
|
||||||
|
# APP_CONTAINER_NAME=caiwuzongzhang-prod-app
|
||||||
|
# APP_PORT_MAP=127.0.0.1:4174:4173
|
||||||
|
|
||||||
|
# ---- 正式 HTTPS 反代(COMPOSE_PROFILES=tls 启用 proxy 服务)----
|
||||||
|
# COMPOSE_PROFILES=tls
|
||||||
|
# PROXY_CONTAINER_NAME=caiwuzongzhang-proxy
|
||||||
|
# PROXY_PORT_MAP=8443:8443
|
||||||
|
# 首次启用前先执行 ./tls/gen-cert.sh 生成证书
|
||||||
|
|||||||
@@ -46,6 +46,45 @@ docker exec caiwuzongzhang-app python -c "import sqlite3; s=sqlite3.connect('/ap
|
|||||||
docker cp caiwuzongzhang-app:/tmp/b.db ../backups/app.db.$STAMP
|
docker cp caiwuzongzhang-app:/tmp/b.db ../backups/app.db.$STAMP
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 3.1 正式环境部署(同宿主机隔离,2026-08-30 首次上线采用)
|
||||||
|
|
||||||
|
测试与正式同宿主机并行,靠 compose 项目名/容器名/端口三隔离:
|
||||||
|
|
||||||
|
| 项 | 测试环境 | 正式环境 |
|
||||||
|
|---|---|---|
|
||||||
|
| 目录 | `/home/leefer/caiwuzongzhang-test` | `/home/leefer/caiwuzongzhang-prod` |
|
||||||
|
| compose 项目 | `deploy`(默认) | `COMPOSE_PROJECT_NAME=caiwuzongzhang-prod` |
|
||||||
|
| 应用容器 | `caiwuzongzhang-app` | `caiwuzongzhang-prod-app` |
|
||||||
|
| 端口 | `4173`(对外) | `127.0.0.1:4174`(仅本机,反代上游) |
|
||||||
|
| HTTPS | 无 | nginx 反代 `8443`(`COMPOSE_PROFILES=tls`) |
|
||||||
|
| 访问 | http://192.168.200.36:4173/ | https://192.168.200.36:8443/ |
|
||||||
|
|
||||||
|
首次部署顺序:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone <repo> /home/leefer/caiwuzongzhang-prod && cd /home/leefer/caiwuzongzhang-prod
|
||||||
|
git checkout release/prod
|
||||||
|
mkdir -p data/files backups && chown -R 10001:10001 data # 容器内 app uid
|
||||||
|
cd deploy
|
||||||
|
cp .env.example .env # 按下述差异填写
|
||||||
|
./tls/gen-cert.sh # 生成内部 CA + 服务器证书(SAN 含对外 IP)
|
||||||
|
TAG=<发布镜像tag> docker compose up -d # 不带 --build,直接用已验收镜像
|
||||||
|
docker logs <应用容器> 2>&1 | grep "shown once" # 首启一次性初始口令,只出现一次
|
||||||
|
```
|
||||||
|
|
||||||
|
正式 `.env` 与测试的差异(铁律):
|
||||||
|
|
||||||
|
- `COMPOSE_PROJECT_NAME=caiwuzongzhang-prod`、`APP_CONTAINER_NAME=caiwuzongzhang-prod-app`、`APP_PORT_MAP=127.0.0.1:4174:4173`
|
||||||
|
- `COMPOSE_PROFILES=tls`、`PROXY_CONTAINER_NAME=caiwuzongzhang-proxy`、`PROXY_PORT_MAP=8443:8443`
|
||||||
|
- `APP_UID=10001` / `APP_GID=10001`(data/ 已 chown 10001)
|
||||||
|
- 管理员用户名自定(如 `jinniu-admin`);**不设** `APP_BOOTSTRAP_ADMIN_PASSWORD`(首启控制台取随机一次性口令,首登立即改密)
|
||||||
|
- **不设** `APP_LOGIN_RATE_LIMIT_DISABLED`(限流默认开启)
|
||||||
|
|
||||||
|
证书与信任分发:
|
||||||
|
|
||||||
|
- `tls/certs/ca.crt` 发给各公司电脑安装到「受信任的根证书颁发机构」(安装指引见交接文档);`ca.key`/`server.key` 永不离开宿主机。
|
||||||
|
- 服务器证书有效期 5 年,到期前用 `./tls/gen-cert.sh` 重签并 `docker compose restart proxy`。
|
||||||
|
|
||||||
## 4. 回滚(应用层,5 分钟内)
|
## 4. 回滚(应用层,5 分钟内)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -68,6 +107,16 @@ cd deploy && ./rollback.sh <上一个镜像tag>
|
|||||||
|
|
||||||
cron `*/5 * * * *` 运行 `deploy/monitor.sh`;连续失败或磁盘 ≥80% 时查看 `deploy/ALERT.log` 并按需扩容/清理。容器重启策略 `unless-stopped`。
|
cron `*/5 * * * *` 运行 `deploy/monitor.sh`;连续失败或磁盘 ≥80% 时查看 `deploy/ALERT.log` 并按需扩容/清理。容器重启策略 `unless-stopped`。
|
||||||
|
|
||||||
|
宿主机 crontab(leefer,2026-08-30 起,正式环境):
|
||||||
|
|
||||||
|
```cron
|
||||||
|
15 2 * * * cd /home/leefer/caiwuzongzhang-prod/deploy && CONTAINER=caiwuzongzhang-prod-app ./backup.sh >> backup.log 2>&1
|
||||||
|
*/5 * * * * cd /home/leefer/caiwuzongzhang-prod/deploy && CONTAINER=caiwuzongzhang-prod-app HEALTH_URL=http://127.0.0.1:4174/ ./monitor.sh >/dev/null 2>&1
|
||||||
|
01 3 1 * * rsync -a /home/leefer/caiwuzongzhang-prod/backups/monthly/ /vol1/caiwuzongzhang-backups/monthly/
|
||||||
|
```
|
||||||
|
|
||||||
|
(第三行为月度异盘副本:`/vol1` 是与系统盘不同的物理卷;如后续提供真正的异机目标,改为该目标。)
|
||||||
|
|
||||||
## 7. 升级数据库
|
## 7. 升级数据库
|
||||||
|
|
||||||
应用启动时自动执行迁移(`MIGRATIONS`,当前版本 10)。迁移只前不改写历史;回退 schema 用对应 down 迁移,先备份后操作。
|
应用启动时自动执行迁移(`MIGRATIONS`,当前版本 10)。迁移只前不改写历史;回退 schema 用对应 down 迁移,先备份后操作。
|
||||||
|
|||||||
+55
-2
@@ -1,10 +1,16 @@
|
|||||||
# 测试/正式环境部署编排(正式口令不得写入本文件)。
|
# 测试/正式环境部署编排(正式口令不得写入本文件)。
|
||||||
# 使用:复制 .env.example 为 .env 填写;TAG 指定镜像标签(默认取当前提交 sha)。
|
# 使用:复制 .env.example 为 .env 填写;TAG 指定镜像标签(默认取当前提交 sha)。
|
||||||
# 例:TAG=v1.0.0-rc1 docker compose -f compose.yaml up -d --build
|
# 例:TAG=v1.0.0-rc1 docker compose -f compose.yaml up -d --build
|
||||||
|
#
|
||||||
|
# 同宿主机多套环境(测试 + 正式)必须各自设置:
|
||||||
|
# COMPOSE_PROJECT_NAME / APP_CONTAINER_NAME / APP_PORT_MAP 互不相同。
|
||||||
|
# 正式环境启用 HTTPS 反代:.env 中设 COMPOSE_PROFILES=tls(先跑 tls/gen-cert.sh)。
|
||||||
|
|
||||||
|
name: ${COMPOSE_PROJECT_NAME:-deploy}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
container_name: caiwuzongzhang-app
|
container_name: ${APP_CONTAINER_NAME:-caiwuzongzhang-app}
|
||||||
image: caiwuzongzhang:${TAG:-latest}
|
image: caiwuzongzhang:${TAG:-latest}
|
||||||
build:
|
build:
|
||||||
context: ..
|
context: ..
|
||||||
@@ -15,7 +21,7 @@ services:
|
|||||||
# APP_UID/APP_GID,正式与测试环境各自填写,默认镜像内 app=10001)
|
# APP_UID/APP_GID,正式与测试环境各自填写,默认镜像内 app=10001)
|
||||||
user: "${APP_UID:-10001}:${APP_GID:-10001}"
|
user: "${APP_UID:-10001}:${APP_GID:-10001}"
|
||||||
ports:
|
ports:
|
||||||
- "4173:4173"
|
- "${APP_PORT_MAP:-4173:4173}"
|
||||||
volumes:
|
volumes:
|
||||||
- ../data:/app/data
|
- ../data:/app/data
|
||||||
init: true
|
init: true
|
||||||
@@ -30,9 +36,56 @@ services:
|
|||||||
pids_limit: 128
|
pids_limit: 128
|
||||||
mem_limit: 512m
|
mem_limit: 512m
|
||||||
cpus: 1.0
|
cpus: 1.0
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:4173/', timeout=2)"]
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:4173/', timeout=2)"]
|
||||||
interval: 15s
|
interval: 15s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 5
|
retries: 5
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
|
||||||
|
# 可选 HTTPS 反向代理(正式环境用):COMPOSE_PROFILES=tls 启用。
|
||||||
|
# 证书由 tls/gen-cert.sh 生成的内部 CA 签发(内网自受管信任),公司电脑
|
||||||
|
# 安装 tls/certs/ca.crt 一次即可无告警访问。
|
||||||
|
proxy:
|
||||||
|
container_name: ${PROXY_CONTAINER_NAME:-caiwuzongzhang-proxy}
|
||||||
|
image: nginx:1.27-alpine
|
||||||
|
profiles: ["tls"]
|
||||||
|
ports:
|
||||||
|
- "${PROXY_PORT_MAP:-8443:8443}"
|
||||||
|
volumes:
|
||||||
|
- ./tls/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
- ./tls/certs/server.crt:/etc/nginx/tls/server.crt:ro
|
||||||
|
- ./tls/certs/server.key:/etc/nginx/tls/server.key:ro
|
||||||
|
# 与证书文件属主一致的 uid/gid(默认 101=镜像 nginx 用户;宿主机生成证书时
|
||||||
|
# 设为属主 uid/gid,保持 server.key 0600 不放宽)
|
||||||
|
user: "${PROXY_UID:-101}:${PROXY_GID:-101}"
|
||||||
|
depends_on:
|
||||||
|
- app
|
||||||
|
restart: unless-stopped
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /var/cache/nginx:size=16m,uid=${PROXY_UID:-101},gid=${PROXY_GID:-101},mode=700
|
||||||
|
- /var/run:size=1m,uid=${PROXY_UID:-101},gid=${PROXY_GID:-101},mode=700
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
pids_limit: 64
|
||||||
|
mem_limit: 64m
|
||||||
|
cpus: 0.5
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-q", "--no-check-certificate", "-O", "/dev/null", "https://127.0.0.1:8443/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# 生成内网专用 CA 与服务器证书(自受管内部信任,不涉及公网域名)。
|
||||||
|
# 用法:在正式环境 deploy/ 目录执行 ./tls/gen-cert.sh
|
||||||
|
# 可用环境变量覆盖:
|
||||||
|
# CERT_IP 对外访问 IP(默认 192.168.200.36)
|
||||||
|
# CERT_DNS 额外 DNS 名称(空格分隔,默认 caiwuzongzhang.jinniu.internal)
|
||||||
|
# 输出:tls/certs/{ca.crt,ca.key,server.crt,server.key}(key 永不入库、不分发)
|
||||||
|
# 分发:仅把 ca.crt 发给公司电脑安装(受信任的根证书颁发机构)。
|
||||||
|
set -euo pipefail
|
||||||
|
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
CERTS="$ROOT/certs"
|
||||||
|
mkdir -p "$CERTS"
|
||||||
|
cd "$CERTS"
|
||||||
|
|
||||||
|
CERT_IP="${CERT_IP:-192.168.200.36}"
|
||||||
|
CERT_DNS="${CERT_DNS:-caiwuzongzhang.jinniu.internal}"
|
||||||
|
|
||||||
|
SAN="IP:${CERT_IP}"
|
||||||
|
for d in $CERT_DNS; do SAN="$SAN,DNS:$d"; done
|
||||||
|
|
||||||
|
if [[ ! -f ca.key ]]; then
|
||||||
|
# 内部根 CA:10 年,仅本系统使用
|
||||||
|
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
|
||||||
|
-keyout ca.key -out ca.crt -days 3650 -nodes -subj "/CN=Jinniu Ledger Internal CA" \
|
||||||
|
-addext "basicConstraints=critical,CA:TRUE" \
|
||||||
|
-addext "keyUsage=critical,keyCertSign,cRLSign"
|
||||||
|
chmod 600 ca.key
|
||||||
|
echo "ca 已生成"
|
||||||
|
fi
|
||||||
|
|
||||||
|
openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
|
||||||
|
-keyout server.key -out server.csr -nodes \
|
||||||
|
-subj "/CN=${CERT_DNS%% *}" >/dev/null 2>&1
|
||||||
|
cat > server.ext <<EXT
|
||||||
|
subjectAltName=${SAN}
|
||||||
|
basicConstraints=CA:FALSE
|
||||||
|
keyUsage=digitalSignature,keyEncipherment
|
||||||
|
extendedKeyUsage=serverAuth
|
||||||
|
EXT
|
||||||
|
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||||
|
-out server.crt -days 1825 -sha256 -extfile server.ext >/dev/null 2>&1
|
||||||
|
chmod 600 server.key
|
||||||
|
rm -f server.csr server.ext ca.srl
|
||||||
|
echo "server 证书已生成(SAN: ${SAN}),有效期 5 年"
|
||||||
|
echo "分发文件:${CERTS}/ca.crt(其余文件不得离开本机)"
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# 正式环境 HTTPS 反代(nginx)。证书挂载自 deploy/tls/certs/(gen-cert.sh 生成)。
|
||||||
|
# 上游为同 compose 网络内的 app:4173;对外仅暴露本代理的 8443。
|
||||||
|
server {
|
||||||
|
listen 8443 ssl;
|
||||||
|
listen [::]:8443 ssl;
|
||||||
|
http2 on;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/tls/server.crt;
|
||||||
|
ssl_certificate_key /etc/nginx/tls/server.key;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_session_cache shared:SSL:2m;
|
||||||
|
ssl_session_timeout 1h;
|
||||||
|
|
||||||
|
# 银行流水 Excel 上传上限
|
||||||
|
client_max_body_size 25m;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
proxy_send_timeout 120s;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://app:4173;
|
||||||
|
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 https;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user