4.3 暴露容器服务
Pod对象的IP地址仅在集群内可达,它们无法直接接收来自集群外部客户端的请求流量,尽管它们的服务可达性不受工作节点边界的约束,但依然受制于集群边界。不考虑通过Service资源进行服务暴露的情况下,服务于集群外部的客户端的常用方式有两种:一种是在其运行的节点上进行端口映射,由节点IP和选定的协议端口向Pod内的应用容器进行DNAT转发;另一种是让Pod共享其所在的工作节点的网络名称空间,应用进程将直接监听工作节点IP地址和协议端口。
4.3.1 其他容器端口映射
其他Kubernetes系统的网络模型中,各Pod的IP地址处于同一网络平面,无论是否为容器指定了要暴露的端口都不会影响集群中其他节点之上的Pod客户端对其进行访问,这意味着,任何在非本地回环接口lo上监听的端口都可直接通过Pod网络被请求。从这个角度来说,容器端口只是信息性数据,它仅为集群用户提供了一个快速了解相关Pod对象的可访问端口的途径,但显式指定容器的服务端口可额外为其赋予一个名称以方便按名称调用。定义容器端口的ports字段的值是一个列表,由一到多个端口对象组成,它的常用嵌套字段有如下几个。
▪containerPort <integer>:必选字段,指定在Pod对象的IP地址上暴露的容器端口,有效范围为(0,65536);使用时,需要指定为容器应用程序需要监听的端口。
▪name <string>:当前端口的名称标识,必须符合IANA_SVC_NAME规范且在当前Pod内要具有唯一性;此端口名可被Service资源按名调用。
▪protocol <string>:端口相关的协议,其值仅支持TCP、SCTP或UDP三者之一,默认为TCP。
需要借助于Pod所在节点将容器服务暴露至集群外部时,还需要使用hostIP与hostPort两个字段来指定占用的工作节点地址和端口。如图4-11所示的Pod A与Pod C可分别通过各自所在节点上指定的hostIP和hostPort服务于客户端请求。
▪hostPort <integer>:主机端口,它将接收到的请求通过NAT机制转发至由container-Port字段指定的容器端口。
▪hostIP <string>:主机端口要绑定的主机IP,默认为主机之上所有可用的IP地址;该字段通常使用默认值。
下面的资源配置清单示例(pod-using-hostport.yaml)中定义的demo容器指定了要暴露容器上TCP协议的80端口,并将之命名为http,该容器可通过工作节点的10080端口接入集群外部客户端的请求。
apiVersion: v1 kind: Pod metadata: name: pod-using-hostport namespace: default spec: containers: - name: demo image: ikubernetes/demoapp:v1.0 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 protocol: TCP hostPort: 10080
在集群中创建配置清单中定义的Pod对象后,需获取其被调度至的目标节点,例如下面第二个命令结果中的k8s-node02.ilinux.io/172.29.9.12,而后从集群外部向该节点的10080端口发起Web请求进行访问测试:
~$ kubectl apply -f pod-using-hostport.yaml pod/pod-using-hostport ~$ kubectl describe pods/ pod-using-hostport | grep "^Node:" Node: k8s-node02.ilinux.io/172.29.9.12 ~$ curl 172.29.9.12:10080 iKubernetes demoapp v1.0 !! ClientIP: 172.29.0.1, ServerName: pod-using-hostport, ServerIP: 10.244.2.9!
注意,hostPort与NodePort类型的Service对象暴露端口的方式不同,NodePort是通过所有节点暴露容器服务,而hostPort则能经由Pod对象所在节点的IP地址进行。但Pod对象绑定的工作节点都由调度器根据其调度机制确定,除非人为地指示调度器将其绑定到指定的工作节点,否则多数情况下其目标节点都难以预测。
4.3.2 配置Pod使用节点网络
同一个Pod对象的各容器运行于一个独立、隔离的Network、UTS和IPC名称空间中,共享同一个网络协议栈及相关的网络设备,但也有些特殊的Pod对象需要运行于所在节点的名称空间中,执行系统级的管理任务(例如查看和操作节点的网络资源甚至是网络设备等),或借助节点网络资源向集群外客户端提供服务等,如图4-12中的右图所示。
由kubeadm部署的Kubernetes集群中的kube-apiserver、kube-controller-manager、kube-scheduler,以及kube-proxy和kube-flannel等通常都是第二种类型的Pod对象。网络名称空间是Pod级别的属性,用户配置的Pod对象,仅需要设置其spec.hostNetwork的属性为true即可创建共享节点网络名称空间的Pod对象,如下面保存在pod-using-hostnetwork.yaml文件中的配置清单所示。
apiVersion: v1 kind: Pod metadata: name: pod-using-hostnetwork namespace: default spec: containers: - name: demo image: ikubernetes/demoapp:v1.0 imagePullPolicy: IfNotPresent hostNetwork: true
将上面配置清单中定义的pod-using-hostnetwork创建于集群上,并查看主机名称或网络接口的相关属性信息以验证它是否能共享使用工作节点的网络名称空间。
~$ kubectl apply -f pod-using-hostnetwork.yaml pod/pod-using-hostnetwork created ~$ kubectl exec -it pod-using-hostnetwork -- hostname k8s-node01.ilinux.io
上面第二个命令的结果显示出的主机名称,表示该Pod已然共享了其所在节点的UTS名称空间,以及Network和IPC名称空间。这意味着,Pod对象中运行容器化应用可在其所在的工作节点的IP地址之上监听,这可以通过直接向k8s-node01.ilinux.io节点发起请求来验证。
~$ curl k8s-node01.ilinux.io iKubernetes demoapp v1.0 !! ClientIP: 172.29.9.1, ServerName: k8s-node01.ilinux.io, ServerIP: 172.29.9.11!
与容器端口映射存在的同样问题是,用户无法事先预知Pod对象会调度至哪个节点,除非事先指示调度器将Pod绑定至固定的目标节点之上。