赋权: chmod +x milvus_compose_diag.sh
运行(默认前缀milvus): sudo ./milvus_compose_diag.sh
自定义容器前缀: sudo ./milvus_compose_diag.sh milvus-v2
bash
#!/usr/bin/env bash
#===============================================================================
# 优化后的 Milvus 向量数据库故障系统化检查脚本
# 适用:Docker Compose 部署 Milvus,支持分段单独终端执行
# 特性:自动缓存容器NAME/ID/挂载卷,高亮可复制参数,故障取证打包
# 融合哲学元认知:本体论、奥卡姆剃刀、辩证法、实用主义、无为、整体论
# 用法:bash milvus_compose_diag.sh [容器名前缀,默认milvus]
# 权限建议:sudo 执行(完整读取docker/内核日志)
#===============================================================================
set -uo pipefail
# =====================【独立可执行块1:全局初始化、颜色、缓存、工具函数】=====================
# 入参容器前缀
PREFIX="${1:-milvus}"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
WHITE='\033[1;37m'
NC='\033[0m'
# 基础打印函数
print_header() { echo -e "\n${GREEN}========== $1 ==========${NC}"; }
warn() { echo -e "${YELLOW}[!] $1${NC}"; }
error() { echo -e "${RED}[✖] $1${NC}"; }
success() { echo -e "${GREEN}[✓] $1${NC}"; }
# 打印可复制参数并写入缓存文件
print_copy_param() {
local label="$1"
local value="$2"
echo -e "${WHITE}【可复制参数 | $label】${RED}$value${NC}"
echo "$label=$value" >> "$CACHE_FILE"
}
# 输出预填充好参数的一键执行命令
quick_run_cmd() {
echo -e "${YELLOW}🔧 一键复制执行:$1${NC}"
echo "快捷命令:$1" >> "$CACHE_FILE"
}
# 取证缓存目录初始化(所有分段共享)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DIAG_DIR="/tmp/milvus_compose_diag_${TIMESTAMP}"
CACHE_FILE="${DIAG_DIR}/container_cache.txt"
RECORD_LOG="${DIAG_DIR}/full_diagnose.log"
mkdir -p "$DIAG_DIR"
# 清空缓存文件
> "$CACHE_FILE"
> "$RECORD_LOG"
echo "Milvus Compose故障取证时间:$(date)" >> "$RECORD_LOG"
# 全局缓存变量(自动填充,全脚本复用)
ALL_MILVUS_CONTAINER_NAMES=""
ALL_MILVUS_CONTAINER_IDS=""
EXIT_CONTAINER_NAMES=""
ETCD_CONTAINER_NAME=""
MINIO_CONTAINER_NAME=""
PROXY_CONTAINER_NAME=""
# ==========================================================================================
# =====================【独立可执行块2:0. Docker环境前置校验】=====================
print_header "0. Docker 环境检查 (实用主义:最小前置依赖校验)"
# 校验docker命令
if ! command -v docker &> /dev/null; then
error "未找到 docker 命令,请确认 Docker 已安装。脚本终止。"
exit 1
fi
# 校验docker服务状态
if ! docker info &> /dev/null; then
error "Docker 守护进程未运行/当前用户无docker权限"
quick_run_cmd "sudo usermod -aG docker \$USER && newgrp docker"
quick_run_cmd "systemctl status docker"
exit 1
fi
success "Docker环境校验通过"
# ==========================================================================================
# =====================【独立可执行块3:1.本体论-容器实体拓扑扫描,缓存所有容器ID/NAME】=====================
print_header "1. 容器全局状态 (本体论:勾勒Milvus全组件实体拓扑)"
# 拉取全部匹配前缀容器名称、ID
mapfile -t ALL_CON_NAMES < <(docker ps -a --filter "name=$PREFIX" --format "{{.Names}}" 2>/dev/null)
mapfile -t ALL_CON_IDS < <(docker ps -a --filter "name=$PREFIX" --format "{{.ID}}" 2>/dev/null)
# 无容器直接退出
if [ ${#ALL_CON_NAMES[@]} -eq 0 ]; then
warn "未找到名称含 '$PREFIX' 的容器,请核对compose容器前缀"
quick_run_cmd "docker ps -a | grep milvus"
exit 0
fi
# 存入全局缓存变量
ALL_MILVUS_CONTAINER_NAMES="${ALL_CON_NAMES[*]}"
ALL_MILVUS_CONTAINER_IDS="${ALL_CON_IDS[*]}"
print_copy_param "全部Milvus容器名称" "$ALL_MILVUS_CONTAINER_NAMES"
print_copy_param "全部Milvus容器ID" "$ALL_MILVUS_CONTAINER_IDS"
# 表格打印容器完整状态
printf "%-32s %-18s %-10s %s\n" "容器名" "运行状态" "退出码" "重启次数"
printf "%-32s %-18s %-10s %s\n" "------" "------" "------" "--------"
EXIT_CONTAINERS=()
# 遍历所有容器,识别etcd/minio/proxy组件
for name in "${ALL_CON_NAMES[@]}"; do
status=$(docker inspect --format '{{.State.Status}}' "$name" 2>/dev/null)
exit_code=$(docker inspect --format '{{.State.ExitCode}}' "$name" 2>/dev/null)
restart_cnt=$(docker inspect --format '{{.RestartCount}}' "$name" 2>/dev/null)
printf "%-32s %-18s %-10s %s\n" "$name" "$status" "$exit_code" "$restart_cnt"
# 收集异常退出容器
if [[ "$status" != "running" ]]; then
EXIT_CONTAINERS+=("$name")
fi
# 自动识别核心组件容器
if [[ "$name" == *etcd* ]]; then ETCD_CONTAINER_NAME="$name"; fi
if [[ "$name" == *minio* ]]; then MINIO_CONTAINER_NAME="$name"; fi
if [[ "$name" == *proxy* ]]; then PROXY_CONTAINER_NAME="$name"; fi
done
# 缓存核心组件名称
EXIT_CONTAINER_NAMES="${EXIT_CONTAINERS[*]}"
print_copy_param "异常退出容器列表" "$EXIT_CONTAINER_NAMES"
print_copy_param "ETCD容器名称" "$ETCD_CONTAINER_NAME"
print_copy_param "MinIO容器名称" "$MINIO_CONTAINER_NAME"
print_copy_param "Milvus Proxy容器名称" "$PROXY_CONTAINER_NAME"
# 输出状态判定
if [ ${#EXIT_CONTAINERS[@]} -gt 0 ]; then
error "检测到 ${#EXIT_CONTAINERS[@]} 个非运行容器:${EXIT_CONTAINER_NAMES},优先排查崩溃根因"
else
success "所有Milvus容器正常运行,故障方向:业务超时/索引构建缓慢/连接异常"
fi
quick_run_cmd "docker compose ps"
# ==========================================================================================
# =====================【独立可执行块4:2.奥卡姆剃刀-崩溃强信号扫描(OOM/段错误/内核日志)】=====================
print_header "2. 关键崩溃信号扫描 (奥卡姆剃刀:优先抓取致命退出码、OOM Killer)"
OOM_TRIGGER=false
# 遍历退出容器分析退出码
for con_name in "${EXIT_CONTAINERS[@]}"; do
exit_code=$(docker inspect --format '{{.State.ExitCode}}' "$con_name")
case $exit_code in
137)
warn "$con_name 退出码137 → OOM内核杀死 / 手动docker kill"
OOM_TRIGGER=true
quick_run_cmd "docker inspect $con_name | jq '.[0].HostConfig.Memory'"
;;
139)
warn "$con_name 退出码139 → 程序段错误(SIGSEGV),向量索引/底层代码异常"
;;
1)
warn "$con_name 退出码1 → Milvus应用内部启动失败,优先查看容器日志"
quick_run_cmd "docker logs --tail 200 $con_name"
;;
esac
done
# 宿主机内核OOM记录
echo -e "\n宿主机内核OOM历史记录(最近10条)"
OOM_LOG=$(dmesg -T 2>/dev/null | grep -i "out of memory" | tail -10)
if [[ -n "$OOM_LOG" ]]; then
echo "$OOM_LOG"
OOM_TRIGGER=true
else
echo "未检测到内核OOM杀死进程记录"
fi
# OOM风险总判定
if $OOM_TRIGGER; then
error "OOM内存溢出为核心矛盾,优先扩容容器内存限制或清理向量数据"
quick_run_cmd "grep -i 'killed process' /var/log/kern.log"
else
success "无明显OOM内存杀死信号,转向磁盘/依赖/配置排查"
fi
# ==========================================================================================
# =====================【独立可执行块5:3.本体论-核心依赖ETCD/MinIO连通健康检测】=====================
print_header "3. 核心依赖校验 (本体论:etcd元数据、MinIO对象存储两大基础实体)"
# ETCD健康检查
check_etcd_health() {
if [[ -z "$ETCD_CONTAINER_NAME" ]]; then
warn "未找到运行中的etcd容器,手动从proxy探测连通性"
quick_run_cmd "docker exec -it $PROXY_CONTAINER_NAME curl -s http://etcd:2379/health"
else
echo "检测ETCD容器:$ETCD_CONTAINER_NAME"
if docker exec "$ETCD_CONTAINER_NAME" etcdctl endpoint health --cluster 2>/dev/null; then
success "ETCD集群健康检查通过"
else
error "ETCD健康检测失败,Milvus无法读写集合、索引元数据"
quick_run_cmd "docker exec $ETCD_CONTAINER_NAME etcdctl member list"
fi
fi
}
# MinIO存储健康检查
check_minio_health() {
if [[ -z "$MINIO_CONTAINER_NAME" ]]; then
warn "未识别内置MinIO容器,确认外部对象存储连通性"
else
echo "检测MinIO容器:$MINIO_CONTAINER_NAME"
if docker exec "$MINIO_CONTAINER_NAME" curl -s http://127.0.0.1:9000/minio/health/live 2>/dev/null; then
success "MinIO存储存活检测通过"
else
error "MinIO健康端点无响应,检查磁盘空间、数据卷权限"
quick_run_cmd "docker logs --tail 100 $MINIO_CONTAINER_NAME"
fi
fi
}
check_etcd_health
check_minio_health
# ==========================================================================================
# =====================【独立可执行块6:4.辩证法-系统资源瓶颈深度排查(磁盘/内存/CPU/卷挂载)】=====================
print_header "4. 资源瓶颈排查 (辩证法:区分内存/磁盘/CPU主要矛盾)"
# 1. 宿主机磁盘&inode占用
echo "=== 宿主机磁盘使用率(过滤临时文件系统)==="
df -h --type=ext4 --type=xfs --type=overlay 2>/dev/null | grep -vE 'tmpfs|devtmpfs' || df -h
echo -e "\n=== 磁盘inode占用(Milvus海量segment易耗尽inode)==="
df -i | grep -v tmpfs
warn "磁盘/Inode使用率>90%会直接导致向量写入、索引构建失败"
# 2. Milvus专属数据卷挂载检测
echo -e "\n=== Milvus相关Docker数据卷挂载信息 ==="
VOL_LIST=$(docker volume ls --filter "name=${PREFIX}" --format "{{.Name}}")
for vol in $VOL_LIST; do
mount_point=$(docker volume inspect "$vol" --format '{{.Mountpoint}}')
disk_use=$(df -h "$mount_point" 2>/dev/null | awk 'NR==2{print $5}')
inode_use=$(df -i "$mount_point" 2>/dev/null | awk 'NR==2{print $5}')
echo "卷名:$vol | 挂载路径:$mount_point | 磁盘占用:$disk_use | inode占用:$inode_use"
done
# 3. 内存资源总览
echo -e "\n=== 宿主机内存、Swap总览 ==="
free -h
echo -e "\n=== Milvus容器内存占用Top5 ==="
docker stats --no-stream --format "table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}" 2>/dev/null | head -6
# 4. CPU负载与容器限流
echo -e "\n=== 宿主机CPU负载 ==="
uptime
echo "CPU物理核心数:$(nproc),负载持续大于核心数代表CPU瓶颈"
# 容器CPU节流检测
for con in "${ALL_CON_NAMES[@]}"; do
if [[ "$(docker inspect --format '{{.State.Status}}' "$con")" == "running" ]]; then
cgroup_throttle=$(cat /sys/fs/cgroup/cpu/cpu.stat 2>/dev/null | grep throttled_time | awk '{print $2}')
if [[ -n "$cgroup_throttle" && "$cgroup_throttle" -gt 0 ]]; then
warn "容器 $con 存在CPU限流,查询/插入性能受限"
fi
fi
done
quick_run_cmd "iostat -x 1 3"
# ==========================================================================================
# =====================【独立可执行块7:5.实用主义-错误日志过滤取证】=====================
print_header "5. 致命错误日志快速过滤(实用主义:精准匹配崩溃关键词)"
LOG_TAIL_NUM=80
# 高危错误匹配规则
ERR_KEYWORD='out of memory|OOM|no space left|permission denied|too many open files|connection refused|etcd timeout|segment load failed|panic|fatal'
# 先打印异常退出容器日志
if [[ -n "$EXIT_CONTAINER_NAMES" ]]; then
for con_name in "${EXIT_CONTAINERS[@]}"; do
echo -e "\n===== 崩溃容器【$con_name】最近${LOG_TAIL_NUM}行错误日志 ====="
crash_log=$(docker logs --tail $LOG_TAIL_NUM "$con_name" 2>&1 | grep -iE "$ERR_KEYWORD")
if [[ -n "$crash_log" ]]; then
echo -e "${RED}$crash_log${NC}"
else
warn "该容器日志未匹配致命关键词,查看完整日志"
quick_run_cmd "docker logs $con_name > ${DIAG_DIR}/log_$con_name.log"
fi
done
fi
# 检测运行中但健康检查失败的容器
echo -e "\n===== 运行中容器健康状态异常日志 ====="
for con_name in "${ALL_CON_NAMES[@]}"; do
if [[ "$(docker inspect --format '{{.State.Status}}' "$con_name")" == "running" ]]; then
health_state=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}no-healthcheck{{end}}' "$con_name")
if [[ "$health_state" == "unhealthy" ]]; then
warn "$con_name 健康检查失败,提取错误日志"
docker logs --tail 40 "$con_name" 2>&1 | grep -iE "$ERR_KEYWORD" || true
fi
fi
done
# 全量日志保存至取证目录
quick_run_cmd "for c in $ALL_MILVUS_CONTAINER_NAMES; do docker logs \$c > ${DIAG_DIR}/log_\$c.log 2>&1; done"
# ==========================================================================================
# =====================【独立可执行块8:6.整体论-系统内核参数约束校验】=====================
print_header "6. 全局系统内核参数校验(整体论:容器运行底层约束)"
# vm内存映射参数(Milvus硬性要求)
echo "vm.max_map_count = $(sysctl -n vm.max_map_count) | 生产推荐≥262144"
# 文件句柄限制
echo "宿主机nofile软限制:$(ulimit -Sn) 硬限制:$(ulimit -Hn)"
warn "docker-compose必须为etcd/querynode配置ulimits nofile:65536"
# 时间同步校验
if command -v timedatectl &> /dev/null; then
sync_status=$(timedatectl show --property=NTPSynchronized --value)
echo "系统时间同步状态:$sync_status"
if [[ "$sync_status" == "no" ]]; then
warn "系统时间未同步,会导致etcd集群、分布式组件超时异常"
fi
fi
# Docker根目录磁盘
docker_root=$(docker info --format '{{.DockerRootDir}}' 2>/dev/null)
if [[ -n "$docker_root" ]]; then
echo "Docker存储根目录:$docker_root"
df -h "$docker_root" | awk 'NR==2{print "分区占用:"$5}'
fi
quick_run_cmd "sysctl -w vm.max_map_count=262144"
# ==========================================================================================
# =====================【独立可执行块9:7.道家无为-自愈、可观测全局提示】=====================
print_header "7. 自愈与全局可观测建议(道家无为:前置监控避免事后抢修)"
echo "==== Milvus容器自愈统计 ===="
for con in "${ALL_CON_NAMES[@]}"; do
restart_times=$(docker inspect --format '{{.RestartCount}}' "$con")
last_exit_time=$(docker inspect --format '{{.State.FinishedAt}}' "$con")
echo "$con | 累计重启次数:$restart_times | 上次崩溃时间:$last_exit_time"
done
cat << TIPS
# 生产环境监控告警配置(无为前置方案)
1. 容器存活告警:任意milvus组件容器退出触发告警
2. 日志关键词告警:panic/OOM/no space/etcd failed
3. 资源阈值告警:磁盘>85%、swap占用>30%、文件句柄>80%限制
4. 依赖健康告警:etcd endpoint health、minio live接口检测
5. 业务指标监控:segment加载失败率、向量插入失败、查询延迟
# 可观测优化
- Prometheus采集milvus内置metrics,监控索引构建、段文件数量
- OpenTelemetry全链路追踪,定位proxy→coord→node调用耗时瓶颈
TIPS
# ==========================================================================================
# =====================【独立可执行块10:收尾-故障现场打包归档】=====================
print_header "诊断收尾:全量故障取证打包,缓存参数查看指引"
# 打包所有诊断快照
tar -czf "${DIAG_DIR}.tar.gz" -C /tmp "milvus_compose_diag_${TIMESTAMP}" 2>/dev/null
success "✅ Milvus Compose完整故障取证压缩包生成完成"
print_copy_param "取证压缩包完整路径" "${DIAG_DIR}.tar.gz"
echo -e "\n${WHITE}【查看全部缓存容器ID/名称/路径参数】执行命令:${RED}cat ${CACHE_FILE}${NC}"
echo -e "\n${GREEN}Milvus Docker Compose系统化故障诊断全部执行完成${NC}"
echo -e "${YELLOW}根因排查优先级:崩溃日志 > OOM内核记录 > 磁盘空间 > ETCD/MinIO依赖 > 内存/文件句柄限制${NC}"
# ==========================================================================================
# 内置完整手工排查手册(脚本内注释,可复制单独执行)
: <<'MANUAL_GUIDE'
===============================================================================
手工深度排查全套命令(自动复用缓存容器名,直接复制运行)
===============================================================================
# 1. ETCD元数据深度排查
docker exec -it $ETCD_CONTAINER_NAME etcdctl member list
docker exec -it $PROXY_CONTAINER_NAME curl http://etcd:2379/v2/keys/milvus/meta -w "\n"
# 2. MinIO向量存储目录检查
docker exec $MINIO_CONTAINER_NAME du -sh /data
docker exec $MINIO_CONTAINER_NAME ls -lh /data/milvus-bucket
# 3. OOM内存限制核查
docker inspect $PROXY_CONTAINER_NAME | jq '.[0].HostConfig.Memory / 1024 / 1024 / 1024'
# 4. 容器内文件句柄限制查看
docker exec $PROXY_CONTAINER_NAME ulimit -n
# 5. Compose重启指定故障组件
docker compose up -d --force-recreate $PROXY_CONTAINER_NAME
# 6. 开启Milvus调试日志
docker exec -it $PROXY_CONTAINER_NAME sed -i 's/log.level: info/log.level: debug/' /milvus/conf/milvus.yaml
docker compose restart $PROXY_CONTAINER_NAME
# 7. 内核OOM完整日志检索
journalctl -k --no-pager | grep -i 'killed process'
===============================================================================
MANUAL_GUIDE