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

【Docker-Day 24】K8s网络解密:深入NodePort与LoadBalancer,让你的应用走出集群

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Python系列文章目录

Go语言系列文章目录

Docker系列文章目录

01-【Docker-Day 1】告别部署噩梦:为什么说 Docker 是每个开发者的必备技能?
02-【Docker-Day 2】从零开始:手把手教你在 Windows、macOS 和 Linux 上安装 Docker
03-【Docker-Day 3】深入浅出:彻底搞懂 Docker 的三大核心基石——镜像、容器与仓库
04-【Docker-Day 4】从创建到删除:一文精通 Docker 容器核心操作命令
05-【Docker-Day 5】玩转 Docker 镜像:search, pull, tag, rmi 四大金刚命令详解
06-【Docker-Day 6】从零到一:精通 Dockerfile 核心指令 (FROM, WORKDIR, COPY, RUN)
07-【Docker-Day 7】揭秘 Dockerfile 启动指令:CMD、ENTRYPOINT、ENV、ARG 与 EXPOSE 详解
08-【Docker-Day 8】高手进阶:构建更小、更快、更安全的 Docker 镜像
09-【Docker-Day 9】实战终极指南:手把手教你将 Node.js 应用容器化
10-【Docker-Day 10】容器的“持久化”记忆:深入解析 Docker 数据卷 (Volume)
11-【Docker-Day 11】Docker 绑定挂载 (Bind Mount) 实战:本地代码如何与容器实时同步?
12-【Docker-Day 12】揭秘容器网络:深入理解 Docker Bridge 模式与端口映射
13-【Docker-Day 13】超越默认Bridge:精通Docker Host、None与自定义网络模式
14-【Docker-Day 14】Docker Compose深度解析
15-【Docker-Day 15】一键部署 WordPress!Docker Compose 实战终极指南
16-【Docker-Day 16】告别单机时代:为什么 Docker Compose 不够用,而你需要 Kubernetes?
17-【Docker-Day 17】K8s 架构全解析:深入理解 Kubernetes 的大脑 (Master) 与四肢 (Node)
18-【Docker-Day 18】告别选择困难症:一文掌握 Minikube、kind、k3d,轻松搭建你的第一个 K8s 集群
19-【Docker-Day 19】万物皆 YAML:掌握 Kubernetes 声明式 API 的艺术
20-【Docker-Day 20】揭秘 Kubernetes 的原子单位:深入理解 Pod
21-【Docker-Day 21】Pod的守护神:ReplicaSet与ReplicationController,轻松实现应用高可用
22-【K8s-Day 22】深入解析 Kubernetes Deployment:现代应用部署的基石与滚动更新的艺术
23-【K8s-Day 23】从 Pod 的“失联”到 Service 的“牵线”:深入理解 ClusterIP 核心原理
24-【Docker-Day 24】K8s网络解密:深入NodePort与LoadBalancer,让你的应用走出集群


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • Python系列文章目录
  • Go语言系列文章目录
  • Docker系列文章目录
  • 摘要
  • 一、回顾:为何需要将服务暴露到集群外部?
    • 1.1 ClusterIP 的局限性
    • 1.2 外部访问的核心诉求
  • 二、NodePort:在每个节点上打开一个“窗口”
    • 2.1 NodePort 是什么?
    • 2.2 NodePort 的工作原理
    • 2.3 实战:创建一个 NodePort Service
        • (1) 准备应用 Deployment
        • (2) 创建 NodePort Service
        • (3) 部署与验证
        • (4) 访问服务
    • 2.4 NodePort 的优缺点与适用场景
  • 三、LoadBalancer:云时代的“智能网关”
    • 3.1 LoadBalancer 是什么?
    • 3.2 LoadBalancer 的工作原理
    • 3.3 实战:创建一个 LoadBalancer Service
    • 3.4 LoadBalancer 的优缺点与适用场景
  • 四、三大 Service 类型的终极对决
  • 五、常见问题与注意事项
    • 5.1 我在本地 Minikube/kind 环境,能用 LoadBalancer 吗?
    • 5.2 NodePort 的端口可以自己指定吗?
    • 5.3 LoadBalancer 的 `EXTERNAL-IP` 一直是 `<pending>` 怎么办?
  • 六、总结


摘要

在上一篇 中,我们深入探讨了 ClusterIP 类型的 Service,它是 Kubernetes 集群内部服务间通信的基石。然而,ClusterIP 仅在集群内部可见,就像一个公司的内线电话,外部用户无法拨入。那么,如何才能让我们的应用冲出集群的“内网”,真正面向公网用户提供服务呢?

本文将作为 Kubernetes Service 探索之旅的下篇,聚焦于另外两种至关重要的 Service 类型:NodePortLoadBalancer。我们将详细解析它们的工作原理、配置方法、适用场景及优缺点,并通过图文并茂的实战案例,彻底打通从集群内部到外部世界的流量路径。读完本文,您将能够根据不同需求,自信地为您的应用选择并配置最合适的外部访问方案。

一、回顾:为何需要将服务暴露到集群外部?

在深入新技术之前,我们先快速回顾一下将服务暴露至集群外部的根本原因。

1.1 ClusterIP 的局限性

ClusterIP Service 为一组 Pod 提供了一个稳定、唯一的虚拟 IP 地址。集群内的任何其他 Pod 都可以通过这个 ClusterIP 访问到后端的服务,实现了服务发现和负载均衡。但它的核心限制在于:ClusterIP 是一个私有地址,仅在 Kubernetes 集群网络内部才能访问

这就像一个大楼内部的房间号,楼内的人可以根据房间号轻松找到彼此,但楼外的人根本不知道这个房间号的存在,也无法直接到达。

1.2 外部访问的核心诉求

现实世界的应用,很少是完全封闭的。我们部署在 Kubernetes 中的服务,往往需要被集群外部的实体访问,这些实体包括:

  • 最终用户:通过浏览器或移动 App 访问我们的 Web 应用或 API。
  • 第三方系统:例如,支付网关需要回调我们的订单处理接口。
  • 开发与运维人员:需要从本地机器直接访问测试环境中的服务进行调试。

为了满足这些诉求,Kubernetes 提供了 NodePortLoadBalancer 这两种官方解决方案,它们为集群内部的服务打开了通往外部世界的大门。

二、NodePort:在每个节点上打开一个“窗口”

NodePort 是最直接、最基础的一种将服务暴露给外部流量的方式。

2.1 NodePort 是什么?

NodePort Service,顾名思义,就是在每个 Worker Node (工作节点) 上开放一个静态的、特定的端口(即 NodePort)。任何发送到 任意节点IP:NodePort 的流量,都会被 Kubernetes 自动转发到该 Service 所代理的后端 Pod 上。

一个关键点是:NodePort Service 是建立在 ClusterIP Service 之上的。当你创建一个 type: NodePort 的 Service 时,Kubernetes 会自动完成三件事:

  1. 创建一个 ClusterIP,供集群内部通信使用。
  2. 在所有节点上预留一个端口(NodePort)。
  3. 配置 kube-proxy,将 NodeIP:NodePort 的流量导向该 Service 的 ClusterIP

2.2 NodePort 的工作原理

NodePort 的流量路径相对直观,我们可以通过下面的流程图来理解:

graph TDsubgraph 外部网络User[外部用户/客户端]endsubgraph Kubernetes 集群subgraph Node 1KubeProxy1[kube-proxy]PodA1[Pod A]NodePort1[eth0: 30080]endsubgraph Node 2KubeProxy2[kube-proxy]PodA2[Pod A]NodePort2[eth0: 30080]endSVC[Service (ClusterIP: 10.96.0.10, Port: 80)]endUser -- "请求 http://<Node1_IP>:30080" --> NodePort1User -- "或请求 http://<Node2_IP>:30080" --> NodePort2NodePort1 --> KubeProxy1NodePort2 --> KubeProxy2KubeProxy1 -- "转发" --> SVCKubeProxy2 -- "转发" --> SVCSVC -- "负载均衡" --> PodA1SVC -- "负载均衡" --> PodA2

流量转发详解

  1. 外部请求:客户端向集群中任意一个节点的 IP 地址和指定的 NodePort(例如 http://192.168.1.101:30080)发起请求。
  2. 节点接收:请求到达 Node 1。Node 1 上的 kube-proxy 组件早已通过监听 API Server 得知了 NodePort Service 的存在,并配置了相应的 iptablesIPVS 规则。
  3. 内部转发kube-proxy 捕获到访问 30080 端口的流量,将其转发给该 Service 内部的 ClusterIP
  4. 负载均衡:Service 接收到流量后,再根据其负载均衡策略,将请求最终分发给一个健康的后端 Pod(如 Pod A1 或 Pod A2),即使这个 Pod 运行在另一个节点上(如 Node 2)

NodePort 的默认端口范围通常是 30000-32767。这个高位端口范围是为了避免与节点上可能运行的其他系统服务(如 SSH 的 22 端口)发生冲突。

2.3 实战:创建一个 NodePort Service

让我们来为一个 Nginx 应用创建一个 NodePort Service。

(1) 准备应用 Deployment

首先,我们创建一个简单的 Nginx Deployment,包含两个副本。

nginx-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-deployment
spec:replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginximage: nginx:1.21ports:- containerPort: 80
(2) 创建 NodePort Service

接下来,我们编写 Service 的 YAML 文件,注意 type 字段。

nginx-nodeport-service.yaml:

apiVersion: v1
kind: Service
metadata:name: nginx-service
spec:# 关键点: 将 Service 类型设置为 NodePorttype: NodePortselector:# 这个 selector 必须与 Deployment template 中的 labels 匹配app: nginxports:- protocol: TCP# Service 自身的端口 (在 ClusterIP 上)port: 80# Pod 容器暴露的端口targetPort: 80# 在节点上暴露的端口。如果省略,Kubernetes 会自动分配一个# nodePort: 30080 

提示nodePort 字段是可选的。如果省略,Kubernetes 会在 30000-32767 范围内自动选择一个未被占用的端口。在生产中,建议让 K8s 自动分配以避免手动管理和冲突。

(3) 部署与验证

现在,在终端中应用这两个 YAML 文件:

# 部署 Deployment
kubectl apply -f nginx-deployment.yaml# 部署 Service
kubectl apply -f nginx-nodeport-service.yaml

查看 Service 的状态:

kubectl get service nginx-service
# 或者简写: kubectl get svc nginx-service

你会看到类似以下的输出:

NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-service   NodePort   10.109.25.137   <none>        80:30080/TCP   2m

这里的 PORT(S)80:30080/TCP 清晰地告诉我们:

  • Service 的 ClusterIP (10.109.25.137) 在 80 端口上监听。
  • 这个 80 端口被映射到了所有节点上的 30080 端口。
(4) 访问服务

获取你的任意一个 Worker Node 的 IP 地址(如果你使用 Minikube,可以通过 minikube ip 获取):

# 如果是多节点集群,获取所有节点的IP
kubectl get nodes -o wide

假设你的一个节点 IP 是 192.168.49.2,现在你可以通过浏览器或 curl 访问:

curl http://192.168.49.2:30080

你会看到 Nginx 的欢迎页面!你可以尝试访问其他节点的 IP 加上相同的 NodePort,结果是一样的。

2.4 NodePort 的优缺点与适用场景

方面描述
优点简单直观:配置非常简单,无需额外组件。
兼容性强:不依赖任何特定的云环境,在自建机房、本地环境(Minikube/Kind)中都能工作。
快速验证:非常适合开发、测试和演示场景,可以快速暴露服务进行验证。
缺点端口受限:只能使用高位端口范围,不适合用作标准的 80/443 端口服务。
IP 依赖:客户端需要知道至少一个节点的 IP 地址。如果该节点宕机,客户端需要换一个 IP 访问。
管理不便:管理一组节点 IP 列表对最终用户不友好,也不利于做高可用。
无原生负载均衡:它只是端口转发,虽然 Service 层面有负载均衡,但从外部看,流量是直接打到某个具体节点上的。
适用场景1. 开发和测试环境:开发人员需要从本地快速访问集群内的服务。
2. 临时演示:向他人展示应用功能。
3. 特定应用:某些应用本身就需要一个固定的、非标准的端口进行访问。
4. 作为更高级暴露方式(如 LoadBalancer、Ingress)的基础

三、LoadBalancer:云时代的“智能网关”

当你的应用需要正式上线,面向公网提供稳定、高可用的服务时,NodePort 就显得力不从心了。这时,LoadBalancer 类型闪亮登场。

3.1 LoadBalancer 是什么?

LoadBalancer Service 是在 NodePort 之上构建的、与云平台深度集成的解决方案。当你创建一个 type: LoadBalancer 的 Service 时,Kubernetes 会:

  1. 完成 NodePort Service 的所有工作(即创建一个 ClusterIP 和一个 NodePort)。
  2. 额外地,向其所在的云平台(如 AWS, GCP, Azure)发起一个 API 调用,请求创建一个外部负载均衡器 (Load Balancer)
  3. 云平台创建 LB 后,会为其分配一个公网 IP 地址,并将这个 LB 配置为将流量转发到集群中所有节点的同一个 NodePort 上。
  4. 这个公网 IP 地址最终会被写回到 Service 对象的 status.loadBalancer.ingress 字段中,也就是我们 kubectl get svc 时看到的 EXTERNAL-IP

3.2 LoadBalancer 的工作原理

LoadBalancer 引入了云厂商的外部组件,其流量路径更为强大:

graph TDsubgraph InternetUser[外部用户/客户端]endsubgraph "Cloud Provider (e.g., AWS, GCP, Azure)"ExternalLB[Cloud Load Balancer (Public IP: 8.8.8.8)]endsubgraph Kubernetes 集群subgraph Node 1KubeProxy1[kube-proxy]PodA1[Pod A]NodePort1[eth0: 30080]endsubgraph Node 2KubeProxy2[kube-proxy]PodA2[Pod A]NodePort2[eth0: 30080]endSVC[Service (Type: LoadBalancer)]endUser -- "请求 http://8.8.8.8:80" --> ExternalLBExternalLB -- "健康检查 & 转发" --> NodePort1ExternalLB -- "健康检查 & 转发" --> NodePort2NodePort1 --> KubeProxy1 --> SVCNodePort2 --> KubeProxy2 --> SVCSVC -- "负载均衡" --> PodA1SVC -- "负载均衡" --> PodA2

流量转发详解

  1. 公网访问:用户直接访问由云厂商提供的负载均衡器的公网 IP(例如 http://8.8.8.8)。
  2. 云端负载均衡:云平台的 LB 接收到流量。它会持续对集群中所有节点的 NodePort 进行健康检查,只将流量转发到健康的节点上。
  3. 节点接收:流量到达某个健康节点的 NodePort(例如 Node 130080 端口)。
  4. 内部转发:接下来的流程与 NodePort 完全相同,kube-proxy 将流量转发至 ClusterIP,再由 Service 负载均衡到最终的 Pod。

3.3 实战:创建一个 LoadBalancer Service

重要前提LoadBalancer Service 强依赖于云环境的支持。你必须在像阿里云 ACK、腾讯云 TKE、AWS EKS、Google GKE 等托管 K8s 服务上,或者在自建集群中安装了类似 MetalLB 这样的负载均衡器插件,才能成功创建。在本地的 Minikube/Kind 环境中,直接创建会使 EXTERNAL-IP 永远处于 <pending> 状态。

假设我们已在支持的云环境中,修改 Service YAML 如下:

nginx-lb-service.yaml:

apiVersion: v1
kind: Service
metadata:name: nginx-service-lb
spec:# 关键点: 将 Service 类型设置为 LoadBalancertype: LoadBalancerselector:app: nginxports:- protocol: TCP# 外部负载均衡器监听的端口port: 80# Pod 容器暴露的端口targetPort: 80

部署并查看 Service:

kubectl apply -f nginx-lb-service.yaml
kubectl get svc nginx-service-lb -w # -w 参数可以持续观察变化

初始状态下,EXTERNAL-IP 会是 <pending>

NAME               TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
nginx-service-lb   LoadBalancer   10.101.99.10    <pending>     80:31567/TCP   10s

等待一两分钟,待云平台完成 LB 的创建和配置后,状态会自动更新:

NAME               TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
nginx-service-lb   LoadBalancer   10.101.99.10    34.120.55.99   80:31567/TCP   2m

现在,你就可以通过这个公网 IP http://34.120.55.99 从世界任何地方访问你的 Nginx 服务了!

3.4 LoadBalancer 的优缺点与适用场景

方面描述
优点生产级方案:提供一个稳定的单一公网入口点,对用户友好。
高可用:云厂商的 LB 本身就是高可用的,并能自动处理节点故障,只将流量发往健康节点。
集成性好:与云生态(如SSL证书、WAF等)无缝集成。
缺点云厂商锁定:其实现依赖于特定的云提供商,不具备可移植性。
成本:每创建一个 LoadBalancer 类型的 Service,云厂商通常都会创建一个新的 LB 实例,这会产生额外的费用。如果服务众多,成本会迅速增加。
灵活性有限:通常一个 LB 对应一个 Service,对于复杂的基于域名/路径的路由需求,LoadBalancer 难以胜任(这正是 Ingress 要解决的问题)。
适用场景1. 生产环境中的公网服务:Web 服务器、对外 API 等需要稳定公网 IP 的应用。
2. 需要暴露 TCP/UDP 四层服务的场景:如数据库、消息队列等。

四、三大 Service 类型的终极对决

为了帮助你更清晰地做出选择,我们用一个表格来总结 ClusterIPNodePortLoadBalancer 的核心区别。

特性 (Feature)ClusterIPNodePortLoadBalancer
访问层级仅集群内部节点IP + 端口 (间接外部)公网 IP (直接外部)
核心原理创建一个虚拟 IP在节点上映射静态端口集成云厂商的负载均衡器
工作基础自身是基础建立在 ClusterIP 之上建立在 NodePort 之上
公网 IP无 (使用节点公网IP)有 (由云厂商分配)
主要适用场景服务间内部通信开发、测试、临时演示生产环境公网服务
环境依赖强依赖云环境或插件
成本可能产生费用
配置复杂度简单 (默认类型)简单简单 (但依赖环境)

五、常见问题与注意事项

5.1 我在本地 Minikube/kind 环境,能用 LoadBalancer 吗?

可以,但需要“模拟器”。对于 Minikube,它提供了一个非常方便的命令:

minikube tunnel

在一个新的终端窗口运行此命令后,Minikube 会为 LoadBalancer 类型的 Service 分配一个本地 IP 地址,并将其填入 EXTERNAL-IP 字段,让你可以在本地模拟云环境的行为。对于 Kind 等其他本地环境,可以安装 MetalLB 这类裸金属负载均衡器。

5.2 NodePort 的端口可以自己指定吗?

可以。在 Service YAML 的 ports 定义中,通过 nodePort 字段可以手动指定一个端口。

ports:
- port: 80targetPort: 80nodePort: 32000 # 手动指定

注意:手动指定端口需要自己管理,确保它在 NodePort 范围内且未被其他 Service 占用,否则会导致创建失败。通常建议让 Kubernetes 自动分配。

5.3 LoadBalancer 的 EXTERNAL-IP 一直是 <pending> 怎么办?

这是 LoadBalancer 新手最常见的问题,排查思路如下:

  1. 确认环境:你是否在受支持的云平台或已安装 MetalLB 的环境中?在原生 Minikube/Kind 中这是预期行为。
  2. 查看事件:使用 kubectl describe svc <service-name> 查看 Service 的详细信息和事件(Events)部分,通常会有云控制器管理器的报错信息。
  3. 检查配额:检查你的云账户,是否还有创建负载均衡器的配额。
  4. 网络策略:检查是否有 NetworkPolicy 阻止了从 LB 到节点的流量。

六、总结

至此,我们完成了对 Kubernetes Service 暴露外部访问能力的探索。回顾全文,核心要点如下:

  1. 核心需求ClusterIP 解决了集群内部通信,而 NodePortLoadBalancer 解决了将服务暴露给外部世界的需求。
  2. NodePort:通过在每个节点上映射一个静态端口(NodePort)来实现外部访问。它配置简单,不依赖云厂商,是开发、测试和快速验证的理想选择,但其端口和 IP 管理不便,不适合正式生产。
  3. LoadBalancer:在 NodePort 基础上,自动与云平台集成,创建并配置一个外部负载均衡器,提供一个稳定的公网 IP。这是在云环境中暴露服务的标准、高可用的生产级方案,但会产生额外成本且依赖特定云环境。
  4. 层级关系与选择LoadBalancer 依赖于 NodePortNodePort 依赖于 ClusterIP,三者是层层递进的关系。技术选型应基于应用场景(内部/测试/生产)和运行环境(本地/云)综合决定。

虽然 LoadBalancer 功能强大,但“一个服务一个公网 IP”的模式在微服务架构下成本高昂且管理不便。如果我们需要根据域名或 URL 路径将流量路由到不同服务,该怎么办?这就是我们下一阶段将要深入探讨的、更高级的七层流量管理工具——Ingress。敬请期待!


http://www.xdnf.cn/news/20237.html

相关文章:

  • B 题 碳化硅外延层厚度的确定
  • 【Linux学习笔记】信号的深入理解之软件条件产生信号
  • Docker在Windows与Linux系统安装的一体化教学设计
  • AI 基础设施新范式,百度百舸 5.0 技术深度解析
  • 【AI编程工具】快速搭建图书管理系统
  • 9.5 递归函数+常见算法
  • Preprocessing Model in MPC 7 - Matrix Triples and Convolutions Lookup Tables
  • LinuxC++项目开发日志——高并发内存池(1-定长内存池)
  • finally 与 return的执行顺序
  • Web相关知识(草稿)
  • MySQL高可用之组复制(MGR)
  • Web基础、HTTP/HTTPS协议与Nginx详解
  • 商城系统——项目测试
  • JUC的安全并发包机制
  • Python 值传递 (Pass by Value) 和引用传递 (Pass by Reference)
  • go面试题-什么是用户态和内核态
  • 数组本身的深入解析
  • 研发文档撰写质量参差不齐该怎么办
  • 突破大语言模型推理瓶颈:深度解析依赖关系与优化策略
  • YOLOv8主干网络替换为UniConvNet的详细指南
  • Unity中,软遮罩SoftMaskForUGUI的使用
  • 25高教社杯数模国赛【E题保姆级思路+问题分析】
  • 【Day 20】148.排序链表
  • Electron 执行python脚本
  • IPV6、广播地址
  • 单片机实现分页显示环形更新的历史数据
  • 算法随笔(一)
  • S32K328上芯片内部RTC的使用和唤醒配置
  • 深度学习篇---MNIST:手写数字数据集
  • 基础排序--冒泡--选择--插入