原本覺得一篇就能搞定,仍是低估了本身的廢話,好吧,只能經過兩篇文章向你們介紹K8s核心原理。node
kubernetes API server的和核心功能是提供了kubernetes各種資源對象(pod、RC 、service等)的增、刪、改、查以及watch等HTTP Rest接口,是整個系統的數據總線和數據中心。有時候咱們使用kubectl建立或者查看pod等資源的時候,發現沒有反應,可能就是你的kube-apiservice服務異常退出致使的。
Kubernetes API server經過一個名爲kube-apiservice的進程提供服務,該進程運行與master節點上。默認狀況下該進程的端口是本機的8080提供restful服務。(注意若是是HTTPS,則是6443端口)。
接下來的一些操做,介紹一些如何經過rest 與kubernetes API server交互,這有便於後各k8s各個組件之間通訊的理解:mysql
[root@zy ~]# kubectl cluster-info #查看主節點信息
[root@zy ~]# curl localhost:8080/api #查看kubernetes API的版本信息
[root@zy ~]# curl localhost:8080/api #查看kubernetes API支持的全部的資源對象
固然咱們也能夠訪問具體的資源web
[root@zy ~]# curl localhost:8080/api/v1/pods [root@zy ~]# curl localhost:8080/api/v1/services [root@zy ~]# curl localhost:8080/api/v1/replicationcontrollers
當咱們在運行kubectl get svc時會發現:
有一個以上話紅框的服務,這個是什麼呢,原來爲了讓pod中的進程可以知道kubernetes API server的訪問地址,kubernetes API server自己也是一個service,名字就叫「kubernetes」,而且他的cluster IP 就是cluster IP地址池例的第一個地址,另外它服務的端口就是443。算法
kubernetes API server還提供了一類很特殊的rest接口—proxy接口,這個結構就是代理REST請求,即kubernetes API server把收到的rest請求轉發到某個node上的kubelet守護進程的rest端口上,由該kubelet進程負責相應。
舉例:sql
masterIP:8080/api/v1/proxy/nodes/{node_name}/pods #某個節點下全部pod信息 masterIP:8080/api/v1/proxy/nodes/{node_name}/stats #某個節點內物理資源的統計信息 masterIP:8080/api/v1/proxy/nodes/{node_name}/spec #某個節點的概要信息
#接下來講一下比較重要的pod的相關接口docker
masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name}/{path:*} #訪問pod的某個服務接口 masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/{pod_name} #訪問pod
假如這裏有一個名爲myweb的Tomcat的pod
咱們在瀏覽器中輸入masterIP:8080/api/v1/proxy/namespaces/{namespace}/pods/myweb就能訪問到該pod的http服務了。
若是這裏是一個幾個web的pod組成的service的話:後端
masterIP:8080/api/v1/proxy/namespaces/{namespace}/services/{service_name}
就能訪問到其下面的服務,固然最終會經過kube-proxy被定位到相應的pod下。api
Kubernetes API Server做爲集羣的和核心,負責集羣各功能模塊之間的通訊。集羣內的各個功能模塊經過API server將信息存入etcd,一樣的想要獲取和操做這些數據時,也是經過API Server的REST接口(GET、LIST、WATCH)來實現,從而實現各個模塊之間的交互。
接下來小編經過一張圖,簡單介紹一下幾種典型的交互場景:
瀏覽器
介紹:Controller Manager做爲集羣內部管理控制中心,負責集羣內的Node、Pod副本、EndPoint、命名空間、服務帳號、資源定額等管理。當某個Node意外宕機了,Controller Manager會及時發現此故障並執行自動修復流程,確保集羣始終處於預期的工做狀態。
由上圖所示,Controller Manager中包含不少個controller,每一種controller都負責一種具體的控制流程,而Controller Manager正是這些controller的核心管理者。通常來講,智能系統和自動系統都被稱爲一個「操縱系統」的機構來不斷修正系統的工做狀態。在kubernetes集羣中,每一個controller都有這樣一個「操縱系統」,他們經過API Server提供的接口實時監控整個集羣裏的每個資源對象的當前狀態,當發生各類故障致使系統狀態發生變化,這些controller會嘗試將系統從「現有裝態」修正到「指望狀態」。
接下來小編會介紹一些比較重要的Controller。緩存
在介紹replication controller時小編要強調一點的是,千萬不要把資源對象的那個RC和這個replication controller弄混淆了,咱們這裏介紹的replication controller是副本的控制器,RC只是一個資源對象,上層是replication controlle管理各個RC。(這裏咱們統一的將replication controller叫副本的控制器,資源對象RC叫RC)。
副本的控制器的核心做用是確保任何使用集羣中的一個RC所關聯的Pod副本數量保持預設的值。若是發現Pod副本數超過預設的值,則副本的控制器會銷燬一些Pod的副本,反之則建立一些新的Pod的副本以達到目標值。值得注意的是只有當Pod的重啓策略是always時,副本的控制器纔會管理該pod的操做。一般狀況下,pod對象被成功的建立以後不會消失,惟一例外的是當pod處於success或者failed狀態的時間過長(超時時間能夠設定),該pod會被系統自動回收,管理該pod的副本的控制器將在其餘的工做節點上從新建立、啓動該pod。
RC中的Pod模板就像一個模具,模具製做出來的東西一旦離開模具,兩者將毫無關係,一旦pod建立,不管模板如何變化都不會影響到已經建立的pod,而且刪除一個RC 不會影響它所建立出來的Pod,固然若是想在RC控制下,刪除全部的Pod,須要將RC中設置的pod的副本數該爲0,這樣纔會自動刪除全部的Pod。
replication controller(副本的控制器)的職責:
- 確保當前管理的Pod的數量爲預設值
- 經過調用RC的spec.replicas屬性實現系統擴容和縮容
- 經過改變RC中的Pod的模板中的image,來實現系統的滾動升級
replication controller(副本的控制器)的使用場景 :
- 從新調度:不管是否有節點宕機,仍是pod意外死亡,RC均可以保證本身所管理的正在運行Pod的數量爲預設值
- 彈性伸縮:實現集羣的擴容和縮容(根據集羣的可用資源和負載壓力)
- 滾動升級:應用服務升級新的版本,而且保證整個升級過程,應用服務仍可對外提供服務。
Kubelet進程在啓動時會經過API Server註冊自身的節點信息,並定時的向API Server彙報狀態信息,API Server在接受到這些信息後,將這些信息更新到etcd中。Etcd中存儲的節點信息包括:節點的健康狀態、節點資源、節點名稱、節點地址信息、操做系統版本、docker版本、kubelet版本等。而節點的健康狀態有三種:就緒(True)、未就緒(False)、未知(Unknown)。
接下來小編經過圖來介紹Node Controller的核心工做流程:
具體步驟
- 若是controller manager在啓動時設置了--cluster-cidr,那麼爲每個沒有設置spec.PodCIDR的節點生成一個CIDR地址,並用該地址設置節點的spec.PodCIDR屬性。
- 逐個讀取節點信息,此時node controller中有一個nodestatusMap,裏面存儲了信息,與新發送過來的節點信息作比較,並更新nodestatusMap中的節點信息。Kubelet發送過來的節點信息,有三種狀況:未發送、發送但節點信息未變化、發送而且節點信息變化。此時node controller根據發送的節點信息,更新nodestatusMap,若是判斷出在某段時間內沒有接受到某個節點的信息,則設置節點狀態爲「未知」。
- 最後,將未就緒狀態的節點加入到待刪除隊列中,待刪除後,經過API Server將etcd中該節點的信息刪除。若是節點爲就緒狀態,那麼就向etcd中同步該節點信息。
Kubernetes提供了資源配額管理(resourceQuota controller)這裏高級功能,資源配置管理確保了指定的資源對象在任什麼時候候都不會超量佔用系統物理資源,避免了因爲某些業務進程的設計或者實現的缺陷致使整個系統運行紊亂設置意外宕機,對整個集羣的穩定性有着相當重要的做用。
目前kubernetes支持以下三個層次的資源配額管理:
- 容器級別:對CPU 和 memory的限制
- Pod級別:能夠對一個pod內全部容器的可用資源進行限制
- Namespace級別:爲namespace(多租戶)級別的資源限制,其中限制的資源包括:
△ Pod數量
△ RC數量
△ Service數量
△ ResourceQuota數量
△ Secret數量
△ 可持有的PV數量
Kubernetes的配額管理是經過admission control(准入控制)來控制的。admission control當前提供了兩種方式的配額約束,分別是limitRanger和resourceQuota。其中limitRanger做用於pod和容器上。ResourceQuota做用於namespace上,用於限定一個namespace裏的各種資源的使用總額。
Kubernetes的配額管理是經過admission control(准入控制)來控制的。admission control當前提供了兩種方式的配額約束,分別是limitRanger和resourceQuota。其中limitRanger做用於pod和容器上。ResourceQuota做用於namespace上,用於限定一個namespace裏的各種資源的使用總額。
從上圖中,咱們能夠看出,大概有三條路線,resourceQuota controller在這三條路線中都起着重要的做用:
用戶經過API Server能夠建立新的namespace並保存在etcd中,namespace controller定時經過API Server讀取這些namespace信息。若是namespace被API標記爲優雅刪除(經過設置刪除週期),則將該namespace的狀態設置爲「terminating」並保存到etcd中。同時namespace controller刪除該namespace下的serviceAccount,RC,pod,secret,PV,listRange,resourceQuota和event等資源對象。
當namespace的狀態爲「terminating」後,由admission controller的namespaceLifecycle插件來阻止爲該namespace建立新的資源。同時在namespace controller刪除完該namespace中的全部資源對象後,namespace controller對該namespace 執行finalize操做,刪除namespace的spec.finallizers域中的信息。
固然這裏有一種特殊狀況,當個namespace controller發現namespace設置了刪除週期,而且該namespace 的spec.finalizers域值爲空,那麼namespace controller將經過API Server刪除該namespace 的資源。
上圖所示了service和endpoint與pod的關係,endpoints表示一個service對應的全部的pod副本的訪問地址,而endpoints controller就是負責生成和維護全部endpoints對象的控制器。
它負責監聽service和對應的pod副本的變化,若是檢測到service被刪除,則刪除和該service同名的endpoints對象。若是檢測到新的service被建立或者修改,則根據該service的信息獲取到相關的pod列表,而後建立或者更新service對應的endpoints對象。若是檢測到pod的事件,則更新它對應service的endpoints對象(增長或者刪除或者修改對應的endpoint條目)。
kubernetes scheduler 在整個系統中承擔了「承上啓下」的做用,「承上」是指它負責接收controller manager建立的新的pod,爲其安排一個落腳的「家」,「啓下」是指安置工做完成之後,目標node上的kubelet服務進程接管後繼工做,負責pod生命週期中的「下半生」。
咱們都知道將service和pod經過label關聯以後,咱們訪問service的clusterIP對應的服務,就能經過kube-proxy將路由轉發到對應的後端的endpoint(pod IP +port)上,最終訪問到容器中的服務,實現了service的負載均衡功能。
那麼接下來講一說service controller的做用,它實際上是屬於kubernetes與外部的雲平臺之間的一個接口控制器。Service controller監聽service的變化,若是是一個loadBalancer類型的service,則service controller確保外部的雲平臺上該service對應的loadbalance實例被相應的建立、刪除以及更新路由轉發表(根據endpoint的條目)。
kubernetes scheduler 在整個系統中承擔了「承上啓下」的做用,「承上」是指它負責接收controller manager建立的新的pod,爲其安排一個落腳的「家」,「啓下」是指安置工做完成之後,目標node上的kubelet服務進程接管後繼工做,負責pod生命週期中的「下半生」。
具體的來講,kubernetes scheduler的做用就是將待調度的pod(新建的、補足副本而建立的)按照特定的調度算法和調度策略綁定到集羣中某個合適的node上,並將綁定信息寫入到etcd中。整個調度過程分爲三個對象,分別是:待調度的pod列表、可有的合適的node列表、調度算法和策略。一句話就是經過合適的調度算法和策略,將待調度的pod列表中的pod在合適的node上建立並啓動。
接下來小編經過一幅圖簡單介紹一下scheduler的工做流程:
有圖可知:
遍歷全部目標node,篩選出符合要求的候選節點。爲此,kubernetes內置了多種預選策略
肯定優先節點,在第1步的基礎上,採用優選策略,計算出每個節點候選的積分,積分最高者勝出
最後經過API Server將待調度的Pod,通知給最優node上的kubelet,將其建立並運行
在scheduler中可用的預選算有不少:NoDiskconflict、PodFitsResources、PodSelectorMatches、PodFitsHost、CheckNodeLabelPresence、CheckServiceAffinity、PodFitsPorts等策略。其中的5個默認的預選策略:PodFitsPorts、PodFitsResources、NoDiskconflict、PodSelectorMatches、PodFitsHost每一個節點只有經過這5個預選策略後,才能初步被選中,進入下一個流程。
下面小編介紹幾個經常使用的預選策略:
判斷備選pod的gcePersistentDisk或者AWSElasticBlockStore和備選的節點中已存在的pod是否存在衝突具體檢測過程以下:
- 首先,讀取備選pod的全部的volume信息,對每個volume執行一下步驟的衝突檢測
- 若是該volume是gcePersistentDisk,則將volume和備選節點上的全部pod的每一個volume進行比較,若是發現相同的gcePersistentDisk,則返回false,代表磁盤衝突,檢測結束,反饋給調度器該備選節點不合適做爲備選的pod,若是volume是AWSElasticBlockStore,則將volume和備選節點上的全部pod的每一個volume進行比較,若是發現相同的AWSElasticBlockStore,則返回false,代表磁盤衝突,檢測結束,反饋給調度器該備選節點不合適做爲備選的pod
- 最終,檢查備選pod的全部的volume均爲發現衝突,則返回true,代表不存在磁盤衝突,反饋給調度器該備選節點合適備選pod
判斷備選節點資源是否知足備選pod的需求,檢測過程以下:
- 計算備選pod和節點中已存在的pod的全部容器的需求資源(CPU 和內存)的總和
- 得到備選節點的狀態信息,其中包括節點的資源信息
- 若是備選pod和節點中已存在pod的全部容器的需求資源(CPU和內存)的總和超出了備選節點擁有的資源,則返回false,代表備選節點不適合備選pod,不然返回true,代表備選節點適合備選pod
判斷備選節點是否包含備選pod的標籤選擇器指定的標籤:
- 若是pod沒有指定spec.nodeSelector標籤選擇器,則返回true
- 若是得到備選節點的標籤信息,判斷節點是否包含備選pod的標籤選擇器所指的標籤,若是包含返回true,不包含返回false
判斷備選pod的spec.nodeName域所指定的節點名稱和備選節點的名稱是否一致,若是一致返回true,不然返回false。
判斷備選pod所用的端口列表匯中的端口是否在備選節點中被佔用,若是被佔用,則返回false,不然返回true。
Scheduler中的優選策略有:leastRequestedPriority、CalculateNodeLabelPriority和BalancedResourceAllocation等。每一個節點經過優先策略時都會算出一個得分,計算各項得分,最終選出得分值最大的節點做爲優選結果。
小編接下來就給你們介紹一下一些經常使用的優選策略:
該策略用於從備選節點列表中選出資源消耗最小的節點:
- 計算出全部備選節點上運行的pod和備選pod的CPU佔用量
- 計算出全部備選節點上運行的pod和備選pod的memory佔用量
- 根據特定的算法,計算每一個節點的得分
若是用戶在配置中指定了該策略,則scheduler會經過registerCustomPriorityFunction方法註冊該策略。該策略用於判斷策略列出的標籤在備選節點中存在時,是否選擇該備選節點。若是備選節點的標籤在優選策略的標籤列表中且優選策略的presence值爲true,或者備選節點的標籤不在優選策略的標籤列表中且優選策略的presence值爲false,則備選節點score=10,不然等於0。
該優選策略用於從備選節點列表中選出各項資源使用率最均衡的節點:
- 計算出全部備選節點上運行的pod和備選pod的CPU佔用量
- 計算出全部備選節點上運行的pod和備選pod的memory佔用量
- 根據特定的算法,計算每一個節點的得分
在kubernetes集羣中,每一個node上都會啓動一個kubelet服務進程。該進程用於處理master節點下發到本節點的任務,管理Pod以及Pod中的容器。每一個kubelet進程會在API Server上註冊節點信息,按期向master節點彙報節點資源的使用狀況,並經過cAdvisor監控容器和節點的資源。
節點經過設置kubelet的啓動參數「--register-node」來決定是否向API Server註冊本身。若是該參數爲true,那麼kubelet將試着經過API Server註冊本身。在自注冊時,kubelet啓動時還包括如下參數:
-api-servers:API Server的位置
--kubeconfing:kubeconfig文件,用於訪問API Server的安全配置文件
--cloud-provider:雲服務商地址,僅用於共有云環境
若是沒有選擇自注冊模式,用戶須要手動去配置node的資源信息,同時告知ndoe上的kubelet API Server的位置。Kubelet在啓動時經過API Server註冊節點信息,並定時向API Server發送節點新消息,API Server在接受到這些消息以後,將這些信息寫入etcd中。經過kubelet的啓動參數「--node-status-update-frequency」設置kubelet每一個多長時間向API Server報告節點狀態,默認爲10s。
kubelet經過如下幾種方式獲取自身node上所要運行的pod清單:
文件:kubelet啓動參數「--config」指定的配置文件目錄下的文件(默認爲「/etc/Kubernetes/manifests」)經過--file-check-frequency設置檢查該文件的時間間隔,默認爲20s
HTTP端點:經過「--manifest-url」參數設置。經過「--http-check-frequency」設置檢查該HTTP端點數據的時間間隔,默認爲20s。
API Server:kubelet經過API server監聽etcd目錄,同步pod列表
注意:這裏static pod,不是被API Server建立的,而是被kubelet建立,以前文章中提到了靜態的pod是在kubelet的配置文件中編寫,而且總在kubelet所在node上運行。
Kubelet監聽etcd,全部針對pod的操做將會被kubelet監聽到。若是是新的綁定到本節點的pod,則按照pod清單的要求建立pod,若是是刪除pod,則kubelet經過docker client去刪除pod中的容器,並刪除該pod。
具體的針對建立和修改pod任務,流程爲:
- 爲該pod建立一個目錄
- 從API Server讀取該pod清單
- 爲該pod掛載外部volume
- 下載pod用到的secret
- 檢查已經運行在節點中的pod,若是該pod沒有容器或者Pause容器沒有啓動,則先中止pod裏的全部容器的進程。若是pod中有須要刪除的容器,則刪除這些容器
- 檢查已經運行在節點中的pod,若是該pod沒有容器或者Pause容器沒有啓動,則先中止pod裏的全部容器的進程。若是pod中有須要刪除的容器,則刪除這些容器
- 爲pod中的每一個容器作以下操做
△ 爲容器計算一個hash值,而後用容器的名字去查詢docker容器的hash值。若查找到容器,且二者獲得hash不一樣,則中止docker中的容器的進程,而且中止與之關聯pause容器的進程;若兩個相同,則不作任何處理
△ 若是容器被中止了,且容器沒有指定restartPolicy(重啓策略),則不作任何處理
△調用docker client 下載容器鏡像,調用docker client 運行容器
Pod經過兩類探針來檢查容器的健康狀態。一個是livenessProbe探針,用於判斷容器是否健康,告訴kubelet一個容器何時處於不健康狀態,若是livenessProbe探針探測到容器不健康,則kubelet將刪除該容器,並根據容器的重啓策略作相應的處理;若是一個容器不包含livenessProbe探針,那麼kubelet認爲livenessProbe探針的返回值永遠爲「success」。另外一個探針爲ReadinessProbe,用於判斷容器是否啓動完成,且準備接受請求。若是ReadinessProbe探針檢測到失敗,則pod的狀態將被修改,endpoint controller將從service的endpoints中刪除包含該容器所在pod的IP地址的endpoint條目。
Kubelet按期調用容器中的livenessProbe探針來診斷容器的健康狀態。livenessProbe包括如下三種實現方式:
- Execaction:在容器內執行一個命令,若是該命令的退出狀態碼爲0,表示容器健康
- TCPSocketAction:經過容器的IP地址和端口執行一個TCP檢查,若是端口能被訪問,則代表該容器正常
- TCPSocketAction:經過容器的IP地址和端口執行一個TCP檢查,若是端口能被訪問,則代表該容器正常
具體的配置小編以前的文章中有詳細說明:http://www.javashuo.com/article/p-fbxjufex-dp.html
介紹kube-proxy,不得不說service,這裏小編先帶你們回顧一下service,因爲pod每次建立時它的IP地址是不固定的,爲了訪問方便以及負載均衡,這裏引入了service的概念,service在建立後有一個clusterIP,這個IP是固定的,經過labelselector與後端的pod關聯,這樣咱們若是想訪問後端的應用服務,只須要經過service的clusterIP,而後就會將請求轉發到後端的pod上,即便一個反向代理,又是一個負載均衡。
可是在不少狀況下service只是一個概念,而真正將service的做用落實的這是背後的kube-proxy服務進程。那麼接下來就具體的介紹kube-proxy。
在kubernetes集羣中的每個node上都有一個kube-proxy進程,這個進程能夠看作service的透明代理兼負載均衡,其核心功能就是將到某個service的訪問請求轉發到後端的多個pod實例上。對每個TCP類型的kubernetes service,kube-proxy都會在本地node上創建一個socketserver來負責接收請求,而後均勻發送到後端的某個pod的端口上,這個過程默認採用round robin負載均衡算法。另外,kubernetes也提供經過修改service的service.spec.sessionAffinity參數的值來實現會話保持特性的定向發送,若是設置的值爲「clientIP」,那麼則未來來自同一個clientIP的請求都轉發到同一個後端的pod上。
此外,service的clusterIP和nodePort等概念是kube-proxy服務經過Iptables的NAT轉換實現的,kube-proxy在運行過程當中動態建立於service相關的Iptable規則,這些規則實現了clusterIP以及nodePort的請求流量重定向到kube-proxy進程上對應的服務的代理端口的功能。因爲Iptable機制針對的是本地的kube-proxy端口,全部每個node上都要運行kube-proxy組件,這樣一來,在kubernetes集羣內部,咱們能夠在任意node上發起對service的訪問。由此看來,因爲kube-proxy的做用,在service的調用過程當中客戶端無序關心後端有幾個pod,中間過程的通訊,負載均衡以及故障恢復都是透明。
目前kube-proxy的負載均衡只支持round robin算法。round robin算法按照成員列表逐個選取成員,若是一輪循環結束,便從頭開始下一輪循環,如此循環往復。Kube-proxy的負載均衡器在round robin算法獲得基礎上還支持session保持。若是service在定義中指定了session保持,則kube-proxy接受請求時會從本地內存中查找是否存在來自該請求IP的affinitystate對象,若是存在該對象,且session沒有超時,則kube-proxy將請求轉向該affinitystate所指向的後端的pod。若是本地存在沒有來自該請求IP的affinitystate對象,則按照round robin算法算法爲該請求挑選一個endpoint,並建立一個affinitystate對象,記錄請求的IP和指向的endpoint。後面請求就會「黏連」到這個建立好的affinitystate對象上,這就實現了客戶端IP會話保持的功能。
kube-proxy經過查詢和監聽API Server中service與endpoint的變換,爲每個service都創建一個「服務代理對象「,並自動同步。服務代理對相關是kube-proxy程序內部的一種數據結構,它包括一個用於監聽此務請求的socketServer, socketServer的端口是隨機指定的是本地一個空閒端口。此外,kube-proxy內部也建立了一個負載均衡器—loadBalancer, loadBalancer上保存了service到對應的後端endpoint列表的動態路由轉發表,而具體的路由選擇則取決於round robin算法和service的session會話保持。
針對發生變化的service列表,kube-proxy會逐個處理,下面是具體的處理流程:
- 若是service沒有設置集羣IP,這不作任何處理,不然,獲取該service的全部端口定義列表
- 逐個讀取服務端口定義列表中的端口信息,根據端口名稱、service名稱和namespace判斷本地是否已經存在對應的服務代理對象,若是不存在則建立,若是存在而且service端口被修改過,則先刪除Iptables中和該service端口相關的規則,關閉服務代理對象,而後走新建流程併爲該service建立相關的Iptables規則
- 更新負載均衡組件中對應service的轉發地址列表,對於新建的service,肯定轉發時的會話保持策略
- 對於已刪除的service則進行清理
接下來小編經過一個具體的案例,實際的給你們介紹一下kube-proxy的原理:
#首先建立一個service:
apiVersion: v1 kind: Service metadata: labels: name: mysql role: service name: mysql-service spec: ports: - port: 3306 targetPort: 3306 nodePort: 30964 type: NodePort selector: mysql-service: "true"
mysql-service對應的nodePort暴露出來的端口爲30964,對應的cluster IP(10.254.162.44)的端口爲3306,進一步對應於後端的pod的端口爲3306。這裏的暴露出來的30964也就是爲mysql-service服務建立的代理對象在本地的端口,在ndoe上訪問該端口,則會將路由轉發到service上。
mysql-service後端代理了兩個pod,ip分別是192.168.125.129和192.168.125.131。先來看一下iptables。
[root@localhost ~]# iptables -S -t nat
首先若是是經過node的30964端口訪問,則會進入到如下鏈:
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/mysql-service:" -m tcp --dport 30964 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/mysql-service:" -m tcp --dport 30964 -j KUBE-SVC-67RL4FN6JRUPOJYM
而後進一步跳轉到KUBE-SVC-67RL4FN6JRUPOJYM的鏈
-A KUBE-SVC-67RL4FN6JRUPOJYM -m comment --comment "default/mysql-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-ID6YWIT3F6WNZ47P -A KUBE-SVC-67RL4FN6JRUPOJYM -m comment --comment "default/mysql-service:" -j KUBE-SEP-IN2YML2VIFH5RO2T
這裏利用了iptables的--probability的特性,使鏈接有50%的機率進入到KUBE-SEP-ID6YWIT3F6WNZ47P鏈,50%的機率進入到KUBE-SEP-IN2YML2VIFH5RO2T鏈。
KUBE-SEP-ID6YWIT3F6WNZ47P的鏈的具體做用就是將請求經過DNAT發送到192.168.125.129的3306端口。
-A KUBE-SEP-ID6YWIT3F6WNZ47P -s 192.168.125.129/32 -m comment --comment "default/mysql-service:" -j KUBE-MARK-MASQ -A KUBE-SEP-ID6YWIT3F6WNZ47P -p tcp -m comment --comment "default/mysql-service:" -m tcp -j DNAT --to-destination 192.168.125.129:3306
同理KUBE-SEP-IN2YML2VIFH5RO2T的做用是經過DNAT發送到192.168.125.131的3306端口。
-A KUBE-SEP-IN2YML2VIFH5RO2T -s 192.168.125.131/32 -m comment --comment "default/mysql-service:" -j KUBE-MARK-MASQ -A KUBE-SEP-IN2YML2VIFH5RO2T -p tcp -m comment --comment "default/mysql-service:" -m tcp -j DNAT --to-destination 192.168.125.131:3306
總的來講就是:在建立service時,若是不指定nodePort則爲其建立代理對象時代理對象再本地監聽一個隨機的空閒端口,若是設置了nodePort則以nodePort爲本地代理對象的端口。客戶端在訪問本地代理對象的端口後此時會根據iptables轉發規則,將請求轉發到service的clusterIP+port上,而後根據負載均衡策略指定的轉發規則,將請求再次轉發到後端的endpoint的target Port上,最終訪問到具體pod中容器的應用服務,而後將響應返回。
文章內容參考至《kubernetes權威指南》