#!/bin/bash # # FLNXI Agent 安装脚本 # 只安装 Agent,不等于完整安装素材库。 # 用法: 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' _____ _ _ ___ __ ___ | ___| | | \ | \ \/ /|_ _| | |_ | | | \| |\ / | | | _| | |___| |\ |/ \ | | |_| |_____|_| \_/_/\_\|___| 智能运维平台 - Agent 安装脚本 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:-}" -e "FLNXI_MODULE_NETWORK=${FLNXI_MODULE_NETWORK:-flnxi-network}" -e "FLNXI_MODULE_STATUS_CALLBACK_URL=${FLNXI_MODULE_STATUS_CALLBACK_URL:-}" -e "FLNXI_MODULE_STATUS_CALLBACK_TOKEN=${FLNXI_MODULE_STATUS_CALLBACK_TOKEN:-}" ) 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 Agent 安装脚本" 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 "$@"