@@ -1,186 +1,307 @@
#!/bin/bash
# EasyAI 一键部署脚本
# 支持交互式问答配置,兼容 IP 与域名两种访问方式
# 一行命令部署: bash -c "$(curl -fsSL https://git.51easyai.com/wangbo/easyai/raw/branch/main/start.sh)"
set -e # 发生错误时终止脚本执行
set -e
echo "==========================="
echo "🚀 开始自动安装 Docker 和 Docker Compose "
echo "==========================="
# 仅配置模式(验证用):DEPLOY_DRY_RUN=1 只生成配置文件,不执行 Docker 安装和启动
DEPLOY_DRY_RUN = " ${ DEPLOY_DRY_RUN :- 0 } "
# 获取操作系统类型和版本
OS_FAMILY = $( hostnamectl | grep "Operating System" | awk '{print $3}' )
OS_VERSION_ID = $( grep -oP '(?<=^VERSION_ID=")[0-9.]+' /etc/os-release | cut -d'.' -f1)
OS_CODENAME = ""
if [ [ " $OS_FAMILY " = = "Ubuntu" ] ] ; then
OS_CODENAME = $( lsb_release -cs)
fi
# 定义国内镜像源
# Ubuntu 镜像源
UBUNTU_DOCKER_MIRROR_URL = "https://mirrors.ustc.edu.cn/docker-ce" # 中科大
UBUNTU_DOCKER_GPG_URL = "https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg"
# CentOS 镜像源 (选择一个即可,这里提供清华和阿里云)
CENTOS_DOCKER_MIRROR_URL = "https://mirrors.tuna.tsinghua.edu.cn/docker-ce" # 清华大学
# CENTOS_DOCKER_MIRROR_URL="https://mirrors.aliyun.com/docker-ce" # 阿里云 - 如果清华源不稳定可以尝试这个
# 函数:检查并等待网络连接
check_network( ) {
echo "🌐 检查网络连接到 Docker 官方仓库 (备用)..."
if curl -sSf https://download.docker.com/ & > /dev/null; then
echo "✅ 网络连接到 Docker 官方仓库正常。"
# ==================== 项目初始化 ====================
# 若通过 curl | bash 运行,需先克隆项目
init_project_dir( ) {
local script_source
script_source = " $( cd " $( dirname " ${ BASH_SOURCE [0] :- $0 } " ) " && pwd ) "
if [ -f " ${ script_source } /docker-compose.yml " ] ; then
echo " 📁 检测到项目目录: ${ script_source } "
cd " $script_source "
return 0
fi
# 通过 curl 运行时,克隆项目
echo "📥 未检测到项目目录,正在克隆 EasyAI 仓库..."
EASYAI_DIR = " ${ EASYAI_DIR :- $HOME /easyai } "
if [ -d " $EASYAI_DIR " ] ; then
echo " 📁 使用已有目录: $EASYAI_DIR "
cd " $EASYAI_DIR "
else
echo "⚠️ 无法连接到 Docker 官方仓库。将尝试使用国内镜像源。"
# 即使无法连接官方源,也继续尝试国内源
return 0
git clone --depth 1 https://git.51easyai.com/wangbo/easyai.git " $EASYAI_DIR " || {
echo "❌ 克隆失败,请检查网络或手动克隆: git clone https://git.51easyai.com/wangbo/easyai.git"
exit 1
}
cd " $EASYAI_DIR "
fi
}
# 预检网络连接(非阻塞,仅作为提示)
check_network
# ==================== 配置变量(支持环境变量非交互模式) ====================
DEPLOY_MODE = "" # ip | domain
DEPLOY_IP = ""
DEPLOY_DOMAIN = ""
DEPLOY_HTTPS = false
# Docker 安装
if command -v docker & > /dev/null; then
echo "✅ Docker 已安装,跳过安装步骤"
else
if [ [ " $OS_FAMILY " = = "Ubuntu" ] ] ; then
echo "📦 安装依赖 (Ubuntu)..."
sudo apt update -y
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
prompt_or_env( ) {
local var_name = $1
local prompt_text = $2
local env_name = $3
local default = $4
echo "🔑 添加 Docker GPG 密钥 (Ubuntu) - 优先使用国内镜像源..."
# 现代 Ubuntu 推荐使用 gpg --dearmor
sudo install -m 0755 -d /etc/apt/keyrings
if ! curl -fsSL " $UBUNTU_DOCKER_GPG_URL " | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg; then
echo "❌ 无法从国内镜像源获取 GPG 密钥,尝试从官方源获取..."
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg || {
echo "❌ 无法获取 Docker GPG 密钥,请检查网络或 GPG 密钥 URL。"
exit 1
}
fi
sudo chmod a+r /etc/apt/keyrings/docker.gpg # 确保可读权限
echo "🌍 添加 Docker 源 (Ubuntu) - 使用国内镜像源..."
echo \
" deb [arch= $( dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $UBUNTU_DOCKER_MIRROR_URL /linux/ubuntu \
$OS_CODENAME stable " | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update -y
echo "🐳 安装 Docker (Ubuntu)..."
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || {
echo "❌ Docker 或其组件安装失败。请检查错误日志或尝试手动安装。"
exit 1
}
elif [ [ " $OS_FAMILY " = = "CentOS" ] ] ; then
echo "📦 安装依赖 (CentOS)..."
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
echo "🌍 添加 Docker 源 (CentOS) - 使用国内镜像源..."
# 添加 Docker CE 官方 repo
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 替换为国内镜像源
echo " 🔄 替换 Docker 源为国内镜像 ( $CENTOS_DOCKER_MIRROR_URL )... "
sudo sed -i " s+https://download.docker.com+ $CENTOS_DOCKER_MIRROR_URL + " /etc/yum.repos.d/docker-ce.repo || {
echo "❌ 替换 Docker 源失败,可能 repo 文件路径不正确或无权限。"
exit 1
}
# CentOS 8+ 可能会遇到 module 冲突,禁用默认的 container-tools 模块
if [ [ " $OS_VERSION_ID " -ge "8" ] ] ; then
echo "⚙️ 禁用 CentOS 8+ 默认的 container-tools 模块以避免冲突..."
sudo yum module disable -y container-tools
sudo yum module enable -y container-tools:docker
fi
sudo yum makecache
echo "🐳 安装 Docker (CentOS)..."
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || {
echo "❌ Docker 或其组件安装失败。请检查错误日志或尝试手动安装。"
echo "提示:如果仍遇到下载问题,请检查网络、防火墙或尝试切换另一个国内镜像源。"
exit 1
}
if [ -n " ${ !env_name } " ] ; then
eval " $var_name =\" ${ !env_name } \" "
return
fi
if [ -n " $default " ] ; then
read -r -p " ${ prompt_text } [ $default ]: " input
eval " $var_name =\" ${ input :- $default } \" "
else
echo " ❌ 未知操作系统 ( $OS_FAMILY ),无法安装 Docker。 "
read -r -p " ${ prompt_text } : " input
eval " $var_name =\" $input \" "
fi
}
run_deploy_questions( ) {
echo ""
echo "================================"
echo " EasyAI 部署配置(问答模式)"
echo "================================"
echo ""
# 非交互模式:环境变量已完整设置则直接使用(CI/自动化部署)
if [ -n " $DEPLOY_ACCESS " ] ; then
if [ " $DEPLOY_ACCESS " = "ip" ] && [ -n " $DEPLOY_IP " ] ; then
DEPLOY_MODE = "ip"
echo " 使用环境变量: IP 模式, IP= $DEPLOY_IP "
return
fi
if [ " $DEPLOY_ACCESS " = "domain" ] && [ -n " $DEPLOY_DOMAIN " ] ; then
DEPLOY_MODE = "domain"
DEPLOY_HTTPS = " ${ DEPLOY_HTTPS_INPUT :- false } "
echo " 使用环境变量: 域名模式, 域名= $DEPLOY_DOMAIN "
return
fi
fi
# 1. IP 或域名访问
if [ -z " $DEPLOY_ACCESS " ] ; then
echo "1. 通过 IP 地址还是域名访问?"
echo " [1] IP 地址(需开放 3001、3002、3003 端口)"
echo " [2] 域名"
read -r -p "请选择 [1/2]: " choice
case " $choice " in
1) DEPLOY_MODE = "ip" ; ;
2) DEPLOY_MODE = "domain" ; ;
*) echo "❌ 无效选择" ; exit 1 ; ;
esac
else
DEPLOY_MODE = " $DEPLOY_ACCESS "
fi
if [ " $DEPLOY_MODE " = "ip" ] ; then
# 2. 输入服务器 IP
prompt_or_env DEPLOY_IP "2. 请输入服务器 IP 地址" "DEPLOY_IP" ""
[ -z " $DEPLOY_IP " ] && { echo "❌ IP 不能为空" ; exit 1; }
echo " 请确保防火墙已放行 3001、3002、3003 端口"
else
# 3. 输入域名
prompt_or_env DEPLOY_DOMAIN "3. 请输入域名(不含 https:// 前缀,如 51easyai.com) " "DEPLOY_DOMAIN" ""
[ -z " $DEPLOY_DOMAIN " ] && { echo "❌ 域名不能为空" ; exit 1; }
# 3.1 是否启用 HTTPS
if [ -n " $DEPLOY_HTTPS_INPUT " ] ; then
DEPLOY_HTTPS = $DEPLOY_HTTPS_INPUT
else
read -r -p "3.1 是否启用 HTTPS? [y/N]: " https_choice
DEPLOY_HTTPS = false
[ [ " $https_choice " = ~ ^[ yY] ] ] && DEPLOY_HTTPS = true
fi
fi
}
# ==================== 生成配置文件 ====================
setup_env_files( ) {
echo ""
echo "📝 配置环境文件..."
# 6. 复制 .env.tools 和 .env.ASG
[ ! -f .env.tools ] && cp .env.tools.sample .env.tools && echo " ✓ .env.tools"
[ ! -f .env.ASG ] && cp .env.ASG.sample .env.ASG && echo " ✓ .env.ASG"
# 4/5. 配置 .env
if [ ! -f .env ] ; then
cp .env.sample .env
fi
if [ " $DEPLOY_MODE " = "ip" ] ; then
# IP 模式
sed -i.bak " s|^NUXT_PUBLIC_BASE_APIURL=.*|NUXT_PUBLIC_BASE_APIURL=http:// ${ DEPLOY_IP } :3001| " .env
sed -i.bak " s|^NUXT_PUBLIC_BASE_SOCKETURL=.*|NUXT_PUBLIC_BASE_SOCKETURL=ws:// ${ DEPLOY_IP } :3002| " .env
sed -i.bak " s|^NUXT_PUBLIC_SG_APIURL=.*|NUXT_PUBLIC_SG_APIURL=http:// ${ DEPLOY_IP } :3003| " .env
echo " ✓ .env 已配置为 IP 模式 ( ${ DEPLOY_IP } ) "
else
# 域名模式
sed -i.bak "s|^NUXT_PUBLIC_BASE_APIURL=.*|NUXT_PUBLIC_BASE_APIURL=/api|" .env
sed -i.bak " s|^NUXT_PUBLIC_BASE_SOCKETURL=.*|NUXT_PUBLIC_BASE_SOCKETURL=wss:// ${ DEPLOY_DOMAIN } /socket.io| " .env
sed -i.bak "s|^NUXT_PUBLIC_SG_APIURL=.*|NUXT_PUBLIC_SG_APIURL=/asg-api|" .env
echo " ✓ .env 已配置为域名模式 ( ${ DEPLOY_DOMAIN } ) "
# 7. Nginx 配置(域名模式)
PROXY_CONF = " ${ DEPLOY_DOMAIN } .conf "
if [ ! -f " $PROXY_CONF " ] ; then
sed " s/51easyai.com/ ${ DEPLOY_DOMAIN } /g " easyai-proxy.conf.sample > " $PROXY_CONF "
echo " ✓ Nginx 配置已生成: $PROXY_CONF "
fi
fi
rm -f .env.bak
}
# ==================== Docker 安装(复用原 start.sh 逻辑) ====================
install_docker( ) {
echo ""
echo "================================"
echo " Docker 安装与检查"
echo "================================"
if [ -f /etc/os-release ] ; then
. /etc/os-release
case " ${ ID :- } " in
ubuntu| debian) OS_FAMILY = "Ubuntu" ; ;
centos| rhel| fedora) OS_FAMILY = "CentOS" ; ;
*) OS_FAMILY = " ${ ID :- Unknown } " ; ;
esac
else
OS_FAMILY = $( hostnamectl 2>/dev/null | grep "Operating System" | awk '{print $3}' || echo "Unknown" )
fi
OS_VERSION_ID = $( grep -oP '(?<=^VERSION_ID=")[0-9.]+' /etc/os-release 2>/dev/null | cut -d'.' -f1 || echo "0" )
OS_CODENAME = ""
[ [ " $OS_FAMILY " = = "Ubuntu" ] ] && OS_CODENAME = $( lsb_release -cs 2>/dev/null || ( grep VERSION_CODENAME /etc/os-release 2>/dev/null | cut -d= -f2 | tr -d '"' ) )
UBUNTU_DOCKER_MIRROR_URL = "https://mirrors.ustc.edu.cn/docker-ce"
UBUNTU_DOCKER_GPG_URL = "https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg"
CENTOS_DOCKER_MIRROR_URL = "https://mirrors.tuna.tsinghua.edu.cn/docker-ce"
check_network( ) {
if curl -sSf --connect-timeout 5 https://download.docker.com/ & >/dev/null; then
echo "✅ 网络连接正常"
return 0
fi
echo "⚠️ 将使用国内镜像源"
return 0
}
check_network
if command -v docker & >/dev/null; then
echo "✅ Docker 已安装"
else
if [ [ " $OS_FAMILY " = = "Ubuntu" ] ] ; then
sudo apt update -y
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL " $UBUNTU_DOCKER_GPG_URL " | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null || \
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo " deb [arch= $( dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $UBUNTU_DOCKER_MIRROR_URL /linux/ubuntu $OS_CODENAME stable " | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
elif [ [ " $OS_FAMILY " = = "CentOS" ] ] ; then
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo sed -i " s+https://download.docker.com+ $CENTOS_DOCKER_MIRROR_URL +g " /etc/yum.repos.d/docker-ce.repo
[ [ " $OS_VERSION_ID " -ge "8" ] ] && sudo yum module disable -y container-tools 2>/dev/null || true
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
else
echo " ❌ 不支持的操作系统: $OS_FAMILY "
exit 1
fi
sudo systemctl enable docker
sudo systemctl start docker
getent group docker | grep -qw " $USER " || sudo usermod -aG docker " $USER "
fi
if docker compose version & >/dev/null; then
echo "✅ Docker Compose 已就绪"
elif command -v docker-compose & >/dev/null; then
echo "✅ Docker Compose (兼容模式) 已就绪"
else
echo "❌ 请安装 docker-compose-plugin"
exit 1
fi
}
echo "✅ 启动并设置 Docker 开机自启..."
sudo systemctl enable docker
sudo systemctl start docker
# 将当前用户添加到 docker 组,以便无需 sudo 运行 Docker 命令
if ! getent group docker | grep -qw " $USER " ; then
echo " 👥 将当前用户 ( $USER ) 添加到 docker 组... "
sudo usermod -aG docker " $USER "
echo "🔔 请注销并重新登录,以便更改生效。"
fi
fi
# Docker Compose 检查和安装 (优先使用 docker-compose-plugin)
if command -v docker & > /dev/null && docker compose version & > /dev/null; then
echo "✅ Docker Compose (插件版) 已安装,跳过安装步骤"
elif command -v docker-compose & > /dev/null && ! command -v docker & > /dev/null; then
echo "⚠️ 检测到旧版 Docker Compose 独立安装,但未检测到 Docker 插件版。"
echo "建议在安装 Docker CE 时一起安装 docker-compose-plugin。"
else
echo "⚙️ 安装 Docker Compose (插件版)..."
PLUGIN_INSTALL_SUCCESS = 1
# 如果 Docker CE 安装成功,docker-compose-plugin 应该已经安装了。
# 这里是额外的检查,以防万一。
if [ [ " $OS_FAMILY " = = "Ubuntu" ] ] ; then
sudo apt-get install -y docker-compose-plugin || PLUGIN_INSTALL_SUCCESS = 0
elif [ [ " $OS_FAMILY " = = "CentOS" ] ] ; then
sudo yum install -y docker-compose-plugin || PLUGIN_INSTALL_SUCCESS = 0
# ==================== 启动服务 ====================
start_services( ) {
echo ""
echo "🚀 启动 EasyAI 服务..."
if docker compose version & >/dev/null; then
sudo docker compose pull && sudo docker compose up -d
else
echo "❌ 未知操作系统,无法安装 D ocker C ompose 插件版。"
PLUGIN_INSTALL_SUCCESS = 0
sudo docker-compose pull && sudo d ocker-c ompose up -d
fi
echo "🎉 EasyAI 应用启动成功"
}
# 检查插件版安装是否成功
if [ [ $PLUGIN_INSTALL_SUCCESS -eq 1 ] ] ; then
echo "✅ Docker Compose 插件版安装成功"
else
echo "⚠️ Docker Compose 插件版安装失败,尝试使用本地二进制文件安装 ..."
#将文件移动至/usr/bin目录下,并重命名
sudo cp ./docker-compose-linux-x86_64 /usr/bin/docker-compose
# 添加执行权限
sudo chmod +x /usr/bin/docker-compose
# 验证安装
if command -v docker-compose & > /dev/null; then
echo "✅ Docker Compose 二进制文件安装成功"
# ==================== 执行 HTTPS 配置 ====================
run_https_setup( ) {
if [ " $DEPLOY_HTTPS " = true ] && [ -n " $DEPLOY_DOMAIN " ] ; then
echo ""
echo "🔒 执行 HTTPS 配置 ..."
if [ -f "./https.sh" ] ; then
# https.sh 依赖 easyai-proxy.conf,需使用生成的域名配置文件
export EASYAI_PROXY_CONF = " ${ DEPLOY_DOMAIN } .conf "
bash ./https.sh
else
echo "❌ Docker Compose 二进制文件安装失败,请手动检查。 "
exit 1
echo "⚠️ 未找到 https.sh,请手动配置 HTTPS "
fi
fi
fi
}
# ==================== 主流程 ====================
main( ) {
init_project_dir
echo "📌 Docker 运行状态:"
sudo systemctl status docker --no-pager || true
# 检查是否已有 .env 且非强制重新配置
if [ -f .env ] && [ -z " $DEPLOY_FORCE_RECONFIG " ] && [ -z " $DEPLOY_ACCESS " ] ; then
echo "📁 检测到已有 .env 配置"
read -r -p "是否重新配置部署方式?[y/N]: " reconfigure
if [ [ ! " $reconfigure " = ~ ^[ yY] ] ] ; then
echo "⏭️ 使用现有配置继续..."
DEPLOY_MODE = "skip"
fi
fi
echo "📌 Docker Compose 版本:"
# 优先使用 docker compose 命令(新版),如果不行再尝试 docker-compose(旧版或别名)
if command -v docker & > /dev/null && docker compose version & > /dev/null; then
docker compose version
elif command -v docker-compose & > /dev/null; then
docker-compose -v
else
echo "❌ 无法检测 Docker Compose 版本。"
fi
if [ " $DEPLOY_MODE " != "skip" ] ; then
run_deploy_questions
fi
echo "🎉 Docker 和 Docker Compose 已就绪!"
if [ " $DEPLOY_MODE " != "skip" ] ; then
setup_env_files
else
[ ! -f .env.tools ] && cp .env.tools.sample .env.tools
[ ! -f .env.ASG ] && cp .env.ASG.sample .env.ASG
fi
echo "🚀 启动EasyAI"
# 对于新版 docker-compose-plugin,命令是 'docker compose'
if command -v docker & > /dev/null && docker compose version & > /dev/null; then
sudo docker compose pull && sudo docker compose up -d
else # 兼容旧版独立安装的 docker-compose
sudo docker-compose pull && sudo docker-compose up -d
fi
echo "🎉EasyAI应用启动成功"
if [ " $DEPLOY_DRY_RUN " = "1" ] ; then
echo ""
echo "⚠️ dry-run 模式:跳过 Docker 安装和服务启动"
echo " 配置文件已生成,可直接运行 ./start.sh 完成部署"
else
install_docker
start_services
run_https_setup
fi
echo ""
echo "================================"
echo " 部署完成"
echo "================================"
if [ " $DEPLOY_MODE " = "ip" ] && [ -n " $DEPLOY_IP " ] ; then
echo " 访问地址: http:// ${ DEPLOY_IP } :3010 "
elif [ " $DEPLOY_MODE " = "domain" ] && [ -n " $DEPLOY_DOMAIN " ] ; then
echo " 访问地址: http:// ${ DEPLOY_DOMAIN } (配置 Nginx 后) "
[ " $DEPLOY_HTTPS " = true ] && echo "HTTPS 已启用"
fi
echo ""
}
main " $@ "