Ingress API 的增強屬性

我們知道在 Kubernetes 集羣內部使用 kube-dns 實現服務發現的功能,那麼我們部署在 Kubernetes 集羣中的應用如何暴露給外部的用戶使用呢?我們知道可以使用 NodePortLoadBlancer 類型的 Service 可以把應用暴露給外部用戶使用,除此之外,Kubernetes 還爲我們提供了一個非常重要的資源對象可以用來暴露服務給外部用戶,那就是 Ingress。對於小規模的應用我們使用 NodePort 或許能夠滿足我們的需求,但是當你的應用越來越多的時候,你就會發現對於 NodePort 的管理就非常麻煩了,這個時候使用 Ingress 就非常方便了,可以避免管理大量的端口。

資源對象

Ingress 資源對象是 Kubernetes 內置定義的一個對象,是從 Kuberenets 集羣外部訪問集羣的一個入口,將外部的請求轉發到集羣內不同的 Service 上,其實就相當於 nginx、haproxy 等負載均衡代理服務器,可能你會覺得我們直接使用 nginx 就實現了,但是隻使用 nginx 這種方式有很大缺陷,每次有新服務加入的時候怎麼改 Nginx 配置?不可能讓我們去手動更改或者滾動更新前端的 Nginx Pod 吧?那我們再加上一個服務發現的工具比如 consul 如何?貌似是可以,對吧?Ingress 實際上就是這樣實現的,只是服務發現的功能自己實現了,不需要使用第三方的服務了,然後再加上一個域名規則定義,路由信息的刷新依靠 Ingress Controller 來提供。

Ingress Controller 可以理解爲一個監聽器,通過不斷地監聽 kube-apiserver,實時的感知後端 Service、Pod 的變化,當得到這些信息變化後,Ingress Controller 再結合 Ingress 的配置,更新反向代理負載均衡器,達到服務發現的作用。其實這點和服務發現工具 consul、 consul-template 非常類似。

定義

一個常見的 Ingress 資源清單如下所示:

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

上面這個 Ingress 資源的定義,配置了一個路徑爲 /testpath 的路由,所有 /testpath/** 的入站請求,會被 Ingress 轉發至名爲 test 的服務的 80 端口的 / 路徑下。可以將 Ingress 狹義的理解爲 Nginx 中的配置文件 nginx.conf

此外 Ingress 經常使用註解 annotations 來配置一些選項,當然這具體取決於 Ingress 控制器的實現方式,不同的 Ingress 控制器支持不同的註解。

另外需要注意的是當前集羣版本是 v1.22,這裏使用的 apiVersion 是 networking.k8s.io/v1,所以如果是之前版本的 Ingress 資源對象需要進行遷移。Ingress 資源清單的描述我們可以使用 kubectl explain 命令來了解:

➜ kubectl explain ingress.spec
KIND:     Ingress
VERSION:  networking.k8s.io/v1

RESOURCE: spec <Object>

DESCRIPTION:
     Spec is the desired state of the Ingress. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

     IngressSpec describes the Ingress the user wishes to exist.

FIELDS:
   defaultBackend       <Object>
     DefaultBackend is the backend that should handle requests that don't match
     any rule. If Rules are not specified, DefaultBackend must be specified. If
     DefaultBackend is not set, the handling of requests that do not match any
     of the rules will be up to the Ingress controller.

   ingressClassName     <string>
     IngressClassName is the name of the IngressClass cluster resource. The
     associated IngressClass defines which controller will implement the
     resource. This replaces the deprecated `kubernetes.io/ingress.class`
     annotation. For backwards compatibility, when that annotation is set, it
     must be given precedence over this field. The controller may emit a warning
     if the field and annotation have different values. Implementations of this
     API should ignore Ingresses without a class specified. An IngressClass
     resource may be marked as default, which can be used to set a default value
     for this field. For more information, refer to the IngressClass
     documentation.

   rules        <[]Object>
     A list of host rules used to configure the Ingress. If unspecified, or no
     rule matches, all traffic is sent to the default backend.

   tls  <[]Object>
     TLS configuration. Currently the Ingress only supports a single TLS port,
     443. If multiple members of this list specify different hosts, they will be
     multiplexed on the same port according to the hostname specified through
     the SNI TLS extension, if the ingress controller fulfilling the ingress
     supports SNI.

從上面描述可以看出 Ingress 資源對象中有幾個重要的屬性:defaultBackendingressClassNamerulestls

rules

其中核心部分是 rules 屬性的配置,每個路由規則都在下面進行配置:

此外一般情況下在 Ingress 控制器中會配置一個 defaultBackend 默認後端,當請求不匹配任何 Ingress 中的路由規則的時候會使用該後端。defaultBackend 通常是 Ingress 控制器的配置選項,而非在 Ingress 資源中指定。

Resource

backend 後端除了可以引用一個 Service 服務之外,還可以通過一個 resource 資源進行關聯,Resource 是當前 Ingress 對象命名空間下引用的另外一個 Kubernetes 資源對象,但是需要注意的是 ResourceService 配置是互斥的,只能配置一個,Resource 後端的一種常見用法是將所有入站數據導向帶有靜態資產的對象存儲後端,如下所示:

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

該 Ingress 資源對象描述了所有的 /icons 請求會被路由到同命名空間下的名爲 icon-assetsStorageBucket 資源中去進行處理。

pathType

上面的示例中在定義路徑規則的時候都指定了一個 pathType 的字段,事實上每個路徑都需要有對應的路徑類型,當前支持的路徑類型有三種:

Exact 比較簡單,就是需要精確匹配 URL 路徑,對於 Prefix 前綴匹配,需要注意如果路徑的最後一個元素是請求路徑中最後一個元素的子字符串,則不會匹配,例如 /foo/bar 可以匹配 /foo/bar/baz, 但不匹配 /foo/barbaz,可以查看下錶瞭解更多的匹配場景(來自官網):

在某些情況下,Ingress 中的多條路徑會匹配同一個請求,這種情況下最長的匹配路徑優先,如果仍然有兩條同等的匹配路徑,則精確路徑類型優先於前綴路徑類型。

IngressClass

Kubernetes 1.18 起,正式提供了一個 IngressClass 資源,作用與 kubernetes.io/ingress.class 註解類似,因爲可能在集羣中有多個 Ingress 控制器,可以通過該對象來定義我們的控制器,例如:

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

其中重要的屬性是 metadata.namespec.controller,前者是這個 IngressClass 的名稱,需要設置在 Ingress 中,後者是 Ingress 控制器的名稱。

Ingress 中的 spec.ingressClassName 屬性就可以用來指定對應的 IngressClass,並進而由 IngressClass 關聯到對應的 Ingress 控制器,如:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp
spec:
  ingressClassName: external-lb  # 上面定義的 IngressClass 對象名稱
  defaultBackend:
    service:
      name: myapp
      port:
        number: 80

不過需要注意的是 spec.ingressClassName 與老版本的 kubernetes.io/ingress.class 註解的作用並不完全相同,因爲 ingressClassName 字段引用的是 IngressClass 資源的名稱,IngressClass 資源中除了指定了 Ingress 控制器的名稱之外,還可能會通過 spec.parameters 屬性定義一些額外的配置。

比如 parameters 字段有一個 scopenamespace 字段,可用來引用特定於命名空間的資源,對 Ingress 類進行配置。scope 字段默認爲 Cluster,表示默認是集羣作用域的資源。將 scope 設置爲 Namespace 並設置 namespace 字段就可以引用某特定命名空間中的參數資源,比如:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: external-lb
spec:
  controller: nginx-ingress-internal-controller
  parameters:
    apiGroup: k8s.example.com
    kind: IngressParameters
    name: external-lb
    namespace: external-configuration
    scope: Namespace

由於一個集羣中可能有多個 Ingress 控制器,所以我們還可以將一個特定的 IngressClass 對象標記爲集羣默認是 Ingress 類。只需要將一個 IngressClass 資源的 ingressclass.kubernetes.io/is-default-class 註解設置爲 true 即可,這樣未指定 ingressClassName 字段的 Ingress 就會使用這個默認的 IngressClass。

如果集羣中有多個 IngressClass 被標記爲默認,准入控制器將阻止創建新的未指定 ingressClassName 的 Ingress 對象。最好的方式還是確保集羣中最多隻能有一個 IngressClass 被標記爲默認。

TLS

Ingress 資源對象還可以用來配置 Https 的服務,可以通過設定包含 TLS 私鑰和證書的 Secret 來保護 Ingress。Ingress 只支持單個 TLS 端口 443,如果 Ingress 中的 TLS 配置部分指定了不同的主機,那麼它們將根據通過 SNI TLS 擴展指定的主機名 (如果 Ingress 控制器支持 SNI)在同一端口上進行復用。需要注意 TLS Secret 必須包含名爲 tls.crttls.key 的鍵名,例如:

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

在 Ingress 中引用此 Secret 將會告訴 Ingress 控制器使用 TLS 加密從客戶端到負載均衡器的通道,我們需要確保創建的 TLS Secret 創建自包含 https-example.foo.com 的公用名稱的證書,如下所示:

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 控制器,例如 ingress-nginx,現在可以供大家使用的 Ingress 控制器有很多,比如 traefik、nginx-controller、Kubernetes Ingress Controller for Kong、HAProxy Ingress controller,當然你也可以自己實現一個 Ingress Controller,現在普遍用得較多的是 traefik 和 ingress-nginx,traefik 的性能比 ingress-nginx 差,但是配置使用要簡單許多。

實際上社區目前還在開發一組高配置能力的 API,被稱爲 Service API,新 API 會提供一種 Ingress 的替代方案,它的存在目的不是替代 Ingress,而是提供一種更具配置能力的新方案。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Zqmto0fQ28w9xYqRqxxtCA