400 lines
12 KiB
Bash
400 lines
12 KiB
Bash
#!/bin/bash
|
||
#
|
||
# FLNXI 一键部署脚本
|
||
# 用法: curl -sSL https://install.flnxi.com | bash
|
||
#
|
||
# 或者带参数:
|
||
# curl -sSL https://install.flnxi.com | bash -s -- --port 9090
|
||
#
|
||
|
||
set -e
|
||
|
||
# 颜色定义
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 默认配置
|
||
AGENT_PORT=${AGENT_PORT:-9090}
|
||
AGENT_PUBLISH_HOST=${AGENT_PUBLISH_HOST:-127.0.0.1}
|
||
MAX_PORT_SCAN_STEPS=${MAX_PORT_SCAN_STEPS:-20}
|
||
AGENT_IMAGE=${AGENT_IMAGE:-}
|
||
CONTAINER_NAME="flnxi-agent"
|
||
REQUESTED_AGENT_PORT=$AGENT_PORT
|
||
AGENT_AUTH_USERNAME=${AGENT_AUTH_USERNAME:-}
|
||
AGENT_AUTH_PASSWORD=${AGENT_AUTH_PASSWORD:-}
|
||
|
||
# 打印带颜色的消息
|
||
print_info() {
|
||
echo -e "${BLUE}[INFO]${NC} $1"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}[ERROR]${NC} $1"
|
||
}
|
||
|
||
# 显示 Banner
|
||
show_banner() {
|
||
echo -e "${BLUE}"
|
||
cat << 'EOF'
|
||
_____ _ _ ___ __ ___
|
||
| ___| | | \ | \ \/ /|_ _|
|
||
| |_ | | | \| |\ / | |
|
||
| _| | |___| |\ |/ \ | |
|
||
|_| |_____|_| \_/_/\_\|___|
|
||
|
||
智能运维平台 - 一键部署脚本
|
||
EOF
|
||
echo -e "${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# 检测操作系统
|
||
detect_os() {
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
OS=$ID
|
||
VERSION=$VERSION_ID
|
||
elif [ -f /etc/redhat-release ]; then
|
||
OS="centos"
|
||
else
|
||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||
fi
|
||
|
||
print_info "检测到操作系统: $OS"
|
||
}
|
||
|
||
|
||
# 检查是否为 root 用户
|
||
check_root() {
|
||
if [ "$EUID" -ne 0 ]; then
|
||
print_warning "建议使用 root 用户运行此脚本"
|
||
print_info "尝试使用 sudo 继续..."
|
||
SUDO="sudo"
|
||
else
|
||
SUDO=""
|
||
fi
|
||
}
|
||
|
||
# 检查 Docker 是否已安装
|
||
check_docker() {
|
||
if command -v docker &> /dev/null; then
|
||
DOCKER_VERSION=$(docker --version | cut -d ' ' -f3 | cut -d ',' -f1)
|
||
print_success "Docker 已安装 (版本: $DOCKER_VERSION)"
|
||
|
||
# 设置 docker 命令(根据权限决定是否需要 sudo)
|
||
if docker ps &> /dev/null; then
|
||
DOCKER="docker"
|
||
else
|
||
DOCKER="sudo docker"
|
||
print_info "将使用 sudo 执行 docker 命令"
|
||
fi
|
||
return 0
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# 安装 Docker
|
||
install_docker() {
|
||
print_info "正在安装 Docker..."
|
||
|
||
case $OS in
|
||
ubuntu|debian)
|
||
$SUDO apt-get update
|
||
$SUDO apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
||
curl -fsSL https://download.docker.com/linux/$OS/gpg | $SUDO gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/$OS $(lsb_release -cs) stable" | $SUDO tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||
$SUDO apt-get update
|
||
$SUDO apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||
;;
|
||
centos|rhel|fedora|rocky|almalinux)
|
||
$SUDO yum install -y yum-utils
|
||
$SUDO yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||
$SUDO yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||
;;
|
||
*)
|
||
print_warning "未知的操作系统,尝试使用官方安装脚本..."
|
||
curl -fsSL https://get.docker.com | $SUDO sh
|
||
;;
|
||
esac
|
||
|
||
# 启动 Docker 服务
|
||
$SUDO systemctl start docker
|
||
$SUDO systemctl enable docker
|
||
|
||
# 将当前用户添加到 docker 组
|
||
if [ -n "$SUDO_USER" ]; then
|
||
$SUDO usermod -aG docker $SUDO_USER
|
||
elif [ "$EUID" -ne 0 ]; then
|
||
$SUDO usermod -aG docker $USER
|
||
fi
|
||
|
||
print_success "Docker 安装完成"
|
||
}
|
||
|
||
# 检查指定端口是否被占用
|
||
is_port_in_use() {
|
||
local port="$1"
|
||
|
||
if command -v ss &> /dev/null; then
|
||
ss -H -tuln | awk '{print $5}' | grep -Eq "(^|\\]|:)$port$"
|
||
return $?
|
||
fi
|
||
|
||
if command -v netstat &> /dev/null; then
|
||
netstat -tuln 2>/dev/null | awk 'NR>2 {print $4}' | grep -Eq "(^|\\]|:)$port$"
|
||
return $?
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
# 自动选择可用端口
|
||
ensure_available_port() {
|
||
local requested_port="$AGENT_PORT"
|
||
local candidate_port="$requested_port"
|
||
local attempt=0
|
||
|
||
while [ $attempt -le "$MAX_PORT_SCAN_STEPS" ]; do
|
||
if ! is_port_in_use "$candidate_port"; then
|
||
AGENT_PORT="$candidate_port"
|
||
if [ "$AGENT_PORT" != "$requested_port" ]; then
|
||
print_warning "端口 $requested_port 已被占用,自动改用 $AGENT_PORT"
|
||
else
|
||
print_success "端口 $AGENT_PORT 可用"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
attempt=$((attempt + 1))
|
||
candidate_port=$((requested_port + attempt))
|
||
done
|
||
|
||
print_error "从端口 $requested_port 开始连续 $((MAX_PORT_SCAN_STEPS + 1)) 个端口都已被占用"
|
||
print_info "请使用 --port 参数指定其他端口,或释放对应端口后重试"
|
||
exit 1
|
||
}
|
||
|
||
# 停止并删除旧容器
|
||
cleanup_old_container() {
|
||
if $DOCKER ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||
print_info "发现旧的 Agent 容器,正在清理..."
|
||
$DOCKER stop $CONTAINER_NAME 2>/dev/null || true
|
||
$DOCKER rm $CONTAINER_NAME 2>/dev/null || true
|
||
print_success "旧容器已清理"
|
||
fi
|
||
}
|
||
|
||
|
||
# 拉取并运行 Agent
|
||
run_agent() {
|
||
if [ -z "$AGENT_IMAGE" ]; then
|
||
print_error "未指定 Agent 镜像"
|
||
print_info "请通过 AGENT_IMAGE 环境变量或 --image 参数传入完整版本镜像,例如:registry.example.com/flnxi/agent:1.0.1"
|
||
exit 1
|
||
fi
|
||
|
||
print_info "正在拉取 Agent 镜像..."
|
||
$DOCKER pull $AGENT_IMAGE
|
||
|
||
print_info "正在启动 Agent..."
|
||
run_args=(
|
||
-d
|
||
--name "$CONTAINER_NAME"
|
||
--restart unless-stopped
|
||
-p "${AGENT_PUBLISH_HOST}:${AGENT_PORT}:9090"
|
||
-v /var/run/docker.sock:/var/run/docker.sock
|
||
-v flnxi-data:/data
|
||
)
|
||
|
||
run_args+=(
|
||
-e "FLNXI_MATERIAL_IMAGE=${FLNXI_MATERIAL_IMAGE:-}"
|
||
-e "APP_IMAGE=${APP_IMAGE:-}"
|
||
-e "FLNXI_MATERIAL_IMAGE_REPOSITORY=${FLNXI_MATERIAL_IMAGE_REPOSITORY:-}"
|
||
-e "FLNXI_POSTGRES_IMAGE=${FLNXI_POSTGRES_IMAGE:-}"
|
||
-e "POSTGRES_IMAGE=${POSTGRES_IMAGE:-}"
|
||
-e "FLNXI_MINIO_IMAGE=${FLNXI_MINIO_IMAGE:-}"
|
||
-e "MINIO_IMAGE=${MINIO_IMAGE:-}"
|
||
-e "FLNXI_VERSION_LIST_URL=${FLNXI_VERSION_LIST_URL:-}"
|
||
-e "LICENSE_SERVER_URL=${LICENSE_SERVER_URL:-}"
|
||
-e "LICENSE_PROJECT_CODE=${LICENSE_PROJECT_CODE:-}"
|
||
-e "LICENSE_REQUEST_SIGNING_SECRET=${LICENSE_REQUEST_SIGNING_SECRET:-}"
|
||
-e "LICENSE_PUBLIC_KEY=${LICENSE_PUBLIC_KEY:-}"
|
||
-e "LOCAL_ENCRYPTION_KEY=${LOCAL_ENCRYPTION_KEY:-}"
|
||
)
|
||
|
||
if [ -z "${FLNXI_MATERIAL_IMAGE:-${APP_IMAGE:-}}" ]; then
|
||
print_warning "未配置 FLNXI_MATERIAL_IMAGE / APP_IMAGE,Agent 可启动,但安装素材系统前必须补充完整主应用版本镜像"
|
||
fi
|
||
|
||
if [ -z "${LICENSE_SERVER_URL:-}" ] || [ -z "${LICENSE_PROJECT_CODE:-}" ] || [ -z "${LICENSE_REQUEST_SIGNING_SECRET:-}" ] || [ -z "${LICENSE_PUBLIC_KEY:-}" ]; then
|
||
print_warning "未完整配置许可证服务参数,Agent 安装素材系统时会先拦截并提示补齐"
|
||
fi
|
||
|
||
if [ -n "$AGENT_AUTH_USERNAME" ] && [ -n "$AGENT_AUTH_PASSWORD" ]; then
|
||
run_args+=(
|
||
-e "AGENT_AUTH_USERNAME=$AGENT_AUTH_USERNAME"
|
||
-e "AGENT_AUTH_PASSWORD=$AGENT_AUTH_PASSWORD"
|
||
)
|
||
elif [ -n "$AGENT_AUTH_USERNAME" ] || [ -n "$AGENT_AUTH_PASSWORD" ]; then
|
||
print_warning "检测到 AGENT_AUTH_USERNAME / AGENT_AUTH_PASSWORD 只配置了一项,已忽略认证参数"
|
||
fi
|
||
|
||
run_args+=("$AGENT_IMAGE")
|
||
$DOCKER run "${run_args[@]}"
|
||
|
||
# 等待容器启动
|
||
sleep 2
|
||
|
||
if $DOCKER ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||
print_success "Agent 启动成功"
|
||
else
|
||
print_error "Agent 启动失败"
|
||
$DOCKER logs $CONTAINER_NAME
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# 获取本机 IP
|
||
get_local_ip() {
|
||
# 尝试多种方式获取 IP
|
||
LOCAL_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||
if [ -z "$LOCAL_IP" ]; then
|
||
LOCAL_IP=$(ip route get 1 2>/dev/null | awk '{print $7}' | head -1)
|
||
fi
|
||
if [ -z "$LOCAL_IP" ]; then
|
||
LOCAL_IP="localhost"
|
||
fi
|
||
echo $LOCAL_IP
|
||
}
|
||
|
||
# 显示完成信息
|
||
show_completion() {
|
||
LOCAL_IP=$(get_local_ip)
|
||
|
||
echo ""
|
||
echo -e "${GREEN}========================================${NC}"
|
||
echo -e "${GREEN} FLNXI Agent 部署完成! ${NC}"
|
||
echo -e "${GREEN}========================================${NC}"
|
||
echo ""
|
||
if [ "$AGENT_PUBLISH_HOST" = "127.0.0.1" ] || [ "$AGENT_PUBLISH_HOST" = "localhost" ]; then
|
||
echo -e "访问地址: ${BLUE}http://127.0.0.1:${AGENT_PORT}${NC}"
|
||
echo -e "${YELLOW}当前仅发布到宿主机回环地址。建议通过同机浏览器或反向代理访问。${NC}"
|
||
echo -e "${YELLOW}如需局域网直连,请重新部署并设置 --publish-host 0.0.0.0${NC}"
|
||
else
|
||
echo -e "访问地址: ${BLUE}http://${LOCAL_IP}:${AGENT_PORT}${NC}"
|
||
echo -e "${YELLOW}当前已对局域网暴露端口,请确保已配置 Agent 管理认证或前置反向代理。${NC}"
|
||
fi
|
||
echo ""
|
||
echo -e "常用命令:"
|
||
echo -e " 查看日志: ${YELLOW}docker logs -f $CONTAINER_NAME${NC}"
|
||
echo -e " 重启服务: ${YELLOW}docker restart $CONTAINER_NAME${NC}"
|
||
echo -e " 停止服务: ${YELLOW}docker stop $CONTAINER_NAME${NC}"
|
||
echo -e " 卸载服务: ${YELLOW}docker rm -f $CONTAINER_NAME${NC}"
|
||
echo ""
|
||
echo -e "请在浏览器中打开上述地址,按实施方案使用 Agent 编排或运维。"
|
||
echo -e "${YELLOW}正式客户首装主线仍是 docker-compose.prod.yml + .env + smoke-install.ps1;Agent 不是唯一安装入口。${NC}"
|
||
echo ""
|
||
}
|
||
|
||
# 解析命令行参数
|
||
parse_args() {
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
--port)
|
||
AGENT_PORT="$2"
|
||
shift 2
|
||
;;
|
||
--image)
|
||
AGENT_IMAGE="$2"
|
||
shift 2
|
||
;;
|
||
--publish-host)
|
||
AGENT_PUBLISH_HOST="$2"
|
||
shift 2
|
||
;;
|
||
--name)
|
||
CONTAINER_NAME="$2"
|
||
shift 2
|
||
;;
|
||
--help|-h)
|
||
show_help
|
||
exit 0
|
||
;;
|
||
*)
|
||
print_error "未知参数: $1"
|
||
show_help
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# 显示帮助信息
|
||
show_help() {
|
||
echo "FLNXI 一键部署脚本"
|
||
echo ""
|
||
echo "用法: curl -sSL https://install.flnxi.com | bash -s -- [选项]"
|
||
echo ""
|
||
echo "选项:"
|
||
echo " --port PORT 指定 Agent 端口 (默认: 9090)"
|
||
echo " --publish-host 指定宿主机发布地址 (默认: 127.0.0.1)"
|
||
echo " --image IMAGE 指定 Agent 镜像,必须是完整版本镜像,例如 registry.example.com/flnxi/agent:1.0.1"
|
||
echo " --name NAME 指定容器名称 (默认: flnxi-agent)"
|
||
echo " --help, -h 显示帮助信息"
|
||
echo ""
|
||
echo "环境变量:"
|
||
echo " AGENT_IMAGE 必填;完整 Agent 版本镜像"
|
||
echo " AGENT_AUTH_USERNAME / AGENT_AUTH_PASSWORD 可选;若两者都设置,安装脚本会自动透传到容器"
|
||
echo ""
|
||
echo "示例:"
|
||
echo " curl -sSL https://install.flnxi.com | bash -s -- --image registry.example.com/flnxi/agent:1.0.1"
|
||
echo " curl -sSL https://install.flnxi.com | bash -s -- --image registry.example.com/flnxi/agent:1.0.1 --port 8080"
|
||
echo " curl -sSL https://install.flnxi.com | bash -s -- --image registry.example.com/flnxi/agent:1.0.1 --publish-host 0.0.0.0"
|
||
}
|
||
|
||
# 主函数
|
||
main() {
|
||
show_banner
|
||
parse_args "$@"
|
||
|
||
print_info "开始部署 FLNXI Agent..."
|
||
echo ""
|
||
|
||
# 检测环境
|
||
detect_os
|
||
check_root
|
||
|
||
# 检查并安装 Docker
|
||
if ! check_docker; then
|
||
print_info "Docker 未安装,开始安装..."
|
||
install_docker
|
||
fi
|
||
|
||
# 检查端口并自动选择可用端口
|
||
ensure_available_port
|
||
|
||
# 清理旧容器
|
||
cleanup_old_container
|
||
|
||
# 运行 Agent
|
||
run_agent
|
||
|
||
# 显示完成信息
|
||
show_completion
|
||
}
|
||
|
||
# 运行主函数
|
||
main "$@"
|