feat(deploy): add 1Panel Docker one-click install script

Automates clone, docker/.env.prod, compose up, and health check for 2C8G prod stack.
This commit is contained in:
Nexus Deploy
2026-06-05 22:52:44 +08:00
parent bfda70bbe7
commit 7b33b745a0
3 changed files with 323 additions and 1 deletions
+270
View File
@@ -0,0 +1,270 @@
#!/usr/bin/env bash
# Nexus 6.0 — 1Panel + Docker Compose 一键安装(生产 / 2C8G 调优)
#
# 用法(在服务器 root 或 sudo 下):
# export NEXUS_GITEA_TOKEN='你的Gitea令牌' # 勿提交 Git
# export NEXUS_DOMAIN='api.synaglobal.vip'
# bash deploy/install-1panel-docker.sh
#
# 从宝塔 .env 迁密钥:
# export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env'
# bash deploy/install-1panel-docker.sh
#
# 已 clone 到 /opt/nexus 时:
# cd /opt/nexus && bash deploy/install-1panel-docker.sh --skip-clone
#
# 环境变量:
# NEXUS_ROOT 默认 /opt/nexus
# NEXUS_GITEA_HOST 默认 66.154.115.8:3000
# NEXUS_GITEA_TOKEN Gitea HTTP 克隆/拉取用
# NEXUS_DOMAIN 公网域名(写入 CORS/API_BASE_URL
# NEXUS_FROM_ENV 旧机 .env 路径,导入 NEXUS_* 与可选 MYSQL 密码
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
NEXUS_ROOT="${NEXUS_ROOT:-/opt/nexus}"
NEXUS_GITEA_HOST="${NEXUS_GITEA_HOST:-66.154.115.8:3000}"
NEXUS_GITEA_REPO="${NEXUS_GITEA_REPO:-admin/Nexus.git}"
NEXUS_DOMAIN="${NEXUS_DOMAIN:-api.synaglobal.vip}"
COMPOSE_FILE="docker/docker-compose.prod.yml"
ENV_PROD="docker/.env.prod"
SKIP_CLONE=false
FRESH_INSTALL=false
FROM_ENV="${NEXUS_FROM_ENV:-}"
usage() {
sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//'
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help) usage ;;
--skip-clone) SKIP_CLONE=true; shift ;;
--fresh) FRESH_INSTALL=true; shift ;;
--domain) NEXUS_DOMAIN="$2"; shift 2 ;;
--from-env) FROM_ENV="$2"; shift 2 ;;
--root) NEXUS_ROOT="$2"; shift 2 ;;
*)
error "Unknown option: $1"
usage
;;
esac
done
if [[ "$(id -u)" -ne 0 ]]; then
error "请使用 root 运行,或: sudo bash $0"
exit 1
fi
rand_secret() {
openssl rand -hex 24 2>/dev/null || head -c 24 /dev/urandom | od -An -tx1 | tr -d ' \n'
}
rand_pass() {
openssl rand -hex 16 2>/dev/null || head -c 16 /dev/urandom | od -An -tx1 | tr -d ' \n'
}
read_env_val() {
local file="$1" key="$2"
grep -E "^${key}=" "$file" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '\r"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
}
ensure_packages() {
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq git curl openssl ca-certificates >/dev/null 2>&1 || true
fi
}
ensure_docker() {
if docker compose version >/dev/null 2>&1; then
info "Docker Compose: $(docker compose version --short 2>/dev/null || docker compose version)"
return 0
fi
error "未检测到 docker compose。"
error "请在 1Panel → 容器 中安装 Docker,或: apt install -y docker.io docker-compose-v2"
exit 1
}
clone_or_pull() {
local url
if [[ -n "${NEXUS_GITEA_TOKEN:-}" ]]; then
url="http://admin:${NEXUS_GITEA_TOKEN}@${NEXUS_GITEA_HOST}/${NEXUS_GITEA_REPO}"
else
url="http://${NEXUS_GITEA_HOST}/${NEXUS_GITEA_REPO}"
fi
if [[ "$SKIP_CLONE" == true ]]; then
if [[ ! -d "$NEXUS_ROOT/.git" ]]; then
error "--skip-clone 但 $NEXUS_ROOT 不是 git 仓库"
exit 1
fi
info "跳过克隆,在 $NEXUS_ROOT 执行 git pull..."
git -C "$NEXUS_ROOT" pull "$url" main || git -C "$NEXUS_ROOT" pull origin main
return 0
fi
mkdir -p "$(dirname "$NEXUS_ROOT")"
if [[ -d "$NEXUS_ROOT/.git" ]]; then
info "仓库已存在,git pull..."
git -C "$NEXUS_ROOT" pull "$url" main || git -C "$NEXUS_ROOT" pull origin main
return 0
fi
if [[ -d "$NEXUS_ROOT" ]] && [[ -n "$(ls -A "$NEXUS_ROOT" 2>/dev/null)" ]]; then
error "目录非空且不是 git 仓库: $NEXUS_ROOT"
error "请清空后重试,或: export NEXUS_ROOT=/opt/nexus && rm -rf /opt/nexus"
exit 1
fi
rm -rf "$NEXUS_ROOT"
mkdir -p "$NEXUS_ROOT"
info "克隆 Nexus → $NEXUS_ROOT ..."
if [[ -n "${NEXUS_GITEA_TOKEN:-}" ]]; then
git clone "$url" "$NEXUS_ROOT"
else
warn "未设置 NEXUS_GITEA_TOKEN,尝试匿名 clone(可能失败)"
git clone "http://${NEXUS_GITEA_HOST}/${NEXUS_GITEA_REPO}" "$NEXUS_ROOT"
fi
}
write_env_prod() {
local root="$1"
local env_file="$root/$ENV_PROD"
local example="$root/docker/.env.prod.example"
if [[ ! -f "$example" ]]; then
error "缺少 $example"
exit 1
fi
if [[ -f "$env_file" ]]; then
warn "$env_file 已存在,跳过生成(仅更新域名行)"
sed -i "s|^NEXUS_CORS_ORIGINS=.*|NEXUS_CORS_ORIGINS=https://${NEXUS_DOMAIN}|" "$env_file"
sed -i "s|^NEXUS_API_BASE_URL=.*|NEXUS_API_BASE_URL=https://${NEXUS_DOMAIN}|" "$env_file"
chmod 600 "$env_file"
return 0
fi
local mysql_root mysql_pass secret api enc
mysql_root="$(rand_pass)"
mysql_pass="$(rand_pass)"
if [[ -n "$FROM_ENV" && -f "$FROM_ENV" ]]; then
info "$FROM_ENV 导入密钥..."
secret="$(read_env_val "$FROM_ENV" NEXUS_SECRET_KEY)"
api="$(read_env_val "$FROM_ENV" NEXUS_API_KEY)"
enc="$(read_env_val "$FROM_ENV" NEXUS_ENCRYPTION_KEY)"
local old_root old_pass
old_root="$(read_env_val "$FROM_ENV" MYSQL_ROOT_PASSWORD)"
old_pass="$(read_env_val "$FROM_ENV" MYSQL_PASSWORD)"
[[ -n "$old_root" ]] && mysql_root="$old_root"
[[ -n "$old_pass" ]] && mysql_pass="$old_pass"
# DATABASE_URL 里可能带密码
if [[ -z "$old_pass" ]]; then
local dburl
dburl="$(read_env_val "$FROM_ENV" NEXUS_DATABASE_URL)"
if [[ "$dburl" =~ nexus:([^@]+)@ ]]; then
mysql_pass="${BASH_REMATCH[1]}"
fi
fi
elif [[ "$FRESH_INSTALL" == true ]]; then
secret=""
api=""
enc=""
info "全新安装模式:NEXUS_*_KEY 留空,完成后访问 /app/install.html"
else
secret="$(rand_secret)"
api="$(rand_secret)"
enc=""
warn "未指定 --from-env / --fresh,已生成随机 SECRET_KEY/API_KEY(仅适合新机测试)"
warn "生产迁机请务必: export NEXUS_FROM_ENV=/path/to/.env"
fi
cp "$example" "$env_file"
chmod 600 "$env_file"
sed -i "s|^MYSQL_ROOT_PASSWORD=.*|MYSQL_ROOT_PASSWORD=${mysql_root}|" "$env_file"
sed -i "s|^MYSQL_PASSWORD=.*|MYSQL_PASSWORD=${mysql_pass}|" "$env_file"
sed -i "s|^NEXUS_SECRET_KEY=.*|NEXUS_SECRET_KEY=${secret}|" "$env_file"
sed -i "s|^NEXUS_API_KEY=.*|NEXUS_API_KEY=${api}|" "$env_file"
sed -i "s|^NEXUS_ENCRYPTION_KEY=.*|NEXUS_ENCRYPTION_KEY=${enc}|" "$env_file"
sed -i "s|^NEXUS_CORS_ORIGINS=.*|NEXUS_CORS_ORIGINS=https://${NEXUS_DOMAIN}|" "$env_file"
sed -i "s|^NEXUS_API_BASE_URL=.*|NEXUS_API_BASE_URL=https://${NEXUS_DOMAIN}|" "$env_file"
info "已写入 $env_file"
info " MYSQL_ROOT_PASSWORD 与 MYSQL_PASSWORD 已设置(见文件)"
if [[ "$FRESH_INSTALL" != true && -n "$FROM_ENV" ]]; then
info " 已从旧 .env 导入 NEXUS 密钥"
fi
}
compose_up() {
local root="$1"
cd "$root"
info "构建并启动 Docker Compose(首次可能 1020 分钟)..."
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_PROD" up -d --build
}
verify_health() {
local root="$1"
local i health spa
info "等待服务就绪..."
for i in $(seq 1 60); do
health=$(curl -sf "http://127.0.0.1:8600/health" 2>/dev/null || true)
if [[ "$health" == "ok" ]]; then
spa=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:8600/app/" 2>/dev/null || echo "000")
echo ""
info "═══ 安装完成 ═══"
info " /health → ok"
info " /app/ → HTTP $spa"
info " 代码目录: $root"
echo ""
info "请在 1Panel → 网站 创建站点并反代: http://127.0.0.1:8600"
info " 域名: ${NEXUS_DOMAIN}"
info " 可选自定义配置: $root/deploy/1panel/openresty-nexus.conf.example"
if [[ "$FRESH_INSTALL" == true ]] || ! grep -q '^NEXUS_SECRET_KEY=.\+' "$root/$ENV_PROD" 2>/dev/null; then
info " 浏览器打开: https://${NEXUS_DOMAIN}/app/install.html 完成安装向导"
else
info " 浏览器打开: https://${NEXUS_DOMAIN}/app/"
fi
echo ""
info "常用命令:"
info " cd $root && docker compose -f $COMPOSE_FILE --env-file $ENV_PROD ps"
info " cd $root && docker compose -f $COMPOSE_FILE --env-file $ENV_PROD logs -f nexus"
return 0
fi
sleep 5
done
error "健康检查超时。查看日志:"
error " cd $root && docker compose -f $COMPOSE_FILE --env-file $ENV_PROD logs --tail=80 nexus"
exit 1
}
main() {
echo ""
echo "=========================================="
echo " Nexus 1Panel + Docker 一键安装"
echo "=========================================="
echo ""
ensure_packages
ensure_docker
clone_or_pull
write_env_prod "$NEXUS_ROOT"
compose_up "$NEXUS_ROOT"
verify_health "$NEXUS_ROOT"
}
main "$@"
@@ -0,0 +1,33 @@
# Changelog — 1Panel Docker 一键安装脚本
## 摘要
新增 `deploy/install-1panel-docker.sh`:在 1Panel 服务器上自动 clone/pull、生成 `docker/.env.prod`、启动生产 Compose 并健康检查。
## 动机
用户刚装好 1Panel,需要减少手工步骤与复制 token 错误。
## 涉及文件
- `deploy/install-1panel-docker.sh`
- `SECRETS.md` — 用法示例
- `docs/design/plans/2026-06-04-1panel-docker-production.md` — 引用脚本
## 用法
```bash
export NEXUS_GITEA_TOKEN='...'
export NEXUS_DOMAIN='api.synaglobal.vip'
bash deploy/install-1panel-docker.sh
```
迁机:`export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env'`
## 验证
`bash -n deploy/install-1panel-docker.sh`;服务器执行后 `/health``ok`
## 回滚
`docker compose -f docker/docker-compose.prod.yml down`;删除 `/opt/nexus`
@@ -119,7 +119,26 @@ sudo bash quick_start.sh
---
### Phase 2 — 准备代码与生产 env
### Phase 2 — 一键安装(推荐)
在 1Panel **终端**
```bash
export NEXUS_GITEA_TOKEN='你的Gitea令牌'
export NEXUS_DOMAIN='api.synaglobal.vip'
# 从宝塔迁机:
# export NEXUS_FROM_ENV='/www/wwwroot/api.synaglobal.vip/.env'
cd /opt/nexus 2>/dev/null && git pull || true
# 若尚未 clone,从任意目录:
curl -fsSL "http://66.154.115.8:3000/admin/Nexus/raw/branch/main/deploy/install-1panel-docker.sh" | bash
# 或已 clone 后:
# cd /opt/nexus && bash deploy/install-1panel-docker.sh --skip-clone
```
脚本:`deploy/install-1panel-docker.sh`clone → `.env.prod``compose up``/health`)。
### Phase 2b — 手工准备代码与生产 env(可选)
在 1Panel **终端**或 SSH