k8s service服务发现详解:ipvs代理模式、服务类型
目錄
- k8s服務(wù)發(fā)現(xiàn)Service
- 理解
- Service的實(shí)現(xiàn)模型
- userspace代理模式
- iptables代理模式
- ipvs代理模式
- Service定義
- Service配置清單重要字段
- 創(chuàng)建ClusterIP類型Service
- 創(chuàng)建NodePort類型Service
- Pod的會話保持
- Headless無頭Service
- 參考資料
Kubernetes之(十)服務(wù)發(fā)現(xiàn)Service
理解
Service是對一組提供相同功能的Pods的抽象,并為它們提供一個(gè)統(tǒng)一的入口。借助Service,應(yīng)用可以方便的實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)與負(fù)載均衡,并實(shí)現(xiàn)應(yīng)用的零宕機(jī)升級。Service通過標(biāo)簽來選取服務(wù)后端,一般配合Replication Controller或者Deployment來保證后端容器的正常運(yùn)行。這些匹配標(biāo)簽的Pod IP和端口列表組成endpoints,由kubeproxy負(fù)責(zé)將服務(wù)IP負(fù)載均衡到這些endpoints上。
Service有四種類型:
- ClusterIP:默認(rèn)類型,自動分配一個(gè)僅cluster內(nèi)部可以訪問的虛擬IP
- NodePort:在ClusterIP基礎(chǔ)上為Service在每臺機(jī)器上綁定一個(gè)端口,這樣就可以通過 :NodePort 來訪問該服務(wù)
- LoadBalancer:在NodePort的基礎(chǔ)上,借助cloud provider創(chuàng)建一個(gè)外部的負(fù)載均衡器,并將請求轉(zhuǎn)發(fā)到 :NodePort
- ExternalName:將服務(wù)通過DNS CNAME記錄方式轉(zhuǎn)發(fā)到指定的域名(通過 spec.externlName 設(shè)定) 。需要kube-dns版本在1.7以上。
另外,也可以將已有的服務(wù)以Service的形式加入到Kubernetes集群中來,只需要在創(chuàng)建
Service的時(shí)候不指定Label selector,而是在Service創(chuàng)建好后手動為其添加endpoint。
Service的實(shí)現(xiàn)模型
在 Kubernetes 集群中,每個(gè) Node 運(yùn)行一個(gè) kube-proxy 進(jìn)程。kube-proxy 負(fù)責(zé)為 Service 實(shí)現(xiàn)了一種 VIP(虛擬 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默認(rèn)的運(yùn)行模式。 從 Kubernetes v1.2 起,默認(rèn)就是 iptables 代理。在Kubernetes v1.8.0-beta.0中,添加了ipvs代理。在 Kubernetes v1.0 版本,Service 是 “4層”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用來表示 “7層”(HTTP)服務(wù)。
kube-proxy 這個(gè)組件始終監(jiān)視著apiserver中有關(guān)service的變動信息,獲取任何一個(gè)與service資源相關(guān)的變動狀態(tài),通過watch監(jiān)視,一旦有service資源相關(guān)的變動和創(chuàng)建,kube-proxy都要轉(zhuǎn)換為當(dāng)前節(jié)點(diǎn)上的能夠?qū)崿F(xiàn)資源調(diào)度規(guī)則(例如:iptables、ipvs)。
userspace代理模式
當(dāng)客戶端Pod請求內(nèi)核空間的service iptables后,把請求轉(zhuǎn)到給用戶空間監(jiān)聽的kube-proxy 的端口,由kube-proxy來處理后,再由kube-proxy將請求轉(zhuǎn)給內(nèi)核空間的 service ip,再由service iptalbes根據(jù)請求轉(zhuǎn)給各節(jié)點(diǎn)中的的service pod。
這個(gè)模式有很大的問題,由客戶端請求先進(jìn)入內(nèi)核空間的,又進(jìn)去用戶空間訪問kube-proxy,由kube-proxy封裝完成后再進(jìn)去內(nèi)核空間的iptables,再根據(jù)iptables的規(guī)則分發(fā)給各節(jié)點(diǎn)的用戶空間的pod。這樣流量從用戶空間進(jìn)出內(nèi)核帶來的性能損耗是不可接受的。在Kubernetes 1.1版本之前,userspace是默認(rèn)的代理模型。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-w8x639QH-1612426479772)(http://10.0.0.14:8181/uploads/kubetnetes/images/m_4b007aa6eec83d58515d8f8d67bfe1f2_r.png)]
iptables代理模式
客戶端IP請求時(shí),直接請求本地內(nèi)核service ip,根據(jù)iptables的規(guī)則直接將請求轉(zhuǎn)發(fā)到到各pod上,因?yàn)槭褂胕ptable NAT來完成轉(zhuǎn)發(fā),也存在不可忽視的性能損耗。另外,如果集群中存在上萬的Service/Endpoint,那么Node上的iptables rules將會非常龐大,性能還會再打折扣。iptables代理模式由Kubernetes 1.1版本引入,自1.2版本開始成為默認(rèn)類型。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-ZElqJDJo-1612426479783)(http://10.0.0.14:8181/uploads/kubetnetes/images/m_ec099b48ed77d624c4b7b1f32ea6efe8_r.png)]
ipvs代理模式
Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本開始成為默認(rèn)設(shè)置。客戶端IP請求時(shí)到達(dá)內(nèi)核空間時(shí),根據(jù)ipvs的規(guī)則直接分發(fā)到各pod上。kube-proxy會監(jiān)視Kubernetes Service對象和Endpoints,調(diào)用netlink接口以相應(yīng)地創(chuàng)建ipvs規(guī)則并定期與Kubernetes Service對象和Endpoints對象同步ipvs規(guī)則,以確保ipvs狀態(tài)與期望一致。訪問服務(wù)時(shí),流量將被重定向到其中一個(gè)后端Pod。
與iptables類似,ipvs基于netfilter 的 hook 功能,但使用哈希表作為底層數(shù)據(jù)結(jié)構(gòu)并在內(nèi)核空間中工作。這意味著ipvs可以更快地重定向流量,并且在同步代理規(guī)則時(shí)具有更好的性能。此外,ipvs為負(fù)載均衡算法提供了更多選項(xiàng),如,rr輪詢,lc最小連接數(shù),dh目標(biāo)哈希,sh源哈希,sed最短期望延遲,nq不排隊(duì)調(diào)度。
注意: ipvs模式假定在運(yùn)行kube-proxy之前在節(jié)點(diǎn)上都已經(jīng)安裝了IPVS內(nèi)核模塊。當(dāng)kube-proxy以ipvs代理模式啟動時(shí),kube-proxy將驗(yàn)證節(jié)點(diǎn)上是否安裝了IPVS模塊,如果未安裝,則kube-proxy將回退到iptables代理模式。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-N2TuElAA-1612426479786)(http://10.0.0.14:8181/uploads/kubetnetes/images/m_4e5825eac5d1c326f4499b50c6e6603d_r.png)]
當(dāng)某個(gè)服務(wù)后端pod發(fā)生變化,標(biāo)簽選擇器適應(yīng)的pod有多一個(gè),適應(yīng)的信息會立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息變化,而將它立即轉(zhuǎn)為ipvs或者iptables中的規(guī)則,這一切都是動態(tài)和實(shí)時(shí)的,刪除一個(gè)pod也是同樣的原理。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-NaKXgfPF-1612426479789)(http://10.0.0.14:8181/uploads/kubetnetes/images/m_1479135c6b7b3ab8944da7f559b45247_r.png)]
Service定義
Service配置清單重要字段
apiVersion: kind: metadata: spec:clusterIP: 可以自定義,也可以動態(tài)分配ports:(與后端容器端口關(guān)聯(lián))selector:(關(guān)聯(lián)到哪些pod資源上)type:服務(wù)類型Service的服務(wù)類型
對一些應(yīng)用(如 Frontend)的某些部分,可能希望通過外部(Kubernetes 集群外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 允許指定一個(gè)需要的類型的 Service,默認(rèn)是 ClusterIP 類型。
Type 的取值以及行為如下:
- **ClusterIP:**通過集群的內(nèi)部 IP 暴露服務(wù),選擇該值,服務(wù)只能夠在集群內(nèi)部可以訪問,這也是默認(rèn)的 ServiceType。
- **NodePort:**通過每個(gè) Node 上的 IP 和靜態(tài)端口(NodePort)暴露服務(wù)。NodePort 服務(wù)會路由到 ClusterIP 服務(wù),這個(gè) ClusterIP 服務(wù)會自動創(chuàng)建。通過請求 :,可以從集群的外部訪問一個(gè) NodePort 服務(wù)。
- **LoadBalancer:**使用云提供商的負(fù)載均衡器,可以向外部暴露服務(wù)。外部的負(fù)載均衡器可以路由到 NodePort 服務(wù)和 ClusterIP 服務(wù)。
- **ExternalName:**通過返回 CNAME 和它的值,可以將服務(wù)映射到 externalName 字段的內(nèi)容(例如, foo.bar.example.com)。 沒有任何類型代理被創(chuàng)建,這只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
創(chuàng)建ClusterIP類型Service
[root@master manifests]# vim redis-svc.yaml apiVersion: v1 kind: Service metadata:name: redisnamespace: default spec:selector:app: redisrole: logstoreclusterIP: 10.97.97.97type: ClusterIPports:- port: 6379 #容器端口targetPort: 6379 #Pod端口創(chuàng)建并查看svc:
[root@master manifests]# kubectl apply -f redis-svc.yaml service/redis created [root@master manifests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d22h redis ClusterIP 10.97.97.97 <none> 6379/TCP 4s查看redis服務(wù)詳細(xì)信息
[root@master manifests]# kubectl describe svc redis Name: redis Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration:{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","... Selector: app=redis,role=logstore Type: ClusterIP IP: 10.97.97.97 #service ip Port: <unset> 6379/TCP TargetPort: 6379/TCP Endpoints: 10.244.2.43:6379 #此處的ip+端口就是pod的ip+端口 Session Affinity: None Events: <none>[root@master manifests]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES filebeat-ds-h8rwk 1/1 Running 0 8m36s 10.244.1.39 node01 <none> <none> filebeat-ds-kzhxw 1/1 Running 0 8m36s 10.244.2.44 node02 <none> <none> readiness-httpget-pod 1/1 Running 0 2d21h 10.244.2.18 node02 <none> <none> redis-76c85b5744-94djm 1/1 Running 0 8m36s 10.244.2.43 node02 <none> <none>總結(jié):
- service不會直接到pod,service是直接到endpoint資源,就是地址加端口,再由endpoint再關(guān)聯(lián)到pod。
- service只要?jiǎng)?chuàng)建完,就會在dns中添加一個(gè)資源記錄進(jìn)行解析,添加完成即可進(jìn)行解析。資源記錄的格式為:SVC_NAME.NS_NAME.DOMAIN.LTD.
- 默認(rèn)的集群service 的A記錄:svc.cluster.local.
- redis服務(wù)創(chuàng)建的A記錄:redis.default.svc.cluster.local.
創(chuàng)建NodePort類型Service
將myapp-deploy發(fā)布出去,并可以通過集群外部進(jìn)行訪問
[root@master manifests]# kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS filebeat-ds-h8rwk 1/1 Running 0 12m app=filebeat,controller-revision-hash=7f59445876,pod-template-generation=1,release=stable filebeat-ds-kzhxw 1/1 Running 0 12m app=filebeat,controller-revision-hash=7f59445876,pod-template-generation=1,release=stable myapp-deploy-65df64765c-257gl 1/1 Running 0 26s app=myapp,pod-template-hash=65df64765c,release=canary myapp-deploy-65df64765c-czwkg 1/1 Running 0 26s app=myapp,pod-template-hash=65df64765c,release=canary myapp-deploy-65df64765c-hqmkd 1/1 Running 0 26s app=myapp,pod-template-hash=65df64765c,release=canary myapp-deploy-65df64765c-kvj92 1/1 Running 0 26s app=myapp,pod-template-hash=65df64765c,release=canary readiness-httpget-pod 1/1 Running 0 2d22h <none> redis-76c85b5744-94djm 1/1 Running 0 12m app=redis,pod-template-hash=76c85b5744,role=logstore#編輯yaml文件 [root@master manifests]# vim myapp-svc.yaml apiVersion: v1 kind: Service metadata:name: myappnamespace: default spec:selector:app: myapprelease: canaryclusterIP: 10.99.99.99type: NodePortports:- port: 80targetPort: 80nodePort: 31111 #節(jié)點(diǎn)端口設(shè)置非常用端口創(chuàng)建并查看svc
[root@master manifests]# kubectl apply -f myapp-svc.yaml service/myapp created [root@master manifests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d22h myapp NodePort 10.99.99.99 <none> 80:31111/TCP 8s redis ClusterIP 10.97.97.97 <none> 6379/TCP 10m此時(shí)可以在集群外使用31111端口進(jìn)行訪問
[root@nfs ~]# while true;do curl http://10.0.0.11:31111/hostname.html;sleep 1;done myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-hqmkd myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-czwkg myapp-deploy-65df64765c-kvj92**總結(jié):**從以上例子,可以看到通過NodePort方式已經(jīng)實(shí)現(xiàn)了從集群外部端口進(jìn)行訪問,訪問鏈接如下:http://10.0.0.10:31111 。實(shí)踐中并不鼓勵(lì)用戶自定義使用節(jié)點(diǎn)的端口,因?yàn)槿菀缀推渌F(xiàn)存的Service沖突,建議留給系統(tǒng)自動配置。
Pod的會話保持
Service資源還支持Session affinity(粘性會話)機(jī)制,可以將來自同一個(gè)客戶端的請求始終轉(zhuǎn)發(fā)至同一個(gè)后端的Pod對象,這意味著它會影響調(diào)度算法的流量分發(fā)功用,進(jìn)而降低其負(fù)載均衡的效果。因此,當(dāng)客戶端訪問Pod中的應(yīng)用程序時(shí),如果有基于客戶端身份保存某些私有信息,并基于這些私有信息追蹤用戶的活動等一類的需求時(shí),那么應(yīng)該啟用session affinity機(jī)制。
Service affinity的效果僅僅在一段時(shí)間內(nèi)生效,默認(rèn)值為10800秒,超出時(shí)長,客戶端再次訪問會重新調(diào)度。該機(jī)制僅能基于客戶端IP地址識別客戶端身份,它會將經(jīng)由同一個(gè)NAT服務(wù)器進(jìn)行原地址轉(zhuǎn)換的所有客戶端識別為同一個(gè)客戶端,由此可知,其調(diào)度的效果并不理想。Service 資源 通過. spec. sessionAffinity 和. spec. sessionAffinityConfig 兩個(gè)字段配置粘性會話。 spec. sessionAffinity 字段用于定義要使用的粘性會話的類型,它僅支持使用“ None” 和“ ClientIP” 兩種屬性值。如下:
[root@master ~]# kubectl explain svc.spec.sessionAffinity. KIND: Service VERSION: v1FIELD: sessionAffinity <string>DESCRIPTION:Supports "ClientIP" and "None". Used to maintain session affinity. Enableclient IP based session affinity. Must be ClientIP or None. Defaults toNone. More info:https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxiessessionAffinity支持ClientIP和None 兩種方式,默認(rèn)是None(隨機(jī)調(diào)度) ClientIP是來自于同一個(gè)客戶端的請求調(diào)度到同一個(gè)pod中:
[root@master manifests]# vim myapp-svc-clientip.yaml apiVersion: v1 kind: Service metadata:name: myappnamespace: default spec:selector:app: myapprelease: canarysessionAffinity: ClientIPtype: NodePortports:- port: 80targetPort: 80nodePort: 31111[root@master manifests]# kubectl apply -f myapp-svc-clientip.yaml service/myapp created[root@master manifests]# kubectl describe svc myapp Name: myapp Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration:{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"ports":[{"nodePort":31111,... Selector: app=myapp,release=canary Type: NodePort IP: 10.106.188.204 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31111/TCP Endpoints: 10.244.1.40:80,10.244.1.41:80,10.244.2.45:80 + 1 more... Session Affinity: ClientIP External Traffic Policy: Cluster Events: <none>[root@nfs ~]# while true;do curl http://10.0.0.11:31111/hostname.html;sleep 1;done myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl myapp-deploy-65df64765c-257gl也可以使用kubectl patch來動態(tài)修改
kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClusterIP"}}' #session保持,同一ip訪問同一個(gè)pod kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"None"}}' #取消sessionHeadless無頭Service
有時(shí)不需要或不想要負(fù)載均衡,以及單獨(dú)的Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 “None” 來創(chuàng)建 Headless Service。
這個(gè)選項(xiàng)允許開發(fā)人員自由尋找他們自己的方式,從而降低與 Kubernetes 系統(tǒng)的耦合性。 應(yīng)用仍然可以使用一種自注冊的模式和適配器,對其它需要發(fā)現(xiàn)機(jī)制的系統(tǒng)能夠很容易地基于這個(gè) API 來構(gòu)建。
對這類 Service 并不會分配 Cluster IP,kube-proxy 不會處理它們,而且平臺也不會為它們進(jìn)行負(fù)載均衡和路由。 DNS 如何實(shí)現(xiàn)自動配置,依賴于 Service 是否定義了 selector
[root@master manifests]# vim myapp-svc-headless.yaml apiVersion: v1 kind: Service metadata:name: myapp-headlessnamespace: default spec:selector:app: myapprelease: canaryclusterIP: "None"ports:- port: 80targetPort: 80部署并查看
[root@master manifests]# kubectl apply -f myapp-svc-headless.yaml service/myapp-headless created [root@master manifests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d23h myapp NodePort 10.106.188.204 <none> 80:31111/TCP 10m myapp-headless ClusterIP None <none> 80/TCP 2s redis ClusterIP 10.97.97.97 <none> 6379/TCP 68m使用coredns進(jìn)行解析驗(yàn)證
[root@master manifests]# dig -t A myapp-svc.default.svc.cluster.local. @10.96.0.10; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> -t A myapp-svc.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 35237 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;myapp-svc.default.svc.cluster.local. IN A;; AUTHORITY SECTION: cluster.local. 30 IN SOA ns.dns.cluster.local. hostmaster.cluster.local. 1554105626 7200 1800 86400 30;; Query time: 2 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: 一 4月 01 16:03:40 CST 2019 ;; MSG SIZE rcvd: 157#10.96.0.10是coredns服務(wù)的svc地址 [root@master manifests]# kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 5d解析普通的svc 來對比查看區(qū)別
[root@master manifests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10; <<>> DiG 9.9.4-RedHat-9.9.4-73.el7_6 <<>> -t A myapp.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26217 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0;; QUESTION SECTION: ;myapp.default.svc.cluster.local. IN A;; ANSWER SECTION: myapp.default.svc.cluster.local. 5 IN A 10.99.99.99;; Query time: 0 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: 一 4月 01 16:09:34 CST 2019 ;; MSG SIZE rcvd: 96從以上的演示可以看到對比普通的service和headless service,headless service做dns解析是直接解析到pod的,而servcie是解析到ClusterIP的。
headless無頭服務(wù)主要用在statefulset中。
參考資料
https://www.cnblogs.com/linuxk
https://www.cnblogs.com/wlbl/p/10694316.html
總結(jié)
以上是生活随笔為你收集整理的k8s service服务发现详解:ipvs代理模式、服务类型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: k8s常用命令整理
- 下一篇: k8s集群dns问题解决办法