后浪笔记一零二四

cni规范: https://www.cni.dev/docs/spec/

1. cni是什么?

CNI插件:遵循专门为交付操作设计的appc/CNI规范 Kubelet插件:使用bridge和host-local的CNI插件来实现基本的cbr0

  1. 安装 kubelet默认有个简单的网络插件和一个默认的整个集群公用的网络。它会在启动时探测插件并记住它,然后在pod生命周期的适当时间执行该插件(这仅适用于Docker,因为rkt管理自己的CNI插件)。可以使用kubelet的两个命令行参数来指定要使用的插件:
  • cni-bin-dir: kubelet会在启动的时候探测这个目录(并记录下来)
  • network-plugin: 指定在cni-bin-dir目录下的某个插件。它必须和探针之前记录的插件名保持一致。例如cni插件,它的插件名是"cni"
  1. Network Plugin的要求 除了要提供用于配置和清理Pod网路的NetworkPlugin的接口,还要给插件提供和kube-proxy相关的支持。因为iptables proxy直接依赖iptables,所以插件可能需要确保iptables可以使用容器的流量。例如,如果这个插件连接容器到一个linux网桥,就必须设置net/bridge/bridge-nf-call-iptables这个sysctl的值为1,以确保iptables proxy功能的正常。如果插件没有使用Linux网桥(但是需要像Open vSwitch之类的其他机制)就需要确保流量被正确路由到该代理。

  2. CNI 在kubelet使用–network-plugin=cni命令行参数的时候就会启用CNI网络插件。kubelet会从–cni-conf-dir目录下(默认值为/etc/cni/net.d)读取以字典顺序排序的第一个文件的内容,并从这个文件中获取CNI配置来建立每个pod的网络。这个CNI配置文件必须满足CNI规范,任何被CNI配置文件引用的CNI插件都必须存放在–cni-bin-dir目录下(默认值是/opt/cni/bin)。

a. Support hostPort CNI插件支持hostPort。你可以使用官方的portmap插件或者自己的CNI插件的portMapping参数来使用hostPort功能。 例如:

 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
{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    }
  ]
}

b. 支持流量整形(实验特性,kubernetes v1.18) CNI网络插件也支持Pod网络的入口和出口流量的整形。你可以使用官方的bandwidth插件或者自己的CNI插件的bandwidth参数来使用它。 需要将bandwidth或者你自己的cni插件放到/etc/cni/net.d文件夹下才行。

 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
{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.0",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "datastore_type": "kubernetes",
      "nodename": "127.0.0.1",
      "ipam": {
        "type": "host-local",
        "subnet": "usePodCidr"
      },
      "policy": {
        "type": "k8s"
      },
      "kubernetes": {
        "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

接下来,就可以在你的pod中添加kubernetes.io/ingress-bandwidth和kubernetes.io/egress-bandwidth注解来引用它了。

1
2
3
4
5
6
7
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubernetes.io/ingress-bandwidth: 1M
    kubernetes.io/egress-bandwidth: 1M
...

c. kubenet

kubenet是一个非常基本且简单的网络插件,只能在linux中使用。它本身并没有实现更高级的功能,例如跨节点联网或网络策略。 它通常使用云供应商的能力来建立节点之间或单节点环境中的路由规则。

Kubenet创建一个名为cbr0的Linux桥,并为每个Pod创建一个veth对,每对的主机端都连接到cbr0。 通过配置或控制器管理器,为veth对的另一端分配ip地址, 该ip地址必须在该pod所在节点所能支配的ip范围内。 在主机上已启动常规接口的MTU中找到最小的那个分配给cbr0。

这个插件需要:

  • 需要标准的CNI bridge, lo和host-local插件,而且最小版本必须是0.2.0。kubelet会先在/opt/cni/bin下搜索他们。也可以使用cni-bin-dir参数指定额外的搜索目录。第一个被找到的生效。
  • kubelet必须使用–network-plugin=kubenet参数启动。
  • Kubelet还应该使用–non-masquerade-cidr = 参数运行,以确保到此范围之外的IP的流量将使用IP伪装。
  • 使用kubelet的–pod-cidr参数或者使用controller-manager的–allocate-node-cidrs=true –cluster-cidr=来指定ip subnet。

d. 指定 the MTU(with kubenet) 网路插件通常会尝试推断出合理的MTU,但是某些特殊情况插件是没有办法推断出合理MTU的,这个时候就需要手动指定MTU了。 例如,如果Docker网桥或者其他接口有一个小的MTU,kubenet就会使用这个MTU。或者,你正在使用IPSEC封装,则必须降低MTU,并且此计算对于大多数网络插件而言是超出范围的。

可以使用kubelet的–network-plugin-mtu命令行参数来指定MTU。例如,在AWS中,eth0这个MTU的值是9001,这个时候就可以使用–network-plugin-mtu=9001。

注意,只有kubenet插件支持–network-plugin-mtu这个参数。

2. 如何使用flannel实现跨主机网络

flannel主要是给不同节点主机下的docker容器分配全局唯一的虚拟ip地址。 flannel属于“覆盖网络(overlay network)”,即运行在一个网上的网(应用层网络),它并不依靠ip地址来传递消息,而是采用一种映射机制,把ip地址和identifiers做映射来定位资源,即将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式。

flannel使用etcd存储配置数据和子网分配信息。flannel启动之后,后台进程首先检索配置和正在使用的子网列表,然后选择一个可用的子网,然后尝试去注册它。

etcd也存储这个每个主机对应的ip。

flannel使用etcd的watch机制监视/coreos.com/network/subnets下面所有元素的变化信息,并且根据它来维护一个路由表。

为了提高性能,flannel优化了Universal TAP/TUN设备,对TUN和UDP之间的ip分片做了处理。

  1. 网络拓扑
                etcd[10.0.2.10]
                        v
                      [sw]
                        +
              +---------+---------+
              |                   |
         [node01]             [node02]
         [10.0.2.10]          [10.0.2.11]
  1. 实验环境
centos 7.6.1810

node1 10.0.2.10   docker+flannel+etcd
node2 10.0.2.11   docker+flannel
  1. 准备工作(所有节点都要执行)
1
2
3
4
5
6
7
8
9
# 开启内核ipv4转发功能
$ echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
$ sysctl -p
# 清除iptables底层默认规则,并开启允许转发功能
$ iptables -P INPUT ACCEPT
$ iptables -P FORWARD ACCEPT
$ iptables -F
$ iptables -L -n
# 关闭selinux防火墙
  1. 部署etcd

1) 在node01上安装etcd服务

1
$ yum install -y etcd

2) 修改etcd的配置文件

#[Member]
#ETCD_CORS=""
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
#ETCD_WAL_DIR=""
#ETCD_LISTEN_PEER_URLS="http://localhost:2380"
ETCD_LISTEN_CLIENT_URLS="http://192.168.32.213:2379"
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
ETCD_NAME="default"
#ETCD_SNAPSHOT_COUNT="100000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
#ETCD_QUOTA_BACKEND_BYTES="0"
#ETCD_MAX_REQUEST_BYTES="1572864"
#ETCD_GRPC_KEEPALIVE_MIN_TIME="5s"
#ETCD_GRPC_KEEPALIVE_INTERVAL="2h0m0s"
#ETCD_GRPC_KEEPALIVE_TIMEOUT="20s"
#
#[Clustering]
#ETCD_INITIAL_ADVERTISE_PEER_URLS="http://localhost:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.32.213:2379"
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_INITIAL_CLUSTER="default=http://localhost:2380"
#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
#ETCD_INITIAL_CLUSTER_STATE="new"
#ETCD_STRICT_RECONFIG_CHECK="true"
#ETCD_ENABLE_V2="true"
#
#[Proxy]
#ETCD_PROXY="off"
#ETCD_PROXY_FAILURE_WAIT="5000"
#ETCD_PROXY_REFRESH_INTERVAL="30000"
#ETCD_PROXY_DIAL_TIMEOUT="1000"
#ETCD_PROXY_WRITE_TIMEOUT="5000"
#ETCD_PROXY_READ_TIMEOUT="0"
#
#[Security]
#ETCD_CERT_FILE=""
#ETCD_KEY_FILE=""
#ETCD_CLIENT_CERT_AUTH="false"
#ETCD_TRUSTED_CA_FILE=""
#ETCD_AUTO_TLS="false"
#ETCD_PEER_CERT_FILE=""
#ETCD_PEER_KEY_FILE=""
#ETCD_PEER_CLIENT_CERT_AUTH="false"
#ETCD_PEER_TRUSTED_CA_FILE=""
#ETCD_PEER_AUTO_TLS="false"
#
#[Logging]
#ETCD_DEBUG="false"
#ETCD_LOG_PACKAGE_LEVELS=""
#ETCD_LOG_OUTPUT="default"
#
#[Unsafe]
#ETCD_FORCE_NEW_CLUSTER="false"
#
#[Version]
#ETCD_VERSION="false"
#ETCD_AUTO_COMPACTION_RETENTION="0"
#
#[Profiling]
#ETCD_ENABLE_PPROF="false"
#ETCD_METRICS="basic"
#
#[Auth]
#ETCD_AUTH_TOKEN="simple"

3) 启动etcd

1
$ systemctl restart etcd && systemctl enable etcd && systemctl status etcd

4) 在etcd中添加flannel的key

1
$ etcdctl --endpoints="http://10.0.2.10:2379" set /atomic.io/network/config '{"Network": "10.0.2.0/16", "Backend":{"Type":"vxlan"}}'

‘/atomic.io/network/config’这个key与下文/etc/sysconfig/flannel中的配置项FLANNEL_ETCD_PREFIX是相对应的

上面flannel设置的ip网段可以任意设定,随便设定一个网段都可以。容器的ip就是根据这个网段进行自动分配的,ip分配后,容器一般是可以对外联网的(网桥模式,只要宿主机能上网就可以)

  1. 部署flannel

1) node01和node02安装flannel

1
$ yum install -y flannel

2) node01和node02修改flannel配置文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Flanneld configuration options  

# etcd url location.  Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://192.168.32.213:2379"

# etcd config key.  This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/atomic.io/network"

# Any additional options that you want to pass
#FLANNEL_OPTIONS=""

3) 启动flannel node1和node2

1
$ systemctl restart flanneld.service && systemctl enable flanneld.service

4) 查看flannel信息

1
$ ps -ef | grep flannel

5) 查看flannel为节点分配的ip node1

1
2
3
4
5
6
$ cat /var/run/flannel/docker
DOCKER_OPT_BIP="--bip=10.1.83.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=10.1.83.1/24 --ip-masq=true --mtu=1450"
#在node1上分发的是10.1.83.1/24的网络

6) 部署docker 在node1和node2上执行:

1
2
3
4
5
6
7
8
9
#下载docker-ce的yum源
wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
#把docker-ce源的地址修改为清华源的地址
sed -i 's#download.docker.com#mirrors.tuna.tsinghua.edu.cn/docker-ce#g' /etc/yum.repos.d/docker-ce.repo
#更新docker-ce.repo
yum makecache fast
#安装docker-ce
yum install -y docker-ce
systemctl start docker

7) 配置docker node1

1
2
3
4
5
$ vim /usr/lib/systemd/system/docker.service配置文件中的在ExecStart行添加$DOCKER_NETWORK_OPTIONS

ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $DOCKER_NETWORK_OPTIONS

$ systemctl daemon-reload && systemctl restart docker
  1. 测试

在node1上启动一个叫做test01的busybox

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ docker run -itd --name test01 busybox
$ docker exec -it test01 /bin/sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:01:53:02  
          inet addr:10.1.83.2  Bcast:10.1.83.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:656 (656.0 B)  TX bytes:0 (0.0 B)

在node2上启动一个叫做test02的busybox

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ docker run -itd --name test02 busybox
$ docker exec -it test02 /bin/sh
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:0A:01:18:02  
          inet addr:10.1.24.2  Bcast:10.1.24.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:656 (656.0 B)  TX bytes:0 (0.0 B)

test01可以ping通test02

1
2
3
4
5
6
$ docker exec -it test01 /bin/sh
/ # ping 10.1.24.2
PING 10.1.24.2 (10.1.24.2): 56 data bytes
64 bytes from 10.1.24.2: seq=0 ttl=62 time=2.147 ms
64 bytes from 10.1.24.2: seq=1 ttl=62 time=0.539 ms
64 bytes from 10.1.24.2: seq=2 ttl=62 time=0.481 ms

3. Q&A

  1. 容器内要想访问本宿主机的某个端口,就必须让本宿主机的防火墙放行docker或者pod的网段。出了这台机器就不需要,因为出了容器所在的那台机器,源ip就不再是容器ip了。

  2. 多节点k8s集群,节点间需要放行pod网段么?

不需要完全放行,只需要放行所有节点的ip就行了,例如:

1
2
3
# 示例:仅允许节点 IP(192.168.1.17/32)访问 Pod 网段(10.244.0.0/16)
iptables -A INPUT -s 192.168.1.17/32 -d 10.244.0.0/16 -j ACCEPT
iptables -A INPUT -d 10.244.0.0/16 -j DROP

如果不放行,会导致跨节点Pod通信异常,但是不影响本节点Pod间的通信。

当 Pod A(10.244.1.2) 访问 Pod B(10.244.2.3)时,流量如何通过防火墙:

  • 数据包从 Pod A 发出,源 IP 10.244.1.2,目的 IP 10.244.2.3。
  • Node 1 的路由表将数据包转发到 Node 2(通过 overlay 网络或路由协议)。
  • 此时数据包被封装,源 IP 变为 Node 1 的 IP(如 192.168.1.101)。
  • Node 2 的防火墙规则检查:
    • 源 IP:192.168.1.101(Node 1 的 IP,合法)。
    • 目的 IP:10.244.2.3(属于 Pod 网段,合法)。

本文发表于 0001-01-01,最后修改于 0001-01-01。

本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。


上一篇 « 下一篇 »

赞赏支持

请我吃鸡腿 =^_^=

i ysf

云闪付

i wechat

微信

推荐阅读

Big Image