Ingress

使用一种感知协议的配置机制,使得 HTTP (或 HTTPS) 网络服务变得可用。这种机制理解 URI、主机名、路径等 Web 概念。Ingress 概念允许你根据通过 Kubernetes API 定义的规则,将流量映射到不同的后端。

特性状态: Kubernetes v1.19 [stable]

一个管理集群内服务外部访问的 API 对象,通常是 HTTP。

Ingress 可以提供负载均衡、SSL 终结以及基于名称的虚拟主机。

术语

为清晰起见,本指南定义以下术语

  • 节点(Node):Kubernetes 中的工作机器,是集群的一部分。
  • 集群(Cluster):一组运行由 Kubernetes 管理的容器化应用的节点。在此示例中,以及在大多数常见的 Kubernetes 部署中,集群中的节点不属于公共互联网。
  • 边缘路由器(Edge router):为你的集群强制执行防火墙策略的路由器。它可以是由云提供商管理的网关,也可以是物理硬件。
  • 集群网络(Cluster network):一组逻辑或物理连接,根据 Kubernetes 网络模型 促进集群内的通信。
  • Service(服务):一个 Kubernetes Service,它使用标签选择器识别一组 Pod。除非另有说明,否则假定 Service 仅具有在集群网络内可路由的虚拟 IP。

什么是 Ingress?

Ingress 将来自集群外部的 HTTP 和 HTTPS 路由暴露给集群内部的服务。流量路由由 Ingress 资源上定义的规则控制。

以下是一个简单示例,其中 Ingress 将所有流量发送到一个 Service

ingress-diagram

图. Ingress

Ingress 可以配置为提供外部可访问的 URL、负载均衡流量、终止 SSL / TLS,并提供基于名称的虚拟主机。一个 Ingress 控制器 负责实现 Ingress,通常通过负载均衡器,但它也可以配置你的边缘路由器或其他前端来帮助处理流量。

Ingress 不暴露任意端口或协议。将 HTTP 和 HTTPS 以外的服务暴露到互联网通常使用类型为 Service.Type=NodePortService.Type=LoadBalancer 的 Service。

前提条件

你必须拥有一个 Ingress 控制器 来满足 Ingress 的需求。仅仅创建 Ingress 资源本身不会产生任何效果。

你可能需要部署一个 Ingress 控制器,例如 ingress-nginx。你可以从许多Ingress 控制器中选择。

理想情况下,所有 Ingress 控制器都应符合参考规范。实际上,各种 Ingress 控制器操作方式略有不同。

Ingress 资源

一个最小的 Ingress 资源示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

一个 Ingress 需要 apiVersionkindmetadataspec 字段。Ingress 对象的名称必须是合法的DNS 子域名。有关使用配置文件的通用信息,请参见部署应用配置容器管理资源。Ingress 经常使用注解来配置某些选项,具体取决于 Ingress 控制器,例如rewrite-target 注解。不同的Ingress 控制器支持不同的注解。查阅你选择的 Ingress 控制器的文档以了解支持哪些注解。

Ingress spec 包含配置负载均衡器或代理服务器所需的所有信息。最重要的是,它包含一组与所有传入请求进行匹配的规则列表。Ingress 资源仅支持用于引导 HTTP(S) 流量的规则。

如果省略了 ingressClassName 字段,则应定义一个默认 Ingress 类

有些 Ingress 控制器可以在未定义默认 IngressClass 的情况下工作。例如,Ingress-NGINX 控制器可以通过 标志 --watch-ingress-without-class 进行配置。但是,推荐如下所示指定默认 IngressClass

Ingress 规则

每条 HTTP 规则包含以下信息

  • 可选的主机。在此示例中,未指定主机,因此规则适用于通过指定 IP 地址的所有入站 HTTP 流量。如果提供了主机(例如 foo.bar.com),则规则适用于该主机。
  • 路径列表(例如 /testpath),每个路径都有一个关联的后端,通过 service.nameservice.port.nameservice.port.number 进行定义。主机和路径都必须与传入请求的内容匹配,负载均衡器才会将流量导向所引用的 Service。
  • 后端(backend)是 Service 和端口名称的组合,如 Service 文档中所述,或者通过CRD定义的自定义资源后端。与规则的主机和路径匹配的发送到 Ingress 的 HTTP (和 HTTPS) 请求将被发送到列出的后端。

Ingress 控制器中通常配置一个 defaultBackend 来处理任何与规约中路径不匹配的请求。

默认后端 (DefaultBackend)

没有规则的 Ingress 会将所有流量发送到单个默认后端,此时 .spec.defaultBackend 是应该处理请求的后端。defaultBackend 通常是 Ingress 控制器的一个配置选项,而不是在 Ingress 资源中指定的。如果没有指定 .spec.rules,则必须指定 .spec.defaultBackend。如果未设置 defaultBackend,则不匹配任何规则的请求的处理方式将取决于 Ingress 控制器(查阅你的 Ingress 控制器文档以了解它如何处理这种情况)。

如果 Ingress 对象中的任何主机或路径都不匹配 HTTP 请求,则流量将路由到你的默认后端。

资源后端

Resource 后端是对与 Ingress 对象位于同一命名空间内的另一个 Kubernetes 资源的 ObjectRef。Resource 与 Service 是互斥设置,如果两者都指定,则验证将失败。Resource 后端的常见用途是将数据入站到带有静态资源的后端对象存储。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-resource-backend
spec:
  defaultBackend:
    resource:
      apiGroup: k8s.example.com
      kind: StorageBucket
      name: static-assets
  rules:
    - http:
        paths:
          - path: /icons
            pathType: ImplementationSpecific
            backend:
              resource:
                apiGroup: k8s.example.com
                kind: StorageBucket
                name: icon-assets

创建上述 Ingress 后,你可以使用以下命令查看它

kubectl describe ingress ingress-resource-backend
Name:             ingress-resource-backend
Namespace:        default
Address:
Default backend:  APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /icons   APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
Annotations:  <none>
Events:       <none>

路径类型

Ingress 中的每个路径都需要有相应的路径类型。未包含显式 pathType 的路径将导致验证失败。支持三种路径类型

  • ImplementationSpecific:对于此路径类型,匹配取决于 IngressClass。实现可以将其视为单独的 pathType,也可以将其与 PrefixExact 路径类型相同处理。

  • Exact:精确匹配 URL 路径,区分大小写。

  • Prefix:基于按 / 分隔的 URL 路径前缀进行匹配。匹配区分大小写,并且逐个路径元素进行。路径元素是指路径中按 / 分隔的标签列表。如果路径 p 的每个元素都是请求路径 p 对应元素的元素前缀,则请求与路径 p 匹配。

示例

类型路径请求路径匹配?
Prefix/(所有路径)
Exact/foo/foo
Exact/foo/bar
Exact/foo/foo/
Exact/foo//foo
Prefix/foo/foo, /foo/
Prefix/foo//foo, /foo/
Prefix/aaa/bb/aaa/bbb
Prefix/aaa/bbb/aaa/bbb
Prefix/aaa/bbb//aaa/bbb是,忽略尾随斜杠
Prefix/aaa/bbb/aaa/bbb/是,匹配尾随斜杠
Prefix/aaa/bbb/aaa/bbb/ccc是,匹配子路径
Prefix/aaa/bbb/aaa/bbbxyz否,不匹配字符串前缀
Prefix/, /aaa/aaa/ccc是,匹配 /aaa 前缀
Prefix/, /aaa, /aaa/bbb/aaa/bbb是,匹配 /aaa/bbb 前缀
Prefix/, /aaa, /aaa/bbb/ccc是,匹配 / 前缀
Prefix/aaa/ccc否,使用默认后端
混合/foo (Prefix), /foo (Exact)/foo是,优先匹配 Exact

多重匹配

在某些情况下,Ingress 中的多个路径会匹配一个请求。在这种情况下,优先权首先给予最长匹配路径。如果两个路径仍然同样匹配,优先权将给予具有 Exact 路径类型的路径而不是 Prefix 路径类型。

主机名通配符

主机可以是精确匹配(例如 “foo.bar.com”)或通配符(例如 “*.foo.com”)。精确匹配要求 HTTP host 头部与 host 字段完全匹配。通配符匹配要求 HTTP host 头部等于通配符规则的后缀。

主机Host 头部匹配?
*.foo.combar.foo.com基于共享后缀匹配
*.foo.combaz.bar.foo.com不匹配,通配符只覆盖单个 DNS 标签
*.foo.comfoo.com不匹配,通配符只覆盖单个 DNS 标签
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-wildcard-host
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: "*.foo.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80

Ingress 类

Ingress 可以由不同的控制器实现,通常具有不同的配置。每个 Ingress 都应指定一个类,它是对 IngressClass 资源的引用,该资源包含附加配置,包括应实现该类的控制器的名称。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: example.com/ingress-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb

IngressClass 的 .spec.parameters 字段允许你引用提供与该 IngressClass 相关配置的另一个资源。

要使用的参数的具体类型取决于你在 IngressClass 的 .spec.controller 字段中指定的 ingress 控制器。

IngressClass 作用域

根据你的 ingress 控制器,你可能会使用集群范围或仅针对单个命名空间的参数。

IngressClass 参数的默认作用域是集群范围。

如果你设置了 .spec.parameters 字段但未设置 .spec.parameters.scope,或者将 .spec.parameters.scope 设置为 Cluster,则该 IngressClass 引用的是集群范围的资源。参数的 kind(与 apiGroup 组合)引用一个集群范围的 API(可能是自定义资源),参数的 name 标识该 API 的特定集群范围资源。

例如

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-1
spec:
  controller: example.com/ingress-controller
  parameters:
    # The parameters for this IngressClass are specified in a
    # ClusterIngressParameter (API group k8s.example.net) named
    # "external-config-1". This definition tells Kubernetes to
    # look for a cluster-scoped parameter resource.
    scope: Cluster
    apiGroup: k8s.example.net
    kind: ClusterIngressParameter
    name: external-config-1

特性状态: Kubernetes v1.23 [stable]

如果你设置了 .spec.parameters 字段并将 .spec.parameters.scope 设置为 Namespace,则该 IngressClass 引用的是命名空间范围的资源。你还必须将 .spec.parameters 中的 namespace 字段设置为包含要使用的参数的命名空间。

参数的 kind(与 apiGroup 组合)引用一个命名空间范围的 API(例如:ConfigMap),参数的 name 标识你在 namespace 中指定的命名空间中的特定资源。

命名空间范围的参数有助于集群操作员委托对用于工作负载的配置(例如:负载均衡器设置、API 网关定义)的控制权。如果你使用集群范围的参数,那么:

  • 集群操作员团队需要批准不同团队的每次新的配置更改。
  • 集群操作员必须定义特定的访问控制,例如 RBAC 角色和绑定,以允许应用团队更改集群范围的参数资源。

IngressClass API 本身始终是集群范围的。

以下是引用命名空间参数的 IngressClass 示例

---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb-2
spec:
  controller: example.com/ingress-controller
  parameters:
    # The parameters for this IngressClass are specified in an
    # IngressParameter (API group k8s.example.com) named "external-config",
    # that's in the "external-configuration" namespace.
    scope: Namespace
    apiGroup: k8s.example.com
    kind: IngressParameter
    namespace: external-configuration
    name: external-config

已弃用的注解

在 Kubernetes 1.18 添加 IngressClass 资源和 ingressClassName 字段之前,Ingress 类通过 Ingress 上的 kubernetes.io/ingress.class 注解指定。此注解从未正式定义,但被 Ingress 控制器广泛支持。

Ingress 上较新的 ingressClassName 字段是该注解的替代品,但并非直接等价。虽然该注解通常用于引用应实现该 Ingress 的 Ingress 控制器的名称,但该字段是对 IngressClass 资源的引用,其中包含附加的 Ingress 配置,包括 Ingress 控制器的名称。

默认 IngressClass

在 IngressClass 资源上将 ingressclass.kubernetes.io/is-default-class 注解设置为 true 将确保未指定 ingressClassName 字段的新 Ingress 被分配此默认 IngressClass。

有些 ingress 控制器可以在未定义默认 IngressClass 的情况下工作。例如,Ingress-NGINX 控制器可以通过 标志 --watch-ingress-without-class 进行配置。然而,仍然建议 指定默认 IngressClass

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

Ingress 类型

单个 Service 支持的 Ingress

Kubernetes 中存在一些概念,允许你暴露单个 Service(参见替代方案)。你也可以通过指定一个没有规则的 默认后端 来使用 Ingress 实现此功能。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
spec:
  defaultBackend:
    service:
      name: test
      port:
        number: 80

如果你使用 kubectl apply -f 创建它,应该能够查看你添加的 Ingress 的状态

kubectl get ingress test-ingress
NAME           CLASS         HOSTS   ADDRESS         PORTS   AGE
test-ingress   external-lb   *       203.0.113.123   80      59s

其中 203.0.113.123 是 Ingress 控制器为满足此 Ingress 而分配的 IP。

简单扇出 (Fanout)

扇出配置根据请求的 HTTP URI 将流量从单个 IP 地址路由到多个 Service。Ingress 允许你将负载均衡器的数量降至最少。例如,如下设置:

ingress-fanout-diagram

图. Ingress 扇出

这需要一个 Ingress,例如

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-fanout-example
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 4200
      - path: /bar
        pathType: Prefix
        backend:
          service:
            name: service2
            port:
              number: 8080

使用 kubectl apply -f 创建 Ingress 后

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

只要 Service (service1, service2) 存在,Ingress 控制器就会提供一个实现特定的负载均衡器来满足 Ingress 的要求。完成此操作后,你可以在 Address 字段看到负载均衡器的地址。

基于名称的虚拟主机

基于名称的虚拟主机支持将 HTTP 流量路由到同一 IP 地址上的多个主机名。

ingress-namebase-diagram

图. Ingress 基于名称的虚拟主机

以下 Ingress 告诉后端负载均衡器根据 Host 头部路由请求。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: bar.foo.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80

如果你创建的 Ingress 资源在规则中没有定义任何主机,那么发送到你的 Ingress 控制器 IP 地址的任何 Web 流量都可以在不要求基于名称的虚拟主机的情况下得到匹配。

例如,以下 Ingress 将请求 first.bar.com 的流量路由到 service1,将请求 second.bar.com 的流量路由到 service2,并将请求主机头部与 first.bar.comsecond.bar.com 都不匹配的任何流量路由到 service3

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: name-virtual-host-ingress-no-third-host
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service1
            port:
              number: 80
  - host: second.bar.com
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service2
            port:
              number: 80
  - http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: service3
            port:
              number: 80

TLS

你可以通过指定包含 TLS 私钥和证书的Secret来保护 Ingress。Ingress 资源仅支持单个 TLS 端口 443,并假定 TLS 在入口点终止(到 Service 及其 Pod 的流量是明文的)。如果 Ingress 中的 TLS 配置部分指定了不同的主机,它们将根据通过 SNI TLS 扩展指定的主机名在同一端口上进行多路复用(前提是 Ingress 控制器支持 SNI)。TLS Secret 必须包含名为 tls.crttls.key 的密钥,其中包含用于 TLS 的证书和私钥。例如

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
type: kubernetes.io/tls

在 Ingress 中引用此 Secret 会告诉 Ingress 控制器使用 TLS 来保护从客户端到负载均衡器的信道。你需要确保你创建的 TLS Secret 来自一个证书,该证书包含一个通用名称(CN),也称为 https-example.foo.com 的完全限定域名(FQDN)。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

负载均衡

Ingress 控制器会使用一些负载均衡策略设置进行引导,这些设置适用于所有 Ingress,例如负载均衡算法、后端权重方案等。Ingress 尚未暴露更高级的负载均衡概念(例如持久会话、动态权重)。你可以改为通过用于 Service 的负载均衡器获得这些功能。

同样值得注意的是,即使健康检查没有直接通过 Ingress 暴露,Kubernetes 中也存在并行概念,例如就绪探针,可以实现同样的结果。请查阅特定控制器文档,了解它们如何处理健康检查(例如:nginx,或者GCE)。

更新 Ingress

要更新现有的 Ingress 以添加新的 Host,可以通过编辑资源来更新它

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

这会弹出一个编辑器,显示现有 YAML 格式的配置。修改它以包含新的 Host

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          service:
            name: service1
            port:
              number: 80
        path: /foo
        pathType: Prefix
  - host: bar.baz.com
    http:
      paths:
      - backend:
          service:
            name: service2
            port:
              number: 80
        path: /foo
        pathType: Prefix
..

保存更改后,kubectl 会更新 API 服务器中的资源,这会通知 Ingress 控制器重新配置负载均衡器。

验证此操作

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   service2:80 (10.8.0.91:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

你也可以通过在修改后的 Ingress YAML 文件上调用 kubectl replace -f 来达到同样的结果。

跨可用区故障

在不同的云提供商之间,跨故障域分散流量的技术有所不同。请查看相关Ingress 控制器的文档了解详细信息。

备选方案

你可以通过多种不直接涉及 Ingress 资源的方式暴露 Service

下一步

最后修改时间:2024 年 9 月 13 日上午 9:33 (太平洋标准时间):修复 Markdown 文件中的一些超链接 (e6855623c7)