K8s 集群外部访问 Pod 的几种方式

K8s 集群外部访问 Pod 的几种方式

记录时间:2026-08-20
环境:K8s 1.28 测试集群(Calico 网络插件);生产 K8s 1.28 arm64 集群(MetalLB v0.14.5 L2 模式 + ingress-nginx)

一、背景

集群内访问 Pod 很简单,直接走 Pod IP 或者对应 Service 的 ClusterIP 就行。但从集群外面访问就是另一回事了:在 Flannel、Calico 这类方案下,Pod IP 是容器网段的内部地址,集群外的机器根本路由不到。最近看了一篇讲这个问题的文章,把文章里列的方式挨个和我们自己集群的实际情况对了一遍,整理下来。

我们集群的情况是:测试和生产现在都是 1.28。测试集群网络插件用的 Calico(没跟上联交换机开 BGP 对等);生产集群对外服务主要走 ingress-nginx,另外部署过 MetalLB 给需要直连 IP 的服务用。所以下面每种方式我都会带一句”我们环境里用没用、怎么用的”。

二、几种方式

2.1 hostNetwork:Pod 直接用宿主机网络

Pod 加 hostNetwork: true 后,容器直接用宿主机节点的网络命名空间,Pod IP 就是 node IP,集群外拿 node IP + 端口就能访问。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  hostNetwork: true
  containers:
    - name: nginx
      image: nginx

验证方法就是对比两个 IP 是否一致:

kubectl get pods -o wide nginx
kubectl get nodes -o wide
curl http://<node-ip>   # 应该返回 nginx 欢迎页

它的问题:Pod 漂移到别的 node 后 IP 跟着变;多个 Pod 抢同一个宿主机端口会调度失败;Pod 里能看到宿主机的全部网络,安全上不干净。

文章里给的变通是配合 keepalived 漂 VIP,再绑固定 node。容器里这么搞太绕,一般不这么干。

2.2 hostPort:只映射一个端口到宿主机

hostPorthostNetwork 效果类似,都是拿 Pod 所在 node 的 IP 加端口访问,但 Pod 自己的 IP 还是容器网段的内部地址。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
    - name: nginx
      image: nginx
      ports:
        - containerPort: 80
          hostPort: 80

原理上 hostPort 是 CNI 在宿主机上写的一套 iptables 规则做 fullnat(完整 NAT),文章里贴的规则核心就三条:

-A CNI-DN-1652b489c7cf2eeec2243 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.244.0.156:80
-A CNI-HOSTPORT-MASQ -m mark --mark 0x2000/0x2000 -j MASQUERADE
-A CNI-HOSTPORT-SETMARK -m comment --comment "CNI portfwd masquerade mark" -j MARK --set-xmark 0x2000/0x2000

访问流量先打标(0x2000),DNAT 到 Pod IP 的容器端口,回包再 MASQUERADE 回 node 地址。比 hostNetwork 好的地方是不用把整个宿主机网络交给 Pod,差的地方是多了一层 iptables 转发,性能略低。

hostPort 最典型的用途是暴露 ingress-controller:ingress 本身也要有入口,常见做法就是给 controller 配 hostPort 或 hostNetwork 挂在固定节点上,外面再指域名过去。

2.3 NodePort:最常用的 Service 暴露方式

前两种改的是 Pod,NodePort 改的是 Service。配一个 type: NodePort 的 Service,集群里任意一个 node 的 IP 加 nodePort 都能访问到服务,哪怕这个 node 上根本没跑目标 Pod。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    name: nginx
spec:
  containers:
    - name: nginx
      image: nginx
      ports:
        - containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
  name: nginx
spec:
  type: NodePort
  ports:
    - name: nginx
      port: 80
      nodePort: 30018
  selector:
    name: nginx
kubectl get svc -o wide nginx     # 输出里是 80:30018/TCP
curl http://<任意node-ip>:30018   # 能访问到

任意 node 都能转发的原因是 kube-proxy 把相同的 iptables 规则下发到了所有 node 上。nodePort 不显式指定的话 K8s 从 30000-32767 自动分配,显式指定要保证不和其他服务冲突。

参数 说明
port 80 Service 对集群内暴露的端口
nodePort 30018 集群外通过 node IP 访问的端口,30000-32767
targetPort 默认同 port 转发到 Pod 的容器端口

我们环境早期没有 LoadBalancer 方案的时候,对外服务基本靠 NodePort。

2.4 LoadBalancer:需要外部 LB 或者 MetalLB

LoadBalancer 和 NodePort 的唯一配置区别就是把 type 换了,但它需要外部负载均衡设备配合,公有云有现成的,自建机房就得自己解决。文章里用的 MetalLB,我们集群里也部署过一套。

kind: Service
apiVersion: v1
metadata:
  name: nginx
spec:
  type: LoadBalancer
  ports:
    - name: nginx
      port: 80
  selector:
    name: nginx
kubectl get svc nginx -o wide
# TYPE=LoadBalancer,EXTERNAL-IP 是 MetalLB 从地址池分配的地址
curl http://<EXTERNAL-IP>

我们部署的 MetalLB 是 v0.14.5,L2 模式,地址池 192.168.120.150-192.168.120.155,部署细节我单独记过一篇(MetalLB LoadBalancer 部署记录)。L2 模式不要求路由器支持 BGP,内网够用,所以选 L2 而不是 BGP 模式。

2.5 Ingress:七层转发

NodePort 和 LoadBalancer 都是四层,Ingress 管的是七层,按域名和路径把流量路由到不同 Service。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
    - host: nginx.example.com
      http:
        paths:
          - backend:
              serviceName: nginx
              servicePort: 80

流程:Ingress 对象绑定域名到 Service,ingress-controller 监听 Service 拿 endpoints 动态刷新自己的配置,外部请求按 host 匹配转发。K8s 本身不提供 Ingress 实现,controller 要自己部署(nginx、traefik 都行)。

注意一点:ingress-controller 自己接收集群外流量,还是得靠 2.2 的 hostPort/hostNetwork 或者 NodePort 暴露。我们生产对外就是走 ingress-nginx,真实 IP 透传的问题我之前单独记过一篇(Ingress 获取真实 IP)。

2.6 Pod IP 全局可达:Calico BGP 发布路由

如果网络插件是 Calico 或 Contiv,可以让宿主机和上联交换机建 BGP 邻居,宿主机把 Pod 网段路由发布出去,交换机之间再交换路由,外面的机器就能直接路由到 Pod IP,什么 Service、端口映射都不用配。

我们测试集群虽然是 Calico,但默认没有跟上联交换机做 BGP 对等,Pod IP 只到宿主机为止,这条路我们没走,集群外访问还是靠上面几种方式。要做的话网络设备要配合改配置,动起来牵涉面大。

三、怎么选

方式 层级 依赖 主要代价
hostNetwork 网络命名空间 Pod 漂移 IP 变、端口冲突、能看到宿主机网络
hostPort iptables NAT 类似 hostNetwork,性能略低
NodePort 四层 Service kube-proxy 占用各 node 端口(30000-32767)
LoadBalancer 四层 Service 外部 LB / MetalLB 需要额外组件支持
Ingress 七层 ingress-controller 要部署 controller,controller 自身也要暴露
Pod IP 全局可达 网络层 Calico/Contiv + BGP 依赖 CNI 和交换机 BGP 支持

我的选择顺序:对外 HTTP 服务一律 Ingress(域名管理方便);需要固定 IP 直连的服务用 LoadBalancer + MetalLB;什么都没有的环境 NodePort 兜底;hostNetwork 和 hostPort 只留给 ingress-controller 这类基础组件。

四、注意事项

  • hostNetwork 和 hostPort 都占用宿主机端口,端口规划要提前做,冲突了 Pod 会一直调度失败。
  • NodePort 的端口范围是 30000-32767,显式指定 nodePort 时先查一遍集群现有 Service,别撞了。
  • LoadBalancer 在自建机房没公有云的情况下,MetalLB 是主流方案;L2 模式和 BGP 模式的选择取决于路由器支不支持 BGP。
  • Ingress 对象只是规则声明,controller 不部署的话规则不生效。
  • 我们环境的真实情况:测试集群 Calico 默认没跟上联交换机做 BGP 对等,Pod IP 不可达外部;MetalLB 地址池是 192.168.120.150-192.168.120.155。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇