Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • XML
      • Ruby
      • Perl
      • Lua
      • Rust
      • Network
      • IoT
      • GIS
      • 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
      • XML
      • Ruby
      • Perl
      • Lua
      • Rust
      • Network
      • IoT
      • GIS
      • 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

  • Draft学习笔记
  • Kubernetes的Service Catalog机制
  • 通过ExternalDNS集成外部DNS服务
  • 为裸金属K8S集群提供外部负载均衡器
  • 基于Rook的Kubernetes存储方案

Recent Posts

  • IPVS模式下ClusterIP泄露宿主机端口的问题
  • 念爷爷
  • 杨梅坑
  • 34759
  • 2020年10月拈花湾
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
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

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

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • 彩彩 2020年6月黄崖关

  • 总部远眺 2020年5月深圳

  • 绚丽之花 寻味顺德

  • tuanbolake 团泊湖野餐

  • Istio中的透明代理问题
    为何需要透明代理 Istio的Sidecar作为一个网络代理,它拦截入站、出站的网络流量。拦截入站流量后,会使 ...
  • 重温iptables
    工作流图 下面这张图描述了一个L3的IP封包如何通过iptables:  对于此图的说明: Ipt ...
  • 从镜像中抽取文件
    动机 在某个应用场景中,我们需要在每个K8S节点上运行一个Agent,此Agent能够执行运维人员动态配置的P ...
  • 服务网格的现状和未来
    引言 服务网格(Service Mesh)是一种微服务治理基础设施,用于控制、监测微服务之间的东西向流量。它通 ...
  • 如何在Pod中执行宿主机上的命令
    基础知识回顾 要回答标题中的疑问,我们首先要清楚,Pod是什么? Pod的翻译叫容器组,顾名思义,是一组容器 ...
  • 通过ExternalDNS集成外部DNS服务
    简介 ExternalDNS项目的目的是,将Kubernetes的Service/Ingress暴露的服务(的 ...
  • Kubefed学习笔记
    简介 联邦简介 集群联邦(Federation)的目的是实现单一集群统一管理多个Kubernetes集群的机 ...
  • 限制Pod磁盘空间用量
    Pod如何使用磁盘 容器在运行期间会产生临时文件、日志。如果没有任何配额机制,则某些容器可能很快将磁盘写满,影 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 63 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 4 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 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学习笔记 63 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • 基于Calico的CNI 27 people like this
  • Ceph学习笔记 25 people like this
  • Three.js学习笔记 22 people like this
Tag Cloud
ActiveMQ AngularJS Apache AspectJ CDT Ceph Chrome Command Cordova Coroutine CXF Cygwin Django Docker Eclipse ExtJS F7 FAQ GNU Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS jQuery JSON JVM K8S LB libvirt Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT Photoshop PHP Porting RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • bytebuddy简单入门 – FIXBBS on Byte Buddy学习笔记
  • 4.深入Istio源码:Pilot的Discovery Server如何执行xDS异步分发-站长之家 on Istio Pilot与Envoy的交互机制解读
  • 4.深入Istio源码:Pilot的Discovery Server如何执行xDS异步分发? - luozhiyun`s Blog on Istio Pilot与Envoy的交互机制解读
  • Shan Shuog on Socket.io学习笔记
  • Alex on Socket.io学习笔记
  • Shan Shuog on Socket.io学习笔记
  • Alex on 内核缺陷触发的NodePort服务63秒延迟问题
  • 林哲緯 on 内核缺陷触发的NodePort服务63秒延迟问题
  • smileyihui on Bazel学习笔记
  • Android分享:代码混淆那些事 – FIXBBS on ProGuard学习笔记
  • core on Bazel学习笔记
  • atuter on Bazel学习笔记
  • Alex on Istio中的透明代理问题
  • Yann on Istio中的透明代理问题
  • haige on H.264学习笔记
  • dandelion on Bazel学习笔记
  • 别放不下 on Gradle学习笔记
  • yanick on 内核缺陷触发的NodePort服务63秒延迟问题
  • 许铭毅 on 基于Kurento搭建WebRTC服务器
  • 许铭毅 on 基于Kurento搭建WebRTC服务器
  • Alex on 基于C/C++的WebSocket库
  • chengjiaxi on 基于C/C++的WebSocket库
  • Alex on 2018年10月淮安区
©2005-2021 Gmem.cc | Powered by WordPress