Kubernetes作为编排引擎管理着分布在不同节点上的容器和Pod。Pod、Service、外部组件之间需要⼀种可靠的方找到彼此并进行通信,Kubernetes网络则负责提供这个保障。

1.1 Kubernetes网络模型

  1. Container-to-Container的网络
    当Pod被调度到某个节点,Pod中的所有容器都在这个节点上运行,这些容器共享相同的本地问件系统、IPC和网络命名空间。不同Pod之间不存在端口冲突的问题,因为每个Pod都有自己的IP地址。当某个容器使用localhost时,意味着使用的是容器所属Pod的地址空间。


2. Pod之间的通信
Pod的IP是集群可见的,即集群中的任何其他Pod和节点都可以通过IP直接与Pod通信,这种通信不需要借助任何网络地址转换、隧道或代理技术。Pod内部和外部使用的是同⼀个IP,这也意味着标准的命名服务和发现机制,比如如DNS可以直接使用

数据包的传递:Pod-to-Pod,同节点
在 network namespace 将每一个 Pod 隔离到各自的网络堆栈的情况下,虚拟以太网设备(virtual Ethernet device)将每一个 namespace 连接到 root namespace,网桥将 namespace 又连接到一起,此时,Pod 可以向同一节点上的另一个 Pod 发送网络报文了。下图演示了同节点上,网络报文从一个Pod传递到另一个Pod的情况。

数据包的传递:Pod-to-Pod,跨节点
在了解了如何在同节点上 Pod 之间传递数据包之后,我们接下来看看如何在跨节点的 Pod 之间传递数据包。Kubernetes 网络模型要求 Pod 的 IP 在整个网络中都可访问,但是并不指定如何实现这一点。实际上,这是所使用网络插件相关的,但是,仍然有一些模式已经被确立了。

3. Pod与Service的通信
在Kubernetes集群中,Pod可能会频繁地销毁和创建,也就是说Pod的IP不是固定的。为了解决这个问题,Service提供了访问Pod的抽象层。无论后端的Pod如何变化,Service都作为稳定的前端对外提供服务。同时,Service还提供了高可用和负载均衡功能,Service负责将请求转发给正确的Pod。

  1. 外部访问
    无论是Pod的IP还是Service的Cluster IP,它们只能在Kubernetes集群中可见,对集群之外的世界,这些IP都是私有的。

Kubernetes提供了两种方式让外界能够与Pod通信:

  • NodePort。Service通过Cluster节点的静态端口对外提供服务。
    外部可以通过:访问Service。
  • LoadBalancer。Service利用cloudprovider提供的loadbalancer对外提供服务,cloudprovider负责将loadbalancer的流量导向Service。目前支持的cloudprovider有GCP、AWS、Azur等。

参考:kubernetes基本概念

1.2 各种网络方案

为了保证网络方案的标准化、扩展性和灵活性,K8S采用了CNI(Container Networking Interface)规范。CNI是一个Pod网络集成标准,简化了K8S和不同Pod网络实现技术的集成。

CNI最大的优点就是支持多种容器runtime,而不仅仅是Docker。目前已经有多种支持K8S的网络方案,包括 Flannel、Calico、Canal等,它们都实现了CNI规范,因此无论我们选择哪种具体方案,它们的网络模型都是一致的。

1.3 Network Policy

Network Policy是K8S的一种资源,它使K8S可以通过Label选择Pod,并指定其他Pod或外界如何与这些Pod通信。换句话说,当Pod被定义了Network Policy时,只有Policy允许的流量才能访问Pod(默认情况下,任何来源的流量都可以访问Pod,是没有限制的)即帮助K8S实现更为精细的流量控制,实现租户隔离机制。

但是,并不是所有K8S网络方案都支持Network Policy,比如Flannel就不支持,而Calico是支持的。

1.4 Network Policy实践

1.5 部署Canal

想要部署Canal,需要切换网络方案,这里我们使用最简单粗暴的方式:重建当前K8S集群

kubeadm reset # 在每个节点上执行一次

然后,重新对Master节点进行初始化:

kubeadm init \--apiserver-advertise-address=192.168.200.128 \--image-repository registry.aliyuncs.com/google_containers \--kubernetes-version v1.18.0 \--service-cidr=10.1.0.0/16 \--pod-network-cidr=10.244.0.0/16

在两个Node节点上执行以下命令重新加入集群:
kubeadm join 192.168.200.160:6443 –token ekqxk2.iiu5wx5bbnbdtxsw –discovery-token-ca-cert-hash
sha256:c50bb83d04f64f4a714b745f04682b27768c1298f331e697419451f3550f2d05

最后,通过以下命令部署Canal:
kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/canal.yaml

1.6 部署测试应用

这里通过一个httpd应用来演示Network Policy,该应用的yaml定义如下:

apiVersion: apps/v1kind: Deploymentmetadata:name: httpdspec:replicas: 3selector:matchLabels:name: networkpolicy-demotemplate:metadata:labels:name: networkpolicy-demospec:containers:- name: httpdimage: httpd:latestports:- containerPort: 80imagePullPolicy: IfNotPresent---kind: ServiceapiVersion: v1metadata:name: httpd-svcspec:type: NodePortports:- protocol: TCPnodePort: 33000port: 8080targetPort: 80selector:name: networkpolicy-demo

通过kubectl将其部署到K8S集群:

kubectl apply -f httpd-demo.yaml
[root@k8s-master ~]# kubectl get pod -o wideNAME READY STATUS RESTARTS AGE IP NODENOMINATED NODE READINESS GATEShttpd-595dc58589-c4hcv 1/1 Running52d2h10.244.1.157 k8s-node1 <none> <none>httpd-595dc58589-lxj9w 1/1 Running62d2h10.244.1.156 k8s-node1 <none> <none>httpd-595dc58589-v6wzw 1/1 Running11 4d20h 10.244.1.153 k8s-node1 <none> <none>

1.7 测试Network Policy有效性

现在我们创建一个Network Policy,其配置文件yaml如下:

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: access-httpdspec:podSelector:matchLabels:name: networkpolicy-demoingress:- from:- podSelector:matchLabels:access: "true"ports:- protocol: TCPport: 80

该Network Policy定义了如下规则:
  (1)应用于所有 label 为 name : networkpolicy-demo 的Pod,这里即刚刚创建的三个httpd pod。
  (2)ingress中定义了只有 label 为 access : “true” 的Pod才能访问应用。
  (3)即使通过Policy也只能访问80端口
通过kubectl将其应用到K8S集群中:

kubectl apply -f networkpolicy.yaml

下面再次在busybox pod中验证Network Policy的有效性:

[root@k8s-master ~]# kubectl run busvbox --rm -it --image=busybox bin/sh/ #wget httpd-svc:8080Connecting to httpd-svc:8080 (10.1.216.8:8080)wget: can't connect to remote host (10.1.216.8) Connection timed out

从上可以看到,已经无法再成功访问Service,也无法再ping通三个Pod节点。
这个时候,集群外也无法再通过NodePort访问到Service。
如果想要让测试Pod(busybox)能访问到应用了Network Policy的httpd应用,我们可以对busybox pod加一个label就可以:

[root@k8s-master ~]# kubectl run busybox --rm -it --image=busybox --labels="access=true" /bin/sh/ #wget httpd-svc:8080Connecting to httpd-svc:8080 (10.1.216.8:8080)saving to 'index.html'index.html 100% |**************************************************************************************************|450:00:00 ETA'index.html' saved

运行后的验证结果如下,可以访问到Service,但Ping却被禁止:

/ # ping -c 3 10.244.1.150PING 10.244.1.150 (10.244.1.150): 56 data bytes--- 10.244.1.150 ping statistics ---3 packets transmitted, 0 packets received, 100% packet loss

但是,此时集群节点(k8s-master与两个node)与集群仍然无法访问到应用了Network Policy的httpd应用,如果想要让它们也访问到,则需要修改Network Policy做一个类似于开防火墙白名单的操作(注意下面的ipBlock配置):

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: access-httpdspec:podSelector:matchLabels:name: networkpolicy-demoingress:- from:- podSelector:matchLabels:access: "true"- ipBlock:cidr: 192.168.200.0/24ports:- protocol: TCPport: 80

再次应用到K8S集群后,再来通过集群访问试试:

[root@k8s-master ~]# kubectl exec -it busvbox -- /bin/sh/ # ping -c 3 10.244.1.150PING 10.244.1.150 (10.244.1.150): 56 data bytes64 bytes from 10.244.1.150: seq=0 ttl=62 time=2.633 ms64 bytes from 10.244.1.150: seq=1 ttl=62 time=0.288 ms64 bytes from 10.244.1.150: seq=2 ttl=62 time=0.319 ms--- 10.244.1.150 ping statistics ---3 packets transmitted, 3 packets received, 0% packet lossround-trip min/avg/max = 0.288/1.080/2.633 ms