Ingress API 的增強屬性
我們知道在 Kubernetes 集群內(nèi)部使用 kube-dns 實現(xiàn)服務(wù)發(fā)現(xiàn)的功能,那么我們部署在 Kubernetes 集群中的應(yīng)用如何暴露給外部的用戶使用呢?我們知道可以使用 NodePort 和 LoadBlancer 類型的 Service 可以把應(yīng)用暴露給外部用戶使用,除此之外,Kubernetes 還為我們提供了一個非常重要的資源對象可以用來暴露服務(wù)給外部用戶,那就是 Ingress。對于小規(guī)模的應(yīng)用我們使用 NodePort 或許能夠滿足我們的需求,但是當(dāng)你的應(yīng)用越來越多的時候,你就會發(fā)現(xiàn)對于 NodePort 的管理就非常麻煩了,這個時候使用 Ingress 就非常方便了,可以避免管理大量的端口。
資源對象
Ingress 資源對象是 Kubernetes 內(nèi)置定義的一個對象,是從 Kuberenets 集群外部訪問集群的一個入口,將外部的請求轉(zhuǎn)發(fā)到集群內(nèi)不同的 Service 上,其實就相當(dāng)于 nginx、haproxy 等負(fù)載均衡代理服務(wù)器,可能你會覺得我們直接使用 nginx 就實現(xiàn)了,但是只使用 nginx 這種方式有很大缺陷,每次有新服務(wù)加入的時候怎么改 Nginx 配置?不可能讓我們?nèi)ナ謩痈幕蛘邼L動更新前端的 Nginx Pod 吧?那我們再加上一個服務(wù)發(fā)現(xiàn)的工具比如 consul 如何?貌似是可以,對吧?Ingress 實際上就是這樣實現(xiàn)的,只是服務(wù)發(fā)現(xiàn)的功能自己實現(xiàn)了,不需要使用第三方的服務(wù)了,然后再加上一個域名規(guī)則定義,路由信息的刷新依靠 Ingress Controller 來提供。

Ingress Controller 可以理解為一個監(jiān)聽器,通過不斷地監(jiān)聽 kube-apiserver,實時的感知后端 Service、Pod 的變化,當(dāng)?shù)玫竭@些信息變化后,Ingress Controller 再結(jié)合 Ingress 的配置,更新反向代理負(fù)載均衡器,達(dá)到服務(wù)發(fā)現(xiàn)的作用。其實這點和服務(wù)發(fā)現(xiàn)工具 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 轉(zhuǎn)發(fā)至名為 test 的服務(wù)的 80 端口的 / 路徑下??梢詫?Ingress 狹義的理解為Nginx 中的配置文件 nginx.conf。
此外 Ingress 經(jīng)常使用注解 annotations 來配置一些選項,當(dāng)然這具體取決于 Ingress 控制器的實現(xiàn)方式,不同的 Ingress 控制器支持不同的注解。
另外需要注意的是當(dāng)前集群版本是 v1.22,這里使用的 apiVersion 是 networking.k8s.io/v1,所以如果是之前版本的 Ingress 資源對象需要進行遷移。Ingress 資源清單的描述我們可以使用 kubectl explain 命令來了解:
??kubectl?explain?ingress.spec
KIND:?????Ingress
VERSION:??networking.k8s.io/v1
RESOURCE:?spec?從上面描述可以看出 Ingress 資源對象中有幾個重要的屬性:defaultBackend、ingressClassName、rules、tls。
rules
其中核心部分是 rules 屬性的配置,每個路由規(guī)則都在下面進行配置:
host:可選字段,上面我們沒有指定 host 屬性,所以該規(guī)則適用于通過指定 IP 地址的所有入站 HTTP 通信,如果提供了 host 域名,則rules則會匹配該域名的相關(guān)請求,此外host主機名可以是精確匹配(例如foo.bar.com)或者使用通配符來匹配(例如*.foo.com)。http.paths:定義訪問的路徑列表,比如上面定義的/testpath,每個路徑都有一個由backend.service.name和backend.service.port.number定義關(guān)聯(lián)的 Service 后端,在控制器將流量路由到引用的服務(wù)之前,host和path都必須匹配傳入的請求才行。backend:該字段其實就是用來定義后端的 Service 服務(wù)的,與路由規(guī)則中host和path匹配的流量會將發(fā)送到對應(yīng)的 backend 后端去。
此外一般情況下在 Ingress 控制器中會配置一個
defaultBackend默認(rèn)后端,當(dāng)請求不匹配任何 Ingress 中的路由規(guī)則的時候會使用該后端。defaultBackend通常是 Ingress 控制器的配置選項,而非在 Ingress 資源中指定。
Resource
backend 后端除了可以引用一個 Service 服務(wù)之外,還可以通過一個 resource 資源進行關(guān)聯(lián),Resource 是當(dāng)前 Ingress 對象命名空間下引用的另外一個 Kubernetes 資源對象,但是需要注意的是 Resource 與 Service 配置是互斥的,只能配置一個,Resource 后端的一種常見用法是將所有入站數(shù)據(jù)導(dǎo)向帶有靜態(tài)資產(chǎn)的對象存儲后端,如下所示:
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
上面的示例中在定義路徑規(guī)則的時候都指定了一個 pathType 的字段,事實上每個路徑都需要有對應(yīng)的路徑類型,當(dāng)前支持的路徑類型有三種:
ImplementationSpecific:該路徑類型的匹配方法取決于IngressClass,具體實現(xiàn)可以將其作為單獨的 pathType 處理或者與Prefix或Exact類型作相同處理。Exact:精確匹配 URL 路徑,且區(qū)分大小寫。Prefix:基于以/分隔的 URL 路徑前綴匹配,匹配區(qū)分大小寫,并且對路徑中的元素逐個完成,路徑元素指的是由/分隔符分隔的路徑中的標(biāo)簽列表。
Exact 比較簡單,就是需要精確匹配 URL 路徑,對于 Prefix 前綴匹配,需要注意如果路徑的最后一個元素是請求路徑中最后一個元素的子字符串,則不會匹配,例如 /foo/bar 可以匹配 /foo/bar/baz, 但不匹配 /foo/barbaz,可以查看下表了解更多的匹配場景(來自官網(wǎng)):

在某些情況下,Ingress 中的多條路徑會匹配同一個請求,這種情況下最長的匹配路徑優(yōu)先,如果仍然有兩條同等的匹配路徑,則精確路徑類型優(yōu)先于前綴路徑類型。
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 的名稱,需要設(shè)置在 Ingress 中,后者是 Ingress 控制器的名稱。
Ingress 中的 spec.ingressClassName 屬性就可以用來指定對應(yīng)的 IngressClass,并進而由 IngressClass 關(guān)聯(lián)到對應(yīng)的 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 字段默認(rèn)為 Cluster,表示默認(rèn)是集群作用域的資源。將 scope 設(shè)置為 Namespace 并設(shè)置 namespace 字段就可以引用某特定命名空間中的參數(shù)資源,比如:
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 對象標(biāo)記為集群默認(rèn)是 Ingress 類。只需要將一個 IngressClass 資源的 ingressclass.kubernetes.io/is-default-class 注解設(shè)置為 true 即可,這樣未指定 ingressClassName 字段的 Ingress 就會使用這個默認(rèn)的 IngressClass。
如果集群中有多個
IngressClass被標(biāo)記為默認(rèn),準(zhǔn)入控制器將阻止創(chuàng)建新的未指定ingressClassName的 Ingress 對象。最好的方式還是確保集群中最多只能有一個IngressClass被標(biāo)記為默認(rèn)。
TLS
Ingress 資源對象還可以用來配置 Https 的服務(wù),可以通過設(shè)定包含 TLS 私鑰和證書的 Secret 來保護 Ingress。Ingress 只支持單個 TLS 端口 443,如果 Ingress 中的 TLS 配置部分指定了不同的主機,那么它們將根據(jù)通過 SNI TLS 擴展指定的主機名 (如果 Ingress 控制器支持 SNI)在同一端口上進行復(fù)用。需要注意 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 加密從客戶端到負(fù)載均衡器的通道,我們需要確保創(chuàng)建的 TLS Secret 創(chuàng)建自包含 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
現(xiàn)在我們了解了如何定義 Ingress 資源對象了,但是僅創(chuàng)建 Ingress 資源本身沒有任何效果。還需要部署 Ingress 控制器,例如 ingress-nginx,現(xiàn)在可以供大家使用的 Ingress 控制器有很多,比如 traefik、nginx-controller、Kubernetes Ingress Controller for Kong、HAProxy Ingress controller,當(dāng)然你也可以自己實現(xiàn)一個 Ingress Controller,現(xiàn)在普遍用得較多的是 traefik 和 ingress-nginx,traefik 的性能比 ingress-nginx 差,但是配置使用要簡單許多。
實際上社區(qū)目前還在開發(fā)一組高配置能力的 API,被稱為 Service API,新 API 會提供一種 Ingress 的替代方案,它的存在目的不是替代 Ingress,而是提供一種更具配置能力的新方案。
