本章核心:环境准备不是“有手就行”。 我见过太多团队在环境这一步翻车——缺一个依赖、版本不对、端口被占用,最后排查两小时白费。这篇内容,就是让你在动手部署前,把“坑”先填上。
环境准备与依赖检查
做部署验证这些年,我踩过最深的一个坑是什么?
——不是配置写错了,而是环境没准备好。你想想看,辛辛苦苦写好的部署脚本,跑起来报个“command not found”,或者“port already in use”,是不是特别让人崩溃?
说白了,环境准备就是部署的“地基”。地基不稳,上层建筑再漂亮也没用。我个人习惯,在每次部署之前,都会先花15分钟把环境从头到尾捋一遍。这叫“磨刀不误砍柴工”。
2.1 操作系统与内核参数核对
不同发行版、不同内核版本,行为差异很大。我建议你先把这几项确认死:
- 操作系统版本:CentOS 7 和 Ubuntu 20.04 的包管理器、默认防火墙规则完全不同。
- 内核版本:至少 3.10 以上(CentOS 7 默认就是这个),如果需要跑容器或高并发,建议 4.18+。
- 文件描述符限制:
ulimit -n默认 1024,对生产服务器来说太低了。我一般会改成 65535 或更高。 - 时间同步:NTP 或 chronyd 必须成功运行。证书校验、日志审计都依赖准确的时间。
我个人习惯:写一个 env_check.sh 脚本,把这些检查项做成自动化。每到一个新环境,先跑一遍,心里就踏实了。
#!/bin/bash
# 简化版环境预检脚本
echo "=== 操作系统 ==="
cat /etc/os-release | head -2
echo "=== 内核版本 ==="
uname -r
echo "=== 文件描述符限制 ==="
ulimit -n
echo "=== 时间同步状态 ==="
timedatectl status | grep "NTP synchronized"
我曾经遇到过一次:客户的环境是 CentOS 6,内核 2.6.32。部署容器时发现 Docker 根本跑不起来。后来查了才知道,Docker 要求内核至少 3.10。所以,版本核对真的不能偷懒。
2.2 网络连通性与端口检查
部署过程中,很多问题都出在网络层。
你可能会问:不就是 ping 通就行吗?
嗯,这里要注意:ping 通只代表 ICMP 通,不代表应用层通。
我建议按下面这个顺序检查:
- 基础连通性:用
ping确认目标 IP 可达。 - 端口可达性:用
telnet或nc -zv测试指定端口。 - DNS 解析:
nslookup确认域名能正确解析。 - 路由与防火墙:
traceroute看路径,iptables -L -n看规则是否放行了必要端口。
| 检查项 | 命令示例 | 预期结果 |
|---|---|---|
| 端口监听 | ss -tlnp |
看到服务已监听在 0.0.0.0:8080 |
| 端口可达 | nc -zv 10.0.0.1 8080 |
返回 "succeeded" |
| DNS 解析 | nslookup api.example.com |
返回正确 IP |
| 防火墙规则 | iptables -L INPUT -n --line-numbers |
没有误拦 |
实战技巧:如果 nc 不通,别忘了检查 selinux 和 firewalld。getenforce 返回 Enforcing 时,很多端口会被默认拒绝。
2.3 依赖库与组件版本核对
依赖管理,是让我又爱又恨的一环。爱的是包管理工具让安装变简单,恨的是版本冲突一旦出现,排查起来特别麻烦。
常见的依赖检查清单:
- 编程语言运行时:Java、Python、Node、Go 等,版本必须精确匹配。Java 8 和 Java 11 的某些 API 都不兼容。
- 系统库:比如
openssl、libcurl、zlib。用ldd检查二进制文件的依赖。 - 容器基础镜像:如果使用 Docker,确认基础镜像的 OS 版本和打包的库版本是否一致。
- 数据库驱动:比如 Java 应用需要特定版本的 MySQL Connector。
避坑指南: 我习惯把依赖写成 requirements.txt(Python)或 package.json(Node),并且锁死主版本号。千万别用 latest——生产环境里最怕“意外升级”。
# requirements.txt 示例(Python)
flask==2.2.3
gunicorn==20.1.0
requests==2.28.1
# 不要写成 flask>=2.2.0,不指定主版本号就是给自己埋雷
2.4 容器 Runtime 与编排工具校验
现代部署基本离不开容器。但容器环境本身也需要验证:
- Docker/containerd 是否运行:
systemctl status docker,docker info。 - 镜像拉取权限:
docker pull测试私有仓库是否可以认证。 - 容器网络:
docker network ls确认 overlay 网络正常。 - 资源限制:cgroup 版本、swap 是否禁用、CPU/memory 限制是否生效。
- Kubernetes 集群:
kubectl get nodes所有节点 Ready,kubectl cluster-info正常。
| 组件 | 检查命令 | 关键输出 |
|---|---|---|
| Docker Engine | docker version |
Server 版本 ≥ 20.10 |
| Kubernetes | kubectl version |
Server version 与 Client version 差异 ≤ 1 个小版本 |
| 镜像仓库 | docker login xxx.com |
Login Succeeded |
我曾经遇到过最头疼的一个问题:有个项目同时用到 Docker 和 Containerd,但两个 runtime 的配置里都用了同一个端口映射,导致容器启动失败。排查了好久才发现。所以,不同容器 runtime 之间的端口和资源冲突,一定要在部署前检查清楚。
2.5 云平台配置一致性
如果你在云上部署,环境准备的维度就更多了。我个人习惯做一张“配置核对表”:
- 安全组/ACL:出入站规则是否放行了应用端口?有没有误屏蔽了健康检查 IP?
- 负载均衡:健康检查路径、间隔、超时时间是否与应用一致?
- IAM 权限:应用使用的服务账号是否有权限访问对象存储、数据库?
- 环境变量:
.env或配置中心的变量值是否与目标环境匹配(开发/测试/生产)?
一个真实的教训: 有一次我们部署一个 Java 应用,在测试环境一切正常,但上了生产就报 403。最后发现是生产环境的 IAM 策略里,没有允许该服务读取 S3 的 Action。从那以后,我每次部署前都会让人工走一遍“最小权限原则”检查。
2.6 环境预检的整体流程
说了这么多,我们来把整个流程串一下。我个人习惯按照“从下到上”的原则检查:
先底层(OS、网络),再中间层(部署工具、运行时),最后应用层(配置、依赖)。
下面这张图,是我自己项目里一直在用的依赖检查总览:
图里这个顺序是按照依赖关系画的:
环境基础层 错了,后面网络和依赖都没意义;
网络与端口层 不通,应用根本连不上;
应用依赖层 有问题,服务就跑不起来或者运行不稳定。
我的一个习惯: 每次做环境预检,我都会把发现的问题记在一个小本本上(其实是个 Markdown 文件)。比如“某环境的 openssl 版本太旧”、“某台机器缺 libffi”。时间长了,就能总结出一套“常见环境问题模式”,直接写到自动化脚本里,一劳永逸。
好了,环境准备部分就是这些。
记住一句话:花15分钟做预检,能省下2小时的排查时间。 这笔账,怎么算都划算。