当前位置: 首页 > news >正文

Kubernetes ClusterIP 端口深度解析:虚拟服务与流量转发机制

事情的起因是创建了一个 NodePort 类型 Service,其端口映射关系为 8000:30948/TCP。既然30948是在每个node开的端口,那8000是开在哪的呢?出于好奇回顾了一下K8s的Cluster IP和Service


端口映射关系解析

在 Kubernetes 的 NodePort Service 中,端口配置遵循以下格式:

<ClusterIP 端口>:<NodePort 端口>/<协议>
  • 8000:Service 的 ClusterIP 端口(集群内部访问端口)
  • 30948:NodePort 端口(节点外部访问端口)
外部用户
节点IP:30948
集群内部Pod
Service IP:8000
Service
后端Pod

访问测试结果

1. 在宿主机上访问 IP:8000
curl http://<节点IP>:8000

结果:连接失败
原因:

  • 8000 端口仅在集群内部监听(通过 Service 的 ClusterIP)
  • 节点操作系统没有在 8000 端口监听请求
2. 在宿主机上访问 IP:30948
curl http://<节点IP>:30948

结果:成功访问服务
原因:

  • kube-proxy 在所有节点上监听了 30948 端口
  • 流量会被转发到 Service 的后端 Pod

技术原理详解

1. NodePort 工作原理

当创建 NodePort Service 时:

  1. kube-proxy 在所有节点上打开指定端口(30948)

  2. 创建 iptables/IPVS 规则:

    -A KUBE-NODEPORTS -p tcp --dport 30948 -j KUBE-SVC-XXXXXX
    
  3. 流量转发路径:

    外部用户 → 节点IP:30948 → kube-proxy → Service → Pod
    
2. ClusterIP 端口用途
  • 集群内部访问入口:

    # 在集群内部Pod中访问
    curl http://my-dep.default.svc.cluster.local:8000
    
  • 服务发现的基础端口


实际验证步骤

1. 查看 Service 完整定义
kubectl describe svc my-dep

输出关键部分:

Port:             8000/TCP
TargetPort:       80/TCP  # 后端Pod实际端口
NodePort:         30948/TCP
Endpoints:        10.244.1.2:80,10.244.2.3:80 # 后端Pod IP
2. 测试端口访问
# 1. 访问NodePort (应成功)
curl http://<任一节点IP>:30948# 2. 访问ClusterIP端口 (应失败)
curl http://<节点IP>:8000# 3. 在集群内部访问 (在Pod中执行)
kubectl run test --image=busybox -it --rm --restart=Never -- \wget -qO- http://my-dep:8000
3. 检查节点端口监听
# 在K8s节点上执行
sudo netstat -tuln | grep 30948
# 应输出: tcp6  0  0 :::30948  :::*  LISTEN

那么回到最开始的问题,8000端口开在哪呢?

如果你采用的是原生搭建k8s,那么你一定会记得初始化的时候有这样一个命令

kubeadm init \
--apiserver-advertise-address=172.31.0.4 \
--control-plane-endpoint=cluster-endpoint \
--image-repository registry.cn-hangzhou.aliyuncs.com/k8s_images \
--kubernetes-version v1.20.9 \
--service-cidr=10.96.0.0/16 \
--pod-network-cidr=192.168.0.0/16

在这里就指定了创建svc和pod的网段

集群内部访问端口的本质

ClusterIP 端口是 Kubernetes 服务抽象层的核心设计。

请求 10.96.91.238:8000
Client Pod
Service ClusterIP
iptables/IPVS 规则
Pod 1:80
Pod 2:80
Pod 3:80

ClusterIP 端口的三层抽象

1. 虚拟 IP 层 (Service ClusterIP)

  • 非真实接口:ClusterIP (如 10.96.91.238) 是 kube-proxy 创建的虚拟 IP
  • 无端口监听:节点操作系统上没有进程真正监听 8000 端口
  • 内核级拦截:通过 Linux 内核的 netfilter 框架实现流量拦截

2. 规则转发层 (kube-proxy)

kube-proxy 创建转发规则(以 iptables 为例):

# 查看 Service 规则链
sudo iptables -t nat -L KUBE-SERVICES# 示例输出
KUBE-SVC-XYZ  tcp  --  anywhere  10.96.91.238  tcp dpt:8000

具体规则细节:

# DNAT 规则
-A KUBE-SVC-XYZ -m statistic --mode random --probability 0.333 -j KUBE-SEP-111
-A KUBE-SVC-XYZ -m statistic --mode random --probability 0.5 -j KUBE-SEP-222
-A KUBE-SVC-XYZ -j KUBE-SEP-333# 终结点规则
-A KUBE-SEP-111 -p tcp -m tcp -j DNAT --to-destination 10.244.1.2:80
-A KUBE-SEP-222 -p tcp -m tcp -j DNAT --to-destination 10.244.1.3:80
-A KUBE-SEP-333 -p tcp -m tcp -j DNAT --to-destination 10.244.2.4:80

3. 真实端点层 (Pod)

  • 实际端口监听:在 Pod 内部的容器端口(如配置的 80 端口)

  • Endpoint 对象管理

    apiVersion: v1
    kind: Endpoints
    metadata:name: my-dep
    subsets:
    - addresses:- ip: 10.244.1.2- ip: 10.244.1.3- ip: 10.244.2.4ports:- port: 80protocol: TCP
    

流量转发全路径

当集群内部客户端访问 10.96.91.238:8000 时:

  1. 客户端发起请求

    resp, err := http.Get("http://10.96.91.238:8000")
    
  2. 内核网络栈拦截

    • 目标 IP 匹配 Service CIDR (如 10.96.0.0/16)
    • 进入 KUBE-SERVICES
  3. DNAT 转换

    • 根据 iptables 规则
    • 目标 IP:Port 被替换为 Pod IP:Port (如 10.244.1.2:80)
  4. 路由到目标 Pod

    • 通过 CNI 插件创建的网络路由
    • 流量进入 Pod 网络命名空间
  5. 容器接收请求

    • 容器内进程监听 80 端口
    • 处理请求并返回响应

与 NodePort 的关键区别

特性ClusterIP 端口 (8000)NodePort 端口 (30948)
可见性仅集群内部可见可从集群外部访问
实现层级内核网络栈 (L3/L4)用户空间监听 (L4)
监听位置无真实监听,仅规则kube-proxy 进程真实监听
访问控制受网络策略控制受节点防火墙控制
性能开销低(内核转发)中(用户态转发)
数据包变化目标地址被修改目标地址不变

查看内核规则

在任意节点执行:

# 查看NAT表规则
sudo iptables -t nat -L KUBE-SERVICES -n --line-numbers# 查找Service规则
sudo iptables -t nat -L KUBE-SVC-$(kubectl get svc my-dep -o jsonpath='{.spec.ports[0].name}') -n

为什么需要 ClusterIP

  1. 稳定访问端点
    Pod 可能随时重建,但 Service IP 保持不变

  2. 负载均衡
    自动将流量分发到多个后端 Pod

  3. 服务发现
    通过 DNS 名称解耦服务位置

  4. 流量策略
    支持会话保持、流量权重等高级特性

  5. 安全隔离
    默认仅集群内部可访问,减少攻击面

生产环境实践

  1. 避免直接使用 NodePort 配合 Ingress 或 LoadBalancer 使用:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:name: my-ingress
    spec:rules:- http:paths:- path: /pathType: Prefixbackend:service:name: my-depport:number: 8000  # 使用ClusterIP端口
    
  2. 自定义 NodePort 范围 修改 apiserver 配置:

    apiServer:extraArgs:service-node-port-range: "30000-35000"
    
  3. 防火墙规则 仅开放必要的 NodePort 端口:

    sudo ufw allow 30948/tcp
    

相关文章:

  • pocketflow库实现guardrail
  • 恶补电源:1.电桥
  • 【K8S】Kubernetes从入门到实战:全面指南
  • 微机原理与接口技术,期末冲刺复习资料(四)
  • EU 2023/1669与EU 2023/1670 的区别
  • 浅谈二叉堆实现的优先队列
  • c++第七天--继承与派生
  • 【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!
  • 鸢尾花分类(KNN)
  • 【数据结构】图论最短路径算法深度解析:从BFS基础到全算法综述​
  • 数据结构 - 栈与队列
  • LangChain + LangSmith + DeepSeek 入门实战:构建代码生成助手
  • Tomcat 安装和配置
  • 定位触发DMA2_Stream1_IRQHandler中断的具体原因简述
  • Jenkins构建时出现报错`ERROR: Failed to install JDK. Exit code=2`的终极解决方案
  • 深入理解Optional:处理空指针异常
  • 造成服务器重启的原因都有哪些?
  • RKNN开发环境搭建2-RKNN Model Zoo 环境搭建
  • 游戏盾的功能是什么
  • 力扣-35.搜索插入位置
  • 做外贸找客户的网站/万州网站建设
  • 泉州网站建设方案外包/设计网站排行
  • 自己建设网站怎么盈利/舆情监控系统
  • 网站有什么/百度推广开户费用
  • 中国设计网app/太原seo
  • 公司做网站最好/网站制作需要多少钱