Ingress API 的增強屬性
我們知道在 Kubernetes 集羣內部使用 kube-dns 實現服務發現的功能,那麼我們部署在 Kubernetes 集羣中的應用如何暴露給外部的用戶使用呢?我們知道可以使用 NodePort
和 LoadBlancer
類型的 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 資源對象中有幾個重要的屬性:defaultBackend
、ingressClassName
、rules
、tls
。
rules
其中核心部分是 rules
屬性的配置,每個路由規則都在下面進行配置:
-
host
:可選字段,上面我們沒有指定 host 屬性,所以該規則適用於通過指定 IP 地址的所有入站 HTTP 通信,如果提供了 host 域名,則rules
則會匹配該域名的相關請求,此外host
主機名可以是精確匹配(例如foo.bar.com
)或者使用通配符來匹配(例如*.foo.com
)。 -
http.paths
:定義訪問的路徑列表,比如上面定義的/testpath
,每個路徑都有一個由backend.service.name
和backend.service.port.number
定義關聯的 Service 後端,在控制器將流量路由到引用的服務之前,host
和path
都必須匹配傳入的請求才行。 -
backend
:該字段其實就是用來定義後端的 Service 服務的,與路由規則中host
和path
匹配的流量會將發送到對應的 backend 後端去。
此外一般情況下在 Ingress 控制器中會配置一個
defaultBackend
默認後端,當請求不匹配任何 Ingress 中的路由規則的時候會使用該後端。defaultBackend
通常是 Ingress 控制器的配置選項,而非在 Ingress 資源中指定。
Resource
backend
後端除了可以引用一個 Service 服務之外,還可以通過一個 resource
資源進行關聯,Resource
是當前 Ingress 對象命名空間下引用的另外一個 Kubernetes 資源對象,但是需要注意的是 Resource
與 Service
配置是互斥的,只能配置一個,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-assets
的 StorageBucket
資源中去進行處理。
pathType
上面的示例中在定義路徑規則的時候都指定了一個 pathType
的字段,事實上每個路徑都需要有對應的路徑類型,當前支持的路徑類型有三種:
-
ImplementationSpecific
:該路徑類型的匹配方法取決於IngressClass
,具體實現可以將其作爲單獨的 pathType 處理或者與Prefix
或Exact
類型作相同處理。 -
Exact
:精確匹配 URL 路徑,且區分大小寫。 -
Prefix
:基於以/
分隔的 URL 路徑前綴匹配,匹配區分大小寫,並且對路徑中的元素逐個完成,路徑元素指的是由/
分隔符分隔的路徑中的標籤列表。
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.name
和 spec.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
字段有一個 scope
和 namespace
字段,可用來引用特定於命名空間的資源,對 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.crt
和 tls.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