Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

限制Pod磁盘空间用量

12
Feb
2020

限制Pod磁盘空间用量

By Alex
/ in PaaS
/ tags K8S
0 Comments
Pod如何使用磁盘

容器在运行期间会产生临时文件、日志。如果没有任何配额机制,则某些容器可能很快将磁盘写满,影响宿主机内核和所有应用。

容器的临时存储,例如emptyDir,位于目录/var/lib/kubelet/pods下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/var/lib/kubelet/pods/
└── ac0810f5-a1ce-11ea-9caf-00e04c687e45  # POD_ID
    ├── containers
    │   ├── istio-init
    │   │   └── 32390fd7
    │   ├── istio-proxy
    │   │   └── 70ed81da
    │   └── zookeeper
    │       └── e9e21e59
    ├── etc-hosts          # 命名空间的Host文件
    └── volumes            # Pod的卷
        ├── kubernetes.io~configmap  # ConfigMap类型的卷
        │   └── istiod-ca-cert
        │       └── root-cert.pem -> ..data/root-cert.pem
        ├── kubernetes.io~downward-api
        │   └── istio-podinfo
        │       ├── annotations -> ..data/annotations
        │       └── labels -> ..data/labels
        ├── kubernetes.io~empty-dir # Empty类型的卷
        │   ├── istio-data
        │   └── istio-envoy
        │       ├── envoy-rev0.json
        │       └── SDS
        ├── kubernetes.io~rbd       # RBD卷
        │   └── pvc-644a7e30-845e-11ea-a4e1-70e24c686d29 # /dev/rbd0挂载到这个挂载点
        ├── kubernetes.io~csi       # CSI卷
        └── kubernetes.io~secret    # Secret类型的卷
            └── default-token-jp4n8
                ├── ca.crt -> ..data/ca.crt
                ├── namespace -> ..data/namespace
                └── token -> ..data/token

持久卷的挂载点也位于/var/lib/kubelet/pods下,但是不会导致存储空间的消耗。

容器的日志,存放在/var/log/pods目录下。

使用Docker时,容器的rootfs位于/var/lib/docker下,具体位置取决于存储驱动。

Pod驱逐机制
磁盘容量不足触发的驱逐

具体细节参考:/kubernetes-study-note#out-of-resource。

当不可压缩资源(内存、磁盘)不足时,节点上的Kubelet会尝试驱逐掉某些Pod,以释放资源,防止整个系统受到影响。

其中,磁盘资源不足的信号来源有两个:

  1. imagefs:容器运行时用作存储镜像、可写层的文件系统
  2. nodefs:Kubelet用作卷、守护进程日志的文件系统

当imagefs用量到达驱逐阈值,Kubelet会删除所有未使用的镜像,释放空间。

当nodefs用量到达阈值,Kubelet会选择性的驱逐Pod(及其容器)来释放空间。 

本地临时存储触发的驱逐

较新版本的K8S支持设置每个Pod可以使用的临时存储的request/limit,驱逐行为可以更具有针对性。

如果Pod使用了超过限制的本地临时存储,Kubelet将设置驱逐信号,触发Pod驱逐流程:

  1. 对于容器级别的隔离,如果一个容器的可写层、日志占用磁盘超过限制,则Kubelet标记Pod为待驱逐
  2. 对于Pod级别的隔离,Pod总用量限制,是每个容器限制之和。如果各容器用量之和+Pod的emptyDir卷超过Pod总用量限制,标记Pod为待驱逐
从编排层限制

从K8S 1.8开始,支持本地临时存储(local ephemeral storage),ephemeral的意思是,数据的持久性(durability)不做保证。临时存储可能Backed by 本地Attach的可写设备,或者内存。

Pod可以使用本地临时存储来作为暂存空间,或者存放缓存、日志。Kubelet可以利用本地临时存储,将emptyDir卷挂载给容器。Kubelet也使用本地临时存储来保存节点级别的容器日志、容器镜像、容器的可写层。

Kubelet会将日志写入到你配置好的日志目录,默认 /var/log。其它文件默认都写入到 /var/lib/kubelet。在典型情况下,这两个目录可能都位于宿主机的rootfs之下。

Kubernetes支持跟踪、保留/限制Pod能够使用的本地临时存储的总量。

限制Pod用量

打开特性开关: LocalStorageCapacityIsolation,可以限制每个Pod能够使用的临时存储的总量。

注意:以内存为媒介(tmpfs)的emptyDir,其用量计入容器内存消耗,而非本地临时存储消耗。

使用类似限制内存、CPU用量的方式,限制本地临时存储用量:

YAML
1
2
spec.containers[].resources.limits.ephemeral-storage
spec.containers[].resources.requests.ephemeral-storage

单位可以是E, P, T, G, M, K,或者Ei, Pi, Ti, Gi, Mi, Ki(1024)。

下面这个例子,Pod具有两个容器,每个容器最多使用4GiB的本地临时存储:

YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
  - name: wp
    image: wordpress
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
对Pod用量的监控
不监控

如果禁用Kubelet对本地临时存储的监控,则Pod超过limit限制后不会被驱逐。但是,如果磁盘整体上容量太低,节点会被打上污点,所有不能容忍此污点的Pod都会被驱逐。

周期性扫描

Kubelet可以执行周期性的扫描,检查emptyDir卷、容器日志目录、可写容器层,然后计算Pod/容器使用了多少磁盘。

这个模式下有个问题需要注意,Kubelet不会跟踪已删除文件的描述符。也就是说,如果你创建一个文件,打开文件,写入1GB,然后删除文件,这种情况下inode仍然存在(直到你关闭文件),空间仍然被占用,但是Kubelet却没有算这1GB.

Project Quotas

此特性在1.15+处于Alpha状态。

Project quotas是Linux操作系统级别的特性,用于在目录级别限制磁盘用量。只有本地临时存储(例如emptyDir)的后备(Backing)文件系统支持Project quotas,才可以使用该特性。XFS、ext4都支持Project quotas。

K8S将占用从1048576开始的Project ID,占用中的ID注册在/etc/projects、/etc/projid文件中。如果系统中其它进程占用Project ID,则也必须在这两个文件中注册,这样K8S才会改用其它ID。

Quotas比周期性扫描快,而且更加精准。当一个目录被分配到一个Project中后,该目录中创建的任何文件,都是在Project中创建的。为了统计用量,内核只需要跟踪Project中创建了多少block就可以了。

如果文件被创建、然后删除,但是它的文件描述符仍然处于打开状态,这种情况下,它仍然消耗空间,不会出现周期性扫描的那种漏统计的问题。

要启用Project Quotas,你需要:

  1. 开启Kubelet特性开关: LocalStorageCapacityIsolationFSQuotaMonitoring
  2. 确保文件系统支持Project quotas:
    1. XFS文件系统默认支持,不需要操作
    2. ext4文件系统,你需要在未挂载之前,启用:
      Shell
      1
      sudo tune2fs -O project -Q prjquota /dev/vda 
  3. 确保文件系统挂载时,启用了Project quotas。使用挂载选项 prjquota
inode耗尽问题

有的时候,我们会发现磁盘写入时会报磁盘满,但是 df查看容量并没有100%使用,此时可能只是因为inode耗尽造成的。

当前k8s并不支持对Pod的临时存储设置inode的limits/requests。

但是,如果node进入了inode紧缺的状态,kubelet会将node设置为 under pressure,不再接收新的Pod请求。

从容器引擎限制
Docker

Docker提供了配置项 --storage-opt,可以限制容器占用磁盘空间的大小,此大小影响镜像和容器文件系统,默认10G。

你也可以在/etc/docker/daemon.json中修改此配置项:

/etc/docker/daemon.json
JSON
1
2
3
4
5
6
7
8
9
{
    "storage-driver": "devicemapper",
    "storage-opts": [
        // devicemapper
        "dm.basesize=20G",
        // overlay2
        "overlay2.size=20G",
    ]
}

但是这种配置无法影响那些挂载的卷,例如emptyDir。

从系统层限制

你可以使用Linux系统提供的任何能够限制磁盘用量的机制,为了和K8S对接,需要开发Flexvolume或CSI驱动。

磁盘配额

前文已经介绍过,K8S目前支持基于Project quotas来统计Pod的磁盘用量。这里简单总结一下Linux磁盘配额机制。

配额目标

Linux系统支持以下几种角度的配额:

  1. 在文件系统级别,限制群组能够使用的最大磁盘额度
  2. 在文件系统级别,限制单个用户能够使用的最大磁盘额度
  3. 限制某个目录(directory, project)能够占用的最大磁盘额度

前面2种配额,现代Linux都支持,不需要前提条件。你甚至可以在一个虚拟的文件系统上进行配额:

Shell
1
2
3
4
5
6
7
8
# 写一个空白文件
dd if=/dev/zero of=/path/to/the/file bs=4096 count=4096
# 格式化
...
# 挂载为虚拟文件系统
mount -o loop,rw,usrquota,grpquota /path/to/the/file /path/of/mount/point
 
# 进行配额设置...

第3种需要较新的文件系统,例如XFS、ext4fs。

配额角度

配额可以针对Block用量进行,也可以针对inode用量进行。

配额可以具有软限制、硬限制。超过软限制后,仍然可以正常使用,但是登陆后会收到警告,在grace time倒计时完毕之前,用量低于软限制后,一切恢复正常。如果grace time到期仍然没做清理,则无法创建新文件。

统计用量

启用配额,内核自然需要统计用量。管理员要查询用量,可以使用 xfs_quota这样的命令,比du这种遍历文件计算的方式要快得多。

启用配额

在保证底层文件系统支持之后,你需要修改挂载选项来启用配额:

  1. uquota/usrquota/quota:针对用户设置配额
  2. gquota/grpquota:针对群组设置配额
  3. pquota/prjquota:针对目录设置配额
LVM

使用LVM你可以任意创建具有尺寸限制的逻辑卷,把这些逻辑卷挂载给Pod即可:

YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
volumes:
- flexVolume:
    # 编写的flexVolume驱动放到
    # /usr/libexec/kubernetes/kubelet-plugins/volume/exec/kubernetes.io~lvm/lvm
    driver: kubernetes.io/lvm
    fsType: ext4
    options:
      size: 30Gi
      volumegroup: docker
  name: mnt
volumeMounts:
  - mountPath: /mnt
    name: mnt 

这需要修改编排方式,不使用emptyDir这种本地临时存储,还需要处理好逻辑卷清理工作。

Flexvolume驱动的示例可以参考:/flexvolume-study-note#lvm。

DeviceMapper

使用Device Mapper也可以创建具有尺寸限制的卷,比起LVM的优势是thin-provisioning,不必预先分配空间。

DM是从2.6引入内核的通用设备映射机制,LVM就是基于DM的。DM为块设备驱动提供了模块化的内核架构。

基本概念
术语 说明
Mapped Device

由内核映射出的、逻辑的设备,它和Target Device的关系,由Mapping Table维护

Mapped Device可以:

  1. 映射到一个Target Device
  2. 映射到多个Target Device
  3. 映射到另外一个Mapped Device
Mapping Table 包含字段:Mapped Device的逻辑起始地址、范围、关联Target Device所在物理设备的地址偏移量(以扇区,512字节为单位)、Target类型,等等
Target Device Mapped Device所映射的物理空间段
Target Driver

在内核中,DM通过模块化的Target Driver插件,实现对IO请求的过滤、重定向等操作。插件的实现包括:软Raid、加密、镜像、快照、Thin Provisioning 

命令行

使用dmsetup命令,你可以创建、删除虚拟卷,具体参考文章:Linux命令知识集锦。

下面的例子,以文件为后备,创建虚拟块设备(loopback),并以此块设备为基础,创建thin-provisioning池。然后,在池中分配限制大小的卷:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 创建空白文件
# 声明大小10G,实际占用空间(seek)4K
dd if=/dev/zero of=/tmp/data.img bs=1K count=1 seek=10M
# 声明大小100M
dd if=/dev/zero of=/tmp/meta.img bs=1K count=1 seek=100K
 
# 映射为虚拟(loopback)块设备,实际场景中,你可以考虑将整块磁盘交由DM管理
losetup /dev/loop10 /tmp/meta.img
losetup /dev/loop11 /tmp/data.img
 
# 基于上述块设备,创建一个mapped device(thin-provisioning池)
dmsetup create thinpool0 \
#          数据设备起始扇区
#            数据设备结束扇区 * 512 = 10G      
#                               元数据设备
#                                           数据设备    
  --table "0 20971522 thin-pool /dev/loop10 /dev/loop11 \
# 最小可以分配的扇区数
#    最少可用的扇区阈值
#          有1个附加参数
#            附加参数,跳过用0填充的块
128 65536 1 skip_block_zeroing"
 
# 设备 /dev/mapper/thinpool0 现在可用
 
# 在thinpool0上创建一个thin-provisioning卷
#                                        标识符
dmsetup message thinpool0 0 "create_thin 0"
dmsetup create thinvol0 --table "0 2097152 thin /dev/mapper/thinpool0 0"
 
# 设备 /dev/mapper/thinvol0 现在可用
 
# 格式化
mkfs.ext4 /dev/mapper/thinvol0
 
# 挂载
mkdir /tmp/thinvol
mount /dev/mapper/thinvol0 /tmp/thinvol
参考
  1. https://blog.spider.im/post/control-disk-size-in-docker
  2. https://ieevee.com/tech/2019/05/23/ephemeral-storage.html
  3. https://wizardforcel.gitbooks.io/vbird-linux-basic-4e/content/125.html
  4. https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#local-ephemeral-storage
  5. https://coolshell.cn/articles/17200.html
← Flexvolume学习笔记
Kubefed学习笔记 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 利用kind搭建本地K8S集群
  • CRIU和Pod在线迁移
  • Galaxy学习笔记
  • Flannel学习笔记
  • Argo学习笔记

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 38 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2