追加Docker镜像加速检测功能的新版Deploy.sh

This commit is contained in:
2026-08-05 22:51:56 +08:00
parent 889474303a
commit bcad884069
+379 -1
View File
@@ -24,9 +24,28 @@ DEFAULT_DEPLOY_DIR="/opt/frpc-console"
IMAGE_NAME="frpc-console"
KEEP_IMAGES=3
# ---------- 状态变量 ----------
# ---------- 镜像加速配置 ----------
DEFAULT_MIRRORS=(
"https://docker.1panel.live"
"https://docker.1panel.dev"
"https://docker.1ms.run"
)
DAEMON_JSON="/etc/docker/daemon.json"
MIRROR_CHECK_TIMEOUT=5
# ---------- 镜像加速状态变量 ----------
MIRROR_STATUS="" # no_config | has_mirrors | default_unavailable
NEED_RESTART=false # 是否需要重启 Docker
NEED_REORDER=false # 是否需要重排已有镜像源
HAS_REGISTRY_MIRRORS_KEY=false # daemon.json 中是否存在 registry-mirrors 字段
AVAILABLE_MIRRORS=() # 可用的默认地址
USER_MIRRORS_AVAILABLE=() # 用户配置中可用的地址
USER_MIRRORS_UNAVAILABLE=() # 用户配置中不可用的地址
# ---------- 其他状态变量 ----------
OS=""; OS_VERSION=""; ARCH=""
HAS_GIT=false; HAS_CURL=false; HAS_WGET=false; HAS_DOCKER=false
HAS_JQ=false
NEED_INSTALL_GIT=false; NEED_INSTALL_CURL=false; NEED_INSTALL_WGET=false
PORT=${DEFAULT_PORT}
DEPLOY_DIR=${DEFAULT_DEPLOY_DIR}
@@ -36,6 +55,7 @@ SKIP_CONFIRM=false; CHECK_ONLY=false; DRY_RUN=false
CHANNEL=""; BRANCH=""; TARGET_VERSION=""; IMAGE_TAG=""
CURRENT_VERSION=""
# ---------- 打印函数 ----------
print_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
print_success() { echo -e "${GREEN}[✓]${NC} $1"; }
print_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
@@ -65,6 +85,7 @@ parse_args() {
done
}
# ---------- 权限检测 ----------
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "请使用 root 权限运行此脚本"
@@ -73,6 +94,7 @@ check_root() {
fi
}
# ---------- 系统检测 ----------
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
@@ -96,11 +118,13 @@ detect_arch() {
esac
}
# ---------- 工具检测 ----------
check_tools() {
command -v git &> /dev/null && HAS_GIT=true || NEED_INSTALL_GIT=true
command -v curl &> /dev/null && HAS_CURL=true || NEED_INSTALL_CURL=true
command -v wget &> /dev/null && HAS_WGET=true || NEED_INSTALL_WGET=true
command -v docker &> /dev/null && HAS_DOCKER=true
command -v jq &> /dev/null && HAS_JQ=true || HAS_JQ=false
}
check_container() {
@@ -112,6 +136,328 @@ check_container() {
fi
}
# ---------- 镜像加速:连通性检测 ----------
check_mirror_available() {
local url="$1"
local http_code
http_code=$(curl -s --max-time "$MIRROR_CHECK_TIMEOUT" -o /dev/null -w "%{http_code}" \
"${url}/v2/" 2>/dev/null)
if [[ "$http_code" =~ ^2[0-9][0-9]$ ]] || [ "$http_code" = "401" ] || [ "$http_code" = "403" ]; then
# 2xx 表示成功,401/403 表示需要认证但服务可达(也是可用的)
return 0
fi
return 1
}
# ---------- 镜像加速:安装 jq ----------
install_jq() {
print_info "尝试安装 jq..."
case $OS in
opensuse*|suse*|opensuse-tumbleweed|opensuse-slowroll|opensuse-leap)
zypper install -y jq 2>/dev/null || true
;;
ubuntu|debian|linuxmint)
apt update -qq 2>/dev/null && apt install -y jq 2>/dev/null || true
;;
centos|rhel|fedora|rocky|almalinux)
yum install -y jq 2>/dev/null || true
;;
alpine)
apk add jq 2>/dev/null || true
;;
arch|manjaro|endeavouros)
pacman -S --needed --noconfirm jq 2>/dev/null || true
;;
*)
return 1
;;
esac
command -v jq &> /dev/null && HAS_JQ=true || HAS_JQ=false
if [ "$HAS_JQ" = true ]; then
print_success "jq 安装成功"
return 0
else
print_warn "jq 安装失败,将使用 sed 模式(功能受限)"
return 1
fi
}
# ---------- 镜像加速:检测阶段 ----------
check_docker_mirrors() {
print_step "检测 Docker 镜像源配置..."
# 1. 检测三个默认地址的连通性
print_info "检测默认镜像源连通性 (超时 ${MIRROR_CHECK_TIMEOUT}s)..."
for mirror in "${DEFAULT_MIRRORS[@]}"; do
if check_mirror_available "$mirror"; then
AVAILABLE_MIRRORS+=("$mirror")
print_info "$mirror 可用"
else
print_info "$mirror 不可用"
fi
done
if [ ${#AVAILABLE_MIRRORS[@]} -eq 0 ]; then
MIRROR_STATUS="default_unavailable"
print_warn "所有默认镜像源均不可用,将跳过镜像加速配置"
print_warn "如果 Docker 构建失败,请检查网络或手动配置镜像源"
return 0
fi
print_success "找到 ${#AVAILABLE_MIRRORS[@]} 个可用默认镜像源"
# 2. 检查 daemon.json
if [ ! -f "$DAEMON_JSON" ]; then
MIRROR_STATUS="no_config"
HAS_REGISTRY_MIRRORS_KEY=false
print_info "daemon.json 不存在,将创建新配置"
return 0
fi
# 3. 检测 jq 是否可解析
if ! jq -e '.' "$DAEMON_JSON" >/dev/null 2>&1 2>/dev/null; then
print_warn "daemon.json 格式不标准,将备份并重建"
local backup_path="${DAEMON_JSON}.bak.$(date +%Y%m%d-%H%M%S)"
cp "$DAEMON_JSON" "$backup_path"
print_info "已备份至: $backup_path"
echo '{}' > "$DAEMON_JSON"
print_warn "已重建 daemon.json,请确认是否需要将其他自定义配置迁回"
MIRROR_STATUS="no_config"
HAS_REGISTRY_MIRRORS_KEY=false
return 0
fi
# 4. 检查是否存在 registry-mirrors 字段
local has_key
has_key=$(jq -e 'has("registry-mirrors")' "$DAEMON_JSON" 2>/dev/null)
if [ "$has_key" != "true" ]; then
HAS_REGISTRY_MIRRORS_KEY=false
MIRROR_STATUS="has_mirrors"
print_info "daemon.json 中无 registry-mirrors 字段,将仅插入该字段(保留其他配置)"
return 0
fi
HAS_REGISTRY_MIRRORS_KEY=true
# 5. 读取 registry-mirrors 数组
local mirror_list
mirror_list=$(jq -r '."registry-mirrors" // [] | .[]' "$DAEMON_JSON" 2>/dev/null)
if [ -z "$mirror_list" ]; then
MIRROR_STATUS="has_mirrors"
print_info "registry-mirrors 为空数组,将写入默认地址"
return 0
fi
# 6. 检测用户配置中每个地址的连通性
print_info "检测用户配置的镜像源连通性..."
USER_MIRRORS_AVAILABLE=()
USER_MIRRORS_UNAVAILABLE=()
while IFS= read -r mirror; do
if [ -n "$mirror" ]; then
if check_mirror_available "$mirror"; then
USER_MIRRORS_AVAILABLE+=("$mirror")
print_info "$mirror (用户配置) 可用"
else
USER_MIRRORS_UNAVAILABLE+=("$mirror")
print_info "$mirror (用户配置) 不可用"
fi
fi
done <<< "$mirror_list"
MIRROR_STATUS="has_mirrors"
# 7. 判断是否需要重排
if [ ${#USER_MIRRORS_AVAILABLE[@]} -gt 0 ]; then
# 检查可用地址是否已经全部排在不可用地址前面
NEED_REORDER=false
local seen_unavailable=false
for mirror in "${USER_MIRRORS_AVAILABLE[@]}" "${USER_MIRRORS_UNAVAILABLE[@]}"; do
# 遍历原始顺序:在 USER_MIRRORS_AVAILABLE 和 USER_MIRRORS_UNAVAILABLE 的拼接中,
# 如果先遇到不可用,再遇到可用,说明需要重排
# 但我们这里无法直接还原原始顺序,用另一个方法:检查原始列表中是否存在可用在不可用之后
# 更简单的方法:如果存在任何一个不可用在可用之前
# 用原始列表检测
local raw_list
raw_list=$(jq -r '."registry-mirrors" // [] | .[]' "$DAEMON_JSON" 2>/dev/null)
local raw_available=()
local raw_unavailable=()
while IFS= read -r m; do
if [ -n "$m" ]; then
local found=false
for a in "${USER_MIRRORS_AVAILABLE[@]}"; do
if [ "$a" = "$m" ]; then
raw_available+=("$m")
found=true
break
fi
done
if [ "$found" = false ]; then
raw_unavailable+=("$m")
fi
fi
done <<< "$raw_list"
# 检查原始列表中,是否有不可用地址出现在可用地址之前
local seen_avail=false
for m in "${raw_available[@]}" "${raw_unavailable[@]}"; do
# 这里只检查原始顺序
true
done
# 更准确的方法:遍历原始列表,记录是否见过不可用
local seen_unavail=false
local need_reorder=false
while IFS= read -r m; do
if [ -n "$m" ]; then
local is_avail=false
for a in "${USER_MIRRORS_AVAILABLE[@]}"; do
if [ "$a" = "$m" ]; then
is_avail=true
break
fi
done
if [ "$is_avail" = true ] && [ "$seen_unavail" = true ]; then
need_reorder=true
break
fi
if [ "$is_avail" = false ]; then
seen_unavail=true
fi
fi
done <<< "$raw_list"
NEED_REORDER=$need_reorder
done
if [ "$NEED_REORDER" = true ]; then
print_info "检测到可用镜像源排在不可用镜像源之后,需要重排"
else
print_info "用户镜像源配置正常,无需调整"
fi
elif [ ${#USER_MIRRORS_UNAVAILABLE[@]} -gt 0 ] && [ ${#AVAILABLE_MIRRORS[@]} -gt 0 ]; then
# 用户配置全部不可用,但有可用的默认地址
NEED_REORDER=true
print_info "用户配置全部不可用,将插入可用默认地址"
fi
print_success "镜像源检测完成"
}
# ---------- 镜像加速:配置执行阶段 ----------
configure_docker_mirrors() {
print_step "配置 Docker 镜像源..."
if [ "$MIRROR_STATUS" = "default_unavailable" ]; then
print_warn "无可用默认镜像源,跳过配置"
return 0
fi
if [ ${#AVAILABLE_MIRRORS[@]} -eq 0 ]; then
print_warn "无可用的默认镜像源,跳过配置"
return 0
fi
# 确保 jq 可用
if [ "$HAS_JQ" = false ]; then
print_warn "jq 未安装,尝试安装..."
if ! install_jq; then
print_error "jq 安装失败,无法配置镜像源"
print_error "请手动配置 /etc/docker/daemon.json 中的 registry-mirrors"
return 1
fi
fi
local new_mirrors=()
local need_write=false
local need_restart=false
# 情况判断
if [ "$HAS_REGISTRY_MIRRORS_KEY" = false ]; then
# 不存在 registry-mirrors 字段,直接插入
new_mirrors=("${AVAILABLE_MIRRORS[@]}")
need_write=true
need_restart=true
print_info "插入 registry-mirrors 字段: ${AVAILABLE_MIRRORS[*]}"
elif [ ${#USER_MIRRORS_AVAILABLE[@]} -eq 0 ] && [ ${#USER_MIRRORS_UNAVAILABLE[@]} -eq 0 ]; then
# 空数组,直接写入
new_mirrors=("${AVAILABLE_MIRRORS[@]}")
need_write=true
need_restart=true
print_info "写入 registry-mirrors: ${AVAILABLE_MIRRORS[*]}"
elif [ ${#USER_MIRRORS_AVAILABLE[@]} -gt 0 ]; then
if [ "$NEED_REORDER" = true ]; then
# 重排:可用地址在前 + 不可用地址在后(仅限用户配置中不可用的)
new_mirrors=("${USER_MIRRORS_AVAILABLE[@]}" "${USER_MIRRORS_UNAVAILABLE[@]}")
need_write=true
need_restart=true
print_info "重排镜像源: 可用前置"
else
print_info "镜像源配置正常,无需修改"
return 0
fi
elif [ ${#USER_MIRRORS_AVAILABLE[@]} -eq 0 ] && [ ${#USER_MIRRORS_UNAVAILABLE[@]} -gt 0 ]; then
# 用户配置全部不可用,插入可用默认地址到最前面
new_mirrors=("${AVAILABLE_MIRRORS[@]}" "${USER_MIRRORS_UNAVAILABLE[@]}")
need_write=true
need_restart=true
print_info "插入可用默认镜像源到最前面: ${AVAILABLE_MIRRORS[*]}"
else
print_info "无需修改镜像源配置"
return 0
fi
if [ "$need_write" = false ]; then
return 0
fi
# 检查是否需要重启 Docker(交互模式确认)
if [ "$need_restart" = true ] && [ "$SKIP_CONFIRM" != true ]; then
echo ""
echo -e "${YELLOW}⚠ 修改 Docker 镜像源配置需要重启 Docker 服务。${NC}"
echo -e "${YELLOW} 重启可能影响其他正在运行的容器。${NC}"
read -p "确认继续? [y/N]: " confirm </dev/tty
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
print_info "已取消配置修改"
return 0
fi
fi
# 备份 daemon.json(如果存在且可读)
if [ -f "$DAEMON_JSON" ]; then
local backup_path="${DAEMON_JSON}.bak.$(date +%Y%m%d-%H%M%S)"
cp "$DAEMON_JSON" "$backup_path"
print_info "已备份: $backup_path"
fi
# 写入新配置
local json_mirrors
json_mirrors=$(printf '%s\n' "${new_mirrors[@]}" | jq -R . | jq -s .)
if ! jq --argjson mirrors "$json_mirrors" '."registry-mirrors" = $mirrors' \
"$DAEMON_JSON" > "${DAEMON_JSON}.tmp" 2>/dev/null; then
print_error "写入 daemon.json 失败"
return 1
fi
mv "${DAEMON_JSON}.tmp" "$DAEMON_JSON"
print_success "daemon.json 已更新"
# 重启 Docker
if [ "$need_restart" = true ]; then
print_step "重启 Docker 服务..."
if systemctl restart docker 2>/dev/null; then
print_success "Docker 重启完成"
sleep 2
else
print_error "Docker 重启失败,请手动重启后重新运行脚本"
print_info "手动重启命令: systemctl restart docker"
exit 1
fi
fi
print_success "镜像源配置完成"
}
# ---------- 版本管理 ----------
read_version_from_remote() {
local branch="$1"
@@ -227,6 +573,22 @@ print_environment_summary() {
print_error "Docker 未安装,请先安装 Docker"
exit 1
fi
echo ""
echo " ${CYAN}镜像源状态:${NC}"
if [ ${#AVAILABLE_MIRRORS[@]} -gt 0 ]; then
echo " 可用默认镜像源: ${AVAILABLE_MIRRORS[*]}"
else
echo " 可用默认镜像源: 无"
fi
if [ ${#USER_MIRRORS_AVAILABLE[@]} -gt 0 ]; then
echo " 用户配置可用: ${USER_MIRRORS_AVAILABLE[*]}"
elif [ ${#USER_MIRRORS_UNAVAILABLE[@]} -gt 0 ]; then
echo " 用户配置: ${#USER_MIRRORS_UNAVAILABLE[@]} 个地址均不可用"
elif [ "$HAS_REGISTRY_MIRRORS_KEY" = false ]; then
echo " 用户配置: 无 registry-mirrors"
else
echo " 用户配置: 无"
fi
if [ "$CONTAINER_EXISTS" = true ]; then
echo ""
echo " ${CYAN}容器状态:${NC}"
@@ -251,6 +613,11 @@ generate_plan() {
[ "$NEED_INSTALL_WGET" = true ] && pkgs="${pkgs} wget"
PLAN="${PLAN} • 安装必要工具:${pkgs}\n"
fi
if [ "$MIRROR_STATUS" != "default_unavailable" ] && [ ${#AVAILABLE_MIRRORS[@]} -gt 0 ]; then
if [ "$HAS_REGISTRY_MIRRORS_KEY" = false ] || [ ${#USER_MIRRORS_AVAILABLE[@]} -eq 0 -a ${#USER_MIRRORS_UNAVAILABLE[@]} -eq 0 ] || [ "$NEED_REORDER" = true ]; then
PLAN="${PLAN} • 配置 Docker 镜像加速\n"
fi
fi
PLAN="${PLAN} • 拉取 frpc-console 源码 (${BRANCH} 分支)\n"
PLAN="${PLAN} • 构建 Docker 镜像: ${IMAGE_NAME}:${IMAGE_TAG}\n"
PLAN="${PLAN} • 目标版本: ${TARGET_VERSION}\n"
@@ -384,6 +751,14 @@ do_deploy() {
mkdir -p bin static
print_success "代码拉取完成 (分支: ${BRANCH})"
# ============================================================
# 镜像加速配置:拉取代码之后、docker build 之前执行
# ============================================================
if ! configure_docker_mirrors; then
print_error "镜像源配置失败,请检查后重试"
exit 1
fi
# ----- 停止旧容器 -----
if [ "$CONTAINER_EXISTS" = true ]; then
print_step "停止旧容器..."
@@ -594,6 +969,9 @@ main() {
check_tools
check_container
# ---------- 镜像源检测(拉取代码前,只检测不修改) ----------
check_docker_mirrors
select_channel
generate_target_version "$CHANNEL" "$BRANCH"