作者: Kevin Hannon (Red Hat)
译者: Rui Yang
运行或者操作 Kubernetes 集群的一个常见问题是磁盘空间不足。 在预分配节点时,你的目标应该是为容器镜像和正在运行的容器提供大量存储空间。
容器运行时通常会写入到 /var
目录下。
他可以位于单独的分区或者位于根文件系统上。
在默认情况下,CRI-O 将其容器和镜像写入 /var/lib/containers
目录下,而 containerd 将其容器和镜像写入 /var/lib/containerd
目录下。
在这篇博文中,我们希望引起您的注意,您可以配置容器运行时以将其内容与默认分区分开存储。 这使得配置 Kubernetes 变得更加灵活,并支持为容器存储添加更大的磁盘,同时保持默认文件系统不变。
需要详细了解的是 Kubernetes 会写入到磁盘哪里以及写入哪些内容。
理解 Kubernetes 磁盘的使用
Kubernetes 有持久化的数据和临时数据。kubelet 和本地 Kubernetes
特定的存储的根路径是可配置的,但是通常认为它位于 /var/lib/kubelet
。在 Kubernetes 文档中,这有时被称为根或节点文件系统。该数据的大部分可以分为:
- 临时存储
- 日志
- 容器运行时
这与大多数 POSIX 系统不同,因为根或者节点文件系统不是 /
,而是 /var/lib/kubelet
所在的磁盘。
临时存储
Pod 和容器可能需要临时或暂时的本地存储来进行操作。 临时存储的生命周期不会超过单个 pod 的生命周期,并且临时存储不能跨 pod 共享。
日志
默认情况下,Kubernetes 会将每个运行容器的日志,作为文件存储在 /var/log
目录下。
这些日志是临时的,由 kubelet 监控,以确保它们在运行期间不会变得太大。
你可以自定义每个节点的 日志轮转 设置,以管理这些日志的大小,以及配置日志传输(使用第三方解决方案)以避免依赖于节点本地存储。
容器运行时
容器运行时有两个不同的存储区域用于容器和镜像。
- 只读层: 镜像通常被标记为只读层,因为它们在容器运行时不会被修改。 只读层可以由多个层组成,然后组合成一个单独的只读层。 在容器的顶层上,有一个薄层提供容器的临时存储,如果容器在文件系统上写入,则其可以提供为临时存储。
- 可写层: 依赖于您的容器运行时,本地写入可能会使用基于层的写机制(例如,Linux 上的
overlayfs
或 Windows 上的CimFS
)。 这被称为可写层。 本地写入也可以使用可写文件系统,该文件系统使用容器镜像的完整克隆进行初始化;这用于基于虚拟机管理程序的一些运行时。
容器运行时文件系统包含只读层和可写层。
在 Kubernetes 文档中,这被称为 imagefs
。
容器运行时配置
CRI-O
CRI-O 使用 TOML 格式的存储配置文件,让你可以控制容器运行时存储持久化和临时数据。
CRI-O 使用 storage library。
一些 Linux 发行版提供了存储的手册页 (man 5 containers-storage.conf
)。
存储的主要配置位于 /etc/containers/storage.conf
,用户可以控制临时数据的位置和根目录。
根目录是 CRI-O 存储持久数据的位置。
|
|
graphroot
- 容器运行时的持久数据存储
- 如果启用 SELinux,则必须与
/var/lib/containers/storage
相匹配
runroot
- 临时访问容器的读/写访问
- 建议将此放在临时文件系统中
这里有一个快速的方法来重新标记你的 graphroot 目录,以匹配/var/lib/containers/storage
:
|
|
containerd
containerd 使用 TOML 配置文件来控制持久数据和临时数据的存储位置。
默认的配置文件路径位于 /etc/containerd/config.toml
。
containerd 存储的相关字段是 root
和 state
。
root
- 容器运行时元数据的根目录
- 默认位置为
/var/lib/containerd
- 如果您的操作系统需要,Root 也需要 SELinux 标签
state
- 容器运行时的临时数据
- 默认位置为
/run/containerd
Kubernetes 节点压力驱逐
Kubernetes 将自动检测容器文件系统是否与节点文件系统分离。 当将文件系统分离时,Kubernetes 负责监控节点文件系统和容器运行时文件系统。 Kubernetes 文档将节点文件系统和容器运行时文件系统称为 nodefs 和 imagefs。 如果 nodefs 或 imagefs 中的任何一个磁盘空间不足,则整个节点被认为存在磁盘压力。 Kubernetes 首先通过删除未使用的容器和镜像来回收空间,然后将逐出 Pod。 在具有 nodefs 和 imagefs 的节点上,kubelet 将在 imagefs 上使用垃圾收集来清理未使用的容器镜像,并从 nodefs 中删除死亡的 Pod 及其容器。 如果只有一个 nodefs,则 Kubernetes 垃圾收集包括死亡的容器、死亡的 Pod 和未使用的镜像。
Kubernetes 提供了许多配置选项来确定磁盘是否已满。
kubelet 中的驱逐管理器有一些配置设置,可以让你控制相关的阈值。
对于文件系统,相关的度量是nodefs.available
、nodefs.inodesfree
、imagefs.available
和imagefs.inodesfree
。
如果没有专门用于容器运行时的磁盘,则会忽略 imagefs。
用户可以使用现有的默认值有:
memory.available
< 100MiBnodefs.available
< 10%imagefs.available
< 15%nodefs.inodesFree
< 5% (Linux 节点)
Kubernetes 允许您在 kubelet 配置文件中设置用户自定义值,具体位置为 EvictionHard
和 EvictionSoft
配置项。
EvictionHard
- 定义限制;一旦超过这些限制,Pod 将立即驱逐,没有任何宽限期。
EvictionSoft
- 定义限制;一旦超过这些限制,Pod 将按照各信号所设置的宽限期后被。
如果你为 EvictionHard
指定了值,那么它将替代默认值。
这意味着在你的配置中设置所有的信号显得非常重要。
举个例子,以下的 kubelet 配置可用于配置驱逐信号和宽限期选项。
|
|
问题
Kubernetes 项目建议你遵循默认驱逐设置或着设置所有驱逐字段。
你可以使用默认设置或指定你自己的 evictionHard
设置。 如果你漏掉一个信号,那么 Kubernetes 将不会监控该资源。
管理员或用户常见的一个配置错误是将新的文件系统挂载在 /var/lib/containers/storage
或 /var/lib/containerd
目录上。Kubernetes 会检测到一个单独的文件系统,因此如果你已经这样做,请确保检查 imagefs.inodesfree
和 imagefs.available
符合你的需要。
另一个令人困惑的是,当为节点定义一个镜像文件系统时,临时存储报告不会改变。镜像文件系统(imagefs
)被用于存储容器镜像层;如果一个容器向自己的根文件系统写入,那么这种本地写入不会计入到容器镜像的大小中。容器运行时用于存储本地修改的位置由运行时本身决定,通常情况下,这个位置就是镜像文件系统。
如果一个 Pod 中的容器在 emptyDir
文件系统上写入数据,那么它将使用 nodefs
文件系统中的空间。
kubelet 一直根据 nodefs
表示的文件系统报告临时存储容量和分配情况;当临时写入操作实际上是写到镜像文件系统时,这种差别可能会让人困惑
后续工作
为了修复临时存储报告的相关限制和为容器运行时提供更多的配置选项,SIG Node 正在开发 KEP-4191。 在 KEP-4191 中,Kubernetes 将检测可写层和只读层(镜像)是否分离。 这可以让我们将所有临时存储,包括可写层,都放在同一个磁盘上,同时还可以为镜像分配一个单独的磁盘。
参与其中
如果你有兴趣参与其中,你可以加入 Kubernetes Node 特性兴趣小组 (SIG)。
如果你有兴趣分享反馈,你可以分享到我们的#sig-node Slack 频道。 如果你还没有加入该 Slack 工作空间,请访问 https://slack.k8s.io/ 获取邀请。
特别感谢所有提供出色评审、分享宝贵见解或建议主题想法的贡献者。
- Peter Hunt
- Mrunal Patel
- Ryan Phillips
- Gaurav Singh
原文地址: https://kubernetes.io/blog/2024/01/23/kubernetes-separate-image-filesystem/
官方译文: https://kubernetes.io/zh-cn/blog/2024/01/23/kubernetes-separate-image-filesystem/