做运维这行,有个不成文的真理:永远不要相信“一切正常”的监控报表,除非你亲眼看见那个绿色的“Online”状态已经亮过了三个凌晨三点。
我见过太多新人运维,拿着完美的配置文档入职,三个月后头发掉了一半,因为现实世界里,代码不会在真空中运行,电缆会老化,硬盘会暴毙,甚至松鼠也会咬断光纤。今天咱不聊虚头巴脑的理论,就聊聊那些真刀真枪踩过的坑,以及我是怎么从坑里爬出来,顺便把坑填平的。
第一类坑:你以为的“离线”,其实是“诈尸”
场景一:小区深夜监控集体黑屏
这是最经典的新手村副本。某高档小区物业报警,说晚上十点后所有摄像头同时黑屏。运维小A第一时间冲到现场,重启录像机(NVR),好了。第二天晚上十点,又黑了。再重启,又好了。
坑点所在: 小A一直在查硬件,查电源,查网线。他忽略了最朴素的一个问题——温度。
这个小区的视频综合机柜位于地下室,散热极差。夏季晚上十点是用电高峰,加上地下排水泵启动,环境温度飙升。那些老旧的NVR设备,电源模块电容早就干涸了,一热就保护性关机。重启后温度暂时下降,机器又能苟活几个小时,直到热平衡再次达成。
解决之道:
- 物理层验证: 不要只看日志。用红外测温枪打一下NVR电源部分的温度。如果烫手,问题就找到了。
- 环境改造: 加装工业级空调或大功率排气扇。对于地下室机柜,这是性价比最高的投资。
- 设备更替: 这种老式NVR建议整体更换为支持宽温工作的工业级设备,或者至少更换电源模块。
给小朋友的比喻: 就像你下午体育课跑完步,脸红扑扑的,如果不喝凉水、不扇扇子,就会晕倒(黑屏)。睡觉休息(重启)能让你暂时恢复,但如果天气一直那么热,你睡醒了还是会晕。所以,得给身体(设备)装个空调(散热),或者换个体质更强(工业级)的身体。
场景二:交换机端口“时断时续”
某办公区网络偶尔卡顿,Ping包有丢包。运维查日志,发现交换机某个端口频繁Up/Down。换个网线、换个接口,问题依旧。
坑点所在: 光模块不匹配或老化。
很多时候,我们用便宜的第三方光模块替代原厂模块,或者混用了不同品牌的光模块。在白天温度低、负载小时尚可维持,一旦环境温度升高或光信号衰减到临界值,就会出现链路震荡。
解决之道:
- 日志分析: 查看交换机的
log-buffer,寻找link-flap或carrier相关的报错。 - 替换测试: 只换同品牌、同型号的光模块。
- 诊断命令: 使用
show interfaces或厂商专用诊断命令(如华为的display transceiver diagnose)查看光功率。如果接收光功率接近灵敏度下限(比如-27dBm以下),信号肯定不稳。
# 华为交换机查看光模块状态示例
display transceiver interface GigabitEthernet 0/0/1 verbose
# 关注Rx Power(接收光功率)
# 正常值应在 -3dBm 到 -15dBm 之间
# 如果显示 -28dBm 或 "High Alarm",说明光模块有问题或光纤损耗过大
第二类坑:被“自动化”背刺的冤魂
场景三:工厂流水线自动重启
这是一起典型的“连环雷”事件。某汽车零部件工厂,MES系统(制造执行系统)每天早上8点准时从服务器拉取生产数据。运维团队为了省事,写了一个Python脚本,自动备份数据库并清理日志。
某天清晨,流水线所有PLC(可编程逻辑控制器)控制的上位机同时蓝屏重启,导致生产线瘫痪两小时。
坑点所在: 资源竞争与死锁。
那个自动备份脚本在凌晨4点运行,占用了大量CPU和IO。而MES系统在8点开始高并发读写数据库。更糟糕的是,清理日志的脚本误删了某些正在被PLC进程锁定的.log文件,导致PLC软件抛出未捕获异常,直接崩溃重启。
解决之道:
- 依赖分析: 任何自动化脚本上线前,必须画出资源依赖图。明确哪些进程是“实时关键”的(如PLC通信),哪些是“后台批量”的(如日志清理)。
- 时间窗口隔离: 将非关键任务(日志清理、备份)严格限制在凌晨2:00-5:00的低负载窗口,并设置依赖锁。如果PLC通信进程活跃,备份任务必须挂起。
- 权限最小化: 备份脚本不应有删除生产环境日志的权限。应改为“归档”而非“删除”。
代码示例(带依赖检查的备份脚本):
import subprocess
import time
import schedule
def is_plc_active():
"""检查PLC通信进程是否在运行"""
try:
result = subprocess.run(['pgrep', '-f', 'plc_comm_daemon'], capture_output=True, text=True)
return bool(result.stdout.strip())
except Exception:
return False
def safe_backup():
"""安全备份任务"""
if is_plc_active():
print("警告:PLC通信进程正在运行,跳过本次备份。")
return False
try:
# 执行备份逻辑...
print("备份完成。")
return True
except Exception as e:
print(f"备份失败: {e}")
return False
# 每天凌晨3点执行
schedule.every().day.at("03:00").do(safe_backup)
while True:
schedule.run_pending()
time.sleep(60)
场景四:脚本“成功”了,但事没办成
运维小B写了一个自动化巡检脚本,每天发送“巡检完成”的邮件。他自信满满地设置了报警阈值:如果邮件没发出来,就打电话叫他。
结果有一天,脚本执行了,但是报错信息被重定向到了/dev/null,而成功日志照常输出。邮件发出来了,但里面没有数据。小B以为一切正常,直到业务部门投诉数据缺失三天。
坑点所在: 误报与静默失败。
很多自动化脚本只检查“进程是否退出”,而不检查“业务逻辑是否成功”。在Linux中,exit 0代表成功,但如果你的脚本在中间某步失败后直接exit 0了,监控就会误判。
解决之道:
- 返回值校验: 脚本的每一个关键步骤都要检查返回值。
- 内容校验: 不仅要看邮件发没发,还要解析邮件内容,检查关键字段是否为空。
- 告警分层: 建立“进程级告警”和“数据级告警”。进程挂了要叫醒你,数据不对也要叫醒你。
import sys
def critical_step():
# 模拟一个可能失败的操作
if some_condition_fails():
return False
return True
if __name__ == "__main__":
if not critical_step():
print("错误:关键步骤失败", file=sys.stderr)
sys.exit(1) # 必须非零退出,否则监控会认为成功
print("巡检完成")
sys.exit(0)
第三类坑:云端的“黑洞”
场景五:AWS EC2实例突然“失联”
某创业公司的服务器在AWS上,某次大促前夜,运维在控制台看到服务器状态检查失败,SSH连不上。他以为是黑客攻击,紧急联系云厂商,结果发现是磁盘空间满导致系统服务无法启动。
坑点所在: 云环境的不可见性。
物理机坏了,你能听见风扇停转的声音,看见红灯。云服务器坏了,你看到的只是一个红色的“Status Check Failed”。更坑的是,很多初创团队为了省钱,没有配置CloudWatch的高级告警,只设置了“实例停止”才报警。而磁盘满,实例并没有停止,只是卡死了。
解决之道:
- 云监控深度集成: 必须配置CloudWatch指标,包括
Disk Space Utilization、CPU Credit Balance(针对T系列突发性能实例)、Network In/Out。 - 自动扩展与自愈: 设置CloudWatch Alarms,当磁盘使用率超过85%时,自动触发Lambda函数清理日志,或者自动重启实例。
- FinOps意识: 定期检查闲置资源。很多时候,故障是因为为了省钱开了太小规格的实例,导致性能瓶颈引发的连锁反应。
# 使用AWS CLI检查磁盘空间(如果在实例内部执行)
aws ssm send-command \
--instance-ids "i-0123456789abcdef0" \
--document-name "AWS-RunShellScript" \
--parameters 'commands=["df -h | grep /"]'
场景六:K8s Pod“一直重启”
微服务架构下,运维发现某个Pod状态一直是CrashLoopBackOff。一看日志,全是OOMKilled(内存溢出被杀)。但是,应用代码明明只用了50MB内存,为什么容器被限制在64MB就死了?
坑点所在: 容器内存计算的误区。
很多人以为容器内存=应用程序内存。错!容器内存=应用程序内存 + JVM堆外内存 + 线程栈 + 内存映射文件 + 容器本身的管理开销。如果是Java应用,默认JVM堆大小往往是宿主机内存的1/4,而不是容器限制的大小。所以,即使你给容器设了64MB限制,JVM启动时可能就想申请更多内存,直接被cgroup杀掉。
解决之道:
- 明确限制: 在K8s YAML中明确设置
resources.limits和resources.requests,并且limits要大于requests。 - JVM调优: 对于Java应用,必须传入JVM参数
-XX:MaxRAMPercentage=50.0,让JVM知道容器只有64MB,不要试图申请更多。 - 监控基线: 使用Prometheus + Grafana监控容器的实际内存使用曲线,找出峰值,再设定合理的Limit。
# K8s Deployment 资源配置示例
resources:
limits:
memory: "128Mi"
cpu: "500m"
requests:
memory: "64Mi"
cpu: "250m"
# Java 启动参数示例
java -XX:MaxRAMPercentage=50.0 -jar myapp.jar
第四类坑:人为的“神操作”
场景七:一条错误的SQL删库
这是运维界的“经典永流传”。某DBA在执行线上数据清理时,忘记加WHERE条件,执行了DELETE FROM user_table。虽然及时从备份恢复,但造成了部分数据不一致。
坑点所在: 缺乏二次确认机制和权限分离。
很多团队认为“DBA都受过专业训练,不会犯这种错”。但人非圣贤,何况是凌晨三点困得睁不开眼的时候。
解决之道:
- SQL审核平台: 引入如Yearning、Archery等SQL审核平台。所有线上SQL必须经过审批流程才能执行。
- 权限最小化: DBA账号不应有直接在生产库执行DELETE/UPDATE的权限。必须通过跳板机,且有操作审计。
- 备份验证: 定期做备份恢复演练。备份不恢复,等于没备份。
场景八:配置文件“复制粘贴”的灾难
某团队迁移服务器,运维直接从旧服务器scp了整个/etc目录到新乡服务器。结果旧服务器的 hostname 和 IP 绑定信息带了过来,导致新服务器在网络中冲突,且服务启动时因找不到正确的网络接口而报错。
坑点所在: 基础设施即代码(IaC)的缺失。
靠“复制粘贴”做运维,永远是在堆垃圾。配置漂移(Configuration Drift)是系统不稳定的一大根源。
解决之道:
- 使用Ansible/Terraform: 将所有服务器配置定义为代码。新服务器上线,是从代码生成,而不是从旧服务器拷贝。
- 幂等性检查: 确保脚本执行多次结果一致。
- 版本控制: 配置文件必须入库Git,任何变更都有迹可查。
第五类坑:看不见的“慢故障”
场景九:数据库连接池耗尽
某网站平时运行流畅,但每到整点(如10:00, 11:00)就会卡顿几秒。运维查CPU、查内存,一切正常。最后发现是数据库连接池配置不当。应用层在整点批量处理任务时,瞬间创建大量连接,而旧连接没有及时释放,导致后续请求排队等待。
坑点所在: 峰值流量的潮汐效应。
监控图表看的是平均值,平均值会掩盖瞬态峰值。
解决之道:
- 全链路追踪: 使用SkyWalking或Jaeger追踪请求链路,找出慢查询和连接等待的瓶颈。
- 连接池调优: 根据实际并发量调整
maxTotal、maxIdle、minIdle。不要使用默认值。 - 熔断降级: 在流量峰值时,对非核心接口进行熔断,保护核心数据库。
// HikariCP 连接池配置示例
spring:
datasource:
hikari:
maximum-pool-size: 20 # 根据核心数调整,通常核心数*2或核心数+1
minimum-idle: 5
idle-timeout: 600000
max-lifetime: 1800000
connection-timeout: 30000
场景十:SSL证书过期
某重要业务系统在某个周五下午突然无法访问,报错“SSL握手失败”。运维排查后发现,是因为某子域名的SSL证书在凌晨过期,而监控只报了“服务不可达”,没有细化到SSL层面。
坑点所在: 监控粒度不够细。
解决之道:
- 证书监控: 使用工具(如certbot、letsencrypt或专门的SSL监控服务)提前30天、7天、1天告警。
- 自动化续签: 部署Certbot+Cron或K8s的cert-manager,实现证书自动续签。
# Certbot 自动续签脚本示例
0 3 * * * root certbot renew --quiet && systemctl reload nginx
运维的“保命”心法
聊了这么多坑,最后总结几条运维界的“保命心法”,希望能帮大家在坑里少摔几次:
- 敬畏生产环境: 每一次操作,都假设它可能会毁掉一切。操作前备份,操作后验证。
- 日志是圣经: 没有日志,你就是瞎子。确保所有关键组件都有合理级别的日志(DEBUG/INFO/ERROR),并集中收集到ELK或Loki中。
- 监控要有层次: 基础设施层(CPU/内存)、应用层(QPS/延迟/错误率)、业务层(订单量/支付成功率)。不要只看第一个,那只是表象。
- 自动化不等于无人化: 自动化脚本更要测试,测试环境不是垃圾场,要模拟生产数据的10%规模进行测试。
- 保持学习,保持谦卑: 技术栈迭代太快,今天熟悉的K8s,明天可能就有新的Mesh架构。承认自己不懂,比假装懂更安全。
运维不是简单的“修电脑”,它是保障业务连续性的最后一道防线。每一个黑屏的夜晚,每一次自动重启,都是系统在向我们求救。读懂它们,解决问题,我们才是真正有价值的工程师。
希望这篇文章能给你一些启发。如果你也有什么踩坑的经历,欢迎在评论区分享,我们一起避坑。
