谈谈k8s1.12新特性--Mount propagation(挂载命名空间的传播)

Mount propagation

挂载传播允许将Container挂载的卷共享到同一Pod中的其他Container,甚至可以共享到同一节点上的其他Pod。
一个卷的挂载传播由Container.volumeMounts中的mountPropagation字段控制。它的值是:

  • None 此卷挂载不会接收到任何后续挂载到该卷或是挂载到该卷的子目录下的挂载。以类似的方式,在主机上不会显示Container创建的装载。这是默认模式。

此模式等同于Linux内核文档中所述的 private 传播。

  • HostToContainer 此卷挂载将会接收到任何后续挂载到该卷或是挂载到该卷的子目录下的挂载。

换句话说,如果主机在卷挂载中挂载任何内容,则Container将看到它挂载在那里。
类似地,如果任何具有 Bidirectional 挂载传播设置的Pod挂载到同一个卷中,那么具有HostToContainer挂载传播的Container将会看到它。
此模式等同于Linux内核文档中描述的rslave挂载传播。

  • Bidirectional 此卷挂载的行为与HostToContainer挂载相同。此外,Container创建的所有卷挂载都将传播回主机和所有使用相同卷的Pod的所有容器。

此模式的典型用例是具有Flexvolume或CSI驱动程序的Pod需要使用hostPath 卷模式 在主机上挂载内容。
此模式等同于Linux内核文档中描述的rshared安装传播。
PS:
Bidirectional 挂载传播可能很危险。它可能会损坏主机操作系统,因此只允许在特权容器中使用它。强烈建议您熟悉Linux内核行为。此外,容器在容器中创建的任何卷装入必须在终止时由容器销毁(卸载)。

Bidirectional一些使用场景:

  • 在不同的pod之间共享设备,其中挂载发生在pod中,但是在pod之间共享。
  • 从容器内部附加设备。例如,从容器内部附加ISCSI设备。这时候因为如果容器死掉,主机将不能获得所需的信息(除非使用双向安装传播)来正确刷新写入和分离设备。

sample

deployment:
  containers:
  - image: gcr.io/google_containers/busybox:1.24
    name: reader
    volume:
    - mount: /usr/test-pod
      store: local-vol
      propagation: bidirectional
  name: local-test-reader
  version: extensions/v1beta1
  volumes:
    local-vol: pvc:example-local-claim

配置

在挂载传播可以在某些部署(CoreOS,RedHat / Centos,Ubuntu)上正常工作之前,必须在Docker中正确配置挂载共享,如下所示。
编辑Docker的systemdservice 文件。设置MountFlags如下:

MountFlags=shared

或者,如果存在,则删除MountFlags = slave。然后重启Docker守护进程:

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

具体案例

之所以关注这块,是由于我们在实际使用k8s的过程中遇到了相关的问题。
我们容器业务日志的收集有很大一部分通过sidecar的方式,mount日志到主机某个目录。由于我们的日志量非常大,即使我们每个主机挂载了1T的硬盘,经常会写满这张盘。所以我们实现了一个log-tracer的项目,专门用于根据pod的日志挂载信息clear过期的日志。通过daemonset方式部署。
但是如果设置mount为private的话,缺乏广播通知。在实际新增pod后,有一部分的日志挂载是无法感测到,因而也无法彻底清理。

刨根问底

究其深层次分析,该特性并不是k8s或是docker实现的。本质上是mount namespace 的特性。

mount namespace通过隔离文件系统挂载点对隔离文件系统提供支持,它是历史上第一个Linux namespace,所以它的标识位比较特殊,就是CLONE_NEWNS。隔离后,不同mount namespace中的文件结构发生变化也互不影响。你可以通过/proc/[pid]/mounts查看到所有挂载在当前namespace中的文件系统,还可以通过/proc/[pid]/mountstats看到mount namespace中文件设备的统计信息,包括挂载文件的名字、文件系统类型、挂载位置等等。

进程在创建mount namespace时,会把当前的文件结构复制给新的namespace。新namespace中的所有mount操作都只影响自身的文件系统,而对外界不会产生任何影响。这样做非常严格地实现了隔离,但是某些情况可能并不适用。比如父节点namespace中的进程挂载了一张CD-ROM,这时子节点namespace拷贝的目录结构就无法自动挂载上这张CD-ROM,因为这种操作会影响到父节点的文件系统。

2006 年引入的挂载传播(mount propagation)解决了这个问题,挂载传播定义了挂载对象(mount object)之间的关系,系统用这些关系决定任何挂载对象中的挂载事件如何传播到其他挂载对象(参考自:http://www.ibm.com/developerw…)。所谓传播事件,是指由一个挂载对象的状态变化导致的其它挂载对象的挂载与解除挂载动作的事件。

  • 共享关系(share relationship)。如果两个挂载对象具有共享关系,那么一个挂载对象中的挂载事件会传播到另一个挂载对象,反之亦然。
  • 从属关系(slave relationship)。如果两个挂载对象形成从属关系,那么一个挂载对象中的挂载事件会传播到另一个挂载对象,但是反过来不行;在这种关系中,从属对象是事件的接收者。

一个挂载状态可能为如下的其中一种:

  • 共享挂载(shared)
  • 从属挂载(slave)
  • 共享/从属挂载(shared and slave)
  • 私有挂载(private)
  • 不可绑定挂载(unbindable)

在默认情况下,所有挂载都是私有的。可以用以下命令将挂载对象显式地标为共享挂载:

mount --make-shared <mount-object>

例如,如果 / 上的挂载必须是共享的,那么执行以下命令:

mount --make-shared /

从共享挂载克隆的挂载对象也是共享的挂载;它们相互传播挂载事件。

通过执行以下命令,可以显式地将一个共享挂载转换为从属挂载:

mount --make-slave <shared-mount-object>

从从属挂载克隆的挂载对象也是从属的挂载,它也从属于原来的从属挂载的主挂载对象。
通过执行以下命令,可以将挂载对象标为私有的:

mount --make-private <mount-object>

通过执行以下命令,可以将挂载对象标为不可绑定的:

mount --make-unbindable <mount-object>

最后,这些设置都可以递归地应用,这意味着它们将应用于目标挂载之下的所有挂载。

mount --make-rshared /

将 / 之下的所有挂载转换为共享挂载。

参考文献

官方文档

Docker背后的内核知识——Namespace资源隔离

    原文作者:iyacontrol
    原文地址: https://segmentfault.com/a/1190000016567617
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞