CNI插件之bridge plugin

CNI网络插件bridge plugin

Bridge插件是典型的CNI基础插件,其工作原理类似物理交换机,通过创建虚拟网桥将所有容器连接到一个二层网络,从而实现容器间的通信。

Bridge插件使用了Linux 原生网桥技术,功能单一结构简单,拥有较高的可靠性,在故障排查上也比较容易。Bridge对比其他网络插件,一方面更容易上手,有利于通过简单实验来揭示cni工作原理;另一方面被很多上层插件依赖,对理解和学习其他插件也有帮助;下面将循序渐进的展开说明该插件的原理和使用方法。

本文首先结合nginx应用来熟悉bridge插件使用,然后通过cnitool观察插件与外部交互行为,再通过模拟试验进一步剥开插件机制,最后分析源码阐述原理和实现;建议先上手操作后再带着问题分析原理,效果会更好。

下面将依次介绍:

  • Bridge插件是什么
  • Bridge插件安装配置
  • Bridge结合ningx应用
  • 通过cnitool验证Bridge
  • Bridge原理和试验
  • Bridge代码分析

Bridge插件概念

Bridge作为最基础的CNI网络插件,它的作用是将容器依附到网桥上实现与宿主机的通信,通过网桥机制建立连接是kubernetes实现容器通信的一种典型方式。bridge插件结合IPAM等插件来管理pod的网络设备和地址配置,在pod创建和删除过程中发挥作用。

如图所示,当容器创建时会建立一个虚拟网桥,可类比物理交换机,刚创建的网桥只接入了协议栈,其他端口未被使用;要让网桥用起来还需要生成一个虚拟网卡对veth pair,它相当于一条网线,其一端接在容器内另一端连到虚拟网桥上,以此来将容器接入网络。
在这里插入图片描述

veth pair设备总是成对出现,两个设备彼此相连,一个设备上收到的数据会被转发到另一个设备上,效果类似于管道,在这里配合网桥作用,就是把一个 namespace 发出的数据包转发到另一个 namespace,实现容器内外交互。

bridge与macvlan等插件属于同一层级,下图展示了相关插件与CNI的关系。
在这里插入图片描述

Bridge功能使用

这里先结合nginx说明bridge插件使用方法,按以下步骤操作,并确保每个阶段的状态检查正常后再进入下一步。

准备Kubernetes环境

(以下步骤略去细节,详细内容可以参考kubernetes快速安装部署实践

  • 启动docker和kubelet服务
    systemctl start docker
    systemctl start kubelet
    确保所有节点均启用服务,这里为了保证后续操作不受干扰,对于已经安装过k8s环境的机器需要通过kubeadm reset命令重置。

  • master节点初始化
    kubeadm init --pod-network-cidr=192.166.0.0/16
    注意分配给容器使用的虚拟地址不能和node地址同网段,此后所有pod都会从该地址池分配IP。

  • 添加工作节点
    kubeadm join 192.168.122.17:6443 --token xxx(需使用上一步init返回的结果)
    所有工作节点直接使用master节点上kubeadm init执行后返回的join命令串。

  • 检查各node状态
    在Master节点运行命令,确保所有节点处于ready状态,至此k8s环境生效。

kubectl get node -o wide
NAME             STATUS   ROLES    AGE   VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION          CONTAINER-RUNTIME
k8s-nfv-master   Ready    master   47h   v1.14.3   192.168.122.17   <none>        CentOS Linux 7 (Core)   3.10.0-957.el7.x86_64   docker://18.9.2
k8s-nfv-node1    Ready    <none>   47h   v1.14.3   192.168.122.18   <none>        CentOS Linux 7 (Core)   3.10.0-957.el7.x86_64   docker://18.9.2
k8s-nfv-node2    Ready    <none>   47h   v1.14.3   192.168.122.19   <none>        CentOS Linux 7 (Core)   3.10.0-957.el7.x86_64   docker://18.9.2

安装配置bridge插件

  • 准备二进制文件
    kubernetes版本1.14.3安装后默认包含bridge插件,其二进制可执行文件位于主节点的/opt/cni/bin目录中;如需通过源码编译生成可以按如下命令操作。
git clone https://github.com/containernetworking/plugins.git 
cd plugins
./build_linux.sh
  • 准备配置文件
    在/etc/cni/net.d中增加cni配置文件10-bgnet.conf,指定使用的cni组件及参数;需要在所有节点配置该文件,各节点分配的IP范围可通过kubectl describe 查看 ;配置文件更新后,kubenet将会自动调用组件配置网络参数;也可以重启服务使配置立即生效。
    systemctl restart docker
    systemctl restart kubelet

Master配置:

{
        "cniVersion":"0.3.1",
        "name": "bgnet",
        "type": "bridge",
        "bridge": "cni0",
        "isGateway": true,
"forceAddress": false,
        "ipMasq": true,
"hairpinMode": true,
        "ipam": {
                "type": "host-local",
                "subnet": "192.166.0.0/24",
                "rangeStart": "192.166.0.100", //可选
                "rangeEnd": "192.166.0.200",  //可选
                "gateway": "192.166.0.1",  //可选
                "routes": [
                { "dst": "0.0.0.0/0" }
                ]
        }
}

配置字段详细说明:

  • cniVersion: 使用的CNI版本 name: 网络名称,在管理域中唯一 type: 插件类型,即插件名称,这里是bridge
  • bridge:网桥接口名称,如cni0 isGateway:是否为网桥分配IP地址,以便连到网桥的容器可以将其用作网关
  • isDefaultGateway: 设置为true时将网桥配置为虚拟网络的默认网关,默认值为false
  • forceAddress:如果先前的IP已更改,则要求插件分配一个新的IP,默认为false ipMasq:是否创建IP
  • masquerade,即为出口流量启用SNAT将源地址改为网桥IP,默认为false mtu: 设置MTU为指定值,默认使用内核数值
  • hairpinMode:为网桥接口设置反射中继,打开则允许网桥通过接收了以太网帧的虚拟端口将其重新发送回去,即让POD是否可以通过Service访问自己,默认为false
  • promiscMode: 设置网桥的混杂模式,默认为false vlan: 分配Vlan标签,默认为none
  • ipam:
    设置ip分配方案,对于L2-only模式,创建为空即可 type: 指定使用的方案,对应IPAM插件名称,这里是host-local
  • subnet: 分配的子网范围 rangeStart:开始的地址 rangeEnd:结束的地址 gateway: 容器内部网络的网关
  • routes: 路由

Node1配置:

{
   
        "cniVersion":"0.3.1",
        "name": "bgnet",
        "type": "bridge",
        "bridge": "cni0",
        "isGateway": true,
        "ipMasq": true,
        "ipam": {
   
                "type": "host-local",
                "subnet": "192.166.1.0/24",
                "rangeStart": "192.166.1.100",
                "rangeEnd": "192.166.1.200",
                "gateway": "192.166.1.1",
                "routes": [
                {
    "dst": "0.0.0.0/0" }
                ]
        }
}

Node2配置:

{
   
        "cniVersion":"0.3.1",
        "name": "bgnet",
        "type": "bridge",
        "bridge": "cni0",
        "isGateway": true,
        "ipMasq": true,
        "ipam": {
   
                "type": "host-local",
                "subnet": "192.166.2.0/24",
                "rangeStart": "192.166.2.100",
                "rangeEnd": "192.166.2.200",
                "gateway": "192.166.2.1",
                "routes": [
                {
    "dst": "0.0.0.0/0" }
                ]
        }
}
  • 检查pod状态
    在主节点输入命令,查询所有pod均为Running状态,且有分配到地址;比如其中coredns的pod分配到地址192.166.0.105,状态为Running,至此cni插件已生效。
kubectl get pod -n kube-system -o wide
NAME                                     READY   STATUS    RESTARTS   AGE   IP               NODE             NOMINATED NODE   READINESS GATES
coredns-fb8b8dccf-r4hkc                  1/1     Running   2          47h   192.166.0.104    k8s-nfv-master   <none>           <none>
coredns-fb8b8dccf-v8zc6                  1/1     Running   2          47h   192.166.0.105    k8s-nfv-master   <none>           <none>
etcd-k8s-nfv-master                      1/1     Running   2          47h   192.168.122.17   k8s-nfv-master   <none>           <none>
kube-apiserver-k8s-nfv-master            1/1     Running   2          47h   192.168.122.17   k8s-nfv-master   <none>           <none>
kube-controller-manager-k8s-nfv-master   1/1     Running   2          47h   192.168.122.17   k8s-nfv-master   <none>           <none>
kube-proxy-4f7vr                         1/1     Running   2          47h   192.168.122.17   k8s-nfv-master   <none>           <none>
kube-proxy-6gb2d                         1/1     Running   0          47h   192.168.122.19   k8s-nfv-node2    <none>           <none>
kube-proxy-n8wxq                         1/1     Running   0          47h   192.168.122.18   k8s-nfv-node1    <none>           <none>
kube-scheduler-k8s-nfv-master            1/1     Running   2          47h   192.168.122.17   k8s-nfv-master   <none>           <none>

安装ningx容器验证

  • 创建configMap
    ConfigMap提供了向容器中注入配置文件的功能,目的就是为了让镜像和配置文件解耦,以便实现镜像的可移植性和可复用性。注入方式有两种,一种将configMap做为存储卷,一种是将configMap通过env中configMapKeyRef注入到容器中,这里用前者。
    以下是nginx.conf配置文件:
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
	worker_connections  1024;
}

http {
	include       /etc/nginx/mime.types;
	default_type  application/octet-stream;

	log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
						'$status $body_bytes_sent "$http_referer" '
						'"$http_user_agent" "$http_x_forwarded_for"';
	access_log  /var/log/nginx/access.log  main;
	sendfile        on;
	#tcp_nopush     on;
	keepalive_timeout  65;
	#gzip  on;
	#include /etc/nginx/conf.d/*.conf;
	server {
		   listen                    80;
		   server_name               localhost;
		   root                      /home/wwwroot/test;
		   index                     index.html;
	}
}

在默认ns下,运行创建命令
kubectl create configmap confnginx --from-file nginx.conf
查看创建结果

kubectl get configmap
NAME        DATA   AGE
confnginx   1      41h
  • 创建Replication Controller
    RC可以保证在任意时间运行Pod的副本数量,保证Pod总是可用的。如果实际Pod数量比指定的多就结束掉多余的,如果实际数量比指定的少就新创建一些Pod,当Pod失败、被删除或者挂掉后,RC会去自动创建新的Pod来保证副本数量,这里使用RC来管理ngnix Pod。
apiVersion: v1
kind: ReplicationController  //指明RC类型
metadata: //设置元数据
  name: nginx-controller //RC自身命名
spec:  //定义RC具体配置
  replicas: 2  //设置pod数量维持在2个
  selector:  //通过selector来匹配相应pod的标签
    name: nginx  //服务名称
  template: //设置pod模板
    metadata:
      labels: //设置标签
        name: nginx
    spec:
      containers:
      - name: nginx //容器名称
        image: docker.io/nginx:alpine  //使用的镜像
        ports:
        - containerPort: 80 //容器端口配置
        volumeMounts:
        - mountPath: /etc/nginx/nginx.conf   //容器内部的配置目录
          name: nginx-config
          subPath: nginx.conf
        - mountPath: /home/wwwroot/test  //挂载节点主机目录
          name: nginx-data
      volumes:
        - name: nginx-config  //通过挂载存储卷注入configMap
          configMap:
            name: confnginx
        - name: nginx-data
          hostPath:
            path: /home/wwwroot/test

创建nginx的RC pod,后续RC将按配置自动运行相应数量的ngnix容器
kubectl create -f nginx-rc.yaml
查看创建结果:

kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP              NODE            NOMINATED NODE   READINESS GATES
nginx-controller-ggg2z   1/1     Running   0          30h   192.166.1.100   k8s-nfv-node1   <none>           <none>
nginx-controller-s84rt   1/1     Running   0          30h   192.166.2.100   k8s-nfv-node2   <none>           <none>
  • 创建nginx的Service
    Service可以看作是一组提供相同服务的Pod对外的访问接口,借助Service应用可以方便地实现服务发现和负载均衡。
    nginx-svc.yaml如下:
apiVersion: v1
kind: Service // 指明service类型
metadata:
  name: nginx-service-nodeport //指定服务名称
spec:
  ports:
    - port: 8000 //Service将会监听8000端口,并将所有监听到的请求转发给其管理的Pod
      targetPort: 80 //Service监听到8000端口的请求会被转发给其管理的Pod的80端口
      protocol: TCP
      nodePort: 30080  //Service将通过Node上的30080端口暴露给外部
  type: NodePort //此模式下访问任意一个NodeIP:nodePort都将路由到ClusterIP
  selector:
name: nginx //指定service将要使用的标签,即会管理所有ngnix标签的pod

通过命令创建服务
kubectl create -f nginx-svc.yaml
查看服务状态:

kubectl get svc -A
NAMESPACE     NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE
default       kubernetes               ClusterIP   10.96.0.1        <none>        443/TCP                  2d
default       nginx-service-nodeport   NodePort    10.103.145.249   <none>        8000:30080/TCP           31h
kube-system   kube-dns                 ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP   2d

其中10.103.145.249为服务对外提供的地址,端口为8000

  • 验证连通性
    1)Node1
    通过node地址访问:wget http://192.168.122.18:30080/index.html
    通过pod地址访问:wget http://192.166.1.100:80/index.html
    通过service地址访问:wget http://10.103.145.249:8000/index.html
    2)Node2
    通过node地址访问:wget 192.168.122.19:30080
    通过pod地址访问:wget http://192.166.2.100:80/index.html
    通过service地址访问:wget http://10.103.145.249:8000/index.html

    验证节点内部访问正常,但节点之间无法访问,还需配置路由和转发规则

  • 配置iptable规则
    1)增加路由实现不同网段的跨节点访问
    node1:
    ip route add 192.166.2.0/24 via 192.168.122.19 dev ens3
    node2:
    ip route add 192.166.1.0/24 via 192.168.122.18 dev ens3

    2)增加转发规则实现同节点上容器间和网桥的通讯
    node1 & node2:
    iptables -t filter -A FORWARD -s 192.166.0.0/16 -j ACCEPT
    iptables -t filter -A FORWARD -d 192.166.0.0/16 -j ACCEPT

    3)增加NAT规则实现容器内部网络与外部网络的通讯
    node1:
    iptables -t nat -A POSTROUTING -s 192.166.1.0/24 ! -o cni0 -j MASQUERADE
    node2:
    iptables -t nat -A POSTROUTING -s 192.166.2.0/24 ! -o cni0 -j MASQUERADE

    此时在各节点使用wget测试,节点之间能相互连通,wget验证外网地址正常;由于service有进行负载均衡处理,在wget中可能散列到不同节点上获取文件。

  • 进入容器验证
    使用contain2ns.sh enter_ns.sh工具进入容器内部终端
    1)查看接口:
    Node1

ifconfig 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.166.1.100  netmask 255.255.255.0  broadcast 192.166.1.255
        ether 2e:ab:58:fc:45:3e  txqueuelen 0  (Ethernet)
        RX packets 365  bytes 29668 (28.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 288  bytes 31042 (30.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Node2

ifconfig 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.166.2.100  netmask 255.255.255.0  broadcast 192.166.2.255
        ether 9e:ef:6f:db:d3:a6  txqueuelen 0  (Ethernet)
        RX packets 192  bytes 14800 (14.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 146  bytes 14159 (13.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

2)测试容器到容器连通性
在node1上ping node2:ping 192.166.2.100
tcpdump抓包显示:

14:36:32.233236 IP 192.166.1.100 > 192.166.2.100: ICMP echo request, id 30854, seq 1, length 64
14:36:32.234221 IP 192.166.2.100 > 192.166.1.100: ICMP echo reply, id 30854, seq 1, length 64
Node2:
14:35:07.696705 IP 192.168.122.18 > 192.166.2.100: ICMP echo request, id 30768, seq 1, length 64
14:35:07.696800 IP 192.166.2.100 > 192.
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在部署CNI插件时,出现异常可能是由于多种原因引起的。以下是一些可能的原因和解决方法: 1. 网络插件版本不匹配:CNI插件的版本需要与Kubernetes版本匹配。如果版本不匹配,可能会导致CNI插件部署失败。可以参考CNI插件的官方文档,选择适合Kubernetes版本的CNI插件版本。 2. 网络插件配置错误:在部署CNI插件时,需要正确配置CNI插件的参数。如果配置错误,可能会导致插件部署失败。可以参考CNI插件的官方文档,了解CNI插件的配置参数和正确使用方法。 3. 网络插件镜像拉取失败:如果CNI插件的镜像拉取失败,可能会导致插件部署失败。可以使用`docker images`命令查看镜像是否存在,或者使用`docker pull`命令重新拉取镜像。 4. 网络环境问题:在部署CNI插件时,需要确保网络环境正常。如果网络环境存在问题,可能会导致插件部署失败。可以使用`ping`命令测试节点之间的网络连通性,并确保节点间的防火墙设置正确。 5. 容器运行时问题:在部署CNI插件时,需要确保容器运行时环境正常。如果容器运行时存在问题,可能会导致插件部署失败。可以使用`docker ps`命令查看容器是否正常运行,并检查容器运行时的日志文件。 需要注意的是,CNI插件的部署可能涉及到多个节点,因此需要仔细检查每个节点的配置和运行状态,以便及时发现和解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codemillion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值