Kubernetes网络插件CNI调研整理

概要

  • 项目背景(XX银行客户):私有云上要在K8S上跑像mysql这类状态的数据库服务,对性能和延时都比较敏感,并不像web偏应用的无状态延时性能差点可接受。而基于overlay方式等网络性能和延时比较差,网络架构又比较复杂。并且银行对于IP网络管理需要简单可控。SR-IOV是基于硬件实现虚拟网卡,性能损失少,接近宿主机,此外有支持QOS,vlan等特性也是客户需要的。即要根据用户定制基于SR-IOV网络插件。

  • 问题: 目前kubernetes(1.8)(以后版本可能支持大)中,POD并没有网络相关的配置,kubelete 调用 CNI plugin 默认只会以CNI_ARGS传入pod_name基本信息。如果要固定分配IP地址,以及配置QOS,vlan等网络特性,没法通过CNI_ARGS方式传入,不能像volume一样在pod SPEC中配置options可选的网络参数来传入cni plugin。

  • 一种可行解决方案:声明一个POD前,先根据pod_name来在外部configMap或其他地方存放网络配置信息,定制的CNI,IPAM的网络插件根据pod_name来从外部获取配置信息。

CNI工作原理

Kubernetes指南 cni

CNI:容器网络接口

  • 网络插件是独立的可执行文件,被上层的容器管理平台调用。网络插件只有两件事情要做:把容器加入到网络以及把容器从网络中删除。
  • 调用插件的数据通过两种方式传递:环境变量和标准输入。
  • kubernetes 使用了 CNI 网络插件之后 工作流程:
    • kubernetes 先创建 pause 容器生成对应的 network namespace
    • 调用网络 driver(因为配置的是 CNI,所以会调用 CNI 相关代码
    • CNI driver 根据配置调用具体的 cni 插件
    • cni 插件给 pause 容器配置正确的网络,pod 中其他的容器都是用 pause 的网络.

Kubernetes的网络接口CNI及灵雀云的实践

  • 运维人员视角,在传统运维工作强调对IP要有很强的管控(银行等),POD需要固定IP:
    • 于运维来说,网络方面是很重要的资源,要对IP进行强管控,服务来回飘,会让他的安全感下降很多。
    • 运维服务有很多基于IP的东西,有流量和突发的监控,如果你服务的IP一直变化,通过这个IP它很难用到这个服务,相当于IP的监控就没有意义,因为根本不知道IP流量上去了是哪个服务的,很难对应到这个事。
    • 还有是对于IP安全策略没有办法做。
    • kubelet 与 CNI plugin调用逻辑图:
      cni
  • hackers-guide-kubernetes-networking
    • Kubernetes unfortunately still supports only one CNI interface per POD with one cluster-wide configuration. This is very limiting since we may want to configure multiple network interfaces per POD, potentially using different overlay solutions with different policies (subnet, security, QoS).
    • Kubelet will pass the POD name and namespace as part of the CNI_ARGS variable (for example “K8S_POD_NAMESPACE=default;K8S_POD_NAME=mytests-1227152546-vq7kw;” ). We can use this to customize the network configuration per POD or POD namespace (e.g. put every namespace in a different subnet).
    • Future Kubernetes versions will treat networks as equal citizens and include network configuration as part of the POD or namespace spec just like memory, CPUs and volumes. For the time being, we can use annotations to store configuration or record POD networking data/state.
    • multus-cni
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  //kubernetes/pkg/kubelet/network/cni/cni.go
func (plugin *cniNetworkPlugin) buildCNIRuntimeConf(podName string, podNs string, podSandboxID kubecontainer.ContainerID, podNetnsPath string) (*libcni.RuntimeConf, error) {
rt := &libcni.RuntimeConf{
ContainerID: podSandboxID.ID,
NetNS: podNetnsPath,
IfName: network.DefaultInterfaceName,
Args: [][2]string{
{"IgnoreUnknown", "1"},
{"K8S_POD_NAMESPACE", podNs},
{"K8S_POD_NAME", podName},
{"K8S_POD_INFRA_CONTAINER_ID", podSandboxID.ID},
},
}

//libcni
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf)
invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
//将RuntimeConf.Args以环境变量方式传入。
stdoutBytes, err := e.RawExec.ExecPlugin(pluginPath, netconf, args.AsEnv())


type CNI interface {
AddNetworkList(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
DelNetworkList(net *NetworkConfigList, rt *RuntimeConf) error

AddNetwork(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
}

type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
// A dictionary of capability-specific data passed by the runtime
// to plugins as top-level keys in the 'runtimeConfig' dictionary
// of the plugin's stdin data. libcni will ensure that only keys
// in this map which match the capabilities of the plugin are passed
// to the plugin
CapabilityArgs map[string]interface{}
}

CNI开发参考资源

kubelet原理

  • Kubernetes指南 kubelet
  • kubernetes 简介:kubelet 和 pod
    • 它的核心工作是监听 apiserver,配置目录(默认/etc/kubernetes/manifests/)等清单,一旦发现当前节点的 pod 配置发生变化,就根据最新的配置执行响应的动作,保证运行的 pod 状态和期望的一致。
      • 如果发现本地的Pod被修改,则Kubelet会做出相应的修改,比如删除Pod中某个容器时,则通过Docker Client删除该容器。 如果发现删除本节点的Pod,则删除相应的Pod,并通过Docker Client删除Pod中的容器。
    • 定时汇报当前节点的状态给 apiserver,以供调度的时候使用,通过cAdvisor监控节点和容器的资源。
    • 用“kubernetes/pause”镜像为每个Pod创建一个容器。Pause容器用于接管Pod中所有其他容器的网络。每创建一个新的Pod,Kubelet都会先创建一个Pause容器,然后创建其他容器。

kubelet源码分析

kubelet 源码分析:启动流程 (v1.5.0版本)

  • 解析参数配置信息等初始化准备后, 创建kubeDeps这个对象:
    • 其实它内部保存了 kubelet 各个重要组件的对象,之所以要把它作为参数传递,是为了实现 dependency injection。简单地说,就是把 kubelet 依赖的组件对象作为参数传进来,这样可以控制 kubelet 的行为。比如在测试的时候,只要构建 fake 的组件实现,就能很轻松进行测试。KubeDeps 包含的组件很多,下面列出一些:
    • CAdvisorInterface:提供 cAdvisor 接口功能的组件,负责收集容器的监控信息
    • DockerClient:docker 客户端,用来和 docker 交互
    • KubeClient:apiserver 客户端,用来和 api server 通信
    • Mounter:执行 mount 相关操作
    • NetworkPlugins:网络插件,执行网络设置工作
    • VolumePlugins:volume 插件,执行 volume 设置工作
  • RunKubelet 函数:
    • 通过 builder 创建出来 Kubelet对象(pkg/kubelet/kubelet.go#NewMainKubelet)
    • 根据运行模式,运行 Kubelet对象,各种组件以 goroutine运行启动
      • 异步事件驱动:syncLoop 是 kubelet 的主循环方法,它从不同的管道(文件、URL 和 apiserver)监听变化,并把它们汇聚起来。当有新的变化发生时,它会调用对应的处理函数,保证 pod 处于期望的状态。
  • kubelet对象 中包含的重要对象:
    • podConfig:这个对象里面会从文件、网络和 apiserver 三个来源中汇聚节点要运行的 pod 信息,并通过管道发送出来,读取这个管道就能获取实时的 pod 最新配置
    • runtime:容器运行时,对容器引擎(docker 或者 rkt)的一层封装,负责调用容器引擎接口管理容器的状态,比如启动、暂停、杀死容器等
    • probeManager:如果 pod 配置了状态监测,那么 probeManager 会定时检查 pod 是否正常工作,并通过 statusManager 向 apiserver 更新 pod 的状态
    • volumeManager:负责容器需要的 volume 管理。检测某个 volume 是否已经 mount、获取 pod 使用的 volume 等
    • podWorkers:具体的执行者,每次有 pod 需要更新的时候都会发送给它
    • podManager:缓存了 pod 的信息,是所有需要该信息都会去访问的地方
    • nodeLister:能够读取 apiserver 中节点的信息

kubelet源码分析-启动运行与信息处理
kubelet 源码分析:pod 新建流程(v1.5.0版本):

  • pod的创建顺序留意点:
    • 创建 pod 的数据目录,存放 volume 和 plugin 信息
    • 如果定义了 PV,等待所有的 volume mount 完成(volumeManager 会在后台做这些事情)
    • 如果有 image secrets,去 apiserver 获取对应的 secrets 数据
    • 调用 container runtime 的 SyncPod 方法,去实现真正的容器创建逻辑
      • 通过 docker 创建出来一个运行的 pause 容器(Pause容器用于接管Pod中所 有其他容器的网络)。
      • 网络配置:如果 pod 是主机模式,容器也是;其他情况下,容器会使用 None 网络模式,让 kubelet 的网络插件自己进行网络配置

文章出处: https://yucs.github.io/2017/12/06/2017-12-6-CNI/

markdown文件放在 github.com/yucs/yucs-awesome-resource 持续更新,欢迎star ,watch

如有出入请请教,文章持续更新中…

如果觉得我写的帮助到您,请随意打赏,是我最大的肯定和动力
分享