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:
@@ -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(首次可能 10~20 分钟)..."
|
||||
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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user