介绍
hostport需要cni插件支持,比如calico,flannel等都支持该功能。如果你想要启动hostPort,则必须在cni的配置文件中指定portMappings capability
。
cat /etc/cni/net.d/10-calico.conflist
hostNetwork:true
,开启后pod内服务可以访问到宿主机网络,局域网内其他服务也可以直接访问该服务。
区别
相同点
- hostport和hostNetwork都可以实现外部服务访问集群内服务。
- 可以用宿主机IP+容器端口或hostport端口进行访问。
不同点
- 网络地址空间不同。hostport使用CNI分配的地址,hostNetwork使用宿主机网络地址空间;
- 宿主机端口生成。hostport宿主机不生成端口,hostNetwork宿主机生成端口;
- hostport通过iptables防火墙的nat表进行转发,hostNetwork 直接通过主机端口到容器中;
- 定义的路径不同。
deploy.spec.template.spec.containers.ports.hostPort
与deploy.spec.template.spec.hostNetwork
; - 优先级不同,hostNetwork高于hostPort。
hostport原理
部署服务
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:stable-alpine
ports:
- containerPort: 80
hostPort: 10000
name: http
protocol: TCP
部署nginx服务,查看主机上没有监听10000端口。
使用hostport端口访问nginx服务,可以正常访问。
curl 172.16.255.180:10000
查看iptables nat表规则,确定是通过iptables进行转发的。
k get po nginx-666b766f9c-g9dc8 -o wide
iptables -t nat -nvL | grep 192.168.137.84:80
转发路径
我的环境中kube-proxy是ipvs模式。
所以当外部客户端访问当前节点的容器时,流量包通常会先后经过以下链:
- PREROUTING 链(iptables): 外部流量进入主机后,首先经过 PREROUTING 链(位于 nat 表),这是网络地址转换(NAT)处理的第一个步骤。你可以在 PREROUTING 链中创建规则,以便将流量导入其他自定义的 iptables 链或 ipvs 转发路径。
- IPVS 转发路径: 如果你使用了 ipvs 来进行负载均衡,外部流量可能会直接进入 ipvs 的负载均衡路径。这是流量被转发到正确的后端 Pod 的地方,绕过了 iptables 的后续处理。
- FORWARD 链(iptables): 如果流量需要在主机内部进行转发,它会进入 FORWARD 链(位于 filter 表),在这里可以进行进一步的处理,例如网络策略、防火墙规则等。
- POSTROUTING 链(iptables): 流量最终经过 POSTROUTING 链(位于 nat 表),这是 NAT 处理的最后一步。你可以在这里进行出站流量的地址转换等处理。
大体上会按照 PREROUTING -> IPVS -> FORWARD -> POSTROUTING 的顺序进行处理。但是,实际流量的经过路径可能会受到你的网络配置的影响。
PREROUTING链
外部流量进来后首先经过cali-PREROUTING
链,然后到KUBE-SERVICES
链,再到DOCKER
链,匹配不成功后到CNI-HOSTPORT-DNAT
链。
然后查看CNI-HOSTPORT-DNAT
链规则。dnat名字为k8s-pod-network
,id是nginx pod的pause容器id。
再查看CNI-DN-2fe3a07970a494f609ee7
链规则。只要访问当前节点的10000端口,都会进行dnat转换,转换成PodIP:Pod端口
,这里就是nginx pod的192.168.137.102:80
。
IPVS规则
查找关于10000端口的规则,没有输出。
ipvsadm -L -n | grep 10000
FORWARD链规则
FORWARD链在filter和mangle表中。前者用于对数据包进行过滤,后者用于修改数据包的服务类型,生存周期,为数据包设置标记,实现流量整形、策略路由等。
iptables -t filter -nvL FORWARD
iptables -t mangle -nvL FORWARD
可以看到没有CNI-HOSTPORT
链相关规则。
POSTROUTING链规则
流量到达cali-POSTROUTING
链后,转发到CNI-HOSTPORT-MASQ
链,这个链就是hostport相关的链。
再查看CNI-HOSTPORT-MASQ
链的规则,MASQUERADE
在hostport的流量出去时将源地址转换成主机网卡的ip,并对数据包打标签。
最后根据路由将数据包发送到pod内。
hostnetwork原理
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: nginx
image: nginx:stable-alpine
查看pod ip为主机ip,主机上启动了容器的80端口。
这时可以直接通过主机ip+80端口访问服务。
dns策略
pod的yaml中定义了dnsPolicy,这个有什么用呢?
- Default: Pod 从运行所在的节点继承名称解析配置。
- ClusterFirst: 与配置的集群域后缀不匹配的任何 DNS 查询(例如 "www.kubernetes.io") 都会由 DNS 服务器转发到上游名称服务器。集群管理员可能配置了额外的存根域和上游 DNS 服务器。
- ClusterFirstWithHostNet: 对于以 hostNetwork 方式运行的 Pod,应将其 DNS 策略显式设置为 "ClusterFirstWithHostNet"。否则,以 hostNetwork 方式和 "ClusterFirst" 策略运行的 Pod 将会做出回退至 "Default" 策略的行为。注意:这在 Windows 上不支持。
- None: 此设置允许 Pod 忽略 Kubernetes 环境中的 DNS 设置。Pod 会使用其 dnsConfig 字段所提供的 DNS 设置。
说人话就是:如果nginx服务有多个pod,如果没有使用ClusterFirstWithHostNet
,那么这些pod就无法通过svc name访问集群中其他pod。开启该参数之后,pod内的/etc/resolv.conf
中dns服务器的地址就会被设置为k8s集群的dns地址。这样就可以访问其他服务。
去掉ClusterFirstWithHostNet
后,pod的dns就变成了主机dns,无法通过svc访问其他服务。
总结
虽然hostport和hostNetwork都可以对外暴露服务,但是一般不要为Pod指定hostPort,除非非常有必要这样做。当你为Pod绑定了hostPort,那么能够运行该Pod的节点就有限了,因为每个 <hostIP, hostPort, protocol>
组合必须是唯一的。
如果你没有明确指定hostIP和protocol,k8s将使用 0.0.0.0
作为默认的hostIP,使用TCP作为默认的protocol。hostNetwork同样的原因也不建议使用。