再次了解kubernetes
的基本概念和術語
1、kubernetes整體概要
在Kubernetes中,Service(服務)是分布式集群架構的一個核心,一個Service對象擁有如下特征。
- 擁有一個唯一指定的名字(如mysql-server)。
- 擁有一個虛擬IP(Cluster IP、Service IP或VIP)。
- 能夠提供某個遠程服務的能力。
- 被印射到了提供這種服務能力的一組容器應用之上。
Service的服務進程目前都是居于Socker通信方式對外提供服務,如Redis、Memcache、Mysql、Web Service,或是實現了某個具體業務的一個特定的TCP Server進程。雖然一個Service通常由多個相關的服務進程來提供服務,每個服務進程都有一個Endpoint(ip+port)訪問點,但kubernetes能夠讓我們通過Service(虛擬Cluster IP + Service Port)連接到指定的Service上面。
kubernetes內建的透明負載均衡、故障恢復機制及service創建后ip不變化,基于此可以向用戶提供穩定的服務。
容器提供了強大的隔離功能,所以有必要把Service提供服務的這組進程放到容器中進行隔離。為此,kubernetes設計了Pod對象,將每個服務進程包裝到相應的Pod中,使其成為Pod中運行的一個容器(Container)。為了建立Service和Pod間的關聯聯系,kubernetes首先給每個Pod貼上一個標簽(Label),如給運行mysql的Pod貼上name=mysql的標簽,然后給相應的Service定義標簽選擇器(Label Selector),如mysql的Service的標簽選擇器的選擇條件為name=mysql,意為Service下所有包含name-=mysql的Label的Pod上。
說到Pod,簡單介紹其概念。首先,Pod運行在一個我們稱之為節點(Node)的環境中,這個節點可能是物理機或虛擬機,通常一個節點上上面運行幾百個Pod;其次每個Pod運行著一個特殊的被稱之為Pause的容器,其它的容器被稱為業務容器,這些業務容器共享Pause容器的網絡棧和Volume掛載卷,因此它們之間的通信和數據交換效率更為高效。在設計時我們可以充分利用這一特性將一組密切相關的服務進程放到同一個Pod中。最后需要注意的是并不是每一個Pod和它里面運行的容器都能映射到一個Service上,只有那些提供服務的一組Pod才會被映射成一個服務。
在集群管理方面,kubernetes將集群中的機器劃分為一個Master節點和一群工作節點(Node)。其中Master節點運行著集群管理的一組進程kube-apiserver
、kube-controller-manage
、kube-scheduler
。這些進程實現了整個集群的資源管理、Pod調度、彈性收縮、安全控制、系統控制和糾錯等管理功能,并且全是自動完成的。Node作為一個集群中的工作節點,運行真正的應用程序,在Node上kubernetes管理的最小單元是Pod。Node運行著kubernetes的kubelet
、kube-proxy
服務進程,這些服務進程負責Pod的創建、啟動、監控、重啟和銷毀,以及實現軟件模式的均衡負載。
解決傳統的服務擴容和服務升級兩個難題里,kubernetes集群中,我們只需要為擴容的service關聯的Pod創建一個Replication Controller
(簡稱RC),在這個RC定義文件中寫好下面幾個關鍵信息:
- 目標Pod的定義。
- 目標Pod需要運行的副本數量(Replicas)。
- 要監控目標Pod的標簽(Label)。
在創建好RC(系統將自動創建好Pod)后,kubernetes會通過RC中定義的Label篩選出對應的Pod實例并全自動實時監控其狀態和數量。
2、基本概念和術語
kubernetes中大部分概念如Node
、Pod
、Replication Controller
、Service
等都可以被看作一種資源對象,幾乎所有的資源對象都可以通過kubernetes提供的kubectl
工具(或API編程調用)執行增刪改查等操作,并將其保存在etcd中持久化存儲。從這個角度來看,kubernetes其實是一個高度自動化的資源控制系統,它通過跟蹤對比保存在etcd庫里的“資源期望狀態”和當前環境中的“實際資源狀態”的差異來實現自動控制和自動糾錯的高級功能。
2.1 Master
kubernetes里的Master指的是集群控制節點。每個kubernetes集群里都需要一個Master節點來負責整個集群的管理和控制,基本上kubernetes所有的控制命令都是發給它,它來負責具體的執行過程,我們后面所有的執行的命令基本上都是在Master節點上運行的。Master節點通常會占據一個獨立的服務器或虛擬機,就是它的重要性體現,一個集群的大腦,如果它宕機,那么整個集群將無法響應控制命令。
Master節點上運行著以下幾組關鍵進程:
- Kubernetes API Server(kube-apiserver):提供了HTTP rest接口的關進服務進程,是Kubernetes里所有資源增刪改查等操作的唯一入口,也是集群管理的入口進程。
- Kubernetes Controller Manage(kube-controller-manage):Kubernetes里所有資源對象的自動化控制中心,可以理解為資源對象的“大總管”。
- Kubernetes Scheduler(kube-scheduler):負責資源的調度(Pod調度)的進程。
Master還啟動了一個etcd server進程,因為kubernetes里所有的資源對象的數據都是保存在etcd中的。
2.2 Node
除了Master,kubernetes集群的其它機器也被稱為Node節點,在較早的版本也被稱為Monion。同樣的它也是一臺物理機或是虛擬機。Node節點才是Kubernetes集群中工作負載節點,每個Node都會被Master分配一些工作負載(Docker容器),當某個Node宕機之后,其上的工作負載會被Master自動轉移到其它節點上面去。
每個Node節點運行著以下幾個關鍵進程:
- kubelet:負責pod對應的同期創建、啟動停止等任務,同時與Master節點密切協作,實現集群管理的基本功能。
- kube-proxy:實現Kubernetes Service的通信與負載均衡的重要組件。
- Docker-Engine(docker):docker引擎,負責本機容器的創建和管理工作。
Node節點可以在運行期間動態增加到Kubernetes集群中,前提是這個節點已經正確安裝、配置和啟動的上述關鍵進程。在默認情況下kubenet會向Master注冊自己,這也是kubernetes推薦的管理方式。一旦Node被納入集群管理范圍,kubelet進程就會定時向Master節點匯報自身的情況,包括操作系統、Docker版本、機器cpu和內存情況及有哪些pod在運行。這樣Master可以獲知Node的資源使用情況并實現高效均衡的資源調度策略。而某個Node超過時間不進行上報信息時,master將會將其標記為失聯(Not Ready),隨后會觸發資源轉移的自動流程。
2.3 Pod
Pod是Kubernets最重要也是最基礎的概念,其由下圖示意組成:
需求推動發展,下面解釋為何會有pod的設計和其如此特殊的結構。
- 一組容器作為單元的情況下,難以去對“整體”進行簡單判斷并采取有效的行動。如一個容器掛了,是算整體服務掛了,還是算N/M的死亡率。引入業務無關并且不易掛掉的
Pause
容器作為Pod的根容器,以它的狀態作為整體容器組的狀態,就簡單巧妙的解決了這個問題。 - Pod多個業務容器共享Pause容器的IP,共享Pause容器掛接的Volume卷。這樣既簡化了密切關聯業務容器之間的通信問題,還很好地解決了它們之間文件共享的問題。
k8s為每個Pod都都分配了唯一的ip,稱之為Pod IP,一個Pod里的多個容器共享Pod IP地址。k8s要求底層網絡支持集群內任意兩個Pod之間的TCP/IP直接通信,這通常采用虛擬二層網絡技術來實現,如Flannel、Openswith等,牢記在k8s中,一個Pod容器與另外主機上的Pod容器能夠直接通信。
Pod其實有兩種類型:普通的Pod及靜態的Pod(static Pod),后者比較特殊,并不存放在k8s的etcd里面存儲,而是存放在某一個具體的Node上的一個文件中,并且只在此Node上啟動運行。而普通的Pod一旦被創建,就會被放入到etcd中存儲,隨后會被k8s Master調度到某個具體的Node上面進行綁定(Binding),隨后該Pod被對應的Node上的kubblet進程實例化成一組的Docker容器并啟動起來。默認情況下,當Pod里的某個容器停止時,k8s會自動檢測這個問題并重啟啟動這個Pod(Pod里面的所有容器),如果Pod所在的Node宕機,則會將這個Node上的所有Pod重新調度到其它節點上。Pod、容器與Node的關系如下:
k8s里的所有的資源對象都可以采用yaml或JSON格式的文件定義或描述,下面是某個Pod文件的資源定義文件。
apiSerdion: v1
kind: Pod
matadata:
name: myweb
labels:
name: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
kind為Pod表明這是一個Pod的定義;matadata的name屬性為Pod的名字,還能定義資源對象的標簽Label,這里聲明myweb擁有一個name=myweb
的標簽。Pod里面所包含的容器組的定義則在spec中聲明,這里定義一個名字為myweb,鏡像為kubeguide/tomcat-app:v1
的容器,該容器注入了名為MYSQL_SERVICE_HOST='mysql'
和MYSQL_SERVICE_PORT='3306'
的環境變量(env關鍵字)。Pod的IP加上這里的容器端口(containerPort)就組成了一個新的概念Endpoint(端口),它代表Pod的一個服務進程對外的通信地址。一個Pod也存在著具有多個Endpoint的情況,比如tomcat定義pod時候,可以暴露管理端口和服務端口兩個Endpoint。
我們所熟悉的Docker Volume在k8s中也有相應的概念 - Pod Volume,后者有一些幻化,比如可以用分布式系統GlusterFS實現后端存儲功能;Pod Volume是定義在Pod之上,然后被各個容器掛載到自己的文件系統中的。
最后是k8s中Event的概念,Event是一個事件的記錄,記錄了時間的最早產生時間、最后重現時間、重復次數、發起者、類型及導致此次事件的原因等總舵信息。Event通常會關聯到某個具體的資源對象上,是排查故障的重要信息參考信息。Node描述信息包含Event,而Pod同樣有Event記錄。當我們發現某個Pod遲遲無法創建時,可以用kubectl describe pod name
來查看它的信息,用來定位問題的原因。
每個Pod都可以對其能使用的服務器上的計算資源設置限額,當前可以設置的限額的計算資源有CPU和Memory兩種,其中CPU的資源單位為CPU(core)核數,一個絕對值。
一個cpu的配額對絕大多數容器去是一個相當大的資源配額,所以在k8s中,通常以千分之一的cpu配額為最小單位,用m
來表示,即一個容器的限額為100-300m,即占用0.1-0.3個cpu限額。由于它是絕對值,所以無論在1個cpu的機器還是多個cpu的機器,這個100m的配額都是一樣的。類似的Memory配額也是一個絕對值,單位是內存字節數。
在k8s中,一個計算資源進行配額限定需要設定下面兩個參數:
- Request;該資源的最小申請量,系統必須滿足要求。
- Limits:該資源允許最大的使用量,不能被突破,當容器視圖使用超出這個量的資源時,可能會被k8s殺死并重啟。
通常會把Request設置為一個比較小的數值,符合同期平時的工作負載情況下的資源要求,而把Limit設置為峰值負載情況下資源占用的最大量。如下面的設定:
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
即表明mysql容器在申請最少0.25個cpu及64Mib的內存,在運行過程中mysql容器所能使用的資源配額為0.5個cpu及128Mib的內存。
2.4 Label(標簽)
Label是k8s系統中另一個核心的概念。一個Label是一個key=value的鍵值對,其中key和value由用戶自己指定。Label對象可以附加到各種資源對象上面,如Node、Pod、Service、RC等,一個資源對象可以定義任意數量的Label,同一個Label也可以被添加到任意資源對象上去,Label通常在資源對象定義時確定,也可以通過對象創建之后動態添加或刪除。
我們可以通過給指定的資源對象捆綁一個或多個不同的Label來實現多維度的資源分組管理功能,以便于靈活、方便地進行資源分配、調度、配置和布署等管理工作。一些常用的label如下:
- 版本控制:"release": "stable", "release": "canary"
- 環境標簽:"environment": "dev", "environment": "qa", "environment": "production"
- 架構標簽:"tier": "frontend", "release": "backend", "release": "middkeware"
- 分區標簽:"partition": "customerA"
- 質量管控標簽:"track": "daily", "track": "weekly"
給某個資源對象定義一個Label,隨后可以通過Label Selector(標簽選擇器)查詢和篩選擁有某些Label的資源對象,k8s通過這種方式實現了類是SQL的簡單通用的對象查詢方式。
當前有兩種Label Selector
的表達式:基于等式的(Equality-based)和基于集合(Set-based)。
基于等式的表達式匹配標簽實例:
- name=redis-slave:匹配所有具有標簽name=redis-slave的資源對象。
- env!=production: 匹配不具有標簽name=production的資源對象。
基于集合方式的表達式匹配標簽實例:
- name in (redis-slave, redis-master):匹配所有具有標簽name=redis-slave或name=redis-master的資源對象。
- name not in (php-frontend):匹配不具有標簽name=php-frontend的資源對象。
可以通過多個Label Selector表達式的組合實現復雜的條件選擇,多個表達式之間用“,”分隔即可,幾個條件是and的關系,即同時滿足多個條件,如:
- name=redis-slave, env!=production
- name not in (php-frontend), env!=production
Label Selector在k8s中重要使用場景有下面幾處:
- kube-controller-manage進程通過資源對象RC上定義的Label Selector來篩選要監控的Pod副本的數量。
- kube-proxy進程通過Service的Label Selector來選擇對于那個的Pod,自動建立起Service對應每個Pod的請求轉發路由表,從而實現Service的智能負載均衡機制。
- 通過對某些Node定義特定的Label,并且Pod定義文件中使用NodeSelector這種標簽調度策略,kube-schedule進程可以實現Pod“定向調度”的特性。
總結:使用Label可以給資源對象創建多組標簽,Label和Label Selector共同構成了k8s系統中最核心的應用模型,使得被管理對象能夠被精細地分組管理,同時實現了整個集群的高可用性。
2.5 Replication Controller(RC)
前面部分對Replication Controller(簡稱RC)的定義和作用做了一些說明,本節對RC的概念再進行深入研究下。
RC是k8s系統中的核心概念之一,簡單來說,它是定義了一個期望的場景。即聲明某種Pod的副本數量在任意時刻都符合某個預期值,所以RC的定義包含如下幾個部分:
- Pod期待的副本數(replicas)。
- 用于篩選目標Pod的Label Selector。
- 當Pod的副本數量小于預期數量的時候,用于創建新Pod的Pod模版(templates)。
下面是一個完整的RC定義的例子,即確保擁有tier=frontend
標簽的這個Pod(運行Tomcat容器)在整個集群中始終只有一個副本。
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 1
selector:
tier: frontend
templete:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
當我們定義了一個RC并提交到k8s集群中以后,Master節點上的Controller Manager組件就得到了通知,定期巡檢系統中當前存活目標的Pod,并確保目標Pod實例的數量剛好等于此RC的期望值。如果有過多的Pod副本在進行,系統就會停掉一些Pod,否則系統會自動創建一些Pod。可以說,通過RC,k8s實現了用戶應用集群的高可用性,并且大大減少了系統管理員在傳統環境中完成許多的運維工作(主機監控、應用監控、故障恢復)。
需要注意的有幾點:刪除RC并不會影響已經通過該RC創建好的Pod。為了刪除所有的Pod,可以設置replicas為0,然后更新RC。另外,kubectl提供了stop和delete命令來一次性刪除RC和RC控制的全部Pod。
當我們應用升級時,通常會通過Build一個新的Docker鏡像,并用新的鏡像版本來代替舊的版本方式達到目的。在系統升級的過程中,我們希望是平滑的方式,如系統中10個對應舊版本的Pod,最佳的方式就是就舊版本的Pod每次停止一個,同時創建一個新版本的Pod,在整個升級的過程中,此消彼長,而運行中的Pod始終是10個,幾分鐘后,當所有的Pod都是新版本之后,升級過程完成。通過RC的機制,k8s很容易就實現這種高級實用的特性,被稱為“滾動升級”(Rolling Update)。
由于Replication Controller與k8s代碼中的模塊Replication Controller同名,同時這個詞又無法準確表達它的本意,所以在k8s1.2的時候,它就升級成了另外一個新的概念 - Replica Set,官網解釋為“下一代的RC”。與當前RC區別在于:Replica Sets支持基于集合的Label Selector(Set-based selector),而當前RC只支持基于等式的Label Selector(equality-based selector)。
當前我們很少使用Replica Set,它主要被Deployment這個更高層的資源對象使用,從而形成一整套Pod的創建、刪除、更新的編排機制。當我們使用Deployment時,無需關系它是如何創建和維護Replica Set,都是自動完成的。
Replica Set與Deployment這兩個重要的資源對象逐步替換了之前RC的作用,是k8s1.3 Pod自動擴容(伸縮)這個告警功能實現的基礎。
最后總結RC(Replica Set)的的一些特性與作用:
- 多數情況下,我們通過定義一個RC實現Pod的創建過程及副本數量的自動控制,可以實現Pod的彈性收縮。
- RC里包括完整的Pod定義模版。
- RC通過Label Selector機制實現對Pod的自動控制。
- 通過改變RC里Pod模版的鏡像版本,可以實現Pod的滾動升級功能。
2.6 Deployment
Deployment是k8s 1.2之后引入的新概念,引入的目的就是為了更好的解決Pod的編排問題。為此,Deployment在內部使用了Replica Set來實現目的,可以看作是RC的一次升級。
Deployment典型使用場景有以下幾個:
- 創建一個Deployment對象來生成對應的Replica Set并完成Pod副本的創建過程。
- 檢查Deployment的狀態來看布署動作是否完成(Pod副本的數量是否達到預期值)。
- 更新Deployment以創建新的Pod(如鏡像升級)。
- 如果當前Deployment不穩定,則回滾到早先的Deployment版本。
- 掛起或恢復一個Deployment。
下面是一個實例,文件名為tomcat-deployment.yaml:
apiVersion: extension/v1beta1
kind: Deployment
matadata:
name: frontend
spec:
replica: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
matadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
命令kubectl create -f tomcat-deployment.yaml
,命令以后再嘗試。
2.7 Horizontal Pod Autoscaler(HPA)
之前提到過,通過手工執行kubectl scale
的命令,我們可以實現Pod擴容或縮容,但谷歌對k8s的定位是自動化、智能化,即分布式系統要能根據當前負載情況變化自動觸發水平擴展或縮容的行為,因為這過程是頻繁發生、不可預料的。
k8s 1.1版本中首次發布這一特性:Horizontal Pod Autoscaling,隨后在1
2版本中HPA被升級為穩定版本(apiVersion: autoscaling/v1),但同時仍保留舊版本(apiVersion: extension/v1beta1),官方計劃是1.3移除舊版本,1.4徹底移除舊版本的支持。
Horizontal Pod Autoscaling(HPA),意味Pod的橫向自動擴容,與之前RC、Deployment一樣,也是k8s資源對象的一種。通過分析RC控制的所有目標Pod的負載變化情況,來確定是否需要針對性地調整目標Pod的副本數,即HPA的實現原理。當前有下兩種方式作為Pod負載的度量指標。
- CPU Utilization Percentage。
- 應用程序自定義度量指標,如服務每秒內相應的請求數(TPS或QPS)。
CPU Utilization Percentage是一個算術平均值,即目標Pod所有副本的CPU利用率的平均值。一個Pod自身的CPU利用率是該Pod當前CPU的使用量除以它的Pod Resquest的值。如我們定義一個Pod的Pod Resquest為0.4,而當前Pod的CPU使用量是0.2,即它的CPU使用率是50%,如此我們就可以算出一個RC所控制的所有Pod副本的CPU利用率的算術平均值了。某一時刻,CPU Utilization Percentage的值超過80%,則意味著當前Pod副本數量可能不支持接下來所有的請求,需要進行動態擴容,而請求高峰時段過去后,Pod利用率又會降下來,對應Pod副本也會減少數量。
CPU Utilization Percentage 計算過程中使用到的Pod的COU的量通常是1分鐘的平均值,目前通過查詢Heapster擴展組件來得到這個值,所以需要安裝布署Heapster,這樣便增加了系統的復雜度和實施HPA特性的復雜度。未來的計劃是實現一個自身性能數據采集的模塊。
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
matadata:
name: php-apache
namespace: default
spec:
maxReplica: 10
minReplica: 1
scaleTargetRef:
kind: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
2.8 Service(服務)
2.8.1 概述
Service同樣也是k8s核心資源對象之一,它即是我們常提起微服務構架的一個“微服務”,之前我們說的Pod、RC等資源對象其實都是為這節所說的Service(服務)做準備的。
k8s的Service定義了一個服務的訪問入口地址,前端的應用(Pod)通過這個入口地址訪問其背后由Pod副本組成的集群實例,Service和其后端Pod副本集群之間則是通過Label Selector來實現無縫對接的;RC的作用實際是保證Service的服務能力與服務質量始終處于預期的標準。
通過分析、識別并建模系統中的所有服務為微服務 - kubernetes Service,最終系統由多個提供不同業務能力而又彼此獨立的微服務單元所組成,服務之間通過TCP/IP進行通信,從而形成強大而又靈活的彈性網絡,擁有了強大的分布式能力、彈性擴展能力、容錯能力,同時程序架構也變得靈活很多。
既然每個Pod都會被分配一個單獨的IP地址,而且每個Pod都提供了一個獨立Endpoint(Pod IP+ContainerPort)以被客戶端訪問,現在多個Pod副本組成了一個集群來提供服務,一般的做法都是布署一個負載局衡器(軟件或硬件),為這組Pod開啟一個對外的服務端口8000,并將這些Pod的Endpoint列表加入8000端口的轉發列表中,客戶端就可以通過負載均衡器的對外IP地址+服務器端口來訪問此服務,而客戶端的請求最后會被轉發到那個Pod,則由負載均衡器的算法決定。
k8s也遵循了上述做法,運行在每個Node上的kube-proxy進程其實就是只能的軟件負載均衡器,它負責把對Service的請求轉發到后端的某個Pod上面,并在內部實現服務的負載均衡和會話保持機制。但k8s發明了一種巧妙影響深遠的設計:Service不是共用一個負載均衡器的IP地址,而是每個Service分配了一個全局唯一的虛擬IP地址,這個IP地址又被稱為Cluster IP。這樣的話,雖然Pod的Endpoint會隨著Pod的創建和銷毀而發生改變,但是Cluster IP會伴隨Service的整個生命周期,只要通過Service的Name和Cluster IP進行DNS域名映射,Pod的服務發現通過訪問Service即可解決。
2.8.2 k8s的服務發現機制
任何分布式系統都會有服務發現的基礎問題,大部分的分布式系統通過提供特定的API接口,但這增加了系統的可侵入性。
最早k8s采用Linux環境變量去解決這個問題,即每個Service生成一些對應的Linux變量(ENV),并在每個Pod容器在啟動時,自動注入這些環境變量。后來k8s通過Add-On增值包的方式引入了DNS系統,把服務名作為DNS域名,這樣程序就可以直接使用服務名來建立通信連接。
2.8.3 外部系統訪問Service的問題
為了更加深入的理解和掌握k8s,我們要弄明白k8s的三個IP的關鍵問題:
- Node IP:Node節點的IP地址。
- Pod IP:Pod的IP地址。
- Cluster IP:Service的IP地址。
Node IP是k8s集群中每個節點物理網卡的IP地址,這是一個真是存在的物理網絡,所有屬于這個網絡的服務器之間都能通過這個網絡直接通信,不管它們中是否有部分節點不屬于這個k8s集群。這也表明了k8s集群之外的節點訪問的k8s集群內的某個節點或者TCP/IP服務的時候,必須通過Node IP進行通信。
Pod IP是每個Pod的IP地址,它是Docker Engine根據docker0網橋的IP地址段進行分配的,通常是一個虛擬的二層網絡。前面說過,k8s要求Pod里的容器訪問另一個Pod的容器時,就是通過Pod IP所在的虛擬二層網絡進行通信的,而真實的TCP/IP流量則是通過Node IP所在的物理網卡流出的。這個參考docker通信相關的細節。
最后看Service的Cluster IP,它也是一個虛擬的IP。更像一個假的IP:
- Cluster IP僅僅作用域k8s Service這個對象,并由k8s管理和分配IP地址(來源Cluster IP地址池)。
- Cluster IP無法被ping,因為沒有一個“網絡實體對象”來響應。
- Cluster IP只能結合Service Port組成一個具體的通信端口,單獨的Cluster IP不具備TCP/IP通信的基礎,并且它們屬于k8s集群這樣一個封閉的空間,集群之外節點若想訪問這個通信端口,需要做額外的操作。
- 在k8s集群之內,Node IP網、Pod IP網與Cluster IP網之間的通信,采用的是k8s自己設計的一種編程方式的特殊的路由規則,和我們熟知的IP路由有很大的不同。
那么基于上述,我們知道:Service的Cluster IP屬于k8s集群內部的地址,無法在集群外部直接使用這個地址。那么矛盾在實際中就是開發的業務肯定是一部分提供給k8s集群外部的應用或用戶使用的,典型的就是web,那用戶如何訪問。
采用NodePort是解決上述最直接、最常用、最有效的方法。
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31002
selector:
tier: frontend
nodePort: 31002這個屬性表名我們手動指定tomcat-service的NodePort為31002,否則k8s就會自動分配一個可用的端口,這個端口我們可以直接在瀏覽器中訪問:http://<nodePort IP>:31002
。
NodePort的實現方式就是在k8s集群里每個Node上為外部訪問的Service開啟一個對應的TCP監聽端口,外部系統只要用任意一個Node的IP地址和具體的NodePort端口號就能訪問這個服務。在任意的Node上運行netstat命令,我們就能看到NodePort端口被監聽。
但NodePort并沒有完全解決Service的所有問題,如負載均衡的問題。假如我們的集群中10個Node,則此時最好有一個均衡負載器,外部的請求只需要訪問此負載均衡器的IP地址,則負載均衡器負責轉發流量到后面某個Node的NodePort上面。
Load Balance組件獨立于k8s集群之外,通常是一個硬件的負載均衡器或是軟件方式實現的,如HAProxy或Nginx。對每個Service,我們通常配置一個對應的Load Balance的實例來轉發流量到后端的Node上,這增加了工作量即出錯的概率。
未完、待續……