为什么 linux sys fs 修改在普通 docker 中有效,但在 kubernetes 下无效?

为什么 linux sys fs 修改在普通 docker 中有效,但在 kubernetes 下无效?

容器内运行的命令是:

echo never | tee /sys/kernel/mm/transparent_hugepage/enabled

两个容器都以特权运行。但在 kubernetes docker 容器中,命令失败并显示错误: tee: /sys/kernel/mm/transparent_hugepage/enabled: Read-only file system

并且只需简单的docker run -it --privileged alpine /bin/sh命令就可以正常工作。

我已docker inspect在 k8s 和非 k8s 容器上使用它来验证特权状态,但没有看到任何其他可能导致此问题的内容 - 我diff在两个输出之间运行,然后使用docker run修改来尝试在普通 docker 中重现该问题,但失败了(它仍然有效)。知道为什么 kubernetes docker 容器失败而普通 docker 容器成功吗?

这可以通过此处的 pod 定义重现:

apiVersion: v1
kind: Pod
metadata:
  name: sys-fs-edit
spec:
  containers:
  - image: alpine
    command:
    - /bin/sh
    args:
      - -c
      - echo never | tee /sys/kernel/mm/transparent_hugepage/enabled && sysctl -w net.core.somaxconn=8192 vm.overcommit_memory=1 && sleep 9999999d
    imagePullPolicy: Always
    name: sysctl-buddy
    securityContext:
      privileged: true

解决方法

虽然我仍然不知道造成差异的原因,但可以通过将 /sys 重新挂载为读写来缓解该问题。

apiVersion: v1
kind: Pod
metadata:
  name: sys-fs-edit
spec:
  containers:
  - image: alpine
    command:
    - /bin/sh
    args:
      - -c
      - echo never | tee /sys/kernel/mm/transparent_hugepage/enabled && sysctl -w net.core.somaxconn=8192 vm.overcommit_memory=1 && sleep 9999999d
    imagePullPolicy: Always
    name: sysctl-buddy
    securityContext:
      privileged: true
    volumeMounts:
    - mountPath: /sys
      name: sys
      readOnly: false
  volumes:
  - hostPath:
      path: /sys
    name: sys

答案1

Kubernetes它的工作原理略有不同。privileged: truesecurityContexta中设置 acontainer不足以修改任何sysctl此类容器。

看一眼官方 kubernetes 文档中有这样一段描述在 Kubernetes 集群中使用 sysctls.正如你所读到的这里

Sysctl 分为 安全的不安全 sysctls。除了正确的命名空间之外, 安全的 sysctl 必须正确 孤立 同一节点上的 Pod 之间。这意味着设置 安全的 一个 pod 的 sysctl

  • 不得对节点上的任何其他 pod 产生任何影响
  • 绝不能损害节点的健康
  • 不得允许获取超出 pod 资源限制的 CPU 或内存资源。

到目前为止,大多数 命名空间 不一定会考虑 sysctl 安全的. 支持以下 sysctl 安全的 放:

  • kernel.shm_rmid_forced
  • net.ipv4.ip_local_port_range
  • net.ipv4.tcp_syncookies
  • net.ipv4.ping_group_range (自 Kubernetes 1.18 起)。

简而言之,有安全和不安全的 sysctl。其中大多数被认为是不安全的,甚至许多命名空间的 sysctl 也是如此。集群管理员需要逐个节点地启用不安全的 sysctl:

全部 安全的 默认情况下,sysctls 是启用的。

全部 不安全 默认情况下,sysctl 处于禁用状态,必须由集群管理员根据每个节点手动允许。禁用了不安全 sysctl 的 Pod 将被调度,但无法启动。

考虑到上述警告,集群管理员可以允许某些 不安全 sysctls 用于非常特殊的情况,例如高性能或实时应用程序调整。 不安全 sysctls 是使用 kubelet 的标志在每个节点上启用的;例如:

kubelet --allowed-unsafe-sysctls \  
'kernel.msg*,net.core.somaxconn' ...

因此,你不能随意设置任何 sysctl,即使是privileged在你的服务器上运行的容器中Kubernetes簇。

答案2

您尝试设置的 sysctl 适用于整个主机,而不是单个容器。无法在非特权容器内设置它,这就是为什么您无法在 Kubernetes 中执行此操作,但可以在特权 Docker 容器中执行此操作。

如果您需要此设置来运行特定容器,则应在集群中所有节点的主机上设置它,而不是在容器或 pod 定义中设置它。

相关内容