Linux(centos7)安装 docker + ollama+ deepseek-r1:7b + Open WebUI(内含一键安装脚本)_linux安装deepseek web ui
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\"}# 检查是否为 rootif [ \"$EUID\" -ne 0 ]; then log_error \"请以 root 权限运行此脚本\" exit 1fi# 安装依赖if ! command -v curl &> /dev/null; then log_info \"📥 安装 curl...\" yum install -y curl || apt-get update && apt-get install -y curlfi# 设置 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 fielse log_info \"✅ Docker 已安装\"fi# 确保 Docker 正在运行if ! systemctl is-active --quiet docker; then log_info \"🔄 Docker 未运行,正在启动...\" systemctl start dockerfi# 创建 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/nullfi# 启动 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 1fi# 下载模型(可修改为你需要的模型)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://:11434\"log_info \"👉 Open WebUI 地址:http://: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 - $basearchbaseurl=https://mirrors.aliyun.com/docker-ce/linux/centos/7/$basearch/stableenabled=1gpgcheck=1gpgkey=https://mirrors.aliyun.com/docker-ce/linux/centos/gpgEOF# 更新 yum 缓存yum makecache# 安装必要软件包echo \"正在安装依赖...\"yum install -y yum-utils device-mapper-persistent-data lvm2# 安装 Docker CEecho \"正在安装 Docker CE...\"yum install -y docker-ce docker-ce-cli containerd.io# 启动 Docker 服务并设置开机自启echo \"启动 Docker 服务...\"systemctl start dockersystemctl enable docker# 验证 Docker 是否安装成功docker --versionsystemctl 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-reloadsystemctl 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/bashset -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 $?}# 安装高版本GCCinstall_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系系统安装GCCinstall_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系系统安装GCCinstall_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系系统安装GCCinstall_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}# 从源码编译安装GCCinstall_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}# 检查是否为 rootif [ \"$EUID\" -ne 0 ]; then log_error \"请以 root 权限运行此脚本\" exit 1fi# 设置变量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\" # 使用虚拟环境的pythonPIP_BIN=\"${VENV_DIR}/bin/pip\" # 使用虚拟环境的pipOPENSSL_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=9000PIP_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 Vaultsetup_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 - Basebaseurl=https://vault.centos.org/7.9.2009/os/x86_64/gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7enabled=1[updates]name=CentOS-7.9.2009 - Updatesbaseurl=https://vault.centos.org/7.9.2009/updates/x86_64/gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7enabled=1[extras]name=CentOS-7.9.2009 - Extrasbaseurl=https://vault.centos.org/7.9.2009/extras/x86_64/gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7enabled=1[centosplus]name=CentOS-7.9.2009 - Plusbaseurl=https://vault.centos.org/7.9.2009/centosplus/x86_64/gpgcheck=1gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7enabled=0EOL 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}# 下载 OpenSSLdownload_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.11install_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_MIRRORtrusted-host = $(echo $PIP_MIRROR | awk -F/ \'{print $3}\')timeout = $PIP_TIMEOUTEOF # 同时在虚拟环境中配置pip mkdir -p \"${VENV_DIR}/pip\" cat > \"${VENV_DIR}/pip/pip.conf\" << EOF[global]index-url = $PIP_MIRRORtrusted-host = $(echo $PIP_MIRROR | awk -F/ \'{print $3}\')timeout = $PIP_TIMEOUTEOF}# 在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 ServiceAfter=network.target[Service]User=rootEnvironment=\"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_PORTRestart=alwaysRestartSec=5[Install]WantedBy=multi-user.targetEOF 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://:$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.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 ollamadocker rm ollamadocker 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_HOSTdocker exec -it ollama ollama run deepseek-r1:7b
然后再重装 open-webui (我这里重装是因为我之前的 open-webui 安装时指定的OLLAMA_BASE_URL 是http://localhost:11434 ,要更改为 ollama的IP+端口)
docker stop open-webuidocker rm open-webuidocker 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
这里就正常完成啦