《博客崩溃源记》

原文链接:https://www.hezebin.com/publish/66a393d03ad6ee047813fe57

背景

百年未有之大变局年夏初,最近在负责前端同学招聘的一面工作,对于一些常见的八股文基础问题做了一下记录…

忽有一日,面试在即,打开博客见文章列表无了!!!ERROR!!😱

排查

从上述现象看,所有 api 接口超时报错,但是页面能打开,说明至少服务器机器和 Nginx 服务没问题,由于所有服务都部署在 K8S 环境中,此时大致猜想是博客服务 Clash 崩溃了… 于是查看了一下 Pod 状态:

从上图看是 mongo-sts-1 挂了,Pod 的异常状态是 ImagePullBackOf,由于该 Pod 在副本集中还是主节点,且我没有设置 MongoDB 读取偏好的原因,默认使用了 Primary 只从主节点读取数据,所以读取连接超时了。


到这时,主要在比较纳闷为什么会拉取镜像失败呢?所以尝试性的将这个 Pod 删了,让其自动重启一下,仍然没解决,然后又呆滞的将 StatefulSet 重启了一下,OK 现在 3 个 Pod 都挂了,都是拉取不到镜像 😭!


于是,我开始怀疑是不是网络问题了,便尝试直接拉取了一下 Docker 镜像(容器用的 Containerd):

ctr images pull docker.io/library/mongo:latest

失败!网络超时!到这里也算是能确认排查方向就是网络问题了,于是先查看了一下配置的镜像仓库:

cat /etc/rancher/k3s/registries.yaml

结果竟然是空的,我竟然没配,但是之前竟然正常拉取到了镜像!二话不说,先把国内 Docker 镜像源配置上,重启 K3s 后重新尝试直接命令拉取镜像,但是还是超时失败…


这个时候,和网络相关的,我就只能怀疑是我配置的代理问题了,当初部署搭建 K3s 环境时,为了下载国外各种安装包和镜像源方便,在云服务机器上安装了 V2ray,并配置了代理,但是代理的环境变量是临时的:

export http_proxy=http://127.0.0.1:1087
export https_proxy=http://127.0.0.1:1087
export ALL_PROXY=socks5://127.0.0.1:1080

所以我理解我关闭了终端后,这个代理的环境变量就失效了,且我在当前终端打印查看了环境变量,确实是空的,说明宿主机上的网络请求确实没走代理 ( ping 了 google.com 也确实不通啊 ),即使 V2ray 的代理服务一直运行着,但是没走代理就不应该有影响。但是,抱着尝试验证一下的想法,决定把这个代理服务给停掉,确认一下是否真的是由这个代理服务导致的网络问题。


于是关闭代理后我继续命令拉取镜像,仍然失败!此时潜意识的 k get pod了一下想看最新的服务状态,侥幸万一镜像拉到了呢? 突然!很快啊!这一操作竟然也失败了?


错误信息明确提示由于代理网关失败!这个信息就很关键了,说明虽然宿主机网络没走代理,但是 K8S 内的网络通信走了代理,虽然暂时不知道为什么,但是想尽快解决问题就只能先把 V2ray 再启动起来了!


!!! note
忽然!该死的记忆疯狂涌入了大脑🧠,让我想起了点什么事,我昨天在代理服务商那边重置了我的订阅地址,因为当时在用 GPT 时有点卡,所有节点普遍延迟有点高,想通过刷新订阅地址更新一下服务器节点列表,但这会使原先的订阅地址和对应的配置、认证等失效 …
!!!

然后将新的 V2ray 配置更新并重启 V2ray 后,此时普天同庆!终于通过命令直接拉取到镜像了!!!


但事实是我高兴的还是太早了,重新查看 Pod 状态,发现仍然没有启动成功,虽然不是镜像拉取失败了,但是此时的状态变更为了:CrashLoopBackOff,“突闻噩耗,这让本就不健康的状态雪❄️上加霜🩸”…


查看 Pod 日志,有大量的警告和错误日志,关键信息大致是在描述版本不兼容:

{"t":{"$date":"2024-07-19T09:03:16.660+00:00"},"s":"F",  "c":"-",        "id":23093,   "ctx":"initandlisten","msg":"Fatal assertion","attr":{"msgid":28579,"error":"UnsupportedFormat: Application metadata for table:index-37-7408967326262778357 has unsupported format version: 14. Index: {name: user_1_db_1, ns: admin.system.users} - version either too old or too new for this mongod.","file":"src/mongo/db/storage/wiredtiger/wiredtiger_index.cpp","line":514}}

这就又让我很疑惑了,我使用的镜像地址固定是 mongo:latest,即最新镜像版本,为什么在重新拉取后就直接不兼容了?而且我查看了 Mongo 官方的更新日志并没有大版本的更新。。。 此情此景,已经是有点脑袋晕晕没有办法了,此刻觉着重要的是先把我的文章数据给搞出来,别让数据丢了,但是看了下导出挂载在本地服务器的数据文件竟然有 2 个 G 左右,但实际我的所有文章 JSON 数据大小 1M 都不到,这导出来成本太大了,且去恢复官方的原始数据文件也是一个极为繁琐的过程,劝退 ing…

很庆幸的是,一开始挂掉的 MongoDB Pod 只有主节点那一个容器,修复代理网络问题重启后仲裁节点也因为兼容问题报错挂掉了,但是副本节点还在,所以赶紧在本地机器上通过 kt-connect 打通K8S集群网络,使用Mongo 官方的可视化管理工具 Compass 连上该副本集,然后将数据导出成 JSON 文件,至此,在此次危机中,至少文章数据得以留存…

最后将 Mongo 副本集完全 Flush 后重新安装,然后将数据重新导入后博客暂时恢复正常~

根因

服务器安装了代理服务软件,在重置了代理服务商的订阅地址后,导致服务器代理失效!🌚

分析

上述的各种问题和过程中疑点重重,下面来逐一分析原因~

为什么 Pod 会重启?

Mongo 的 StatefulSet 重启并尝试重新拉取镜像,可能原因包括:

  • 镜像策略设置:imagePullPolicy 设为 Always,导致每次启动都尝试拉取最新镜像。
  • Probe 配置敏感:Readiness 或 Liveness Probe 配置过于严格,导致频繁重启。
  • 特定应用依赖:MongoDB 可能有网络依赖,导致在网络波动时触发重启。
  • 资源限制:临时网络问题导致资源(如内存或 CPU)不足,引发重启。

为什么重启时没使用之前的本地镜像缓存?

在 Kubernetes 中,如果没有配置 imagePullPolicy,默认值取决于镜像标签:

  • latest 标签:默认为 Always。
  • 其他标签或具体版本:默认为 IfNotPresent。

因此,如果使用 mongo:latest,会每次启动都尝试拉取镜像。建议将 imagePullPolicy 设置为 IfNotPresent 来避免这种情况。

为什么 K3s 配置了代理?

K3s 在部署时自动创建了一个环境变量的配置文件:/etc/systemd/system/k3s.service.env,该配置文件下刚好存在网络代理的环境变量:

目前没去深究 K3s 在部署时的具体操作行为,只能是猜测它尤其关注网络配置相关的环境变量,若存在则延用到 K3s 中。当时的环境变量虽然是临时的,结果被应用到K3s 的配置文件中成K3s 永久的网络代理配置了。

为什么重新拉取镜像后出现了版本不兼容情况?

通过查看本地的镜像缓存列表,然后进一步查看镜像详细信息,在镜像信息中发现了一个叫MONGO_VERSION 的变量:

最新的 latest 指向的镜像其版本为 5.0.x,而之前的均为 7.0.x,这跨了 2 个大版本,难怪会版本不兼容。

为什么 latest 出现了拉取到的最新版本是老版本?

上述提到都是 mongo:latest,之前是 7.x,但现在竟然变成了 5.x ,版本还更旧了,真很夸张!

复盘了一下经过,大概想到原因是一开始由于配置了代理,可以访问外网,所以我没有配置 K3s 的镜像仓库,于是所有的镜像都是直接从 Docker 官方镜像源拉取的。但是在排查问题的过程中,我发现了没配国内镜像源就给配上了,结果导致后面拉取的镜像都是从国内镜像源拉取的,国内的镜像仓库可能没有同步最新的版本,导致 latest 拉到的还是老版本,就酱紫了…

为什么副本节点能够正常,其他节点挂掉了?

目前还没找到具体原因,只能猜测是 MongoDB 自身在不同节点角色上的兼容差异导致副本节点幸免于难版本的冲突

解决方案

如何避免出现本次的这些问题,和以后如何更好的处理此类问题呢?

固定版本

不要使用 latest,选择固定的稳定版本,每次升级前仔细阅读和确认官方的变更情况,然后再做升级。

数据备份

这次问题的出现在整个过程中我最担心的就是我的博客文章数据怎么办?只能说鸡蛋🥚还是不能放在一个篮子里,要多做数据备份,万一哪次彻底没法找回数据了,也好用备份的数据做恢复。

目前写了一个简易的专门用于做数据备份的服务,定时将数据备份到 OSS:https://github.com/ihezebin/data-backup

tips:以前遇到过数据库被劫持勒索,被清空的情况,所以唯一解法还是做好数据备份。

补充

这里补充一个在 K8S 中部署中间件分片或副本集集群的坑,如 Redis 分片集群存在一个问题是:即使你在初始化集群注册节点时用的 dns 名称:

redis-cli -a root --cluster create redis-sts-0.redis.default.svc.cluster.local:7000 redis-sts-1.redis.default.svc.cluster.local:7000 redis-sts-2.redis.default.svc.cluster.local:7000 --cluster-replicas 0

在 Redis 维护的节点数据文件里,其节点信息记录的是当时 dns 名称解析出的具体 ip,若 sts 重启,那 Pod 的 ip 必然会发生变化,这就会导致集群状态异常和崩溃:

目前我的解法就是用 Pod 来部署,然后每个 Pod 绑定一个 Service,只要 Service 不删,ip 就不变。

参考文献 🐶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值