赞
踩
- ########service###########
- apiVersion: v1
- kind: Service
- metadata:
- labels:
- name: app1
- name: app1
- namespace: default
- spec:
- type: NodePort
- ports:
- - <strong>port: 8080
- targetPort: 8080
- nodePort: 30062</strong>
- selector:
- name: app1
-
- ########deployment###########
-
- apiVersion: apps/v1
- kind: Deployment
- ......
- spec:
- ......
- template:
- .......
- spec:
- .......
- containers:
- ports:
- - containerPort: 8080
- name: http
- protocol: TCP
- - containerPort: 8443
- name: https
- protocol: TCP
- - containerPort: 12200
- name: rpc
- protocol: TCP
外部机器可访问的端口,一般为node机器上开放的端口。 场景案例如下:
nodePort是kubernetes提供给集群外部客户访问service入口的一种方式(另一种方式是LoadBalancer),所以,<nodeIP>:nodePort 是提供给集群外部客户访问service的入口。
kubernetes中的服务之间访问的端口,service暴露在cluster ip上的端口,<cluster ip>:port 是提供给集群内部客户访问service的入口。尽管mysql容器暴露了3306端口(参考 https://github.com/docker-library/mysql/的DockerFile),但是集群内其他容器需要通过33306端口访问该服务,外部机器不能访问mysql服务,因为没有配置NodePort类型。
容器的端口(最根本的端口入口),与制作容器时暴露的端口一致(DockerFile中EXPOSE),例如docker.io官方的nginx暴露的是80端口。
docker.io官方的nginx容器的DockerFile参考 https://github.com/nginxinc/docker-nginx。
targetPort是pod上的端口,从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。
containerPort是在pod控制器中定义的、pod中的容器需要暴露的端口。
targetPort和containerPort必须一致。
port和nodePort都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod上的容器内。
在 Kubernetes 中,hostPort 是一种用于将主机上的特定端口映射到运行在 Pod 内部容器的端口的配置选项。通过使用 hostPort,你可以在主机上暴露容器的服务,从而允许外部网络通过主机的 IP 地址和指定的端口访问容器内的应用程序。如:
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: tomcat-v1-deployment
- spec:
- replicas: 2
- selector:
- matchLabels:
- app: tomcat-v1
- template:
- metadata:
- labels:
- app: tomcat-v1
- spec:
- containers:
- - name: tomcat
- image: tomcat
- ports:
- - containerPort: 8080
- hostPort: 9000
一个 Node 只能启动一个 hostPort,所以最初是用于把守护进程集(DaemonSets)部署到每个 Node (确保一个 Node 只有一个 hostPort )。如下图所示,3个 Node 上部署4个带 hostPort 的 Pod,会有一个不成功。
即便是3个 Node 上部署3个带 hostPort 的 Pod 滚动升级时也会有问题,所以使用 hostPort 的服务在升级的时候一定要保障先停掉旧版本的 Pod 实例再启动新版本的 Pod 实例,服务升级这块内容配置详情可以参见 《Kubernetes应用编排与管理 —— Deployment升级策略》这篇博文。
讲到 hostPort 就不得不提 hostNetwork。hostNetwork 也是一种用于将主机上的特定端口映射到运行在 Pod 内部容器的端口的配置选项,对于客户端来说这两种访问方式都是一样的,但是它们的原理却大不相同,下面说一下 hostPort 与 hostNetwork 的异同。
相同点:
不同点:
hostPort 截图:
hostNetwork 截图:
当前示例Kubernetes集群节点信息如下(共五个节点,k8s版本为1.21.14):
- [root@master1 ~]# kubectl get nodes -o wide
-
- NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
-
- master1 Ready control-plane,master 19d v1.21.14 10.20.32.201 <none> Kylin Linux Advanced Server V10 (Lance) 4.19.90-52.22.v2207.ky10.x86_64 docker://20.10.9
-
- master2 Ready control-plane,master 19d v1.21.14 10.20.32.202 <none> Kylin Linux Advanced Server V10 (Lance) 4.19.90-52.22.v2207.ky10.x86_64 docker://20.10.9
-
- master3 Ready control-plane,master 19d v1.21.14 10.20.32.203 <none> Kylin Linux Advanced Server V10 (Lance) 4.19.90-52.22.v2207.ky10.x86_64 docker://20.10.9
-
- worker1 Ready worker 19d v1.21.14 10.20.32.204 <none> Kylin Linux Advanced Server V10 (Lance) 4.19.90-52.22.v2207.ky10.x86_64 docker://20.10.9
-
- worker2 Ready worker 19d v1.21.14 10.20.32.205 <none> Kylin Linux Advanced Server V10 (Lance) 4.19.90-52.22.v2207.ky10.x86_64 docker://20.10.9
当前集群kube-proxy模式为ipvs:
- [root@master1 ~]# kubectl get configmaps -n=kube-system kube-proxy -o yaml|grep mode
-
- mode: ipvs
启动一个 Pod 示例,其是使用 Go 语言来创建一个简单的 HTTP 服务器,使用 hostPort 将主机上的 80 端口映射到运行在 Pod 内部的容器端口上,其调度到 woker1 (10.20.32.204)节点上。
客户端通过主机 ip:hostPort 访问服务能够正常响应。
现在连接到 worker1 机器,由于当前集群 kube-proxy 模式选用的是 ipvs,所以当外部客户端的访问当前节点的容器时,流量包通常会先后经过以下链:
总结起来,流量通常会按照 PREROUTING -> IPVS -> FORWARD -> POSTROUTING 的顺序进行处理。但是,实际流量的经过路径可能会受到应用配置和网络堆栈的影响,所以在进行流量管理和路由时,请务必仔细考虑业务需求和设置。
1)分析 PREROUTING 链规则
外部流量进来后首先经过PREROUTING 链,通过规则匹配只要是访问当前节点的80端口,都会进行dnat转换,转换成 PodIP:Pod端口。
2)分析 IPVS 规则
执行 ipvsadm -L -n 命令查看当前节点 IPVS 规则,在规则中没有查找到 10.20.32.204:80 匹配项。
3)分析 FORWARD 链规则规则
无 CNI-HOSTPORT 相关规则。
4)分析 POSTROUTING 链规则
CNI-HOSTPORT_MASQ规则在最前面,并且无需源地址转换。
5) 抓包验证
进入示例容器网络命令空间:
- [root@worker1 ~]# docker ps|grep http-request-printer
-
- 267f462be8b6 25585bdfb0f7 "/usr/local/bin/pilo…" About an hour ago Up About an hour k8s_istio-proxy_http-request-printer-v1-856959f7d9-kxx6g_tracing_fa8a0a82-e233-4757-ba28-f16eb2d311f4_0
-
- e60564d42fc6 6246a84777e8 "./http_request_prin…" About an hour ago Up About an hour k8s_container-rr19ea_http-request-printer-v1-856959f7d9-kxx6g_tracing_fa8a0a82-e233-4757-ba28-f16eb2d311f4_0
-
- 432a811e77e1 10.20.32.201:80/library/pause:3.4.1 "/pause" About an hour ago Up About an hour k8s_POD_http-request-printer-v1-856959f7d9-kxx6g_tracing_fa8a0a82-e233-4757-ba28-f16eb2d311f4_0
-
- [root@worker1 ~]# nsenter -n -t 308222
-
- nsenter: cannot open /proc/308222/ns/net: No such file or directory
-
- [root@worker1 ~]# docker ps|grep http-request-printer
-
- 267f462be8b6 25585bdfb0f7 "/usr/local/bin/pilo…" About an hour ago Up About an hour k8s_istio-proxy_http-request-printer-v1-856959f7d9-kxx6g_tracing_fa8a0a82-e233-4757-ba28-f16eb2d311f4_0
-
- e60564d42fc6 6246a84777e8 "./http_request_prin…" About an hour ago Up About an hour k8s_container-rr19ea_http-request-printer-v1-856959f7d9-kxx6g_tracing_fa8a0a82-e233-4757-ba28-f16eb2d311f4_0
-
- 432a811e77e1 10.20.32.201:80/library/pause:3.4.1 "/pause" About an hour ago Up About an hour k8s_POD_http-request-printer-v1-856959f7d9-kxx6g_tracing_fa8a0a82-e233-4757-ba28-f16eb2d311f4_0
-
- [root@worker1 ~]# docker inspect --format "{{.State.Pid}}" 267f462be8b6
-
- 145003
-
- [root@worker1 ~]# nsenter -n -t 145003
-
- [root@worker1 ~]# ifconfig
-
- eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
-
- inet 10.233.1.133 netmask 255.255.255.0 broadcast 10.233.1.255
-
- ether 2e:2c:82:08:eb:8e txqueuelen 0 (Ethernet)
-
- RX packets 15372 bytes 9093248 (8.6 MiB)
-
- RX errors 0 dropped 0 overruns 0 frame 0
-
- TX packets 14493 bytes 23701690 (22.6 MiB)
-
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
-
-
-
- lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
-
- inet 127.0.0.1 netmask 255.0.0.0
-
- loop txqueuelen 1000 (Local Loopback)
-
- RX packets 8483 bytes 37018407 (35.3 MiB)
-
- RX errors 0 dropped 0 overruns 0 frame 0
-
- TX packets 8483 bytes 37018407 (35.3 MiB)
-
- TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
抓包来确定进入容器中的包没有进行源地址转换(抓包时客户端 10.20.32.201 通过 curl http://10.20.32.204:80 命令访问此服务)。
- [root@worker1 ~]# tcpdump -i eth0 host 10.233.1.133 and dst port 80 -w svc_host_port.pcap
-
- dropped privs to tcpdump
-
- tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
-
- ^C6 packets captured
-
- 6 packets received by filter
-
- 0 packets dropped by kernel
将抓的包下载到本地通过并使用 wireshark 软件进行分析,可以看到当节点外客户端通过 hostPort 访问容器时,进入容器中的包没有进行源地址转换。
现在连接到 worker1 机器,由于当前集群 kube-proxy 模式选用的是 ipvs,所以当当前节点客户端的访问当前节点的容器时,流量包通常会先后经过以下链:
OUTPUT -> IPVS -> POSTROUTING
1)分析 OUTPUT 链规则
当前节点流量先经过OUTPUT 链,通过规则匹配只要是访问当前节点的80端口,都会进行dnat转换,转换成 PodIP:Pod端口,除了源地址是PodIp或127.0.0.1时会对数据包打标签,其他规则和3.1.1中类似,这里不再赘余。
2)分析 IPVS 规则
执行 ipvsadm -L -n 命令查看当前节点 IPVS 规则,在规则中没有查找到 10.20.32.204:80 匹配项。
3)分析 POSTROUTING 链规则
CNI-HOSTPORT_MASQ规则在最前面,如果源地址是PodIp或者127.0.0.1需要源地址转换。
注意 1:没太想明白Pod是互通的,为什么源PodIP还需要进行源地址转换。
4) 路由匹配将流量发送到 Pod 里面
在 Kubernetes 中,hostPort 是一种用于将主机上的特定端口映射到运行在 Pod 内部容器的端口的配置选项。通过使用 hostPort,你可以在主机上暴露容器的服务,从而允许外部网络通过主机的 IP 地址和指定的端口访问容器内的应用程序。
一个 Node 只能启动一个 hostPort,所以最初是用于把守护进程集(DaemonSets)部署到每个 Node (确保一个 Node 只有一个 hostPort ),或者部署固定在特定节点的应用程序(比如 Nginx Ingress Controller 通常以高可用形式部署在固定三个节点上面)。
按照官方文档说的,除非绝对必要,否则不要为 Pod 指定 hostPort。 将 Pod 绑定到 hostPort 时,它会限制 Pod 可以调度的位置数,因为每个 <hostIP, hostPort, protocol> 组合必须是唯一的。 如果您没有明确指定 hostIP 和 protocol,Kubernetes 将使用 0.0.0.0 作为默认 hostIP 和 TCP 作为默认 protocol,请在使用 hostPort 之前考虑使用 NodePort 服务。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。