topfans/docker/dify-deploy.sh
2026-07-09 16:08:25 +08:00

393 lines
14 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# ===================================================================
# TopFans Dify 部署脚本
# 功能:服务器上安装 Dify + 配置环境变量
# ===================================================================
#
# 使用前提:
# 1. 服务器已配置 SSH 免密登录
# 2. 服务器有足够的内存(推荐 4GB+
#
# 使用方式:
# ./dify-deploy.sh install # 安装 Dify 到服务器
# ./dify-deploy.sh status # 查看 Dify 服务状态
# ./dify-deploy.sh logs # 查看 Dify 日志
# ./dify-deploy.sh restart # 重启 Dify
# ./dify-deploy.sh upgrade # 升级 Dify 版本
# ./dify-deploy.sh uninstall # 卸载 Dify
# ./dify-deploy.sh show-config # 显示需要配置到 TopFans 的环境变量
#
# ===================================================================
set -e
# ==================== 颜色定义 ====================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
NC='\033[0m'
# ==================== 路径配置 ====================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# ==================== 服务器配置 ====================
SERVER_HOST="101.132.250.62"
SERVER_PORT="22"
SERVER_USER="root"
SERVER_PATH="/opt/dify/docker"
# ==================== Dify 配置 ====================
# 端口说明:
# EXPOSE_NGINX_PORT: 对外统一入口nginxTopFans DIFY_API_BASE 走这个
# CONSOLE_WEB_PORT: Dify 管理控制台(浏览器手动访问)
# APP_WEB_PORT: Dify API 容器端口5001内部用外部不直接连
# 默认值对齐 docker/.env.prod 中 DIFY_API_BASE=http://101.132.250.62:8083/v1
DIFY_NGINX_PORT="8083" # Dify Web/API 统一入口nginx
DIFY_CONSOLE_PORT="8084" # Dify 控制台
DIFY_API_PORT="8085" # Dify API 容器直连(内部)
# 密码与密钥留空install 时由 openssl rand 生成强随机值
# (避免硬编码弱密码,所有 Dify 实例使用不同密钥)
DIFY_DB_PASSWORD=""
DIFY_REDIS_PASSWORD=""
# ==================== SSH 命令 ====================
ssh_cmd() {
ssh -o StrictHostKeyChecking=no -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "$@"
}
# ==================== docker-compose 命令检测 ====================
# 兼容 v1 二进制与 v2 插件(v2 在新版 Docker 默认安装,但 v1 命令不存在)
# 优先用 v1;没有则检查 v2 plugin 并建软链伪装成 v1参考 deploy.sh:341
dc_cmd() {
if ssh_cmd "command -v docker-compose" &>/dev/null; then
echo "docker-compose"
return 0
fi
if ssh_cmd "docker compose version" &>/dev/null; then
# v2 插件存在,建软链伪装成 v1
ssh_cmd "ln -sf /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose 2>/dev/null" || true
fi
if ! ssh_cmd "command -v docker-compose" &>/dev/null; then
print_msg "$RED" "❌ docker-compose 未安装且 v2 插件建软链失败"
return 1
fi
echo "docker-compose"
}
# ==================== 打印函数 ====================
print_step() {
echo ""
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
print_msg() {
local color=$1
local msg=$2
echo -e "${color}${msg}${NC}"
}
# ==================== 帮助信息 ====================
show_help() {
cat << EOF
${MAGENTA}TopFans Dify 部署脚本${NC}
${YELLOW}命令:${NC}
${GREEN}install${NC} 安装 Dify 到服务器(首次部署)
${GREEN}status${NC} 查看 Dify 服务状态
${GREEN}logs${NC} 查看 Dify 日志
${GREEN}restart${NC} 重启 Dify
${GREEN}upgrade${NC} 升级 Dify 版本
${GREEN}uninstall${NC} 卸载 Dify
${GREEN}show-config${NC} 显示需要配置到 TopFans 的环境变量
${YELLOW}前提准备:${NC}
1. 服务器已配置 SSH 密钥登录
2. 服务器内存 >= 4GB
${YELLOW}注意事项:${NC}
- Dify 使用独立端口(不与 TopFans 冲突)
- Dify 使用独立的 PostgreSQL 和 Redis
- Workflow 需要在 Dify 控制台手动导入
EOF
}
# ==================== 1. 安装 Dify ====================
do_install() {
print_step "🚀 安装 Dify 到服务器"
print_msg "$YELLOW" "目标服务器: ${SERVER_USER}@${SERVER_HOST}:${SERVER_PORT}"
print_msg "$YELLOW" "安装路径: ${SERVER_PATH}"
echo ""
# 检查服务器 Docker 环境
print_msg "$YELLOW" "检查 Docker 环境..."
local docker_version=$(ssh_cmd "docker --version 2>/dev/null || echo 'NOT_INSTALLED'")
print_msg "$GREEN" "Docker: ${docker_version}"
# 如果 Docker 未安装,先安装
if [ "$docker_version" = "NOT_INSTALLED" ]; then
print_msg "$YELLOW" "Docker 未安装,开始安装..."
ssh_cmd "curl -fsSL https://get.docker.com | sh"
ssh_cmd "systemctl start docker && systemctl enable docker"
print_msg "$GREEN" "✅ Docker 安装完成"
fi
# 检查 docker-compose
local compose_version=$(ssh_cmd "docker-compose --version 2>/dev/null || docker compose version 2>/dev/null || echo 'NOT_INSTALLED'")
print_msg "$GREEN" "docker-compose: ${compose_version}"
# 创建服务器目录
print_msg "$YELLOW" "创建目录..."
ssh_cmd "mkdir -p ${SERVER_PATH}"
print_msg "$GREEN" "✅ 目录创建完成"
# 检测并设置 docker-compose 命令(v1/v2 兼容)
local DC
DC=$(dc_cmd) || { print_msg "$RED" "❌ docker-compose 不可用,请先安装 Docker"; return 1; }
# 保护已存在的 .env:重跑 install 时保留用户配置(SECRET_KEY/管理员密码等)
if ssh_cmd "[ -f ${SERVER_PATH}/.env ]"; then
print_msg "$YELLOW" "⚠️ 检测到已存在的 .env,跳过下载和配置"
print_msg "$YELLOW" " 如需重新配置,请先备份 ${SERVER_PATH}/.env 后删除"
else
# 下载 Dify docker-compose
# ★ 版本说明v0.15.0 的 sandbox 镜像有 seccomp bugPython 启动崩溃)
# 必须用 Dify v1.x 或更新版本
print_msg "$YELLOW" "下载 Dify docker-composev1.x..."
ssh_cmd "cd ${SERVER_PATH} && \
curl -L https://raw.githubusercontent.com/langgenius/dify/main/docker/docker-compose.yaml \
-o docker-compose.yml 2>/dev/null || \
curl -L https://raw.githubusercontent.com/langgenius/dify/1.4.0/docker/docker-compose.yaml \
-o docker-compose.yml"
print_msg "$GREEN" "✅ docker-compose.yml 下载完成v1.x"
# 下载 .env 示例文件
print_msg "$YELLOW" "下载 .env 配置v1.x..."
ssh_cmd "cd ${SERVER_PATH} && \
curl -L https://raw.githubusercontent.com/langgenius/dify/main/docker/.env.example \
-o .env 2>/dev/null || \
curl -L https://raw.githubusercontent.com/langgenius/dify/1.4.0/docker/.env.example \
-o .env"
print_msg "$GREEN" "✅ .env 下载完成v1.x"
# 修改 .env 配置(端口、密码等)
# ★ heredoc delimiter 不可加单引号,否则 \$VAR 不展开
print_msg "$YELLOW" "配置 Dify 环境变量..."
ssh_cmd "cat > ${SERVER_PATH}/.env.custom << ENVEOF
# 端口配置(避免与 TopFans 冲突)
EXPOSE_NGINX_PORT=${DIFY_NGINX_PORT}
CONSOLE_WEB_PORT=${DIFY_CONSOLE_PORT}
APP_WEB_PORT=${DIFY_API_PORT}
# 数据库配置
DB_USERNAME=postgres
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=dify
POSTGRES_PASSWORD=\$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
# Redis 配置
REDIS_PASSWORD=\$(openssl rand -base64 24 | tr -d '/+=' | head -c 32)
# API 配置(Dify 官方要求 SECRET_KEY 至少 42 字符)
SECRET_KEY=\$(openssl rand -base64 42)
INIT_SECRET_KEY=\$(openssl rand -base64 42)
ENVEOF
"
ssh_cmd "cd ${SERVER_PATH} && cat .env .env.custom > .env.tmp && mv .env.tmp .env && rm -f .env.custom"
print_msg "$GREEN" "✅ 环境变量配置完成"
fi
# 启动 Dify
print_msg "$YELLOW" "启动 Dify 服务(首次启动需要几分钟)..."
ssh_cmd "cd ${SERVER_PATH} && ${DC} up -d"
# 等待服务启动
print_msg "$YELLOW" "等待服务启动(最多 120 秒)..."
local count=0
while [ $count -lt 120 ]; do
local api_status=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://localhost:${DIFY_API_PORT}/health 2>/dev/null || echo '000'")
if [ "$api_status" = "200" ]; then
print_msg "$GREEN" "✅ Dify API 就绪"
break
fi
sleep 5
count=$((count + 5))
echo -n "."
done
echo ""
print_step "📊 Dify 安装结果"
print_msg "$GREEN" "✅ Dify 安装完成!"
echo ""
print_msg "$CYAN" "访问地址:"
print_msg "$YELLOW" " 控制台: http://${SERVER_HOST}:${DIFY_CONSOLE_PORT}"
print_msg "$YELLOW" " Web: http://${SERVER_HOST}:${DIFY_NGINX_PORT}"
print_msg "$YELLOW" " API: http://${SERVER_HOST}:${DIFY_API_PORT}"
echo ""
print_msg "$RED" "⚠️ 首次访问需要创建管理员账号!"
echo ""
print_msg "$CYAN" "下一步:"
print_msg "$YELLOW" " 1. 访问控制台 http://${SERVER_HOST}:${DIFY_CONSOLE_PORT},创建管理员账号"
print_msg "$YELLOW" " 2. 在控制台导入 Workflowdocs/dify/*.yml"
print_msg "$YELLOW" " 3. 运行 ./dify-deploy.sh show-config 查看 TopFans 配置"
}
# ==================== 2. 查看 Dify 状态 ====================
do_status() {
print_step "📊 Dify 服务状态"
local DC
DC=$(dc_cmd) || return 1
ssh_cmd "cd ${SERVER_PATH} && ${DC} ps 2>/dev/null || echo 'Dify 未安装或未启动'"
echo ""
# 健康检查
local api_status=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://localhost:${DIFY_API_PORT}/health 2>/dev/null || echo '000'")
if [ "$api_status" = "200" ]; then
print_msg "$GREEN" "✅ Dify API 健康"
else
print_msg "$RED" "❌ Dify API 异常 (HTTP ${api_status})"
fi
}
# ==================== 3. 查看日志 ====================
do_logs() {
print_step "📋 Dify 日志"
print_msg "$YELLOW" "按 Ctrl+C 退出日志"
echo ""
local DC
DC=$(dc_cmd) || return 1
ssh_cmd "cd ${SERVER_PATH} && ${DC} logs -f 2>/dev/null || echo 'Dify 未安装'"
}
# ==================== 4. 重启 ====================
do_restart() {
print_step "🔄 重启 Dify"
print_msg "$YELLOW" "正在重启..."
local DC
DC=$(dc_cmd) || return 1
ssh_cmd "cd ${SERVER_PATH} && ${DC} restart 2>/dev/null"
sleep 10
do_status
}
# ==================== 5. 升级 ====================
do_upgrade() {
print_step "⬆️ 升级 Dify"
print_msg "$RED" "警告: 升级前建议备份数据!"
read -p "确认升级? (y/N): " confirm < /dev/tty
if [ "$confirm" != "y" ]; then
print_msg "$YELLOW" "已取消"
exit 0
fi
local DC
DC=$(dc_cmd) || return 1
print_msg "$YELLOW" "下载最新 docker-compose..."
ssh_cmd "cd ${SERVER_PATH} && \
curl -L https://raw.githubusercontent.com/langgenius/dify/main/docker/docker-compose.yaml \
-o docker-compose.yml"
print_msg "$YELLOW" "执行升级..."
ssh_cmd "cd ${SERVER_PATH} && ${DC} up -d"
print_msg "$GREEN" "✅ 升级完成"
}
# ==================== 6. 卸载 ====================
do_uninstall() {
print_step "🗑️ 卸载 Dify"
print_msg "$RED" "警告: 此操作将删除所有 Dify 数据!"
read -p "确认卸载? (输入 'YES' 确认): " confirm < /dev/tty
if [ "$confirm" != "YES" ]; then
print_msg "$YELLOW" "已取消"
exit 0
fi
local DC
DC=$(dc_cmd) || return 1
print_msg "$YELLOW" "停止并删除容器..."
ssh_cmd "cd ${SERVER_PATH} && ${DC} down -v 2>/dev/null || true"
print_msg "$YELLOW" "删除数据卷..."
ssh_cmd "docker volume rm \$(docker volume list -q -f name=dify) 2>/dev/null || true"
print_msg "$GREEN" "✅ Dify 卸载完成"
}
# ==================== 7. 显示 TopFans 配置 ====================
show_topfans_config() {
print_step "📝 TopFans 后端配置"
print_msg "$CYAN" "需要在 /opt/topfans/docker/.env.prod 中配置以下环境变量:"
echo ""
print_msg "$YELLOW" "# ========== Dify 配置 =========="
print_msg "$YELLOW" "# Dify API 地址(走 nginx 统一入口 ${DIFY_NGINX_PORT},与 Dify 容器内 APP_WEB_PORT 不同)"
print_msg "$CYAN" "DIFY_API_BASE=http://${SERVER_HOST}:${DIFY_NGINX_PORT}/v1"
echo ""
print_msg "$YELLOW" "# Dify API Key从 Dify 控制台获取)"
print_msg "$CYAN" "DIFY_API_KEY=app-你的APIKey"
echo ""
print_msg "$RED" "⚠️ 配置完成后需要重启 TopFans 服务:"
print_msg "$RED" " ./deploy.sh restart --server ${SERVER_HOST}"
}
# ==================== 主函数 ====================
main() {
if [ $# -eq 0 ]; then
show_help
exit 0
fi
local command=$1
shift
case $command in
install)
do_install
;;
status)
do_status
;;
logs)
do_logs
;;
restart)
do_restart
;;
upgrade)
do_upgrade
;;
uninstall)
do_uninstall
;;
show-config)
show_topfans_config
;;
-h|--help|help)
show_help
;;
*)
print_msg "$RED" "错误: 未知命令 '$command'"
show_help
exit 1
;;
esac
}
main "$@"