摘要:本文屬於原創,歡迎轉載,轉載請保留出處:https://github.com/jasonGeng88/blogphp
文章一:帶着問題學 Kubernetes 架構git
文章二:帶着問題學 Kubernetes 基本單元 Podgithub
Mac OS 10.11.xdocker
kubectl == v1.6.4api
minikube == v0.19.1架構
docker == 1.11.1app
Service 的 Selector 與 Label 匹配機制負載均衡
Service 與 Pods 的地址映射關係性能
kube-proxy 的 iptables 代理機制學習
Service 的服務發現機制
Service 的服務暴露方式
上一篇講述了 Pod 的相關內容,瞭解了 Pod 的定義、生命週期以及通訊機制等。正如上文說的,Pod 是存在生命週期的,它的崩潰、更新都是以建立新 Pod 替換原有的 Pod 的方式進行的,因此經過固定 Pod 地址的訪問變得不太可行。咱們須要經過一種上層調用的方式,來解決底層 Pod 的動態變化的場景。
慶幸,K8S 引入了 Service 這個抽象的概念。Service 會建立一個虛擬的服務,由它來整合集羣內的 Pod。Service 會虛擬出一個 VIP,並在它銷燬以前保持該 VIP 地址保持不變。經過對它的訪問,以代理的方式負載到對應的 Pod 上,同時 Pod 生命週期的變換,也會及時反應在代理上。
下面咱們幾種常見的場景,來具體看看 Service 是如何工做的。
鏡像名:jasonn/php-echoserver
做用:打印當前容器的 IP
文件名:deploy-echoserver.yml (這裏以 Deployment 的方式來建立與管理 Pod)
文件內容:
apiVersion: apps/v1beta1 kind: Deployment metadata: # Deployment 實例名稱 name: echoserver spec: # 設置 Pod 個數 replicas: 2 template: metadata: # 設置 Pod 標籤 labels: app: echoserver spec: # 運行 docker 鏡像 containers: - name: echoserver image: jasonn/php-echoserver
啓動命令:
kubectl create -f deploy-echoserver.yml
至此,準備工做所有完成。短暫的等待後,Pod 建立成功,而且也由 deployment 管理着。
查看 deployment 啓動狀況:
對 Pod 的訪問狀況以下(經過kubectl describe pods
獲取 Pod 的 IP 地址):
這裏所說的 Pod 默認都是帶有標籤(label)的,咱們以前建立的兩個 Pod 所賦予的標籤是 app: echoserver
,因此咱們在建立 Service 時,就要經過選擇器(selector)來獲取符合條件的 Pod 進行整合。
Service 建立腳本內容以下(service-echoserver.yml):
apiVersion: v1 kind: Service metadata: # Service 實例名稱 name: svc-echoserver spec: ports: - protocol: TCP # Service 端口地址 port: 8080 # Pod 端口地址 targetPort: 80 selector: # 匹配符合標籤條件的 Pod app: echoserver
建立 Service 命令:
kubectl create -f service-echoserver.yml
由此,咱們建立好了一個 Service,同時也生成了一個對應的 VIP。
查看 Serivce 建立狀況:
下面,咱們來驗證下是否如以前所說,對 VIP 的訪問能訪問到 Pod 的內容。
咱們發現不只能成功訪問,並且還提供了負載均衡的功能。後面會講負載是怎麼實現的
PS: 標籤篩選查找範圍僅在同個命名空間(namespace)內。
這裏引出了另外一個概念 Endpoints。咱們先來看看它的一個具體狀況。
發如今 Service 建立的同時,還生成了一個 Endpoints。 該 Endpoints 與 Service 同名,它所暴露的地址信息正是對應 Pod 的地址。由此猜想是 Endpoints 維護了 Service 與 Pod 的映射關係。
爲了驗證咱們的猜想,咱們手動刪除 Endpoints,發現以前能成功訪問到 Pod 的 VIP,如今已經已經訪問不到了。
咱們在手動把 Endpoints 建立回來,建立腳本以下(endpoint-echoserver.yml):
apiVersion: v1 kind: Endpoints metadata: # Endpoints 實例的名稱 name: svc-echoserver subsets: - addresses: - ip: 172.17.0.5 - ip: 172.17.0.6 ports: - port: 80
建立命令:
kubectl create -f endpoint-echoserver.yml
注意:Endpoints 與 Service 的綁定關係經過名稱來關聯的,因此這二者的名稱(name)必定要一致。
若是建立失敗,出現的錯誤信息是「...endpoints "svc-echoserver" already exists」,說明 Service 已經更新了 Endpoints。這裏就說到了 Service 會按期去檢查 Pod 的狀態,而且將結果更新到 Endpoints 上。
VIP 再次訪問時又能成功訪問到,如圖:
如今咱們已經能輕鬆的解決場景2的問題了,在建立 Service 時,只要不設置 Selector
屬性,那麼將不會自動建立 Endpoints,這是咱們能夠根據需求手動的建立指定地址(address)的 Endpoints,來解決標籤沒法實現的整合。
咱們如今要作的呢,是將 VIP 請求給轉發到對應的 Pod 上。而實現此的正是 iptables。
瞭解 iptables 的同窗都知道四表五鏈的概念,而作端口地址轉發的呢,主要是在 nat 表中實現。咱們下面看一下一個 VIP 請求在 nat 表中是如何一步步被轉發到 Pod 上的。
根據 iptables 的機制,請求是先到 nat 表的 PREROUTING 鏈(chain)上的,它的規則以下:
從圖中發現,請求首先通過 KUBE-SERVICE 鏈,其次再到 DOCKER 鏈上的。
咱們看一下 KUBE-SERVICE 的狀況:
咱們發現 KUBE-SERVICE 中包含了一系列 Service 的規則。根據咱們請求的 VIP 的目的地址,對應到了下一個名叫 KUBE-SVC-PRQ3AXYQLQGIVVIU 的 Service 鏈上。
KUBE-SVC-PRQ3AXYQLQGIVVIU 規則以下:
從規則的名字上能夠看出,該條 Service 鏈上記錄的是2個 Endpoints 鏈,具體的選擇是經過 50% 的隨機性的進行決定(這也是它的一個負載規則)。
咱們來看第一個名叫 KUBE-SEP-JSFY3ZFM2EVD64VQ 的 Endpoints 鏈的狀況:
從圖中,咱們已經很清晰的看到了它轉發到 Pod 的具體規則。
下面以一張簡單的流程圖,看一下請求的轉發狀況:
關於 DOCKER 鏈的跟蹤,方法是差很少的,請求 從 nat 表結束後,在到 filter 表中,這裏就不加以說明了。
而這些規則的實現正是由 Service、Endpoints 來完成的。咱們在建立、更新、以及其自身的檢測機制,都會對這些規則進行更新。
對於服務與服務間的調用,實際上就是 Pod 對 Servie 的調用。而 Pod 是如何發現 Service 的,這裏可選擇的方式有2種。
咱們經過啓動一個名爲 busybox 的 Pod 來觀察這兩種方式:
kubectl run -i --tty busybox --image=busybox --restart=Never -- sh
環境變量:
在 Pod 中,集羣中的 Service 會以環境變量的方式賦值在容器中,咱們能夠經過 {SERVICE_NAME}_SERVICE_HOST
和 {SERVICE_NAME}_SERVICE_PORT
進行獲取(對於有多個 Port 的,能夠經過帶指定 PORT 名稱的變量得到。)。
busybox 中 環境變量以下:
查看訪問狀況:
dns 解析:
第二種方式是經過 kube-dns 對 Service 進行域名解析,一樣能達到服務發現的目的。
查看 DNS 域名解析配置:
經過 nslookup 查詢 dns 記錄:
查看訪問結果:
在 Service 的配置文件中,屬性spec.type
就是用來設置服務暴露的方式,它提供的三種方式以下:
ClusterIP: 提供一個集羣內部的虛擬IP以供Pod訪問(默認類型,咱們上述使用的正是這種方式)。
NodePort: 在每一個Node上打開一個端口以供外部訪問。
LoadBalancer: 經過外部的負載均衡器來訪問(通常須要雲提供商提供 LB 支持)。
咱們這裏簡單起見,仍是經過 NodePort 方式進行。
修改 Service 配置文件,並從新啓動:
apiVersion: v1 kind: Service metadata: # Service 實例名稱 name: svc-echoserver spec: ports: - protocol: TCP # Service 端口地址 port: 8080 # Pod 端口地址 targetPort: 80 selector: # 匹配符合標籤條件的 Pod app: echoserver type: NodePort
注意:這裏若是要以kubecrl replace -f service-echoserver.yml
方式進行平滑更新,配置中需添加spec.clusterIP
屬性,值爲當前 Service 的 VIP,不然更新會失敗。這也符合了一開始說的 Service 在它終止以前,VIP 是不會改變的。
查看 Service 更新狀況:
外部訪問(該 Node 地址是:192.168.64.6):
文本從 Service 的標籤與選擇器開始,講了 Service 整合 Pod 的過程,引出了 Service, Endpoints, Pods 三者的關係狀況。隨後又經過 iptables 詳細展開了 kube-proxy 的代理機制。最後,以 Service 的集羣內與集羣外的訪問設置,講述了 Service 的服務發現與服務暴露機制。
關於 Service 的有遺漏重要的知識點,或者有講的不對的地方,也歡迎提出和指正!最後,但願本篇對你學習 K8S 有所幫助~