1、概述

在 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

注意 1:hostPort 与 NodePort 的区别是,NodePort 服务默认是把请求转发到随机的一个运行的 Pod 上,而 hostPort 是直接转发到本 Node 上的指定 Pod。

一个 Node 只能启动一个 hostPort,所以最初是用于把守护进程集(DaemonSets)部署到每个 Node (确保一个 Node 只有一个 hostPort )。如下图所示,3个 Node 上部署4个带 hostPort 的 Pod,会有一个不成功。

即便是3个 Node 上部署3个带 hostPort 的 Pod 滚动升级时也会有问题,所以使用 hostPort 的服务在升级的时候一定要保障先停掉旧版本的 Pod 实例再启动新版本的 Pod 实例,服务升级这块内容配置详情可以参见 《Kubernetes应用编排与管理 —— Deployment升级策略》这篇博文。

2、hostPort 与 hostNetwork 的异同

讲到 hostPort 就不得不提 hostNetwork。hostNetwork 也是一种用于将主机上的特定端口映射到运行在 Pod 内部容器的端口的配置选项,对于客户端来说这两种访问方式都是一样的,但是它们的原理却大不相同,下面说一下 hostPort 与 hostNetwork 的异同。

相同点:

  1. hostPort 与 hostNetwork 本质上都是终端用户能访问到pod中的业务;
  2. 访问的时候,只能用 pod所有宿主机IP + 容器端口或 hostport 端口进行访问。

不同点:

  1. 网络地址空间不同。hostport使用CNI分配的地址,hostNetwork使用宿主机网络地址空间;
  2. 宿主机端口生成。hostport宿主机不生成端口,hostNetwork宿主机生成端口;
  3. hostport通过 iptables 防火墙的 nat 表进行转发,hostNetwork 直接通过主机端口到容器中;
  4. 定义的路径不同。deploy.spec.template.spec.containers.ports.hostPort 与 deploy.spec.template.spec.hostNetwork;
  5. 优先级不同,hostNetwork 高于 hostPort。

hostPort 截图:

hostNetwork 截图:

3、hostPort 原理

(1)示例环境

当前示例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

(2)示例

启动一个 Pod 示例,其是使用 Go 语言来创建一个简单的 HTTP 服务器,使用 hostPort 将主机上的 80 端口映射到运行在 Pod 内部的容器端口上,其调度到 woker1 (10.20.32.204)节点上。

客户端通过主机 ip:hostPort 访问服务能够正常响应。

(3)原理解析

3.1)节点外客户端访问当前节点容器

现在连接到 worker1 机器,由于当前集群 kube-proxy 模式选用的是 ipvs,所以当外部客户端的访问当前节点的容器时,流量包通常会先后经过以下链:

  1. PREROUTING 链(iptables): 外部流量进入主机后,首先经过 PREROUTING 链(位于 nat 表),这是网络地址转换(NAT)处理的第一个步骤。你可以在 PREROUTING 链中创建规则,以便将流量导入其他自定义的 iptables 链或 ipvs 转发路径。

  2. IPVS 转发路径: 如果你使用了 ipvs 来进行负载均衡,外部流量可能会直接进入 ipvs 的负载均衡路径。这是流量被转发到正确的后端 Pod 的地方,绕过了 iptables 的后续处理。

  3. FORWARD 链(iptables): 如果流量需要在主机内部进行转发,它会进入 FORWARD 链(位于 filter 表),在这里可以进行进一步的处理,例如网络策略、防火墙规则等。

  4. POSTROUTING 链(iptables): 流量最终经过 POSTROUTING 链(位于 nat 表),这是 NAT 处理的最后一步。你可以在这里进行出站流量的地址转换等处理。

总结起来,流量通常会按照 PREROUTING -> IPVS -> FORWARD -> POSTROUTING 的顺序进行处理。但是,实际流量的经过路径可能会受到你的配置和网络堆栈的影响,所以在进行流量管理和路由时,请务必仔细考虑你的需求和设置。

3.1.1)分析 PREROUTING 链规则

外部流量进来后首先经过PREROUTING 链,通过规则匹配只要是访问当前节点的80端口,都会进行dnat转换,转换成 PodIP:Pod端口。

3.1.2)分析 IPVS 规则

执行 ipvsadm -L -n 命令查看当前节点 IPVS 规则,在规则中没有查找到 10.20.32.204:80 匹配项。

3.1.3)分析 FORWARD 链规则规则

无 CNI-HOSTPORT 相关规则。

3.1.4)分析 POSTROUTING 链规则

CNI-HOSTPORT_MASQ规则在最前面,并且无需源地址转换。

3.1.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/cloudbases/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/cloudbases/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 [root@worker1 ~]#

抓包来确定进入容器中的包没有进行源地址转换(抓包时客户端 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 访问容器时,进入容器中的包没有进行源地址转换。

3.2)节点上客户端访问当前节点容器

现在连接到 worker1 机器,由于当前集群 kube-proxy 模式选用的是 ipvs,所以当当前节点客户端的访问当前节点的容器时,流量包通常会先后经过以下链:

OUTPUT -> IPVS  -> POSTROUTING 

3.2.1)分析 OUTPUT 链规则

当前节点流量先经过OUTPUT 链,通过规则匹配只要是访问当前节点的80端口,都会进行dnat转换,转换成 PodIP:Pod端口,除了源地址是PodIp或127.0.0.1时会对数据包打标签,其他规则和3.1.1中类似,这里不再赘余。

3.2.2)分析 IPVS 规则

执行 ipvsadm -L -n 命令查看当前节点 IPVS 规则,在规则中没有查找到 10.20.32.204:80 匹配项。

3.2.3)分析 POSTROUTING 链规则

CNI-HOSTPORT_MASQ规则在最前面,如果源地址是PodIp或者127.0.0.1需要源地址转换。

注意 1:没太想明白Pod是互通的,为什么源PodIP还需要进行源地址转换。

3.2.4) 路由匹配将流量发送到 Pod 里面。

4、总结

在 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 服务。

参考:https://knowledge.zhaoweiguo.com/build/html/cloudnative/k8s/yamls/option_hostport

参考:hostPort 与 hostNetwork 异同

Kubernetes hostPort 使用的更多相关文章

  1. Docker Kubernetes hostPort 代理转发

    Docker Kubernetes  hostPort 代理转发 hostPort: 1. 类似docker -p 映射宿主级端口到容器. 2. 容器所在的主机暴露端口转发到指定容器中. 3. hos ...

  2. Docker 运维高级应用管理

     Docker 基本应用 1.Docker 介绍及安装 2.Docket 使用命令 3.Docker run命令参数整理 4.Docker 构建镜像 Docker Compose 高级应用 1.Doc ...

  3. 基于Kubernetes在AWS上部署Kafka时遇到的一些问题

    作者:Jack47 转载请保留作者和原文出处 欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 交代一下背景:我们的后台系统是一套使用Kafka消息队列的数据处理管线 ...

  4. kubernetes多节点部署解析

    注:以下操作均基于centos7系统. 安装ansible ansilbe可以通过yum或者pip安装,由于kubernetes-ansible用到了密码,故而还需要安装sshpass: pip in ...

  5. [经验交流] Kubernetes Nginx Ingress 安装与使用

    Ingress 介绍 Kubernetes 上部署的微服务运行在它的私有网络中, 通过Pod实例的hostPort或Service实例的NodePort可以暴露到主机端口上,便于用户访问.但这样的方法 ...

  6. Docker实践(6)—CentOS7上部署Kubernetes

    Kubernetes架构 Kubernetes的整体架构如下: Master为主控节点,上面运行apiserver,scheduler,controller-manager等组件.Minion相当于工 ...

  7. CoreOS实践(2)—在coreos上安装Kubernetes

    下载kubernetes sudo mkdir -p /opt/bin sudo wget https://storage.googleapis.com/kubernetes/binaries.tar ...

  8. 基于kubernetes构建Docker集群管理详解-转

    http://blog.liuts.com/post/247/ 一.前言        Kubernetes 是Google开源的容器集群管理系统,基于Docker构建一个容器的调度服务,提供资源调度 ...

  9. kubernetes集群部署

    鉴于Docker如此火爆,Google推出kubernetes管理docker集群,不少人估计会进行尝试.kubernetes得到了很多大公司的支持,kubernetes集群部署工具也集成了gce,c ...

  10. Docker系列(八)Kubernetes介绍

    Kubernetes组件功能图   各组件说明: 节点 节点在Kubernetes由虚拟机或者实体机表示,常称为Minion,即从属主机.当一个节点加入到Kubernetes系统中时,它将会创建一个数 ...

随机推荐

  1. 2020-12-31:tcp三次握手,最后一次失败,网络会怎么样?

    福哥答案2020-12-31:[答案来自此链接:](https://www.zhihu.com/question/437249958)第一次握手:建立连接时,客户端发送syn包(syn=a)到服务器, ...

  2. Rust如何引入源码作为依赖

    问题描述 通常我们在rust项目中引入第三方依赖包时,会直接指定包的版本,这种方式指定后,Cargo在编译时会从crates.io这个源中下载这些依赖包. [package] name = " ...

  3. 【HDU】1312 Red andBlack (DFS&BFS经典好题)

    Red and Black 题目 我是题目链接 题解 找出所能到达的所有黑色的数量,用DFS和BFS均可. BFS: #include <iostream> #include <qu ...

  4. 玩转服务器之网站篇:新手使用WordPress搭建博客和静态网站部署

    静态网站部署和WordPress搭建博客都是网站运营中常见的工作.静态网站是一种不需要服务器端脚本的网站形式,通常使用HTML.CSS和JavaScript等静态资源进行构建和显示.而WordPres ...

  5. 10 款 VS Code 插件神器,第 7 款超级实用!

    VS Code 是这两年非常热门的一款 IDE,它不仅有提升开发体验的界面.轻量化的编辑器,还有丰富而强大的插件,这些优秀的插件使得 VS Code 生态体系更加吸引人,让开发效率大大提升.本文来介绍 ...

  6. jenkins中的坑_CreateProcess error=1392

    环境:windows11,jdk1.8,jenkins_2.346.war 起因 最近在使用jenkins部署项目的时候,填写仓库的url地址时,发现填完后报500这个错误,于是我打开jenkins的 ...

  7. 【技术积累】Java中的泛型【一】

    泛型是什么 Java中的泛型是一种能够让用户在编写代码时避免使用明确的类型而进行类型参数化的机制.Java中的泛型可以让编程者在代码编写时不必关心具体类型,只用关心类型之间的关系和相互转换,从而在编写 ...

  8. Vulhub靶场的搭建

    Vulhub靶场的搭建(基于centos7) 1>简述 很多人在搭建Vulhub靶场的时候,可能也搜到过许多的文章,但是大多数的文章只是有一个流程,对其中的原理,步骤没有进行详细的说明,这也就导 ...

  9. 浅谈 thinkphp composer 扩展包加载原理

    浅谈 thinkphp composer 扩展包加载原理 本文将介绍 ThinkPHP 中 Composer 扩展包的加载原理,帮助读者更好地理解和应用该功能. 前言 如题,今天感觉好久没有更新博客了 ...

  10. SpringBoot项目从0到1配置logback日志打印

    大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教. 以下是正文! 一.写文背景 我们在写后端项目的时候 ...