本文收錄在容器技術學習系列文章總目錄html
Kubernetes Pod 是有生命週期的,它們能夠被建立,也能夠被銷燬,然而一旦被銷燬生命就永遠結束。 經過 ReplicationController 可以動態地建立和銷燬 Pod(例如,須要進行擴縮容,或者執行 滾動升級)。 每一個 Pod 都會獲取它本身的 IP 地址,即便這些 IP 地址不老是穩定可依賴的。 這會致使一個問題:在 Kubernetes 集羣中,若是一組 Pod(稱爲 backend)爲其它 Pod (稱爲 frontend)提供服務,那麼那些 frontend 該如何發現,並鏈接到這組 Pod 中的哪些 backend 呢?答案是:Service。node
Kubernetes Service 定義了這樣一種抽象:一個 Pod 的邏輯分組,一種能夠訪問它們的策略 —— 一般稱爲微服務。 這一組 Pod 可以被 Service 訪問到,一般是經過 Label Selector(下面咱們會講到咱們爲何須要一個沒有label selector的服務)實現的。redis
舉個例子,考慮一個圖片處理 backend,它運行了3個副本。這些副本是可互換的 —— frontend 不須要關心它們調用了哪一個 backend 副本。 然而組成這一組 backend 程序的 Pod 實際上可能會發生變化,frontend 客戶端不該該也不必知道,並且也不須要跟蹤這一組 backend 的狀態。 Service 定義的抽象可以解耦這種關聯。算法
對 Kubernetes 集羣中的應用,Kubernetes 提供了簡單的 Endpoints API,只要 Service 中的一組 Pod 發生變動,應用程序就會被更新。 對非 Kubernetes 集羣中的應用,Kubernetes 提供了基於 VIP 的網橋的方式訪問 Service,再由 Service 重定向到 backend Pod。vim
這種模式,kube-proxy 會監視 Kubernetes master 對 Service 對象和 Endpoints 對象的添加和移除。 對每一個 Service,它會在本地 Node 上打開一個端口(隨機選擇)。 任何鏈接到「代理端口」的請求,都會被代理到 Service 的backend Pods 中的某個上面(如 Endpoints 所報告的同樣)。 使用哪一個 backend Pod,是基於 Service 的 SessionAffinity 來肯定的。 最後,它安裝 iptables 規則,捕獲到達該 Service 的 clusterIP(是虛擬 IP)和 Port 的請求,並重定向到代理端口,代理端口再代理請求到 backend Pod。後端
網絡返回的結果是,任何到達 Service 的 IP:Port 的請求,都會被代理到一個合適的 backend,不須要客戶端知道關於 Kubernetes、Service、或 Pod 的任何信息。api
默認的策略是,經過 round-robin 算法來選擇 backend Pod。 實現基於客戶端 IP 的會話親和性,能夠經過設置 service.spec.sessionAffinity 的值爲 "ClientIP" (默認值爲 "None")。bash
這種模式,kube-proxy 會監視 Kubernetes master 對 Service 對象和 Endpoints 對象的添加和移除。 對每一個 Service,它會安裝 iptables 規則,從而捕獲到達該 Service 的 clusterIP(虛擬 IP)和端口的請求,進而將請求重定向到 Service 的一組 backend 中的某個上面。 對於每一個 Endpoints 對象,它也會安裝 iptables 規則,這個規則會選擇一個 backend Pod。服務器
默認的策略是,隨機選擇一個 backend。 實現基於客戶端 IP 的會話親和性,能夠將 service.spec.sessionAffinity 的值設置爲 "ClientIP" (默認值爲 "None")。網絡
和 userspace 代理相似,網絡返回的結果是,任何到達 Service 的 IP:Port 的請求,都會被代理到一個合適的 backend,不須要客戶端知道關於 Kubernetes、Service、或 Pod 的任何信息。 這應該比 userspace 代理更快、更可靠。然而,不像 userspace 代理,若是初始選擇的 Pod 沒有響應,iptables 代理可以自動地重試另外一個 Pod,因此它須要依賴 readiness probes。
ipvs (IP Virtual Server) 實現了傳輸層負載均衡,也就是咱們常說的4層LAN交換,做爲 Linux 內核的一部分。ipvs運行在主機上,在真實服務器集羣前充當負載均衡器。ipvs能夠將基於TCP和UDP的服務請求轉發到真實服務器上,並使真實服務器的服務在單個 IP 地址上顯示爲虛擬服務。
在kubernetes v1.8 中引入了 ipvs 模式,在 v1.9 中處於 beta 階段,在 v1.11 中已經正式可用了。 iptables 模式在 v1.1 中就添加支持了,從 v1.2 版本開始 iptables 就是 kube-proxy 默認的操做模式,ipvs 和 iptables 都是基於netfilter的, ipvs 模式和 iptables 模式之間的差別:
同時ipvs 也依賴 iptables,ipvs 會使用 iptables 進行包過濾、SNAT、masquared(假裝)。具體來講,ipvs 將使用ipset來存儲須要DROP或masquared的流量的源或目標地址,以確保 iptables 規則的數量是恆定的,這樣咱們就不須要關心咱們有多少服務了
ipvs雖然在v1.1版本中已經支持,可是想要使用,還需激活ipvs:
① 修改配置文件
[root@master ~]# vim /etc/sysconfig/kubelet KUBE_PROXY=MODE=ipvs
② 編寫腳本,讓kubelet所在的主機,啓動時裝入如下幾個模塊:
ip_vs,ip_vs_rr,ip_vs_wrr,ip_vs_sh,nf_conntrack_ipv4
(1)編寫yaml文件並建立名爲redis的service
先建立一個deployment,啓動一個redis pod;在使用service綁定這個pod
[root@master manifests]# vim redis-svc.yaml apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: default spec: replicas: 1 selector: matchLabels: app: redis role: logstor template: metadata: labels: app: redis role: logstor spec: containers: - name: redis image: redis:4.0-alpine ports: - name: redis containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis namespace: default spec: selector: app: redis role: logstor clusterIP: 10.99.99.99 type: ClusterIP ports: - port: 6380 targetPort: 6379 [root@master manifests]# kubectl apply -f redis-svc.yaml deployment.apps/redis created service/redis created
(2)查詢驗證
[root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 142d redis ClusterIP 10.99.99.99 <none> 6380/TCP 12s ---查詢service詳細信息,pod綁定成功 [root@master ~]# 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.99.99.99","ports":[{"por... Selector: app=redis,role=logstor Type: ClusterIP IP: 10.99.99.99 Port: <unset> 6380/TCP TargetPort: 6379/TCP Endpoints: 10.244.2.94:6379 Session Affinity: None Events: <none>
(1)編寫yaml文件並建立名爲myapp的service
先建立一個deployment,啓動3個myapp pod;在使用service綁定這3個pod
[root@master manifests]# vim myapp-svc.yaml apiVersion: apps/v1 kind: Deployment metadata: name: myapp-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: myapp release: canary template: metadata: labels: app: myapp release: canary spec: containers: - name: myapp image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp release: canary clusterIP: 10.97.97.97 type: NodePort ports: - port: 80 targetPort: 80 nodePort: 31180 [root@master manifests]# kubectl apply -f myapp-svc.yaml deployment.apps/myapp-deploy unchanged service/myapp created
(2)查詢驗證
[root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 145d myapp NodePort 10.97.97.97 <none> 80:31180/TCP 39s redis ClusterIP 10.99.99.99 <none> 6380/TCP 2d [root@master ~]# 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":{"clusterIP":"10.97.97.97","ports":[{"nod... Selector: app=myapp,release=canary Type: NodePort IP: 10.97.97.97 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31180/TCP Endpoints: 10.244.1.96:80,10.244.2.101:80,10.244.2.102:80 Session Affinity: None External Traffic Policy: Cluster Events: <none>
(3)在集羣外訪問服務
(1)sessionAffinity默認是None,沒有修改前,訪問業務是隨機調度
[root@master ~]# while true; do curl 192.168.10.103:31180/hostname.html; sleep 1; done myapp-deploy-69b47bc96d-mmb5v myapp-deploy-69b47bc96d-wtbx7 myapp-deploy-69b47bc96d-wtbx7 myapp-deploy-69b47bc96d-cj48v ... ...
(2)打補丁修改sessionAffinity爲clientip;實現會話鏈接
也可使用exec修改;或者直接修改yaml文件也能夠;
[root@master ~]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClientIP"}}' service/myapp patched
(3)查詢驗證
[root@master ~]# 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":{"clusterIP":"10.97.97.97","ports":[{"nod... Selector: app=myapp,release=canary Type: NodePort IP: 10.97.97.97 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 31180/TCP Endpoints: 10.244.1.96:80,10.244.2.101:80,10.244.2.102:80 Session Affinity: ClientIP External Traffic Policy: Cluster Events: <none>
(4)訪問業務查詢驗證;發現同一客戶端的請求始終發往同一pod
[root@master ~]# while true; do curl 192.168.10.103:31180/hostname.html; sleep 1; done myapp-deploy-69b47bc96d-cj48v myapp-deploy-69b47bc96d-cj48v myapp-deploy-69b47bc96d-cj48v myapp-deploy-69b47bc96d-cj48v ... ...
(5)從新打補丁修改成None,當即恢復爲隨機調度
[root@master ~]# kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"None"}}' service/myapp patched [root@master ~]# while true; do curl 192.168.10.103:31180/hostname.html; sleep 1; done myapp-deploy-69b47bc96d-cj48v myapp-deploy-69b47bc96d-mmb5v myapp-deploy-69b47bc96d-cj48v myapp-deploy-69b47bc96d-mmb5v
(1)編寫yaml文件並建立名爲myapp-svc的service
綁定上面建立myapp的3個pod
[root@master manifests]# vim myapp-svc-headless.yaml apiVersion: v1 kind: Service metadata: name: myapp-svc 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-svc created
(2)查詢驗證
[root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 145d myapp NodePort 10.97.97.97 <none> 80:31180/TCP 2h myapp-svc ClusterIP None <none> 80/TCP 6s redis ClusterIP 10.99.99.99 <none> 6380/TCP 2d
(3)和有頭正常myapp的service對比
無頭service的解析:
[root@master manifests]# dig -t A myapp-svc.default.svc.cluster.local. @10.96.0.10 ... ... ;; ANSWER SECTION: myapp-svc.default.svc.cluster.local. 5 IN A 10.244.1.96 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.2.101 myapp-svc.default.svc.cluster.local. 5 IN A 10.244.2.102 ... ...
有頭正常myapp的service的解析:
[root@master manifests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10 ... ... ;; ANSWER SECTION: myapp.default.svc.cluster.local. 5 IN A 10.97.97.97 ... ...