k8s的部署架構(gòu)
kubernetes中有兩類資源,分別是master和nodes,master和nodes上跑的服務(wù)如下圖,
kube-apiserver | kubelet
kube-controller-manager |
kube-scheduler | kube-proxy
---------------------- --------------------
k8s master node (non-master)
-
master:負(fù)責(zé)管理整個(gè)集群,例如,對(duì)應(yīng)用進(jìn)行調(diào)度(擴(kuò)縮)、維護(hù)應(yīng)用期望的狀態(tài)、對(duì)應(yīng)用進(jìn)行發(fā)布等。 -
node:集群中的宿主機(jī)(可以是物理機(jī)也可以是虛擬機(jī)),每個(gè)node上都有一個(gè)agent,名為kubelet,用于跟master通信。同時(shí)一個(gè)node需要有管理容器的工具包,用于管理在node上運(yùn)行的容器(docker或rkt)。一個(gè)k8s集群至少要有3個(gè)節(jié)點(diǎn)。
kubelet通過master暴露的API與master通信,用戶也可以直接調(diào)用master的API做集群的管理。
k8s中的對(duì)象Objects
pod
k8s中的最小部署單元,不是一個(gè)程序/進(jìn)程,而是一個(gè)環(huán)境(包括容器、存儲(chǔ)、網(wǎng)絡(luò)ip:port、容器配置)。其中可以運(yùn)行1個(gè)或多個(gè)container(docker或其他容器),在一個(gè)pod內(nèi)部的container共享所有資源,包括共享pod的ip:port和磁盤。
pod是臨時(shí)性的,用完即丟棄的,當(dāng)pod中的進(jìn)程結(jié)束、node故障,或者資源短缺時(shí),pod會(huì)被干掉?;诖耍脩艉苌僦苯觿?chuàng)建一個(gè)獨(dú)立的pods,而會(huì)通過k8s中的controller來對(duì)pod進(jìn)行管理。
controller通過pod templates來創(chuàng)建pod,pod template是一個(gè)靜態(tài)模板,創(chuàng)建出來之后的pod就跟模板沒有關(guān)系了,模板的修改也不會(huì)影響現(xiàn)有的pod。
services
由于pod是臨時(shí)性的,pod的ip:port也是動(dòng)態(tài)變化的。這種動(dòng)態(tài)變化在k8s集群中就涉及到一個(gè)問題:如果一組后端pod作為服務(wù)提供方,供一組前端的pod所調(diào)用,那服務(wù)調(diào)用方怎么自動(dòng)感知服務(wù)提供方。這就引入了k8s中的另外一個(gè)核心概念,services.
service是通過apiserver創(chuàng)建出來的對(duì)象實(shí)例,舉例,
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
這個(gè)配置將創(chuàng)建出來一個(gè)新的Service對(duì)象,名為my-service,后端是所有包含app=MyApp的pod,目標(biāo)端口是9376,同時(shí)這個(gè)service也會(huì)被分配一個(gè)ip,被稱為集群ip,對(duì)應(yīng)的端口是80. 如果不指定targetPort, 那么targetPort與port相同。關(guān)于targetPort更靈活的設(shè)定是,targetPort可以是一個(gè)String類型的名字,該名字對(duì)應(yīng)的真實(shí)端口值由各個(gè)后端pod自己定義,這樣同一組pod無需保證同一個(gè)port,更加靈活。
在某種場景下,可能你不想用label selector,不想通過選擇標(biāo)簽的方式來獲取所有的后端pod,如測試環(huán)境下同一個(gè)service name可能指向自己的pod,而非生產(chǎn)環(huán)境中的正式pod集群?;蛘遬od集群并不在k8s中維護(hù)。針對(duì)這個(gè)場景,k8s也有優(yōu)雅的方案進(jìn)行適配。就是在創(chuàng)建service的時(shí)候不指定selector的部分,而是先創(chuàng)建出來service,之后手動(dòng)綁定后端pod的ip和端口。
終于知道為什么k8s是目前風(fēng)頭最勁的服務(wù)編排技術(shù)了,它充分地做了解耦,由于google的業(yè)務(wù)復(fù)雜性,它的組件和組件之間,充分的解耦、靈活,整個(gè)系統(tǒng)松散且牢固。
services組件與bns不同的一點(diǎn),bns的節(jié)點(diǎn)是自己指定了name和后端的關(guān)聯(lián)關(guān)系,而services是根據(jù)pod上的標(biāo)簽(label)自動(dòng)生成的,更靈活。ali的group就更別提了,group是隸屬于app的,擴(kuò)展性方面更弱一些。
上文說在創(chuàng)建service的時(shí)候,系統(tǒng)為service分配了一個(gè)集群虛IP和端口,服務(wù)使用方通過這個(gè)vip:port來訪問真實(shí)的服務(wù)提供方。這里的vip就是kube-proxy提供出來的。
虛ip和service proxies
kube-proxy的模式
- userspace: client -> iptables -> kube-proxy -> backend pod(rr), iptables只是把虛ip轉(zhuǎn)換成kube-proxy的ip,通過kube-proxy自己維護(hù)的不同端口來輪詢轉(zhuǎn)發(fā)到后端的pod上。
- iptables: client -> iptables -> backend pod(random),kube-proxy只是監(jiān)聽master上service的創(chuàng)建,之后動(dòng)態(tài)添加/刪除本機(jī)上的iptables規(guī)則
- ipvs: client -> ipvs ->backend pod, ipvs是一個(gè)內(nèi)核模塊
服務(wù)發(fā)現(xiàn)
服務(wù)使用方如何找到我們定義的Service? 在k8s中用了兩個(gè)方案,環(huán)境變量 && DNS。
環(huán)境變量
每當(dāng)有service被創(chuàng)建出來之后,各個(gè)node(宿主機(jī))上的kubelet,就會(huì)把service name加到自己宿主機(jī)的環(huán)境變量中,供所有Pod使用。環(huán)境變量的命名規(guī)則是{SERVICE_NAME}_SERVICE_HOST, ${SERVICE_NAME}SERVICE_PORT,其中SERVICE_NAME是新serviceName的大寫形式,serviceName中的橫杠-會(huì)被替換成下劃線.
使用環(huán)境變量有一個(gè)隱含的創(chuàng)建順序,即服務(wù)使用方在通過環(huán)境變量訪問一個(gè)service的時(shí)候,這個(gè)service必須已經(jīng)存在了。
這么簡單粗暴的方案...這樣做有個(gè)好處,就是省的自己搞名字解析服務(wù),相當(dāng)于本地的agent做了“域名劫持”。serviceName對(duì)應(yīng)到上文提到的,由kube-proxy提供的vip:port上。DNS
這是官方不推薦的做法,推薦用來跟k8s的外部服務(wù)進(jìn)行交互,ExternalName.
Headless services
在創(chuàng)建service的時(shí)候,用戶可以給spec.clusterIp指定一個(gè)特定的ip。前提是這個(gè)ip需要是一個(gè)合法的IP,并且要在apiServer定義的service-cluster-ip-range范圍內(nèi)。
當(dāng)然,如果用戶有自己的服務(wù)發(fā)現(xiàn)服務(wù),也可以不用k8s原生的service服務(wù),這時(shí),需要顯式地給spec.clusterIp設(shè)置None,這樣k8s就不會(huì)給service分配clusterIp了。
如果用戶將spec.cluster設(shè)置為None,但指定了selector,那么endpoints controller還是會(huì)創(chuàng)建Endpoints的,會(huì)創(chuàng)建一個(gè)新的DNS記錄直接指向這個(gè)service描述的后端pod。否則,不會(huì)創(chuàng)建Endpoints記錄。 // 這塊沒看懂
發(fā)布服務(wù),服務(wù)類型
- ClusterIp: 默認(rèn)配置,給service分配一個(gè)集群內(nèi)部IP,k8s集群外部不識(shí)別。
- NodePort:宿主機(jī)端口,集群外部的服務(wù)也訪問,使用nodeIp:nodePort
- LoadBalancer:通過云負(fù)載均衡,將service暴露出去 // 沒理解
- ExternalName:將serviceName與externalName綁定,讓外網(wǎng)可以訪問到。要求
kube-dns的版本>= 1.7