# 三层网络实现
# 字典
下一跳:如果ip包从主机A发送到主机B,需要经过路由设备X的中转,那么X的IP地址就应该配置为主机A的下一跳地址。
Host-gw模式:将每个 Flannel 子网(Flannel Subnet,比如10.244.1.0/24)的下一跳 ,设置成该子网对应宿主机的IP地址。也就是主机(host)充当这条容器通信路径里的网关(gateway),也就是host-gw的含义;无封装,纯路由,只经过协议栈一次,性能较高
边际网关协议:BGP,Border Gateway Protocol,大规模数据中心维护不同“自治系统”之间路由信息、无中心的路由协议; 它不会在宿主上创建任何网桥设备
相当于:每个边界网关上运行着一个小程序,它们会将各自的路由表信息,通过TCP传输给其它边界网关。其它边界网关上的这个小程序,则会对收到的这些数据进行分析,然后把需要的信息添加到自己的路由表。
- CNI:与k8s对接的部分
- Felix:DeamonSet,wmgm宿主机上插入路由规则,即写入Linux内核的FIB转发信息库,以及维护Calico所需要的网络设备等工作
- BIRD:BGP客户端,专门负责在集群里分发路由信息
边际网关:负责把自治系统连接在一起的路由器。它的路由表中有其它自治系统里的主机路由信息;
Calico:集群中所有的节点,都是边界路由器,被称为BGP Peer;默认情况下,是一个Node-to-Node Mesh的模式。随着节点的增多,会以N^2的规模快速增长。一般推荐在少于100个节点的集群中
Calico Route Reflector的模式,大集群,它指定一个或几个专门节点,来负责与所有节点建立BGP连接,从而学习全局路由规则,其它节点,只需要给它交换路由信息。
Calico IPIP模式:添加的路由规则
10.233.2.0/24 via 192.168.2.2 tunl0
,下一跳地址是Node2 IP地址,但发出去包的设备是tunl0(注意,不是flannel UDP模式的tun0,它们的功能是不同的)它是一个IP隧道设备(IP tunnel),IP包进入IP隧道后,被内核IPIP驱动接管。它会将这个IP包直接封装在一个宿主机网络的Ip包中。
Calico IPIP模式:

Flannel Host-gw模式

$ ip route
...
10.244.1.0/24 via 10.168.0.3 dev eth0
2
3
目的地址属于10.244.1.0/24网段的IP包,应该经过本机的eth0设备(dev eth0)设备发出去,并且它的下一跳(next-lop)是10.168.0.3(via 10.168.0.3)
从示意图上看,下一跳地址是目的宿主机Node2,所以IP包从应用层到链路层被封装成帆时,etho会使用下一跳地址对应的MAC地址,做为数据帧的MAC地址。
Node 2的内核网络从二层拿到IP包,就会看到这个目的IP地址:10.244.1.3,根据Node 2的路由表,会匹配到第二条路由规则,从而进入cni0网桥,进入infra-containeere-2当中。
Flannel子网和主机的信息,都是保存在Etcd中的。flanneld只需要wacth这些数据变化,然后实时更新路由表则可。
性能损失在10%左右,其它基于VXLAN隧道机制的网络方案,性能损失在20%~30%左右。
Flannel host-gw限制,要求宿主机必须是二层连通;必须有其它宿主机的路由信息;
# 36 为什么k8s只有soft multi-tenancy
# 字典:
NetworkPolicy:宿主机上的一系统iptables规则,与传统Iaas里面的安全组其实是类似的;注意定义的方式是OR还是AND,Flannel不支持NetworkPolicy
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: # 定义限制范围 matchLabels: role: db policyTypes: - Ingress - Egress ingress: - from: # 允许注入的“白名单” - ipBlock: # 定义限制范围 cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: # 定义限制范围 matchLabels: project: myproject - podSelector: # 定义限制范围 matchLabels: role: frontend ports: # 允许注入的端口 - protocol: TCP port: 6379 egress: - to: # 允许流出的“白名单”和端口 - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978
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
34iptables:操作Linux内核Netfilter子系统的界面,挡在网卡与用户态进程之间的一道“防火墙”
- 第一种:继续本机处理
- 第二种 :被转发到其它目的地
# 同一台宿主机上容器之间经过CNI网桥进行通信的注入数据包,-physdev-js-bridged意思是,这个FORWARD链匹配的是,通过本机上的网桥设备,发往目的地址podIP的IP包 iptables -A FORWARD -d $podIP -m physdev --physdev-js-bridged -j KUBE-POD-SPEC-FW-CHAIN # 容器间跨主通信,注入容器的数据包,都是经过路由(FORWARD检查点)来的 iptables -A FORWARD -d $podIP -j KUBE-POD-SPECIFIC-FW-CHAIN
1
2
3
4iptables -A KUBE-POD-SPECIFIC-FW-CHAIN -j KUBE-NWPLCY-CHAIN iptables -A KUBE-POD-SPECIFIC-FW-CHAIN -j REJECT --reject-with icmp-port-unreachable
1
2k8s定位:基础设施与Paas之间的中间层。容器本质上就是进程的抽象粒度;
Paas: platform as a service,平台即服务,把服务器平台做为一种服务提供的商业模式
saas: Software as a service ,通过网络运行程序提供的服务叫says
Iaas: Infrastructure as a Service,基础设备即服务
# 37 找到容器
# 字典:
Service:由kube-proxy组件,加上iptables共同实现的 原因:
- Pod的ip不是固定的
- Pod实例间总有负载均衡的需求
apiVersion: v1 kind: Service metadata: name: hostnames spec: selector: app: hostnames ports: - name: default protocol: TCP port: 80 targetPort: 9376 # 被service选中的pod,就称为service的Endpoints,可使用kubectl get endpoints hostnames 查看 # 只有readinessProbe检查通过的Pod,才会出现在Endpoints列表中,出问题后会自动摘除掉 --- apiVersion: apps/v1 kind: Deployment metadata: name: hostnames spec: selector: matchLabels: app: hostnames replicas: 3 template: metadata: labels: app: hostnames spec: containers: - name: hostnames image: k8s.gcr.io/serve_hostname ports: - containerPort: 9376 protocol: TCP
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访问
kubectl get sec hostnames
就可以访问到它代理的pod了,vip是k8s自动为Service分 配的 ,这 种 模式是 ClusterIP 模式的Service$ kubectl get svc hostnamesNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEhostnames ClusterIP 10.0.1.175 80/TCP 5s $ curl 10.0.1.175:80 hostnames-0uton $ curl 10.0.1.175:80 hostnames-yp2kp $ curl 10.0.1.175:80 hostnames-bvc05 # 添加的iptables规则 -A KUBE-SERVICES -d 10.0.1.175/32 -p tcp -m comment --comment "default/hostname: cluster IP" -m tcp --dport 80 -j KUBE-SVC-NWV5X2332I40T4T3 # 规则的集合,随机(--mode random)模式的iptables链 -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-WNBA2IHDGP2BOBGZ -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-X3P2623AGDH6CDF3 -A KUBE-SVC-NWV5X2332I4OT4T3 -m comment --comment "default/hostnames:" -j KUBE-SEP-57KPRZ3JQVENLNBR # 三条DNAT规则。在规则前对注入的 IP包设置了一个标志(--set-xmark),作用是在路由之ueej,将注入IP包的目的地址和端口,改成-to-destination指定的新的目的地址和端口。 -A KUBE-SEP-57KPRZ3JQVENLNBR -s 10.244.3.6/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000 -A KUBE-SEP-57KPRZ3JQVENLNBR -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.3.6:9376 -A KUBE-SEP-WNBA2IHDGP2BOBGZ -s 10.244.1.7/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000 -A KUBE-SEP-WNBA2IHDGP2BOBGZ -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.1.7:9376 -A KUBE-SEP-X3P2623AGDH6CDF3 -s 10.244.2.3/32 -m comment --comment "default/hostnames:" -j MARK --set-xmark 0x00004000/0x00004000 -A KUBE-SEP-X3P2623AGDH6CDF3 -p tcp -m comment --comment "default/hostnames:" -m tcp -j DNAT --to-destination 10.244.2.3:9376
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
28iptable规则相当多,而且还需要在控制循环里不断刷新这些规则,保证它们是正确的;基于iptables的Service实现是制约k8s承载更多量级的Pod的主要障碍
ClusterIP 模式:它的A记录的格式是
..svc.cluster.local
当访问这个A记录时,解析到的是这个Service的VIP地址Headless Service:clusterIP=None,A记录也是,但是,访问时返回的是Pod的IP 地址的集合。如果本身声明了hostname和subdomain字段,A记录变 为
<pod的hostname>...svc.clustere.local
比如:apiVersion: v1 kind: Service metadata: name: defalt-subdomain spec: selector: name: busybox clusterIP: None ports: - name: foo port: 1234 targetPort: 1234 --- apiVersion: v1 kind: Pod metadata: name: busybox1 labels: name: busybox spec: hostname; busybox-1 subdomain: default-subdomain containers: - image: busybox command: - sleep - 3600 name: busybox
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创建后可用:
buusybox-1.default-subdomain.default.svc.clusteer.local
解析到pod的析ip地址Kube-proxy IPVS模式:创建Service后,会在宿主机上创建一个虚拟网卡,kube-ipvs0,并为它分配Service VIP做为IP地址。
# ip addr ... 73:kube-ipvs0: mtu 1500 qdisc noop state DOWN qlen 1000 link/ether 1a:ce:f5:5f:c1:4d brd ff:ff:ff:ff:ff:ff inet 10.0.1.175/32 scope global kube-ipvs0 valid_lft forever preferred_lft forever
1
2
3通过linux的ipvs模块,为IP地址设置三个IPVS虚拟主机,并设置 它们之间通过轮询模式作为负载均衡策略
# ipvsadm -ln IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.102.128.4:80 rr -> 10.244.3.6:9376 Masq 1 0 0 -> 10.244.1.7:9376 Masq 1 0 0 -> 10.244.2.3:9376 Masq 1 0 0
1
2
3
4
5
6
7
8实现:基于Netfilter的Nat模式,转发上没有特别的性能提升。不需要在宿主机上设置iptables规则,而把它们的处理放到了内核态,降低了维护规则的代价。“将重要操作放入内核态”
# 连通Service与调试
# 字典
访问入口
- 每台宿主机上kube-proxy生成的iptables规则
- kube-dns生成的DNS记录
NodePort
apiVersion: v1 kind: Service metadata: name: my-nginx labels: run: my-nginx spec: type: nodePort ports: # Service的8080代理Pod的80端口,任何一台宿主机IP+8080,就可以访问到这个服务 - nodePort: 8080 targetPort: 80 protocol: TCP name: http - nodePort: 443 targetPort: 443 protocol: TCP name: https selector; run: my-nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20LoadBalancer:在CloudProvider里创建一个与该Service对应的负载均衡服务
ExternalName
# Service与Ingress
# 字典:
Ingress:全局的、为了代理不同后端Sercie而设置的负载均衡服务
apiVersion: extensions/v1beat1 kind: Ingress metadata: name: cafe-ingress spec: tls: - hosts: # 标准域名格式,不能是IP地址 - cafe.example.com secretName: cafe-secret rules: - hosts: cafe.example.com http: - path: /tea backend: serviceName: tea-svc servicePort: 80 - path: /caffe backend: serviceName: caffee-svc sercvicePort: 80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21站点:
- https://cafe.example.com
- https://cafe.example.com/tea -> tea - > deploment
- https://cafe.example.com/caffee caffee -> deploment
Ingress对象:k8s项目对反向代理的一种抽象
Ingress Controller:目前支持Nginx、Haproxy、Envoy、Traefik等;nginx-ingress-controller只是被代理的Serive对象被更新,是不需要重新加载的,因为它的Lua方案实现了Nginx Upstream的动态配置。
nginx-igress-service:暴露服务出去,如果是公有云,需要创建LoadBalancer类型的Service了。
default-backend-service:默认规则
Ingress只能工作在7层,而Service只能工作在四层
查看nginx配置
kubectl get pods -n ingress-nginx
kubectl exec nginx-ingress-controller-xxxx -it --namespace="ingress-nginx" --bash
cat /etc/nginx/nginx.conf
2
3
# 从集群外访问到服务
- Service工作原理:通过kube-proxy设置宿主机iptables规则来实现,kube-proxy通过观察service的创建,然后通过修改本机的iptables规则,将访问service vip的请求转发到真实的Pod上
- 基于iptables规则的service的实现,宿主机上有大量Pod时,规则的不断刷新占用大量CPU资源,新的模式:ipvs,通过把规则放到内核态,降低了维护规则的代价
- Service的DNS记录:
<myservice>.<mynamespace>.svc.cluster.local
,访问这条记录时,返回的是Service的VIP或代理Pod的IP地址集合 - Pod的DNS记录:
<pod_hostname>.<subdomain>.<mynamespace>.svc.cluster.local
,注意pod的hostname和subdomain都是在Pod中定义的 - 外部宿主机访问
- NodePort:外部client访问任意一台宿主机的8080端口,就是访问Servicer所代理的Pod的80端口,由接收外部请示请求的宿主机做转发,即
client -> nodeIP:nodePort -> serviceVIP:port -> podIp:targetIp
- LoadBalance:公有云提供的k8s服务自带的loadbalancer做负载均衡和外部流量 的入口
- ExternalName:通过ExternalName或ExternalIp给Service挂在一个公有IP或者域名,当访问这个公有IP地址时,就会转发到Service所代理的Pod服务上。类似于软链或快捷方式
- ClusterIP:虚拟IP地址,外部网络无法访问,只有k8s内部访问使用。更像是一个伪造的IP网络
- 仅仅用于Service这个对象,并由k8s管理和分配IP地址
- 无法被Ping通,没有一个“实体网络对象”来响应
- 只能结合Service Port组成一个具体的通信端口,单独的ClusterIP不具备通信的基础,并且它们属于k8s集群这样一个封闭的空间
- 不同Service下的Pod节点在集群间相互访问可以通过ClusterIP
- NodePort:外部client访问任意一台宿主机的8080端口,就是访问Servicer所代理的Pod的80端口,由接收外部请示请求的宿主机做转发,即
# 32 容器网络

# 字典
- 覆盖网络(Overlay Network)