环境准备与依赖检查:别让脚本死在起跑线上
说实话,我见过太多升级脚本写得很漂亮,结果一跑就挂。十有八九,问题出在环境检查没做透。你想想看,脚本跑了一半才发现内核参数不对,或者磁盘空间不够,那得多尴尬?
这一章,我就把环境准备这块的坑,一个一个给你掰开揉碎讲清楚。
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 网络连通性验证
升级脚本经常要下载包、拉镜像、连仓库。网络不通,后面全是白搭。
我一般分三层检查:
- 基础连通性:ping 目标主机
- 端口可达性:telnet 或 nc 测试
- 服务可用性: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 使用率。
知识体系总览
下面这张图,把环境准备与依赖检查的完整流程串起来了。你可以把它贴在脚本注释里,或者画在团队的白板上。
这五个检查点,按顺序跑一遍。任何一个失败,脚本就该停下来,别硬撑。我见过太多人图省事跳过检查,结果升级到一半回滚都来不及。