Kuryr-Kubernetes通过使用OpenStack Neutron和Octavia为Kubernetes pod提供网络。

如何用Kubernetes管理OpenStack服务_java


Kuryr-Kubernetes是一个用Python编写的OpenStack项目,它作为一个容器网络接口(CNI)插件,通过使用OpenStack Neutron和Octavia为Kubernetes pods提供网络。这个项目经过了实验阶段,成为OpenStack Queens版本(第17版)中完全支持的OpenStack生态系统项目。


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


还可以在裸机节点上运行Kuryr-Kubernetes作为普通的OpenStack服务。这样,可以在Kubernetes pod和OpenStack VM之间提供互连,即使这些集群是分开的,只需将Neutron-agent和Kuryr-Kubernetes放在Kubernetes节点上即可。


Kuryr-Kubernetes由三部分组成:


  • kuryr-controller观察Kubernetes资源,决定如何将它们转换为OpenStack资源,并创建这些资源。有关OpenStack资源的信息将保存到相应Kubernetes资源的注释中。

  • kuryr-cni是由CNI运行的可执行文件,它将调用传递给kuryr-daemon。

  • kuryr-daemon应该在每个Kubernetes节点上运行。它监视在主机上创建的pod,当CNI请求进入时,根据pod注释中包含的Neutron端口连接pod。


一般来说,CNI插件的控制部分(如Calico或Nuage)在Kubernetes集群上作为一个pod运行,它提供网络,因此,Kuryr团队自然决定遵循该模型。但是将OpenStack服务转换为Kubernetes应用程序并不是一项简单的任务。


Kuryr-Kubernetes要求

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


kuryr控制器


  • 应该只有一个kuryr-controller实例(尽管在OpenStack Rocky中实现的A/P高可用性功能可能会更高)。使用Kubernetes的部署原语很容易实现。

  • Kubernetes ServiceAccounts可以通过一组精细的权限提供对Kubernetes API的访问。

  • 不同的SDN以不同方式提供对OpenStack API的访问。还应提供API SSL证书,例如通过在pod中安装Secret。

  • 为了避免鸡与蛋的问题,kuryr-controller应该与hostNetworking一起运行以绕过使用Kuryr来获取IP。

  • 提供kuryr.conf文件,最好将其安装为ConfigMap。


最后,我们得到一个类似于此的部署清单:


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配置,因此必须在pod上安装Kubernetes节点的/ opt / cni / bin和/etc/cni/net.d目录。

  • 它还需要访问Kubernetes节点的网络,因此/ 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 venvs不是为便携而设计的。尽管virtualenv命令行工具中有一个--relocatable选项,但它并不总是有效。我们放弃了这种方法。


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


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


经验教训

最后,我们有一个易于部署和管理的工作系统,因为它在Kubernetes上运行。我们已经证明Kuryr-Kubernetes只是一个应用程序。虽然花了很多时间和精力,但结果是值得的。 “Kubernetized”应用程序更易于管理和分发


原文链接:

https://opensource.com/article/18/10/how-kubernetize-openstack-service