運行在Pod中的應用是向客戶端提供服務的守護進程,好比,nginx、tomcat、etcd等等,它們都是受控於控制器的資源對象,存在生命週期,咱們知道Pod資源對象在自願或非自願終端後,只能被重構的Pod對象所替代,屬於不可再生類組件。而在動態和彈性的管理模式下,Service爲該類Pod對象提供了一個固定、統一的訪問接口和負載均衡能力。是否是以爲一堆話都沒聽明白呢????html
其實,就是說Pod存在生命週期,有銷燬,有重建,沒法提供一個固定的訪問接口給客戶端。而且爲了同類的Pod都可以實現工做負載的價值,由此Service資源出現了,能夠爲一類Pod資源對象提供一個固定的訪問接口和負載均衡,相似於阿里雲的負載均衡或者是LVS的功能。node
可是要知道的是,Service和Pod對象的IP地址,一個是虛擬地址,一個是Pod IP地址,都僅僅在集羣內部能夠進行訪問,沒法接入集羣外部流量。而爲了解決該類問題的辦法能夠是在單一的節點上作端口暴露(hostPort)以及讓Pod資源共享工做節點的網絡名稱空間(hostNetwork)之外,還可使用NodePort或者是LoadBalancer類型的Service資源,或者是有7層負載均衡能力的Ingress資源。mysql
Service是Kubernetes的核心資源類型之一,Service資源基於標籤選擇器將一組Pod定義成一個邏輯組合,並經過本身的IP地址和端口調度代理請求到組內的Pod對象,以下圖所示,它向客戶端隱藏了真是的,處理用戶請求的Pod資源,使得從客戶端上看,就像是由Service直接處理並響應同樣,是否是很像負載均衡器呢!nginx
Service對象的IP地址也稱爲Cluster IP,它位於爲Kubernetes集羣配置指定專用的IP地址範圍以內,是一種虛擬的IP地址,它在Service對象建立以後保持不變,而且可以被同一集羣中的Pod資源所訪問。Service端口用於接受客戶端請求,並將請求轉發至後端的Pod應用的相應端口,這樣的代理機制,也稱爲端口代理,它是基於TCP/IP 協議棧的傳輸層。git
在 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)服務。redis
kube-proxy 這個組件始終監視着apiserver中有關service的變更信息,獲取任何一個與service資源相關的變更狀態,經過watch監視,一旦有service資源相關的變更和建立,kube-proxy都要轉換爲當前節點上的可以實現資源調度規則(例如:iptables、ipvs)算法
這種模式,當客戶端Pod請求內核空間的service iptables後,把請求轉到給用戶空間監聽的kube-proxy 的端口,由kube-proxy來處理後,再由kube-proxy將請求轉給內核空間的 service ip,再由service iptalbes根據請求轉給各節點中的的service pod。sql
因而可知這個模式有很大的問題,由客戶端請求先進入內核空間的,又進去用戶空間訪問kube-proxy,由kube-proxy封裝完成後再進去內核空間的iptables,再根據iptables的規則分發給各節點的用戶空間的pod。這樣流量從用戶空間進出內核帶來的性能損耗是不可接受的。在Kubernetes 1.1版本以前,userspace是默認的代理模型。vim
客戶端IP請求時,直接請求本地內核service ip,根據iptables的規則直接將請求轉發到到各pod上,由於使用iptable NAT來完成轉發,也存在不可忽視的性能損耗。另外,若是集羣中存在上萬的Service/Endpoint,那麼Node上的iptables rules將會很是龐大,性能還會再打折扣。iptables代理模式由Kubernetes 1.1版本引入,自1.2版本開始成爲默認類型。後端
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爲負載均衡算法提供了更多選項,例如:
輪詢調度
dh
:目標哈希sh
:源哈希sed
:最短時間望延遲nq
:不排隊調度注意: ipvs模式假定在運行kube-proxy以前在節點上都已經安裝了IPVS內核模塊。當kube-proxy以ipvs代理模式啓動時,kube-proxy將驗證節點上是否安裝了IPVS模塊,若是未安裝,則kube-proxy將回退到iptables代理模式。
若是某個服務後端pod發生變化,標籤選擇器適應的pod有多一個,適應的信息會當即反映到apiserver上,而kube-proxy必定能夠watch到etc中的信息變化,而將它當即轉爲ipvs或者iptables中的規則,這一切都是動態和實時的,刪除一個pod也是一樣的原理。如圖:
3.一、清單建立Service
1 [root@k8s-master ~]# kubectl explain svc 2 KIND: Service 3 VERSION: v1 4 5 DESCRIPTION: 6 Service is a named abstraction of software service (for example, mysql) 7 consisting of local port (for example 3306) that the proxy listens on, and 8 the selector that determines which pods will answer requests sent through 9 the proxy. 10 11 FIELDS: 12 apiVersion <string> 13 APIVersion defines the versioned schema of this representation of an 14 object. Servers should convert recognized schemas to the latest internal 15 value, and may reject unrecognized values. More info: 16 https://git.k8s.io/community/contributors/devel/api-conventions.md#resources 17 18 kind <string> 19 Kind is a string value representing the REST resource this object 20 represents. Servers may infer this from the endpoint the client submits 21 requests to. Cannot be updated. In CamelCase. More info: 22 https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds 23 24 metadata <Object> 25 Standard object's metadata. More info: 26 https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata 27 28 spec <Object> 29 Spec defines the behavior of a service. 30 https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status 31 32 status <Object> 33 Most recently observed status of the service. Populated by the system. 34 Read-only. More info: 35 https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
其中重要的4個字段:
apiVersion:
kind:
metadata:
spec:
clusterIP: 能夠自定義,也能夠動態分配
ports:(與後端容器端口關聯)
selector:(關聯到哪些pod資源上)
type:服務類型
3.二、service的類型
對一些應用(如 Frontend)的某些部分,可能但願經過外部(Kubernetes 集羣外部)IP 地址暴露 Service。
Kubernetes ServiceTypes
容許指定一個須要的類型的 Service,默認是 ClusterIP
類型。
Type
的取值以及行爲以下:
ClusterIP
:經過集羣的內部 IP 暴露服務,選擇該值,服務只可以在集羣內部能夠訪問,這也是默認的 ServiceType
。NodePort
:經過每一個 Node 上的 IP 和靜態端口(NodePort
)暴露服務。NodePort
服務會路由到 ClusterIP
服務,這個 ClusterIP
服務會自動建立。經過請求 <NodeIP>:<NodePort>
,能夠從集羣的外部訪問一個 NodePort
服務。LoadBalancer
:使用雲提供商的負載均衡器,能夠向外部暴露服務。外部的負載均衡器能夠路由到 NodePort
服務和 ClusterIP
服務。ExternalName
:經過返回 CNAME
和它的值,能夠將服務映射到 externalName
字段的內容(例如, foo.bar.example.com
)。 沒有任何類型代理被建立,這隻有 Kubernetes 1.7 或更高版本的 kube-dns
才支持。[root@k8s-master mainfests]# cat redis-svc.yaml apiVersion: v1 kind: Service metadata: name: redis namespace: default spec: selector: #標籤選擇器,必須指定pod資源自己的標籤 app: redis role: logstor type: ClusterIP #指定服務類型爲ClusterIP ports: #指定端口 - port: 6379 #暴露給服務的端口 - targetPort: 6379 #容器的端口 [root@k8s-master mainfests]# kubectl apply -f redis-svc.yaml service/redis created [root@k8s-master mainfests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d redis ClusterIP 10.107.238.182 <none> 6379/TCP 1m [root@k8s-master mainfests]# 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":{"ports":[{"port":6379,"targetPort":6379}... Selector: app=redis,role=logstor Type: ClusterIP IP: 10.107.238.182 #service ip Port: <unset> 6379/TCP TargetPort: 6379/TCP Endpoints: 10.244.1.16:6379 #此處的ip+端口就是pod的ip+端口 Session Affinity: None Events: <none> [root@k8s-master mainfests]# kubectl get pod redis-5b5d6fbbbd-v82pw -o wide NAME READY STATUS RESTARTS AGE IP NODE redis-5b5d6fbbbd-v82pw 1/1 Running 0 20d 10.244.1.16 k8s-node01
從上演示能夠總結出:service不會直接到pod,service是直接到endpoint資源,就是地址加端口,再由endpoint再關聯到pod。
service只要建立完,就會在dns中添加一個資源記錄進行解析,添加完成便可進行解析。資源記錄的格式爲:SVC_NAME.NS_NAME.DOMAIN.LTD.
默認的集羣service 的A記錄:svc.cluster.local.
redis服務建立的A記錄:redis.default.svc.cluster.local.
NodePort即節點Port,一般在部署Kubernetes集羣系統時會預留一個端口範圍用於NodePort,其範圍默認爲:30000~32767之間的端口。定義NodePort類型的Service資源時,須要使用.spec.type進行明確指定。
[root@k8s-master mainfests]# kubectl get pods --show-labels |grep myapp-deploy myapp-deploy-69b47bc96d-4hxxw 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-95bc4 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-hwbzt 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-pjv74 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary myapp-deploy-69b47bc96d-rf7bs 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary [root@k8s-master mainfests]# cat myapp-svc.yaml #爲myapp建立service apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp release: canary type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30080 [root@k8s-master mainfests]# kubectl apply -f myapp-svc.yaml service/myapp created [root@k8s-master mainfests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d myapp NodePort 10.101.245.119 <none> 80:30080/TCP 5s redis ClusterIP 10.107.238.182 <none> 6379/TCP 28m [root@k8s-master mainfests]# while true;do curl http://192.168.56.11:30080/hostname.html;sleep 1;done myapp-deploy-69b47bc96d-95bc4 myapp-deploy-69b47bc96d-4hxxw myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-rf7bs myapp-deploy-69b47bc96d-95bc4 myapp-deploy-69b47bc96d-rf7bs myapp-deploy-69b47bc96d-95bc4 myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-4hxxw myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-4hxxw myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-pjv74 myapp-deploy-69b47bc96d-95bc4 myapp-deploy-69b47bc96d-hwbzt
[root@k8s-master mainfests]# while true;do curl http://192.168.56.11:30080/;sleep 1;done
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
從以上例子,能夠看到經過NodePort方式已經實現了從集羣外部端口進行訪問,訪問連接以下:http://192.168.56.11:30080/。實踐中並不鼓勵用戶自定義使用節點的端口,由於容易和其餘現存的Service衝突,建議留給系統自動配置。
Service資源還支持Session affinity(粘性會話)機制,能夠未來自同一個客戶端的請求始終轉發至同一個後端的Pod對象,這意味着它會影響調度算法的流量分發功用,進而下降其負載均衡的效果。所以,當客戶端訪問Pod中的應用程序時,若是有基於客戶端身份保存某些私有信息,並基於這些私有信息追蹤用戶的活動等一類的需求時,那麼應該啓用session affinity機制。
Service affinity的效果僅僅在一段時間內生效,默認值爲10800秒,超出時長,客戶端再次訪問會從新調度。該機制僅能基於客戶端IP地址識別客戶端身份,它會將經由同一個NAT服務器進行原地址轉換的全部客戶端識別爲同一個客戶端,由此可知,其調度的效果並不理想。Service 資源 經過. spec. sessionAffinity 和. spec. sessionAffinityConfig 兩個字段配置粘性會話。 spec. sessionAffinity 字段用於定義要使用的粘性會話的類型,它僅支持使用「 None」 和「 ClientIP」 兩種屬性值。以下:
[root@k8s-master mainfests]# 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@k8s-master mainfests]# vim myapp-svc.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: 30080 [root@k8s-master mainfests]# kubectl apply -f myapp-svc.yaml service/myapp configured [root@k8s-master mainfests]# 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":30080,"port":80,"ta... Selector: app=myapp,release=canary Type: NodePort IP: 10.101.245.119 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP Endpoints: 10.244.1.18:80,10.244.1.19:80,10.244.2.15:80 + 2 more... Session Affinity: ClientIP External Traffic Policy: Cluster Events: <none> [root@k8s-master mainfests]# while true;do curl http://192.168.56.11:30080/hostname.html;sleep 1;done myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt myapp-deploy-69b47bc96d-hwbzt
也可使用打補丁的方式進行修改yaml內的內容,以下:
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。
(1)編寫headless service配置清單
[root@k8s-master mainfests]# cp myapp-svc.yaml myapp-svc-headless.yaml [root@k8s-master mainfests]# vim myapp-svc-headless.yaml apiVersion: v1 kind: Service metadata: name: myapp-headless namespace: default spec: selector: app: myapp release: canary clusterIP: "None" #headless的clusterIP值爲None ports: - port: 80 targetPort: 80
(2)建立headless service [root@k8s-master mainfests]# kubectl apply -f myapp-svc-headless.yaml service/myapp-headless created [root@k8s-master mainfests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d myapp NodePort 10.101.245.119 <none> 80:30080/TCP 1h myapp-headless ClusterIP None <none> 80/TCP 5s redis ClusterIP 10.107.238.182 <none> 6379/TCP 2h
(3)使用coredns進行解析驗證 [root@k8s-master mainfests]# dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10 ; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp-headless.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62028 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;myapp-headless.default.svc.cluster.local. IN A ;; ANSWER SECTION: myapp-headless.default.svc.cluster.local. 5 IN A 10.244.1.18 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.1.19 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.15 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.16 myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.17 ;; Query time: 4 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: Thu Sep 27 04:27:15 EDT 2018 ;; MSG SIZE rcvd: 349 [root@k8s-master mainfests]# 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 36d [root@k8s-master mainfests]# kubectl get pods -o wide -l app=myapp NAME READY STATUS RESTARTS AGE IP NODE myapp-deploy-69b47bc96d-4hxxw 1/1 Running 0 1h 10.244.1.18 k8s-node01 myapp-deploy-69b47bc96d-95bc4 1/1 Running 0 1h 10.244.2.16 k8s-node02 myapp-deploy-69b47bc96d-hwbzt 1/1 Running 0 1h 10.244.1.19 k8s-node01 myapp-deploy-69b47bc96d-pjv74 1/1 Running 0 1h 10.244.2.15 k8s-node02 myapp-deploy-69b47bc96d-rf7bs 1/1 Running 0 1h 10.244.2.17 k8s-node02
(4)對比含有ClusterIP的service解析 [root@k8s-master mainfests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10 ; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp.default.svc.cluster.local. @10.96.0.10 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50445 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;myapp.default.svc.cluster.local. IN A ;; ANSWER SECTION: myapp.default.svc.cluster.local. 5 IN A 10.101.245.119 ;; Query time: 1 msec ;; SERVER: 10.96.0.10#53(10.96.0.10) ;; WHEN: Thu Sep 27 04:31:16 EDT 2018 ;; MSG SIZE rcvd: 107 [root@k8s-master mainfests]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d myapp NodePort 10.101.245.119 <none> 80:30080/TCP 1h myapp-headless ClusterIP None <none> 80/TCP 11m redis ClusterIP 10.107.238.182 <none> 6379/TCP 2h
從以上的演示能夠看到對比普通的service和headless service,headless service作dns解析是直接解析到pod的,而servcie是解析到ClusterIP的,那麼headless有什麼用呢???這將在statefulset中應用到,這裏暫時僅僅作了解什麼是headless service和建立方法。