热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

彻底理解kubernetesCNI

"kubernetes各版本离线安装包"CNI接口很简单,特别一些新手一定要克服恐惧心里,和我一探究竟,本文结合原理与实践,认真读下来一定会对原

kubernetes各版本离线安装包

CNI接口很简单,特别一些新手一定要克服恐惧心里,和我一探究竟,本文结合原理与实践,认真读下来一定会对原理理解非常透彻。

环境介绍

我们安装kubernetes时先不安装CNI. 如果使用了sealyun离线包 那么修改下 kube/conf/master.sh

只留如下内容即可:

[root@helix105 shell]# cat master.sh 
kubeadm init --config ../conf/kubeadm.yaml
mkdir ~/.kube
cp /etc/kubernetes/admin.conf ~/.kube/config

kubectl taint nodes --all node-role.kubernetes.io/master-

清空CNI相关目录:

rm -rf /opt/cni/bin/*
rm -rf /etc/cni/net.d/*

启动kubernetes, 如果已经装过那么kubeadm reset一下:

cd kube/shell && sh init.sh && sh master.sh

此时你的节点是notready的,你的coredns也没有办法分配到地址:

[root@helix105 shell]# kubectl get pod -n kube-system -o wide
NAME                                            READY   STATUS    RESTARTS   AGE   IP              NODE                    NOMINATED NODE   READINESS GATES
coredns-5c98db65d4-5fh6c                        0/1     Pending   0          54s                                          
coredns-5c98db65d4-dbwmq                        0/1     Pending   0          54s                                          
kube-controller-manager-helix105.hfa.chenqian   1/1     Running   0          19s   172.16.60.105   helix105.hfa.chenqian              
kube-proxy-k74ld                                1/1     Running   0          54s   172.16.60.105   helix105.hfa.chenqian              
kube-scheduler-helix105.hfa.chenqian            1/1     Running   0          14s   172.16.60.105   helix105.hfa.chenqian              
[root@helix105 shell]# kubectl get node
NAME                    STATUS     ROLES    AGE   VERSION
helix105.hfa.chenqian   NotReady   master   86s   v1.15.0

安装CNI

创建CNI配置文件

$ mkdir -p /etc/cni/net.d
$ cat >/etc/cni/net.d/10-mynet.conf </etc/cni/net.d/99-loopback.conf <

这里两个配置一个是给容器塞一个网卡挂在网桥上的,另外一个配置负责撸(本地回环)。。

配置完后会发现节点ready:

[root@helix105 shell]# kubectl get node
NAME                    STATUS   ROLES    AGE   VERSION
helix105.hfa.chenqian   Ready    master   15m   v1.15.0

但是coredns会一直处于ContainerCreating状态,是因为bin文件还没有:

failed to find plugin "bridge" in path [/opt/cni/bin]

plugins里实现了很多的CNI,如我们上面配置的bridge.

$ cd $GOPATH/src/github.com/containernetworking/plugins
$ ./build_linux.sh
$ cp bin/* /opt/cni/bin
$ ls bin/
bandwidth  dhcp      flannel      host-local  loopback  portmap  sbr     tuning
bridge     firewall  host-device  ipvlan      macvlan   ptp      static  vlan

这里有很多二进制,我们学习的话不需要关注所有的,就看ptp(就简单的创建了设备对)或者bridge

再看coredns已经能分配到地址了:

[root@helix105 plugins]# kubectl get pod -n kube-system -o wide
NAME                                            READY   STATUS    RESTARTS   AGE     IP              NODE                    NOMINATED NODE   READINESS GATES
coredns-5c98db65d4-5fh6c                        1/1     Running   0          3h10m   10.22.0.8       helix105.hfa.chenqian              
coredns-5c98db65d4-dbwmq                        1/1     Running   0          3h10m   10.22.0.9

看一下网桥,cni0上挂了两个设备,与我们上面的cni配置里配置的一样,type字段指定用哪个bin文件,bridge字段指定网桥名:

[root@helix105 plugins]# brctl show
bridge name bridge id       STP enabled interfaces
cni0        8000.8ef6ac49c2f7   no      veth1b28b06f
                                        veth1c093940

原理

为了更好理解kubelet干嘛了,我们可以找一个脚本来解释 script 这个脚本也可以用来测试你的CNI:

为了易读,我删除一些不重要的东西,原版脚本可以在连接中去拿

# 先创建一个容器,这里只为了拿到一个net namespace
cOntid=$(docker run -d --net=none golang:1.12.7 /bin/sleep 10000000) 
pid=$(docker inspect -f '{{ .State.Pid }}' $contid)
netnspath=/proc/$pid/ns/net # 这个我们需要

kubelet启动pod时也是创建好容器就有了pod的network namespaces,再去把ns传给cni 让cni去配置

./exec-plugins.sh add $contid $netnspath # 传入两个参数给下一个脚本,containerid和net namespace路径

docker run --net=container:$contid $@
NETCOnFPATH=${NETCONFPATH-/etc/cni/net.d}

i=0
# 获取容器id和网络ns
cOntid=$2 
netns=$3

# 这里设置了几个环境变量,CNI命令行工具就可以获取到这些参数
export CNI_COMMAND=$(echo $1 | tr '[:lower:]' '[:upper:]')
export PATH=$CNI_PATH:$PATH # 这个指定CNI bin文件的路径
export CNI_COnTAINERID=$contid 
export CNI_NETNS=$netns

for netconf in $(echo $NETCONFPATH/10-mynet.conf | sort); do
        name=$(jq -r '.name' <$netconf)
        plugin=$(jq -r '.type' <$netconf) # CNI配置文件的type字段对应二进制程序名
        export CNI_IFNAME=$(printf eth%d $i) # 容器内网卡名

        # 这里执行了命令行工具
        res=$($plugin <$netconf) # 这里把CNI的配置文件通过标准输入也传给CNI命令行工具
        if [ $? -ne 0 ]; then
                # 把结果输出到标准输出,这样kubelet就可以拿到容器地址等一些信息
                errmsg=$(echo $res | jq -r '.msg')
                if [ -z "$errmsg" ]; then
                        errmsg=$res
                fi

                echo "${name} : error executing $CNI_COMMAND: $errmsg"
                exit 1
        let "i=i+1"
done

总结一下:

         CNI配置文件
         容器ID
         网络ns
kubelet -------------->  CNI command
   ^                        |
   |                        |
   +------------------------+
       结果标准输出

bridge CNI实现

既然这么简单,那么就可以去看看实现了:

bridge CNI代码

//cmdAdd 负责创建网络
func cmdAdd(args *skel.CmdArgs) error 

//入参数都已经写到这里面了,前面的参数从环境变量读取的,CNI配置从stdin读取的
type CmdArgs struct {
    ContainerID string
    Netns       string
    IfName      string
    Args        string
    Path        string
    StdinData   []byte
}

所以CNI配置文件除了name type这些特定字段,你自己也可以加自己的一些字段.然后自己去解析

然后啥事都得靠自己了

//这里创建了设备对,并挂载到cni0王桥上
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode, n.Vlan)

具体怎么挂的就是调用了netlink 这个库,sealos在做内核负载时同样用了该库。

err := netns.Do(func(hostNS ns.NetNS) error { //创建设备对
    hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)
    ...
    //配置容器内的网卡名mac地址等
    contIface.Name = containerVeth.Name
    contIface.Mac = containerVeth.HardwareAddr.String()
    contIface.Sandbox = netns.Path()
    hostIface.Name = hostVeth.Name
    return nil
})
...

// 根据index找到宿主机设备对名
hostVeth, err := netlink.LinkByName(hostIface.Name)
...
hostIface.Mac = hostVeth.Attrs().HardwareAddr.String()

// 把宿主机端设备对挂给网桥
if err := netlink.LinkSetMaster(hostVeth, br); err != nil {}

// 设置hairpin mode
if err = netlink.LinkSetHairpin(hostVeth, hairpinMode); err != nil {
}

// 设置vlanid
if vlanID != 0 {
    err = netlink.BridgeVlanAdd(hostVeth, uint16(vlanID), true, true, false, true)
}

return hostIface, contIface, nil

最后把结果返回:

type Result struct {
    CNIVersion string         `json:"cniVersion,omitempty"`
    Interfaces []*Interface   `json:"interfaces,omitempty"`
    IPs        []*IPConfig    `json:"ips,omitempty"`
    Routes     []*types.Route `json:"routes,omitempty"`
    DNS        types.DNS      `json:"dns,omitempty"`
}

// 这样kubelet就收到返回信息了
func (r *Result) PrintTo(writer io.Writer) error {
    data, err := json.MarshalIndent(r, "", "    ")
    if err != nil {
        return err
    }
    _, err = writer.Write(data)
    return err
}

如:

{
  "cniVersion": "0.4.0",
  "interfaces": [                                            (this key omitted by IPAM plugins)
      {
          "name": "",
          "mac": "",                            (required if L2 addresses are meaningful)
          "sandbox": "" (required for container/hypervisor interfaces, empty/omitted for host interfaces)
      }
  ],
  "ips": [
      {
          "version": "<4-or-6>",
          "address": "",
          "gateway": "",          (optional)
          "interface": 
      },
      ...
  ],
  "routes": [                                                (optional)
      {
          "dst": "",
          "gw": ""                           (optional)
      },
      ...
  ],
  "dns": {                                                   (optional)
    "nameservers":                      (optional)
    "domain":                          (optional)
    "search":             (optional)
    "options":                              (optional)
  }
}

总结

CNI接口层面是非常简单的,所以更多的就是在CNI本身的实现了,懂了上文这些就可以自己去实现一个CNI了,是不是很酷,也会让大家更熟悉网络以更从容的姿态排查网络问题了。

扫码关注sealyun

探讨可加QQ群:98488045


推荐阅读
  • Envoy 流量分配策略优化
    在本研究中,我们对Envoy的流量分配策略进行了优化,旨在提高系统的稳定性和性能。实验环境包括一个前端代理服务(Envoy,IP地址为172.31.57.10)和五个后端服务。通过调整Envoy的配置,实现了更高效的流量分发和负载均衡,显著提升了整体系统的响应速度和可靠性。 ... [详细]
  • Python学习day3网络基础之网络协议篇
    一、互联网协议连接两台计算机之间的Internet实际上就是一系列统一的标准,这些标准称之为互联网协议,互联网的本质就是一系列网络协议。二、为什么要有互联网协议互联网协议就相当于计 ... [详细]
  • 为什么多数程序员难以成为架构师?
    探讨80%的程序员为何难以晋升为架构师,涉及技术深度、经验积累和综合能力等方面。本文将详细解析Tomcat的配置和服务组件,帮助读者理解其内部机制。 ... [详细]
  • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
  • 笔记说明重学前端是程劭非(winter)【前手机淘宝前端负责人】在极客时间开的一个专栏,每天10分钟,重构你的前端知识体系& ... [详细]
  • ArcBlock 发布 ABT 节点 1.0.31 版本更新
    2020年11月9日,ArcBlock 区块链基础平台发布了 ABT 节点开发平台的1.0.31版本更新,此次更新带来了多项功能增强与性能优化。 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 调试利器SSH隧道
    在开发微信公众号或小程序的时候,由于微信平台规则的限制,部分接口需要通过线上域名才能正常访问。但我们一般都会在本地开发,因为这能快速的看到 ... [详细]
  • 华硕笔记本无法开启热点的解决办法
    当您的华硕笔记本电脑无法开启热点时,可能是因为多种原因导致的。本文将详细介绍几种有效的解决方法,帮助您快速恢复热点功能。 ... [详细]
  • 本文介绍了如何解决无线网络连接受限的问题,包括手动重置网络连接、使用命令提示符刷新网络配置等方法。 ... [详细]
  • Hadoop MapReduce 实战案例:手机流量使用统计分析
    本文通过一个具体的Hadoop MapReduce案例,详细介绍了如何利用MapReduce框架来统计和分析手机用户的流量使用情况,包括上行和下行流量的计算以及总流量的汇总。 ... [详细]
  • CRZ.im:一款极简的网址缩短服务及其安装指南
    本文介绍了一款名为CRZ.im的极简网址缩短服务,该服务采用PHP和SQLite开发,体积小巧,约10KB。本文还提供了详细的安装步骤,包括环境配置、域名解析及Nginx伪静态设置。 ... [详细]
  • Android目录遍历工具 | AppCrawler自动化测试进阶(第二部分):个性化配置详解
    终于迎来了“足不出户也能为社会贡献力量”的时刻,但有追求的测试工程师绝不会让自己的生活变得乏味。与其在家消磨时光,不如利用这段时间深入研究和提升自己的技术能力,特别是对AppCrawler自动化测试工具的个性化配置进行详细探索。这不仅能够提高测试效率,还能为项目带来更多的价值。 ... [详细]
  • 点击上方蓝色“程序猿DD”,选择“设为星标”回复“资源”获取独家整理的学习资料!来源|https:github.comwizardbyronprinci ... [详细]
author-avatar
手机用户2502887971_699
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有