Linux(centos7)安装 docker + ollama+ deepseek-r1:7b + Open WebUI(内含一键安装脚本)
windows版本的 ollama :https://blog.csdn.net/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
的官方二进制文件是使用较新的 glibc
和 GCC
编译的,它要求:
glibc
>= 2.27
libstdc++
>= 4.8.5(最好 GCC 9+)
系统 glibc
和 libstdc++
版本太旧,需要更新
系统又安装一些其他程序无法升级,只升级这两个依赖又害怕搞出兼容性的问题
更新半天费劲巴拉的放弃了,采用docker
安装得了
以下是一个完整的 Bash 脚本,用于在 CentOS 7 系统上:
-
检测是否安装 Docker
未安装:自动安装并启动 Docker
-
检查 Docker 是否运行
未运行:启动 Docker 服务
-
检查是否已有 Ollama 容器
有:尝试启动它(如果未运行)
无:拉取并运行 Ollama 容器
注: 此脚本会更改服务器的下载源/etc/yum.repos.d
配置 会更改docker
的 daemon.json
配置
脚本中 /home/ollama
挂载到容器内的 /root/.ollama
,是用于持久化模型数据,如有需要自行更改路径
ollama的脚本处理完后我就想着deepseek模型
(想下载其他模型的也只需要更换脚本下载模型处的 deepseek-r1:7b 即可)和Open WebUI
也放到脚本中自动安装启动,这之后就整理个一键安装脚本得了,以下脚本为一键安装脚本(经过多次优化已验证无误,安装open webui巨慢,不想等或者不需要的可以装完模型就结束)
install_ollama.sh
#!/bin/bashset -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 ]; thenlog_error "请以 root 权限运行此脚本"exit 1
fi# 安装依赖
if ! command -v curl &> /dev/null; thenlog_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; thenlog_info "🔍 Docker 未安装,正在调用 install_docker.sh 安装..."if [ -f "./install_docker.sh" ]; thenchmod +x ./install_docker.sh./install_docker.shelselog_error "❌ 未找到 install_docker.sh 脚本,正在安装采用其他方式安装 Docker"curl -fsSL https://get.docker.com | shsystemctl enable docker --nowexit 1fi
elselog_info "✅ Docker 已安装"
fi# 确保 Docker 正在运行
if ! systemctl is-active --quiet docker; thenlog_info "🔄 Docker 未运行,正在启动..."systemctl start docker
fi# 创建 Ollama 数据目录
DATA_DIR="/home/ollama"
if [ ! -d "$DATA_DIR" ]; thenlog_info "📁 创建 Ollama 数据目录:$DATA_DIR"mkdir -p "$DATA_DIR"
fi# 检查是否已存在 ollama 容器
if docker inspect ollama > /dev/null 2>&1; thenlog_info "🔄 检测到已存在的 ollama 容器,正在停止并删除..."docker stop ollama > /dev/nulldocker 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; thenlog_info "✅ Ollama 服务启动成功"
elselog_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 dockerecho "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.csdn.net/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=$1shiftlocal command=("$@")local retry_delay=5for ((retry=1; retry<=max_retries; retry++)); doif "${command[@]}"; thenreturn 0fiif [ $retry -lt $max_retries ]; thenlog_info "命令执行失败,$retry_delay 秒后重试(第 $retry/$max_retries 次)..."sleep $retry_delayretry_delay=$((retry_delay + 5))fidonelog_error "命令执行失败,已达到最大重试次数"return 1
}# 带重试的pip安装函数
pip_install_with_retry() {local full_package=$1local max_retries=3local retry_delay=10local extra_args=${@:2}# 提取包名(去掉版本号部分)local package=$(echo "$full_package" | sed -E 's/[=<>~].*//')for ((retry=1; retry<=max_retries; retry++)); dolog_info "尝试安装 $full_package(第 $retry/$max_retries 次)"if $PIP_BIN install $extra_args $full_package; thenlog_info "$full_package 安装成功"return 0fiif [ $retry -lt $max_retries ]; thenlog_info "安装失败,$retry_delay 秒后重试..."sleep $retry_delayretry_delay=$((retry_delay + 5))fidonelog_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-releaseOS=$NAMEVERSION=$VERSION_IDif [[ "$OS" == "CentOS Linux" ]]; thenOS="CentOS"fielif type lsb_release >/dev/null 2>&1; thenOS=$(lsb_release -si)VERSION=$(lsb_release -sr)elif [ -f /etc/lsb-release ]; then. /etc/lsb-releaseOS=$DISTRIB_IDVERSION=$DISTRIB_RELEASEelif [ -f /etc/debian_version ]; thenOS=DebianVERSION=$(cat /etc/debian_version)elif [ -f /etc/redhat-release ]; thenOS=$(cat /etc/redhat-release | cut -d' ' -f1)VERSION=$(cat /etc/redhat-release | grep -oE '[0-9]+\.[0-9]+' | head -1)elseOS=$(uname -s)VERSION=$(uname -r)fi# 检测包管理器if command -v yum >/dev/null 2>&1; thenPACKAGE_MANAGER="yum"elif command -v dnf >/dev/null 2>&1; thenPACKAGE_MANAGER="dnf"elif command -v apt-get >/dev/null 2>&1; thenPACKAGE_MANAGER="apt-get"elif command -v zypper >/dev/null 2>&1; thenPACKAGE_MANAGER="zypper"elsePACKAGE_MANAGER="unknown"filog_info "检测到系统: $OS $VERSION,包管理器: $PACKAGE_MANAGER"
}# 检测OpenSSL版本
check_openssl_version() {log_info "🔍 检测OpenSSL版本(要求>=3.0.0)..."# 检查OpenSSL是否安装if ! command -v openssl &>/dev/null; thenlog_info "未安装OpenSSL,需要安装3.0.0及以上版本"return 1fi# 获取完整版本号(如3.1.4)local openssl_version=$(openssl version | awk '{print $2}')if [ -z "$openssl_version" ]; thenlog_warn "无法解析OpenSSL版本,视为不兼容"return 1fi# 提取主版本号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.0if [ "$major_version" -ge 3 ] && [ "$minor_version" -ge 0 ]; thenlog_info "✅ OpenSSL版本兼容(>=3.0.0)"return 0elselog_warn "❌ OpenSSL版本过低($openssl_version < 3.0.0),需要升级"return 1fi
}# 校验FFmpeg版本是否兼容(必须>=3.1)
check_ffmpeg_compatibility() {log_info "🔍 校验FFmpeg版本兼容性(要求>=3.1)..."# 检查FFmpeg是否安装if ! command -v ffmpeg &>/dev/null; thenlog_info "未安装FFmpeg,需要安装兼容版本"return 1fi# 获取主版本号(如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" ]; thenlog_warn "无法解析FFmpeg版本,视为不兼容"return 1filog_info "当前FFmpeg主版本号: $ffmpeg_version"# 检查是否>=3if [ "$ffmpeg_version" -ge 3 ]; thenlog_info "✅ FFmpeg版本兼容(>=3.1)"return 0elselog_warn "❌ FFmpeg版本过低($ffmpeg_version < 3),需要升级"return 1fi
}# 检测CMake版本(新增函数)
check_cmake_version() {local required_version="3.25"log_info "🔍 检测CMake版本(要求>=${required_version})..."# 检查CMake是否安装if ! command -v cmake &>/dev/null; thenlog_info "未安装CMake,需要安装${required_version}及以上版本"return 1fi# 获取完整版本号local cmake_version=$(cmake --version | head -n1 | awk '{print $3}')if [ -z "$cmake_version" ]; thenlog_warn "无法解析CMake版本,视为不兼容"return 1filog_info "当前CMake版本: $cmake_version"# 检查是否>=required_versionif version_ge "$cmake_version" "$required_version"; thenlog_info "✅ CMake版本兼容(>=${required_version})"return 0elselog_warn "❌ CMake版本过低($cmake_version < ${required_version}),需要升级"return 1fi
}# 卸载旧版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 || trueelif [ "$PACKAGE_MANAGER" = "apt-get" ]; thenapt-get remove -y ffmpeg libavcodec-dev libavformat-dev >/dev/null 2>&1 || trueelif [ "$PACKAGE_MANAGER" = "zypper" ]; thenzypper remove -y ffmpeg ffmpeg-devel >/dev/null 2>&1 || truefi# 清理残留文件(关键步骤)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/nulllog_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 || trueelif [ "$PACKAGE_MANAGER" = "apt-get" ]; thenapt-get remove -y cmake >/dev/null 2>&1 || trueelif [ "$PACKAGE_MANAGER" = "zypper" ]; thenzypper remove -y cmake >/dev/null 2>&1 || truefi# 安装编译依赖log_info "安装CMake编译依赖..."if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then$PACKAGE_MANAGER install -y gcc-c++ make openssl-develelif [ "$PACKAGE_MANAGER" = "apt-get" ]; thenapt-get install -y g++ make libssl-develif [ "$PACKAGE_MANAGER" = "zypper" ]; thenzypper install -y gcc-c++ make libopenssl-develfi# 检测系统架构local arch=$(uname -m)case $arch inx86_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"; thenlog_warn "主链接下载失败,尝试备用链接..."if ! retry_command 2 wget --no-check-certificate "$backup_url"; thenlog_error "所有链接下载CMake失败"cd .. && rm -rf "$temp_dir"exit 1fifi# 验证文件是否存在if [ ! -f "$cmake_tar" ]; thenlog_error "CMake安装包下载失败,文件不存在"cd .. && rm -rf "$temp_dir"exit 1fi# 解压源码包log_info "解压CMake安装包..."if ! tar xzf "$cmake_tar"; thenlog_error "CMake安装包解压失败"cd .. && rm -rf "$temp_dir"exit 1fi# 进入源码目录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/cmakeln -sf /opt/cmake/bin/cmake /usr/bin/cmakeln -sf /opt/cmake/bin/cmake /usr/local/bin/cmake# 清理临时文件cd ../.. && rm -rf "$temp_dir"# 验证安装if ! command -v cmake &> /dev/null; thenlog_error "CMake安装失败,无法找到可执行文件"exit 1fi# 再次检查版本if check_cmake_version; thenlog_info "✅ CMake升级成功"return 0elselog_error "CMake升级后仍不满足版本要求"exit 1fi
}# FFmpeg安装函数(修复OpenSSL识别问题)
install_compatible_ffmpeg() {log_info "🔧 安装兼容版本FFmpeg 5.1.3(与av包兼容)..."# 检测OpenSSL是lib还是lib64目录if [ -d "$OPENSSL_DIR/lib64" ]; thenOPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"elseOPENSSL_LIB_DIR="$OPENSSL_DIR/lib"fi# 预检查OpenSSL是否被正确识别log_info "预检查OpenSSL配置..."if ! pkg-config --exists openssl; thenlog_error "pkg-config仍然无法检测到OpenSSL"log_error "请检查$OPENSSL_LIB_DIR/pkgconfig/openssl.pc是否存在"exit 1filocal openssl_version=$(pkg-config --modversion openssl)log_info "pkg-config报告的OpenSSL版本: $openssl_version"# 检查主版本是否>=3local major_version=$(echo "$openssl_version" | cut -d'.' -f1)if [ "$major_version" -lt 3 ]; thenlog_error "pkg-config报告的OpenSSL版本仍然低于3.0.0"exit 1filocal 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-develelif [ "$PACKAGE_MANAGER" = "apt-get" ]; thenapt-get install -y yasm nasm make cmake zlib1g-dev wgetapt-get remove -y openssl libssl-develif [ "$PACKAGE_MANAGER" = "zypper" ]; thenzypper install -y yasm nasm make cmake zlib-devel wgetzypper remove -y openssl openssl-develfi# 创建临时编译目录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=0for mirror in "${mirrors[@]}"; dolog_info "尝试从镜像下载FFmpeg ${ffmpeg_version}:$mirror"if retry_command 2 wget --no-check-certificate "$mirror"; thenlog_info "✅ 从镜像下载成功"download_success=1breakfidoneif [ $download_success -eq 0 ]; thenlog_error "所有镜像下载FFmpeg失败"exit 1fi# 解压源码log_info "解压FFmpeg源码..."if ! tar xjf "$ffmpeg_tar"; thenlog_error "源码解压失败,文件可能损坏"exit 1ficd "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; thenlog_error "多线程编译失败,尝试单线程编译..."if ! make -j1; thenlog_error "FFmpeg编译彻底失败"exit 1fifi# 安装log_info "安装FFmpeg..."make install# 配置系统识别新FFmpeglog_info "配置FFmpeg环境变量..."# 1. 添加动态链接库路径echo "$install_prefix/lib" > /etc/ld.so.conf.d/ffmpeg.confldconfig # 刷新缓存# 2. 创建软链接到系统路径(确保能被找到)ln -sf "$install_prefix/bin/ffmpeg" /usr/bin/ffmpegln -sf "$install_prefix/bin/ffprobe" /usr/bin/ffprobe# 3. 配置pkg-config路径(av包编译需要)ln -sf "$install_prefix/lib/pkgconfig"/* /usr/lib64/pkgconfig/ 2>/dev/nullln -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" ]; thenlog_info "✅ FFmpeg $ffmpeg_version 安装成功"elselog_error "FFmpeg安装版本不符(实际: $installed_version,预期: $ffmpeg_version)"exit 1fi
}# 安装并验证FFmpeg(整合校验、卸载旧版、安装新版)
setup_compatible_ffmpeg() {log_info "🎥 开始FFmpeg环境配置..."# 先校验是否兼容if check_ffmpeg_compatibility; thenreturn 0fi# 不兼容则卸载旧版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[@]}"; doif [[ $path == *"*"* ]]; thenfor matched_path in $(echo $path 2>/dev/null | sort -Vr); doif [ -x "$matched_path" ]; thenlog_info "发现可执行的GCC: $matched_path"echo "$matched_path"return 0fidoneelseif [ -x "$path" ]; thenlog_info "发现可执行的GCC: $path"echo "$path"return 0fifidoneif command -v gcc >/dev/null 2>&1; thenlocal gcc_path=$(command -v gcc)log_info "通过环境变量发现GCC: $gcc_path"echo "$gcc_path"return 0filog_info "未发现GCC可执行文件"echo ""return 1
}# 解析GCC版本号
parse_gcc_version() {local gcc_path=$1if [ -z "$gcc_path" ] || [ ! -x "$gcc_path" ]; thenlog_error "无效的GCC路径: $gcc_path"echo "0"return 1filog_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]+$ ]]; thenlog_error "无法解析GCC版本,输出: $version_output"echo "0"return 1filog_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:$PATHlog_info "当前环境变量PATH: $PATH"local gcc_path=$(search_gcc_paths)local gcc_version=0if [ -n "$gcc_path" ]; thengcc_version=$(parse_gcc_version "$gcc_path")if ! [[ "$gcc_version" =~ ^[0-9]+$ ]]; thenlog_error "获取的GCC版本不是有效整数: $gcc_version"gcc_version=0fifiif [ "$gcc_version" -ge "$required_version" ]; thenlog_info "✅ GCC版本满足要求 ($gcc_version >= $required_version)"echo "$gcc_version" > /tmp/gcc_version.txtreturn 0elif [ "$gcc_version" -gt 0 ]; thenlog_info "❌ GCC版本过低 ($gcc_version < $required_version),需要升级"elselog_info "❌ 未安装有效GCC,将进行安装"fiinstall_gcc "$required_version"return $?
}# 安装高版本GCC
install_gcc() {local required_version=$1log_info "🔧 开始安装GCC $required_version 或更高版本..."case $OS in"CentOS"|"Red Hat"|"Oracle Linux")if install_gcc_rhel "$required_version"; thenreturn 0fi;;"Ubuntu"|"Debian")if install_gcc_debian "$required_version"; thenreturn 0fi;;"SUSE Linux"|"openSUSE")if install_gcc_suse "$required_version"; thenreturn 0fi;;*)log_warn "未识别的系统: $OS,尝试通用安装方法";;esacif install_gcc_from_source "$required_version"; thenreturn 0elselog_error "所有GCC安装方式均失败"return 1fi
}# RHEL系系统安装GCC
install_gcc_rhel() {local required_version=$1log_info "尝试在RHEL系系统安装GCC $required_version..."if [ "$PACKAGE_MANAGER" = "yum" ]; thenyum remove -y gcc gcc-c++ >/dev/null 2>&1 || trueelsednf remove -y gcc gcc-c++ >/dev/null 2>&1 || truefiif [ "$required_version" -le 10 ]; thenlog_info "尝试通过Software Collections安装GCC $required_version..."if ! $PACKAGE_MANAGER install -y centos-release-scl-rh 2>/dev/null; thenlog_info "尝试备选SCL仓库..."if ! $PACKAGE_MANAGER install -y centos-release-scl 2>/dev/null; thenlog_warn "SCL仓库安装失败,尝试其他方法"elselocal scl_package="devtoolset-$required_version-gcc"if ! $PACKAGE_MANAGER install -y "$scl_package" "$scl_package-c++" "devtoolset-$required_version-binutils"; thenlog_warn "安装devtoolset-$required_version失败,尝试更高版本"elsesource /opt/rh/devtoolset-$required_version/enableln -sf /opt/rh/devtoolset-$required_version/root/usr/bin/gcc /usr/local/bin/gccln -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.txtreturn 0fifififilog_info "尝试通过默认仓库安装GCC..."if $PACKAGE_MANAGER install -y gcc gcc-c++; thenlocal new_version=$(parse_gcc_version "$(command -v gcc)")if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; thenlog_info "✅ 成功安装GCC $new_version (默认仓库)"echo "$new_version" > /tmp/gcc_version.txtreturn 0elselog_warn "默认仓库安装的GCC版本不足,需要从源码编译"fielselog_warn "默认仓库安装GCC失败"fireturn 1
}# Debian/Ubuntu系系统安装GCC
install_gcc_debian() {local required_version=$1log_info "尝试在Debian/Ubuntu系系统安装GCC $required_version..."retry_command 3 apt-get update -yapt-get remove -y gcc gcc-c++ >/dev/null 2>&1 || truelocal gcc_package="gcc-$required_version"local gpp_package="g++-$required_version"if apt-get install -y "$gcc_package" "$gpp_package"; thenupdate-alternatives --install /usr/bin/gcc gcc /usr/bin/"$gcc_package" 100update-alternatives --install /usr/bin/g++ g++ /usr/bin/"$gpp_package" 100local new_version=$(parse_gcc_version "$(command -v gcc)")if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; thenlog_info "✅ 成功安装GCC $new_version (apt方式)"echo "$new_version" > /tmp/gcc_version.txtreturn 0fielselog_warn "安装GCC $required_version 失败,尝试安装默认版本"if apt-get install -y gcc g++; thenlocal new_version=$(parse_gcc_version "$(command -v gcc)")if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; thenlog_info "✅ 成功安装GCC $new_version (apt方式)"echo "$new_version" > /tmp/gcc_version.txtreturn 0elselog_warn "默认仓库安装的GCC版本不足,需要从源码编译"fielselog_warn "apt安装GCC失败"fifireturn 1
}# SUSE系系统安装GCC
install_gcc_suse() {local required_version=$1log_info "尝试在SUSE系系统安装GCC $required_version..."if zypper install -y gcc-$required_version gcc-c++-$required_version; thenlocal new_version=$(parse_gcc_version "$(command -v gcc)")if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; thenlog_info "✅ 成功安装GCC $new_version (zypper方式)"echo "$new_version" > /tmp/gcc_version.txtreturn 0fielselog_warn "安装GCC $required_version 失败,尝试安装默认版本"if zypper install -y gcc gcc-c++; thenlocal new_version=$(parse_gcc_version "$(command -v gcc)")if [ -n "$new_version" ] && [ "$new_version" -ge "$required_version" ]; thenlog_info "✅ 成功安装GCC $new_version (zypper方式)"echo "$new_version" > /tmp/gcc_version.txtreturn 0elselog_warn "默认仓库安装的GCC版本不足,需要从源码编译"fielselog_warn "zypper安装GCC失败"fifireturn 1
}# 从源码编译安装GCC
install_gcc_from_source() {local required_version=$1local install_version="9.4.0"if [ "$required_version" -gt 9 ]; theninstall_version="10.5.0"fiif [ "$required_version" -gt 10 ]; theninstall_version="11.4.0"fiif [ "$required_version" -gt 11 ]; theninstall_version="12.3.0"filog_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; thenapt-get install -y libgmp-dev libmpfr-dev libmpc-dev wget make lbzip2 bzip2 patchelif command -v yum >/dev/null 2>&1; thenyum install -y gmp-devel mpfr-devel libmpc-devel wget make lbzip2 bzip2 patchfi;;esaclocal 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=0for mirror in "${mirrors[@]}"; dolog_info "尝试从镜像下载GCC ${install_version}:$mirror"if retry_command 2 wget --no-check-certificate "$mirror"; thenlog_info "✅ 从镜像下载成功"download_success=1breakfidoneif [ $download_success -eq 0 ]; thenlog_error "所有镜像下载失败"return 1filog_info "解压GCC源码..."tar xzf "$gcc_tar" || {log_error "源码解压失败,文件可能损坏"return 1}cd "gcc-${install_version}"log_info "准备GCC依赖项..."if ! ./contrib/download_prerequisites; thenlog_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 gmpwget 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 mpfrwget 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 mpcwget ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 && tar xjf isl-0.18.tar.bz2 && ln -sf isl-0.18 islfimkdir -p buildcd buildlog_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-multiliblog_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; thenlog_error "多线程编译失败,尝试单线程编译..."make -j1filog_info "安装GCC..."make install || {log_error "GCC安装失败"return 1}log_info "配置系统使用新GCC..."ln -sf /usr/local/gcc-${install_version}/bin/gcc /usr/local/bin/gccln -sf /usr/local/gcc-${install_version}/bin/g++ /usr/local/bin/g++ln -sf /usr/local/gcc-${install_version}/bin/gcc /usr/local/bin/ccln -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}.confldconfig 2>/dev/nulllog_info "✅ GCC源码编译安装成功,版本: $gcc_major_version"echo "$gcc_major_version" > /tmp/gcc_version.txtreturn 0
}# 检查是否为 root
if [ "$EUID" -ne 0 ]; thenlog_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; thenyum-config-manager --disable centos-sclo-rh || trueyum-config-manager --disable centos-sclo-sclo || trueyum-config-manager --disable docker-ce-stable || trueyum-config-manager --disable yarn || trueyum-config-manager --save --setopt=*.skip_if_unavailable=true || truefi
}# 替换镜像源为 CentOS Vault
setup_centos_vault() {if [[ "$OS" == "CentOS" && "$VERSION" == "7"* ]]; thenlog_info "🔧 替换CentOS 7镜像源为 Vault..."cd /etc/yum.repos.dmv CentOS-Base.repo CentOS-Base.repo.bak 2>/dev/null || truecat > 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
EOLyum clean allyum makecachefi
}# 安装系统依赖(新增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" ]; thenlog_info "虚拟环境已存在,重新创建..."rm -rf "$VENV_DIR"fi# 使用系统Python创建虚拟环境if ! $SYSTEM_PYTHON_BIN -m venv "$VENV_DIR"; thenlog_error "创建虚拟环境失败,尝试安装venv模块..."# 安装venv所需包if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then$PACKAGE_MANAGER install -y python3-venvelif [ "$PACKAGE_MANAGER" = "apt-get" ]; thenapt-get install -y python3-venvfi# 再次尝试创建if ! $SYSTEM_PYTHON_BIN -m venv "$VENV_DIR"; thenlog_error "创建虚拟环境失败,无法继续安装"exit 1fifilog_info "✅ 虚拟环境创建成功: $VENV_DIR"
}# 准备 OpenSSL 安装包
prepare_openssl_package() {cd "$WORK_DIR"local expected_md5="653ad58812c751b887e8ec37e02bba70"if [ -f "$LOCAL_OPENSSL_TGZ" ]; thenlog_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" ]; thenlog_info "✅ MD5校验通过,直接使用本地包"cp "$LOCAL_OPENSSL_TGZ" "$WORK_DIR/"return 0elselog_warn "❌ MD5校验不匹配,忽略本地包"rm -f "$WORK_DIR/$OPENSSL_TGZ"fielselog_info "ℹ️ 脚本同级目录未找到OpenSSL安装包"fiif [ -f "$WORK_DIR/$OPENSSL_TGZ" ]; thenlog_info "📂 发现工作目录存在OpenSSL安装包"local actual_md5=$(md5sum "$WORK_DIR/$OPENSSL_TGZ" | awk '{print $1}')if [ "$actual_md5" = "$expected_md5" ]; thenlog_info "✅ MD5校验通过,使用工作目录包"return 0elselog_warn "❌ 工作目录包损坏,重新下载"rm -f "$WORK_DIR/$OPENSSL_TGZ"fifilog_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" ]; thenrm -f "$OPENSSL_TGZ"fiMAX_RETRY=5RETRY_DELAY=10for i in $(seq 1 $MAX_RETRY); dolog_info "尝试从官方地址下载(第 $i/$MAX_RETRY 次): $openssl_url"if wget --no-check-certificate -q --show-progress "$openssl_url"; thenlog_info "✅ 下载成功"return 0elselog_error "❌ 第 $i 次下载失败,$RETRY_DELAY 秒后重试..."sleep $RETRY_DELAYRETRY_DELAY=$((RETRY_DELAY + 5))fidonelog_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" ]; thenrm -rf "$OPENSSL_DIR"ficd "$WORK_DIR"prepare_openssl_package# 清理之前可能存在的解压目录rm -rf openssl-3.1.4tar xzf "$OPENSSL_TGZ" || {log_error "❌ 安装包解压失败"exit 1}cd openssl-3.1.4# 确保Perl模块可用log_info "检查Perl模块IPC::Cmd是否可用..."if ! perl -e "use IPC::Cmd;"; thenlog_error "Perl模块IPC::Cmd仍然缺失,尝试手动安装..."# 针对不同系统尝试手动安装缺失的Perl模块if [ "$PACKAGE_MANAGER" = "yum" ] || [ "$PACKAGE_MANAGER" = "dnf" ]; then$PACKAGE_MANAGER install -y perl-IPC-Cmdelif [ "$PACKAGE_MANAGER" = "apt-get" ]; thenapt-get install -y libipc-cmd-perlelif [ "$PACKAGE_MANAGER" = "zypper" ]; thenzypper install -y perl-IPC-Cmdelse# 尝试CPAN安装作为最后的手段log_info "尝试通过CPAN安装IPC::Cmd..."perl -MCPAN -e 'install IPC::Cmd'fi# 再次检查if ! perl -e "use IPC::Cmd;"; thenlog_error "❌ 无法安装Perl模块IPC::Cmd,无法继续"exit 1fifi./Configure linux-x86_64 --prefix="$OPENSSL_DIR" --openssldir="$OPENSSL_DIR/etc" shared zlibmake -j$(nproc)make install_swlog_info "🔗 配置 OpenSSL 环境变量和pkg-config..."# 检测是lib还是lib64目录if [ -d "$OPENSSL_DIR/lib64" ]; thenOPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"log_info "检测到OpenSSL使用lib64目录结构"elseOPENSSL_LIB_DIR="$OPENSSL_DIR/lib"log_info "检测到OpenSSL使用lib目录结构"fi# 配置pkg-config以优先使用我们安装的OpenSSLif [ -d /usr/lib64/pkgconfig ]; then# 备份并移除系统默认的openssl.pcmv /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.pcfiif [ -d /usr/lib/pkgconfig ]; then# 备份并移除系统默认的openssl.pcmv /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.pcfi# 配置环境变量,使用正确的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.confldconfig# 替换系统默认的opensslrm -f /usr/bin/opensslln -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" ]; thenlog_info "openssl.pc文件存在: $OPENSSL_LIB_DIR/pkgconfig/openssl.pc"log_info "pkg-config检测到的OpenSSL版本: $(pkg-config --modversion openssl 2>/dev/null || echo "未检测到")"elselog_error "openssl.pc文件不存在于预期位置: $OPENSSL_LIB_DIR/pkgconfig/openssl.pc"log_error "OpenSSL安装可能不完整"exit 1fi
}# OpenSSL安装主函数
setup_compatible_openssl() {log_info "🔒 开始OpenSSL环境配置..."# 先校验是否兼容if check_openssl_version; thenreturn 0fi# 不兼容或未安装则安装新版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; thenlog_info "✅ Python SSL 支持正常"return 0elselog_error "❌ Python SSL 支持异常"return 1fi
}# 安装 Python 3.11
install_python() {if [ -x "$SYSTEM_PYTHON_BIN" ]; thenlog_info "✅ Python 3.11 已安装"# 创建虚拟环境create_venv# 检查虚拟环境中的Python SSL支持if check_python_ssl; thenreturn 0elselog_error "虚拟环境中Python SSL支持异常,尝试重新安装Python"fifilog_info "📥 安装 Python 3.11..."cd "$WORK_DIR"PYTHON_TGZ="Python-3.11.0.tgz"local expected_py_size=26006345if [ ! -f "$PYTHON_TGZ" ]; thenlog_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; thenlog_error "❌ Python 源码包下载失败"exit 1fielselocal actual_py_size=$(stat -c%s "$PYTHON_TGZ" 2>/dev/null || echo 0)if [ "$actual_py_size" -ne "$expected_py_size" ]; thenlog_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; thenlog_error "❌ Python 源码包下载失败"exit 1fielselog_info "本地Python源码包完整,直接使用"fifiif [ ! -d "Python-3.11.0" ]; thenlog_info "解压Python源码..."tar xzf "$PYTHON_TGZ"elselog_info "Python源码已解压,跳过解压步骤"fichown -R root:root "Python-3.11.0"cd Python-3.11.0# 检测OpenSSL库目录if [ -d "$OPENSSL_DIR/lib64" ]; thenOPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"elseOPENSSL_LIB_DIR="$OPENSSL_DIR/lib"fiCPPFLAGS="-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=installmake -j$(nproc)make installlog_info "🔗 创建 Python 软链接..."ln -sf "$PYTHON_DIR/bin/python3.11" /usr/local/bin/python3ln -sf "$PYTHON_DIR/bin/pip3.11" /usr/local/bin/pip3chown -R root:root "$PYTHON_DIR"# 创建虚拟环境create_venvcheck_python_ssl || {log_error "❌ Python SSL 支持修复失败"exit 1}
}# 检查 Python 版本
check_python_version() {log_info "🔍 验证 Python 版本..."if ! command -v "$PYTHON_BIN" &>/dev/null; thenlog_error "未找到 Python3"exit 1fiPYTHON_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 ]; thenlog_error "需要 Python 3.11 或更高版本"exit 1fi
}# 配置pip镜像源
configure_pip_mirror() {log_info "🔧 配置 pip 镜像源为 $PIP_MIRROR..."mkdir -p /root/.pipcat > /root/.pip/pip.conf << EOF
[global]
index-url = $PIP_MIRROR
trusted-host = $(echo $PIP_MIRROR | awk -F/ '{print $3}')
timeout = $PIP_TIMEOUT
EOF# 同时在虚拟环境中配置pipmkdir -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.rpmyum install -y ./apache-arrow-release.rpm || truerm -f apache-arrow-release.rpm# 强制刷新仓库缓存yum clean allyum 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; thenlog_info "✅ Apache Arrow及Dataset组件安装成功"return 0elselog_error "❌ Apache Arrow Dataset组件安装失败"# 尝试手动安装作为最后的手段log_info "尝试手动编译安装Apache Arrow..."install_arrow_from_sourcereturn $?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.gitcd 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; thenlog_info "✅ 源码安装Apache Arrow成功"return 0elselog_error "❌ 源码安装Apache Arrow失败"return 1fi
}# 安装 Open WebUI(强制使用新FFmpeg)
install_open_webui() {configure_pip_mirrorlog_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"# 尝试直接安装预编译wheelif ! pip_install_with_retry "pyarrow" "--no-cache-dir"; thenlog_warn "预编译包安装失败,尝试指定较低版本..."# 若失败,尝试已知兼容的版本pip_install_with_retry "pyarrow==14.0.1" "--no-cache-dir"fi# 安装open-webuipip_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" ]; thenOPENSSL_LIB_DIR="$OPENSSL_DIR/lib64"elseOPENSSL_LIB_DIR="$OPENSSL_DIR/lib"ficat > /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
EOFlog_info "🚀 启动 Open WebUI 服务(端口: $WEBUI_PORT)..."systemctl daemon-reloadsystemctl enable open-webui --nowif systemctl is-active --quiet open-webui; thenlog_info "✅ Open WebUI 服务已启动"elselog_error "❌ Open WebUI 服务启动失败"log_error "查看日志: journalctl -u open-webui -n 50"exit 1fi
}# 显示安装信息
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_systemmkdir -p "$WORK_DIR"cd "$WORK_DIR"# 1. 检查并升级GCCcheck_and_upgrade_gcc 9 || {log_error "GCC版本检查和升级失败,无法继续安装"exit 1}# 2. 检查并升级CMake(新增步骤)log_info "开始CMake环境配置..."if ! check_cmake_version; thenupgrade_cmake || {log_error "CMake版本检查和升级失败,无法继续安装"exit 1}fi# 3. 检查并安装OpenSSLsetup_compatible_openssl || {log_error "OpenSSL版本检查和安装失败,无法继续安装"exit 1}# 4. 其他系统配置disable_broken_repossetup_centos_vaultinstall_system_dependencies# 5. 配置FFmpegsetup_compatible_ffmpeg# 6. 安装并配置Pythoninstall_pythoncheck_python_version# 7. 安装Open WebUI及依赖install_open_webui# 8. 配置服务并启动configure_serviceshow_info
}main
安装
./install_open_webui.sh
如果openssl-3.1.4.tar.gz 文件下载特别慢可以将 openssl-3.1.4.tar.gz 文件手动下载上传到脚本同级目录,脚本已支持离线安装
安装中发现磁盘空间不够满了(清除了一遍发现空间还是有点少),中途进行了一次Linux磁盘的扩容:https://blog.csdn.net/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),导致兼容性冲突
,我这里将 FFmpeg
的 GPL
许可功能禁用
掉了,结果禁用了出现一大堆问题,没办法,只能升级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
这里就正常完成啦