#!/usr/bin/env bash set -euo pipefail # =============================== # 仅使用你指定的 Docker 离线包 # =============================== URL_PRIMARY="https://pub-b69a7194f4ea42fba6aa990c49bded91.r2.dev/xui/dockde12.zip" URL_BACKUP="https://freeyx.vps3344.dpdns.org/xui/dockde12.zip" WORKDIR="/root/docker_offline_install" ZIP_FILE="$WORKDIR/dockde12.zip" log(){ echo -e "\033[32m[✔]\033[0m $*"; } warn(){ echo -e "\033[33m[!]\033[0m $*"; } err(){ echo -e "\033[31m[✘]\033[0m $*"; } # =============================== # 基础检查 # =============================== [ "$(id -u)" -eq 0 ] || { err "请使用 root 运行"; exit 1; } [ "$(uname -m)" = "x86_64" ] || { err "仅支持 x86_64 架构"; exit 1; } # =============================== # dpkg / apt 锁等待 # =============================== wait_lock() { for i in {1..60}; do if fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1 || \ fuser /var/lib/dpkg/lock >/dev/null 2>&1 || \ fuser /var/cache/apt/archives/lock >/dev/null 2>&1; then sleep 2 else return fi done err "dpkg/apt 锁长期占用,请稍后再试" exit 1 } # =============================== # 环境提示(不强退) # =============================== if command -v systemd-detect-virt >/dev/null 2>&1; then virt=$(systemd-detect-virt || true) if [ "$virt" != "none" ] && [ -n "$virt" ]; then warn "检测到虚拟化/容器环境:$virt(非特权容器可能无法运行 Docker)" fi fi # =============================== # 准备工具(不涉及 Docker 来源) # =============================== wait_lock apt-get update -y >/dev/null 2>&1 || true apt-get install -y curl wget unzip python3 >/dev/null 2>&1 || true # =============================== # 停服务 + 清理冲突 # =============================== log "停止旧 Docker / containerd" systemctl stop docker >/dev/null 2>&1 || true systemctl stop containerd >/dev/null 2>&1 || true log "清理可能冲突的旧包" wait_lock apt-get remove -y docker.io docker-doc docker-compose podman-docker \ docker-ce docker-ce-cli containerd containerd.io runc >/dev/null 2>&1 || true apt-get purge -y docker.io docker-doc docker-compose podman-docker \ docker-ce docker-ce-cli containerd containerd.io runc >/dev/null 2>&1 || true apt-get autoremove -y >/dev/null 2>&1 || true # containerd 老配置是最大坑,直接备份移走 if [ -f /etc/containerd/config.toml ]; then warn "发现旧 containerd 配置,已备份并移除" mv /etc/containerd/config.toml /etc/containerd/config.toml.bak.$(date +%s) fi # =============================== # 下载离线包(双线路) # =============================== rm -rf "$WORKDIR" mkdir -p "$WORKDIR" log "下载 Docker 离线包(主线路)" if ! curl -L -k --retry 3 --connect-timeout 10 -o "$ZIP_FILE" "$URL_PRIMARY"; then warn "主线路失败,切换备用线路" curl -L -k --retry 3 --connect-timeout 10 -o "$ZIP_FILE" "$URL_BACKUP" \ || { err "离线包下载失败"; exit 1; } fi [ -s "$ZIP_FILE" ] || { err "下载的 ZIP 文件为空"; exit 1; } # =============================== # 解压并整理 deb # =============================== log "解压离线包" cd "$WORKDIR" unzip -oq "$ZIP_FILE" || python3 - </dev/null ls *.deb >/dev/null 2>&1 || { err "未找到 deb 文件"; exit 1; } # =============================== # 安装(两段式,保证成功率) # =============================== log "安装 Docker(第一次 dpkg)" set +e dpkg -i --force-overwrite *.deb >/dev/null 2>&1 set -e log "修复依赖" wait_lock apt-get -f install -y || { err "依赖修复失败"; exit 1; } log "重新安装 Docker(第二次 dpkg)" dpkg -i --force-overwrite *.deb >/dev/null 2>&1 || { err "dpkg 安装失败"; exit 1; } # =============================== # 启动并验证 # =============================== log "启动 Docker" systemctl daemon-reload >/dev/null 2>&1 || true systemctl unmask docker >/dev/null 2>&1 || true systemctl enable docker >/dev/null 2>&1 || true systemctl restart docker >/dev/null 2>&1 || true sleep 1 if docker info >/dev/null 2>&1; then log "Docker 安装成功" docker --version docker compose version >/dev/null 2>&1 && docker compose version else err "Docker 启动失败" journalctl -xeu docker.service | tail -n 30 exit 1 fi # =============================== # 清理 # =============================== rm -rf "$WORKDIR" log "完成"