揭秘!containerd 镜像文件丢失问题,竟是镜像生成惹得祸

2024-02-14 14:59

本文主要是介绍揭秘!containerd 镜像文件丢失问题,竟是镜像生成惹得祸,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

导语

作者李志宇,腾讯云后台开发工程师,日常负责集群节点和运行时相关的工作,熟悉 containerd、docker、runc 等运行时组件。近期在为某位客户提供技术支持过程中,遇到了 containerd 镜像丢失文件问题,经过一系列分析、推断、复现、排查,最终成功找到根因并给出解决方案。现将整个详细处理过程整理成文分享出来,希望能够为大家提供一个有价值的问题处理思路以及帮助大家更好地理解相关原理。

containerd 镜像丢失文件问题说明

近期有客户反映某些容器镜像出现了文件丢失的奇怪现象,经过模拟复现汇总出丢失情况如下:

某些特定的镜像会稳定丢失文件;

“丢失”在某些发行版稳定复现,但在 ubuntu 上不会出现;

v1.2 版本的 containerd 会文件丢失,而 v1.3 不会。

通过阅读源码和文档,最终解决了这个 containerd 镜像丢失问题,并写下了这篇文章,希望和大家分享下解决问题的经历和镜像生成的原理。为了方便某些心急的同学,本文接下来将首先揭晓该问题的答案~

根因和解决方案

由于内核 overlay 模块 Bug,当 containerd 从镜像仓库下载镜像的“压缩包”生成镜像的“层”时,overlay 错误地把trusted.overlay.opaque=y这个 xattrs 从下层传递到了上层。如果某个目录设置了这个属性,overlay 则会认为这个目录是不透明的,以至于在进行联合挂载时该目录将会把下面的目录覆盖掉,进而导致镜像文件丢失的问题。

这个问题的解决方案可以有两种,一种简单粗暴,直接升级内核中 overlay 模块即可。

另外一种可以考虑把 containerd 从 v1.2 版本升级到 v1.3,原因在于 containerd v1.3 中会主动设置上述 opaque 属性,该版本 containerd 不会触发 overlayfs 的 bug。当然,这种方式是规避而非彻底解决 Bug。

snapshotter 生成镜像原理分析

虽然根本原因看起来比较简单,但分析的过程还是比较曲折的。在分享下这个问题的排查过程和收获之前,为了方便大家理解,本小节将集中讲解问题排查过程涉及到的 containerd 和 overlayfs 的知识,比较了解或者不感兴趣的同学可以直接跳过。

与 docker daemon 一开始的设计不同,为了减少耦合性,containerd 通过插件的方式由多个模块组成。结合下图可以看出,其中与镜像相关的模块包含以下几种:

enter image description here

  • metadata 是 containerd 通过 bbolt 实现的 kv 存储模块,用来保存镜像、容器或者层等元信息。比如命令行 ctr 列出所有 snapshot 或 kubelet 获取所有 pod 都是通过 metadata 模块查询的数据。

  • content 是负责保存 blob 的模块,其保存的关于镜像的内容一般分为三种:

    1. 镜像的 manifest(一个普通的 json,其中指定了镜像的 config 和镜像的 layers 数组)
    2. 镜像的 config(同样是个 json,其中指定镜像的元信息,比如启动命令、环境变量等)
    3. 镜像的 layer(tar 包,解压、处理后会生成镜像的层)
  • snapshots 是快照模块总称,可以设置使用不同的快照模块,常见的模块有 overlayfs、aufs 或 native。在 unpack 时 snapshots 会把生成镜像层并保存到文件系统;当运行容器时,可以调用 snapshots 模块给容器提供 rootfs 。

容器镜像规范主要有 docker 和 oci v1、v2 三种,考虑到这三种规范在原理上大同小异,可以参考以下示例,将 manifest 当作是每个镜像只有一份的元信息,用于指向镜像的 config 和每层 layer。其中,config 即为镜像配置,把镜像作为容器运行时需要;layer 即为镜像的每一层。

type manifest struct {c configlayers []layer
}

镜像下载流程与图 1 中数字标注出来的顺序一致,每个步骤作用总结如下:

首先在 metadata 模块中添加一个 image,这样我们在执行 list image 时可看到这个 image。

其次是需要下载镜像,因为镜像是有 manifest、config、layers 等多个部分组成,所以先下载镜像的 manifest 并保存到 content 模块,再解析 manifest 获取 config 的地址和 layers 的地址。接下来分别把 config 和每个 layer 下载并保存到 content 模块,这里需要强调镜像的 layer 本来应该是目录,当创建容器时联合挂载到 root 下,但是为了方便网络传输和存储,这里会用 tar + 压缩的方式保存。这里保存到 content 也是不解压的。

③、④、⑤的作用关联性比较强,此处放在一起解释。snapshot 模块去 content 模块读取 manifest,找到镜像的所有层,再去 content 模块把这些层自“下”而“上”读取出来,逐一解压并加工,最后放到 snapshot 模块的目录下,像图 1 中的 1001/fs、1002/fs 这些都是镜像的层。(当创建容器时,需要把这些层联合挂载生成容器的 rootfs,可以理解成1001/fs + 1002/fs + … => 1008/work)。

整个流程的函数调用关系如下图 2,喜欢阅读源码的同学可以照着这个去看下。
enter image description here

为了方便理解,接下来用 layer 表示 snaps

这篇关于揭秘!containerd 镜像文件丢失问题,竟是镜像生成惹得祸的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/708818

相关文章

SpringSecurity整合redission序列化问题小结(最新整理)

《SpringSecurity整合redission序列化问题小结(最新整理)》文章详解SpringSecurity整合Redisson时的序列化问题,指出需排除官方Jackson依赖,通过自定义反序... 目录1. 前言2. Redission配置2.1 RedissonProperties2.2 Red

nginx 负载均衡配置及如何解决重复登录问题

《nginx负载均衡配置及如何解决重复登录问题》文章详解Nginx源码安装与Docker部署,介绍四层/七层代理区别及负载均衡策略,通过ip_hash解决重复登录问题,对nginx负载均衡配置及如何... 目录一:源码安装:1.配置编译参数2.编译3.编译安装 二,四层代理和七层代理区别1.二者混合使用举例

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

java实现docker镜像上传到harbor仓库的方式

《java实现docker镜像上传到harbor仓库的方式》:本文主要介绍java实现docker镜像上传到harbor仓库的方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 前 言2. 编写工具类2.1 引入依赖包2.2 使用当前服务器的docker环境推送镜像2.2

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

springboot项目打jar制作成镜像并指定配置文件位置方式

《springboot项目打jar制作成镜像并指定配置文件位置方式》:本文主要介绍springboot项目打jar制作成镜像并指定配置文件位置方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录一、上传jar到服务器二、编写dockerfile三、新建对应配置文件所存放的数据卷目录四、将配置文

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到