#!/usr/bin/env bash
# ============================================================
# Dify + RAGFlow 一键部署脚本 for CentOS 7/8/9 / Stream / Alibaba Cloud Linux
#
# 默认版本:
# Dify: 1.14.0
# RAGFlow: v0.22.0
#
# 配置原则:
# 1. Dify 不改配置,只复制 .env.example 为 .env,然后按官方默认配置启动。
# 2. RAGFlow 只改两个位置:
# - docker/.env
# - docker/docker-compose-base.yml 中 Redis 服务名
# 3. 不对其他 compose 文件做额外修改。
#
# 最低建议配置:
# CPU >= 8 核
# 内存 >= 12 GB
# 可用磁盘 >= 40 GB
#
# 使用方式:
# chmod +x install_dify_ragflow_centos_menu.sh
# ./install_dify_ragflow_centos_menu.sh
#
# 非交互参数:
# ./install_dify_ragflow_centos_menu.sh --all
# ./install_dify_ragflow_centos_menu.sh --dify
# ./install_dify_ragflow_centos_menu.sh --ragflow
# ./install_dify_ragflow_centos_menu.sh --install-docker
# ./install_dify_ragflow_centos_menu.sh --status
# ./install_dify_ragflow_centos_menu.sh --stop
# ./install_dify_ragflow_centos_menu.sh --restart
# ./install_dify_ragflow_centos_menu.sh --uninstall
# ./install_dify_ragflow_centos_menu.sh --uninstall --remove-files
# ============================================================
set -Eeuo pipefail
# -----------------------------
# 默认配置
# -----------------------------
BASE_DIR="/opt/ai-platform"
DIFY_VERSION="1.14.0"
RAGFLOW_VERSION="v0.22.0"
# Dify 不修改端口,使用官方默认配置。
# Dify 默认 Web 访问端口通常为 80。
DIFY_PORT="80"
# RAGFlow 按手工部署配置修改。
RAGFLOW_WEB_HTTP_PORT="8880"
RAGFLOW_WEB_HTTPS_PORT="8443"
RAGFLOW_REDIS_HOST="ragflow_redis"
RAGFLOW_REDIS_PORT="7379"
INSTALL_DOCKER="true"
DEPLOY_DIFY="false"
DEPLOY_RAGFLOW="false"
ACTION="menu"
GITHUB_MIRROR=""
DOCKER_REGISTRY_MIRROR=""
REMOVE_FILES="false"
LOG_FILE="/tmp/install_dify_ragflow_$(date +%Y%m%d_%H%M%S).log"
# -----------------------------
# 输出函数
# -----------------------------
info() {
echo -e "\033[1;32m[INFO]\033[0m $*" | tee -a "$LOG_FILE"
}
warn() {
echo -e "\033[1;33m[WARN]\033[0m $*" | tee -a "$LOG_FILE"
}
err() {
echo -e "\033[1;31m[ERROR]\033[0m $*" | tee -a "$LOG_FILE"
}
die() {
err "$*"
exit 1
}
run() {
info "执行:$*"
"$@" 2>&1 | tee -a "$LOG_FILE"
}
pause_enter() {
echo
read -r -p "按 Enter 返回菜单..."
}
# -----------------------------
# 帮助信息
# -----------------------------
usage() {
cat <<EOF
Dify + RAGFlow 一键部署脚本 for CentOS
用法:
$0 进入交互式菜单
$0 [选项] 非交互执行
部署选项:
--all 部署 Dify + RAGFlow
--dify 只部署 Dify
--ragflow 只部署 RAGFlow
--install-docker 仅安装 Docker 和 Docker Compose 插件
管理选项:
--status 查看 Dify 和 RAGFlow 容器状态
--stop 停止 Dify 和 RAGFlow
--restart 重启 Dify 和 RAGFlow
--uninstall 卸载 Dify 和 RAGFlow,删除容器、网络和数据卷
--remove-files 配合 --uninstall 使用,同时删除源码目录
版本参数:
--dify-version VERSION 指定 Dify 版本,默认:${DIFY_VERSION}
--ragflow-version VERSION 指定 RAGFlow 版本,默认:${RAGFLOW_VERSION}
路径参数:
--base-dir DIR 安装目录,默认:${BASE_DIR}
RAGFlow 参数:
--ragflow-http-port PORT RAGFlow Web HTTP 端口,默认:${RAGFLOW_WEB_HTTP_PORT}
--ragflow-https-port PORT RAGFlow Web HTTPS 端口,默认:${RAGFLOW_WEB_HTTPS_PORT}
--ragflow-redis-port PORT RAGFlow Redis 映射端口,默认:${RAGFLOW_REDIS_PORT}
--ragflow-redis-host HOST RAGFlow Redis 服务名,默认:${RAGFLOW_REDIS_HOST}
下载优化:
--github-mirror URL GitHub 下载代理前缀,例如:https://gh-proxy.com/
--docker-mirror URL Docker 镜像加速地址,例如:https://xxxx.mirror.aliyuncs.com
示例:
$0
$0 --all
$0 --dify
$0 --ragflow
$0 --uninstall
$0 --uninstall --remove-files
EOF
}
# -----------------------------
# 参数解析
# -----------------------------
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--all)
ACTION="deploy"
DEPLOY_DIFY="true"
DEPLOY_RAGFLOW="true"
shift
;;
--dify)
ACTION="deploy"
DEPLOY_DIFY="true"
shift
;;
--ragflow)
ACTION="deploy"
DEPLOY_RAGFLOW="true"
shift
;;
--install-docker)
ACTION="install-docker"
shift
;;
--status)
ACTION="status"
shift
;;
--stop)
ACTION="stop"
shift
;;
--restart)
ACTION="restart"
shift
;;
--uninstall)
ACTION="uninstall"
shift
;;
--remove-files)
REMOVE_FILES="true"
shift
;;
--base-dir)
BASE_DIR="$2"
shift 2
;;
--dify-version)
DIFY_VERSION="$2"
shift 2
;;
--ragflow-version)
RAGFLOW_VERSION="$2"
shift 2
;;
--ragflow-http-port|--ragflow-port)
RAGFLOW_WEB_HTTP_PORT="$2"
shift 2
;;
--ragflow-https-port)
RAGFLOW_WEB_HTTPS_PORT="$2"
shift 2
;;
--ragflow-redis-port)
RAGFLOW_REDIS_PORT="$2"
shift 2
;;
--ragflow-redis-host)
RAGFLOW_REDIS_HOST="$2"
shift 2
;;
--github-mirror)
GITHUB_MIRROR="$2"
shift 2
;;
--docker-mirror)
DOCKER_REGISTRY_MIRROR="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
die "未知参数:$1。使用 --help 查看帮助。"
;;
esac
done
}
# -----------------------------
# 系统检测
# -----------------------------
need_root() {
if [[ "$(id -u)" -ne 0 ]]; then
die "请使用 root 用户执行,或使用 sudo 执行。"
fi
}
detect_os() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS_ID="${ID:-unknown}"
OS_VERSION_ID="${VERSION_ID:-unknown}"
else
die "无法识别系统,缺少 /etc/os-release"
fi
info "当前系统:${OS_ID} ${OS_VERSION_ID}"
case "$OS_ID" in
centos|rhel|rocky|almalinux|ol|anolis|alinux)
;;
*)
warn "当前系统不是典型 CentOS/RHEL 系,脚本会继续执行,但不保证完全兼容。"
;;
esac
}
check_resources() {
local cpu_count mem_gb disk_gb
cpu_count="$(nproc || echo 0)"
mem_gb="$(free -g | awk '/Mem:/ {print $2}')"
disk_gb="$(df -BG "$BASE_DIR" 2>/dev/null | awk 'NR==2 {gsub("G","",$4); print $4}')"
if [[ -z "${disk_gb:-}" ]]; then
disk_gb="$(df -BG / | awk 'NR==2 {gsub("G","",$4); print $4}')"
fi
info "安装目录:${BASE_DIR}"
info "CPU 核心数:${cpu_count}"
info "内存大小:${mem_gb} GB"
info "可用磁盘:${disk_gb} GB"
if [[ "$DEPLOY_RAGFLOW" == "true" ]]; then
if [[ "$cpu_count" -lt 8 ]]; then
warn "RAGFlow 最低建议 CPU >= 8 核,当前 ${cpu_count} 核。"
fi
if [[ "$mem_gb" -lt 12 ]]; then
warn "RAGFlow 最低建议内存 >= 12GB,当前 ${mem_gb}GB。"
fi
if [[ "$disk_gb" -lt 40 ]]; then
warn "RAGFlow 最低建议磁盘可用空间 >= 40GB,当前 ${disk_gb}GB。"
fi
fi
}
prepare_dirs() {
mkdir -p "$BASE_DIR"
}
github_url() {
local raw="$1"
if [[ -n "$GITHUB_MIRROR" ]]; then
echo "${GITHUB_MIRROR}${raw}"
else
echo "$raw"
fi
}
replace_env_value() {
local file="$1"
local key="$2"
local value="$3"
if grep -qE "^${key}=" "$file"; then
sed -i "s#^${key}=.*#${key}=${value}#g" "$file"
else
echo "${key}=${value}" >> "$file"
fi
}
port_in_use() {
local port="$1"
if ss -lnt 2>/dev/null | awk '{print $4}' | grep -Eq "[:.]${port}$"; then
return 0
fi
return 1
}
check_ports() {
if [[ "$DEPLOY_DIFY" == "true" ]] && port_in_use "$DIFY_PORT"; then
warn "端口 ${DIFY_PORT} 已被占用。若 Dify 已经运行可忽略。"
fi
if [[ "$DEPLOY_RAGFLOW" == "true" ]] && port_in_use "$RAGFLOW_WEB_HTTP_PORT"; then
warn "端口 ${RAGFLOW_WEB_HTTP_PORT} 已被占用。RAGFlow HTTP 访问端口可能冲突。"
fi
if [[ "$DEPLOY_RAGFLOW" == "true" ]] && port_in_use "$RAGFLOW_WEB_HTTPS_PORT"; then
warn "端口 ${RAGFLOW_WEB_HTTPS_PORT} 已被占用。RAGFlow HTTPS 访问端口可能冲突。"
fi
if [[ "$DEPLOY_RAGFLOW" == "true" ]] && port_in_use "$RAGFLOW_REDIS_PORT"; then
warn "端口 ${RAGFLOW_REDIS_PORT} 已被占用。RAGFlow Redis 映射端口可能冲突。"
fi
}
# -----------------------------
# Docker 安装
# -----------------------------
install_base_tools() {
info "安装基础工具..."
if command -v dnf >/dev/null 2>&1; then
run dnf install -y yum-utils device-mapper-persistent-data lvm2 git wget curl tar sed grep gawk ca-certificates
else
run yum install -y yum-utils device-mapper-persistent-data lvm2 git wget curl tar sed grep gawk ca-certificates
fi
}
install_docker() {
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
info "Docker 与 Docker Compose 插件已安装。"
docker --version | tee -a "$LOG_FILE"
docker compose version | tee -a "$LOG_FILE"
return
fi
install_base_tools
info "安装 Docker CE..."
if command -v dnf >/dev/null 2>&1; then
run dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
run dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
else
run yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
run yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
fi
mkdir -p /etc/docker
if [[ -n "$DOCKER_REGISTRY_MIRROR" ]]; then
info "配置 Docker 镜像加速。"
cat >/etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["${DOCKER_REGISTRY_MIRROR}"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
else
if [[ ! -f /etc/docker/daemon.json ]]; then
cat >/etc/docker/daemon.json <<EOF
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
fi
fi
run systemctl enable docker
run systemctl restart docker
docker --version | tee -a "$LOG_FILE"
docker compose version | tee -a "$LOG_FILE"
}
check_docker_version() {
command -v docker >/dev/null 2>&1 || die "Docker 未安装。"
docker compose version >/dev/null 2>&1 || die "Docker Compose 插件未安装。"
local docker_ver compose_ver
docker_ver="$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo unknown)"
compose_ver="$(docker compose version --short 2>/dev/null || echo unknown)"
info "Docker Server 版本:${docker_ver}"
info "Docker Compose 版本:${compose_ver}"
}
# -----------------------------
# Dify 部署
# -----------------------------
deploy_dify() {
info "开始部署 Dify ${DIFY_VERSION}..."
local dify_tar dify_url dify_dir
dify_tar="${BASE_DIR}/dify-${DIFY_VERSION}.tar.gz"
dify_dir="${BASE_DIR}/dify-${DIFY_VERSION}"
dify_url="$(github_url "https://github.com/langgenius/dify/archive/refs/tags/${DIFY_VERSION}.tar.gz")"
if [[ ! -d "$dify_dir" ]]; then
info "下载 Dify 源码包。"
run wget -O "$dify_tar" "$dify_url"
run tar -xf "$dify_tar" -C "$BASE_DIR"
else
info "Dify 目录已存在:${dify_dir}"
fi
[[ -d "${dify_dir}/docker" ]] || die "Dify docker 目录不存在:${dify_dir}/docker"
cd "${dify_dir}/docker"
if [[ ! -f .env ]]; then
[[ -f .env.example ]] || die "Dify 缺少 .env.example 文件"
run cp .env.example .env
else
info "Dify .env 已存在,保持原配置。"
fi
info "启动 Dify..."
run docker compose -p dify up -d
info "Dify 部署完成。"
}
# -----------------------------
# RAGFlow 部署
# -----------------------------
patch_ragflow_env() {
local env_file="$1"
[[ -f "$env_file" ]] || die "RAGFlow .env 文件不存在:$env_file"
replace_env_value "$env_file" "SVR_WEB_HTTP_PORT" "$RAGFLOW_WEB_HTTP_PORT"
replace_env_value "$env_file" "SVR_WEB_HTTPS_PORT" "$RAGFLOW_WEB_HTTPS_PORT"
replace_env_value "$env_file" "REDIS_HOST" "$RAGFLOW_REDIS_HOST"
replace_env_value "$env_file" "REDIS_PORT" "$RAGFLOW_REDIS_PORT"
info "RAGFlow .env 配置完成。"
}
patch_ragflow_compose_base() {
local compose_base="$1"
[[ -f "$compose_base" ]] || die "RAGFlow docker-compose-base.yml 文件不存在:$compose_base"
python3 - "$compose_base" "$RAGFLOW_REDIS_HOST" <<'PY'
import sys
from pathlib import Path
path = Path(sys.argv[1])
new_name = sys.argv[2]
text = path.read_text(encoding="utf-8")
# 只修改 services 下的 redis 服务名:
# redis:
# 修改为:
# ragflow_redis:
text = text.replace("\n redis:\n", f"\n {new_name}:\n")
path.write_text(text, encoding="utf-8")
PY
info "RAGFlow docker-compose-base.yml 配置完成。"
}
deploy_ragflow() {
info "开始部署 RAGFlow ${RAGFLOW_VERSION}..."
local ragflow_dir ragflow_url
ragflow_dir="${BASE_DIR}/RAGFlow"
ragflow_url="$(github_url "https://github.com/infiniflow/RAGFlow.git")"
if [[ ! -d "$ragflow_dir/.git" ]]; then
info "克隆 RAGFlow 源码。"
run git clone "$ragflow_url" "$ragflow_dir"
else
info "RAGFlow 目录已存在:${ragflow_dir}"
fi
cd "$ragflow_dir"
run git fetch --tags --all
run git checkout -f "$RAGFLOW_VERSION"
[[ -d "${ragflow_dir}/docker" ]] || die "RAGFlow docker 目录不存在:${ragflow_dir}/docker"
cd "${ragflow_dir}/docker"
if [[ ! -f .env ]]; then
if [[ -f .env.example ]]; then
run cp .env.example .env
else
die "RAGFlow 缺少 .env 文件,请检查源码版本。"
fi
fi
patch_ragflow_env ".env"
patch_ragflow_compose_base "docker-compose-base.yml"
info "启动 RAGFlow..."
run docker compose -p ragflow up -d
info "RAGFlow 部署完成。"
}
# -----------------------------
# 管理命令
# -----------------------------
show_status() {
info "查看 Dify 状态..."
local dify_dir="${BASE_DIR}/dify-${DIFY_VERSION}/docker"
if [[ -d "$dify_dir" ]]; then
(cd "$dify_dir" && docker compose -p dify ps) || true
else
warn "未找到 Dify 目录:$dify_dir"
fi
info "查看 RAGFlow 状态..."
local ragflow_dir="${BASE_DIR}/RAGFlow/docker"
if [[ -d "$ragflow_dir" ]]; then
(cd "$ragflow_dir" && docker compose -p ragflow ps) || true
else
warn "未找到 RAGFlow 目录:$ragflow_dir"
fi
}
stop_services() {
local dify_dir="${BASE_DIR}/dify-${DIFY_VERSION}/docker"
local ragflow_dir="${BASE_DIR}/RAGFlow/docker"
if [[ -d "$dify_dir" ]]; then
info "停止 Dify..."
(cd "$dify_dir" && docker compose -p dify stop) || true
fi
if [[ -d "$ragflow_dir" ]]; then
info "停止 RAGFlow..."
(cd "$ragflow_dir" && docker compose -p ragflow stop) || true
fi
}
restart_services() {
local dify_dir="${BASE_DIR}/dify-${DIFY_VERSION}/docker"
local ragflow_dir="${BASE_DIR}/RAGFlow/docker"
if [[ -d "$dify_dir" ]]; then
info "重启 Dify..."
(cd "$dify_dir" && docker compose -p dify restart) || true
fi
if [[ -d "$ragflow_dir" ]]; then
info "重启 RAGFlow..."
(cd "$ragflow_dir" && docker compose -p ragflow restart) || true
fi
}
uninstall_services() {
local dify_dir="${BASE_DIR}/dify-${DIFY_VERSION}/docker"
local ragflow_dir="${BASE_DIR}/RAGFlow/docker"
warn "即将卸载 Dify 和 RAGFlow。"
warn "卸载会删除容器、网络和 Compose 数据卷,业务数据将被删除。"
if [[ -d "$dify_dir" ]]; then
info "卸载 Dify..."
(cd "$dify_dir" && docker compose -p dify down -v --remove-orphans) || true
else
warn "未找到 Dify docker 目录:$dify_dir"
fi
if [[ -d "$ragflow_dir" ]]; then
info "卸载 RAGFlow..."
(cd "$ragflow_dir" && docker compose -p ragflow down -v --remove-orphans) || true
else
warn "未找到 RAGFlow docker 目录:$ragflow_dir"
fi
# 兼容历史上未指定 -p 时产生的默认项目名 docker。
if [[ -d "$dify_dir" ]]; then
(cd "$dify_dir" && docker compose -p docker down -v --remove-orphans) >/dev/null 2>&1 || true
fi
if [[ -d "$ragflow_dir" ]]; then
(cd "$ragflow_dir" && docker compose -p docker down -v --remove-orphans) >/dev/null 2>&1 || true
fi
if [[ "$REMOVE_FILES" == "true" ]]; then
info "删除源码目录..."
rm -rf "${BASE_DIR}/dify-${DIFY_VERSION}" \
"${BASE_DIR}/dify-${DIFY_VERSION}.tar.gz" \
"${BASE_DIR}/RAGFlow"
else
warn "源码目录已保留。如需删除源码目录,请使用:--uninstall --remove-files"
fi
info "卸载完成。"
}
print_summary() {
local ip
ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
[[ -z "$ip" ]] && ip="服务器IP"
echo
echo "============================================================"
echo "部署信息"
echo "============================================================"
if [[ "$DEPLOY_DIFY" == "true" ]]; then
echo "Dify 访问地址: http://${ip}:${DIFY_PORT}"
echo "Dify 安装目录: ${BASE_DIR}/dify-${DIFY_VERSION}/docker"
fi
if [[ "$DEPLOY_RAGFLOW" == "true" ]]; then
echo "RAGFlow HTTP地址: http://${ip}:${RAGFLOW_WEB_HTTP_PORT}"
echo "RAGFlow HTTPS地址: https://${ip}:${RAGFLOW_WEB_HTTPS_PORT}"
echo "RAGFlow Redis配置: ${RAGFLOW_REDIS_HOST}:${RAGFLOW_REDIS_PORT}"
echo "RAGFlow 安装目录: ${BASE_DIR}/RAGFlow/docker"
fi
echo "日志文件: ${LOG_FILE}"
echo
echo "常用命令:"
echo " 查看容器:docker ps"
echo " 查看 Dify:cd ${BASE_DIR}/dify-${DIFY_VERSION}/docker && docker compose -p dify ps"
echo " 查看 RAGFlow:cd ${BASE_DIR}/RAGFlow/docker && docker compose -p ragflow ps"
echo " 查看日志:docker compose logs -f"
echo "============================================================"
}
deploy_selected() {
prepare_dirs
check_resources
if [[ "$INSTALL_DOCKER" == "true" ]]; then
install_docker
fi
check_docker_version
check_ports
if [[ "$DEPLOY_DIFY" == "true" ]]; then
deploy_dify
fi
if [[ "$DEPLOY_RAGFLOW" == "true" ]]; then
deploy_ragflow
fi
show_status
print_summary
}
# -----------------------------
# 菜单功能
# -----------------------------
show_menu() {
clear
echo "============================================================"
echo " Dify + RAGFlow 一键部署管理菜单"
echo "============================================================"
echo " 安装目录:${BASE_DIR}"
echo " Dify版本:${DIFY_VERSION}"
echo " RAGFlow版本:${RAGFLOW_VERSION}"
echo "------------------------------------------------------------"
echo " 1. 安装 Docker 和 Docker Compose"
echo " 2. 部署 Dify"
echo " 3. 部署 RAGFlow"
echo " 4. 部署 Dify + RAGFlow"
echo " 5. 查看服务状态"
echo " 6. 停止服务"
echo " 7. 重启服务"
echo " 8. 卸载服务(保留源码目录)"
echo " 9. 卸载服务(删除源码目录)"
echo " 0. 退出"
echo "============================================================"
}
menu_loop() {
while true; do
show_menu
read -r -p "请输入选项 [0-9]:" choice
case "$choice" in
1)
install_docker
pause_enter
;;
2)
DEPLOY_DIFY="true"
DEPLOY_RAGFLOW="false"
deploy_selected
pause_enter
;;
3)
DEPLOY_DIFY="false"
DEPLOY_RAGFLOW="true"
deploy_selected
pause_enter
;;
4)
DEPLOY_DIFY="true"
DEPLOY_RAGFLOW="true"
deploy_selected
pause_enter
;;
5)
show_status
pause_enter
;;
6)
stop_services
pause_enter
;;
7)
restart_services
pause_enter
;;
8)
REMOVE_FILES="false"
uninstall_services
pause_enter
;;
9)
REMOVE_FILES="true"
uninstall_services
pause_enter
;;
0)
echo "已退出。"
exit 0
;;
*)
echo "无效选项,请重新输入。"
sleep 1
;;
esac
done
}
main() {
parse_args "$@"
need_root
detect_os
case "$ACTION" in
menu)
menu_loop
;;
install-docker)
install_docker
;;
status)
show_status
;;
stop)
stop_services
;;
restart)
restart_services
;;
uninstall)
uninstall_services
;;
deploy)
deploy_selected
;;
*)
die "未知动作:$ACTION"
;;
esac
}
main "$@"