首页 / Kubernetes (k8s) 入门教程 / Service 基础

Kubernetes (k8s) 入门教程

Service 基础

本教程共 65 篇 · 第 25 篇 · 更新于 2026-08-14 · 约 12 分钟阅读

KubernetesService虚拟IP标签选择器EndpointSlice服务发现

本节目标:搞懂为什么需要 Service,它怎么用虚拟 IP 把一组会变的 Pod 稳稳兜住,以及 Service 背后的 EndpointSlice 是怎么回事。

先说个扎心的事实。你辛辛苦苦用 Deployment 起了三个副本,它们各有各的 IP。可这些 Pod 说没就没——节点压力过大时被驱逐、程序崩了被重启、滚动更新时旧 Pod 被删新 Pod 顶上。Pod 的 IP 一直变,谁还记得住该连哪个?

这就是 Service(服务)要解决的问题。

25-1 Pod 的 IP 为什么不能直接用

容器组(Pod)在 Kubernetes 里是临时资源。它们随时可能被创建、销毁、调度到别的节点。你永远不该指望某一个 Pod 的 IP 长期有效。

打个比方。Pod 像临时工,今天来明天走。后端的 Pod 集合每时每刻都在变,前端想连后端时,到底该连哪个 IP?难道让前端自己盯着 Pod 的生老病死?这显然不现实。

Note

Kubernetes 给每个 Pod 分配独立 IP,但 Pod 是临时的。把”连某个具体 Pod 的 IP”写进前端代码,是新手最容易踩的坑。

所以 Kubernetes 需要一层抽象,把”会变的 Pod 集合”包装成一个稳定的访问入口。这一层抽象,就是 Service。

25-2 Service 到底是个什么

Service 是 Kubernetes 里的一个对象(Object),和 Pod、ConfigMap 一样,靠声明式(Declarative)的 YAML 来管理。它的作用很简单:把一组 Pod 在网络上公开出去,给它们一个固定的访问地址。

核心机制有两点:

  1. 给这组 Pod 分配一个虚拟 IP(cluster IP),这个 IP 在 Service 生命周期内基本不变。
  2. 用**标签选择器(selector)**挑出”哪些 Pod 算我的后端”。

前端只管连这个虚拟 IP。至于后面到底有几个 Pod、IP 是什么,Service 自己盯着。前端完全不需要知道。

Tip

Service 解耦了”调用方”和”被调用方”。后端随便换,前端连的地址纹丝不动。这就是服务发现的雏形。

25-3 标签选择器怎么挑 Pod

Service 通过 spec.selector 里的标签(Label)去匹配 Pod。只要 Pod 上的标签和它写的一致,就被纳入后端。

下面是个最基础的 Service,挑出所有 app.kubernetes.io/name: MyApp 的 Pod,把它们的 9376 端口收拢成一个服务:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

这里有两个端口要分清:

  • port:Service 自己对外暴露的端口,调用方连的是它。
  • targetPort:后端 Pod 实际监听的端口。

默认情况下 targetPort 会和 port 取同一个值。上面的例子里,调用方访问 Service 的 80 端口,流量被转给后端 Pod 的 9376 端口。

25-4 虚拟 IP 背后的 EndpointSlice

你可能会问:Service 怎么知道后端 Pod 换了一批?答案在 EndpointSlice。

每个 Service 背后,控制平面会维护一组 EndpointSlice 对象。它们记录着当前真正能用的后端端点(也就是那些就绪的 Pod IP + 端口)。Service 的控制器会持续扫描匹配选择器的 Pod,一旦 Pod 集合变化,就更新对应的 EndpointSlice。

所以在 v1.36.2 里,请你记住:真正干活的是 EndpointSlice,不是老旧的 Endpoints

Warning

Endpoints 这个旧 API 在 v1.33 已被标记为弃用。它不支持双栈、不支持新特性,端点超过 1000 还会被截断。新代码和配置一律用 EndpointSlice,别回头去用 Endpoints。

25-5 端口还能起名字

Pod 里的容器端口可以起名字,Service 的 targetPort 直接引用这个名字就行:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
    - name: http-web-svc
      port: 80
      targetPort: http-web-svc
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
    - name: nginx
      image: nginx:stable
      ports:
        - containerPort: 80
          name: http-web-svc

这样做的好处很实在。明天你给后端换了端口,只要 Pod 里端口名字不变,Service 完全不用改。客户端更不会受影响。

25-6 一个 Service 能开多个端口

有些服务同时要暴露 HTTP 和 HTTPS。Service 支持配置多个端口,但有个规矩:每个端口必须起名字,否则会冲突:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      port: 80
      targetPort: 9376
    - name: https
      port: 443
      targetPort: 9377

25-7 没有选择器的 Service 也存在

绝大多数 Service 靠选择器找 Pod。但有些场景,后端压根不在集群里:

  • 生产用外部数据库集群,测试环境用自建库。
  • 想把流量指到另一个命名空间,甚至另一个集群的服务。
  • 正在把老系统往 Kubernetes 迁,暂时只有部分后端进来了。

这种时候,你可以创建一个不带选择器的 Service,然后自己手写 EndpointSlice,把外部地址填进去。Kubernetes 不会自动帮你发现这些端点,得你来手动映射。

Note

端点 IP 不能是本地回路(127.0.0.0/8)或链路本地地址。也不能是别的 Service 的集群 IP,因为 kube-proxy 不支持把虚拟 IP 当目标。

25-8 Service 类型先有个印象

Service 不止一种。后面章节会细讲,这里先打个照面:

  • ClusterIP:默认类型,只在集群内部可访问。
  • NodePort:在每个节点上开一个固定端口,集群外能直接连节点 IP 访问。
  • LoadBalancer:借助云平台负载均衡器把服务暴露出去。
  • ExternalName:用 DNS 名字映射,不代理流量。

Ingress(入口)不是 Service 类型,它更像是集群的 HTTP 入口,后面有专门一章讲。

25-9 该记住的几个点

Service 的本质,是为”易变的后端 Pod 集合”提供一个稳定的虚拟 IP 和访问规则。它靠标签选择器挑后端,靠 EndpointSlice 跟踪实际端点。

它是解耦前后端的胶水层。前端连固定的 Service 地址,后端 Pod 怎么生灭都不影响调用方。

下章我们逐个拆开 Service 的三种基础类型,看 ClusterIP、NodePort、LoadBalancer 分别在什么场景用。