《Kubernetes》,你需要掌握的 Service 和 Ingress
本文轉載自微信公眾號「小菜良記」,作者蔡不菜丶 。轉載本文請聯(lián)系小菜良記公眾號。
k8s 我們已經(jīng)從 NameSpace、Pod、PodController到Volumn都介紹過了,相信看完的小伙伴們也會很有收獲的~那么今天我們繼續(xù)來到k8s的課堂,這節(jié)我們將要來說下 k8S 搭建完服務后如何訪問!
首先我們要清楚什么是Service 和 Ingress。簡單來說,這兩個組件都是用來做流量負載的。那么什么又是流量負載呢?當我們在集群內部已經(jīng)通過 pod 部署了我們的應用服務,那么下一步要干啥?那就是讓用戶訪問到我們的應用服務,這個才是最重要的,不然你部署完了,用戶卻訪問不了,那豈不是無用功~
一、Service
在 k8s 中,pod 是應用程序的載體,我們可以通過 pod的 IP 來訪問我們的應用程序,但是我們已經(jīng)清楚了 pod 是具有生命周期的,一旦 pod 出現(xiàn)問題,pod控制器將會將pod銷毀進行重新創(chuàng)建。那么這個時候 pod 的Ip就會發(fā)生變化,因此利用 pod IP 訪問應用程序的方式直接 pass了,那么為了解決這個問題,k8s 引入了 Service 的資源概念,通過這個資源,可以整合多個pod,提供一個統(tǒng)一的入口地址,通過訪問 Service 的入口地址就能訪問到后面的 pod服務!
Service不是憑空出現(xiàn)的,不知道你是否還記得 Node 節(jié)點上的關鍵組件 kube-proxy!關鍵點來了哦~我們看個老圖回憶一下:
這張圖有看過之前的博文都不會陌生,是的!kube-proxy 在這里面起到了關鍵性的作用,每個 Node 節(jié)點上都運行著一個 kube-proxy 服務進程,當創(chuàng)建 Service 的時候會通過 api-server 向 etc寫入創(chuàng)建的 service 的信息,而 kube-proxy 會基于監(jiān)聽的機制發(fā)現(xiàn)這種 Service 的變動,然后 它會將最新的Service信息轉換成對應的訪問規(guī)則
到這里,應該對Service有個大概的概念,起碼知道了它的用處,接下來我們不妨更加深入的了解一下~
1)工作模式
kube-proxy 支持 3 種工作模式,如下:
1. userSpace
這個模式比較穩(wěn)定,但是效率比較低!在 userSpace 模式下,kube-proxy 會為每一個 Service 創(chuàng)建一個監(jiān)聽端口,當有請求發(fā)往Cluster IP 的時候,會被 Iptables 規(guī)則重定向到 kube-proxy 監(jiān)聽的端口上,kube-proxy 會根據(jù) LB 算法選擇一個 Pod 提供服務并建立起連接。
這個模式下,kube-proxy 充當?shù)慕巧且粋€ 四層負責均衡器,由于 kube-proxy 運行在 userSpace 模式下,在進行轉發(fā)處理的時候會增加內核和用戶空間之間的數(shù)據(jù)拷貝,因此效率比較低。
2. iptables
在 iptables 模式下,kube-proxy 會為 Service 后端的每個 pod 都創(chuàng)建對應的 iptable 規(guī)則,直接將發(fā)往 Cluster IP 的請求重定向到一個 pod IP 上。該模式下 kube-proxy 不承擔四層負載均衡器的角色,只負責創(chuàng)建 iptables 的規(guī)則。該模式的優(yōu)點便是較 userspace 模式來說效率更高,但是不能提供靈活的 LB 策略。當后端Pod不可用的時候也無法進行重試。
3. ipvs
這種模式與 iptables 模式形似,kube-proxy 會監(jiān)控pod的變化并且創(chuàng)建相應的 ipvs 規(guī)則。但是 ipvs 規(guī)則相對于 iptables 來說轉發(fā)效率更高,而且支持更多的 LB 算法。
實踐
上面了解到3種工作模式。我們來簡單試一下 ipvs 的作用。首先準備一份資源清單:
這份清單上半部分是創(chuàng)建一個 Pod控制器,下半部分是創(chuàng)建一個 Service。
然后我們輸入 ipvsadm -Ln命令即可看到 ipvs規(guī)則策略:
10.108.230.12 是 service 提供的訪問入口,當訪問這個入口的時候,可以發(fā)現(xiàn)后面有三個 pod 的服務在等待調用,kube-proxy 會基于 rr(輪詢)的策略,將請求分發(fā)到其中一個pod上去,這個規(guī)則會同時在集群內的所有節(jié)點上都生成,所以在任何一個節(jié)點上訪問都可以!
此模式必須安裝 ipvs 內核模塊,否則會降低為 iptables
開啟 ipvs:
- kubectl edit cm kube-proxy -n kube-system
編輯后保存(:wq) 退出
- kubectl delete pod -l k8s-app=kube-proxy -n kube-system
- ipvsadm -Ln
2)Service 使用
上面已經(jīng)介紹完了 Service 的幾種工作模式。下面我們進入Service 的使用階段。我們上面已經(jīng)做了簡單的實踐,創(chuàng)建了一個 Deploy ,一個 Service ,然后我們可以通過 serviceIp + targetPort 或 nodeIp + nodePort訪問資源
但是在學習 Service 的使用,僅僅這個是不夠的,Service又分為5種類型,下面將一一介紹。
1. ClusterIP
我們先看下 ClusterIP 類型的Service的資源清單:
通過創(chuàng)建后測試訪問 clusterIp + port
我們再查看下 ipvs 規(guī)則,可以看到該service已經(jīng)可以轉發(fā)到對應的3個pod上
接下來我們可以通過 describe 指令查看該service有哪些信息:
掃了一遍發(fā)現(xiàn) Endpoints 和 Session Affinity 都是我們之間沒有見過的。那這個又是個什么東西呢?
Endpoint
Endpoint 是 k8s 中的一個資源對象,存儲在etcd中,用來記錄一個 service 對應的所有Pod 的訪問地址,它是根據(jù) service 配置文件中 selector 描述產(chǎn)生的。一個Service由一組Pod組成,這些Pod通過 Endpoint 暴露出來,可以說 Endpoint 是實際實現(xiàn)服務的端口的集合。通俗來說,Endpoint 是 service 和 pod 之間的橋梁
既然是一個資源,那么我們就可以獲取到
負載分發(fā)
我們上面已經(jīng)成功的實現(xiàn)了通過 Service 訪問到Pod 資源,那么我們再做一些修改,分別進入3個pod編輯 usr/share/nginx/index.html 文件:
- # pod01
- Pod01 : ip - 10.244.1.73
- # pod02
- Pod01 : ip - 10.244.1.73
- # pod03
- Pod03 : ip - 10.244.2.63
然后我們再次嘗試通過 curl 10.96.10.10:80命令查看結果:
眼尖的你是否有發(fā)現(xiàn),這種負載分發(fā)策略不就是輪詢嗎!對于 Service 的訪問,k8s提供了兩種負載分發(fā)策略:
- 如果未定義分發(fā)策略,默認使用 kube-proxy 的策略,比如隨機、輪詢
- 基于客戶端地址的會話保持模式,即來自同一個客戶端發(fā)起的所有請求都會轉發(fā)到固定的一個pod上。而這里就需要用到我們上面提到的沒有見過的東西 sessionAffinity
之前我們用 ipvsadm -Ln 命令查看分發(fā)策略的時候,里面有個 rr 字段不知道你有沒有注意到,沒錯,這個 rr 值得就是輪詢的意思
如果我們想要開啟會話保持的分發(fā)策略,那么只需要在spec中添加 sessionAffinity:ClientIP 選項
再次通過 ipvsadm -Ln 命令查看分發(fā)策略就可以發(fā)現(xiàn)結果已經(jīng)發(fā)生變化了
我們簡單測試一下:
這樣子就已經(jīng)實現(xiàn)了會話保持的分發(fā)策略!
注意:ClusterIp 的 Service,不支持外部訪問,也就是說通過瀏覽器訪問是不生效的,只能在集群內部訪問
2. HeadLiness
很多服務都需要支持定制化,如果將產(chǎn)品定位為服務,那么這個產(chǎn)品毋庸是成功。在某些場景中,開發(fā)人員并不想要使用 service 提供的負載均衡功能,而是希望自己來控制負載均衡策略。針對這種情況的發(fā)生,k8s也是很好的支持了,引入了 HeadLiness Service,這類 Service 不會分配 ClusterIp,如果想要訪問 service,只能通過 Service 域名進行查詢。
我們來看下 HeadLiness 的資源清單模板:
唯一跟 ClusterIp 不同的便是 clusterIP: None 屬性的變化。
通過創(chuàng)建后可以發(fā)現(xiàn),ClusterIP并未分配,我們繼續(xù)查看 Service 的詳情
通過詳情我們可以發(fā)現(xiàn) Endpoints 已經(jīng)生效了,然后我們任意進入到一個pod中,查看域名解析情況:
可以看到域名也已經(jīng)解析完成,默認域名為service名稱.命名空間.svc.cluster.local
3. NodePort
上面的兩個service類型,都是只能在集群內部才能訪問,但是我們部署服務肯定是想讓用戶通過集群外部可以使用的。那么這個時候就需要用到我們開頭創(chuàng)建的service類型,那就是 NodePort service。
這種類型的Service的工作原理也不難,其實 就是將 service的端口映射到 Node 的一個端口上,然后通過 NodeIp+NodePort進行訪問
看了原理圖是不是感覺豁然開朗啦。那么來看看是怎么通過資源清單創(chuàng)建的:
我們通過以上資源清單創(chuàng)建service,然后訪問:
可以看出通過兩種方式都是可以訪問的,我們也可以在瀏覽器試試看:
這結果也是如我們所愿!
不要感覺到這里就已經(jīng)心滿意足了哦,雖然說已經(jīng)可以成功讓用戶訪問到了~我們趁熱打鐵繼續(xù)再了解剩下的兩種類型~
4. LoadBalancer
LoadBalancer 聽名字就知道跟負載均衡有關。這個類型與 NodePort 很相似,目的都是向外部暴露一個端口,主要的區(qū)別在于 LoadBalancer 會在集群的外部再做一個負載均衡器,而這個設備是需要外部環(huán)境支持的,外部環(huán)境發(fā)送到這個設備的請求,會被設備負載之后轉發(fā)到集群中。
圖中有個Vip的概念,這里的Vip指的是 Vitual IP,也就是虛擬IP,外部用戶通過訪問這個虛擬IP,可以負載到我們不同的service上,達到負載均衡和高可用的特點
5. ExternalName
ExternalName 類型的service 是用于引入集群外部的服務,它通過 externalName 屬性指定外部一個服務的地址,然后在集群內部訪問此service就可以訪問到外部服務了。
資源清單:
創(chuàng)建后我們可以查看域名解析,發(fā)現(xiàn)已經(jīng)解析成功:
- dig @10.96.0.10 svc-externalname.cbuc-test.svc.cluster.local
二、Ingress
1)工作模式
上面我們已經(jīng)講完了 Service幾種類型的用法,我們已經(jīng)知曉了想讓外部用戶訪問到我們pod中的服務有兩種類型的service是支持的,分別是:NodePort和LoadBalancer,但是其實認真分析一下,我們不難發(fā)現(xiàn)這兩種service 的缺點:
NodePort:會占用集群機器的很多端口,當集群服務變多的時候,這個缺點就越發(fā)明顯
LoadBalancer:每個Service都需要一個LB,比較麻煩和浪費資源,并且需要 k8s之外的負載均衡設備支持
這種缺點當然不只是我們能夠發(fā)現(xiàn),作為k8s的啟動者早已意識到了,緊接著便推出了 Ingress 的概念。Ingress 僅需要一個 NodePort或 LB 就可以滿足暴露多個Service的需求:
實際上,Ingress就相當于一個7層的負載均衡器,是 K8s 對反向代理的一個抽象,它的工作原理類似于 Nginx,可以理解成在 Ingress 里 建立諸多的隱射規(guī)則,然后 Ingress Controller通過監(jiān)聽這些配置規(guī)則轉化成 Nginx 的反向代理配置,然后對外提供該服務。這邊涉及到了兩個重要的概念:
- Ingress:K8s 中的一個資源對象,作用是定義請求如何轉發(fā)到 service 的規(guī)則
- Ingress Controller:具體實現(xiàn)反向代理及負載均衡的程序,對Ingress定義的規(guī)則進行解析,根據(jù)配置的規(guī)則來實現(xiàn)請求轉發(fā),有很多種實現(xiàn)方式,如 Nginx、Contor、Haproxy等
Ingress 控制器 有很多中可以實現(xiàn)請求轉發(fā)的方式,我們通常上也會選擇我們比較熟悉的 Nginx 作為負載,接下來我們就以 Nginx 為例,我們先來了解一下其工作原理:
- 用戶編寫 Ingress Service規(guī)則, 說明每個域名對應 K8s集群中的哪個Service
- Ingress控制器會動態(tài)感知到 Ingress 服務規(guī)則的變化,然后生成一段對應的Nginx反向代理配置
- Ingress控制器會將生成的Nginx配置寫入到一個運行中的Nginx服務中,并動態(tài)更新
- 然后客戶端通過訪問域名,實際上Nginx會將請求轉發(fā)到具體的Pod中,到此就完成了整個請求的過程
了解了工作原理,我們就來落地實現(xiàn)~
2)Ingress使用
1. 環(huán)境搭建
在使用 Ingress之前,我們需要先搭建一個 Ingress 環(huán)境
步驟一:
- # 拉取我們需要的資源清單
- wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
- wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
步驟二:
- # 創(chuàng)建資源
- kubectl apply -f ./
步驟三:
查看資源是否創(chuàng)建成功
到這里我們就已經(jīng)準備好了 Ingress 環(huán)境,接下來來到測試環(huán)節(jié)~
我們準備了兩個Service,兩個 Deployment,和創(chuàng)建了6個副本的Pod
如果到現(xiàn)在還準備不出這些資源的小伙伴得回頭做功課了哦~
大致結構圖如下:
那我們現(xiàn)在就準備一個 Ingress 來達到以下的結果
準備 Ingress 的資源清單:
- apiVersion: extensions/v1beta1
- kind: Ingress
- metadata:
- name: ingress-htpp
- namespace: cbuc-test
- spec:
- rules:
- - host: dev.cbuc.cn
- http:
- paths:
- - path: /
- backend:
- serviceName: svc-nodeport-dev
- servicePort: 80
- - host: pro.cbuc.cn
- http:
- paths:
- - path: /
- backend:
- serviceName: svc-nodeport-pro
- servicePort: 80
通過創(chuàng)建后我們還需要在我們電腦的本地 hosts 添加域名映射:
然后在網(wǎng)頁上通過 域名+nodePort 的方式就可以訪問到了
到這里我們就實現(xiàn)了Ingress 的訪問方式!