Service与Ingress

Ingress

Ingress 是 Kubernetes 中的一个原生 API 资源对象,用于管理集群外部对集群内部服务的 HTTP 和 HTTPS 访问。它提供了一种声明式的路由规则配置方式,根据主机名(host)、路径(path)等规则,将外部流量路由到不同的后端 Service,从而实现负载均衡、SSL/TLS 终止、基于名称的虚拟主机等功能。

  • Ingress 实际上就是七层代理,而 service 是基于 iptables 或 ipvs 实现的四层代理。

核心概念

  • Ingress Controller:负责读取 Ingress 中的配置,然后转换成自身的配置(每一次更新 Ingress 的配置 都会在 Ingress Controller 生成相应的转发规则)
    • 实际实现路由的组件(如 NGINX、Traefik、HAProxy 等),它监视 Ingress 资源的变化,并配置反向代理(如负载均衡器)来处理外部请求。集群中必须部署至少一个 Ingress Controller,Ingress 才能生效。
    • 常见 Ingress Controller:
      • NGINX Ingress Controller(最流行,但官方已宣布将于 2026 年 3 月后停止维护)。
      • Traefik、HAProxy、Istio 等。
      • 云厂商特定:如 GKE 的 Google Cloud Load Balancer、AWS ALB Ingress Controller。
  • Ingress 资源:定义路由规则的 YAML 对象,仅描述“应该如何路由”,本身不处理流量。
  • IngressClass:用于指定使用哪个 Controller 处理特定的 Ingress 资源,避免冲突。

相比其他暴露服务的方式(如 NodePort 或 LoadBalancer 类型 Service),Ingress 的优势在于:

  • 使用单个外部 IP 暴露多个服务,节省资源。
  • 支持高级路由(如路径重写、流量拆分)。
  • 便于统一管理域名和 TLS 证书。

注意事项与现状(截至 2025 年底):

  • Kubernetes 官方已冻结 Ingress API 的开发,不再添加新功能,并推荐迁移到更现代的 Gateway API(更灵活、支持更多协议和高级特性)。
  • Ingress 仍被广泛使用,但对于新项目,建议优先考虑 Gateway API。
  • 参考官方文档:https://kubernetes.io/docs/concepts/services-networking/ingress/

Ingress 是 Kubernetes 中暴露 HTTP 服务的最常用方式之一,适合大多数 Web 应用场景。如果需要更复杂的流量管理(如服务网格),可结合 Istio 等工具使用。

参考文档:


Ingress 数据流

  1. 客户端访问 Ingress Controller 的 Service(NodePort / LoadBalancer)
  2. Ingress Controller 的 Service,会转发到 Ingress Controller 的 Pod
  3. Ingress Controller Pod 监视 Ingress 资源和后端 Service 的 Endpoints/EndpointSlices。并根据主机名(host)和路径(path)匹配规则,决定转发到哪个后端 Service。
  4. 最后直接将数据报文发送到后端 Pod 而不再经由后端服务的 Service
  • 转发到后端 Pod 大多数主流 Ingress Controller(如 NGINX Ingress Controller、Traefik)会直接将流量发送到后端 Pod 的 IP(通过监视 Endpoints API 获取),而非经过后端 Service 的 ClusterIP(VIP)和 kube-proxy。
  • 原因:这样可以绕过 kube-proxy 的 L4 负载均衡,支持更高级的 L7 特性(如基于 cookie 的 session affinity、自定义负载均衡算法、正则路径等)。
  • 这不会丢失健康检查或端点更新:Controller 实时监视 Endpoints,避免将流量发送到不健康的或已终止的 Pod。
  • 官方 NGINX Ingress 文档明确说明:“The Ingress-Nginx Controller does not use Services to route traffic to the pods. Instead it uses the Endpoints API in order to bypass kube-proxy…”
  • Traefik 类似:“Traefik automatically requests endpoint information… will connect directly to the endpoints (pods)"。
  • 这种设计减少了不必要的跳转(少一次 NAT 和 kube-proxy 处理),提升了性能,尤其在高流量场景下。

Ingress 语法

apiVersion: networking.k8s.io/v1   # 资源所属的API群组和版本,k8s1.19 后 ingress 才为稳定版v1
kind: Ingress   # 资源类型标识符
metadata:  # 元数据
  name <string>  # 资源名称
  annotations:   # 资源注解
    xxx
  namespace <string>  # 名称空间
spec:
  ingressClassName  <string>   # ingress类名称,用于指定适配的控制器
  rules <[]Object>   # Ingress规则列表
  - host <string>   # 可选字段,虚拟主机的FQDN,支持“*”前缀通配;如未指定host,则只能使用IP进行访问
    http <Object>
      paths <[]Object>   # 虚拟主机PATH定义的列表,由path和backend组成;每个路径都有一个由 serviceName 和 servicePort 定义的关联后端。
      - path <string>   # 流量匹配的HTTP PATH,必须以/开头
        pathType <string>  # 必选字段,支持Exact、Prefix和ImplementationSpecific
        backend <Object>   # 匹配到的流量转发到的目标后端
          resource <Object>   # 引用的同一名称空间下的资源,与service互斥
          service <object>  # 关联的后端Service对象
            name <string>  # 后端Service的名称
            port <object>  # 后端Service上的端口对象
              name <string>   # 端口名称
              number <integer>   # 端口号
  tls <[]Object>   # TLS配置,用于指定上rules中定义的哪些host需要工作HTTPS模式
  - hosts <[]string>   # 使用同一组证书的主机名称列表
    secretName <string>   # 保存于数字证书和私钥信息的secret资源名称
  defaultBackend <Object>   # 默认backend的定义,可嵌套字段及使用格式跟rules字段中的相同
    resource	<Object>
    service	<Object>

.spec.rules[].host

host host 头部 匹配与否?
*.foo.com bar.foo.com 基于相同的后缀匹配
*.foo.com baz.bar.foo.com 不匹配,通配符仅覆盖了一个 DNS 标签
*.foo.com foo.com 不匹配,通配符仅覆盖了一个 DNS 标签

.spec.rules[].http.paths[].pathType

在 Kubernetes Ingress(API 版本 networking.k8s.io/v1)中,每个路径(path)必须显式指定 pathType,否则资源验证会失败。自 Kubernetes 1.18 引入该字段以来,支持以下 三种 类型:

pathType 描述 匹配行为示例(假设 path: /foo)
Exact 精确匹配 URL 路径,大小写敏感。必须完全一致,不考虑尾部斜杠差异。 匹配:/foo
不匹配:/foo//FOO/foo/bar
Prefix 前缀匹配,按 / 分割路径逐元素匹配,大小写敏感。尾部斜杠被忽略,不支持子字符串匹配。 匹配:/foo/foo//foo/bar/foo/bar/baz
不匹配:/foobar/foo/barbaz
ImplementationSpecific 匹配行为由具体的 IngressClass(Controller)决定。可视为 Prefix/Exact 的变体,或自定义(如支持正则)。许多 Controller 默认等同于 Prefix。 取决于 Controller(如 NGINX 可结合注解支持正则匹配)

匹配优先级规则

  • 最长路径优先(例如 /foo/bar 优先于 /foo)。
  • 长度相同时,Exact 优先于 Prefix
  • ImplementationSpecific 的优先级取决于具体实现。

Ingress YAML 示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1  # 示例注解,根据 Controller 不同而变
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /app1
        pathType: Prefix
        backend:
          service:
            name: app1-service
            port:
              number: 80
      - path: /app2
        pathType: Prefix
        backend:
          service:
            name: app2-service
            port:
              number: 80
  tls:  # 可选:TLS 配置
  - hosts:
    - example.com
    secretName: example-tls-secret

Traefik

部署

# 添加官方 Traefik Helm repo
helm repo add traefik https://traefik.github.io/charts
helm repo update

# 创建专用 namespace(推荐)
kubectl create namespace traefik

# 拉取 chart(为后续升级提速)
helm pull traefik/traefik --version 35.2.0

# 导出并修改 values 文件
helm show values traefik/traefik --version 35.2.0 > values-traefik-35.2.0.yaml

# 安装/升级
helm upgrade --install traefik traefik/traefik \
  --version 35.2.0 \
  --namespace traefik \
  -f values-traefik-35.2.0.yaml

Service

Endpoints

Endpoints 资源代表了 Service 后端实际运行的 Pod 的网络地址(IP 地址和端口)。当一个 Service 被创建时,Kubernetes 会根据 Service 的选择器(selector)来查找匹配的 Pod,并将这些 Pod 的 IP 和端口信息填充到 Endpoints 对象中。它是 手动管理服务后端 的方式之一,但在大多数情况下,EndpointSlice 资源和 Endpoint 控制器会自动创建和管理它。

apiVersion: v1
kind: Endpoints
metadata:
  name: my-service # Endpoints对象的名称通常与它关联的Service名称相同
subsets:
  - addresses: # 匹配的后端Pod的IP地址列表
      - ip: "10.244.0.4"
      - ip: "10.244.0.5"
    ports: # 匹配的后端Pod的端口列表
      - port: 8080
        name: http
        protocol: TCP

EndpointSlice

EndpointSlice 是一种更具可扩展性和效率的 Endpoints 替代品。它旨在解决在拥有大量 Pod 后端的 Service 中,单个 Endpoints 对象变得非常大而导致的性能问题。每个 EndpointSlice 对象通常只包含少量的 Endpoint(最多约 100 个),这意味着对于一个大型 Service,可能会有多个 EndpointSlice 对象,每个对象包含一部分后端信息。这使得网络传输和客户端(如 kube-proxy)处理更新时更加高效。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-abcxyz # 自动生成的名称,通常包含Service名称
  labels:
    kubernetes.io/service-name: my-service # 关联的Service名称
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 8080
endpoints: # 包含的具体端点列表
  - addresses: # 后端Pod的IP地址
      - "10.244.0.6"
    conditions: # 端点的状态信息
      ready: true
      serving: true
    hostname: pod-name-a
  - addresses:
      - "10.244.0.7"
    conditions:
      ready: false # 示例:这个Pod尚未准备好接收流量
      serving: true
    hostname: pod-name-b

Endpoints 与 EndpointSlice 的区别

K8s(Kubernetes)中的 EndpointsEndpointSlice 都是用来跟踪 Service 对应后端 Pods 网络地址信息的对象,但 EndpointSlice 是为了解决大规模集群中 Endpoints 对象的扩展性问题而引入的。


特性 Endpoints EndpointSlice
引入时间 早期(v1 API) v1.17 引入,v1.21 默认启用
结构 单个对象,包含所有后端 Pod 地址和端口的完整列表。 分片 的对象,每个对象只包含一部分后端 Pod 地址和端口。
可扩展性 :当 Service 后端 Pod 数量巨大时(如数千个),单个 Endpoints 对象会变得非常大,导致: 1. etcd 压力:对象更新、存储和监控(watch)消耗大量资源。 2. 网络延迟:大型对象在网络传输和 API Server 与客户端之间同步时效率低下。 :将大型 Service 的 Endpoint 信息分割成多个较小的 Slice,显著减少单个对象的大小,从而: 1. 降低 etcd 压力:更新只影响必要的 Slice。 2. 提高性能:减少网络开销和处理时间。
主要用途 早期和小型集群。 推荐用于大规模集群,是 Kube-proxy、Ingress Controller 等组件获取 Service 后端信息的首选方式。
控制器 由 Endpoint Controller 维护。 由 EndpointSlice Controller 维护。

Endpoints (传统方式)

  • 格式举例 (简化):

    {
      "apiVersion": "v1",
      "kind": "Endpoints",
      "metadata": {
        "name": "my-service" 
      },
      "subsets": [
        {
          "addresses": [
            {"ip": "10.0.0.1"},
            {"ip": "10.0.0.2"},
            // ... 几千个 IP
          ],
          "ports": [
            {"port": 8080}
          ]
        }
      ]
    }
  • 问题所在: 想象一个 Service 有 3000 个 Pod 后端。任何一个 Pod 的状态变化(例如重启、IP 变化)都会导致这个包含 3000 个地址的 巨大 Endpoints 对象被 完全替换,给 API Server、etcd 和所有监视它的组件(如 Kube-proxy)带来巨大的负担。

EndpointSlice (现代方式)

  • 设计理念: 分而治之。一个 Service 的后端地址不再存储在一个对象中,而是分散存储在多个 EndpointSlice 对象中。

  • 分片规则: 每个 EndpointSlice 默认最多存储大约 100 个 Endpoints (可以配置)。如果一个 Service 有 3000 个 Pod,它将会有大约 30 个 EndpointSlice 对象。

  • 格式举例 (简化):

    // EndpointSlice 1 of 30 for 'my-service'
    {
      "apiVersion": "discovery.k8s.io/v1",
      "kind": "EndpointSlice",
      "metadata": {
        "name": "my-service-abcde",
        "labels": {
          "kubernetes.io/service-name": "my-service"
        }
      },
      "addressType": "IPv4",
      "endpoints": [
        {"addresses": ["10.0.0.1"], "targetRef": ...},
        {"addresses": ["10.0.0.2"], "targetRef": ...},
        // ... 最多约 100 个 endpoints
      ],
      "ports": [
        {"port": 8080}
      ]
    }
  • 优势: 当 Pod 状态发生变化时,只需要更新或替换包含该 Pod 的 小型 EndpointSlice 对象,而不是整个 Service 的 Endpoints 列表。这极大地提高了大规模集群的性能和可扩展性。


总结:EndpointSlice 是对 Endpoints 机制的优化升级。

对于集群内需要获取 Service 后端地址信息的组件(如 Kube-proxy),它们现在主要通过监视 EndpointSlice 对象来获取信息,这让 Kubernetes 在处理拥有大量后端 Pod 的 Service 时更高效、更具弹性。