总结/var/lib/containerd 和 /var/lib/kubelet 目录迁移后服务异常, 数据丢失.
迁移数据期间, k8s 重启异常:
- local-storage 类型的PV 全部被清空
- nginx ingress 无法重启
…
导火索是, 需要在现有的k8s 中部署一个MinerU 的服务, 官方提供的镜像打包出来后是40G, 而containerd 和kubelete 数据都在系统盘中, 系统盘总共100G, 在尝试部署期间触发了磁盘盘占用超过85%的警告而终止.
而我们的数据盘有1T容量, 并且余量很富足.
思考后决定: 迁移 /var/lib/containerd 和 /var/lib/kubelet 目录到数据盘.
在查阅资料后,实操步骤是:
- 停止 kukelet 服务:
systemctl stop kubelet - 停止 containerd 服务:
systemctl stop containerd - 复制数据:
rsync -av --progress /var/lib/containerd/ /data900/containerd/和rsync -av --progress /var/lib/kubelet/ /data900/kubelet/ - 修改原文件夹到新路径:
mv /var/lib/containerd /var/lib/containerd.bak和mv /var/lib/kubelet /var/lib/kubelet.bak - 创建软连接:
ln -s /data900/containerd /var/lib/containerd和ln -s /data900/kubelet /var/lib/kubelet - 重启containerd 和 kubelet 服务
但是, 启动就发现各项应用异常, 排查后发现是原本哪些使用了local-storage 挂载本地磁盘路径的PV, 仍然在, 但磁盘下的数据全清空, 各项服务均初始化了.
并且在部署 MinerU 后, 试图通过Ingress 向外发布时, 始终无法更新ingress, 在尝试主动 结束 nginx-ingress 对应的pod后, ingress 再也无法启动, 提示80 端口占用————但本机确实端口未占用.
从某种程度上来讲, 这是不幸的, 自己亲自触发了一个顶级事故, 服务异常, 数据永久丢失.
但是呢, 又是幸运的, 和MinerU 相关的任务, 在交代的任务时间点前, 做完了(停掉containerd 和kubelet,用docker run 人工配置启动每个基础服务+应用), 同时这台服务器上没有生产数据.
但, 警钟长鸣, 实际作业中的标准/细节, 都需要改善:
- 快照. 在对应用软件下层基础设施改动时, 在已经有很多软件运行的前提下, 还是在阿里云上租的云服务器, 都值得用快照临时备份. 这里低估了迁移数据重启带来的风险, 没有想到连PV 挂载的本地路径下的数据都会被清空.
- local-storage. k8s 官方其实一直强调, local-storage 仅用于开发、测试环境. 但是我一直在云端的环境中使用local-storage. 从成本的角度来说, 非结构化文件, 放到对象存储中挂载到k8s 中, 更合适, 而且也比阿里云的云盘便宜.
- 单节点k8s. k8s 推荐的, 能用于生产的部署结构应该是3 + N , 即最少3个节点且为奇数个的k8s 控制节点, 和多个工作节点. 我们一直只有一台云服务器, 但却一直以自建的k8s 来部署服务. 此前选择自建单机k8s 更多是为了学习, 而且当时团队更多是部署一些算法原型, 今年才开始试着往企业方向做一些软件/服务. 但没有想到 4-5 年过去, 单机 的 k8s 逐渐成为了日常的核心, 但它其实不具备高可用, 而且从目前团队的规模, 服务器规模来看, 当初使用 k8s 是不合适的————后续计划的是给团队建议, 对于有稳定要求的应用(有可能发生实际生产数据的应用),使用阿里云的ACS, 测试环境用自建的单机k8s, 或者扩大服务器规模或者准备运维人员.
- 过分相信AI. 对于这次这种相对紧急的开发+实施任务, 在实操过程中省去了很多原本人去核实的流程. 比如实操步骤是AI 问答 + 查看原文结合, 快速推进的.(任务指标需要在2天内完成开发+实施).AI 是对回答不负责的, 而实际使用AI 的人, 也就是我, 是需要对他的结果做核实的.
- 异常未及时终止. 在停掉服务后, rsync 复制完文件后, 执行MV 操作时, 期间出现过异常, 导致kubelet 目录移动失败. 对于实操
- 基础服务要遵循标准. 最初安装k8s , 亦或者docker 时, 就应该区分数据和软件, 让数据直接放到数据盘, 此处就不会触发磁盘占用超85% 的问题, 更不会出现数据永久丢失的问题 ———— 多年前的子弹正中眉心.
- 个人/团队对钱的态度. 其实想过用快照, 但当时没有想到过数据会丢失, 想着最多也就是服务启动异常, 只要数据在,怎么都可以恢复. 另一方面就是, 手里并没有一笔来自团队的公款, 供这些日常操作(比如快照这种), 还是有点舍不得花钱. 如果自己在钱这块的态度更随意, 或者团队能允许一些杂项的报销, 那这里实操的时候, 下意识选择会多一些,发生问题后, 也能多次回滚试错, 直到成功.
回过头来, 思考一下, 这次迁移出现问题, 是为什么呢?很难回溯了, 结合AI 问答, 我能理解并接受的理由如下:
- /var/lib/kubelet 和 /var/lib/containerd 目录下大量包含软连接,硬链接和挂载点.
- rsync 在跨磁盘(系统盘和数据盘, 数据盘挂载在/data下)复制时, 硬链接,挂载点是不能被复制的,且实操时传递给rsync 的参数, 也很少, 不能复制全部数据(元数据).
- kubelet 和containerd 重启后,containerd 会触发POD 重建(因为数据缺失), kubelete 在元数据丢失且POD 重建时, 会进入”宁可错删,不可错用” 的机制, 这不同于K8S 教程中关于PV local-storage 不会清理数据的机制.
- 数据被强制初始化丢失.
而端口没被占用却一直提示占用, 可能就是因为数据丢失, 导致数据不一致, 记录中显示有80 端口被占用.
那为什么查到的攻略是可以这么做, 而自己踩坑了, 出现元数据丢失呢?
因为, 没有通过 drain 标记节点不可用, 来驱逐POD.
为什么又没有选择标记不可用, 并驱逐POD 呢?
因为一开始想着是单机版K8S, 不应该驱逐. 现在回想起来, 还是应该先驱逐.
我不应该既把它当作一个集群来使用, 又把它当作一个单机版软件来维护, 这是不对的.
那如果驱逐了, 我再迁移数据, 就不会出问题了么?
实际情况: k8s apiserver 这类的控制面板, 当初是通过kubeadm 来安装到宿主机中的单机k8s 的 ———— 他在本地中以静态POD 的形式存在.
驱逐节点时, 这类POD 不会被驱逐, 仍然会运行 ———— 也就是说, 现实情况是, 如果驱逐了, /var/lib/kubelet 目录下仍然有POD的记录, POD 如果有挂载点、文件映射, 该有的软硬链接mount等,也都会有, 迁移之后还是会存在元数据不一致.
那以当前的情况, 如果再给我一次实操的机会, 我该怎么做?
标记节点不可用,驱逐POD
检查是否还有POD 在运行
这个时候应该是没有, 或者只有几个关于k8s 控制面板的静态 POD 在运行.
通过移除 manifest 中yaml 来移除静态POD(控制面板)
关于集群的状态, 是存放在etcd中, etcd 在通过kubeadm 部署时有指定路径.POD 结束不会清空磁盘(不会像本次, 因为数据在迁移后前对不上,而触发异常流程,强制清空)
检查containerd 和 k8s 是否有容器运行
此时是希望能看到, 既没有POD 在k8s中运行,也没有容器在containerd 中运行(不然containerd 在迁移时也会出现类似于跨磁盘移动, 挂载无法保留的问题)————甚至, k8s 的客户端已经无法链接k8s 了, 因为控制面板移除, 这个集群, 或者这个节点已经没有了.
停止kubelet 服务
停止containerd 服务
转移 containerd 和 kubelet 目录
软连接 或者 mount 新路径到原本路径
启动containerd
检查contaienrd 运行的容器
启动kubelet
恢复 manifest 下的文件
尝试检查k8s 中运行的POD
这里是希望manifest 下的静态POD 能恢复, 即, 能通过kubectl 客户端查询到集群状态.
标记节点可用
等待服务启动