原文地址: https://dramasamy.medium.com/life-of-a-packet-in-kubernetes-part-2-a07f5bf0ff14
正如我们在第一部分中讨论的,CNI插件在Kubernetes网络中发挥着关键作用。今天有许多第三方CNI插件可用; Calico就是其中之一。许多工程师更喜欢Calico; 主要原因之一是它的易用性以及它如何塑造网络架构。
Calico支持广泛的平台,包括Kubernetes、OpenShift、Docker EE、OpenStack和裸机服务。Calico节点以Docker容器的形式运行在Kubernetes主节点和集群中的每个Kubernetes工作节点上。calico-cni插件直接与每个节点上的Kubernetes kubelet进程集成,以发现创建了哪些Pod并将它们添加到Calico网络中。
我们将讨论安装、Calico模块(Felix、BIRD和Confd)以及路由模式。
没有涵盖什么? 网络策略 - 它需要一篇单独的文章,因此现在暂时跳过。
Topics — Part 2
- CNI 该做的事情
- 模块以及它的功能
- 路由模式
- 安装 (calico 和 calicoctl)
CNI 该做的事情
- 创建 veth 对并将其移入容器内
- 确定正确的 POD CIDR
- 创建 CNI 配置文件
- 分配和管理 IP 地址
- 在容器内添加默认路由
- 向所有对等节点广播路由(不适用于 VxLan)
- 在主机服务器中添加路由
- 强制执行网络策略
还有许多其他需求,但上述是基本的。让我们来看看主节点和工作节点中的路由表。每个节点都有一个容器,该容器具有 IP 地址和默认容器路由。
通过查看路由表可以明显看出,Pod 间可以通过 L3 网络完美通信。那么哪个模块负责添加此路由,以及它是怎么知道远程路由的? 以及,为什么会有一个默认网关为169.254.1.1的默认路由?我们稍后会谈到这些。
Calico的核心组件有Bird、Felix、ConfD、Etcd和Kubernetes API Server。数据存储用于存储配置信息(ip池、端点信息、网络策略等)。在我们的示例中,我们将使用Kubernetes作为Calico的数据存储。
BIRD (BGP)
Bird是一个每节点的BGP守护进程,它与运行在其他节点上的BGP守护进程交换路由信息。常见的拓扑结构可以是节点到节点的网状连接,其中每个BGP与其他所有BGP建立对等连接。
对于大规模部署,这种方式会变得很混乱。可以使用路由反射器(Route Reflector)来完成路由传播(可以将某些BGP节点配置为路由反射器),以减少BGP到BGP连接的数量。与自治系统内的每个BGP系统相互建立联盟不同,每个BGP发言者与一个路由反射器建立联盟。发送到路由反射器的路由广播然后会反射到所有其他的BGP发言者。了解更请参考 RFC4456
BIRD 实例负责将路由传播到其他 BIRD 实例。默认配置是“BGP 网状连接”,这可以用于小规模部署。在大规模部署中,建议使用路由反射器以避免问题。可以有多个路由反射器以实现高可用性。此外,可以使用外部机架路由反射器代替 BIRD。
ConfD
ConfD 是一个简单的配置管理工具,它运行在 Calico 节点容器。它从 etcd 读取值(Calico 的 BIRD 配置),并将它们写入磁盘文件。它循环遍历池(网络和子网)以应用配置数据(CIDR 密钥),并提供给 BIRD 使用。因此,每当网络发生更改时,BIRD 就可以检测到并将路由传播到其他节点。
Felix
Calico Felix 守护进程在 Calico 节点容器中运行, 并做了如下的操作:
- 从 Kubernetes etcd 读取信息
- 构建路由表
- 配置 IPTables (kube-proxy 模式 IPTables)
- 配置 IPVS (kube-proxy 模式 IPVS)
让我们看看具有所有 Calico 模块的集群,
看起来有点不一样? 是的,veth的一端悬空着,没有连接到任何地方;它在内核空间中。
数据包如何被路由到对等节点?
- master中的Pod试图ping 10.0.2.11
- Pod向网关发送ARP请求。
- 获取ARP响应及MAC地址。
- 等等,谁发送的ARP响应?
发生了什么?一个容器如何路由到一个不存在的IP地址?让我们来看看都发生了什么。你们中有些读者可能已经注意到169.254.1.1是一个IPv4链路本地地址。容器有一个默认路由指向一个链路本地地址。容器希望这个IP地址在其直接连接的接口上可达,在这种情况下,是容器的eth0地址。当容器希望通过默认路由发出时,它将尝试为该IP地址执行ARP。
如果我们捕获ARP响应,它将显示veth的另一端的MAC地址(cali123)。所以你可能想知道主机如何回复一个它没有IP接口的ARP请求。答案是代理ARP。如果我们检查主机端的VETH接口,我们会看到启用了代理ARP。
1 | master $ cat /proc/sys/net/ipv4/conf/cali123/proxy_arp |
“代理ARP是一种技术,由给定网络上的代理设备回答不在该网络上的IP地址的ARP查询。代理知道流量目标的位置,并提供自己的MAC地址作为(表面上是最后的)目标。1 然后通常由代理通过另一个接口或通过隧道将指向代理地址的流量路由到预期的目标。这种节点响应自己的MAC地址到ARP请求的过程中,是为了代理目的而发布不同IP地址,有时称为 publishing“
让我们仔细看看 worker 节点,
一旦数据包到达内核,它会根据路由表条目路由数据包。
入站流量
- 数据包到达工作节点内核。
- 内核将数据包放入cali123中。
路由模式
Calico支持3种路由模式;在本节中,我们将看到每种方法的优缺点以及使用方式。
- IP-in-IP:默认;封装
- Direct/NoEncapMode:无封装(首选)
- VXLAN: 封装(无BGP)
IP-in-IP(默认)
IP-in-IP是一种简单的封装形式,通过将一个IP数据包放入另一个IP数据包中实现。传输的数据包包含具有主机源和目标IP的外部头和具有容器源和目标IP的内部头。
据我所知,Azure不支持IP-IP;因此,我们无法在该环境中使用IP-IP。最好禁用IP-IP以获得更好的性能。
NoEncapMode
在此模式下,直接从pod发送数据包。由于没有封装和解封装开销,直接路由的性能非常高。
必须在AWS中禁用源IP检查才能使用此模式。
VXLAN
Calico 3.7+支持VXLAN路由。
VXLAN代表虚拟可扩展局域网。VXLAN是一种封装技术,其中二层以太网帧被封装在UDP数据包中。
VXLAN是一种网络虚拟化技术。 当设备在软件定义的数据中心内通信时,在这些设备之间建立VXLAN隧道。这些隧道可以在物理交换机和虚拟交换机上建立。交换机端口称为
VXLAN隧道端点(VTEP),负责 VXLAN数据包的封装和解封装。
不支持VXLAN的设备连接到具有VTEP功能的交换机上。交换机将提供到和从VXLAN的转换。
对于不支持IP-in-IP的网络(如Azure或任何其他不支持BGP的数据中心),VXLAN非常不错。
Demo — IPIP and UnEncapMode
在安装 Calico 之前检查集群状态。
1 | master $ kubectl get nodes |
检查CNI bin 和conf 目录。不需要有任何配置文件或 calico 二进制文件,因为 calico 安装将通过卷挂载来填充这些目录。
1 | master $ cd /etc/cni |
检查master/worker节点的IP路由。1
2
3
4
5master $ ip route
default via 172.17.0.1 dev ens3
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.32
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown
curl https://docs.projectcalico.org/manifests/calico.yaml -O
根据您的环境下载并应用 calico.yaml。1
2curl https://docs.projectcalico.org/manifests/calico.yaml -O
kubectl apply -f calico.yaml
让我们看一下一些有用的配置参数,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
39cni_network_config: |-
{
"name": "k8s-pod-network",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "calico", >>> Calico's CNI plugin
"log_level": "info",
"log_file_path": "/var/log/calico/cni/cni.log",
"datastore_type": "kubernetes",
"nodename": "__KUBERNETES_NODE_NAME__",
"mtu": __CNI_MTU__,
"ipam": {
"type": "calico-ipam" >>> Calico's IPAM instaed of default IPAM
},
"policy": {
"type": "k8s"
},
"kubernetes": {
"kubeconfig": "__KUBECONFIG_FILEPATH__"
}
},
{
"type": "portmap",
"snat": true,
"capabilities": {"portMappings": true}
},
{
"type": "bandwidth",
"capabilities": {"bandwidth": true}
}
]
}
# 启用IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Always" >> Set this to 'Never' to disable IP-IP
# 在默认 IP 池上启用或禁用 VXLAN。
- name: CALICO_IPV4POOL_VXLAN
value: "Never"
Calico 安装后检查 POD 和节点状态。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17master $ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-799fb94867-6qj77 0/1 ContainerCreating 0 21s
kube-system calico-node-bzttq 0/1 PodInitializing 0 21s
kube-system calico-node-r6bwj 0/1 PodInitializing 0 21s
kube-system coredns-66bff467f8-52tkd 0/1 Pending 0 7m5s
kube-system coredns-66bff467f8-g5gjb 0/1 ContainerCreating 0 7m5s
kube-system etcd-controlplane 1/1 Running 0 7m7s
kube-system kube-apiserver-controlplane 1/1 Running 0 7m7s
kube-system kube-controller-manager-controlplane 1/1 Running 0 7m7s
kube-system kube-proxy-b2j4x 1/1 Running 0 6m46s
kube-system kube-proxy-s46lv 1/1 Running 0 7m5s
kube-system kube-scheduler-controlplane 1/1 Running 0 7m6s
master $ kubectl get nodes
NAME STATUS ROLES AGE VERSION
controlplane Ready master 7m30s v1.18.0
node01 Ready <none> 6m59s v1.18.0
探索 CNI 配置,因为这正是 Kubelet 设置网络所需的。
1 | master $ cd /etc/cni/net.d/ |
检查 CNI 二进制文件,1
2
3master $ ls
bandwidth bridge calico calico-ipam dhcp flannel host-device host-local install ipvlan loopback macvlan portmap ptp sample tuning vlan
master $
让我们安装 calicoctl 来获取有关 calico 的详细信息,并修改 Calico 配置。
1 | master $ cd /usr/local/bin/ |
检查BGP对等体状态。这会将“工作”节点显示为对等节点。
1 | master $ calicoctl node status |
创建一个具有两个副本和主节点容忍度的 busybox POD。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
27cat > busybox.yaml <<"EOF"
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment
spec:
selector:
matchLabels:
app: busybox
replicas: 2
template:
metadata:
labels:
app: busybox
spec:
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
containers:
- name: busybox
image: busybox
command: ["sleep"]
args: ["10000"]
EOF
master $ kubectl apply -f busybox.yaml
deployment.apps/busybox-deployment created
获取 Pod 和端点状态,
1 | master $ kubectl get pods -o wide |
获取主节点busybox POD的主机端veth对等体的详细信息。1
2
3
4
5
6
7
8master $ ifconfig cali9861acf9f07
cali9861acf9f07: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1440
inet6 fe80::ecee:eeff:feee:eeee prefixlen 64 scopeid 0x20<link>
ether ee:ee:ee:ee:ee:ee txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 446 (446.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
获取master Pod接口的详细信息,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20master $ kubectl exec busybox-deployment-8c7dc8548-x6ljh -- ifconfig
eth0 Link encap:Ethernet HWaddr 92:7E:C4:15:B9:82
inet addr:192.168.49.66 Bcast:192.168.49.66 Mask:255.255.255.255
UP BROADCAST RUNNING MULTICAST MTU:1440 Metric:1
RX packets:5 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:446 (446.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
master $ kubectl exec busybox-deployment-8c7dc8548-x6ljh -- ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
master $ kubectl exec busybox-deployment-8c7dc8548-x6ljh -- arp
master $
获取主节点路由,1
2
3
4
5
6
7
8master $ ip route
default via 172.17.0.1 dev ens3
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.32
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown
blackhole 192.168.49.64/26 proto bird
192.168.49.65 dev calic22dbe57533 scope link
192.168.49.66 dev cali9861acf9f07 scope link
192.168.196.128/26 via 172.17.0.40 dev tunl0 proto bird onlink
我们尝试 ping 工作节点 Pod 以触发 ARP。1
2
3
4
5master $ kubectl exec busybox-deployment-8c7dc8548-x6ljh -- ping 192.168.196.131 -c 1
PING 192.168.196.131 (192.168.196.131): 56 data bytes
64 bytes from 192.168.196.131: seq=0 ttl=62 time=0.823 ms
master $ kubectl exec busybox-deployment-8c7dc8548-x6ljh -- arp
? (169.254.1.1) at ee:ee:ee:ee:ee:ee [ether] on et
网关的MAC地址正是 cali9861acf9f07。从现在开始,每当流量发出时,它将直接命中内核;
而且,基于IP路由,内核知道它必须将数据包写入tunl0。
代理ARP配置,1
2master $ cat /proc/sys/net/ipv4/conf/cali9861acf9f07/proxy_arp
1
目的节点如何处理数据包?1
2
3
4
5
6
7
8
9node01 $ ip route
default via 172.17.0.1 dev ens3
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.40
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown
192.168.49.64/26 via 172.17.0.32 dev tunl0 proto bird onlink
blackhole 192.168.196.128/26 proto bird
192.168.196.129 dev calid4f00d97cb5 scope link
192.168.196.130 dev cali257578b48b6 scope link
192.168.196.131 dev calib673e730d42 scope link
收到数据包后,内核根据路由表发送正确的 veth。
如果我们捕获数据包,我们可以看到线路上的 IP-IP 协议。 Azure不支持IP-IP(据我所知);因此,我们不能在该环境中使用IP-IP。最好禁用 IP-IP 以获得更好的性能。我们尝试禁用一下,看看效果如何。
禁用IP-IP
更新 ipPool 配置以禁用 IPIP。1
2master $ calicoctl get ippool default-ipv4-ippool -o yaml > ippool.yaml
master $ vi ippool.yaml
打开 ippool.yaml 并将 IPIP 设置为“从不”,然后通过 calicoctl 应用 yaml。1
2master $ calicoctl apply -f ippool.yaml
Successfully applied 1 'IPPool' resource(s)
重新检查IP路由,1
2
3
4
5
6
7
8master $ ip route
default via 172.17.0.1 dev ens3
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.32
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown
blackhole 192.168.49.64/26 proto bird
192.168.49.65 dev calic22dbe57533 scope link
192.168.49.66 dev cali9861acf9f07 scope link
192.168.196.128/26 via 172.17.0.40 dev ens3 proto bird
该设备不再是 tunl0;设置为主节点的管理界面。
让我们 ping 工作节点 POD 并确保一切正常。从现在开始,将不再涉及任何IPIP协议。1
2
3
4
5
6master $ kubectl exec busybox-deployment-8c7dc8548-x6ljh -- ping 192.168.196.131 -c 1
PING 192.168.196.131 (192.168.196.131): 56 data bytes
64 bytes from 192.168.196.131: seq=0 ttl=62 time=0.653 ms
--- 192.168.196.131 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.653/0.653/0.653 ms
注意:要使用此模式,应在 AWS 环境中禁用源 IP 检查。
Demo - XVLAN
重新启动集群并下载 calico.yaml 文件以应用以下更改,
- 从 livenessProbe 和 readinessProbe 中删除 bird
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15livenessProbe:
exec:
command:
- /bin/calico-node
- -felix-live
- -bird-live >> Remove this
periodSeconds: 10
initialDelaySeconds: 10
failureThreshold: 6
readinessProbe:
exec:
command:
- /bin/calico-node
- -felix-ready
- -bird-ready >> Remove this - 将 calico_backend 更改为“vxlan”,因为我们不再需要 BGP。3.禁用IPIP
1
2
3
4
5
6
7
8
9
10kind: ConfigMap
apiVersion: v1
metadata:
name: calico-config
namespace: kube-system
data:
# Typha is disabled.
typha_service_name: "none"
# Configure the backend to use.
calico_backend: "vxlan"让我们应用这个新的 yaml。1
2
3
4
5
6# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Never" >> Set this to 'Never' to disable IP-IP
# Enable or Disable VXLAN on the default IP pool.
- name: CALICO_IPV4POOL_VXLAN
value: "Never"获取 POD 状态,1
2
3
4
5
6
7
8
9
10
11
12
13
14
15master $ ip route
default via 172.17.0.1 dev ens3
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.15
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown
192.168.49.65 dev calif5cc38277c7 scope link
192.168.49.66 dev cali840c047460a scope link
192.168.196.128/26 via 192.168.196.128 dev vxlan.calico onlink
vxlan.calico: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1440
inet 192.168.196.128 netmask 255.255.255.255 broadcast 192.168.196.128
inet6 fe80::64aa:99ff:fe2f:dc24 prefixlen 64 scopeid 0x20<link>
ether 66:aa:99:2f:dc:24 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 11 overruns 0 carrier 0 collisions 0从以下位置 ping 工作节点 POD:1
2
3
4master $ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-deployment-8c7dc8548-8bxnw 1/1 Running 0 11s 192.168.49.67 controlplane <none> <none>
busybox-deployment-8c7dc8548-kmxst 1/1 Running 0 11s 192.168.196.130 node01 <none> <none>触发ARP请求,1
2
3master $ kubectl exec busybox-deployment-8c7dc8548-8bxnw -- ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link1
2
3
4
5
6
7
8master $ kubectl exec busybox-deployment-8c7dc8548-8bxnw -- arp
master $ kubectl exec busybox-deployment-8c7dc8548-8bxnw -- ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=116 time=3.786 ms
^C
master $ kubectl exec busybox-deployment-8c7dc8548-8bxnw -- arp
? (169.254.1.1) at ee:ee:ee:ee:ee:ee [ether] on eth0
master $
概念与前面的模式相同,但唯一的区别是数据包到达vxland,vxland将数据包与节点IP及其MAC封装在内部标头中并发送。
此外,vxlan 原型的 UDP 端口将为 4789。etcd 在此帮助获取可用节点及其支持的 IP 范围的详细信息,以便 vxlan-calico 可以构建数据包。
注意:VxLAN 模式比之前的模式需要更多的处理能力。
免责声明
本文不提供任何技术建议或推荐;如果你这么认为,这是我个人的观点,与我工作的公司无关。
引用
https://docs.projectcalico.org/
https://www.openstack.org/videos/summits/vancouver-2018/kubernetes-networking-with-calico-deep-dive
https://kubernetes.io/
https://www.ibm.com/support/knowledgecenter/en/SSBS6K_3.1.2/manage_network/calico.html
https://github.com/coreos/flannel