k8s之网络

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

  1. Kubernetes 网络模型
  2. 各种网络方案
  3. Network Policy
Kubernetes 网络模型

Kubernetes 采用的是基于扁平地址空间的网络模型,集群中的每个 Pod 都有自己的 IP 地址,Pod 之间不需要配置 NAT 就能直接通信。另外,同一个 Pod 中的容器共享 Pod 的 IP,能够通过 localhost 通信。

这种网络模型对应用开发者和管理员相当友好,应用可以非常方便地从传统网络迁移到 Kubernetes。每个 Pod 可被看作是一个个独立的系统,而 Pod 中的容器则可被看做同一系统中的不同进程。

下面讨论在这个网络模型下集群中的各种实体如何通信。知识点前面都已经涉及,这里可当做复习和总结。

Pod 内容器之间的通信

当 Pod 被调度到某个节点,Pod 中的所有容器都在这个节点上运行,这些容器共享相同的本地文件系统、IPC 和网络命名空间。

不同 Pod 之间不存在端口冲突的问题,因为每个 Pod 都有自己的 IP 地址。当某个容器使用 localhost 时,意味着使用的是容器所属 Pod 的地址空间。

比如 Pod A 有两个容器 container-A1 和 container-A2,container-A1 在端口 1234 上监听,当 container-A2 连接到 localhost:1234,实际上就是在访问 container-A1。这不会与同一个节点上的 Pod B 冲突,即使 Pod B 中的容器 container-B1 也在监听 1234 端口。

Pod 之间的通信

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

Pod 与 Service 的通信

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

外部访问

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

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

  1. NodePort
    Service 通过 Cluster 节点的静态端口对外提供服务。外部可以通过 <NodeIP>:<NodePort> 访问 Service。

  2. LoadBalancer
    Service 利用 cloud provider 提供的 load balancer 对外提供服务,cloud provider 负责将 load balancer 的流量导向 Service。目前支持的 cloud provider 有 GCP、AWS、Azur 等。
k8s网络方案

网络模型有了,如何实现呢?

为了保证网络方案的标准化、扩展性和灵活性,Kubernetes 采用了 Container Networking Interface(CNI)规范。

CNI 是由 CoreOS 提出的容器网络规范,它使用了插件(Plugin)模型创建容器的网络栈。

CNI 的优点是支持多种容器 runtime,不仅仅是 Docker。CNI 的插件模型支持不同组织和公司开发的第三方插件,这对运维人员来说很有吸引力,可以灵活选择适合的网络方案。

目前已有多种支持 Kubernetes 的网络方案,比如 Flannel、Calico、Canal、Weave Net 等。因为它们都实现了 CNI 规范,用户无论选择哪种方案,得到的网络模型都一样,即每个 Pod 都有独立的 IP,可以直接通信。区别在于不同方案的底层实现不同,有的采用基于 VxLAN 的 Overlay 实现,有的则是 Underlay,性能上有区别。再有就是是否支持 Network Policy。

Network Policy

Network Policy 是 Kubernetes 的一种资源。Network Policy 通过 Label 选择 Pod,并指定其他 Pod 或外界如何与这些 Pod 通信。

默认情况下,所有 Pod 是非隔离的,即任何来源的网络流量都能够访问 Pod,没有任何限制。当为 Pod 定义了 Network Policy,只有 Policy 允许的流量才能访问 Pod。

不过,不是所有的 Kubernetes 网络方案都支持 Network Policy。比如 Flannel 就不支持,Calico 是支持的。我们接下来将用 Canal 来演示 Network Policy。Canal 这个开源项目很有意思,它用 Flannel 实现 Kubernetes 集群网络,同时又用 Calico 实现 Network Policy。

部署 Canal

部署 Canal 与部署其他 Kubernetes 网络方案非常类似,都是在执行了 kubeadm init 初始化 Kubernetes 集群之后通过 kubectl apply 安装相应的网络方案。也就是说,没有太好的办法直接切换使用不同的网络方案,基本上只能重新创建集群。

要销毁当前集群,最简单的方法是在每个节点上执行 kubeadm reset。然后初始化集群。

kubeadm init --apiserver-advertise-address 192.168.56.105 --pod-network-cidr=10.244.0.0/16

然后按照文档 https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ 安装 Canal。文档列出了各种网络方案的安装方法:

执行如下命令部署 Canal

kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/canal.yaml

部署成功后,可以查看到 Canal 相关组件:

Canal 作为 DaemonSet 部署到每个节点,地属于 kube-system 这个 namespace。

Canal 准备就绪,下节我们将实践 Network Policy。

实践 Network Policy

为了演示 Network Policy,我们先部署一个 httpd 应用,其配置文件 httpd.yaml 为:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: httpd
spec:
  replicas: 3
  template:
    metadata:
      labels:
        run: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: httpd-svc
spec:
  type: NodePort
  selector:
    run: httpd
  ports:
  - protocol: TCP
    nodePort: 30000
    port: 8080
    targetPort: 80

httpd 有三个副本,通过 NodePort 类型的 Service 对外提供服务。部署应用:

[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
httpd-64554b6c67-8sx9d   1/1     Running   0          63s   10.244.2.3   k8s-node2   <none>           <none>
httpd-64554b6c67-csbzd   1/1     Running   0          63s   10.244.2.2   k8s-node2   <none>           <none>
httpd-64554b6c67-kkmdg   1/1     Running   0          63s   10.244.1.3   k8s-node1   <none>           <none>
[root@k8s-master ~]# kubectl get service httpd-svc 
NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
httpd-svc   NodePort   10.108.126.45   <none>        8080:30000/TCP   80s

当前没有定义任何 Network Policy,验证应用可以被访问:

启动一个 busybox Pod,可以访问 Service,也可以 Ping 到副本 Pod。

[root@k8s-master ~]# kubectl run busybox --rm -it --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget httpd-svc:8080
Connecting to httpd-svc:8080 (10.108.126.45:8080)
saving to 'index.html'
index.html           100% |**********************************************************************************************************|    45  0:00:00 ETA
'index.html' saved
/ # ping 10.244.2.3
PING 10.244.2.3 (10.244.2.3): 56 data bytes
64 bytes from 10.244.2.3: seq=0 ttl=62 time=1.316 ms
64 bytes from 10.244.2.3: seq=1 ttl=62 time=0.862 ms
64 bytes from 10.244.2.3: seq=2 ttl=62 time=1.204 ms
^C
--- 10.244.2.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.862/1.127/1.316 ms
/ # exit

集群节点可以访问 Service, 也可以 Ping 到副本 Pod。

[root@k8s-node1 ~]# curl 10.108.126.45:8080
<html><body><h1>It works!</h1></body></html>
[root@k8s-node1 ~]# ping -c 3 10.244.2.3
PING 10.244.2.3 (10.244.2.3) 56(84) bytes of data.
64 bytes from 10.244.2.3: icmp_seq=1 ttl=63 time=0.732 ms
64 bytes from 10.244.2.3: icmp_seq=2 ttl=63 time=0.824 ms
^C
--- 10.244.2.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.732/0.778/0.824/0.046 ms

集群外(192.168.56.1)可以访问 Service。

现在创建如下 Network Policy:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-httpd
spec:
  podSelector:
    matchLabels:
      run: httpd
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"
    ports:
    - protocol: TCP
      port: 80

① 定义将此 Network Policy 中的访问规则应用于 label 为 run: httpd 的 Pod,即 httpd 应用的三个副本 Pod。

② ingress 中定义只有 label 为 access: "true" 的 Pod 才能访问应用。

③ 只能访问 80 端口。

通过 kubectl apply 创建 Network Policy。

[root@k8s-master ~]# kubectl apply -f policy.yml
networkpolicy.networking.k8s.io/access-httpd created
[root@k8s-master ~]# kubectl get networkpolicies.
NAME           POD-SELECTOR   AGE
access-httpd   run=httpd      32s

验证 Network Policy 的有效性:

busybox Pod 已经不能访问 Service。

[root@k8s-master ~]# kubectl run busybox --rm -it  --image=busybox /bin/sh                      
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget httpd-svc:8080 --timeout=5
Connecting to httpd-svc:8080 (10.108.126.45:8080)
wget: download timed out
/ # exit

如果 Pod 添加了 label access: "true" 就能访问到应用,但 Ping 已经被禁止。

[root@k8s-master ~]# kubectl run busybox --rm -it --labels="access=true" --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget httpd-svc:8080
Connecting to httpd-svc:8080 (10.108.126.45:8080)
saving to 'index.html'
index.html           100% |**********************************************************************************************************|    45  0:00:00 ETA
'index.html' saved
/ # ping  10.244.2.3
PING 10.244.2.3 (10.244.2.3): 56 data bytes
^C
--- 10.244.2.3 ping statistics ---
68 packets transmitted, 0 packets received, 100% packet loss
/ # exit

集群节点已经不能访问 Service, 也 Ping 不到副本 Pod。

[root@k8s-node1 ~]# curl 10.108.126.45:8080
^C
[root@k8s-node1 ~]# ping -c 3 10.244.2.3   
PING 10.244.2.3 (10.244.2.3) 56(84) bytes of data.
^C
--- 10.244.2.3 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 1999ms

集群外(192.168.56.1)已经不能访问 Service。

[root@k8s-node1 ~]# curl 192.168.77.10:30000 --connect-timeout 5
curl: (28) Connection timed out after 5001 milliseconds

如果希望让集群节点和集群外(192.168.56.1)也能够访问到应用,可以对 Network Policy 做如下修改:

应用新的 Network Policy:

现在,集群节点和集群外(192.168.56.1)已经能够访问了:

除了通过 ingress 限制进入的流量,也可以用 egress 限制外出的流量。大家可以参考官网相关文档和示例,这里就不赘述了。

小结
Kubernetes 采用的是扁平化的网络模型,每个 Pod 都有自己的 IP,并且可以直接通信。

CNI 规范使得 Kubernetes 可以灵活选择多种 Plugin 实现集群网络。

Network Policy 则赋予了 Kubernetes 强大的网络访问控制机制。

猜你喜欢

转载自blog.51cto.com/5157495/2443095