这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

计算、存储和网络扩展

本节介绍不属于 Kubernetes 本身组成部分的一些集群扩展。 你可以使用这些扩展来增强集群中的节点,或者提供将 Pod 关联在一起的网络结构。

  • CSIFlexVolume 存储插件

    容器存储接口 (CSI) 插件提供了一种扩展 Kubernetes 的方式使其支持新类别的卷。 这些卷可以由持久的外部存储提供支持,可以提供临时存储,还可以使用文件系统范型为信息提供只读接口。

    Kubernetes 还包括对 FlexVolume 插件的扩展支持,该插件自 Kubernetes v1.23 起被弃用(被 CSI 替代)。

    FlexVolume 插件允许用户挂载 Kubernetes 本身不支持的卷类型。 当你运行依赖于 FlexVolume 存储的 Pod 时,kubelet 会调用一个二进制插件来挂载该卷。 归档的 FlexVolume 设计提案对此方法有更多详细说明。

    Kubernetes 存储供应商的卷插件 FAQ 包含了有关存储插件的通用信息。

  • 设备插件

    设备插件允许一个节点发现新的 Node 设施(除了 cpumemory 等内置的节点资源之外), 并向请求资源的 Pod 提供了这些自定义的节点本地设施。

  • 网络插件

    网络插件可以让 Kubernetes 使用不同的网络拓扑和技术。 你的 Kubernetes 集群需要一个 网络插件 才能拥有一个正常工作的 Pod 网络, 才能支持 Kubernetes 网络模型的其他方面。

    Kubernetes 1.26 兼容 CNI 网络插件。

1 - 网络插件

Kubernetes 1.26 支持用于集群联网的容器网络接口 (CNI) 插件。 你必须使用和你的集群相兼容并且满足你的需求的 CNI 插件。 在更广泛的 Kubernetes 生态系统中你可以使用不同的插件(开源和闭源)。

要实现 Kubernetes 网络模型,你需要一个 CNI 插件。

你必须使用与 v0.4.0 或更高版本的 CNI 规范相符合的 CNI 插件。 Kubernetes 推荐使用一个兼容 v1.0.0 CNI 规范的插件(插件可以兼容多个规范版本)。

安装

在网络语境中,容器运行时(Container Runtime)是在节点上的守护进程, 被配置用来为 kubelet 提供 CRI 服务。具体而言,容器运行时必须配置为加载所需的 CNI 插件,从而实现 Kubernetes 网络模型。

要了解容器运行时如何管理 CNI 插件的具体信息,可参见对应容器运行时的文档,例如:

要了解如何安装和管理 CNI 插件的具体信息,可参阅对应的插件或 网络驱动(Networking Provider) 的文档。

网络插件要求

对于插件开发人员以及时常会构建并部署 Kubernetes 的用户而言, 插件可能也需要特定的配置来支持 kube-proxy。 iptables 代理依赖于 iptables,插件可能需要确保 iptables 能够监控容器的网络通信。 例如,如果插件将容器连接到 Linux 网桥,插件必须将 net/bridge/bridge-nf-call-iptables sysctl 参数设置为 1,以确保 iptables 代理正常工作。 如果插件不使用 Linux 网桥,而是使用类似于 Open vSwitch 或者其它一些机制, 它应该确保为代理对容器通信执行正确的路由。

默认情况下,如果未指定 kubelet 网络插件,则使用 noop 插件, 该插件设置 net/bridge/bridge-nf-call-iptables=1,以确保简单的配置 (如带网桥的 Docker)与 iptables 代理正常工作。

本地回路 CNI

除了安装到节点上用于实现 Kubernetes 网络模型的 CNI 插件外,Kubernetes 还需要容器运行时提供一个本地回路接口 lo,用于各个沙箱(Pod 沙箱、虚机沙箱……)。 实现本地回路接口的工作可以通过复用 CNI 本地回路插件来实现, 也可以通过开发自己的代码来实现 (参阅 CRI-O 中的示例)。

支持 hostPort

CNI 网络插件支持 hostPort。你可以使用官方 portmap 插件,它由 CNI 插件团队提供,或者使用你自己的带有 portMapping 功能的插件。

如果你想要启动 hostPort 支持,则必须在 cni-conf-dir 指定 portMappings capability。 例如:

{
  "name": "k8s-pod-network",
  "cniVersion": "0.4.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true},
      "externalSetMarkChain": "KUBE-MARK-MASQ"
    }
  ]
}

支持流量整形

实验功能

CNI 网络插件还支持 Pod 入站和出站流量整形。 你可以使用 CNI 插件团队提供的 bandwidth 插件,也可以使用你自己的具有带宽控制功能的插件。

如果你想要启用流量整形支持,你必须将 bandwidth 插件添加到 CNI 配置文件 (默认是 /etc/cni/net.d)并保证该可执行文件包含在你的 CNI 的 bin 文件夹内 (默认为 /opt/cni/bin)。

{
  "name": "k8s-pod-network",
  "cniVersion": "0.4.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

现在,你可以将 kubernetes.io/ingress-bandwidthkubernetes.io/egress-bandwidth 注解添加到 Pod 中。例如:

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/ingress-bandwidth: 1M
    kubernetes.io/egress-bandwidth: 1M
...

接下来

2 - 设备插件

设备插件可以让你配置集群以支持需要特定于供应商设置的设备或资源,例如 GPU、NIC、FPGA 或非易失性主存储器。
特性状态: Kubernetes v1.26 [stable]

Kubernetes 提供了一个 设备插件框架, 你可以用它来将系统硬件资源发布到 Kubelet

供应商可以实现设备插件,由你手动部署或作为 DaemonSet 来部署,而不必定制 Kubernetes 本身的代码。目标设备包括 GPU、高性能 NIC、FPGA、 InfiniBand 适配器以及其他类似的、可能需要特定于供应商的初始化和设置的计算资源。

注册设备插件

kubelet 提供了一个 Registration 的 gRPC 服务:

service Registration {
	rpc Register(RegisterRequest) returns (Empty) {}
}

设备插件可以通过此 gRPC 服务在 kubelet 进行注册。在注册期间,设备插件需要发送下面几样内容:

  • 设备插件的 Unix 套接字。
  • 设备插件的 API 版本。
  • ResourceName 是需要公布的。这里 ResourceName 需要遵循扩展资源命名方案, 类似于 vendor-domain/resourcetype。(比如 NVIDIA GPU 就被公布为 nvidia.com/gpu。)

成功注册后,设备插件就向 kubelet 发送它所管理的设备列表,然后 kubelet 负责将这些资源发布到 API 服务器,作为 kubelet 节点状态更新的一部分。

比如,设备插件在 kubelet 中注册了 hardware-vendor.example/foo 并报告了节点上的两个运行状况良好的设备后,节点状态将更新以通告该节点已安装 2 个 "Foo" 设备并且是可用的。

然后,用户可以请求设备作为 Pod 规范的一部分, 参见 Container。 请求扩展资源类似于管理请求和限制的方式, 其他资源,有以下区别:

  • 扩展资源仅可作为整数资源使用,并且不能被过量使用
  • 设备不能在容器之间共享

示例

假设 Kubernetes 集群正在运行一个设备插件,该插件在一些节点上公布的资源为 hardware-vendor.example/foo。 下面就是一个 Pod 示例,请求此资源以运行一个工作负载的示例:

---
apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
spec:
  containers:
    - name: demo-container-1
      image: registry.k8s.io/pause:2.0
      resources:
        limits:
          hardware-vendor.example/foo: 2
#
# 这个 pod 需要两个 hardware-vendor.example/foo 设备
# 而且只能够调度到满足需求的节点上
#
# 如果该节点中有 2 个以上的设备可用,其余的可供其他 Pod 使用

设备插件的实现

设备插件的常规工作流程包括以下几个步骤:

  • 初始化。在这个阶段,设备插件将执行供应商特定的初始化和设置, 以确保设备处于就绪状态。

  • 插件使用主机路径 /var/lib/kubelet/device-plugins/ 下的 Unix 套接字启动一个 gRPC 服务,该服务实现以下接口:

    service DevicePlugin {
        // GetDevicePluginOptions 返回与设备管理器沟通的选项。
        rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}
    
        // ListAndWatch 返回 Device 列表构成的数据流。
        // 当 Device 状态发生变化或者 Device 消失时,ListAndWatch
        // 会返回新的列表。
        rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}
    
        // Allocate 在容器创建期间调用,这样设备插件可以运行一些特定于设备的操作,
        // 并告诉 kubelet 如何令 Device 可在容器中访问的所需执行的具体步骤
        rpc Allocate(AllocateRequest) returns (AllocateResponse) {}
    
        // GetPreferredAllocation 从一组可用的设备中返回一些优选的设备用来分配,
        // 所返回的优选分配结果不一定会是设备管理器的最终分配方案。
        // 此接口的设计仅是为了让设备管理器能够在可能的情况下做出更有意义的决定。
        rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}
    
        // PreStartContainer 在设备插件注册阶段根据需要被调用,调用发生在容器启动之前。
        // 在将设备提供给容器使用之前,设备插件可以运行一些诸如重置设备之类的特定于
        // 具体设备的操作,
        rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}
    }
    
  • 插件通过 Unix socket 在主机路径 /var/lib/kubelet/device-plugins/kubelet.sock 处向 kubelet 注册自身。
  • 成功注册自身后,设备插件将以服务模式运行,在此期间,它将持续监控设备运行状况, 并在设备状态发生任何变化时向 kubelet 报告。它还负责响应 Allocate gRPC 请求。 在 Allocate 期间,设备插件可能还会做一些设备特定的准备;例如 GPU 清理或 QRNG 初始化。 如果操作成功,则设备插件将返回 AllocateResponse,其中包含用于访问被分配的设备容器运行时的配置。 kubelet 将此信息传递到容器运行时。

处理 kubelet 重启

设备插件应能监测到 kubelet 重启,并且向新的 kubelet 实例来重新注册自己。 新的 kubelet 实例启动时会删除 /var/lib/kubelet/device-plugins 下所有已经存在的 Unix 套接字。 设备插件需要能够监控到它的 Unix 套接字被删除,并且当发生此类事件时重新注册自己。

设备插件部署

你可以将你的设备插件作为节点操作系统的软件包来部署、作为 DaemonSet 来部署或者手动部署。

规范目录 /var/lib/kubelet/device-plugins 是需要特权访问的, 所以设备插件必须要在被授权的安全的上下文中运行。 如果你将设备插件部署为 DaemonSet,/var/lib/kubelet/device-plugins 目录必须要在插件的 PodSpec 中声明作为 卷(Volume) 被挂载到插件中。

如果你选择 DaemonSet 方法,你可以通过 Kubernetes 进行以下操作: 将设备插件的 Pod 放置在节点上,在出现故障后重新启动守护进程 Pod,来进行自动升级。

API 兼容性

之前版本控制方案要求设备插件的 API 版本与 Kubelet 的版本完全匹配。 自从此特性在 v1.12 中进阶为 Beta 后,这不再是硬性要求。 API 是版本化的,并且自此特性进阶 Beta 后一直表现稳定。 因此,kubelet 升级应该是无缝的,但在稳定之前 API 仍然可能会有变更,还不能保证升级不会中断。