Linux(centos7)安装 docker + ollama+ deepseek-r1:7b + Open WebUI(内含一键安装脚本)

windows版本的 ollama :https://blog.youkuaiyun.com/YXWik/article/details/143871588
环境:centos7
在这里插入图片描述

文中各个脚本
1.docker安装 或者 需要重新安装: install_docker.sh
2.docker已安装只需要安装 ollama+ deepseek-r1:7b + Open WebUI : install_ollama.sh
3.空环境需要安装docker + ollama+ deepseek-r1:7b + Open WebUI :
install_docker.sh install_ollama.sh (这两个脚本放同一目录下运行install_ollama.sh 即可)
4. Python版安装Open WebUI(此脚本半成品,需自行修复,与centos7这个环境兼容太差了):install_open_webui.sh脚本

ollama

ollama是一个简明易用的本地大模型运行框架,只需一条命令即可在本地跑大模型。开源项目,专注于开发和部署先进的大型语言模型(LLM)
官网: https://ollama.com/ 下载地址:https://ollama.com/download/linux
在这里插入图片描述
不着急执行,继续阅读文章,后面一键安装脚本

curl -fsSL https://ollama.com/install.sh | sh

在这里插入图片描述
在这里插入图片描述

ollama --version

在这里插入图片描述
Ollama 的官方二进制文件是使用较新的 glibcGCC 编译的,它要求:

glibc >= 2.27
libstdc++ >= 4.8.5(最好 GCC 9+)
系统 glibclibstdc++ 版本太旧,需要更新
系统又安装一些其他程序无法升级,只升级这两个依赖又害怕搞出兼容性的问题
更新半天费劲巴拉的放弃了,采用docker安装得了
以下是一个完整的 Bash 脚本,用于在 CentOS 7 系统上:

  1. 检测是否安装 Docker

    未安装:自动安装并启动 Docker

  2. 检查 Docker 是否运行

    未运行:启动 Docker 服务

  3. 检查是否已有 Ollama 容器

    有:尝试启动它(如果未运行)
    无:拉取并运行 Ollama 容器

注: 此脚本会更改服务器的下载源/etc/yum.repos.d 配置 会更改dockerdaemon.json 配置
脚本中 /home/ollama 挂载到容器内的 /root/.ollama,是用于持久化模型数据,如有需要自行更改路径
ollama的脚本处理完后我就想着deepseek模型(想下载其他模型的也只需要更换脚本下载模型处的 deepseek-r1:7b 即可)和Open WebUI也放到脚本中自动安装启动,这之后就整理个一键安装脚本得了,以下脚本为一键安装脚本(经过多次优化已验证无误,安装open webui巨慢,不想等或者不需要的可以装完模型就结束)

install_ollama.sh

#!/bin/bash

set -e

# 日志函数
log_info() {
  echo -e "\033[1;32m[INFO] $1\033[0m"
}

log_error() {
  echo -e "\033[1;31m[ERROR] $1\033[0m"
}

# 检查是否为 root
if [ "$EUID" -ne 0 ]; then
  log_error "请以 root 权限运行此脚本"
  exit 1
fi

# 安装依赖
if ! command -v curl &> /dev/null; then
  log_info "📥 安装 curl..."
  yum install -y curl || apt-get update && apt-get install -y curl
fi

# 设置 Docker 镜像加速器
log_info "⚙️ 配置 Docker 镜像加速器..."
cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://dockerproxy.com",
    "https://docker.mirrors.ustc.edu.cn",
    "https://docker.nju.edu.cn"
  ]
}
EOF



# 检查 Docker 是否已安装
if ! command -v docker &> /dev/null; then
  log_info "🔍 Docker 未安装,正在调用 install_docker.sh 安装..."
  if [ -f "./install_docker.sh" ]; then
    chmod +x ./install_docker.sh
    ./install_docker.sh
  else
    log_error "❌ 未找到 install_docker.sh 脚本,正在安装采用其他方式安装 Docker"
    curl -fsSL https://get.docker.com | sh
    systemctl enable docker --now
    exit 1
  fi
else
  log_info "✅ Docker 已安装"
fi

# 确保 Docker 正在运行
if ! systemctl is-active --quiet docker; then
  log_info "🔄 Docker 未运行,正在启动..."
  systemctl start docker
fi

# 创建 Ollama 数据目录
DATA_DIR="/home/ollama"
if [ ! -d "$DATA_DIR" ]; then
  log_info "📁 创建 Ollama 数据目录:$DATA_DIR"
  mkdir -p "$DATA_DIR"
fi

# 检查是否已存在 ollama 容器
if docker inspect ollama > /dev/null 2>&1; then
  log_info "🔄 检测到已存在的 ollama 容器,正在停止并删除..."
  docker stop ollama > /dev/null
  docker rm ollama > /dev/null
fi

# 启动 Ollama 容器
log_info "📦 启动 Ollama 容器..."
docker run -d \
  --name ollama \
  -v "$DATA_DIR":/root/.ollama \
  -p 11434:11434 \
  -e OLLAMA_HOST=0.0.0.0 \
  --add-host=host.docker.internal:host-gateway \
  --restart always \
  ollama/ollama

# 等待服务就绪
log_info "⏳ 等待 Ollama 服务启动..."
sleep 5

# 检查 API 是否可用
if curl -s http://localhost:11434/api/tags > /dev/null; then
  log_info "✅ Ollama 服务启动成功"
else
  log_error "❌ Ollama 服务启动失败,请检查日志:docker logs ollama"
  exit 1
fi

# 下载模型(可修改为你需要的模型)
MODEL_NAME="deepseek-r1:7b"
log_info "🧠 正在下载模型:$MODEL_NAME"
docker exec -it ollama ollama run $MODEL_NAME

# 部署 Open WebUI(可选)
log_info "🌐 正在部署 Open WebUI..."
docker run -d \
  --name open-webui \
  -p 3000:8080 \
  --network=host \
  -e OLLAMA_API_URL=http://192.168.0.180:11434 \
  --add-host=host.docker.internal:host-gateway \
  -v open-webui:/app/backend/data \
  --restart always \
  ghcr.io/open-webui/open-webui:main


# 提示完成
log_info "🎉 所有任务已完成!"
log_info "👉 Ollama 地址:http://<你的服务器IP>:11434"
log_info "👉 Open WebUI 地址:http://<你的服务器IP>:3000"
log_info "🧠 模型已下载完成:$MODEL_NAME"
log_info "📁 模型保存路径:$DATA_DIR/models"

新建 install_ollama.sh 文件 将脚本内容保存,然后给文件赋予权限
在这里插入图片描述
还需要一个安装docker的脚本 install_docker.sh ,依旧是需要赋权,这个脚本单独出来是为了避免服务器已经有docker,运行此脚本会进行重装,只有服务器无docker才需要运行,如果服务器有docker但是无法安装ollama的,也可以单独运行此脚本进行重装或者将脚本中的 Docker 源配置一下

install_docker.sh

#!/bin/bash

# 删除所有旧的 Docker 源文件
echo "正在删除旧的 Docker 源文件..."
rm -f /etc/yum.repos.d/docker-*.repo

# 清理 yum 缓存
yum clean all

# 创建新的阿里云 Docker 源文件
echo "正在添加阿里云 Docker 源..."
cat > /etc/yum.repos.d/docker-ce.repo << 'EOF'
[docker-ce-stable]
name=Docker CE Stable - $basearch
baseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/$basearch/stable
enabled=1
gpgcheck=1
gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpg
EOF

# 更新 yum 缓存
yum makecache

# 安装必要软件包
echo "正在安装依赖..."
yum install -y yum-utils device-mapper-persistent-data lvm2

# 安装 Docker CE
echo "正在安装 Docker CE..."
yum install -y docker-ce docker-ce-cli containerd.io

# 启动 Docker 服务并设置开机自启
echo "启动 Docker 服务..."
systemctl start docker
systemctl enable docker

# 验证 Docker 是否安装成功
docker --version
systemctl status docker

echo "Docker 安装完成!"

两个脚本准备好久可以进行一键安装了
在这里插入图片描述

启动安装ollama脚本

./install_ollama.sh

如果自己有docker但是运行脚本报错如下:
在这里插入图片描述
删除所有 Docker 源文件,重新安装docker 或者更改docker源(脚本中有)
运行 install_docker.sh
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
docker处理完成后重新运行安装ollama的脚本

./install_ollama.sh

如果报错 :docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp 103.42.176.244:443: connect: connection refused.

仍然无法访问 Docker Hub
在这里插入图片描述
解决方案:重启docker,因为我们的脚本中配置了docker下载源,但是需要重启才可以生效

systemctl daemon-reload
systemctl restart docker

在这里插入图片描述
再次运行 ./install_ollama.sh
在这里插入图片描述
这里 ctrl+d 退出输入(这里就可以进行对话聊天了,deepseek已经部署好了)

如果不需要Open WebUI的这里就已经私有化deepseek完成了

安装Open WebUI

OLLAMA_BASE_URL更改为Ollama服务器的URL在进行启动:

docker run -d   -p 3000:8080   -e OLLAMA_BASE_URL=http://192.168.0.180:11434   -v open-webui:/app/backend/data   --name open-webui   --restart always   ghcr.nju.edu.cn/open-webui/open-webui:main

要使用Nvidia GPU支持运行Open WebUI,请使用以下命令:

docker run -d -p 3000:8080 --gpus all --add-host=host.docker.internal:host-gateway --network=host -v open-webui:/app/backend/data --name open-webui --restart always  ghcr.nju.edu.cn/open-webui/open-webui:cuda

在这里插入图片描述
这个也太慢太慢了,五个小时了都没有装完,这个网速被限制了吗,还是外网下载的呢,还是就这么慢,官方给的这 docker下载地址太磨叽了,我之前在windows上用Python装过:https://blog.youkuaiyun.com/YXWik/article/details/143871588,也可以换这种方式进行安装
在这里插入图片描述
不想等了,终止了,搞一个基于python进行安装Open WebUI的脚本(这个脚本更难搞,搞了好几天,但是最终还是采用docker安装好的)

install_open_webui.sh

记得给文件授权
在这里插入图片描述
最终还是需要升级gcc版本
脚本:Python 3.11 + Open WebUI+自动检测和修复 _ssl模块 (脚本内含centos7的gcc版本等依赖的升级,生产环境慎重使用
脚本中安装的openssl是3.4.1版本
脚本会替换CentOS 7镜像源,生产环境需要注意
脚本中将启动的端口该为了9000,需要变更的自行更改下
gcc镜像地址:http://gnu.mirror.constant.com/gcc/
如果 ollama不在本机装的,需要更改脚本中OLLAMA_API_URL的值

install_open_webui.sh (这个脚本是未完成版,修复脚本中途采用docker安装成功了,如果无法使用docker的可以在此脚本基础上进行修复调整)

#!/bin/bash
set -e

# 日志函数
log_info() {
  echo -e "\033[1;32m[INFO] $1\033[0m" >&2
}

log_error() {
  echo -e "\033[1;31m[ERROR] $1\033[0m" >&2
}

log_warn() {
  echo -e "\033[1;33m[WARN] $1\033[0m" >&2
}

# 带重试的命令执行函数
retry_command() {
  local max_retries=$1
  shift
  local command=("$@")
  local retry_delay=5

  for ((retry=1; retry<=max_retries; retry++)); do
    if "${command[@]}"; then
      return 0
    fi
    
    if [ $retry -lt $max_retries ]; then
      log_info "命令执行失败,$retry_delay 秒后重试(第 $retry/$max_retries 次)..."
      sleep $retry_delay
      retry_delay=$((retry_delay + 5))
    fi
  done
  
  log_error "命令执行失败,已达到最大重试次数"
  return 1
}

# 带重试的pip安装函数
pip_install_with_retry() {
  local full_package=$1
  local max_retries=3
  local retry_delay=10
  local extra_args=${@:2}
  
  # 提取包名(去掉版本号部分)
  local package=$(echo "$full_package" | sed -E 's/[=<>~].*//')
  
  for ((retry=1; retry<=max_retries; retry++)); do
    log_info "尝试安装 $full_package(第 $retry/$max_retries 次)"
    
    if $PIP_BIN install $extra_args $full_package; then
      log_info "$full_package 安装成功"
      return 0
    fi
    
    if [ $retry -lt $max_retries ]; then
      log_info "安装失败,$retry_delay 秒后重试..."
      sleep $retry_delay
      retry_delay=$((retry_delay + 5))
    fi
  done
  
  log_error "$full_package 安装失败,已达到最大重试次数"
  return 1
}

# 版本比较函数(判断版本1是否>=版本2)
version_ge() {
  [ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" = "$2" ]
}

# 检测系统类型和包管理器
detect_system() {
  log_info "🔍 检测系统类型..."
  
  if [ -f /etc/os-release ]; then
    . /etc/os-release
    OS=$NAME
    VERSION=$VERSION_ID
    if [[ "$OS" == "CentOS Linux" ]]; then
      OS="CentOS"
    fi
  elif type lsb_release >/dev/null 2>&1; then
    OS=$(lsb_release -si)
    VERSION=$(lsb_release -sr)
  elif [ -f /etc/lsb-release ]; then
    . /etc/lsb-release
    OS=$DISTRIB_ID
    VERSION=$DISTRIB_RELEASE
  elif [ -f /etc/debian_version ]; then
    OS=Debian
    VERSION=$(cat /etc/debian_version)
  elif [ -f /etc/redhat-release ]; then
    OS=$(cat /etc/redhat-release | cut -d' ' -f1)
    VERSION=$(cat /etc/redhat-release | grep -oE '[0-9]+\.[0-9]+' | head -1)
  else
    OS=$(uname -s)
    VERSION=$(uname -r)
  fi

  # 检测包管理器
  if command -v yum >/dev/null 2>&1; then
    PACKAGE_MANAGER="yum"
  elif command -v dnf >/dev/null 2>&1; then
    PACKAGE_MANAGER="dnf"
  elif command -v apt-get >/dev/null 2>&1; then
    PACKAGE_MANAGER="apt-get"
  elif command -v zypper >/dev/null 2>&1; then
    PACKAGE_MANAGER="zypper"
  else
    PACKAGE_MANAGER="unknown"
  fi

  log_info "检测到系统: $OS $VERSION,包管理器: $PACKAGE_MANAGER"
}

# 检测OpenSSL版本
check_openssl_version() {
  log_info "🔍 检测OpenSSL版本(要求>=3.0.0)..."
  
  # 检查OpenSSL是否安装
  if ! command -v openssl &>/dev/null; then
    log_info "未安装OpenSSL,需要安装3.0.0及以上版本"
    return 1
  fi
  
  # 获取完整版本号(如3.1.4)
  local openssl_version=$(openssl version | awk '{print $2}')
  
  if [ -z "$openssl_version" ]; then
    log_warn "无法解析OpenSSL版本,视为不兼容"
    return 1
  fi
  
  # 提取主版本号
  local major_version=$(echo "$openssl_version" | cut -d'.' -f1)
  # 提取次版本号
  local minor_version=$(echo "$openssl_version" | cut -d'.' -f2)
  
  log_info "当前OpenSSL版本: $openssl_version(主版本: $major_version,次版本: $minor_version)"
  
  # 检查是否>=3.0.0
  if [ "$major_version" -ge 3 ] && [ "$minor_version" -ge 0 ]; then
    log_info "✅ OpenSSL版本兼容(>=3.0.0)"
    return 0
  else
    log_warn "❌ OpenSSL版本过低($openssl_version < 3.0.0),需要升级"
    return 1
  fi
}

# 校验FFmpeg版本是否兼容(必须>=3.1)
check_ffmpeg_compatibility() {
  log_info "🔍 校验FFmpeg版本兼容性(要求>=3.1)..."
  
  # 检查FFmpeg是否安装
  if ! command -v ffmpeg &>/dev/null; then
    log_info "未安装FFmpeg,需要安装兼容版本"
    return 1
  fi
  
  # 获取主版本号(如5.1.3 -> 5)
  local ffmpeg_version=$(/usr/bin/ffmpeg -version | head -n1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | cut -d'.' -f1)
  
  if [ -z "$ffmpeg_version" ]; then
    log_warn "无法解析FFmpeg版本,视为不兼容"
    return 1
  fi
  
  log_info "当前FFmpeg主版本号: $ffmpeg_version"
  
  # 检查是否>=3
  if [ "$ffmpeg_version" -ge 3 ]; then
    log_info "✅ FFmpeg版本兼容(>=3.1)"
    return 0
  else
    log_warn "❌ FFmpeg版本过低($ffmpeg_version < 3),需要升级"
    return 1
  fi
}

# 检测CMake版本(新增函数)
check_cmake_version() {
  local required_version="3.25"
  log_info "🔍 检测CMake版本(要求>=${required_version})..."
  
  # 检查CMake是否安装
  if ! command -v cmake &>/dev/null; then
    log_info "未安装CMake,需要安装${required_version}及以上版本"
    return 1
  fi
  
  # 获取完整版本号
  local cmake_version=$(cmake --version | head -n1 | awk '{print $3}')
  
  if [ -z "$cmake_version" ]; then
    log_warn "无法解析CMake版本,视为不兼容"
    return 1
  fi
  
  log_info "当前CMake版本: $cmake_version"
  
  # 检查是否>=required_version
  if version_ge "$cmake_version" "$required_version"; then
    log_info "✅ CMake版本兼容(>=${required_version})"
    return 0
  else
    log_warn "❌ CMake版本过低($cmake_version < ${required_version}),需要升级"
    return 1
  fi
}

# 卸载旧版FFmpeg(清理环境)
uninstall_old_ffmpeg() {
  log_info "🧹 卸载旧版FFmpeg..."
  
  # 根据包管理器卸载
  if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then
    $PACKAGE_MANAGER remove -y ffmpeg ffmpeg-devel >/dev/null 2>&1 || true
  elif [ "$PACKAGE_MANAGER" = "apt-get" ]; then
    apt-get remove -y ffmpeg libavcodec-dev libavformat-dev >/dev/null 2>&1 || true
  elif [ "$PACKAGE_MANAGER" = "zypper" ]; then
    zypper remove -y ffmpeg ffmpeg-devel >/dev/null 2>&1 || true
  fi
  
  # 清理残留文件(关键步骤)
  rm -rf /usr/bin/ffmpeg /usr/lib64/libav* /usr/include/libav* \
         /usr/local/bin/ffmpeg /usr/local/lib/libav* /usr/local/include/libav*
         
  # 刷新动态链接库缓存
  ldconfig 2>/dev/null
  
  log_info "旧版FFmpeg清理完成"
}

# 升级CMake(新增函数)
upgrade_cmake() {
  local required_version="3.25"
  local install_version="3.26.4"
  log_info "🔧 开始安装CMake ${install_version}(要求>=${required_version})..."
  
  # 卸载旧版本
  if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then
    $PACKAGE_MANAGER remove -y cmake >/dev/null 2>&1 || true
  elif [ "$PACKAGE_MANAGER" = "apt-get" ]; then
    apt-get remove -y cmake >/dev/null 2>&1 || true
  elif [ "$PACKAGE_MANAGER" = "zypper" ]; then
    zypper remove -y cmake >/dev/null 2>&1 || true
  fi
  
  # 安装编译依赖
  log_info "安装CMake编译依赖..."
  if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then
    $PACKAGE_MANAGER install -y gcc-c++ make openssl-devel
  elif [ "$PACKAGE_MANAGER" = "apt-get" ]; then
    apt-get install -y g++ make libssl-dev
  elif [ "$PACKAGE_MANAGER" = "zypper" ]; then
    zypper install -y gcc-c++ make libopenssl-devel
  fi
  
  # 检测系统架构
  local arch=$(uname -m)
  case $arch in
    x86_64) local cmake_arch="x86_64" ;;
    aarch64) local cmake_arch="aarch64" ;;
    *) 
      log_error "不支持的架构: $arch"
      exit 1
      ;;
  esac
  
  # 下载并安装新版本
  local temp_dir=$(mktemp -d)
  cd "$temp_dir" || {
    log_error "无法进入临时目录"
    exit 1
  }
  
  local cmake_tar="cmake-3.26.4.tar.gz"
  local cmake_url="https://cmake.org/files/v3.26/cmake-3.26.4.tar.gz"
  local backup_url="https://github.com/Kitware/CMake/releases/download/v3.26.4/cmake-3.26.4.tar.gz"
  
  log_info "下载CMake ${install_version}..."
  if ! retry_command 2 wget --no-check-certificate "$cmake_url"; then
    log_warn "主链接下载失败,尝试备用链接..."
    if ! retry_command 2 wget --no-check-certificate "$backup_url"; then
      log_error "所有链接下载CMake失败"
      cd .. && rm -rf "$temp_dir"
      exit 1
    fi
  fi
  
  # 验证文件是否存在
  if [ ! -f "$cmake_tar" ]; then
    log_error "CMake安装包下载失败,文件不存在"
    cd .. && rm -rf "$temp_dir"
    exit 1
  fi
  
  # 解压源码包
  log_info "解压CMake安装包..."
  if ! tar xzf "$cmake_tar"; then
    log_error "CMake安装包解压失败"
    cd .. && rm -rf "$temp_dir"
    exit 1
  fi
  
  # 进入源码目录
  cd "cmake-3.26.4" || {
    log_error "找不到CMake源码目录"
    cd .. && rm -rf "$temp_dir"
    exit 1
  }
  
  # 配置编译选项
  log_info "配置CMake编译选项..."
  ./bootstrap --prefix=/opt/cmake || {
    log_error "CMake配置失败"
    cd ../.. && rm -rf "$temp_dir"
    exit 1
  }
  
  # 编译(根据CPU核心数调整线程数)
  local cpu_cores=$(nproc)
  local make_jobs=$(( cpu_cores > 4 ? 4 : cpu_cores ))
  log_info "使用 $make_jobs 线程编译CMake..."
  make -j$make_jobs || {
    log_error "CMake编译失败"
    cd ../.. && rm -rf "$temp_dir"
    exit 1
  }
  
  # 安装
  log_info "安装CMake..."
  make install || {
    log_error "CMake安装失败"
    cd ../.. && rm -rf "$temp_dir"
    exit 1
  }
  
  # 配置链接
  rm -f /usr/bin/cmake /usr/local/bin/cmake
  ln -sf /opt/cmake/bin/cmake /usr/bin/cmake
  ln -sf /opt/cmake/bin/cmake /usr/local/bin/cmake
  
  # 清理临时文件
  cd ../.. && rm -rf "$temp_dir"
  
  # 验证安装
  if ! command -v cmake &> /dev/null; then
    log_error "CMake安装失败,无法找到可执行文件"
    exit 1
  fi
  
  # 再次检查版本
  if check_cmake_version; then
    log_info "✅ CMake升级成功"
    return 0
  else
    log_error "CMake升级后仍不满足版本要求"
    exit 1
  fi
}

# FFmpeg安装函数(修复OpenSSL识别问题)
install_compatible_ffmpeg() {
  log_info "🔧 安装兼容版本FFmpeg 5.1.3(与av包兼容)..."
  
  # 检测OpenSSL是lib还是lib64目录
  if [ -d "$OPENSSL_DIR/lib64" ]; then
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"
  else
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib"
  fi
  
  # 预检查OpenSSL是否被正确识别
  log_info "预检查OpenSSL配置..."
  if ! pkg-config --exists openssl; then
    log_error "pkg-config仍然无法检测到OpenSSL"
    log_error "请检查$OPENSSL_LIB_DIR/pkgconfig/openssl.pc是否存在"
    exit 1
  fi
  
  local openssl_version=$(pkg-config --modversion openssl)
  log_info "pkg-config报告的OpenSSL版本: $openssl_version"
  
  # 检查主版本是否>=3
  local major_version=$(echo "$openssl_version" | cut -d'.' -f1)
  if [ "$major_version" -lt 3 ]; then
    log_error "pkg-config报告的OpenSSL版本仍然低于3.0.0"
    exit 1
  fi
  
  local ffmpeg_version="5.1.3"
  local ffmpeg_tar="ffmpeg-${ffmpeg_version}.tar.bz2"
  local install_prefix="/usr/local/ffmpeg"
  
  # 安装编译依赖
  log_info "安装FFmpeg编译依赖..."
  if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then
    $PACKAGE_MANAGER install -y yasm nasm make cmake zlib-devel wget
    # 彻底卸载系统默认的openssl相关包
    $PACKAGE_MANAGER remove -y openssl openssl-devel
  elif [ "$PACKAGE_MANAGER" = "apt-get" ]; then
    apt-get install -y yasm nasm make cmake zlib1g-dev wget
    apt-get remove -y openssl libssl-dev
  elif [ "$PACKAGE_MANAGER" = "zypper" ]; then
    zypper install -y yasm nasm make cmake zlib-devel wget
    zypper remove -y openssl openssl-devel
  fi
  
  # 创建临时编译目录
  local build_dir="/tmp/ffmpeg-build"
  rm -rf "$build_dir"
  mkdir -p "$build_dir"
  cd "$build_dir"
  
  # 下载源码(多镜像重试)
  local mirrors=(
    "https://ffmpeg.org/releases/${ffmpeg_tar}"
    "https://mirror.klaus-uwe.me/ffmpeg/releases/${ffmpeg_tar}"
    "https://ftp.vim.org/ftp/ffmpeg/releases/${ffmpeg_tar}"
  )
  
  local download_success=0
  for mirror in "${mirrors[@]}"; do
    log_info "尝试从镜像下载FFmpeg ${ffmpeg_version}$mirror"
    if retry_command 2 wget --no-check-certificate "$mirror"; then
      log_info "✅ 从镜像下载成功"
      download_success=1
      break
    fi
  done
  
  if [ $download_success -eq 0 ]; then
    log_error "所有镜像下载FFmpeg失败"
    exit 1
  fi
  
  # 解压源码
  log_info "解压FFmpeg源码..."
  if ! tar xjf "$ffmpeg_tar"; then
    log_error "源码解压失败,文件可能损坏"
    exit 1
  fi
  cd "ffmpeg-${ffmpeg_version}"
  
  # 清理之前的配置缓存
  make distclean 2>/dev/null || true
  
  # 配置编译选项
  log_info "配置FFmpeg编译选项,强制使用OpenSSL $OPENSSL_DIR..."
  
  # 显式设置所有必要的环境变量,使用正确的lib目录
  export PKG_CONFIG_PATH="$OPENSSL_LIB_DIR/pkgconfig"
  export CFLAGS="-I$OPENSSL_DIR/include"
  export CPPFLAGS="-I$OPENSSL_DIR/include"
  export LDFLAGS="-L$OPENSSL_LIB_DIR -Wl,-rpath,$OPENSSL_LIB_DIR"
  export LIBS="-ldl"
  
  ./configure \
    --prefix="$install_prefix" \
    --enable-shared \
    --enable-gpl \
    --enable-version3 \
    --enable-openssl \
    --disable-static \
    --enable-pic \
    --extra-cflags="-I$OPENSSL_DIR/include" \
    --extra-ldflags="-L$OPENSSL_LIB_DIR -Wl,-rpath,$OPENSSL_LIB_DIR" \
    --extra-libs="-ldl"
  
  # 编译(根据CPU核心数调整线程数)
  local cpu_cores=$(nproc)
  local make_jobs=$(( cpu_cores > 4 ? 4 : cpu_cores ))
  log_info "使用 $make_jobs 线程编译FFmpeg(耗时较长,请耐心等待)..."
  if ! make -j$make_jobs; then
    log_error "多线程编译失败,尝试单线程编译..."
    if ! make -j1; then
      log_error "FFmpeg编译彻底失败"
      exit 1
    fi
  fi
  
  # 安装
  log_info "安装FFmpeg..."
  make install
  
  # 配置系统识别新FFmpeg
  log_info "配置FFmpeg环境变量..."
  
  # 1. 添加动态链接库路径
  echo "$install_prefix/lib" > /etc/ld.so.conf.d/ffmpeg.conf
  ldconfig  # 刷新缓存
  
  # 2. 创建软链接到系统路径(确保能被找到)
  ln -sf "$install_prefix/bin/ffmpeg" /usr/bin/ffmpeg
  ln -sf "$install_prefix/bin/ffprobe" /usr/bin/ffprobe
  
  # 3. 配置pkg-config路径(av包编译需要)
  ln -sf "$install_prefix/lib/pkgconfig"/* /usr/lib64/pkgconfig/ 2>/dev/null
  ln -sf "$install_prefix/lib/pkgconfig"/* /usr/lib/pkgconfig/ 2>/dev/null
  
  # 验证安装结果
  local installed_version=$(ffmpeg -version | head -n1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
  if [ "$installed_version" = "$ffmpeg_version" ]; then
    log_info "✅ FFmpeg $ffmpeg_version 安装成功"
  else
    log_error "FFmpeg安装版本不符(实际: $installed_version,预期: $ffmpeg_version)"
    exit 1
  fi
}
    
# 安装并验证FFmpeg(整合校验、卸载旧版、安装新版)
setup_compatible_ffmpeg() {
  log_info "🎥 开始FFmpeg环境配置..."
  
  # 先校验是否兼容
  if check_ffmpeg_compatibility; then
    return 0
  fi
  
  # 不兼容则卸载旧版
  uninstall_old_ffmpeg
  
  # 安装兼容版本
  install_compatible_ffmpeg
  
  # 再次验证
  check_ffmpeg_compatibility || {
    log_error "FFmpeg配置后仍不兼容,无法继续安装"
    exit 1
  }
}

# 搜索可能的GCC路径(优先高版本)
search_gcc_paths() {
  log_info "🔍 搜索可能的GCC路径..."
  
  local possible_paths=(
    "/usr/local/gcc-"*"/bin/gcc"
    "/opt/rh/devtoolset-*/root/usr/bin/gcc"
    "/usr/bin/gcc-"*
    "/usr/local/bin/gcc"
    "/usr/bin/gcc"
    "/usr/lib/gcc/"*"/"*"/bin/gcc"
    "$HOME/.local/bin/gcc"
  )
  
  for path in "${possible_paths[@]}"; do
    if [[ $path == *"*"* ]]; then
      for matched_path in $(echo $path 2>/dev/null | sort -Vr); do
        if [ -x "$matched_path" ]; then
          log_info "发现可执行的GCC: $matched_path"
          echo "$matched_path"
          return 0
        fi
      done
    else
      if [ -x "$path" ]; then
        log_info "发现可执行的GCC: $path"
        echo "$path"
        return 0
      fi
    fi
  done
  
  if command -v gcc >/dev/null 2>&1; then
    local gcc_path=$(command -v gcc)
    log_info "通过环境变量发现GCC: $gcc_path"
    echo "$gcc_path"
    return 0
  fi
  
  log_info "未发现GCC可执行文件"
  echo ""
  return 1
}

# 解析GCC版本号
parse_gcc_version() {
  local gcc_path=$1
  
  if [ -z "$gcc_path" ] || [ ! -x "$gcc_path" ]; then
    log_error "无效的GCC路径: $gcc_path"
    echo "0"
    return 1
  fi
  
  log_info "解析GCC版本: $gcc_path"
  
  local version_output=$("$gcc_path" --version 2>/dev/null | head -n1)
  local version=$(echo "$version_output" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1 | cut -d'.' -f1)
  
  if [ -z "$version" ] || ! [[ "$version" =~ ^[0-9]+$ ]]; then
    log_error "无法解析GCC版本,输出: $version_output"
    echo "0"
    return 1
  fi
  
  log_info "解析到GCC主版本号: $version"
  echo "$version"
  return 0
}

# 检查GCC版本并升级
check_and_upgrade_gcc() {
  local required_version=${1:-9}
  log_info "🔍 检查GCC版本(要求: $required_version 及以上)..."
  
  export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$PATH
  log_info "当前环境变量PATH: $PATH"
  
  local gcc_path=$(search_gcc_paths)
  local gcc_version=0
  
  if [ -n "$gcc_path" ]; then
    gcc_version=$(parse_gcc_version "$gcc_path")
    
    if ! [[ "$gcc_version" =~ ^[0-9]+$ ]]; then
      log_error "获取的GCC版本不是有效整数: $gcc_version"
      gcc_version=0
    fi
  fi
  
  if [ "$gcc_version" -ge "$required_version" ]; then
    log_info "✅ GCC版本满足要求 ($gcc_version >= $required_version)"
    echo "$gcc_version" > /tmp/gcc_version.txt
    return 0
  elif [ "$gcc_version" -gt 0 ]; then
    log_info "❌ GCC版本过低 ($gcc_version < $required_version),需要升级"
  else
    log_info "❌ 未安装有效GCC,将进行安装"
  fi
  
  install_gcc "$required_version"
  return $?
}

# 安装高版本GCC
install_gcc() {
  local required_version=$1
  log_info "🔧 开始安装GCC $required_version 或更高版本..."
  
  case $OS in
    "CentOS"|"Red Hat"|"Oracle Linux")
      if install_gcc_rhel "$required_version"; then
        return 0
      fi
      ;;
    "Ubuntu"|"Debian")
      if install_gcc_debian "$required_version"; then
        return 0
      fi
      ;;
    "SUSE Linux"|"openSUSE")
      if install_gcc_suse "$required_version"; then
        return 0
      fi
      ;;
    *)
      log_warn "未识别的系统: $OS,尝试通用安装方法"
      ;;
  esac
  
  if install_gcc_from_source "$required_version"; then
    return 0
  else
    log_error "所有GCC安装方式均失败"
    return 1
  fi
}

# RHEL系系统安装GCC
install_gcc_rhel() {
  local required_version=$1
  log_info "尝试在RHEL系系统安装GCC $required_version..."
  
  if [ "$PACKAGE_MANAGER" = "yum" ]; then
    yum remove -y gcc gcc-c++ >/dev/null 2>&1 || true
  else
    dnf remove -y gcc gcc-c++ >/dev/null 2>&1 || true
  fi
  
  if [ "$required_version" -le 10 ]; then
    log_info "尝试通过Software Collections安装GCC $required_version..."
    
    if ! $PACKAGE_MANAGER install -y centos-release-scl-rh 2>/dev/null; then
      log_info "尝试备选SCL仓库..."
      if ! $PACKAGE_MANAGER install -y centos-release-scl 2>/dev/null; then
        log_warn "SCL仓库安装失败,尝试其他方法"
      else
        local scl_package="devtoolset-$required_version-gcc"
        if ! $PACKAGE_MANAGER install -y "$scl_package" "$scl_package-c++" "devtoolset-$required_version-binutils"; then
          log_warn "安装devtoolset-$required_version失败,尝试更高版本"
        else
          source /opt/rh/devtoolset-$required_version/enable
          ln -sf /opt/rh/devtoolset-$required_version/root/usr/bin/gcc /usr/local/bin/gcc
          ln -sf /opt/rh/devtoolset-$required_version/root/usr/bin/g++ /usr/local/bin/g++
          
          log_info "✅ 成功安装GCC $required_version (SCL方式)"
          echo "$required_version" > /tmp/gcc_version.txt
          return 0
        fi
      fi
    fi
  fi
  
  log_info "尝试通过默认仓库安装GCC..."
  if $PACKAGE_MANAGER install -y gcc gcc-c++; then
    local new_version=$(parse_gcc_version "$(command -v gcc)")
    if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; then
      log_info "✅ 成功安装GCC $new_version (默认仓库)"
      echo "$new_version" > /tmp/gcc_version.txt
      return 0
    else
      log_warn "默认仓库安装的GCC版本不足,需要从源码编译"
    fi
  else
    log_warn "默认仓库安装GCC失败"
  fi
  
  return 1
}

# Debian/Ubuntu系系统安装GCC
install_gcc_debian() {
  local required_version=$1
  log_info "尝试在Debian/Ubuntu系系统安装GCC $required_version..."
  
  retry_command 3 apt-get update -y
  
  apt-get remove -y gcc gcc-c++ >/dev/null 2>&1 || true
  
  local gcc_package="gcc-$required_version"
  local gpp_package="g++-$required_version"
  
  if apt-get install -y "$gcc_package" "$gpp_package"; then
    update-alternatives --install /usr/bin/gcc gcc /usr/bin/"$gcc_package" 100
    update-alternatives --install /usr/bin/g++ g++ /usr/bin/"$gpp_package" 100
    
    local new_version=$(parse_gcc_version "$(command -v gcc)")
    if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; then
      log_info "✅ 成功安装GCC $new_version (apt方式)"
      echo "$new_version" > /tmp/gcc_version.txt
      return 0
    fi
  else
    log_warn "安装GCC $required_version 失败,尝试安装默认版本"
    
    if apt-get install -y gcc g++; then
      local new_version=$(parse_gcc_version "$(command -v gcc)")
      if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; then
        log_info "✅ 成功安装GCC $new_version (apt方式)"
        echo "$new_version" > /tmp/gcc_version.txt
        return 0
      else
        log_warn "默认仓库安装的GCC版本不足,需要从源码编译"
      fi
    else
      log_warn "apt安装GCC失败"
    fi
  fi
  
  return 1
}

# SUSE系系统安装GCC
install_gcc_suse() {
  local required_version=$1
  log_info "尝试在SUSE系系统安装GCC $required_version..."
  
  if zypper install -y gcc-$required_version gcc-c++-$required_version; then
    local new_version=$(parse_gcc_version "$(command -v gcc)")
    if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; then
      log_info "✅ 成功安装GCC $new_version (zypper方式)"
      echo "$new_version" > /tmp/gcc_version.txt
      return 0
    fi
  else
    log_warn "安装GCC $required_version 失败,尝试安装默认版本"
    
    if zypper install -y gcc gcc-c++; then
      local new_version=$(parse_gcc_version "$(command -v gcc)")
      if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; then
        log_info "✅ 成功安装GCC $new_version (zypper方式)"
        echo "$new_version" > /tmp/gcc_version.txt
        return 0
      else
        log_warn "默认仓库安装的GCC版本不足,需要从源码编译"
      fi
    else
      log_warn "zypper安装GCC失败"
    fi
  fi
  
  return 1
}

# 从源码编译安装GCC
install_gcc_from_source() {
  local required_version=$1
  local install_version="9.4.0"
  if [ "$required_version" -gt 9 ]; then
    install_version="10.5.0"
  fi
  if [ "$required_version" -gt 10 ]; then
    install_version="11.4.0"
  fi
  if [ "$required_version" -gt 11 ]; then
    install_version="12.3.0"
  fi
  
  log_info "尝试从源码编译安装GCC ${install_version}..."
  
  log_info "安装GCC编译依赖..."
  case $OS in
    "CentOS"|"Red Hat"|"Oracle Linux")
      $PACKAGE_MANAGER install -y gmp-devel mpfr-devel libmpc-devel wget make lbzip2 bzip2 patch
      ;;
    "Ubuntu"|"Debian")
      apt-get install -y libgmp-dev libmpfr-dev libmpc-dev wget make lbzip2 bzip2 patch
      ;;
    "SUSE Linux"|"openSUSE")
      zypper install -y gmp-devel mpfr-devel libmpc-devel wget make lbzip2 bzip2 patch
      ;;
    *)
      log_warn "未识别系统,尝试安装常见依赖"
      if command -v apt-get >/dev/null 2>&1; then
        apt-get install -y libgmp-dev libmpfr-dev libmpc-dev wget make lbzip2 bzip2 patch
      elif command -v yum >/dev/null 2>&1; then
        yum install -y gmp-devel mpfr-devel libmpc-devel wget make lbzip2 bzip2 patch
      fi
      ;;
  esac
  
  local gcc_workdir="/tmp/gcc-build"
  local gcc_tar="gcc-${install_version}.tar.gz"
  local gcc_major_version=$(echo "$install_version" | cut -d'.' -f1)
  
  rm -rf "$gcc_workdir"
  mkdir -p "$gcc_workdir"
  cd "$gcc_workdir"
  
  # 下载GCC源码
  local mirrors=(
    "http://gnu.mirror.constant.com/gcc/gcc-${install_version}/${gcc_tar}"
    "https://mirror.lzu.edu.cn/gnu/gcc/gcc-${install_version}/${gcc_tar}"
    "https://mirrors.ustc.edu.cn/gnu/gcc/gcc-${install_version}/${gcc_tar}"
    "https://ftp.gnu.org/gnu/gcc/gcc-${install_version}/${gcc_tar}"
  )
  
  local download_success=0
  for mirror in "${mirrors[@]}"; do
    log_info "尝试从镜像下载GCC ${install_version}$mirror"
    if retry_command 2 wget --no-check-certificate "$mirror"; then
      log_info "✅ 从镜像下载成功"
      download_success=1
      break
    fi
  done
  
  if [ $download_success -eq 0 ]; then
    log_error "所有镜像下载失败"
    return 1
  fi
  
  log_info "解压GCC源码..."
  tar xzf "$gcc_tar" || {
    log_error "源码解压失败,文件可能损坏"
    return 1
  }
  
  cd "gcc-${install_version}"
  
  log_info "准备GCC依赖项..."
  if ! ./contrib/download_prerequisites; then
    log_error "自动下载依赖失败,尝试手动下载..."
    wget ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2 && tar xjf gmp-6.1.0.tar.bz2 && ln -sf gmp-6.1.0 gmp
    wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.4.tar.bz2 && tar xjf mpfr-3.1.4.tar.bz2 && ln -sf mpfr-3.1.4 mpfr
    wget ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz && tar xzf mpc-1.0.3.tar.gz && ln -sf mpc-1.0.3 mpc
    wget ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 && tar xjf isl-0.18.tar.bz2 && ln -sf isl-0.18 isl
  fi
  
  mkdir -p build
  cd build
  
  log_info "配置GCC编译选项..."
  ../configure --prefix=/usr/local/gcc-${install_version} \
               --enable-bootstrap \
               --enable-shared \
               --enable-threads=posix \
               --enable-checking=release \
               --with-system-zlib \
               --enable-__cxa_atexit \
               --disable-libunwind-exceptions \
               --enable-gnu-unique-object \
               --enable-linker-build-id \
               --with-gcc-major-version-only \
               --enable-libstdcxx-dual-abi \
               --enable-languages=c,c++ \
               --disable-multilib
  
  log_info "开始编译GCC(这可能需要1-2小时)..."
  local cpu_cores=$(nproc)
  local make_jobs=$(( cpu_cores > 4 ? 4 : cpu_cores ))
  log_info "使用 $make_jobs 线程进行编译"
  
  if ! make -j$make_jobs; then
    log_error "多线程编译失败,尝试单线程编译..."
    make -j1
  fi
  
  log_info "安装GCC..."
  make install || {
    log_error "GCC安装失败"
    return 1
  }
  
  log_info "配置系统使用新GCC..."
  ln -sf /usr/local/gcc-${install_version}/bin/gcc /usr/local/bin/gcc
  ln -sf /usr/local/gcc-${install_version}/bin/g++ /usr/local/bin/g++
  ln -sf /usr/local/gcc-${install_version}/bin/gcc /usr/local/bin/cc
  ln -sf /usr/local/gcc-${install_version}/bin/g++ /usr/local/bin/c++
  
  echo "/usr/local/gcc-${install_version}/lib64" > /etc/ld.so.conf.d/gcc-${install_version}.conf
  ldconfig 2>/dev/null
  
  log_info "✅ GCC源码编译安装成功,版本: $gcc_major_version"
  echo "$gcc_major_version" > /tmp/gcc_version.txt
  return 0
}

# 检查是否为 root
if [ "$EUID" -ne 0 ]; then
  log_error "请以 root 权限运行此脚本"
  exit 1
fi

# 设置变量
WORK_DIR="/root/open-webui"
VENV_DIR="${WORK_DIR}/venv"  # 虚拟环境目录
PYTHON_DIR="/usr/local/python3.11"
SYSTEM_PYTHON_BIN="${PYTHON_DIR}/bin/python3.11"
PYTHON_BIN="${VENV_DIR}/bin/python"  # 使用虚拟环境的python
PIP_BIN="${VENV_DIR}/bin/pip"        # 使用虚拟环境的pip
OPENSSL_DIR="/usr/local/openssl-3.1.4"
OPENSSL_TGZ="openssl-3.1.4.tar.gz"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
LOCAL_OPENSSL_TGZ="${SCRIPT_DIR}/${OPENSSL_TGZ}"
WEBUI_PORT=9000
PIP_MIRROR="https://pypi.tuna.tsinghua.edu.cn/simple"
PIP_TIMEOUT=120

# 禁用失效仓库
disable_broken_repos() {
  log_info "🚫 禁用失效的仓库..."
  if command -v yum-config-manager >/dev/null 2>&1; then
    yum-config-manager --disable centos-sclo-rh || true
    yum-config-manager --disable centos-sclo-sclo || true
    yum-config-manager --disable docker-ce-stable || true
    yum-config-manager --disable yarn || true
    yum-config-manager --save --setopt=*.skip_if_unavailable=true || true
  fi
}

# 替换镜像源为 CentOS Vault
setup_centos_vault() {
  if [[ "$OS" == "CentOS" && "$VERSION" == "7"* ]]; then
    log_info "🔧 替换CentOS 7镜像源为 Vault..."
    cd /etc/yum.repos.d
    mv CentOS-Base.repo CentOS-Base.repo.bak 2>/dev/null || true

    cat > CentOS-Base.repo << 'EOL'
[base]
name=CentOS-7.9.2009 - Base
baseurl=https://vault.centos.org/7.9.2009/os/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[updates]
name=CentOS-7.9.2009 - Updates
baseurl=https://vault.centos.org/7.9.2009/updates/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[extras]
name=CentOS-7.9.2009 - Extras
baseurl=https://vault.centos.org/7.9.2009/extras/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[centosplus]
name=CentOS-7.9.2009 - Plus
baseurl=https://vault.centos.org/7.9.2009/centosplus/x86_64/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=0
EOL

    yum clean all
    yum makecache
  fi
}

# 安装系统依赖(新增Perl模块)
install_system_dependencies() {
  log_info "🧰 安装系统依赖..."
  case $OS in
    "CentOS"|"Red Hat"|"Oracle Linux")
      $PACKAGE_MANAGER install -y epel-release gcc make perl perl-IPC-Cmd perl-Error zlib-devel bzip2-devel \
        readline-devel sqlite-devel libffi-devel xz-devel wget curl
      ;;
    "Ubuntu"|"Debian")
      apt-get install -y build-essential libssl-dev zlib1g-dev libbz2-dev \
        libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \
        xz-utils tk-dev libffi-dev liblzma-dev perl libipc-cmd-perl
      ;;
    "SUSE Linux"|"openSUSE")
      zypper install -y gcc make zlib-devel bzip2-devel readline-devel sqlite3-devel \
        libffi-devel xz-devel wget curl perl perl-IPC-Cmd
      ;;
    *)
      log_warn "未识别系统,尝试安装常见依赖"
      ;;
  esac
}

# 创建Python虚拟环境
create_venv() {
  log_info "🔧 创建Python虚拟环境..."
  
  # 如果虚拟环境已存在,先删除
  if [ -d "$VENV_DIR" ]; then
    log_info "虚拟环境已存在,重新创建..."
    rm -rf "$VENV_DIR"
  fi
  
  # 使用系统Python创建虚拟环境
  if ! $SYSTEM_PYTHON_BIN -m venv "$VENV_DIR"; then
    log_error "创建虚拟环境失败,尝试安装venv模块..."
    
    # 安装venv所需包
    if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then
      $PACKAGE_MANAGER install -y python3-venv
    elif [ "$PACKAGE_MANAGER" = "apt-get" ]; then
      apt-get install -y python3-venv
    fi
    
    # 再次尝试创建
    if ! $SYSTEM_PYTHON_BIN -m venv "$VENV_DIR"; then
      log_error "创建虚拟环境失败,无法继续安装"
      exit 1
    fi
  fi
  
  log_info "✅ 虚拟环境创建成功: $VENV_DIR"
}

# 准备 OpenSSL 安装包
prepare_openssl_package() {
  cd "$WORK_DIR"

  local expected_md5="653ad58812c751b887e8ec37e02bba70"

  if [ -f "$LOCAL_OPENSSL_TGZ" ]; then
    log_info "📂 发现脚本同级目录存在OpenSSL安装包:$LOCAL_OPENSSL_TGZ"
    
    local actual_md5=$(md5sum "$LOCAL_OPENSSL_TGZ" | awk '{print $1}')
    log_info "本地包MD5: $actual_md5,预期MD5: $expected_md5"
    
    if [ "$actual_md5" = "$expected_md5" ]; then
      log_info "✅ MD5校验通过,直接使用本地包"
      cp "$LOCAL_OPENSSL_TGZ" "$WORK_DIR/"
      return 0
    else
      log_warn "❌ MD5校验不匹配,忽略本地包"
      rm -f "$WORK_DIR/$OPENSSL_TGZ"
    fi
  else
    log_info "ℹ️ 脚本同级目录未找到OpenSSL安装包"
  fi

  if [ -f "$WORK_DIR/$OPENSSL_TGZ" ]; then
    log_info "📂 发现工作目录存在OpenSSL安装包"
    
    local actual_md5=$(md5sum "$WORK_DIR/$OPENSSL_TGZ" | awk '{print $1}')
    if [ "$actual_md5" = "$expected_md5" ]; then
      log_info "✅ MD5校验通过,使用工作目录包"
      return 0
    else
      log_warn "❌ 工作目录包损坏,重新下载"
      rm -f "$WORK_DIR/$OPENSSL_TGZ"
    fi
  fi

  log_info "📥 开始在线下载OpenSSL安装包..."
  download_openssl
}

# 下载 OpenSSL
download_openssl() {
  cd "$WORK_DIR"
  local openssl_url="https://github.com/openssl/openssl/releases/download/openssl-3.1.4/openssl-3.1.4.tar.gz"

  if [ -f "$OPENSSL_TGZ" ]; then
    rm -f "$OPENSSL_TGZ"
  fi

  MAX_RETRY=5
  RETRY_DELAY=10
  for i in $(seq 1 $MAX_RETRY); do
    log_info "尝试从官方地址下载(第 $i/$MAX_RETRY 次): $openssl_url"

    if wget --no-check-certificate -q --show-progress "$openssl_url"; then
      log_info "✅ 下载成功"
      return 0
    else
      log_error "❌ 第 $i 次下载失败,$RETRY_DELAY 秒后重试..."
      sleep $RETRY_DELAY
      RETRY_DELAY=$((RETRY_DELAY + 5))
    fi
  done

  log_error "❌ 达到最大重试次数,下载失败"
  log_error "请手动下载 openssl-3.1.4.tar.gz 并放在脚本同级目录后重试"
  exit 1
}

# OpenSSL安装函数(修复lib64目录识别问题)
install_openssl() {
  log_info "🔐 开始安装 OpenSSL 3.1.4..."

  if [ -d "$OPENSSL_DIR" ]; then
    rm -rf "$OPENSSL_DIR"
  fi

  cd "$WORK_DIR"
  prepare_openssl_package

  # 清理之前可能存在的解压目录
  rm -rf openssl-3.1.4
  
  tar xzf "$OPENSSL_TGZ" || {
    log_error "❌ 安装包解压失败"
    exit 1
  }
  cd openssl-3.1.4

  # 确保Perl模块可用
  log_info "检查Perl模块IPC::Cmd是否可用..."
  if ! perl -e "use IPC::Cmd;"; then
    log_error "Perl模块IPC::Cmd仍然缺失,尝试手动安装..."
    
    # 针对不同系统尝试手动安装缺失的Perl模块
    if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then
      $PACKAGE_MANAGER install -y perl-IPC-Cmd
    elif [ "$PACKAGE_MANAGER" = "apt-get" ]; then
      apt-get install -y libipc-cmd-perl
    elif [ "$PACKAGE_MANAGER" = "zypper" ]; then
      zypper install -y perl-IPC-Cmd
    else
      # 尝试CPAN安装作为最后的手段
      log_info "尝试通过CPAN安装IPC::Cmd..."
      perl -MCPAN -e 'install IPC::Cmd'
    fi
    
    # 再次检查
    if ! perl -e "use IPC::Cmd;"; then
      log_error "❌ 无法安装Perl模块IPC::Cmd,无法继续"
      exit 1
    fi
  fi

  ./Configure linux-x86_64 --prefix="$OPENSSL_DIR" --openssldir="$OPENSSL_DIR/etc" shared zlib
  make -j$(nproc)
  make install_sw

  log_info "🔗 配置 OpenSSL 环境变量和pkg-config..."
  
  # 检测是lib还是lib64目录
  if [ -d "$OPENSSL_DIR/lib64" ]; then
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"
    log_info "检测到OpenSSL使用lib64目录结构"
  else
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib"
    log_info "检测到OpenSSL使用lib目录结构"
  fi
  
  # 配置pkg-config以优先使用我们安装的OpenSSL
  if [ -d /usr/lib64/pkgconfig ]; then
    # 备份并移除系统默认的openssl.pc
    mv /usr/lib64/pkgconfig/openssl.pc /usr/lib64/pkgconfig/openssl.pc.bak 2>/dev/null || true
    # 创建指向我们安装的openssl.pc的软链接
    ln -sf "$OPENSSL_LIB_DIR/pkgconfig/openssl.pc" /usr/lib64/pkgconfig/openssl.pc
  fi
  
  if [ -d /usr/lib/pkgconfig ]; then
    # 备份并移除系统默认的openssl.pc
    mv /usr/lib/pkgconfig/openssl.pc /usr/lib/pkgconfig/openssl.pc.bak 2>/dev/null || true
    # 创建指向我们安装的openssl.pc的软链接
    ln -sf "$OPENSSL_LIB_DIR/pkgconfig/openssl.pc" /usr/lib/pkgconfig/openssl.pc
  fi
  
  # 配置环境变量,使用正确的lib目录
  export PKG_CONFIG_PATH="$OPENSSL_LIB_DIR/pkgconfig:$PKG_CONFIG_PATH"
  export LD_LIBRARY_PATH="$OPENSSL_LIB_DIR:$LD_LIBRARY_PATH"
  export PATH="$OPENSSL_DIR/bin:$PATH"
  echo "$OPENSSL_LIB_DIR" > /etc/ld.so.conf.d/openssl.conf
  ldconfig
  
  # 替换系统默认的openssl
  rm -f /usr/bin/openssl
  ln -sf "$OPENSSL_DIR/bin/openssl" /usr/bin/openssl
  
  # 验证OpenSSL版本和pkg-config配置
  log_info "当前系统默认OpenSSL版本: $(openssl version | awk '{print $2}')"
  
  # 检查openssl.pc是否存在
  if [ -f "$OPENSSL_LIB_DIR/pkgconfig/openssl.pc" ]; then
    log_info "openssl.pc文件存在: $OPENSSL_LIB_DIR/pkgconfig/openssl.pc"
    log_info "pkg-config检测到的OpenSSL版本: $(pkg-config --modversion openssl 2>/dev/null || echo "未检测到")"
  else
    log_error "openssl.pc文件不存在于预期位置: $OPENSSL_LIB_DIR/pkgconfig/openssl.pc"
    log_error "OpenSSL安装可能不完整"
    exit 1
  fi
}

# OpenSSL安装主函数
setup_compatible_openssl() {
  log_info "🔒 开始OpenSSL环境配置..."
  
  # 先校验是否兼容
  if check_openssl_version; then
    return 0
  fi
  
  # 不兼容或未安装则安装新版
  install_openssl
  
  # 再次验证
  check_openssl_version || {
    log_error "OpenSSL配置后仍不兼容,无法继续安装"
    exit 1
  }
}

# 检查 Python SSL 支持
check_python_ssl() {
  log_info "🔍 检查 Python SSL 支持..."

  if $PYTHON_BIN -c "import ssl; print('SSL 支持正常:', ssl.OPENSSL_VERSION)" 2>/dev/null; then
    log_info "✅ Python SSL 支持正常"
    return 0
  else
    log_error "❌ Python SSL 支持异常"
    return 1
  fi
}

# 安装 Python 3.11
install_python() {
  if [ -x "$SYSTEM_PYTHON_BIN" ]; then
    log_info "✅ Python 3.11 已安装"
    
    # 创建虚拟环境
    create_venv
    
    # 检查虚拟环境中的Python SSL支持
    if check_python_ssl; then
      return 0
    else
      log_error "虚拟环境中Python SSL支持异常,尝试重新安装Python"
    fi
  fi

  log_info "📥 安装 Python 3.11..."

  cd "$WORK_DIR"
  PYTHON_TGZ="Python-3.11.0.tgz"
  local expected_py_size=26006345
  
  if [ ! -f "$PYTHON_TGZ" ]; then
    log_info "未发现本地Python源码包,开始下载..."
    if ! retry_command 3 curl -LO --retry 3 --connect-timeout 10 https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz; then
      log_error "❌ Python 源码包下载失败"
      exit 1
    fi
  else
    local actual_py_size=$(stat -c%s "$PYTHON_TGZ" 2>/dev/null || echo 0)
    if [ "$actual_py_size" -ne "$expected_py_size" ]; then
      log_info "本地Python源码包不完整,重新下载..."
      rm -f "$PYTHON_TGZ"
      if ! retry_command 3 curl -LO --retry 3 --connect-timeout 10 https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz; then
        log_error "❌ Python 源码包下载失败"
        exit 1
      fi
    else
      log_info "本地Python源码包完整,直接使用"
    fi
  fi

  if [ ! -d "Python-3.11.0" ]; then
    log_info "解压Python源码..."
    tar xzf "$PYTHON_TGZ"
  else
    log_info "Python源码已解压,跳过解压步骤"
  fi
  
  chown -R root:root "Python-3.11.0"
  
  cd Python-3.11.0

  # 检测OpenSSL库目录
  if [ -d "$OPENSSL_DIR/lib64" ]; then
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"
  else
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib"
  fi

  CPPFLAGS="-I$OPENSSL_DIR/include" \
  LDFLAGS="-L$OPENSSL_LIB_DIR -Wl,-rpath,$OPENSSL_LIB_DIR -Wl,--enable-new-dtags" \
  ./configure --prefix="$PYTHON_DIR" \
              --enable-optimizations \
              --with-ssl="$OPENSSL_DIR" \
              --enable-ipv6 \
              --with-ensurepip=install

  make -j$(nproc)
  make install

  log_info "🔗 创建 Python 软链接..."
  ln -sf "$PYTHON_DIR/bin/python3.11" /usr/local/bin/python3
  ln -sf "$PYTHON_DIR/bin/pip3.11" /usr/local/bin/pip3

  chown -R root:root "$PYTHON_DIR"

  # 创建虚拟环境
  create_venv

  check_python_ssl || {
    log_error "❌ Python SSL 支持修复失败"
    exit 1
  }
}

# 检查 Python 版本
check_python_version() {
  log_info "🔍 验证 Python 版本..."
  
  if ! command -v "$PYTHON_BIN" &>/dev/null; then
    log_error "未找到 Python3"
    exit 1
  fi
  
  PYTHON_VERSION=$($PYTHON_BIN -c 'import sys; print(".".join(map(str, sys.version_info[:2])))')
  log_info "当前 Python 版本: $PYTHON_VERSION"
  
  if [ "$(echo "$PYTHON_VERSION >= 3.11" | bc)" -ne 1 ]; then
    log_error "需要 Python 3.11 或更高版本"
    exit 1
  fi
}

# 配置pip镜像源
configure_pip_mirror() {
  log_info "🔧 配置 pip 镜像源为 $PIP_MIRROR..."
  mkdir -p /root/.pip
  cat > /root/.pip/pip.conf << EOF
[global]
index-url = $PIP_MIRROR
trusted-host = $(echo $PIP_MIRROR | awk -F/ '{print $3}')
timeout = $PIP_TIMEOUT
EOF
  
  # 同时在虚拟环境中配置pip
  mkdir -p "${VENV_DIR}/pip"
  cat > "${VENV_DIR}/pip/pip.conf" << EOF
[global]
index-url = $PIP_MIRROR
trusted-host = $(echo $PIP_MIRROR | awk -F/ '{print $3}')
timeout = $PIP_TIMEOUT
EOF
}
# 在install_open_webui函数前新增安装Apache Arrow的函数
install_apache_arrow() {
  log_info "📦 安装Apache Arrow及Dataset组件(pyarrow需要)..."
  
  case $OS in
    "CentOS"|"Red Hat"|"Oracle Linux")
      # 安装EPEL仓库(忽略已安装提示)
      yum install -y epel-release || true
      
      # 安装Arrow仓库配置(忽略已安装提示)
      curl -fsSL https://apache.jfrog.io/artifactory/arrow/centos/$(rpm -E %rhel)/apache-arrow-release-latest.rpm -o apache-arrow-release.rpm
      yum install -y ./apache-arrow-release.rpm || true
      rm -f apache-arrow-release.rpm
      
      # 强制刷新仓库缓存
      yum clean all
      yum makecache fast
      
      # 关键:安装所有必需的开发包,包括Dataset组件
      log_info "安装Arrow核心库和Dataset组件..."
      yum install -y arrow-devel arrow-glib-devel arrow-dataset-devel
      ;;
      
    # 保持其他系统的安装逻辑不变
    "Ubuntu"|"Debian")
      # ... 原有代码 ...
      ;;
    "SUSE Linux"|"openSUSE")
      # ... 原有代码 ...
      ;;
  esac
  
  # 验证ArrowDataset是否安装成功
  if pkg-config --exists arrow-dataset; then
    log_info "✅ Apache Arrow及Dataset组件安装成功"
    return 0
  else
    log_error "❌ Apache Arrow Dataset组件安装失败"
    # 尝试手动安装作为最后的手段
    log_info "尝试手动编译安装Apache Arrow..."
    install_arrow_from_source
    return $?
  fi
}

# 新增:从源码安装Apache Arrow的函数(当包管理器安装失败时)
install_arrow_from_source() {
  local arrow_version="14.0.1"
  log_info "从源码安装Apache Arrow $arrow_version..."
  
  # 安装编译依赖
  yum install -y cmake gcc-c++ git python3-devel boost-devel rapidjson-devel
  
  # 创建临时目录
  local temp_dir=$(mktemp -d)
  cd "$temp_dir" || {
    log_error "无法进入临时目录"
    return 1
  }
  
  # 克隆源码
  git clone --branch apache-arrow-$arrow_version https://github.com/apache/arrow.git
  cd arrow/cpp || {
    log_error "找不到Arrow源码目录"
    return 1
  }
  
  # 创建编译目录
  mkdir build && cd build
  
  # 配置编译选项
  cmake .. \
    -DCMAKE_INSTALL_PREFIX=/usr/local \
    -DARROW_DATASET=ON \
    -DARROW_PYTHON=ON \
    -DARROW_WITH_BZ2=ON \
    -DARROW_WITH_ZLIB=ON \
    -DARROW_WITH_ZSTD=ON \
    -DCMAKE_BUILD_TYPE=Release
  
  # 编译并安装
  make -j$(nproc)
  make install
  
  # 刷新动态链接库
  ldconfig
  
  # 清理临时文件
  cd ../../../../ && rm -rf "$temp_dir"
  
  # 验证安装
  if pkg-config --exists arrow-dataset; then
    log_info "✅ 源码安装Apache Arrow成功"
    return 0
  else
    log_error "❌ 源码安装Apache Arrow失败"
    return 1
  fi
}


# 安装 Open WebUI(强制使用新FFmpeg)
install_open_webui() {
  configure_pip_mirror
  
  log_info "📦 通过 pip 安装 Open WebUI..."
  pip_install_with_retry "pip" "--upgrade"
  
  log_info "📦 安装兼容版本的numpy..."
  pip_install_with_retry "numpy==1.26.4" "--no-cache-dir"
  
  # 安装Apache Arrow系统依赖
  install_apache_arrow
  
  # 安装av包
  log_info "📦 安装av包(强制关联新FFmpeg)..." 
  export PKG_CONFIG_PATH="/usr/local/ffmpeg/lib/pkgconfig:$PKG_CONFIG_PATH"
  export CFLAGS="-I/usr/local/ffmpeg/include"
  export LDFLAGS="-L/usr/local/ffmpeg/lib -Wl,-rpath=/usr/local/ffmpeg/lib"
  pip_install_with_retry "av==10.0.0" "--no-cache-dir"
  
  # 使用预编译的pyarrow wheel包
  log_info "📦 安装pyarrow(优先使用预编译包)..."
  export CMAKE_PREFIX_PATH="/usr/local:/usr"
  
  # 尝试直接安装预编译wheel
  if ! pip_install_with_retry "pyarrow" "--no-cache-dir"; then
    log_warn "预编译包安装失败,尝试指定较低版本..."
    # 若失败,尝试已知兼容的版本
    pip_install_with_retry "pyarrow==14.0.1" "--no-cache-dir"
  fi
  
  # 安装open-webui
  pip_install_with_retry "open-webui" "--no-cache-dir"
}

# 配置系统服务(修复端口配置错误)
configure_service() {
  log_info "⚙️ 配置 systemd 服务(端口: $WEBUI_PORT)..."
  
  OPEN_WEBUI_BIN="${VENV_DIR}/bin/open-webui"
  
  GCC_VERSION=$(cat /tmp/gcc_version.txt 2>/dev/null || echo "9")
  
  # 检测OpenSSL库目录
  if [ -d "$OPENSSL_DIR/lib64" ]; then
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"
  else
    OPENSSL_LIB_DIR="$OPENSSL_DIR/lib"
  fi
  
  cat > /etc/systemd/system/open-webui.service << EOF
[Unit]
Description=Open WebUI Service
After=network.target

[Service]
User=root
Environment="PATH=/usr/local/bin:/usr/bin:${PYTHON_DIR}/bin:${VENV_DIR}/bin"
Environment="LD_LIBRARY_PATH=${OPENSSL_LIB_DIR}:/usr/local/gcc-${GCC_VERSION}.4.0/lib64:/usr/local/ffmpeg/lib"
Environment="PKG_CONFIG_PATH=/usr/local/ffmpeg/lib/pkgconfig:${OPENSSL_LIB_DIR}/pkgconfig"
ExecStart=${OPEN_WEBUI_BIN} serve --port $WEBUI_PORT
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

  log_info "🚀 启动 Open WebUI 服务(端口: $WEBUI_PORT)..."
  systemctl daemon-reload
  systemctl enable open-webui --now
  
  if systemctl is-active --quiet open-webui; then
    log_info "✅ Open WebUI 服务已启动"
  else
    log_error "❌ Open WebUI 服务启动失败"
    log_error "查看日志: journalctl -u open-webui -n 50"
    exit 1
  fi
}

# 显示安装信息
show_info() {
  log_info "🎉 安装完成!"
  log_info "🌐 访问地址: http://<你的服务器IP>:$WEBUI_PORT"
  log_info "📄 服务状态: systemctl status open-webui"
  log_info "📜 日志查看: journalctl -u open-webui -f"
  log_info "🔄 重启服务: systemctl restart open-webui"
  log_info "📈 升级服务: ${PIP_BIN} install --upgrade open-webui"
  log_info "🔍 虚拟环境位置: ${VENV_DIR}"
}

# 主函数(新增依赖检测步骤)
main() {
  log_info "🔧 开始安装 Open WebUI(将使用 $WEBUI_PORT 端口)..."
  
  detect_system
  
  mkdir -p "$WORK_DIR"
  cd "$WORK_DIR"

  # 1. 检查并升级GCC
  check_and_upgrade_gcc 9 || {
    log_error "GCC版本检查和升级失败,无法继续安装"
    exit 1
  }

  # 2. 检查并升级CMake(新增步骤)
  log_info "开始CMake环境配置..."
  if ! check_cmake_version; then
    upgrade_cmake || {
      log_error "CMake版本检查和升级失败,无法继续安装"
      exit 1
    }
  fi

  # 3. 检查并安装OpenSSL
  setup_compatible_openssl || {
    log_error "OpenSSL版本检查和安装失败,无法继续安装"
    exit 1
  }

  # 4. 其他系统配置
  disable_broken_repos
  setup_centos_vault
  install_system_dependencies
  
  # 5. 配置FFmpeg
  setup_compatible_ffmpeg
  
  # 6. 安装并配置Python
  install_python
  check_python_version
  
  # 7. 安装Open WebUI及依赖
  install_open_webui
  
  # 8. 配置服务并启动
  configure_service
  show_info
}

main

安装

./install_open_webui.sh

如果openssl-3.1.4.tar.gz 文件下载特别慢可以将 openssl-3.1.4.tar.gz 文件手动下载上传到脚本同级目录,脚本已支持离线安装

安装中发现磁盘空间不够满了(清除了一遍发现空间还是有点少),中途进行了一次Linux磁盘的扩容:https://blog.youkuaiyun.com/YXWik/article/details/149566023

验证openssl的MD5

md5sum openssl-3.1.4.tar.gz

如果MD5值与脚本中配置的不一致,需要手动将查出来的md值写到脚本
在这里插入图片描述

因为我的服务器环境什么都没有,所以脚本更适用于什么都没有的空环境,虽然已经适配了各个软件存在的情况,但是没有经过充分的验证,会存在一些意想不到的问题

以下问题是出在安装包时的一个特殊要求:它强制要求在虚拟环境中安装,而当前脚本是在系统全局环境中运行的。要解决这个问题,我们需要修改安装逻辑,为 Open WebUI 创建一个专用的 Python 虚拟环境。问题已在脚本中处理,在此记录一下,安装Open WebUI需要给它虚拟环境
在这里插入图片描述

FFmpeg 启用 GPL 许可功能时,要求 OpenSSL 版本≥3.0.0,但当前安装的是 OpenSSL 1.1.1w(低于 3.0.0),导致兼容性冲突,我这里将 FFmpegGPL 许可功能禁用掉了,结果禁用了出现一大堆问题,没办法,只能升级openssl的版本
在这里插入图片描述

在脚本运行安装的途中,我决定再次使用docker安装尝试一下

在这里插入图片描述

docker run -d -p 3000:8080  -e OLLAMA_BASE_URL=http://host.docker.internal:11434 -v open-webui:/app/backend/data --name open-webui --restart always ghcr.nju.edu.cn/open-webui/open-webui:main

结果很意外,很快很快就成功了,上次可能网络不佳(折腾好几天)
在这里插入图片描述
查看日志,等如下图启动成功就可以访问了(需要等一段时间才能启动成功)

docker logs -f open-webui

在这里插入图片描述
如果访问不到开放下端口

iptables -I INPUT -p tcp --dport 3000 -j  ACCEPT

自行注册一下账号
在这里插入图片描述
启动成功了,但是我发现我的open-webui 容器中是访问不到ollama的
在这里插入图片描述
但是ollama在浏览器这种外部环境是可以访问的
在这里插入图片描述
这种情况是因为ollama proxy 网络代理问题
卸载重装ollama

docker stop ollama
docker rm ollama
docker run -d   --name ollama   -v /home/ollama:/root/.ollama   -p 11434:11434   -e OLLAMA_HOST=0.0.0.0   --add-host=host.docker.internal:host-gateway   --restart always   ollama/ollama
# 验证
docker exec -it ollama env | grep OLLAMA_HOST
docker exec -it ollama ollama run deepseek-r1:7b

在这里插入图片描述
然后再重装 open-webui (我这里重装是因为我之前的 open-webui 安装时指定的OLLAMA_BASE_URL 是http://localhost:11434 ,要更改为 ollama的IP+端口)

docker stop open-webui
docker rm open-webui
docker run -d   -p 3000:8080   -e OLLAMA_BASE_URL=http://192.168.0.180:11434   -v open-webui:/app/backend/data   --name open-webui   --restart always   ghcr.nju.edu.cn/open-webui/open-webui:main
# 验证
 docker exec -it open-webui curl http://192.168.0.180:11434/api/tags

在这里插入图片描述
这里就正常完成啦
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值