2026-06-03 11:37:55 +08:00

404 lines
13 KiB
Bash
Raw Permalink 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
#
# 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 "$@"