為什么需要 Service
在 kubernetes 中淮阐,當(dāng)創(chuàng)建帶有多個(gè)副本的 deployment 時(shí),kubernetes 會創(chuàng)建出多個(gè) pod刁品,此時(shí)即一個(gè)服務(wù)后端有多個(gè)容器泣特,那么在 kubernetes 中負(fù)載均衡怎么做,容器漂移后 ip 也會發(fā)生變化挑随,如何做服務(wù)發(fā)現(xiàn)以及會話保持状您?這就是 Service 的作用,service 是一組具有相同 label pod 集合的抽象镀裤,集群內(nèi)外的各個(gè)服務(wù)可以通過 Service 進(jìn)行互相通信竞阐,當(dāng)創(chuàng)建一個(gè) Service 對象時(shí)也會對應(yīng)創(chuàng)建一個(gè) endpoint 對象,endpoint 是用來做容器發(fā)現(xiàn)的暑劝,Service 只是將多個(gè) pod 進(jìn)行關(guān)聯(lián),實(shí)際的路由轉(zhuǎn)發(fā)都是由 kubernetes 中的 kube-proxy 組件來實(shí)現(xiàn)颗搂,因此担猛,Service 必須結(jié)合 kube-proxy 使用,kube-proxy 組件可以運(yùn)行在 kubernetes 集群中的每一個(gè)節(jié)點(diǎn)上也可以只運(yùn)行在單獨(dú)的幾個(gè)節(jié)點(diǎn)上丢氢,其會根據(jù) service 和 endpoints 的變動來改變節(jié)點(diǎn)上 iptables 或者 ipvs 中保存的路由規(guī)則傅联。
Service 的工作原理
endpoints controller 是負(fù)責(zé)生成和維護(hù)所有 endpoints 對象的控制器,監(jiān)聽 Service 和對應(yīng) pod 的變化疚察,更新對應(yīng) Service 的 endpoints 對象蒸走。當(dāng)用戶創(chuàng)建 Service 后 endpoints controller 會監(jiān)聽 pod 的狀態(tài),當(dāng) pod 處于 running 且準(zhǔn)備就緒時(shí)貌嫡,endpoints controller 會將 pod ip 記錄到 endpoints 對象中比驻,因此该溯,service 的容器發(fā)現(xiàn)是通過 endpoints 來實(shí)現(xiàn)的。而 kube-proxy 會監(jiān)聽 service 和 endpoints 的更新并調(diào)用其代理模塊在主機(jī)上刷新路由轉(zhuǎn)發(fā)規(guī)則别惦。
Service介紹
在kubernetes中狈茉,pod是應(yīng)用程序的載體,我們可以通過pod的ip來訪問應(yīng)用程序掸掸,但是pod的ip地址不是固定的氯庆,這也就意味著不方便直接采用pod的ip對服務(wù)進(jìn)行訪問。
為了解決這個(gè)問題扰付,kubernetes提供了Service資源堤撵,Service會對提供同一個(gè)服務(wù)的多個(gè)pod進(jìn)行聚合,并且提供一個(gè)統(tǒng)一的入口地址羽莺。通過訪問Service的入口地址就能訪問到后面的pod服務(wù)实昨。
Service在很多情況下只是一個(gè)概念,真正起作用的其實(shí)是kube-proxy服務(wù)進(jìn)程禽翼,每個(gè)Node節(jié)點(diǎn)上都運(yùn)行著一個(gè)kube-proxy服務(wù)進(jìn)程屠橄。當(dāng)創(chuàng)建Service的時(shí)候會通過api-server向etcd寫入創(chuàng)建的service的信息,而kube-proxy會基于監(jiān)聽的機(jī)制發(fā)現(xiàn)這種Service的變動闰挡,然后它會將最新的Service信息轉(zhuǎn)換成對應(yīng)的訪問規(guī)則锐墙。
# 10.97.97.97:80 是service提供的訪問入口
# 當(dāng)訪問這個(gè)入口的時(shí)候,可以發(fā)現(xiàn)后面有三個(gè)pod的服務(wù)在等待調(diào)用长酗,
# kube-proxy會基于rr(輪詢)的策略溪北,將請求分發(fā)到其中一個(gè)pod上去
# 這個(gè)規(guī)則會同時(shí)在集群內(nèi)的所有節(jié)點(diǎn)上都生成,所以在任何一個(gè)節(jié)點(diǎn)上訪問都可以夺脾。
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
kube-proxy目前支持三種工作模式:
userspace 模式
userspace模式下之拨,kube-proxy會為每一個(gè)Service創(chuàng)建一個(gè)監(jiān)聽端口,發(fā)向Cluster IP的請求被Iptables規(guī)則重定向到kube-proxy監(jiān)聽的端口上咧叭,kube-proxy根據(jù)LB算法選擇一個(gè)提供服務(wù)的Pod并和其建立鏈接蚀乔,以將請求轉(zhuǎn)發(fā)到Pod上。
該模式下菲茬,kube-proxy充當(dāng)了一個(gè)四層負(fù)責(zé)均衡器的角色吉挣。由于kube-proxy運(yùn)行在userspace中,在進(jìn)行轉(zhuǎn)發(fā)處理時(shí)會增加內(nèi)核和用戶空間之間的數(shù)據(jù)拷貝婉弹,雖然比較穩(wěn)定睬魂,但是效率比較低。
在 userspace 模式下镀赌,訪問服務(wù)的請求到達(dá)節(jié)點(diǎn)后首先進(jìn)入內(nèi)核 iptables氯哮,然后回到用戶空間,由 kube-proxy 轉(zhuǎn)發(fā)到后端的 pod商佛,這樣流量從用戶空間進(jìn)出內(nèi)核帶來的性能損耗是不可接受的喉钢,所以也就有了 iptables 模式姆打。
為什么 userspace 模式要建立 iptables 規(guī)則,因?yàn)?kube-proxy 監(jiān)聽的端口在用戶空間出牧,這個(gè)端口不是服務(wù)的訪問端口也不是服務(wù)的 nodePort穴肘,因此需要一層 iptables 把訪問服務(wù)的連接重定向給 kube-proxy 服務(wù)。
iptables 模式
iptables模式下舔痕,kube-proxy為service后端的每個(gè)Pod創(chuàng)建對應(yīng)的iptables規(guī)則评抚,直接將發(fā)向Cluster IP的請求重定向到一個(gè)Pod IP。
該模式下kube-proxy不承擔(dān)四層負(fù)責(zé)均衡器的角色伯复,只負(fù)責(zé)創(chuàng)建iptables規(guī)則慨代。該模式的優(yōu)點(diǎn)是較userspace模式效率更高,但不能提供靈活的LB策略啸如,當(dāng)后端Pod不可用時(shí)也無法進(jìn)行重試侍匙。
iptables 模式是目前默認(rèn)的代理方式,基于 netfilter 實(shí)現(xiàn)叮雳。當(dāng)客戶端請求 service 的 ClusterIP 時(shí)想暗,根據(jù) iptables 規(guī)則路由到各 pod 上,iptables 使用 DNAT 來完成轉(zhuǎn)發(fā)帘不,其采用了隨機(jī)數(shù)實(shí)現(xiàn)負(fù)載均衡说莫。
iptables 模式與 userspace 模式最大的區(qū)別在于,iptables 模塊使用 DNAT 模塊實(shí)現(xiàn)了 service 入口地址到 pod 實(shí)際地址的轉(zhuǎn)換寞焙,免去了一次內(nèi)核態(tài)到用戶態(tài)的切換储狭,另一個(gè)與 userspace 代理模式不同的是,如果 iptables 代理最初選擇的那個(gè) pod 沒有響應(yīng)捣郊,它不會自動重試其他 pod辽狈。
iptables 模式最主要的問題是在 service 數(shù)量大的時(shí)候會產(chǎn)生太多的 iptables 規(guī)則,使用非增量式更新會引入一定的時(shí)延呛牲,大規(guī)模情況下有明顯的性能問題刮萌。
ipvs 模式
ipvs模式和iptables類似,kube-proxy監(jiān)控Pod的變化并創(chuàng)建相應(yīng)的ipvs規(guī)則娘扩。ipvs相對iptables轉(zhuǎn)發(fā)效率更高尊勿。除此以外,ipvs支持更多的LB算法畜侦。
當(dāng)集群規(guī)模比較大時(shí),iptables 規(guī)則刷新會非常慢躯保,難以支持大規(guī)模集群旋膳,因其底層路由表的實(shí)現(xiàn)是鏈表,對路由規(guī)則的增刪改查都要涉及遍歷一次鏈表途事,ipvs 的問世正是解決此問題的验懊,ipvs 是 LVS 的負(fù)載均衡模塊擅羞,與 iptables 比較像的是,ipvs 的實(shí)現(xiàn)雖然也基于 netfilter 的鉤子函數(shù)义图,但是它卻使用哈希表作為底層的數(shù)據(jù)結(jié)構(gòu)并且工作在內(nèi)核態(tài)减俏,也就是說 ipvs 在重定向流量和同步代理規(guī)則有著更好的性能,幾乎允許無限的規(guī)模擴(kuò)張碱工。
ipvs 支持三種負(fù)載均衡模式:DR模式(Direct Routing)娃承、NAT 模式(Network Address Translation)、Tunneling(也稱 ipip 模式)怕篷。三種模式中只有 NAT 支持端口映射历筝,所以 ipvs 使用 NAT 模式。linux 內(nèi)核原生的 ipvs 只支持 DNAT廊谓,當(dāng)在數(shù)據(jù)包過濾梳猪,SNAT 和支持 NodePort 類型的服務(wù)這幾個(gè)場景中ipvs 還是會使用 iptables。
此外蒸痹,ipvs 也支持更多的負(fù)載均衡算法春弥,例如:
- rr:round-robin/輪詢
- lc:least connection/最少連接
- dh:destination hashing/目標(biāo)哈希
- sh:source hashing/源哈希
- sed:shortest expected delay/預(yù)計(jì)延遲時(shí)間最短
- nq:never queue/從不排隊(duì)
userspace、iptables叠荠、ipvs 三種模式中默認(rèn)的負(fù)載均衡策略都是通過 round-robin 算法來選擇后端 pod 的匿沛,在 service 中可以通過設(shè)置 service.spec.sessionAffinity 的值實(shí)現(xiàn)基于客戶端 ip 的會話親和性,service.spec.sessionAffinity 的值默認(rèn)為”None”蝙叛,可以設(shè)置為 “ClientIP”俺祠,此外也可以使用 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 設(shè)置會話保持時(shí)間。kernelspace 主要是在 windows 下使用的借帘,本文暫且不談蜘渣。
# 此模式必須安裝ipvs內(nèi)核模塊,否則會降級為iptables
# 開啟ipvs
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
Service類型
service 支持的類型也就是 kubernetes 中服務(wù)暴露的方式肺然,默認(rèn)有四種 ClusterIP蔫缸、NodePort、LoadBalancer际起、ExternelName拾碌,此外還有 Ingress,下面會詳細(xì)介紹每種類型 service 的具體使用場景街望。
Service的資源清單文件:
kind: Service # 資源類型
apiVersion: v1 # 資源版本
metadata: # 元數(shù)據(jù)
name: service # 資源名稱
namespace: dev # 命名空間
spec: # 描述
selector: # 標(biāo)簽選擇器校翔,用于確定當(dāng)前service代理哪些pod
app: nginx
type: # Service類型,指定service的訪問方式
clusterIP: # 虛擬服務(wù)的ip地址
sessionAffinity: # session親和性灾前,支持ClientIP防症、None兩個(gè)選項(xiàng)
ports: # 端口信息
- protocol: TCP
port: 3017 # service端口
targetPort: 5003 # pod端口
nodePort: 31122 # 主機(jī)端口
- ClusterIP:默認(rèn)值,它是Kubernetes系統(tǒng)自動分配的虛擬IP,只能在集群內(nèi)部訪問蔫敲。
- NodePort:將Service通過指定的Node上的端口暴露給外部饲嗽,通過此方法,就可以在集群外部訪問服務(wù)奈嘿。
- LoadBalancer:使用外接負(fù)載均衡器完成到服務(wù)的負(fù)載分發(fā)貌虾,注意此模式需要外部云環(huán)境支持。
- ExternalName: 把集群外部的服務(wù)引入集群內(nèi)部裙犹,直接使用尽狠。
Service使用
實(shí)驗(yàn)環(huán)境準(zhǔn)備
在使用service之前,首先利用Deployment創(chuàng)建出3個(gè)pod伯诬,注意要為pod設(shè)置app=nginx-pod
的標(biāo)簽
創(chuàng)建deployment.yaml晚唇,內(nèi)容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.20.1
ports:
- containerPort: 80
[root@master ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created
# 查看pod詳情
[root@master ~]# kubectl get pods -n dev -o wide --show-labels
NAME READY STATUS IP NODE LABELS
pc-deployment-66cb59b984-8p84h 1/1 Running 10.244.1.40 node1 app=nginx-pod
pc-deployment-66cb59b984-vx8vx 1/1 Running 10.244.2.33 node2 app=nginx-pod
pc-deployment-66cb59b984-wnncx 1/1 Running 10.244.1.39 node1 app=nginx-pod
# 為了方便后面的測試,修改下三臺nginx的index.html頁面(三臺修改的IP地址不一致)
# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
# echo "10.244.1.40" > /usr/share/nginx/html/index.html
#修改完畢之后盗似,訪問測試
[root@master ~]# curl 10.244.1.40
10.244.1.40
[root@master ~]# curl 10.244.2.33
10.244.2.33
[root@master ~]# curl 10.244.1.39
10.244.1.39
ClusterIP類型的Service
ClusterIP 類型的 service 是 kubernetes 集群默認(rèn)的服務(wù)暴露方式哩陕,它只能用于集群內(nèi)部通信,可以被各 pod 訪問赫舒,其訪問方式為:
pod ---> ClusterIP:ServicePort --> (iptables)DNAT --> PodIP:containePort
創(chuàng)建service-clusterip.yaml文件
apiVersion: v1
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: 10.97.97.97 # service的ip地址悍及,如果不寫,默認(rèn)會生成一個(gè)
type: ClusterIP
ports:
- port: 80 # Service端口
targetPort: 80 # pod端口
# 創(chuàng)建service
[root@master ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
# 查看service
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 13s app=nginx-pod
# 查看service的詳細(xì)信息
# 在這里有一個(gè)Endpoints列表接癌,里面就是當(dāng)前service可以負(fù)載到的服務(wù)入口
[root@master ~]# kubectl describe svc service-clusterip -n dev
Name: service-clusterip
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: 10.97.97.97
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity: None
Events: <none>
# 查看ipvs的映射規(guī)則
[root@master ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 訪問10.97.97.97:80觀察效果
[root@master ~]# curl 10.97.97.97:80
10.244.2.33
Endpoint
Endpoint是kubernetes中的一個(gè)資源對象心赶,存儲在etcd中,用來記錄一個(gè)service對應(yīng)的所有pod的訪問地址缺猛,它是根據(jù)service配置文件中selector描述產(chǎn)生的缨叫。
一個(gè)Service由一組Pod組成,這些Pod通過Endpoints暴露出來荔燎,Endpoints是實(shí)現(xiàn)實(shí)際服務(wù)的端點(diǎn)集合耻姥。換句話說,service和pod之間的聯(lián)系是通過endpoints實(shí)現(xiàn)的有咨。
負(fù)載分發(fā)策略
對Service的訪問被分發(fā)到了后端的Pod上去琐簇,目前kubernetes提供了兩種負(fù)載分發(fā)策略:
如果不定義,默認(rèn)使用kube-proxy的策略座享,比如隨機(jī)婉商、輪詢
基于客戶端地址的會話保持模式,即來自同一個(gè)客戶端發(fā)起的所有請求都會轉(zhuǎn)發(fā)到固定的一個(gè)Pod上渣叛,此模式可以使在spec中添加
sessionAffinity:ClientIP
選項(xiàng)
# 查看ipvs的映射規(guī)則【rr 輪詢】
[root@master ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 循環(huán)訪問測試
[root@master ~]# while true;do curl 10.97.97.97:80; sleep 5; done;
10.244.1.40
10.244.1.39
10.244.2.33
10.244.1.40
10.244.1.39
10.244.2.33
# 修改分發(fā)策略----sessionAffinity:ClientIP
# 查看ipvs規(guī)則【persistent 代表持久】
[root@master ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr persistent 10800
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 循環(huán)訪問測試
[root@master ~]# while true;do curl 10.97.97.97; sleep 5; done;
10.244.2.33
10.244.2.33
10.244.2.33
# 刪除service
[root@master ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted
HeadLiness類型的Service
在某些場景中丈秩,開發(fā)人員可能不想使用Service提供的負(fù)載均衡功能,而希望自己來控制負(fù)載均衡策略淳衙,針對這種情況癣籽,kubernetes提供了HeadLiness Service挽唉,這類Service不會分配Cluster IP,如果想要訪問service筷狼,只能通過service的域名進(jìn)行查詢。
創(chuàng)建service-headliness.yaml
apiVersion: v1
kind: Service
metadata:
name: service-headliness
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None # 將clusterIP設(shè)置為None匠童,即可創(chuàng)建headliness Service
type: ClusterIP
ports:
- port: 80
targetPort: 80
# 創(chuàng)建service
[root@master ~]# kubectl create -f service-headliness.yaml
service/service-headliness created
# 獲取service埂材, 發(fā)現(xiàn)CLUSTER-IP未分配
[root@master ~]# kubectl get svc service-headliness -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-headliness ClusterIP None <none> 80/TCP 11s app=nginx-pod
# 查看service詳情
[root@master ~]# kubectl describe svc service-headliness -n dev
Name: service-headliness
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity: None
Events: <none>
# 查看域名的解析情況
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
[root@master ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33
NodePort類型的Service
在之前的樣例中,創(chuàng)建的Service的ip地址只有集群內(nèi)部才可以訪問汤求,如果希望將Service暴露給集群外部使用俏险,那么就要使用到另外一種類型的Service,稱為NodePort類型扬绪。NodePort的工作原理其實(shí)就是將service的端口映射到Node的一個(gè)端口上竖独,然后就可以通過NodeIp:NodePort
來訪問service了。
創(chuàng)建service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort # service類型
ports:
- port: 80
nodePort: 30002 # 指定綁定的node的端口(默認(rèn)的取值范圍是:30000-32767), 如果不指定挤牛,會默認(rèn)分配
targetPort: 80
# 創(chuàng)建service
[root@master ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created
# 查看service
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) SELECTOR
service-nodeport NodePort 10.105.64.191 <none> 80:30002/TCP app=nginx-pod
# 接下來可以通過電腦主機(jī)的瀏覽器去訪問集群中任意一個(gè)nodeip的30002端口莹痢,即可訪問到pod
LoadBalancer類型的Service
LoadBalancer和NodePort很相似,目的都是向外部暴露一個(gè)端口墓赴,區(qū)別在于LoadBalancer會在集群的外部再來做一個(gè)負(fù)載均衡設(shè)備竞膳,而這個(gè)設(shè)備需要外部環(huán)境支持的,外部服務(wù)發(fā)送到這個(gè)設(shè)備上的請求诫硕,會被設(shè)備負(fù)載之后轉(zhuǎn)發(fā)到集群中坦辟。
ExternalName類型的Service
ExternalName類型的Service用于引入集群外部的服務(wù),它通過externalName
屬性指定外部一個(gè)服務(wù)的地址章办,然后在集群內(nèi)部訪問此service就可以訪問到外部的服務(wù)了锉走。
apiVersion: v1
kind: Service
metadata:
name: service-externalname
namespace: dev
spec:
type: ExternalName # service類型
externalName: www.baidu.com #改成ip地址也可以
# 創(chuàng)建service
[root@master ~]# kubectl create -f service-externalname.yaml
service/service-externalname created
# 域名解析
[root@master ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 39.156.66.18
www.a.shifen.com. 30 IN A 39.156.66.14
Ingress介紹
在上面已經(jīng)提到,Service對集群之外暴露服務(wù)的主要方式有兩種:NotePort和LoadBalancer藕届,但是這兩種方式挪蹭,都有一定的缺點(diǎn):
- NodePort方式的缺點(diǎn)是會占用很多集群機(jī)器的端口,那么當(dāng)集群服務(wù)變多的時(shí)候翰舌,這個(gè)缺點(diǎn)就愈發(fā)明顯嚣潜。
- LB方式的缺點(diǎn)是每個(gè)service需要一個(gè)LB,浪費(fèi)椅贱、麻煩懂算,并且需要kubernetes之外設(shè)備的支持。
基于這種現(xiàn)狀庇麦,kubernetes提供了Ingress資源對象计技,Ingress只需要一個(gè)NodePort或者一個(gè)LB就可以滿足暴露多個(gè)Service的需求。工作機(jī)制大致如下圖表示:
實(shí)際上山橄,Ingress相當(dāng)于一個(gè)7層的負(fù)載均衡器垮媒,是kubernetes對反向代理的一個(gè)抽象,它的工作原理類似于Nginx,可以理解成在Ingress里建立諸多映射規(guī)則睡雇,Ingress Controller通過監(jiān)聽這些配置規(guī)則并轉(zhuǎn)化成Nginx的反向代理配置 , 然后對外部提供服務(wù)撩轰。在這里有兩個(gè)核心概念:
- ingress:kubernetes中的一個(gè)對象,作用是定義請求如何轉(zhuǎn)發(fā)到service的規(guī)則
- ingress controller:具體實(shí)現(xiàn)反向代理及負(fù)載均衡的程序岸更,對ingress定義的規(guī)則進(jìn)行解析陕习,根據(jù)配置的規(guī)則來實(shí)現(xiàn)請求轉(zhuǎn)發(fā),實(shí)現(xiàn)方式有很多观蓄,比如Nginx, Contour, Haproxy等等
Ingress(以Nginx為例)的工作原理如下:
- 用戶編寫Ingress規(guī)則混移,說明哪個(gè)域名對應(yīng)kubernetes集群中的哪個(gè)Service
- Ingress控制器動態(tài)感知Ingress服務(wù)規(guī)則的變化,然后生成一段對應(yīng)的Nginx反向代理配置
- Ingress控制器會將生成的Nginx配置寫入到一個(gè)運(yùn)行著的Nginx服務(wù)中侮穿,并動態(tài)更新
- 到此為止歌径,其實(shí)真正在工作的就是一個(gè)Nginx了,內(nèi)部配置了用戶定義的請求轉(zhuǎn)發(fā)規(guī)則
Ingress使用
環(huán)境準(zhǔn)備
搭建ingress環(huán)境
# 創(chuàng)建文件夾
[root@master ~]# mkdir ingress-controller
[root@master ~]# cd ingress-controller/
# 獲取ingress-nginx亲茅,本次案例使用的是0.30版本
[root@master ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
[root@master ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
# 修改mandatory.yaml文件中的倉庫
# 修改quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 為quay-mirror.qiniu.com/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 創(chuàng)建ingress-nginx
[root@master ingress-controller]# kubectl apply -f ./
# 查看ingress-nginx
[root@master ingress-controller]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-ingress-controller-fbf967dd5-4qpbp 1/1 Running 0 12h
# 查看service
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.98.75.163 <none> 80:32240/TCP,443:31335/TCP 11h
準(zhǔn)備service和pod
為了后面的實(shí)驗(yàn)比較方便回铛,創(chuàng)建如下圖所示的模型
創(chuàng)建tomcat-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: tomcat-pod
template:
metadata:
labels:
app: tomcat-pod
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
namespace: dev
spec:
selector:
app: tomcat-pod
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
# 創(chuàng)建
[root@master ~]# kubectl create -f tomcat-nginx.yaml
# 查看
[root@master ~]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP None <none> 80/TCP 48s
tomcat-service ClusterIP None <none> 8080/TCP 48s
Http代理
創(chuàng)建ingress-http.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-http
namespace: dev
spec:
rules:
- host: nginx.yibo.com
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.yibo.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
# 創(chuàng)建
[root@master ~]# kubectl create -f ingress-http.yaml
ingress.extensions/ingress-http created
# 查看
[root@master ~]# kubectl get ing ingress-http -n dev
NAME HOSTS ADDRESS PORTS AGE
ingress-http nginx.yibo.com,tomcat.yibo.com 80 22s
# 查看詳情
[root@master ~]# kubectl describe ing ingress-http -n dev
...
Rules:
Host Path Backends
---- ---- --------
nginx.yibo.com / nginx-service:80 (10.244.1.96:80,10.244.1.97:80,10.244.2.112:80)
tomcat.yibo.com / tomcat-service:8080(10.244.1.94:8080,10.244.1.95:8080,10.244.2.111:8080)
...
# 接下來,在本地電腦上配置host文件,解析上面的兩個(gè)域名到192.168.109.100(master)上
# 然后,就可以分別訪問tomcat.yibo.com:32240 和 nginx.yibo.com:32240 查看效果了
Https代理
創(chuàng)建證書
# 生成證書
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima.com"
# 創(chuàng)建密鑰
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
創(chuàng)建ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-https
namespace: dev
spec:
tls:
- hosts:
- nginx.yibo.com
- tomcat.yibo.com
secretName: tls-secret # 指定秘鑰
rules:
- host: nginx.yibo.com
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.yibo.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
# 創(chuàng)建
[root@master ~]# kubectl create -f ingress-https.yaml
ingress.extensions/ingress-https created
# 查看
[root@master ~]# kubectl get ing ingress-https -n dev
NAME HOSTS ADDRESS PORTS AGE
ingress-https nginx.yibo.com,tomcat.yibo.com 10.104.184.38 80, 443 2m42s
# 查看詳情
[root@master ~]# kubectl describe ing ingress-https -n dev
...
TLS:
tls-secret terminates nginx.yibo.com,tomcat.yibo.com
Rules:
Host Path Backends
---- ---- --------
nginx.yibo.com / nginx-service:80 (10.244.1.97:80,10.244.1.98:80,10.244.2.119:80)
tomcat.yibo.com / tomcat-service:8080(10.244.1.99:8080,10.244.2.117:8080,10.244.2.120:8080)
...
# 下面可以通過瀏覽器訪問https://nginx.yibo.com:31335 和 https://tomcat.yibo.com:31335來查看了
參考:
https://blog.tianfeiyu.com/2019/10/31/k8s_service_theory/
https://segmentfault.com/a/1190000019376912