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

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

在某些情況下,Ingress 中的多條路徑會(huì)匹配同一個(gè)請(qǐng)求,這種情況下最長(zhǎng)的匹配路徑優(yōu)先,如果仍然有兩條同等的匹配路徑,則精確路徑類型優(yōu)先于前綴路徑類型。
IngressClass
Kubernetes 1.18 起,正式提供了一個(gè) IngressClass 資源,作用與 kubernetes.io/ingress.class 注解類似,因?yàn)榭赡茉诩褐杏卸鄠€(gè) Ingress 控制器,可以通過(guò)該對(duì)象來(lái)定義我們的控制器,例如:
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,前者是這個(gè) IngressClass 的名稱,需要設(shè)置在 Ingress 中,后者是 Ingress 控制器的名稱。
Ingress 中的 spec.ingressClassName 屬性就可以用來(lái)指定對(duì)應(yīng)的 IngressClass,并進(jìn)而由 IngressClass 關(guān)聯(lián)到對(duì)應(yīng)的 Ingress 控制器,如:
apiVersion:?networking.k8s.io/v1
kind:?Ingress
metadata:
??name:?myapp
spec:
??ingressClassName:?external-lb??#?上面定義的?IngressClass?對(duì)象名稱
??defaultBackend:
????service:
??????name:?myapp
??????port:
????????number:?80
不過(guò)需要注意的是 spec.ingressClassName 與老版本的 kubernetes.io/ingress.class 注解的作用并不完全相同,因?yàn)?ingressClassName 字段引用的是 IngressClass 資源的名稱,IngressClass 資源中除了指定了 Ingress 控制器的名稱之外,還可能會(huì)通過(guò) spec.parameters 屬性定義一些額外的配置。
比如 parameters 字段有一個(gè) scope 和 namespace 字段,可用來(lái)引用特定于命名空間的資源,對(duì) Ingress 類進(jìn)行配置。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
由于一個(gè)集群中可能有多個(gè) Ingress 控制器,所以我們還可以將一個(gè)特定的 IngressClass 對(duì)象標(biāo)記為集群默認(rèn)是 Ingress 類。只需要將一個(gè) IngressClass 資源的 ingressclass.kubernetes.io/is-default-class 注解設(shè)置為 true 即可,這樣未指定 ingressClassName 字段的 Ingress 就會(huì)使用這個(gè)默認(rèn)的 IngressClass。
如果集群中有多個(gè)
IngressClass被標(biāo)記為默認(rèn),準(zhǔn)入控制器將阻止創(chuàng)建新的未指定ingressClassName的 Ingress 對(duì)象。最好的方式還是確保集群中最多只能有一個(gè)IngressClass被標(biāo)記為默認(rèn)。
TLS
Ingress 資源對(duì)象還可以用來(lái)配置 Https 的服務(wù),可以通過(guò)設(shè)定包含 TLS 私鑰和證書的 Secret 來(lái)保護(hù) Ingress。Ingress 只支持單個(gè) TLS 端口 443,如果 Ingress 中的 TLS 配置部分指定了不同的主機(jī),那么它們將根據(jù)通過(guò) SNI TLS 擴(kuò)展指定的主機(jī)名 (如果 Ingress 控制器支持 SNI)在同一端口上進(jìn)行復(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 將會(huì)告訴 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 資源對(duì)象了,但是僅創(chuàng)建 Ingress 資源本身沒(méi)有任何效果。還需要部署 Ingress 控制器,例如 ingress-nginx,現(xiàn)在可以供大家使用的 Ingress 控制器有很多,比如 traefik、nginx-controller、Kubernetes Ingress Controller for Kong、HAProxy Ingress controller,當(dāng)然你也可以自己實(shí)現(xiàn)一個(gè) Ingress Controller,現(xiàn)在普遍用得較多的是 traefik 和 ingress-nginx,traefik 的性能比 ingress-nginx 差,但是配置使用要簡(jiǎn)單許多。
實(shí)際上社區(qū)目前還在開發(fā)一組高配置能力的 API,被稱為 Service API,新 API 會(huì)提供一種 Ingress 的替代方案,它的存在目的不是替代 Ingress,而是提供一種更具配置能力的新方案。
