feat(docker): remove bundled Redis; use external host install
Compose stack is MySQL+Nexus only; wizard Redis check is non-blocking and defaults to host.docker.internal for self-managed Redis. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -28,6 +28,12 @@ curl -fsSL "http://66.154.115.8:3000/admin/Nexus/raw/branch/main/deploy/quick-in
|
||||
|
||||
安装完成后浏览器打开 `https://api.synaglobal.vip/app/install.html` 完成向导。
|
||||
|
||||
## Redis(自行安装,脚本不附带)
|
||||
|
||||
Docker 栈仅含 **MySQL + Nexus**。Redis 请在宿主机或 1Panel 应用商店单独安装,监听 `127.0.0.1:6379`(或改 `docker/.env.prod` 的 `NEXUS_REDIS_URL`)。
|
||||
|
||||
安装向导步骤 3:Docker 部署时 Redis 主机填 **`host.docker.internal`**,端口 `6379`。
|
||||
|
||||
## 1Panel 反代(必须在面板 UI 操作)
|
||||
|
||||
**禁止** Agent/脚本直接 `mkdir` 或 `tee` 到 `/opt/1panel/apps/openresty/` — 会破坏 bind mount。
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run ON production server (宝塔终端 / ssh root@47.254.123.106)
|
||||
# Pulls latest main from Gitea and restarts Nexus. Frontend tar must be uploaded separately
|
||||
# if you did not build on this machine.
|
||||
#
|
||||
# Usage (on server):
|
||||
# cd /www/wwwroot/api.synaglobal.vip
|
||||
# bash deploy/deploy-on-server.sh
|
||||
#
|
||||
# Optional env:
|
||||
# NEXUS_GITEA_TOKEN — if git pull needs auth
|
||||
# SKIP_FRONTEND=1 — backend only
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DEPLOY_PATH="${NEXUS_DEPLOY_PATH:-/www/wwwroot/api.synaglobal.vip}"
|
||||
cd "${DEPLOY_PATH}"
|
||||
|
||||
echo "═══ Nexus server-side deploy ═══"
|
||||
echo "Path: ${DEPLOY_PATH}"
|
||||
|
||||
if [ -n "${NEXUS_GITEA_TOKEN:-}" ]; then
|
||||
git remote set-url origin "http://admin:${NEXUS_GITEA_TOKEN}@66.154.115.8:3000/admin/Nexus.git"
|
||||
fi
|
||||
|
||||
echo "▶ git fetch + reset --hard origin/main..."
|
||||
git fetch --all
|
||||
git reset --hard origin/main
|
||||
echo " HEAD: $(git log -1 --oneline)"
|
||||
|
||||
echo "▶ supervisorctl restart nexus..."
|
||||
supervisorctl restart nexus
|
||||
|
||||
if [ -f /tmp/nexus-frontend.tar.gz ] && [ "${SKIP_FRONTEND:-0}" != "1" ]; then
|
||||
echo "▶ extract /tmp/nexus-frontend.tar.gz..."
|
||||
tar xzf /tmp/nexus-frontend.tar.gz -C web/app
|
||||
rm -f /tmp/nexus-frontend.tar.gz
|
||||
if [ -f deploy/prune_frontend_assets.py ]; then
|
||||
python3 deploy/prune_frontend_assets.py --dry-run
|
||||
python3 deploy/prune_frontend_assets.py
|
||||
fi
|
||||
supervisorctl restart nexus
|
||||
fi
|
||||
|
||||
sleep 3
|
||||
HEALTH=$(curl -s http://127.0.0.1:8600/health || true)
|
||||
SPA=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8600/app/ || echo "000")
|
||||
echo " /health → ${HEALTH}"
|
||||
echo " /app/ → ${SPA}"
|
||||
|
||||
if [ "${HEALTH}" = "ok" ] && [ "${SPA}" = "200" ]; then
|
||||
echo "✓ Server deploy OK"
|
||||
else
|
||||
echo "✗ Verify failed — check: supervisorctl status nexus" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -156,3 +156,13 @@
|
||||
{"ts":"2026-06-04T19:58:01+08:00","date":"2026-06-04","gate":"shell_eol","result":"PASS","detail":"all tracked sh LF"}
|
||||
{"ts":"2026-06-04T19:58:01+08:00","date":"2026-06-04","gate":"text_eol","result":"PASS","detail":"git index LF"}
|
||||
{"ts":"2026-06-04T19:58:01+08:00","date":"2026-06-04","gate":"summary","result":"BLOCK","detail":"5/7"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"changelog","result":"PASS","detail":"2026-06-04-audit-phase2-p2-6-sync-engine-full.md 32lines"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"audit","result":"PASS","detail":"2026-06-04-docs-consolidation.md"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"test","result":"BLOCK","detail":"tests failed"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"lint","result":"PASS","detail":"0 violations"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"import","result":"PASS","detail":"ok"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"security","result":"PASS","detail":"0 HIGH"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"review","result":"BLOCK","detail":"audit missing changed files"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"shell_eol","result":"PASS","detail":"all tracked sh LF"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"text_eol","result":"PASS","detail":"git index LF"}
|
||||
{"ts":"2026-06-04T23:47:38+08:00","date":"2026-06-04","gate":"summary","result":"BLOCK","detail":"5/7"}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# 流程:
|
||||
# ① 可选:官方 1Panel 安装(含 Docker,交互式)
|
||||
# ② 检测 / 安装 Docker Compose
|
||||
# ③ install-nexus-fresh.sh → MySQL + Redis + Nexus 容器
|
||||
# ③ install-nexus-fresh.sh → MySQL + Nexus 容器(Redis 自行安装)
|
||||
#
|
||||
# 反代:必须在 1Panel 面板 UI 配置,禁止脚本直接改 /opt/1panel/apps/
|
||||
# 示例: deploy/1panel/openresty-nexus.conf.example
|
||||
@@ -245,7 +245,7 @@ download_fresh_installer() {
|
||||
}
|
||||
|
||||
phase_nexus() {
|
||||
step "安装 Nexus Docker 栈(MySQL + Redis + Nexus)..."
|
||||
step "安装 Nexus Docker 栈(MySQL + Nexus;Redis 请自行安装)..."
|
||||
local installer
|
||||
if [[ -x "${NEXUS_ROOT}/deploy/install-nexus-fresh.sh" ]]; then
|
||||
installer="${NEXUS_ROOT}/deploy/install-nexus-fresh.sh"
|
||||
|
||||
@@ -542,7 +542,7 @@ write_env_prod() {
|
||||
compose_up() {
|
||||
local root="$1"
|
||||
save_host_profile "$root"
|
||||
info "档位: ${NEXUS_PROFILE}(MySQL/Redis/Nexus 内存与连接池已套用)"
|
||||
info "档位: ${NEXUS_PROFILE}(MySQL/Nexus 内存与连接池已套用;Redis 请自行安装)"
|
||||
info "构建并启动(首次约 10~20 分钟)..."
|
||||
compose_cmd "$root" up -d --build
|
||||
}
|
||||
|
||||
@@ -21,5 +21,9 @@ NEXUS_API_BASE_URL=https://api.synaglobal.vip
|
||||
NEXUS_DB_POOL_SIZE=30
|
||||
NEXUS_DB_MAX_OVERFLOW=20
|
||||
|
||||
# Redis 不在 Docker 栈内 — 请在宿主机或 1Panel 应用商店自行安装
|
||||
# Nexus 容器通过 host.docker.internal 访问宿主机 Redis(见 docker-compose.prod.yml)
|
||||
NEXUS_REDIS_URL=redis://host.docker.internal:6379/0
|
||||
|
||||
# Optional
|
||||
# NEXUS_PUBLISH_PORT=8600
|
||||
|
||||
+2
-2
@@ -44,8 +44,8 @@ docker compose build --no-cache nexus
|
||||
| 服务 | 端口 | 说明 |
|
||||
|------|------|------|
|
||||
| nexus | 8600 | FastAPI + 静态 `/app/` |
|
||||
| mysql | 3306 | 数据库 `nexus` |
|
||||
| redis | 6379 | 仅容器内网(未映射到主机) |
|
||||
| mysql | 3306 | 数据库 `nexus`(Compose 内) |
|
||||
| redis | — | **不在 Compose 内**;宿主机/1Panel 自建,默认 `host.docker.internal:6379` |
|
||||
|
||||
## 生产注意
|
||||
|
||||
|
||||
@@ -34,29 +34,6 @@ services:
|
||||
networks:
|
||||
- nexus-internal
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- redis-server
|
||||
- --appendonly
|
||||
- "yes"
|
||||
- --maxmemory
|
||||
- ${REDIS_MAXMEMORY:-256mb}
|
||||
- --maxmemory-policy
|
||||
- allkeys-lru
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
mem_limit: ${REDIS_MEM_LIMIT:-384m}
|
||||
cpus: ${REDIS_CPUS:-0.5}
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 8
|
||||
networks:
|
||||
- nexus-internal
|
||||
|
||||
nexus:
|
||||
build:
|
||||
context: ..
|
||||
@@ -65,22 +42,21 @@ services:
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
ports:
|
||||
- "127.0.0.1:${NEXUS_PUBLISH_PORT:-8600}:8600"
|
||||
environment:
|
||||
MYSQL_HOST: mysql
|
||||
MYSQL_PORT: "3306"
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: "6379"
|
||||
NEXUS_PERSIST_DIR: /var/lib/nexus
|
||||
NEXUS_DOCKER_WRITE_ENV: "1"
|
||||
NEXUS_HOST: "0.0.0.0"
|
||||
NEXUS_PORT: "8600"
|
||||
NEXUS_DEPLOY_PATH: /app
|
||||
NEXUS_DATABASE_URL: mysql+aiomysql://nexus:${MYSQL_PASSWORD}@mysql:3306/nexus
|
||||
NEXUS_REDIS_URL: redis://redis:6379/0
|
||||
# Redis 不在 Compose 内 — 请在宿主机/1Panel 自建,默认连宿主机 6379
|
||||
NEXUS_REDIS_URL: ${NEXUS_REDIS_URL:-redis://host.docker.internal:6379/0}
|
||||
NEXUS_CORS_ORIGINS: ${NEXUS_CORS_ORIGINS:?set NEXUS_CORS_ORIGINS}
|
||||
NEXUS_SECRET_KEY: ${NEXUS_SECRET_KEY:?set NEXUS_SECRET_KEY}
|
||||
NEXUS_API_KEY: ${NEXUS_API_KEY:?set NEXUS_API_KEY}
|
||||
@@ -109,6 +85,5 @@ networks:
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
redis-data:
|
||||
nexus-state:
|
||||
nexus-web-data:
|
||||
|
||||
+31
-3
@@ -20,6 +20,30 @@ s.close()
|
||||
done
|
||||
}
|
||||
|
||||
wait_tcp_optional() {
|
||||
host="$1"
|
||||
port="$2"
|
||||
tries="${3:-15}"
|
||||
echo "entrypoint: waiting for external Redis ${host}:${port} (max ${tries} tries)..."
|
||||
n=0
|
||||
while [ "$n" -lt "$tries" ]; do
|
||||
if python3 -c "
|
||||
import socket
|
||||
s = socket.socket()
|
||||
s.settimeout(2)
|
||||
s.connect(('${host}', ${port}))
|
||||
s.close()
|
||||
" 2>/dev/null; then
|
||||
echo "entrypoint: Redis ready at ${host}:${port}"
|
||||
return 0
|
||||
fi
|
||||
n=$((n + 1))
|
||||
sleep 2
|
||||
done
|
||||
echo "entrypoint: warn — Redis not reachable at ${host}:${port}; install on host or set NEXUS_REDIS_URL"
|
||||
return 0
|
||||
}
|
||||
|
||||
restore_persisted_env() {
|
||||
if [ -f "${PERSIST_ENV}" ]; then
|
||||
cp "${PERSIST_ENV}" /app/.env
|
||||
@@ -64,11 +88,15 @@ trap persist_env_on_exit EXIT INT TERM
|
||||
|
||||
MYSQL_HOST="${MYSQL_HOST:-mysql}"
|
||||
MYSQL_PORT="${MYSQL_PORT:-3306}"
|
||||
REDIS_HOST="${REDIS_HOST:-redis}"
|
||||
REDIS_PORT="${REDIS_PORT:-6379}"
|
||||
|
||||
wait_tcp "${MYSQL_HOST}" "${MYSQL_PORT}"
|
||||
wait_tcp "${REDIS_HOST}" "${REDIS_PORT}"
|
||||
|
||||
# Redis 由用户自行安装在宿主机/1Panel;安装向导阶段不阻塞启动
|
||||
if [ "${NEXUS_INSTALL_WIZARD_PENDING:-0}" != "1" ] && [ "${NEXUS_SKIP_REDIS_WAIT:-0}" != "1" ]; then
|
||||
REDIS_HOST="${REDIS_HOST:-host.docker.internal}"
|
||||
REDIS_PORT="${REDIS_PORT:-6379}"
|
||||
wait_tcp_optional "${REDIS_HOST}" "${REDIS_PORT}" 15
|
||||
fi
|
||||
|
||||
restore_persisted_env
|
||||
write_env_from_environment
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
MYSQL_CNF_FILE=mysql-prod-1c4g.cnf
|
||||
MYSQL_MEM_LIMIT=1g
|
||||
MYSQL_CPUS=1
|
||||
REDIS_MAXMEMORY=128mb
|
||||
REDIS_MEM_LIMIT=256m
|
||||
REDIS_CPUS=0.25
|
||||
NEXUS_MEM_LIMIT=1g
|
||||
NEXUS_CPUS=1
|
||||
NEXUS_DB_POOL_SIZE=15
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
MYSQL_CNF_FILE=mysql-prod-2c8g.cnf
|
||||
MYSQL_MEM_LIMIT=2g
|
||||
MYSQL_CPUS=1.5
|
||||
REDIS_MAXMEMORY=256mb
|
||||
REDIS_MEM_LIMIT=384m
|
||||
REDIS_CPUS=0.5
|
||||
NEXUS_MEM_LIMIT=2g
|
||||
NEXUS_CPUS=1.5
|
||||
NEXUS_DB_POOL_SIZE=30
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
MYSQL_CNF_FILE=mysql-prod-4c16g.cnf
|
||||
MYSQL_MEM_LIMIT=6g
|
||||
MYSQL_CPUS=3
|
||||
REDIS_MAXMEMORY=512mb
|
||||
REDIS_MEM_LIMIT=768m
|
||||
REDIS_CPUS=1
|
||||
NEXUS_MEM_LIMIT=4g
|
||||
NEXUS_CPUS=3
|
||||
NEXUS_DB_POOL_SIZE=60
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# 2026-06-05 — Docker 栈移除内置 Redis
|
||||
|
||||
## 摘要
|
||||
|
||||
按用户要求,一键安装脚本 / `docker-compose.prod.yml` **不再附带 Redis 容器**。
|
||||
Redis 由用户在宿主机或 1Panel 应用商店自行安装;Nexus 默认连 `host.docker.internal:6379`。
|
||||
|
||||
## 变更
|
||||
|
||||
- `docker/docker-compose.prod.yml` — 删除 `redis` 服务与 `redis-data` 卷
|
||||
- `docker/entrypoint.sh` — 安装向导阶段不等待 Redis;否则有限次等待外置 Redis
|
||||
- `server/api/install.py` — Redis 环境检测改为**不阻塞**(`pass: true`)
|
||||
- `deploy/README-1panel.md` — 补充 Redis 自建说明
|
||||
- `docker/.env.prod.example` — 增加 `NEXUS_REDIS_URL`
|
||||
|
||||
## 迁移(已装旧栈)
|
||||
|
||||
```bash
|
||||
cd /opt/nexus && git pull
|
||||
docker stop nexus-prod-redis-1 2>/dev/null; docker rm nexus-prod-redis-1 2>/dev/null
|
||||
# 在宿主机/1Panel 安装 Redis 并监听 6379
|
||||
docker compose -f docker/docker-compose.prod.yml \
|
||||
--env-file docker/.env.prod \
|
||||
--env-file docker/profiles/$(cat docker/.host-profile).env \
|
||||
up -d --build
|
||||
```
|
||||
|
||||
## 安装向导
|
||||
|
||||
- Docker:Redis 主机 `host.docker.internal`,端口 `6379`
|
||||
- 步骤 2 Redis 未连接也可继续
|
||||
@@ -116,14 +116,13 @@ def _docker_wizard_defaults() -> dict[str, str] | None:
|
||||
"""Prefill install step 3 when running inside docker-compose.prod.yml."""
|
||||
if os.environ.get("NEXUS_DOCKER_WRITE_ENV") != "1" and not os.environ.get("REDIS_HOST"):
|
||||
return None
|
||||
rh, rp, _ = _resolve_redis_probe()
|
||||
return {
|
||||
"db_host": os.environ.get("MYSQL_HOST", "mysql"),
|
||||
"db_port": os.environ.get("MYSQL_PORT", "3306"),
|
||||
"db_name": "nexus",
|
||||
"db_user": "nexus",
|
||||
"redis_host": rh,
|
||||
"redis_port": str(rp),
|
||||
"redis_host": "host.docker.internal",
|
||||
"redis_port": "6379",
|
||||
"redis_db": "0",
|
||||
}
|
||||
|
||||
@@ -443,9 +442,9 @@ async def env_check():
|
||||
"pass": False,
|
||||
})
|
||||
|
||||
# Redis — Docker Compose: redis:6379 (not published on host 127.0.0.1)
|
||||
# Redis — 用户自行安装(1Panel/宿主机),不阻塞安装向导
|
||||
redis_ok = False
|
||||
redis_msg = "未安装"
|
||||
redis_msg = "未检测"
|
||||
rh, rp, rpass = _resolve_redis_probe()
|
||||
redis_label = f"{rh}:{rp}"
|
||||
try:
|
||||
@@ -461,14 +460,14 @@ async def env_check():
|
||||
redis_ok = True
|
||||
redis_msg = f"✓ 已连接 {redis_label}"
|
||||
except Exception as e:
|
||||
redis_msg = f"连接失败: {e}"
|
||||
redis_msg = f"未连接 {redis_label}(可继续,步骤3配置或稍后安装 Redis)— {e}"
|
||||
except ImportError:
|
||||
redis_msg = "redis 库未安装"
|
||||
redis_msg = "redis 库未安装(可继续)"
|
||||
checks.append({
|
||||
"name": "Redis",
|
||||
"required": "已连接",
|
||||
"required": "自行安装(步骤3配置)",
|
||||
"current": redis_msg,
|
||||
"pass": redis_ok,
|
||||
"pass": True,
|
||||
})
|
||||
|
||||
# MySQL database — user configures in step 3
|
||||
|
||||
@@ -182,6 +182,7 @@
|
||||
<!-- Redis -->
|
||||
<div class="border-t-2 border-slate-100 pt-4 mt-4">
|
||||
<h3 class="font-bold text-slate-700 mb-3">⚡ Redis 配置</h3>
|
||||
<p class="text-xs text-slate-500 mb-3">请先在宿主机或 1Panel 安装 Redis。Docker 部署填主机 <code class="bg-slate-100 px-1 rounded">host.docker.internal</code>,端口 6379。</p>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block font-semibold text-sm text-slate-700 mb-1">Redis 主机</label>
|
||||
|
||||
Reference in New Issue
Block a user