Kubernetes作为编排引擎管理着分布在不同节点上的容器和Pod。Pod、Service、外部组件之间需要⼀种可靠的方找到彼此并进行通信,Kubernetes网络则负责提供这个保障。
1.1 Kubernetes网络模型
- 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。
- 外部访问
无论是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/v1
kind: Deployment
metadata:
name: httpd
spec:
replicas: 3
selector:
matchLabels:
name: networkpolicy-demo
template:
metadata:
labels:
name: networkpolicy-demo
spec:
containers:
- name: httpd
image: httpd:latest
ports:
- containerPort: 80
imagePullPolicy: IfNotPresent
---
kind: Service
apiVersion: v1
metadata:
name: httpd-svc
spec:
type: NodePort
ports:
- protocol: TCP
nodePort: 33000
port: 8080
targetPort: 80
selector:
name: networkpolicy-demo
通过kubectl将其部署到K8S集群:
kubectl apply -f httpd-demo.yaml
[root@k8s-master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpd-595dc58589-c4hcv 1/1 Running 5 2d2h 10.244.1.157 k8s-node1 <none> <none>
httpd-595dc58589-lxj9w 1/1 Running 6 2d2h 10.244.1.156 k8s-node1 <none> <none>
httpd-595dc58589-v6wzw 1/1 Running 11 4d20h 10.244.1.153 k8s-node1 <none> <none>
1.7 测试Network Policy有效性
现在我们创建一个Network Policy,其配置文件yaml如下:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: access-httpd
spec:
podSelector:
matchLabels:
name: networkpolicy-demo
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
ports:
- protocol: TCP
port: 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:8080
Connecting 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:8080
Connecting to httpd-svc:8080 (10.1.216.8:8080)
saving to 'index.html'
index.html 100% |**************************************************************************************************| 45 0:00:00 ETA
'index.html' saved
运行后的验证结果如下,可以访问到Service,但Ping却被禁止:
/ # ping -c 3 10.244.1.150
PING 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/v1
kind: NetworkPolicy
metadata:
name: access-httpd
spec:
podSelector:
matchLabels:
name: networkpolicy-demo
ingress:
- from:
- podSelector:
matchLabels:
access: "true"
- ipBlock:
cidr: 192.168.200.0/24
ports:
- protocol: TCP
port: 80
再次应用到K8S集群后,再来通过集群访问试试:
[root@k8s-master ~]# kubectl exec -it busvbox -- /bin/sh
/ # ping -c 3 10.244.1.150
PING 10.244.1.150 (10.244.1.150): 56 data bytes
64 bytes from 10.244.1.150: seq=0 ttl=62 time=2.633 ms
64 bytes from 10.244.1.150: seq=1 ttl=62 time=0.288 ms
64 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 loss
round-trip min/avg/max = 0.288/1.080/2.633 ms