6張圖帶你學(xué)懂 Kubernetes Ingress
Kubernetes Ingress 只是 Kubernetes 中的一個(gè)普通資源對(duì)象,需要一個(gè)對(duì)應(yīng)的 Ingress 控制器來解析 Ingress 的規(guī)則,暴露服務(wù)到外部,比如 ingress-nginx,本質(zhì)上來說它只是一個(gè) Nginx Pod,然后將請(qǐng)求重定向到其他內(nèi)部(ClusterIP)服務(wù)去,這個(gè) Pod 本身也是通過 Kubernetes 服務(wù)暴露出去,最常見的方式是通過 LoadBalancer 來實(shí)現(xiàn)的。
同樣本文我們希望用一個(gè)簡單清晰的概述,讓你來了解 Kubernetes Ingress 背后的東西,讓你更容易理解使用的 Ingress。
我們可以使用 Ingress 來使內(nèi)部服務(wù)暴露到集群外部去,它為你節(jié)省了寶貴的靜態(tài) IP,因?yàn)槟悴恍枰暶鞫鄠€(gè) LoadBalancer 服務(wù)了,此次,它還可以進(jìn)行更多的額外配置。下面我們通過一個(gè)簡單的示例來對(duì) Ingress 進(jìn)行一些說明吧。
簡單 HTTP server
首先,我們先回到容器、Kubernetes 之前的時(shí)代。之前我們更多會(huì)使用一個(gè)(Nginx)HTTP server 來托管我們的服務(wù),它可以通過 HTTP 協(xié)議接收到一個(gè)特定文件路徑的請(qǐng)求,然后在文件系統(tǒng)中檢查這個(gè)文件路徑,如果存在則就返回即可。
例如,在 Nginx 中,我們可以通過下面的配置來實(shí)現(xiàn)這個(gè)功能。
- location /folder {
- root /var/www/;
- index index.html;
- }
除了上面提到的功能之外,我們可以當(dāng) HTTP server 接收到請(qǐng)求后,將該請(qǐng)求重定向到另一個(gè)服務(wù)器(意味著它作為代理)去,然后將該服務(wù)器的響應(yīng)重定向到客戶端去。對(duì)于客戶端來說,什么都沒有改變,接收到的結(jié)果仍然還是請(qǐng)求的文件(如果存在的話)。
同樣如果在 Nginx 中,重定向可以配置成下面的樣子:
- location /folder {
- proxy_pass http://second-nginx-server:8000;
- }
這意味著 Nginx 可以從文件系統(tǒng)中提供文件,或者通過代理將響應(yīng)重定向到其他服務(wù)器并返回它們的響應(yīng)。
簡單的 Kubernetes 示例
使用 ClusterIP 服務(wù)
在 Kubernetes 中部署應(yīng)用后,我們應(yīng)該先去了解 Kubernetes Service 服務(wù)(前文中講解的)。比如我們有兩個(gè) worker 節(jié)點(diǎn),有兩個(gè)服務(wù) service-nginx 和 service-python,它們指向不同的 pods。這兩個(gè)服務(wù)沒有被調(diào)度到任何特定的節(jié)點(diǎn)上,也就是在任何節(jié)點(diǎn)上都有可能,如下圖所示:
在集群內(nèi)部我們可以通過他們的 Service 服務(wù)來請(qǐng)求到 Nginx pods 和 Python pods 上去,現(xiàn)在我們想讓這些服務(wù)也能從集群外部進(jìn)行訪問,按照前文提到的我們就需要將這些服務(wù)轉(zhuǎn)換為 LoadBalancer 服務(wù)。
使用 LoadBalancer 服務(wù)
當(dāng)然使用 LoadBalancer 服務(wù)的前提是我們的 Kubernetes 集群的托管服務(wù)商要能支持才行,如果支持我們可以將上面的 ClusterIP 服務(wù)轉(zhuǎn)換為 LoadBalancer 服務(wù),可以創(chuàng)建兩個(gè)外部負(fù)載均衡器,將請(qǐng)求重定向到我們的節(jié)點(diǎn) IP,然后重定向到內(nèi)部的 ClusterIP 服務(wù)。
我們可以看到兩個(gè) LoadBalancers 都有自己的 IP,如果我們向 LoadBalancer 22.33.44.55 發(fā)送請(qǐng)求,它請(qǐng)被重定向到我們的內(nèi)部的 service-nginx 服務(wù)去。如果發(fā)送請(qǐng)求到 77.66.55.44,它將被重定向到我們的內(nèi)部的 service-python 服務(wù)。
這個(gè)確實(shí)很方便,但是要知道 IP 地址是比較稀有的,而且價(jià)格可不便宜。想象下我們 Kubernetes 集群中不只是兩個(gè)服務(wù),有很多的話,我們?yōu)檫@些服務(wù)創(chuàng)建 LoadBalancers 成本是不是就成倍增加了。那么是否有另一種解決方案可以讓我們只使用一個(gè) LoadBalancer 就可以把請(qǐng)求轉(zhuǎn)發(fā)給我們的內(nèi)部服務(wù)呢?我們先通過手動(dòng)(非 Kubernetes)的方式來探討下這個(gè)問題。
手動(dòng)配置 Nginx 代理服務(wù)
我們知道 Nginx 可以作為一個(gè)代理使用,所以我們可以很容易想到運(yùn)行一個(gè) Nginx 來代理我們的服務(wù)。如下圖所示,我們新增了一個(gè)名為 service-nginx-proxy 的新服務(wù),它實(shí)際上是我們唯一的一個(gè) LoadBalancer 服務(wù)。service-nginx-proxy 仍然會(huì)指向一個(gè)或多個(gè) Nginx-pod-endpoints(為了簡單沒有在圖上標(biāo)識(shí)),之前的另外兩個(gè)服務(wù)轉(zhuǎn)換為簡單的 ClusterIP 服務(wù)了。
可以看到我們只分配了一個(gè) IP 地址為 11.22.33.44 的負(fù)載均衡器,對(duì)于不同的 http 請(qǐng)求路徑我們用黃色來進(jìn)行標(biāo)記,他們的目標(biāo)是一致的,只是包含的不同的請(qǐng)求 URL。service-nginx-proxy 服務(wù)會(huì)根據(jù)請(qǐng)求的 URL 來決定他們應(yīng)該將請(qǐng)求重定向到哪個(gè)服務(wù)去。在上圖中我們有兩個(gè)背后的服務(wù),分別用紅色和藍(lán)色進(jìn)行了標(biāo)記,紅色會(huì)重定向到 service-nginx 服務(wù),藍(lán)色重定向到 service-python 服務(wù)。對(duì)應(yīng)的 Nginx 代理配置如下所示:
- location /folder {
- proxy_pass http://service-nginx:3001;
- }
- location /other {
- proxy_pass http://service-python:3002;
- }
只是目前我們需要去手動(dòng)配置 service-nginx-proxy 服務(wù),比如新增了一個(gè)請(qǐng)求路徑需要路由到其他服務(wù)去,我們就需要去重新配置 Nginx 的配置讓其生效,但是這個(gè)確實(shí)是一個(gè)可行的解決方案,只是有點(diǎn)麻煩而已。而 Kubernetes Ingress 就是為了讓我們的配置更加容易、更加智能、更容易管理出現(xiàn)的,所以在 Kubernetes 集群中我們會(huì)用 Ingress 來代替上面的手動(dòng)配置的方式將服務(wù)暴露到集群外去。
使用 Kubernetes Ingress
現(xiàn)在我們將上面手動(dòng)配置代理的方式轉(zhuǎn)換為 Kubernetes Ingress 的方式,如下圖所示,我們只是使用了一個(gè)預(yù)先配置好的 Nginx(Ingress),它已經(jīng)為我們做了所有的代理重定向工作,這為我們節(jié)省了大量的手動(dòng)配置工作了。
這其實(shí)就已經(jīng)說明了 Kubernetes Ingress 是什么,下面讓我們來看看一些配置實(shí)例吧。
安裝 Ingress 控制器
Ingress 只是 Kubernetes 的一種資源對(duì)象而已,在這個(gè)資源中我們可以去配置我們的服務(wù)路由規(guī)則,但是要真正去實(shí)現(xiàn)識(shí)別這個(gè) Ingress 并提供代理路由功能,還需要安裝一個(gè)對(duì)應(yīng)的控制器才能實(shí)現(xiàn)。
- $ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/mandatory.yaml
- $ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.1/deploy/provider/cloud-generic.yaml
使用下面的命令,可以看到安裝在命名空間 ingress-nginx 中的 k8s 資源。
我們可以看到一個(gè)正常的 LoadBalancer 服務(wù),有一個(gè)外部 IP 和一個(gè)所屬的 pod,我們可以使用命令 kubectl exec 進(jìn)入該 pod,里面包含一個(gè)預(yù)配置的 Nginx 服務(wù)器。
其中的 nginx.conf 文件就包含各種代理重定向設(shè)置和其他相關(guān)配置。
Ingress 配置示例
我們所使用的 Ingress yaml 例子可以是這樣的。
- # just example, not tested
- apiVersion: networking.k8s.io/v1beta1
- kind: Ingress
- metadata:
- annotations:
- kubernetes.io/ingress.class: nginx
- namespace: default
- name: test-ingress
- spec:
- rules:
- - http:
- paths:
- - path: /folder
- backend:
- serviceName: service-nginx
- servicePort: 3001
- - http:
- paths:
- - path: /other
- backend:
- serviceName: service-python
- servicePort: 3002
和其他資源對(duì)象一樣,通過 kubectl create -f ingress.yaml 來創(chuàng)建這個(gè)資源對(duì)象即可,創(chuàng)建完成后這個(gè) Ingress 對(duì)象會(huì)被上面安裝的 Ingress 控制器轉(zhuǎn)換為對(duì)應(yīng)的 Nginx 配置。
如果你的一個(gè)內(nèi)部服務(wù),即 Ingress 應(yīng)該重定向到的服務(wù),是在不同的命名空間里,怎么辦?因?yàn)槲覀兌x的 Ingress 資源是命名空間級(jí)別的。在 Ingress 配置中,只能重定向到同一命名空間的服務(wù)。如果你定義了多個(gè) Ingress yaml 配置,那么這些配置會(huì)被一個(gè)單一的Ingress 控制器合并成一個(gè) Nginx 配置。也就是說所有的人都在使用同一個(gè) LoadBalancer IP。
配置 Ingress Nginx
有時(shí)候我們需要對(duì) Ingress Nginx 進(jìn)行一些微調(diào)配置,我們可以通過 Ingress 資源對(duì)象中的 annotations 注解來實(shí)現(xiàn),比如我們可以配置各種平時(shí)直接在 Nginx 中的配置選項(xiàng)。
- kind: Ingress
- metadata:
- name: ingress
- annotations:
- kubernetes.io/ingress.class: nginx
- nginx.ingress.kubernetes.io/proxy-connect-timeout: '30'
- nginx.ingress.kubernetes.io/proxy-send-timeout: '500'
- nginx.ingress.kubernetes.io/proxy-read-timeout: '500'
- nginx.ingress.kubernetes.io/send-timeout: "500"
- nginx.ingress.kubernetes.io/enable-cors: "true"
- nginx.ingress.kubernetes.io/cors-allow-methods: "*"
- nginx.ingress.kubernetes.io/cors-allow-origin: "*"
- ...
此外也可以做更細(xì)粒度的規(guī)則配置,如下所示:
- nginx.ingress.kubernetes.io/configuration-snippet: |
- if ($host = 'www.qikqiak.com' ) {
- rewrite ^ https://qikqiak.com$request_uri permanent;
- }
這些注釋都將被轉(zhuǎn)換成 Nginx 配置,你可以通過手動(dòng)連接(kubectl exec)到 nginx pod 中檢查這些配置。關(guān)于 ingress-nginx 更多的配置使用可以參考官方文檔相關(guān)說明:
- https://github.com/kubernetes/ingress-nginx/tree/master/docs/user-guide/nginx-configuration
- https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md#lua-resty-waf
查看 ingress-nginx 日志
要排查問題,通過查看 Ingress 控制器的日志非常有幫助。
使用 Curl 測試
如果我們想測試 Ingress 重定向規(guī)則,最好使用 curl -v [yourhost.com](http://yourhost.com) 來代替瀏覽器,可以避免緩存等帶來的問題。
重定向規(guī)則
在本文的示例中我們使用 /folder 和 /other/directory 等路徑來重定向到不同的服務(wù),此外我們也可以通過主機(jī)名來區(qū)分請(qǐng)求,比如將 api.myurl.com 和 site.myurl.com 重定向到不同的內(nèi)部 ClusterIP 服務(wù)去。
- apiVersion: networking.k8s.io/v1beta1
- kind: Ingress
- metadata:
- name: simple-fanout-example
- spec:
- rules:
- - host: api.myurl.com
- http:
- paths:
- - path: /foo
- backend:
- serviceName: service1
- servicePort: 4200
- - path: /bar
- backend:
- serviceName: service2
- servicePort: 8080
- - host: website.myurl.com
- http:
- paths:
- - path: /
- backend:
- serviceName: service3
- servicePort: 3333
SSL/HTTPS
可能我們想讓網(wǎng)站使用安全的 HTTPS 服務(wù),Kubernetes Ingress 也提供了簡單的 TLS 校驗(yàn),這意味著它會(huì)處理所有的 SSL 通信、解密/校驗(yàn) SSL 請(qǐng)求,然后將這些解密后的請(qǐng)求發(fā)送到內(nèi)部服務(wù)去。
如果你的多個(gè)內(nèi)部服務(wù)使用相同(可能是通配符)的 SSL 證書,這樣我們就只需要在 Ingress 上配置一次,而不需要在內(nèi)部服務(wù)上去配置,Ingress 可以使用配置的 TLS Kubernetes Secret 來配置 SSL 證書。
- apiVersion: networking.k8s.io/v1beta1
- kind: Ingress
- metadata:
- name: tls-example-ingress
- spec:
- tls:
- - hosts:
- - sslexample.foo.com
- secretName: testsecret-tls
- rules:
- - host: sslexample.foo.com
- http:
- paths:
- - path: /
- backend:
- serviceName: service1
- servicePort: 80
不過需要注意的是如果你在不同的命名空間有多個(gè) Ingress 資源,那么你的 TLS secret 也需要在你使用的 Ingress 資源的所有命名空間中可用。
總結(jié)
這里我們簡單介紹了 Kubernetes Ingress 的原理,簡單來說:它不過是一種輕松配置 Nginx 服務(wù)器的方法,它可以將請(qǐng)求重定向到其他內(nèi)部服務(wù)去。這為我們節(jié)省了寶貴的靜態(tài) IP 和 LoadBalancers 資源。
另外需要注意的是還有其他的 Kubernetes Ingress 類型,它們內(nèi)部沒有設(shè)置 Nginx 服務(wù),但可能使用其他代理技術(shù),一樣也可以實(shí)現(xiàn)上面的所有功能。