k8s不提供网络通信,提供了CNI接口(Container Network Interface,容器网络接口),由CNI插件实现完成。
Pod通过虚拟Ethernet接口对(Veth Pair)与外部通信,Veth像一根网线,一端在Pod内部,一端在Pod外部。同一节点内的Pod通过网桥通信,如下图:veth的另一端连接到网桥,IP地址通过网桥动态获取,与网桥在同一网段,网桥负责转发所有非本地的流量,实现Pod内的互相访问。
k8s集群内不同节点访问Pod,需要保证Pod ip唯一,所以会给每个节点分配不同的IP,Pod通过网桥获取ip,从而保证Pod IP唯一。在不同节点Pod通过IP互相访问,网络插件大致分成三类:
Overlay模式、路由模式、Underlay模式三类。
1、Pod可能被Deployment重建删除,造成Pod的Ip变化。
2、Pod的Ip只有在服务启动后才会被分配,无法预知。
3、一个镜像最后会变成多个Pod,Pod访问需要随机策略。
举个例子,假设有这样一个应用程序,使用Deployment创建了前台和后台,前台会调用后台做一些计算处理,如图1所示。前台一个Pod,后台运行了3个Pod,这些Pod是相互独立且可被替换的,当Pod出现状况被重建时,新建的Pod的IP地址是新IP,前台的Pod无法直接感知。
k8s的Service就是为了解决Pod访问的问题而设计的。Service有一个固定的IP,Service流量转发给Pod,具体转发给哪些Pod由Label标签选择。Service可以给这些Pod做负载均衡。
添加Service后访问后,Pod访问如下图:
创建一个3副本的Deployment,即3个Pod,且Pod上带有标签“app: nginx”,具体如下所示。
创建一个名为“nginx”的Service,通过selector选择到标签“app:nginx”的Pod,目标Pod的端口为80,Service对外暴露的端口为8080。
访问服务只需要通过“服务名称:对外暴露的端口”接口,对应本例即“nginx:8080”。这样,在其他Pod中,只需要通过“nginx:8080”就可以访问到“nginx”关联的Pod。
执行创建指令
下面创建一个Pod并进入容器,使用ClusterIP访问Pod,可以看到能直接返回内容。
在创建CCE集群的时候,会默认要求安装CoreDNS插件,在kube-system命名空间下可以查看到CoreDNS的Pod。CoreDNS安装成功后会成为DNS服务器,当创建Service后,CoreDNS会记录Service的名称与IP,这样Pod就可以通过向CoreDNS查询Service名称获得Service的IP地址。
访问时通过nginx.
例如上面创建的名为nginx的Service,直接通过“nginx:8080”就可以访问到Service,进而访问后台Pod。
kubectl describe命令查看Service的信息。
可以看到一个Endpoints,Endpoints同样也是Kubernetes的一种资源对象,可以查询得到。Kubernetes正是通过Endpoints监控到Pod的IP,从而让Service能够发现Pod。
172.16.2.132:80是Pod的IP:Port,通过如下命令可以查看到Pod的IP,与上面的IP一致。
如果删除一个Pod,Deployment会将Pod重建,新的Pod IP会发生变化。
再次查看Endpoints,会发现Endpoints的内容随着Pod发生了变化。
下面进一步了解这又是如何实现的。
Service的访问都是通过kube-proxy实现的,当一个Service创建时,API Server会通知所有的kube-proxy,kube-proxy将Service的ip和端口记录到iptables内,当节点内的Pod访问其他节点内的Pod时,通过iptable将目的地的ip:pord替换为真实的ip:pord,从而实现Service访问到Pod。kube-proxy还会记录Serivce和Endpoints的ip变化
Service的类型有:ClusterIP、NodePort、LoadBalancer和Headless Service,这几种类型的Service有着不同的用途。
NodePort类型的Service可以让Kubernetes集群每个节点上保留一个相同的端口, 外部访问连接首先访问节点IP:Port,然后将这些连接转发给服务对应的Pod。如下图所示。
创建一个NodePord类型Service的yaml
创建后查看Pod列映射
此时,通过节点IP:端口访问Service可以访问到Pod,如下所示。
LoadBalancer是NodePort的扩展。通过LB访问Service,LB将请求转发到NodePort。
下面是一个创建LoadBalancer类型的Service。可以通过ELB的IP:Port访问到后台Pod。
HeadLess Service解决了如下两个问题:
Headless Service并不会分配单独的ClusterIP,在进行DNS查询时会返回所有Pod的DNS记录,这样就可查询到每个Pod的IP地址。有状态负载(StatefulSet)中StatefulSet正是使用Headless Service解决Pod间互相访问的问题。
创建完成后可以查询Service。
# kubectl create -f headless.yaml
service/nginx-headless created# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-headless ClusterIP None80/TCP 5s
创建一个Pod来查询DNS,可以看到能返回所有Pod的记录,这就解决了访问所有Pod的问题了。
Service是基于四层TCP和UDP协议转发的,而Ingress可以基于七层的HTTP和HTTPS协议转发,可以通过域名和路径做到更细粒度的划分,如下图所示。
要想使用Ingress功能,必须在Kubernetes集群上安装Ingress Controller。Ingress Controller有很多种实现,最常见的就是Kubernetes官方维护的NGINX Ingress Controller;
外部请求首先到达Ingress Controller,Ingress Controller根据Ingress的路由规则,查找到对应的Service,进而通过Endpoint查询到Pod的IP地址,然后将请求转发给Pod。
Ingress中还可以设置外部域名,这样您就可以通过域名来访问到ELB,进而访问到后端服务。
Ingress可以同时路由到多个服务,配置如下所示。