如何“Kubernetes 化” OpenStack 服务

Kuryr-Kubernetes 通过使用 OpenStack Neutron 和 Octavia 为 Kubernetes Pod 提供网络连接。
354 位读者喜欢这个。
OpenStack

Opensource.com

Kuryr-Kubernetes 是一个 OpenStack 项目,用 Python 编写,充当容器网络接口 (CNI) 插件,通过使用 OpenStack NeutronOctavia 为 Kubernetes Pod 提供网络连接。该项目走出了实验阶段,在 OpenStack Queens 版本(云基础设施软件的第 17 个版本)中成为完全受支持的 OpenStack 生态系统成员。

Kuryr-Kubernetes 的主要优势之一是,您无需为 OpenStackKubernetes 中的网络管理使用多个软件定义网络 (SDN)。它还解决了在 OpenStack 云上运行 Kubernetes 集群时使用双重网络数据包封装的问题。想象一下,为 Kubernetes 网络使用 Calico,为 Kubernetes 集群的虚拟机 (VM) 网络使用 Neutron。使用 Kuryr-Kubernetes,您只需使用一个 SDN - Neutron - 为 Pod 和运行这些 Pod 的虚拟机提供连接。

您还可以在裸机节点上以普通的 OpenStack 服务方式运行 Kuryr-Kubernetes。这样,您可以通过仅在 Kubernetes 节点上放置 Neutron-agent 和 Kuryr-Kubernetes,在 Kubernetes Pod 和 OpenStack VM 之间提供互连,即使这些集群是分开的。

Kuryr-Kubernetes 由三个部分组成

  • kuryr-controller 观察 Kubernetes 资源,决定如何将它们转换为 OpenStack 资源,并创建这些资源。有关 OpenStack 资源的信息保存在相应 Kubernetes 资源的注释中。
  • kuryr-cni 是由 CNI 运行的可执行文件,它将调用传递给 kuryr-daemon
  • kuryr-daemon 应该在每个 Kubernetes 节点上运行。它监视主机上创建的 Pod,当 CNI 请求进入时,根据 Pod 注释中包含的 Neutron 端口连接 Pod。

通常,CNI 插件(如 Calico 或 Nuage)的控制部分作为 Pod 在提供网络的 Kubernetes 集群上运行,因此,自然而然地,Kuryr 团队决定遵循该模型。但是将 OpenStack 服务转换为 Kubernetes 应用程序并非易事。

Kuryr-Kubernetes 要求

Kuryr-Kubernetes 只是一个应用程序,应用程序有其要求。以下是每个组件对环境的需求以及它如何转换为 Kubernetes 的原语。

kuryr-controller

  • 应该只有一个 kuryr-controller 实例(尽管在 OpenStack Rocky 中实现 A/P 高可用性功能后,这个数字可能会更高)。这很容易使用 Kubernetes 的 Deployment 原语来实现。
  • Kubernetes ServiceAccounts 可以提供对 Kubernetes API 的访问,并具有精细的权限集。
  • 不同的 SDN 以不同的方式提供对 OpenStack API 的访问。还应提供 API SSL 证书,例如通过在 Pod 中挂载 Secret
  • 为了避免先有鸡还是先有蛋的问题,kuryr-controller 应该使用 hostNetworking 运行,以绕过使用 Kuryr 获取 IP。
  • 提供 kuryr.conf 文件,最好通过将其作为 ConfigMap 挂载。

最后,我们得到了类似于这样的 Deployment 清单

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  labels:
    name: kuryr-controller
  name: kuryr-controller
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
        name: kuryr-controller
      name: kuryr-controller
    spec:
      serviceAccountName: kuryr-controller
      automountServiceAccountToken: true
      hostNetwork: true
      containers:
      - image: kuryr/controller:latest
        name: controller
        volumeMounts:
        - name: config-volume
          mountPath: "/etc/kuryr/kuryr.conf"
          subPath: kuryr.conf
        - name: certificates-volume
          mountPath: "/etc/ssl/certs"
          readOnly: true
      volumes:
      - name: config-volume
        configMap:
          name: kuryr-config
      - name: certificates-volume
        secret:
          secretName: kuryr-certificates
      restartPolicy: Always

kuryr-daemon 和 kuryr-cni

这两个组件都应该存在于每个 Kubernetes 节点上。当 kuryr-daemon 容器在 Kubernetes 节点上启动时,它会注入 kuryr-cni 可执行文件并重新配置 CNI 以使用它。让我们将其分解为需求。

  • kuryr-daemon 应该在每个 Kubernetes 节点上运行。这意味着它可以表示为 DaemonSet
  • 它应该能够访问 Kubernetes API。这可以使用 ServiceAccounts 实现。
  • 它还需要一个 kuryr.conf 文件。同样,最好的方法是使用 ConfigMap
  • 为了在节点上执行网络操作,它必须使用 hostNetworking 并作为特权容器运行。
  • 因为它需要注入 kuryr-cni 可执行文件和 CNI 配置,所以 Kubernetes 节点的 /opt/cni/bin/etc/cni/net.d 目录必须挂载在 Pod 上。
  • 它还需要访问 Kubernetes 节点的 netns,因此 /proc 必须挂载在 Pod 上。(请注意,您不能使用 /proc 作为挂载目标,因此必须将其命名为不同的名称,并且需要配置 Kuryr 以了解这一点。)
  • 如果它与 Open vSwitch Neutron 插件一起运行,则必须挂载 /var/run/openvswitch
  • 为了识别在其节点上运行的 Pod,应该将 nodeName 传递到 Pod 中。这可以使用环境变量来完成。(Pod 名称也是如此,将在下面解释。)

这产生了一个更复杂的清单

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kuryr-cni
  namespace: kube-system
  labels:
    name: kuryr-cni
spec:
  template:
    metadata:
      labels:
        Name: kuryr-cni
    spec:
      hostNetwork: true
      serviceAccountName: kuryr-controller
      containers:
      - name: kuryr-cni
        image: kuryr/cni:latest
        command: [ "cni_ds_init" ]
        env:
        - name: KUBERNETES_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: KURYR_CNI_POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        securityContext:
          privileged: true
        volumeMounts:
        - name: bin
          mountPath: /opt/cni/bin
        - name: net-conf
          mountPath: /etc/cni/net.d
        - name: config-volume
          mountPath: /etc/kuryr/kuryr.conf
          subPath: kuryr-cni.conf
        - name: proc
          mountPath: /host_proc
        - name: openvswitch
          mountPath: /var/run/openvswitch
      volumes:
        - name: bin
          hostPath:
            path: /opt/cni/bin
        - name: net-conf
          hostPath:
            path: /etc/cni/net.d
        - name: config-volume
          configMap:
            name: kuryr-config
        - name: proc
          hostPath:
            path: /proc
        - name: openvswitch
          hostPath:
            path: /var/run/openvswitch

注入 kuryr-cni 可执行文件

这一部分花费了我们最长的时间。我们尝试了四种不同的方法,直到一切都正常工作。我们的解决方案是将一个 Python 应用程序从容器注入到容器的主机中,并注入 CNI 配置文件(但后者是微不足道的)。大多数问题都与 Python 应用程序不是二进制文件而是脚本有关。

我们首先尝试使用 PyInstaller 将我们的 kuryr-cni 脚本制成二进制文件。尽管这效果相当好,但它有严重的缺点。首先,构建过程很复杂 - 我们必须创建一个包含 PyInstaller 和 Kuryr-Kubernetes 的容器来生成二进制文件,然后使用该二进制文件构建 kuryr-daemon 容器镜像。此外,由于 PyInstaller 的怪癖,我们在 kubelet 日志中最终得到了许多误导性的回溯,即在异常中,我们可能会在日志中得到错误的回溯。决定性因素是 PyInstaller 更改了包含的 Python 模块的路径。这意味着 os.vif 库中的一些检查 失败并破坏了我们的持续集成 (CI)。

我们还尝试注入一个包含 CPython 二进制文件、kuryr-kubernetes 包及其所有要求的 Python 虚拟环境 (venv)。问题是 Python venv 不是为可移植性而设计的。即使 virtualenv 命令行工具中有一个 --relocatable 选项,它也并非总是有效。我们放弃了这种方法。

然后我们尝试了我们认为是“正确”的方法:使用一个可执行脚本注入主机,该脚本在 kuryr-daemon 容器上执行 docker exec -i。因为 kuryr-kubernetes 包安装在该容器中,所以它可以轻松执行 kuryr-cni 二进制文件。所有 CNI 环境变量都必须通过 docker exec 命令传递,这自 Docker API v1.24 以来是可能的。然后,我们只需要识别应该在其上执行的 Docker 容器。

起初,我们尝试从 kuryr-daemon 容器的入口点调用 Kubernetes API 以获取其自身的容器 ID。我们很快发现这会导致竞争条件,有时入口点在 Kubernetes API 使用其容器 ID 更新之前运行。因此,我们没有调用 Kubernetes API,而是让注入的 CNI 脚本调用主机上的 Docker API。然后很容易使用 Kubernetes 添加的标签来识别 kuryr-daemon 容器。

经验教训

最后,我们得到了一个可以正常工作的系统,它易于部署和管理,因为它在 Kubernetes 上运行。我们证明了 Kuryr-Kubernetes 只是一个应用程序。虽然这花费了大量的时间和精力,但结果是值得的。“Kubernetes 化”的应用程序更易于管理和分发。


Michał Dulko 将在 11 月 13 日至 15 日在柏林举行的 OpenStack 峰会上介绍 如何从 OpenStack 服务制作 Kubernetes 应用程序

User profile image.
Michał 是一位在 Red Hat 工作的软件工程师,自 Folsom 版本以来一直从事与 OpenStack 相关的活动。在 Newton、Ocata 和 Pike 周期中,他作为 Cinder 的核心审阅者为 OpenStack 社区服务,他专注于控制平面的可用性、可扩展性和可升级性。现在他正在 Kuryr 项目中解决类似的问题。

评论已关闭。

© . All rights reserved.