目錄html
Service是對一組提供相同功能的Pods的抽象,併爲它們提供一個統一的入口。藉助Service,應用能夠方便的實現服務發現與負載均衡,並實現應用的零宕機升級。Service經過標籤來選取服務後端,通常配合Replication Controller或者Deployment來保證後端容器的正常運行。這些匹配標籤的Pod IP和端口列表組成endpoints,由kubeproxy負責將服務IP負載均衡到這些endpoints上。
Service有四種類型:node
另外,也能夠將已有的服務以Service的形式加入到Kubernetes集羣中來,只須要在建立
Service的時候不指定Label selector,而是在Service建立好後手動爲其添加endpoint。linux
在 Kubernetes 集羣中,每一個 Node 運行一個 kube-proxy 進程。kube-proxy 負責爲 Service 實現了一種 VIP(虛擬 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理徹底在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但並非默認的運行模式。 從 Kubernetes v1.2 起,默認就是 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)服務。
kube-proxy 這個組件始終監視着apiserver中有關service的變更信息,獲取任何一個與service資源相關的變更狀態,經過watch監視,一旦有service資源相關的變更和建立,kube-proxy都要轉換爲當前節點上的可以實現資源調度規則(例如:iptables、ipvs)。redis
當客戶端Pod請求內核空間的service iptables後,把請求轉到給用戶空間監聽的kube-proxy 的端口,由kube-proxy來處理後,再由kube-proxy將請求轉給內核空間的 service ip,再由service iptalbes根據請求轉給各節點中的的service pod。
這個模式有很大的問題,由客戶端請求先進入內核空間的,又進去用戶空間訪問kube-proxy,由kube-proxy封裝完成後再進去內核空間的iptables,再根據iptables的規則分發給各節點的用戶空間的pod。這樣流量從用戶空間進出內核帶來的性能損耗是不可接受的。在Kubernetes 1.1版本以前,userspace是默認的代理模型。
算法
客戶端IP請求時,直接請求本地內核service ip,根據iptables的規則直接將請求轉發到到各pod上,由於使用iptable NAT來完成轉發,也存在不可忽視的性能損耗。另外,若是集羣中存在上萬的Service/Endpoint,那麼Node上的iptables rules將會很是龐大,性能還會再打折扣。iptables代理模式由Kubernetes 1.1版本引入,自1.2版本開始成爲默認類型。
vim
Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本開始成爲默認設置。客戶端IP請求時到達內核空間時,根據ipvs的規則直接分發到各pod上。kube-proxy會監視Kubernetes Service對象和Endpoints,調用netlink接口以相應地建立ipvs規則並按期與Kubernetes Service對象和Endpoints對象同步ipvs規則,以確保ipvs狀態與指望一致。訪問服務時,流量將被重定向到其中一個後端Pod。後端
與iptables相似,ipvs基於netfilter 的 hook 功能,但使用哈希表做爲底層數據結構並在內核空間中工做。這意味着ipvs能夠更快地重定向流量,而且在同步代理規則時具備更好的性能。此外,ipvs爲負載均衡算法提供了更多選項,如,rr輪詢,lc最小鏈接數,dh目標哈希,sh源哈希,sed最短時間望延遲,nq不排隊調度。
注意: ipvs模式假定在運行kube-proxy以前在節點上都已經安裝了IPVS內核模塊。當kube-proxy以ipvs代理模式啓動時,kube-proxy將驗證節點上是否安裝了IPVS模塊,若是未安裝,則kube-proxy將回退到iptables代理模式。
api
當某個服務後端pod發生變化,標籤選擇器適應的pod有多一個,適應的信息會當即反映到apiserver上,而kube-proxy必定能夠watch到etc中的信息變化,而將它當即轉爲ipvs或者iptables中的規則,這一切都是動態和實時的,刪除一個pod也是一樣的原理。
服務器
apiVersion: kind: metadata: spec: clusterIP: 能夠自定義,也能夠動態分配 ports:(與後端容器端口關聯) selector:(關聯到哪些pod資源上) type:服務類型
Service的服務類型
對一些應用(如 Frontend)的某些部分,可能但願經過外部(Kubernetes 集羣外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 容許指定一個須要的類型的 Service,默認是 ClusterIP 類型。
Type 的取值以及行爲以下:session
[root@master manifests]# vim redis-svc.yaml apiVersion: v1 kind: Service metadata: name: redis namespace: default spec: selector: app: redis role: logstore clusterIP: 10.97.97.97 type: ClusterIP ports: - port: 6379 #容器端口 targetPort: 6379 #Pod端口
建立並查看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服務詳細信息
[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>
總結:
將myapp-deploy發佈出去,並能夠經過集羣外部進行訪問
[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: myapp namespace: default spec: selector: app: myapp release: canary clusterIP: 10.99.99.99 type: NodePort ports: - port: 80 targetPort: 80 nodePort: 31111 #節點端口設置很是用端口
建立並查看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
此時能夠在集羣外使用31111端口進行訪問
[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
總結:從以上例子,能夠看到經過NodePort方式已經實現了從集羣外部端口進行訪問,訪問連接以下:http://10.0.0.10:31111 。實踐中並不鼓勵用戶自定義使用節點的端口,由於容易和其餘現存的Service衝突,建議留給系統自動配置。
Service資源還支持Session affinity(粘性會話)機制,能夠未來自同一個客戶端的請求始終轉發至同一個後端的Pod對象,這意味着它會影響調度算法的流量分發功用,進而下降其負載均衡的效果。所以,當客戶端訪問Pod中的應用程序時,若是有基於客戶端身份保存某些私有信息,並基於這些私有信息追蹤用戶的活動等一類的需求時,那麼應該啓用session affinity機制。
Service affinity的效果僅僅在一段時間內生效,默認值爲10800秒,超出時長,客戶端再次訪問會從新調度。該機制僅能基於客戶端IP地址識別客戶端身份,它會將經由同一個NAT服務器進行原地址轉換的全部客戶端識別爲同一個客戶端,由此可知,其調度的效果並不理想。Service 資源 經過. spec. sessionAffinity 和. spec. sessionAffinityConfig 兩個字段配置粘性會話。 spec. sessionAffinity 字段用於定義要使用的粘性會話的類型,它僅支持使用「 None」 和「 ClientIP」 兩種屬性值。以下:
[root@master ~]# kubectl explain svc.spec.sessionAffinity. KIND: Service VERSION: v1 FIELD: sessionAffinity <string> DESCRIPTION: Supports "ClientIP" and "None". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
sessionAffinity支持ClientIP和None 兩種方式,默認是None(隨機調度) ClientIP是來自於同一個客戶端的請求調度到同一個pod中:
[root@master manifests]# vim myapp-svc-clientip.yaml apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp release: canary sessionAffinity: ClientIP type: NodePort ports: - port: 80 targetPort: 80 nodePort: 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來動態修改
kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClusterIP"}}' #session保持,同一ip訪問同一個pod kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"None"}}' #取消session
有時不須要或不想要負載均衡,以及單獨的Service IP。 遇到這種狀況,能夠經過指定 Cluster IP(spec.clusterIP)的值爲 "None" 來建立 Headless Service。
這個選項容許開發人員自由尋找他們本身的方式,從而下降與 Kubernetes 系統的耦合性。 應用仍然可使用一種自注冊的模式和適配器,對其它須要發現機制的系統可以很容易地基於這個 API 來構建。
對這類 Service 並不會分配 Cluster IP,kube-proxy 不會處理它們,並且平臺也不會爲它們進行負載均衡和路由。 DNS 如何實現自動配置,依賴於 Service 是否認義了 selector
[root@master manifests]# vim myapp-svc-headless.yaml apiVersion: v1 kind: Service metadata: name: myapp-headless namespace: default spec: selector: app: myapp release: canary clusterIP: "None" ports: - port: 80 targetPort: 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進行解析驗證
[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服務的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 來對比查看區別
[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無頭服務主要用在statefulset中。
https://www.cnblogs.com/linuxk 馬永亮. Kubernetes進階實戰 (雲計算與虛擬化技術叢書) Kubernetes-handbook-jimmysong-20181218