环境准备与依赖检查:别让脚本死在起跑线上

说实话,我见过太多升级脚本写得很漂亮,结果一跑就挂。十有八九,问题出在环境检查没做透。你想想看,脚本跑了一半才发现内核参数不对,或者磁盘空间不够,那得多尴尬?

这一章,我就把环境准备这块的坑,一个一个给你掰开揉碎讲清楚。

2.1 操作系统版本检测

不同发行版、不同内核版本,行为差异很大。我早期吃过这个亏——脚本在 CentOS 7 上测得好好的,客户现场是 CentOS 6,直接报错。

我的做法是:先获取发行版信息,再匹配白名单。

# 获取操作系统发行版信息
if [ -f /etc/os-release ]; then
    . /etc/os-release
    OS_NAME=$ID
    OS_VERSION=$VERSION_ID
elif [ -f /etc/redhat-release ]; then
    OS_NAME="centos"
    OS_VERSION=$(rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release))
else
    echo "无法识别操作系统"
    exit 1
fi

# 版本白名单检查
case "$OS_NAME" in
    centos|rhel)
        if [[ "$OS_VERSION" != 7* && "$OS_VERSION" != 8* ]]; then
            echo "仅支持 CentOS/RHEL 7.x 或 8.x"
            exit 1
        fi
        ;;
    ubuntu)
        if [[ "$OS_VERSION" != "20.04" && "$OS_VERSION" != "22.04" ]]; then
            echo "仅支持 Ubuntu 20.04 或 22.04 LTS"
            exit 1
        fi
        ;;
    *)
        echo "不支持的发行版: $OS_NAME"
        exit 1
        ;;
esac
注意:别只检查版本号。有些系统改了 /etc/os-release 但内核没变。我建议同时用 uname -r 拿内核版本做二次校验。

2.2 内核参数校验

内核参数不对,升级脚本跑着跑着就崩了。尤其是网络、文件系统相关的参数。

我习惯把关键参数列成一个表,脚本逐项比对。

参数名 推荐值 检查命令 说明
net.ipv4.tcp_tw_reuse 1 sysctl net.ipv4.tcp_tw_reuse 端口复用,高并发场景必须
vm.swappiness 10 sysctl vm.swappiness 减少 swap 使用,提升性能
fs.file-max 6553600 sysctl fs.file-max 文件句柄上限,不够会报 too many open files
net.core.somaxconn 1024 sysctl net.core.somaxconn 连接队列长度,短了丢连接
# 内核参数批量检查脚本片段
declare -A REQUIRED_PARAMS
REQUIRED_PARAMS["net.ipv4.tcp_tw_reuse"]="1"
REQUIRED_PARAMS["vm.swappiness"]="10"
REQUIRED_PARAMS["fs.file-max"]="6553600"

for param in "${!REQUIRED_PARAMS[@]}"; do
    current=$(sysctl -n "$param" 2>/dev/null)
    expected="${REQUIRED_PARAMS[$param]}"
    if [[ "$current" != "$expected" ]]; then
        echo "参数 $param 当前值 $current,期望值 $expected"
        exit 1
    fi
done
小技巧:有些参数改了要重启才生效。我一般会在检查后加一步 sysctl -p 重新加载,确保当前会话也生效。

2.3 依赖软件包检查

依赖缺失是升级脚本最常见的失败原因。我的原则是:宁可多检查,不要少检查。

不同包管理器,检查方式不一样。我统一封装成一个函数:

check_package() {
    local pkg=$1
    if command -v rpm &>/dev/null; then
        rpm -q "$pkg" &>/dev/null
    elif command -v dpkg &>/dev/null; then
        dpkg -l "$pkg" &>/dev/null
    elif command -v pacman &>/dev/null; then
        pacman -Qi "$pkg" &>/dev/null
    else
        echo "无法识别的包管理器"
        return 1
    fi
    return $?
}

# 检查依赖列表
DEPS=("gcc" "make" "openssl-devel" "libffi-devel")
for pkg in "${DEPS[@]}"; do
    if ! check_package "$pkg"; then
        echo "缺少依赖: $pkg"
        exit 1
    fi
done

避坑指南:我曾经遇到一个场景,rpm 包名在不同版本里变了。比如 CentOS 7 叫 python-devel,CentOS 8 叫 python3-devel。所以检查时最好做版本分支判断。

2.4 网络连通性验证

升级脚本经常要下载包、拉镜像、连仓库。网络不通,后面全是白搭。

我一般分三层检查:

  1. 基础连通性:ping 目标主机
  2. 端口可达性:telnet 或 nc 测试
  3. 服务可用性:curl 或 wget 测试 HTTP 状态码
# 网络连通性检查
check_network() {
    local host=$1
    local port=$2
    local url=$3

    # 1. ping 测试
    if ! ping -c 2 -W 3 "$host" &>/dev/null; then
        echo "主机 $host 不可达"
        return 1
    fi

    # 2. 端口测试
    if ! timeout 3 bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null; then
        echo "端口 $host:$port 不通"
        return 1
    fi

    # 3. HTTP 服务测试
    if [[ -n "$url" ]]; then
        local http_code
        http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url")
        if [[ "$http_code" -ge 400 ]]; then
            echo "服务返回异常状态码: $http_code"
            return 1
        fi
    fi

    return 0
}

# 检查仓库
check_network "mirrors.aliyun.com" 443 "https://mirrors.aliyun.com/centos/"
注意:有些内网环境禁 ping。这时候就别用 ping 了,直接测端口。我遇到过客户环境 ICMP 全封,ping 永远不通,但业务端口是好的。

2.5 磁盘空间评估

磁盘空间不够,升级写到一半就报错。更可怕的是——日志文件把磁盘撑爆了。

我一般评估三个目录:

  • 安装目录:至少预留 5GB
  • /tmp 或临时目录:至少 2GB
  • /var/log:至少 1GB
# 磁盘空间检查
check_disk() {
    local path=$1
    local required_mb=$2
    local available_mb

    available_mb=$(df -m "$path" | awk 'NR==2 {print $4}')
    if [[ "$available_mb" -lt "$required_mb" ]]; then
        echo "目录 $path 可用空间 ${available_mb}MB,需要 ${required_mb}MB"
        return 1
    fi
    return 0
}

check_disk "/opt" 5120   # 5GB
check_disk "/tmp" 2048   # 2GB
check_disk "/var/log" 1024  # 1GB
经验之谈:我习惯在脚本开头先跑一遍磁盘检查,然后 再检查一次 inode 数量。有时候磁盘空间够,但 inode 用完了,文件也写不进去。用 df -i 就能看到 inode 使用率。

知识体系总览

下面这张图,把环境准备与依赖检查的完整流程串起来了。你可以把它贴在脚本注释里,或者画在团队的白板上。

环境准备与依赖检查流程 开始环境检查 1. 操作系统版本检测 2. 内核参数校验 3. 依赖软件包检查 4. 网络连通性验证 5. 磁盘空间评估 全部通过 → 继续升级 避坑要点 • 版本白名单 + 内核二次校验 • 包名随版本变化要分支处理 • 网络检查分三层:ping/端口/HTTP 常见失败原因 • 磁盘空间够但 inode 用完 • 内网环境禁 ping 导致误判 • 依赖包名跨版本不一致

这五个检查点,按顺序跑一遍。任何一个失败,脚本就该停下来,别硬撑。我见过太多人图省事跳过检查,结果升级到一半回滚都来不及。