711 lines
23 KiB
Bash
Executable File
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
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 配置变量
INSTALL_DIR="${1:-$(pwd)}" # 使用第一个参数作为安装目录,如果没有参数则使用当前目录
TEMP_DIR="/tmp/metrics-install-$$"
VERSION_FILE="version.json"
# 加载配置文件
load_config() {
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local config_file="$script_dir/config.env"
if [[ -f "$config_file" ]]; then
log_info "加载配置文件: $config_file"
# 导出配置文件中的环境变量
set -a # 自动导出所有变量
source "$config_file"
set +a # 关闭自动导出
log_success "配置文件加载完成"
else
log_warning "配置文件不存在: $config_file,使用默认配置"
fi
}
# 复制配置文件到安装目录
copy_config_files() {
log_info "复制配置文件到安装目录..."
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local source_config="$script_dir/config.env"
local target_config="$INSTALL_DIR/config.env"
if [[ -f "$source_config" ]]; then
# 检查源文件和目标文件是否是同一个文件
if [[ "$source_config" == "$target_config" ]]; then
log_info "配置文件已在目标位置,跳过复制"
log_success "配置文件已存在: $target_config"
else
if cp "$source_config" "$target_config"; then
log_success "配置文件复制完成: $target_config"
else
log_error "配置文件复制失败"
return 1
fi
fi
else
log_warning "源配置文件不存在: $source_config"
fi
}
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本需要 root 权限运行"
log_info "请使用: sudo $0 [安装目录]"
log_info "如果不指定安装目录,将使用当前目录: $(pwd)"
exit 1
fi
}
# 检查系统要求
check_system() {
log_info "检查系统要求..."
# 检查操作系统
if [[ ! -f /etc/os-release ]]; then
log_error "无法检测操作系统版本"
exit 1
fi
source /etc/os-release
log_info "检测到操作系统: $NAME $VERSION"
# 检查系统架构
arch=$(uname -m)
log_info "系统架构: $arch"
# 检查磁盘空间
available_space=$(df / | awk 'NR==2 {print $4}')
if [[ $available_space -lt 10485760 ]]; then # 10GB in KB
log_warning "可用磁盘空间不足 10GB当前可用: $(($available_space / 1024 / 1024))GB"
fi
# 检查内存
total_mem=$(free -m | awk 'NR==2{print $2}')
if [[ $total_mem -lt 4096 ]]; then # 4GB
log_warning "系统内存不足 4GB当前: ${total_mem}MB"
fi
}
# 查找版本文件
find_version_file() {
log_info "查找版本信息文件..."
# 在当前目录查找
if [[ -f "$VERSION_FILE" ]]; then
VERSION_FILE_PATH="$VERSION_FILE"
log_success "找到版本文件: $VERSION_FILE"
return 0
fi
# 在 artifact 目录查找
for version_dir in artifact/*/; do
if [[ -f "${version_dir}${VERSION_FILE}" ]]; then
VERSION_FILE_PATH="${version_dir}${VERSION_FILE}"
log_success "找到版本文件: $VERSION_FILE_PATH"
return 0
fi
done
log_error "未找到版本信息文件 $VERSION_FILE"
exit 1
}
# 解析版本信息
parse_version_info() {
log_info "解析版本信息..."
if [[ ! -f "$VERSION_FILE_PATH" ]]; then
log_error "版本文件不存在: $VERSION_FILE_PATH"
exit 1
fi
# 使用 jq 解析 JSON如果可用
if command -v jq &> /dev/null; then
# 验证JSON文件格式
if ! jq empty "$VERSION_FILE_PATH" 2>/dev/null; then
log_error "JSON文件格式错误请检查 $VERSION_FILE_PATH"
exit 1
fi
VERSION=$(jq -r '.version' "$VERSION_FILE_PATH")
BUILD_TIME=$(jq -r '.build_time' "$VERSION_FILE_PATH")
# 解析 artifact_list
if jq -e '.artifact_list' "$VERSION_FILE_PATH" > /dev/null 2>&1; then
jq -r '.artifact_list | to_entries[] | "\(.key):\(.value)"' "$VERSION_FILE_PATH" > "$TEMP_DIR/components.txt"
else
log_error "version.json 中缺少 artifact_list 字段"
exit 1
fi
# 解析 checksums
if jq -e '.checksums' "$VERSION_FILE_PATH" > /dev/null 2>&1; then
jq -r '.checksums | to_entries[] | "\(.key):\(.value)"' "$VERSION_FILE_PATH" > "$TEMP_DIR/checksums.txt"
else
log_error "version.json 中缺少 checksums 字段"
exit 1
fi
# 解析 install_order现在包含完整的文件名
if jq -e '.install_order' "$VERSION_FILE_PATH" > /dev/null 2>&1; then
jq -r '.install_order[]' "$VERSION_FILE_PATH" > "$TEMP_DIR/install_order.txt"
else
log_error "version.json 中缺少 install_order 字段"
exit 1
fi
else
log_warning "jq 未安装,使用简单的 JSON 解析"
# 简单的 JSON 解析
VERSION=$(grep '"version"' "$VERSION_FILE_PATH" | sed 's/.*"version": *"\([^"]*\)".*/\1/')
BUILD_TIME=$(grep '"build_time"' "$VERSION_FILE_PATH" | sed 's/.*"build_time": *"\([^"]*\)".*/\1/')
# 解析 artifact_list
grep -A 100 '"artifact_list"' "$VERSION_FILE_PATH" | grep -E '^\s*"[^"]+":\s*"[^"]+"' | while read line; do
component=$(echo "$line" | sed 's/.*"\([^"]*\)":\s*"[^"]*".*/\1/')
version=$(echo "$line" | sed 's/.*"[^"]*":\s*"\([^"]*\)".*/\1/')
echo "$component:$version" >> "$TEMP_DIR/components.txt"
done
# 解析 checksums
grep -A 100 '"checksums"' "$VERSION_FILE_PATH" | grep -E '^\s*"[^"]+":\s*"[^"]+"' | while read line; do
component=$(echo "$line" | sed 's/.*"\([^"]*\)":\s*"[^"]*".*/\1/')
checksum=$(echo "$line" | sed 's/.*"[^"]*":\s*"\([^"]*\)".*/\1/')
echo "$component:$checksum" >> "$TEMP_DIR/checksums.txt"
done
# 解析 install_order
grep -A 100 '"install_order"' "$VERSION_FILE_PATH" | grep -E '^\s*"[^"]+"' | while read line; do
component=$(echo "$line" | sed 's/.*"\([^"]*\)".*/\1/')
echo "$component" >> "$TEMP_DIR/install_order.txt"
done
# 验证解析结果
if [[ ! -f "$TEMP_DIR/components.txt" || ! -s "$TEMP_DIR/components.txt" ]]; then
log_error "无法解析 artifact_list请检查 version.json 格式"
exit 1
fi
if [[ ! -f "$TEMP_DIR/checksums.txt" || ! -s "$TEMP_DIR/checksums.txt" ]]; then
log_error "无法解析 checksums请检查 version.json 格式"
exit 1
fi
if [[ ! -f "$TEMP_DIR/install_order.txt" || ! -s "$TEMP_DIR/install_order.txt" ]]; then
log_error "无法解析 install_order请检查 version.json 格式"
exit 1
fi
fi
log_success "版本信息解析完成"
log_info " 版本: $VERSION"
log_info " 构建时间: $BUILD_TIME"
component_count=0
if [[ -f "$TEMP_DIR/components.txt" ]]; then
component_count=$(wc -l < "$TEMP_DIR/components.txt")
log_info " 组件数量: $component_count"
log_info " 组件列表:"
while IFS= read -r line; do
component=$(echo "$line" | cut -d':' -f1)
version=$(echo "$line" | cut -d':' -f2)
log_info " - $component v$version"
done < "$TEMP_DIR/components.txt"
else
log_error "components.txt 文件不存在"
exit 1
fi
}
# 验证文件完整性
verify_checksums() {
log_info "验证文件完整性..."
artifact_dir=$(dirname "$VERSION_FILE_PATH")
failed_verification=0
if [[ -f "$TEMP_DIR/checksums.txt" ]]; then
while IFS= read -r line; do
component=$(echo "$line" | cut -d':' -f1)
expected_checksum=$(echo "$line" | cut -d':' -f2-)
# 查找匹配的 tar 文件
actual_file=""
for file in "$artifact_dir/${component}-"*.tar.gz; do
if [[ -f "$file" ]]; then
actual_file="$file"
break
fi
done
if [[ -z "$actual_file" ]]; then
log_error "找不到组件文件: $component"
failed_verification=1
continue
fi
# 计算实际校验和
actual_checksum="sha256:$(sha256sum "$actual_file" | cut -d' ' -f1)"
if [[ "$actual_checksum" == "$expected_checksum" ]]; then
log_success " $component: 校验通过"
else
log_error " $component: 校验失败"
log_error " 期望: $expected_checksum"
log_error " 实际: $actual_checksum"
failed_verification=1
fi
done < "$TEMP_DIR/checksums.txt"
fi
if [[ $failed_verification -eq 1 ]]; then
log_error "文件完整性验证失败"
exit 1
fi
log_success "所有文件校验通过"
}
# 创建安装目录
create_install_dirs() {
log_info "创建安装目录..."
mkdir -p "$INSTALL_DIR"
mkdir -p "$TEMP_DIR"
log_success "安装目录创建完成: $INSTALL_DIR"
}
# 安装系统依赖包
install_system_deps() {
log_info "检查系统依赖包..."
local script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local deps_dir="$script_dir/deps"
# 检查deps目录是否存在
if [[ ! -d "$deps_dir" ]]; then
log_info "deps 目录不存在,跳过系统依赖包安装"
return 0
fi
# 检查是否有tar.gz文件
local deps_count=$(find "$deps_dir" -name "*.tar.gz" | wc -l)
if [[ $deps_count -eq 0 ]]; then
log_info "deps 目录中没有 tar.gz 文件,跳过系统依赖包安装"
return 0
fi
log_info "找到 $deps_count 个系统依赖包,开始安装..."
# 创建临时目录用于解压依赖包
local deps_temp_dir="$TEMP_DIR/deps"
mkdir -p "$deps_temp_dir"
# 处理每个tar.gz文件
find "$deps_dir" -name "*.tar.gz" | while read tar_file; do
local tar_basename=$(basename "$tar_file")
local extract_name="${tar_basename%.tar.gz}"
log_info "处理依赖包: $tar_basename"
# 解压到临时目录
local extract_dir="$deps_temp_dir/$extract_name"
mkdir -p "$extract_dir"
if tar -xzf "$tar_file" -C "$extract_dir" 2>/dev/null; then
log_success " $tar_basename 解压完成"
else
log_error " $tar_basename 解压失败"
continue
fi
# 进入解压目录查找deb包
cd "$extract_dir"
local deb_count=$(find . -name "*.deb" | wc -l)
if [[ $deb_count -gt 0 ]]; then
log_info " 找到 $deb_count 个 deb 包,开始安装..."
# 1. 先尝试安装所有deb包
log_info " 第1步批量安装deb包..."
if dpkg -i *.deb 2>/dev/null; then
log_success " 所有deb包安装成功"
else
log_warning " 部分deb包安装失败可能存在依赖问题"
# 2. 使用apt-get修复依赖
log_info " 第2步修复依赖关系..."
if apt-get install -f -y; then
log_success " 依赖关系修复完成"
else
log_error " 依赖关系修复失败"
# 继续处理其他包,不退出
fi
fi
else
log_info " $tar_basename 中没有找到deb包跳过"
fi
# 返回到依赖临时目录
cd "$deps_temp_dir"
done
# 检查并启动 cron 服务
start_cron_service
log_success "系统依赖包安装完成"
}
# 启动 cron 服务
start_cron_service() {
log_info "检查并启动 cron 服务..."
# 检查 cron 是否已经在运行
if pgrep -x "cron" > /dev/null; then
log_success "cron 服务已在运行"
return 0
fi
# 检查 /usr/sbin/cron 是否存在
if [[ ! -f "/usr/sbin/cron" ]]; then
log_warning "cron 可执行文件不存在,跳过启动"
return 1
fi
# 启动 cron 服务
log_info "启动 cron 服务..."
if /usr/sbin/cron start 2>/dev/null || /usr/sbin/cron 2>/dev/null; then
log_success "cron 服务启动成功"
sleep 2
if pgrep -x "cron" > /dev/null; then
log_success "cron 服务运行正常"
else
log_warning "cron 服务可能未正常启动"
fi
else
log_error "cron 服务启动失败"
return 1
fi
}
# 安装组件
install_components() {
log_info "开始安装组件..."
artifact_dir=$(dirname "$VERSION_FILE_PATH")
install_count=0
total_count=0
if [[ -f "$TEMP_DIR/install_order.txt" ]]; then
total_count=$(wc -l < "$TEMP_DIR/install_order.txt")
fi
if [[ -f "$TEMP_DIR/install_order.txt" ]]; then
while IFS= read -r filename; do
install_count=$((install_count + 1))
# 从文件名中提取组件名(去掉时间戳后缀)
component=$(echo "$filename" | sed 's/-[0-9]\{8\}-[0-9]\{6\}\.tar\.gz$//')
log_info "[$install_count/$total_count] 安装 $component..."
log_info " 文件名: $filename"
# 直接使用完整的文件名
tar_file="$artifact_dir/$filename"
if [[ ! -f "$tar_file" ]]; then
log_error "找不到组件文件: $filename"
log_info " 期望路径: $tar_file"
log_info " 当前目录: $(pwd)"
log_info " 目录内容:"
ls -la "$artifact_dir" | while read line; do
log_info " $line"
done
exit 1
fi
log_info " 找到文件: $tar_file"
# 解压到临时目录
component_temp_dir="$TEMP_DIR/$component"
mkdir -p "$component_temp_dir"
if tar -xzf "$tar_file" -C "$component_temp_dir" 2>/dev/null; then
log_success " $component 解压完成"
else
log_error " $component 解压失败"
exit 1
fi
# 查找解压后的目录
extracted_dir=""
for dir in "$component_temp_dir"/*; do
if [[ -d "$dir" ]]; then
extracted_dir="$dir"
break
fi
done
if [[ -z "$extracted_dir" ]]; then
log_error " $component 解压后未找到目录"
exit 1
fi
# 执行安装脚本
if [[ -f "$extracted_dir/install.sh" ]]; then
log_info " 执行 $component 安装脚本..."
if (cd "$extracted_dir" && ./install.sh); then
log_success " $component 安装完成"
else
log_error " $component 安装失败"
exit 1
fi
else
log_error " $component 缺少 install.sh 文件"
exit 1
fi
# 将解压后的目录移动到安装目录,保留组件目录
component_install_dir="$INSTALL_DIR/$component"
if [[ -d "$component_install_dir" ]]; then
log_info " 组件目录已存在,备份后更新: $component_install_dir"
mv "$component_install_dir" "${component_install_dir}.backup.$(date +%Y%m%d_%H%M%S)"
fi
mv "$extracted_dir" "$component_install_dir"
log_success " 组件目录已保存: $component_install_dir"
# 清理临时文件
rm -rf "$component_temp_dir"
done < "$TEMP_DIR/install_order.txt"
fi
log_success "所有组件安装完成"
}
# 创建安装记录
create_install_record() {
log_info "创建安装记录..."
# 等待一段时间确保所有进程都已启动
log_info "等待进程启动..."
sleep 3
local install_time=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
local install_record_file=".install_record"
# 创建 JSON 格式的安装记录
cat > "$install_record_file" << EOF
{
"version": "$VERSION",
"build_time": "$BUILD_TIME",
"install_time": "$install_time",
"install_dir": "$INSTALL_DIR",
"install_pid": $$,
"components": {
EOF
# 添加组件信息
local first_component=true
if [[ -f "$TEMP_DIR/components.txt" ]]; then
while IFS= read -r line; do
component=$(echo "$line" | cut -d':' -f1)
version=$(echo "$line" | cut -d':' -f2)
# 获取组件的进程信息
local component_pid=""
# 根据组件名查找进程使用多种方法确保能找到PID
case "$component" in
"node-exporter")
# 查找node_exporter进程
component_pid=$(pgrep -f "node_exporter" | head -1)
if [[ -z "$component_pid" ]]; then
component_pid=$(pgrep -f "node-exporter" | head -1)
fi
if [[ -z "$component_pid" ]]; then
component_pid=$(ps aux | grep -v grep | grep "node_exporter" | awk '{print $2}' | head -1)
fi
;;
"dcgm-exporter")
# 查找dcgm-exporter进程
component_pid=$(pgrep -f "dcgm-exporter" | head -1)
if [[ -z "$component_pid" ]]; then
component_pid=$(pgrep -f "dcgm_exporter" | head -1)
fi
if [[ -z "$component_pid" ]]; then
component_pid=$(ps aux | grep -v grep | grep "dcgm-exporter" | awk '{print $2}' | head -1)
fi
;;
"fluent-bit")
# 查找fluent-bit进程
component_pid=$(pgrep -f "fluent-bit" | head -1)
if [[ -z "$component_pid" ]]; then
component_pid=$(pgrep -f "fluent_bit" | head -1)
fi
if [[ -z "$component_pid" ]]; then
component_pid=$(ps aux | grep -v grep | grep "fluent-bit" | awk '{print $2}' | head -1)
fi
;;
esac
# 记录找到的PID信息
if [[ -n "$component_pid" ]]; then
log_info " 找到 $component 进程 PID: $component_pid"
else
log_warning " 未找到 $component 进程"
fi
# 添加逗号分隔符
if [[ "$first_component" == "true" ]]; then
first_component=false
else
echo "," >> "$install_record_file"
fi
# 添加组件信息
cat >> "$install_record_file" << EOF
"$component": {
"version": "$version",
"pid": "$component_pid",
"install_dir": "$INSTALL_DIR/$component"
}
EOF
done < "$TEMP_DIR/components.txt"
fi
# 结束 JSON
cat >> "$install_record_file" << EOF
}
}
EOF
log_success "安装记录已创建: $install_record_file"
}
# 设置健康检查定时任务
setup_health_check_cron() {
log_info "设置健康检查定时任务..."
# 直接使用当前安装目录不依赖current软链接
# INSTALL_DIR 是 /opt/argus-metric/versions/1.34.0
local check_health_script="$INSTALL_DIR/check_health.sh"
# 检查健康检查脚本是否存在
if [[ ! -f "$check_health_script" ]]; then
log_error "健康检查脚本不存在: $check_health_script"
return 1
fi
# 确保脚本有执行权限
chmod +x "$check_health_script"
# 创建临时crontab文件
local temp_cron="/tmp/crontab_$$"
# 获取当前用户的crontab如果存在
crontab -l 2>/dev/null > "$temp_cron" || touch "$temp_cron"
# 检查并删除旧的健康检查任务
if grep -q "check_health.sh" "$temp_cron"; then
log_info "发现旧的健康检查定时任务,正在更新..."
# 删除所有包含check_health.sh的行
grep -v "check_health.sh" "$temp_cron" > "$temp_cron.new"
mv "$temp_cron.new" "$temp_cron"
log_info "旧的健康检查定时任务已删除"
fi
# 添加新的定时任务每5分钟执行一次
echo "# Argus-Metrics 健康检查定时任务" >> "$temp_cron"
echo "*/5 * * * * $check_health_script >> $INSTALL_DIR/.health_cron.log 2>&1" >> "$temp_cron"
# 安装新的crontab
if crontab "$temp_cron"; then
log_success "健康检查定时任务设置成功"
log_info " 执行频率: 每5分钟"
log_info " 日志文件: $INSTALL_DIR/.health_cron.log"
log_info " 查看定时任务: crontab -l"
log_info " 删除定时任务: crontab -e"
else
log_error "健康检查定时任务设置失败"
rm -f "$temp_cron"
return 1
fi
# 清理临时文件
rm -f "$temp_cron"
log_info "健康检查通过crontab自动执行"
}
# 显示安装信息
show_install_info() {
log_success "Argus-Metrics All-in-One 安装完成!"
}
cleanup() {
if [[ -d "$TEMP_DIR" ]]; then
rm -rf "$TEMP_DIR"
fi
}
trap cleanup EXIT
# 主函数
main() {
echo "=========================================="
echo " Argus-Metrics All-in-One 安装脚本 v1.0"
echo "=========================================="
echo
# 加载配置文件
load_config
log_info "安装目录: $INSTALL_DIR"
echo
check_root
check_system
find_version_file
create_install_dirs
parse_version_info
verify_checksums
install_system_deps
install_components
copy_config_files
create_install_record
setup_health_check_cron
show_install_info
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi