fix: Dockerfile 构建上下文修复 + 部署脚本 SSH 别名
This commit is contained in:
parent
e63601b737
commit
2682232d85
|
|
@ -6,8 +6,8 @@ WORKDIR /app
|
|||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --production=false
|
||||
|
||||
# 2. 复制共享库
|
||||
COPY ../shared/ ./shared/
|
||||
# 2. 复制共享库(构建前由部署脚本创建 symlink: shared -> ../shared)
|
||||
COPY shared/ ./shared/
|
||||
|
||||
# 3. 复制源码并构建
|
||||
COPY . .
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
# monitor-ai 部署脚本
|
||||
#
|
||||
# 用法:
|
||||
# bash deploy-monitor.sh # 本地模式(默认)
|
||||
# bash deploy-monitor.sh # 部署到 txjp 服务器(默认)
|
||||
# bash deploy-monitor.sh --dev # 开发模式(本地构建)
|
||||
# bash deploy-monitor.sh --remote --host <IP> # 远程部署到指定服务器
|
||||
#
|
||||
# 首次部署自动:
|
||||
# 1. 生成 OIDC_CLIENT_SECRET (openssl rand -hex 32)
|
||||
|
|
@ -42,20 +41,9 @@ fi
|
|||
# ============================================================
|
||||
# 解析参数
|
||||
# ============================================================
|
||||
MODE="local"
|
||||
HOST=""
|
||||
MODE="remote"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--remote)
|
||||
MODE="remote"
|
||||
info "模式: 远程部署"
|
||||
shift
|
||||
;;
|
||||
--host)
|
||||
HOST="$2"
|
||||
info "目标主机: $HOST"
|
||||
shift 2
|
||||
;;
|
||||
--dev)
|
||||
MODE="dev"
|
||||
info "模式: 开发构建"
|
||||
|
|
@ -69,7 +57,7 @@ while [[ $# -gt 0 ]]; do
|
|||
done
|
||||
|
||||
# ============================================================
|
||||
# 路径配置
|
||||
# 路径配置(对齐 deploy-ai.sh,使用 SSH 别名 txjp)
|
||||
# ============================================================
|
||||
LOCAL_DIR="/Users/niuniu/programs/docker"
|
||||
MONITOR_DIR="$LOCAL_DIR/monitor-ai"
|
||||
|
|
@ -79,13 +67,6 @@ CONTAINER="monitor-ai"
|
|||
SSH_TARGET="txjp"
|
||||
SNAPSHOT_FILE="/tmp/.snapshot.monitor.md5"
|
||||
|
||||
# 远程模式需要 host 参数
|
||||
if [[ "$MODE" == "remote" && -z "$HOST" ]]; then
|
||||
error "远程部署需要指定 --host <IP>"
|
||||
echo " 用法: bash deploy-monitor.sh --remote --host 43.133.38.210"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# 打包源码
|
||||
# ============================================================
|
||||
|
|
@ -116,28 +97,10 @@ package_source() {
|
|||
# 首次部署:生成密钥和配置
|
||||
# ============================================================
|
||||
first_deploy_setup() {
|
||||
local target="$1" # SSH target or "local"
|
||||
local ssh_cmd=""
|
||||
if [[ "$target" != "local" ]]; then
|
||||
ssh_cmd="ssh $target"
|
||||
fi
|
||||
|
||||
local run
|
||||
if [[ -n "$ssh_cmd" ]]; then
|
||||
run="$ssh_cmd"
|
||||
else
|
||||
run=""
|
||||
fi
|
||||
|
||||
info "首次部署检查..."
|
||||
|
||||
# 检查 .env 是否存在
|
||||
local env_exists
|
||||
if [[ -n "$run" ]]; then
|
||||
env_exists=$($run "test -f $REMOTE_DIR/.env && echo yes || echo no" 2>/dev/null)
|
||||
else
|
||||
env_exists=$(test -f "$MONITOR_DIR/.env" && echo yes || echo no)
|
||||
fi
|
||||
env_exists=$(ssh txjp "test -f $REMOTE_DIR/.env && echo yes || echo no" 2>/dev/null)
|
||||
|
||||
if [[ "$env_exists" == "yes" ]]; then
|
||||
info "已存在 .env,跳过密钥生成"
|
||||
|
|
@ -151,10 +114,8 @@ first_deploy_setup() {
|
|||
LOCALADMIN_PASS=$(openssl rand -hex 16)
|
||||
JWT_SECRET=$(openssl rand -hex 32)
|
||||
|
||||
# 生成 pbkdf2 密码哈希(通过 Authelia 容器或本地 openssl)
|
||||
if [[ -n "$run" ]]; then
|
||||
PBKDF2_HASH=$($run "docker exec authelia authelia crypto hash generate pbkdf2 --password '$LOCALADMIN_PASS' 2>/dev/null | grep 'Digest:' | awk '{print \$2}'" 2>/dev/null)
|
||||
fi
|
||||
# 生成 pbkdf2 密码哈希(通过 Authelia 容器)
|
||||
PBKDF2_HASH=$(ssh txjp "docker exec authelia authelia crypto hash generate pbkdf2 --password '$LOCALADMIN_PASS' 2>/dev/null | grep 'Digest:' | awk '{print \$2}'" 2>/dev/null)
|
||||
|
||||
# 如果 Authelia 不可用,使用 openssl 生成备用哈希
|
||||
if [[ -z "$PBKDF2_HASH" ]]; then
|
||||
|
|
@ -164,8 +125,8 @@ first_deploy_setup() {
|
|||
fi
|
||||
|
||||
# 写入 .env 文件
|
||||
local env_content
|
||||
env_content="DATABASE_PATH=/app/data/monitor.db
|
||||
ssh txjp "cat > $REMOTE_DIR/.env << 'ENVEOF'
|
||||
DATABASE_PATH=/app/data/monitor.db
|
||||
MONITOR_MODE=local
|
||||
AUTHELIA_URL=https://sso.tlyq.ai
|
||||
OIDC_CLIENT_ID=monitor-oidc
|
||||
|
|
@ -176,15 +137,8 @@ COOKIE_DOMAIN=.tlyq.ai
|
|||
LDAP_URL=ldap://ldap-ai:3890
|
||||
LOCALADMIN_PASSWORD=$LOCALADMIN_PASS
|
||||
NODE_ENV=production
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0"
|
||||
|
||||
if [[ -n "$run" ]]; then
|
||||
$run "cat > $REMOTE_DIR/.env << 'ENVEOF'
|
||||
$env_content
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
ENVEOF"
|
||||
else
|
||||
echo "$env_content" > "$MONITOR_DIR/.env"
|
||||
fi
|
||||
|
||||
log "密钥已生成"
|
||||
info " OIDC_CLIENT_SECRET: $OIDC_SECRET"
|
||||
|
|
@ -196,7 +150,6 @@ ENVEOF"
|
|||
# 更新 nginx 配置
|
||||
# ============================================================
|
||||
update_nginx() {
|
||||
local target="$1"
|
||||
local nginx_conf="$LOCAL_DIR/nginx-proxy-ai/conf.d/monitor-ai.conf"
|
||||
|
||||
if [[ ! -f "$nginx_conf" ]]; then
|
||||
|
|
@ -204,21 +157,15 @@ update_nginx() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$target" == "local" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "更新 nginx 配置..."
|
||||
scp "$nginx_conf" "$target:$REMOTE_DIR/../nginx-proxy-ai/conf.d/monitor-ai.conf" 2>/dev/null || true
|
||||
ssh "$target" "docker exec nginx-ai nginx -t && docker exec nginx-ai nginx -s reload" && log "nginx 已重载" || warn "nginx 重载失败"
|
||||
scp "$nginx_conf" txjp:/root/docker/nginx-proxy-ai/conf.d/monitor-ai.conf 2>/dev/null || true
|
||||
ssh txjp "docker exec nginx-ai nginx -t && docker exec nginx-ai nginx -s reload" && log "nginx 已重载" || warn "nginx 重载失败"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# 服务器构建
|
||||
# ============================================================
|
||||
build_on_server() {
|
||||
local target="$1"
|
||||
|
||||
# 1. 计算源码快照
|
||||
log "检查源码是否有变化..."
|
||||
local local_md5
|
||||
|
|
@ -238,13 +185,13 @@ build_on_server() {
|
|||
echo " 源码快照: ${local_md5}"
|
||||
|
||||
local prev_md5
|
||||
prev_md5=$(ssh "$target" "cat ${SNAPSHOT_FILE} 2>/dev/null" 2>/dev/null || echo "")
|
||||
prev_md5=$(ssh txjp "cat ${SNAPSHOT_FILE} 2>/dev/null" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$prev_md5" ]]; then
|
||||
info "首次部署,执行完整构建"
|
||||
elif [[ "$prev_md5" == "$local_md5" ]]; then
|
||||
info "源码无变化,跳过构建,仅重启容器"
|
||||
ssh "$target" "cd $REMOTE_DIR && docker compose down && docker compose up -d"
|
||||
ssh txjp "cd $REMOTE_DIR && docker compose down && docker compose up -d"
|
||||
log "容器已重建"
|
||||
return 0
|
||||
else
|
||||
|
|
@ -256,14 +203,14 @@ build_on_server() {
|
|||
|
||||
# 3. 上传
|
||||
log "上传源码包..."
|
||||
scp /tmp/monitor-ai.tar.gz "$target:/tmp/monitor-ai.tar.gz" || {
|
||||
scp /tmp/monitor-ai.tar.gz txjp:/tmp/monitor-ai.tar.gz || {
|
||||
error "上传失败"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 4. 解压源码到 /tmp,然后 rsync 到目标目录
|
||||
log "解压源码并准备依赖..."
|
||||
ssh "$target" "\
|
||||
ssh txjp "\
|
||||
rm -rf /tmp/deploy_monitor && mkdir -p /tmp/deploy_monitor && \
|
||||
tar xzf /tmp/monitor-ai.tar.gz -C /tmp/deploy_monitor 2>/dev/null || true && \
|
||||
rsync -a --delete \
|
||||
|
|
@ -274,13 +221,14 @@ build_on_server() {
|
|||
rsync -a --delete \
|
||||
--exclude='node_modules' --exclude='.git' \
|
||||
/tmp/deploy_monitor/shared/ /root/docker/shared/ && \
|
||||
ln -sf /root/docker/shared $REMOTE_DIR/shared && \
|
||||
rm -rf /tmp/deploy_monitor"
|
||||
|
||||
# 5. 首次部署:上传 docker-compose.yml 和 Dockerfile
|
||||
ssh "$target" "cd $REMOTE_DIR && docker compose up -d"
|
||||
ssh txjp "cd $REMOTE_DIR && docker compose up -d"
|
||||
|
||||
# 如果 host 上没有 node_modules,从容器内复制一份
|
||||
ssh "$target" "if [ ! -d '${REMOTE_DIR}/node_modules' ]; then
|
||||
ssh txjp "if [ ! -d '${REMOTE_DIR}/node_modules' ]; then
|
||||
echo ' 首次:复制容器内 node_modules 到主机...'
|
||||
docker cp ${CONTAINER}:/app/node_modules ${REMOTE_DIR}/node_modules
|
||||
echo ' 完成'
|
||||
|
|
@ -288,16 +236,16 @@ build_on_server() {
|
|||
|
||||
# 6. 安装可能新增的依赖
|
||||
log "安装新增依赖..."
|
||||
ssh "$target" "cd ${REMOTE_DIR} && npm install --prefer-offline 2>&1 | tail -5 || true"
|
||||
ssh txjp "cd ${REMOTE_DIR} && npm install --prefer-offline 2>&1 | tail -5 || true"
|
||||
|
||||
# 7. 清理可能被上传的本地环境变量文件
|
||||
ssh "$target" "rm -f $REMOTE_DIR/.env.local $REMOTE_DIR/.env.development $REMOTE_DIR/.env.production 2>/dev/null; echo '已清理本地环境文件'"
|
||||
ssh txjp "rm -f $REMOTE_DIR/.env.local $REMOTE_DIR/.env.development $REMOTE_DIR/.env.production 2>/dev/null; echo '已清理本地环境文件'"
|
||||
|
||||
# 8. 服务器上执行 npm run build
|
||||
log "服务器上执行 npm run build..."
|
||||
local build_start build_end build_dur
|
||||
build_start=$(date +%s)
|
||||
ssh "$target" "cd ${REMOTE_DIR} && npm run build 2>&1" | \
|
||||
ssh txjp "cd ${REMOTE_DIR} && npm run build 2>&1" | \
|
||||
grep -vE "^(info|warn|npm warn|audited|packages|funding|vulnerability|npm notice|New major)" | \
|
||||
tail -15
|
||||
local build_exit_code=${PIPESTATUS[0]}
|
||||
|
|
@ -311,30 +259,30 @@ build_on_server() {
|
|||
|
||||
# 9. 重建容器
|
||||
log "重建容器..."
|
||||
ssh "$target" "cd $REMOTE_DIR && docker compose up -d && docker compose restart"
|
||||
ssh txjp "cd $REMOTE_DIR && docker compose up -d && docker compose restart"
|
||||
|
||||
# 10. 清理构建缓存
|
||||
log "清理构建缓存..."
|
||||
ssh "$target" "docker image prune -f 2>/dev/null || true"
|
||||
ssh txjp "docker image prune -f 2>/dev/null || true"
|
||||
|
||||
local disk_usage
|
||||
disk_usage=$(ssh "$target" "df / --output=pcent | tail -1 | tr -d ' %'" 2>/dev/null || echo "50")
|
||||
disk_usage=$(ssh txjp "df / --output=pcent | tail -1 | tr -d ' %'" 2>/dev/null || echo "50")
|
||||
if [ "$disk_usage" -gt 95 ]; then
|
||||
warn "磁盘使用率 ${disk_usage}%,执行紧急清理"
|
||||
ssh "$target" "docker builder prune -f 2>/dev/null || true"
|
||||
ssh txjp "docker builder prune -f 2>/dev/null || true"
|
||||
elif [ "$disk_usage" -gt 85 ]; then
|
||||
info "磁盘使用率 ${disk_usage}%,执行激进清理(6小时)"
|
||||
ssh "$target" "docker builder prune -f --filter 'until=6h' 2>/dev/null || true"
|
||||
ssh txjp "docker builder prune -f --filter 'until=6h' 2>/dev/null || true"
|
||||
elif [ "$disk_usage" -gt 70 ]; then
|
||||
info "磁盘使用率 ${disk_usage}%,执行正常清理(24小时)"
|
||||
ssh "$target" "docker builder prune -f --filter 'until=24h' 2>/dev/null || true"
|
||||
ssh txjp "docker builder prune -f --filter 'until=24h' 2>/dev/null || true"
|
||||
else
|
||||
info "磁盘使用率 ${disk_usage}%,执行保守清理(48小时)"
|
||||
ssh "$target" "docker builder prune -f --filter 'until=48h' 2>/dev/null || true"
|
||||
ssh txjp "docker builder prune -f --filter 'until=48h' 2>/dev/null || true"
|
||||
fi
|
||||
|
||||
# 11. 保存快照
|
||||
ssh "$target" "echo '$local_md5' > ${SNAPSHOT_FILE}"
|
||||
ssh txjp "echo '$local_md5' > ${SNAPSHOT_FILE}"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
|
|
@ -372,34 +320,19 @@ echo ""
|
|||
BUILD_START=$(date +%s)
|
||||
|
||||
case "$MODE" in
|
||||
local)
|
||||
# 本地模式:部署到 txjp 服务器(默认行为)
|
||||
remote)
|
||||
# 远程模式:部署到 txjp 服务器
|
||||
log "部署目标: $MONITOR_DIR → txjp:$REMOTE_DIR"
|
||||
echo ""
|
||||
|
||||
# 首次部署检查
|
||||
first_deploy_setup "$SSH_TARGET"
|
||||
first_deploy_setup
|
||||
|
||||
# 构建
|
||||
build_on_server "$SSH_TARGET"
|
||||
build_on_server
|
||||
|
||||
# 更新 nginx
|
||||
update_nginx "$SSH_TARGET"
|
||||
;;
|
||||
|
||||
remote)
|
||||
# 远程模式:部署到指定服务器
|
||||
log "部署目标: $MONITOR_DIR → $HOST:$REMOTE_DIR"
|
||||
echo ""
|
||||
|
||||
# 首次部署检查
|
||||
first_deploy_setup "$HOST"
|
||||
|
||||
# 构建
|
||||
build_on_server "$HOST"
|
||||
|
||||
# 更新 nginx
|
||||
update_nginx "$HOST"
|
||||
update_nginx
|
||||
;;
|
||||
|
||||
dev)
|
||||
|
|
@ -421,7 +354,7 @@ if [[ "$MODE" == "dev" ]]; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
STATUS=$(ssh "$SSH_TARGET" "curl -s -o /dev/null -w '%{http_code}' -k 'https://monitor.tlyq.ai/api/health' 2>/dev/null" 2>/dev/null || echo "???")
|
||||
STATUS=$(ssh txjp "curl -s -o /dev/null -w '%{http_code}' -k 'https://monitor.tlyq.ai/api/health' 2>/dev/null" 2>/dev/null || echo "???")
|
||||
if [[ "$STATUS" == "200" ]]; then
|
||||
log "部署成功!总耗时: ${TOTAL_DUR}s | 访问 https://monitor.tlyq.ai"
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue