#!/usr/bin/env bash # ============================================================================= # Docker Info Collector - Single Column Edition # ============================================================================= set -euo pipefail # ============================================================================= # Colors # ============================================================================= readonly NC='\033[0m' readonly GRAY='\033[0;90m' readonly CYAN='\033[0;36m' readonly GREEN='\033[1;32m' readonly YELLOW='\033[1;33m' readonly WHITE='\033[1;37m' readonly DIM='\033[2m' readonly C_HEADER="${CYAN}" readonly C_SECTION="${YELLOW}" readonly C_KEY="${WHITE}" readonly C_VALUE="${CYAN}" readonly C_OK="${GREEN}" readonly C_WARN="${YELLOW}" readonly C_ERR="\033[1;31m" readonly C_DIM="${GRAY}" # Separator line (thick line style) SEP_LINE="${C_DIM}══════════════════════════════════════════════════════════════════════════════${NC}" # ============================================================================= # Utils # ============================================================================= has_cmd() { command -v "$1" &>/dev/null; } print_header() { local title="$1" local len=${#title} local pad=$(( (76 - len) / 2 )) echo -e "\n${C_HEADER}╔══════════════════════════════════════════════════════════════════════════════╗" printf "${C_HEADER}║%${pad}s${WHITE} %s %${pad}s║${NC}\n" "" "$title" "" echo -e "${C_HEADER}╚══════════════════════════════════════════════════════════════════════════════╝${NC}" } print_section() { echo -e "\n${C_SECTION}▶ $1${NC}" echo -e "$SEP_LINE" } print_kv() { printf " ${C_KEY}%-14s${NC} ${C_VALUE}%s${NC}\n" "$1:" "$2" } print_ok() { echo -e " ${C_OK}●${NC} $1"; } print_warn() { echo -e " ${C_WARN}●${NC} $1"; } print_err() { echo -e " ${C_ERR}●${NC} $1"; } # ============================================================================= # System Info # ============================================================================= collect_server() { print_header "服务器信息" # OS Information print_section "操作系统" local os_name kernel arch os_name="$(. /etc/os-release 2>/dev/null && echo "$PRETTY_NAME" || uname -s)" kernel="$(uname -r)" arch="$(uname -m)" print_kv "系统" "$os_name" print_kv "内核版本" "$kernel" print_kv "系统架构" "$arch" # Host Info print_section "主机信息" local hostname uptime_str hostname="$(hostname)" uptime_str="$(uptime -p 2>/dev/null | sed 's/up //' || uptime | sed 's/.*up //; s/,.*//')" print_kv "主机名" "$hostname" print_kv "运行时长" "$uptime_str" print_kv "当前时间" "$(date '+%Y-%m-%d %H:%M:%S')" # CPU if [[ -f /proc/cpuinfo ]]; then print_section "处理器" local cpus cpu_model cpus=$(grep -c '^processor' /proc/cpuinfo) cpu_model=$(grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs) print_kv "核心数量" "${cpus} 核" print_kv "型号" "$cpu_model" fi # Memory if has_cmd free; then print_section "内存" local mem_total mem_used mem_free mem_pct mem_total=$(free -h | awk '/^Mem:/ {print $2}') mem_used=$(free -h | awk '/^Mem:/ {print $3}') mem_free=$(free -h | awk '/^Mem:/ {print $7}') mem_pct=$(free | awk '/^Mem:/ {printf "%.1f", $3/$2 * 100}') print_kv "总容量" "$mem_total" print_kv "已使用" "$mem_used (${mem_pct}%)" print_kv "可用" "$mem_free" fi # Disk if has_cmd df; then print_section "磁盘使用" df -h --output=source,fstype,size,used,pcent,target 2>/dev/null | tail -n +2 | while read -r fs type size used pct target; do [[ "$fs" == "tmpfs" || "$fs" == "devtmpfs" || "$fs" == "overlay" ]] && continue [[ -z "$fs" ]] && continue # Escape % for printf local pct_clean="${pct%%%}" printf " ${C_DIM}[${NC}${C_VALUE}%-12s${NC}${C_DIM}]${NC} ${C_KEY}%-8s${NC} ${C_VALUE}%-7s${NC} ${C_DIM}used:${NC} ${C_VALUE}%-7s${NC} ${C_DIM}(%s%%)${NC}\n" \ "$target" "$type" "$size" "$used" "$pct_clean" done fi # Network if has_cmd ip; then print_section "网络接口" ip -o addr show 2>/dev/null | awk '{print $2, $4}' | while read -r iface addr; do [[ "$iface" == "lo" ]] && continue print_kv "$iface" "$addr" done fi } # ============================================================================= # Docker Info # ============================================================================= collect_docker() { print_header "Docker 信息" if ! has_cmd docker; then print_err "Docker 未安装" return 1 fi # Version print_section "版本信息" local client_ver server_ver client_ver=$(docker version --format '{{.Client.Version}}' 2>/dev/null || echo "N/A") server_ver=$(docker version --format '{{.Server.Version}}' 2>/dev/null || echo "N/A") print_kv "客户端" "$client_ver" print_kv "服务端" "$server_ver" if has_cmd docker-compose; then print_kv "Docker Compose" "$(docker-compose version --short 2>/dev/null)" fi # Status if docker info &>/dev/null; then print_ok "守护进程运行中" else print_warn "守护进程未运行" return 1 fi # Stats print_section "资源统计" local containers running images networks volumes containers=$(docker ps -aq 2>/dev/null | wc -l) running=$(docker ps -q 2>/dev/null | wc -l) images=$(docker images -q 2>/dev/null | wc -l) networks=$(docker network ls -q 2>/dev/null | wc -l) volumes=$(docker volume ls -q 2>/dev/null | wc -l) print_kv "容器" "${running} 运行 / ${containers} 总计" print_kv "镜像" "$images" print_kv "网络" "$networks" print_kv "存储卷" "$volumes" # Running containers if [[ $running -gt 0 ]]; then print_section "运行中的容器" docker ps --format "{{.Names}}|{{.Image}}|{{.Status}}" 2>/dev/null | while IFS='|' read -r name image status; do printf " ${C_OK}●${NC} ${C_VALUE}%-20s${NC} ${C_DIM}%-30s${NC} %s\n" "$name" "${image:0:30}" "$status" done fi # All containers if [[ $containers -gt 0 ]]; then print_section "所有容器" docker ps -a --format "{{.Names}}|{{.Image}}|{{.Status}}" 2>/dev/null | head -20 | while IFS='|' read -r name image status; do local icon="${C_DIM}○${NC}" local color="$C_VALUE" [[ "$status" == Up* ]] && icon="${C_OK}●${NC}" && color="$C_OK" [[ "$status" == Exited* ]] && color="$C_DIM" printf " ${icon} ${color}%-20s${NC} ${C_DIM}%-25s${NC} %s\n" "$name" "${image:0:25}" "$status" done [[ $containers -gt 20 ]] && echo -e " ${C_DIM}... 还有 $((containers - 20)) 个容器${NC}" fi # Images if [[ $images -gt 0 ]]; then print_section "镜像列表" docker images --format "{{.Repository}}|{{.Tag}}|{{.Size}}|{{.CreatedAt}}" 2>/dev/null | grep -v "" | head -25 | while IFS='|' read -r repo tag size created; do printf " ${C_DIM}◆${NC} ${C_VALUE}%-35s${NC} ${C_DIM}%-10s %s${NC}\n" "${repo}:${tag}" "$size" "$created" done [[ $images -gt 25 ]] && echo -e " ${C_DIM}... 还有 $((images - 25)) 个镜像${NC}" fi # Networks if [[ $networks -gt 0 ]]; then print_section "网络" docker network ls --format "{{.Name}}|{{.Driver}}|{{.Scope}}" 2>/dev/null | head -15 | while IFS='|' read -r name driver scope; do printf " ${C_DIM}◎${NC} ${C_VALUE}%-20s${NC} ${C_DIM}[%s/%s]${NC}\n" "$name" "$driver" "$scope" done fi # Volumes if [[ $volumes -gt 0 ]]; then print_section "存储卷" docker volume ls --format "{{.Name}}|{{.Driver}}" 2>/dev/null | head -20 | while IFS='|' read -r name driver; do printf " ${C_DIM}▪${NC} ${C_VALUE}%s${NC} ${C_DIM}(%s)${NC}\n" "$name" "$driver" done [[ $volumes -gt 20 ]] && echo -e " ${C_DIM}... 还有 $((volumes - 20)) 个存储卷${NC}" fi } # ============================================================================= # Main # ============================================================================= main() { collect_server collect_docker echo -e "\n${C_HEADER}╔══════════════════════════════════════════════════════════════════════════════╗" echo -e "${C_HEADER}║${C_OK} ✓ 信息收集完成 ${C_HEADER}║${NC}" echo -e "${C_HEADER}╚══════════════════════════════════════════════════════════════════════════════╝${NC}\n" } main "$@"