第4章:vLLM服务暴露:Service类型介绍、ClusterIP、NodePort、LoadBalancer的区别

好,咱们接着聊。上一章我们把 vLLM 服务部署到了 K8s 集群里,但 Pod 这东西说白了是“飘忽不定”的——它可能被调度到任意节点,IP 也会随重启而改变。那问题来了:客户端怎么稳定地访问它?

答案就是 Service。Service 是 K8s 里用来暴露 Pod 的抽象层。它给一组 Pod 提供一个固定的“入口”,不管 Pod 怎么变,客户端只管访问这个入口就行。

我个人习惯把 Service 比作“前台接待”。你打电话到公司,前台帮你转接到具体工位。工位换了,你只要记住前台号码就行。Service 就是那个前台。

K8s 提供了三种主要的 Service 类型:ClusterIPNodePortLoadBalancer。它们各有各的适用场景。下面我一个个讲,顺便聊聊我踩过的坑。

4.1 ClusterIP:集群内部访问

ClusterIP 是默认类型。它给 Service 分配一个集群内部的虚拟 IP,只有集群内的 Pod 能访问。外部网络是连不进来的。

你想想看,如果你的 vLLM 服务只给集群内的其他微服务调用,比如一个前端应用、一个数据处理管道,那 ClusterIP 就够用了。简单、安全、没额外开销。

核心特点:

  • 分配一个虚拟 IP(ClusterIP),仅集群内可达
  • 通过 kube-proxy 实现负载均衡和转发
  • 默认类型,无需额外配置

举个例子,我部署一个 vLLM 推理服务,集群内有个 Python 脚本要调它的 API。我会这么写 Service:

apiVersion: v1
kind: Service
metadata:
  name: vllm-service-clusterip
spec:
  selector:
    app: vllm
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8000
  type: ClusterIP

然后集群内的 Pod 直接用 vllm-service-clusterip:8000 就能访问。注意,这里用的是 Service 名称,K8s 的 DNS 会自动解析成 ClusterIP。

小提示:ClusterIP 的 IP 是虚拟的,你没法在宿主机上 ping 通它。它只在 iptables/IPVS 规则里存在。我第一次用的时候还傻乎乎去 ping,结果超时了……

4.2 NodePort:从集群外部访问(简单粗暴)

NodePort 是在 ClusterIP 基础上,在每个节点的 IP 上开一个端口(范围 30000-32767)。外部流量通过 节点IP:NodePort 就能访问到 Service。

说白了,NodePort 就是把 Service 的端口“映射”到了宿主机上。你想想看,如果你在开发环境想快速验证一下 vLLM 服务能不能跑通,或者你手头没有 LoadBalancer,NodePort 是最快的办法。

我在项目中遇到过这种情况:客户现场没有 LoadBalancer,只有几台裸金属服务器。我就用 NodePort 暴露服务,然后让运维人员在防火墙里放行对应端口。虽然不优雅,但能干活。

注意:NodePort 的端口范围是固定的(30000-32767)。如果你需要暴露多个服务,注意端口不要冲突。我曾经因为两个 Service 用了同一个 NodePort,结果其中一个一直报错,排查了半天才发现是端口冲突。

配置示例:

apiVersion: v1
kind: Service
metadata:
  name: vllm-service-nodeport
spec:
  selector:
    app: vllm
  ports:
    - protocol: TCP
      port: 8000
      targetPort: 8000
      nodePort: 30080   # 可选,不指定则随机分配
  type: NodePort

访问方式:http://任意节点IP:30080。注意,如果节点有多个 IP,kube-proxy 会监听所有 IP 上的这个端口。

NodePort 的优缺点:

  • 优点:配置简单,不依赖外部组件,适合开发测试
  • 缺点:端口范围受限,安全性差(直接暴露节点端口),无法做高级负载均衡

4.3 LoadBalancer:云原生时代的标配

LoadBalancer 是 NodePort 的“升级版”。它在 NodePort 的基础上,额外调用云厂商的负载均衡器(比如阿里云 SLB、AWS ELB),给 Service 分配一个公网 IP。外部流量先到负载均衡器,再转发到节点端口,最后到 Pod。

我个人觉得,如果你在云上跑 K8s,LoadBalancer 是首选。它把网络接入的复杂性交给了云厂商,你只需要声明 type: LoadBalancer,剩下的云平台帮你搞定。

我记得有一次帮客户迁移 vLLM 服务,从自建机房搬到阿里云。原来用 NodePort 暴露,运维要手动配 Nginx 做反向代理。换成 LoadBalancer 后,一行 YAML 就搞定了,省了不少事。

配置示例:

apiVersion: v1
kind: Service
metadata:
  name: vllm-service-lb
spec:
  selector:
    app: vllm
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000
  type: LoadBalancer

创建后,用 kubectl get svc 查看 EXTERNAL-IP 字段,那就是公网 IP。等它从 <pending> 变成具体 IP,就可以用了。

避坑指南:LoadBalancer 的 EXTERNAL-IP 如果一直 <pending>,八成是云厂商的插件没装好,或者账号权限不够。我曾经在测试环境折腾了一下午,最后发现是云平台的 RAM 角色没配对。

4.4 三种类型对比:一张表说清楚

为了方便你对比,我整理了一张表。嗯,面试也经常考这个。

特性 ClusterIP NodePort LoadBalancer
访问范围 集群内部 集群外部(节点IP+端口) 集群外部(公网IP)
端口范围 任意 30000-32767 任意(由负载均衡器决定)
依赖组件 云厂商负载均衡器
适用场景 微服务间调用 开发测试、小规模暴露 生产环境、公网服务
安全性 高(仅集群内) 低(直接暴露节点) 中(可配合安全组)
成本 无额外成本 无额外成本 按负载均衡器计费

4.5 核心逻辑:一张图看懂

下面我用 SVG 画了一张图,帮你理清这三种 Service 的流量路径。你一看就明白。

三种 Service 类型流量路径对比 外部用户 云负载均衡器 节点 1 (IP: 10.0.0.1) kube-proxy Pod (vLLM) NodePort: 30080 节点 2 (IP: 10.0.0.2) kube-proxy Pod (vLLM) NodePort: 30080 集群内部 (ClusterIP: 10.96.0.1:8000) LoadBalancer NodePort ClusterIP 图例: LoadBalancer 流量 NodePort 流量 ClusterIP 流量 注:虚线表示可选路径,实线表示默认路径

从图上你能看到:

  • ClusterIP:流量只在集群内部走,不经过外部网络
  • NodePort:外部用户直接访问节点 IP + 端口,kube-proxy 转发到 Pod
  • LoadBalancer:外部用户先到云负载均衡器,再分发到各节点的 NodePort

4.6 怎么选?我的建议

说了这么多,到底用哪个?我根据经验给个参考:

  • 开发环境:用 ClusterIP 就够了。你可以在集群内跑个测试 Pod 来调 vLLM。如果非要外部访问,NodePort 也行,但记得用完关掉。
  • 测试/预发布环境:NodePort 或者 LoadBalancer 都行。看你们有没有云资源。我习惯在测试环境用 NodePort,省得每次创建 LoadBalancer 还要等云平台分配 IP。
  • 生产环境:必须 LoadBalancer。别省那点钱。NodePort 直接暴露节点端口,安全审计都过不了。而且 LoadBalancer 自带健康检查和流量分发,比 NodePort 靠谱得多。

再次提醒:如果你用 NodePort 暴露 vLLM 服务,记得在安全组里限制来源 IP,别让全网都能扫到你的 30080 端口。我曾经见过一个客户,NodePort 暴露了 Redis,结果被挖矿程序盯上了……

好了,这一章就到这里。三种 Service 类型你心里有数了吧?下一章我们聊聊 Ingress,那才是真正生产环境暴露服务的“正规军”。


公众号:蓝海资料掘金营,微信deep3321