一、 K8s 是什么?
Kubernetes(k8s)是自動(dòng)化容器操作的開(kāi)源平臺(tái),這些操作包括部署,調(diào)度和節(jié)點(diǎn)集群間擴(kuò)展。
二、為什么使用K8s?
使用Kubernetes可以:
1. 自動(dòng)化容器的部署和復(fù)制
2. 隨時(shí)擴(kuò)展或收縮容器規(guī)模
3. 將容器組織成組,并且提供容器間的負(fù)載均衡
4. 很容易地升級(jí)應(yīng)用程序容器的新版本
4. 提供容器彈性,如果容器失效就替換它,等等…
三、Kubernetes解決的問(wèn)題:
1. 調(diào)度 - 容器應(yīng)該在哪個(gè)機(jī)器上運(yùn)行
2. 生命周期和健康狀況 - 容器在無(wú)錯(cuò)的條件下運(yùn)行
3. 服務(wù)發(fā)現(xiàn) - 容器在哪,怎樣與它通信
4. 監(jiān)控 - 容器是否運(yùn)行正常
5. 認(rèn)證 - 誰(shuí)能訪(fǎng)問(wèn)容器
6. 容器聚合 - 如何將多個(gè)容器合并成一個(gè)工程
四、執(zhí)行流程是什么樣的?
通過(guò)Kubectl提交一個(gè)創(chuàng)建RC的請(qǐng)求,該請(qǐng)求通過(guò)APIServer被寫(xiě)入etcd中,此時(shí)Controller Manager通過(guò)
API Server的監(jiān)聽(tīng)資源變化的接口監(jiān)聽(tīng)到這個(gè)RC事件,分析之后,發(fā)現(xiàn)當(dāng)前集群中還沒(méi)有它所對(duì)應(yīng)的Pod實(shí)例,
于是根據(jù)RC里的Pod模板定義生成一個(gè)Pod對(duì)象,通過(guò)APIServer寫(xiě)入etcd,接下來(lái),此事件被Scheduler發(fā)現(xiàn),
它立即執(zhí)行一個(gè)復(fù)雜的調(diào)度流程,為這個(gè)新Pod選定一個(gè)落戶(hù)的Node,然后通過(guò)API Server講這一結(jié)果寫(xiě)入到etcd中,
隨后,目標(biāo)Node上運(yùn)行的Kubelet進(jìn)程通過(guò)APIServer監(jiān)測(cè)到這個(gè)“新生的”P(pán)od,并按照它的定義,啟動(dòng)該P(yáng)od并
任勞任怨地負(fù)責(zé)它的下半生,直到Pod的生命結(jié)束。隨后,我們通過(guò)Kubectl提交一個(gè)新的映射到該P(yáng)od的
Service的創(chuàng)建請(qǐng)求,ControllerManager會(huì)通過(guò)Label標(biāo)簽查詢(xún)到相關(guān)聯(lián)的Pod實(shí)例,然后生成Service的
Endpoints信息,并通過(guò)APIServer寫(xiě)入到etcd中,接下來(lái),所有Node上運(yùn)行的Proxy進(jìn)程通過(guò)APIServer查詢(xún)并監(jiān)聽(tīng)
Service對(duì)象與其對(duì)應(yīng)的Endpoints信息,建立一個(gè)軟件方式的負(fù)載均衡器來(lái)實(shí)現(xiàn)Service訪(fǎng)問(wèn)到后端Pod的流量轉(zhuǎn)發(fā)功能。
Master
Master節(jié)點(diǎn)上面主要由四個(gè)模塊組成:APIServer、scheduler、controller manager、etcd。
APIServer: APIServer負(fù)責(zé)對(duì)外提供RESTful的Kubernetes API服務(wù),
它是系統(tǒng)管理指令的統(tǒng)一入口,任何對(duì)資源進(jìn)行增刪改查的操作都要交給
APIServer處理后再提交給etcd。如架構(gòu)圖中所示,kubectl(Kubernetes提供的客戶(hù)端工具,
該工具內(nèi)部就是對(duì)Kubernetes API的調(diào)用)是直接和APIServer交互的。
schedule: scheduler的職責(zé)很明確,就是負(fù)責(zé)調(diào)度pod到合適的Node上。如果把scheduler看成一個(gè)黑匣子,
那么它的輸入是pod和由多個(gè)Node組成的列表,輸出是Pod和一個(gè)Node的綁定,即將這個(gè)pod部署到這個(gè)Node上。
Kubernetes目前提供了調(diào)度算法,但是同樣也保留了接口,用戶(hù)可以根據(jù)自己的需求定義自己的調(diào)度算法。
controller manager: 如果說(shuō)APIServer做的是“前臺(tái)”的工作的話(huà),那controller manager就是負(fù)責(zé)“后臺(tái)”的。
每個(gè)資源一般都對(duì)應(yīng)有一個(gè)控制器,而controller manager就是負(fù)責(zé)管理這些控制器的。比如我們通過(guò)APIServer
創(chuàng)建一個(gè)pod,當(dāng)這個(gè)pod創(chuàng)建成功后,APIServer的任務(wù)就算完成了。而后面保證Pod的狀態(tài)始終和我們預(yù)期的一樣
的重任就由controller manager去保證了。
etcd: etcd是一個(gè)高可用的鍵值存儲(chǔ)系統(tǒng),Kubernetes使用它來(lái)存儲(chǔ)各個(gè)資源的狀態(tài),從而實(shí)現(xiàn)了Restful的API。
Node
每個(gè)Node節(jié)點(diǎn)主要由三個(gè)模塊組成:kubelet、kube-proxy、runtime。
runtime。runtime指的是容器運(yùn)行環(huán)境,目前Kubernetes支持docker和rkt兩種容器。
kube-proxy。該模塊實(shí)現(xiàn)了Kubernetes中的服務(wù)發(fā)現(xiàn)和反向代理功能。反向代理方面:kube-proxy支持TCP和UDP連接轉(zhuǎn)發(fā),默認(rèn)基于Round
Robin算法將客戶(hù)端流量轉(zhuǎn)發(fā)到與service對(duì)應(yīng)的一組后端pod。服務(wù)發(fā)現(xiàn)方面,
kube-proxy使用etcd的watch機(jī)制,監(jiān)控集群中service和endpoint對(duì)象數(shù)據(jù)的動(dòng)態(tài)變化,并且維護(hù)一個(gè)service到endpoint的映射關(guān)系,
從而保證了后端pod的IP變化不會(huì)對(duì)訪(fǎng)問(wèn)者造成影響。另外kube-proxy還支持session affinity。
kubelet。Kubelet是Master在每個(gè)Node節(jié)點(diǎn)上面的agent,是Node節(jié)點(diǎn)上面最重要的模塊,它負(fù)責(zé)維護(hù)和管理該Node上面的所有容器,
但是如果容器不是通過(guò)Kubernetes創(chuàng)建的,它并不會(huì)管理。本質(zhì)上,它負(fù)責(zé)使Pod得運(yùn)行狀態(tài)與期望的狀態(tài)一致。
至此,Kubernetes的Master和Node就簡(jiǎn)單介紹完了。下面我們來(lái)看Kubernetes中的各種資源/對(duì)象。
五、上面說(shuō)的一堆名詞,那么他們到底是什么?
1、Pod是什么東西?
Pod是Kubernetes的最基本操作單元,包含一個(gè)或多個(gè)緊密相關(guān)的容器,類(lèi)似于豌豆莢的概念。一個(gè)Pod可以被一個(gè)容器化的環(huán)境看作應(yīng)用層的“邏輯宿主機(jī)”(Logical Host)。一個(gè)Pod中的多個(gè)容器應(yīng)用通常是緊耦合的。Pod在Node上被創(chuàng)建、啟動(dòng)或者銷(xiāo)毀。為什么Kubernetes使用Pod在容器之上再封裝一層呢?一個(gè)很重要的原因是,Docker容器之間的通信受到Docker網(wǎng)絡(luò)機(jī)制的限制。在Docker的世界中,一個(gè)容器需要link方式才能訪(fǎng)問(wèn)另一個(gè)容器提供的服務(wù)(端口)。大量容器之間的link將是一個(gè)非常繁重的工作。通過(guò)Pod的概念將多個(gè)容器組合在一個(gè)虛擬的“主機(jī)”內(nèi),可以實(shí)現(xiàn)容器之間僅需要通過(guò)Localhost就能相互通信了。
2、 一個(gè)Pod中的應(yīng)用容器共享同一組資源(Pod內(nèi)的容器是不會(huì)跨宿主機(jī)的),如下所述:
PID命名空間:Pod中的不同應(yīng)用程序可以看到其他應(yīng)用程序的進(jìn)程ID;
網(wǎng)絡(luò)命名空間:Pod中的多個(gè)容器能夠訪(fǎng)問(wèn)同一個(gè)IP和端口范圍;
IPC命名空間:Pod中的多個(gè)容器能夠使用SystemV IPC或者POSIX消息隊(duì)列進(jìn)行通信;
UTS命名空間:Pod中的多個(gè)容器共享一個(gè)主機(jī)名;
Volumes(共享存儲(chǔ)卷):Pod中的各個(gè)容器可以訪(fǎng)問(wèn)在Pod級(jí)別定義的Volumes。
3、 對(duì)Pod的定義
對(duì)Pod的定義通過(guò)Yaml或Json格式的配置文件來(lái)完成。下面的配置文件將定義一個(gè)名為redis-slave的Pod,其中kind為Pod。在spec中主要包含了Containers(容器)的定義,可以定義多個(gè)容器。
apiVersion: v1 #指定api版本,此值必須在kubectl apiversion中
kind: Pod #指定創(chuàng)建資源的角色/類(lèi)型,如Pod, Deployment, Service等等
metadata: #資源的元數(shù)據(jù)/屬性
name: redis-slave #資源的名字,在同一個(gè)namespace中必須唯一
labels: #設(shè)定資源的標(biāo)簽 詳情http://blog.csdn.net/liyingke112/article/details/77482384
name: redis-slave
annotations: #自定義注解列表
- name: String #自定義注解名字
spec: #指定該資源的內(nèi)容
restartPolicy: Always #表明該容器一直運(yùn)行,默認(rèn)k8s的策略,在此容器退出后,會(huì)立即創(chuàng)建一個(gè)相同的容器
nodeSelector: #節(jié)點(diǎn)選擇,先給主機(jī)打標(biāo)簽kubectl label nodes kube-node1 zone=node1
zone: node1
containers:
- name: slave #容器的名字
image: kubeguide/guestbook-redis-slave #容器使用的鏡像地址
imagePullPolicy: Never #三個(gè)選擇Always、Never、IfNotPresent,每次啟動(dòng)時(shí)檢查和更新(從registry)images的策略,
# Always,每次都檢查
# Never,每次都不檢查(不管本地是否有)
# IfNotPresent,如果本地有就不檢查,如果沒(méi)有就拉取
command: ['sh'] #啟動(dòng)容器的運(yùn)行命令,將覆蓋容器中的Entrypoint,對(duì)應(yīng)Dockefile中的ENTRYPOINT
args: ["$(str)"] #啟動(dòng)容器的命令參數(shù),對(duì)應(yīng)Dockerfile中CMD參數(shù)
env: #指定容器中的環(huán)境變量
- name: GET_HOSTS_FROM #變量的名字
value: env #變量的值
resources: #資源管理,請(qǐng)求請(qǐng)見(jiàn)http://blog.csdn.net/liyingke112/article/details/77452630
requests: #容器運(yùn)行時(shí),最低資源需求,也就是說(shuō)最少需要多少資源容器才能正常運(yùn)行
cpu: 0.1 #CPU資源(核數(shù)),兩種方式,浮點(diǎn)數(shù)或者是整數(shù)+m,0.1=100m,最少值為0.001核(1m)
memory: 32Mi #內(nèi)存使用量
limits: #資源限制
cpu: 0.5
memory: 32Mi
ports:
- containerPort: 6379 #容器開(kāi)放對(duì)外的端口
name: redis #名稱(chēng)
protocol: TCP
下面是一個(gè)httpd更詳細(xì)的例子
apiVersion: v1 #指定api版本,此值必須在kubectl apiversion中
kind: Pod #指定創(chuàng)建資源的角色/類(lèi)型
metadata: #資源的元數(shù)據(jù)/屬性
name: web04-pod #資源的名字,在同一個(gè)namespace中必須唯一
labels: #設(shè)定資源的標(biāo)簽,詳情請(qǐng)見(jiàn)http://blog.csdn.net/liyingke112/article/details/77482384
k8s-app: apache
version: v1
kubernetes.io/cluster-service: "true"
annotations: #自定義注解列表
- name: String #自定義注解名字
spec:#specification of the resource content 指定該資源的內(nèi)容
restartPolicy: Always #表明該容器一直運(yùn)行,默認(rèn)k8s的策略,在此容器退出后,會(huì)立即創(chuàng)建一個(gè)相同的容器
nodeSelector: #節(jié)點(diǎn)選擇,先給主機(jī)打標(biāo)簽kubectl label nodes kube-node1 zone=node1
zone: node1
containers:
- name: web04-pod #容器的名字
image: web:apache #容器使用的鏡像地址
imagePullPolicy: Never #三個(gè)選擇Always、Never、IfNotPresent,每次啟動(dòng)時(shí)檢查和更新(從registery)images的策略,
# Always,每次都檢查
# Never,每次都不檢查(不管本地是否有)
# IfNotPresent,如果本地有就不檢查,如果沒(méi)有就拉取
command: ['sh'] #啟動(dòng)容器的運(yùn)行命令,將覆蓋容器中的Entrypoint,對(duì)應(yīng)Dockefile中的ENTRYPOINT
args: ["$(str)"] #啟動(dòng)容器的命令參數(shù),對(duì)應(yīng)Dockerfile中CMD參數(shù)
env: #指定容器中的環(huán)境變量
- name: str #變量的名字
value: "/etc/run.sh" #變量的值
resources: #資源管理,請(qǐng)求請(qǐng)見(jiàn)http://blog.csdn.net/liyingke112/article/details/77452630
requests: #容器運(yùn)行時(shí),最低資源需求,也就是說(shuō)最少需要多少資源容器才能正常運(yùn)行
cpu: 0.1 #CPU資源(核數(shù)),兩種方式,浮點(diǎn)數(shù)或者是整數(shù)+m,0.1=100m,最少值為0.001核(1m)
memory: 32Mi #內(nèi)存使用量
limits: #資源限制
cpu: 0.5
memory: 32Mi
ports:
- containerPort: 80 #容器開(kāi)發(fā)對(duì)外的端口
name: httpd #名稱(chēng)
protocol: TCP
livenessProbe: #pod內(nèi)容器健康檢查的設(shè)置,詳情請(qǐng)見(jiàn)http://blog.csdn.net/liyingke112/article/details/77531584
httpGet: #通過(guò)httpget檢查健康,返回200-399之間,則認(rèn)為容器正常
path: / #URI地址
port: 80
#host: 127.0.0.1 #主機(jī)地址
scheme: HTTP
initialDelaySeconds: 180 #表明第一次檢測(cè)在容器啟動(dòng)后多長(zhǎng)時(shí)間后開(kāi)始
timeoutSeconds: 5 #檢測(cè)的超時(shí)時(shí)間
periodSeconds: 15 #檢查間隔時(shí)間
#也可以用這種方法
#exec: 執(zhí)行命令的方法進(jìn)行監(jiān)測(cè),如果其退出碼不為0,則認(rèn)為容器正常
# command:
# - cat
# - /tmp/health
#也可以用這種方法
#tcpSocket: //通過(guò)tcpSocket檢查健康
# port: number
lifecycle: #生命周期管理
postStart: #容器運(yùn)行之前運(yùn)行的任務(wù)
exec:
command:
- 'sh'
- 'yum upgrade -y'
preStop:#容器關(guān)閉之前運(yùn)行的任務(wù)
exec:
command: ['service httpd stop']
volumeMounts: #詳情請(qǐng)見(jiàn)http://blog.csdn.net/liyingke112/article/details/76577520
- name: volume #掛載設(shè)備的名字,與volumes[*].name 需要對(duì)應(yīng),如下面定義的
mountPath: /data #掛載到容器的某個(gè)路徑下
readOnly: True
volumes: #定義一組掛載設(shè)備
- name: volume #定義一個(gè)掛載設(shè)備的名字
#meptyDir: {}
hostPath:
path: /opt #掛載設(shè)備類(lèi)型為hostPath,路徑為宿主機(jī)下的/opt,這里設(shè)備類(lèi)型支持很多種
4、定義完成后可以使用
創(chuàng)建
kubectl create -f podfile
查看
kubectl get pod yourPodName
kubectl describe pod yourPodName
刪除
kubectl delete pod yourPodName
更新
kubectl replace /path/to/yourNewYaml.yaml
5、Pod的生命周期
- Replication Controller(RC)是Kubernetes中的另一個(gè)核心概念,應(yīng)用托管在Kubernetes之后,Kubernetes需要保證應(yīng)用能夠持續(xù)運(yùn)行,這是RC的工作內(nèi)容,它會(huì)確保任何時(shí)間Kubernetes中都有指定數(shù)量的Pod在運(yùn)行。在此基礎(chǔ)上,RC還提供了一些更高級(jí)的特性,比如滾動(dòng)升級(jí)、升級(jí)回滾等。
- Pod的生命周期過(guò)程包括:通過(guò)模板進(jìn)行定義,然后分配到一個(gè)Node上運(yùn)行,就不會(huì)離開(kāi)這個(gè)Node,直到被刪除。當(dāng)某個(gè)Pod失敗,首先會(huì)被Kubernetes清理掉,之后ReplicationController將會(huì)在其它機(jī)器上(或本機(jī))重建Pod,重建之后Pod的ID發(fā)生了變化,那將會(huì)是一個(gè)新的Pod。所以,Kubernetes中Pod的遷移,實(shí)際指的是在新Node上重建Pod。
在整個(gè)過(guò)程中,Pod處于一下4種狀態(tài)之一:
Pending:Pod定義正確,提交到Master,但其所包含的容器鏡像還未完成創(chuàng)建。通常Master對(duì)Pod進(jìn)行調(diào)度需要一些時(shí)間,之后Node對(duì)鏡像進(jìn)行下載也需要一些時(shí)間;
Running:Pod已被分配到某個(gè)Node上,且其包含的所有容器鏡像都已經(jīng)創(chuàng)建完成,并成功運(yùn)行起來(lái);
Succeeded:Pod中所有容器都成功結(jié)束,并且不會(huì)被重啟,這是Pod的一種最終狀態(tài);
Failed:Pod中所有容器都結(jié)束了,但至少一個(gè)容器是以失敗狀態(tài)結(jié)束的,這也是Pod的一種最終狀態(tài)。
- RC與Pod的關(guān)聯(lián)——Label
RC與Pod的關(guān)聯(lián)是通過(guò)Label來(lái)實(shí)現(xiàn)的。Label機(jī)制是Kubernetes中的一個(gè)重要設(shè)計(jì),通過(guò)Label進(jìn)行對(duì)象的弱關(guān)聯(lián),可以靈活地進(jìn)行分類(lèi)和選擇。對(duì)于Pod,需要設(shè)置其自身的Label來(lái)進(jìn)行標(biāo)識(shí),Label是一系列的Key/value對(duì),在Pod-->metadata-->labeks中進(jìn)行設(shè)置。
Label的定義是任一的,但是Label必須具有可標(biāo)識(shí)性,比如設(shè)置Pod的應(yīng)用名稱(chēng)和版本號(hào)等。另外Lable是不具有唯一性的,為了更準(zhǔn)確的標(biāo)識(shí)一個(gè)Pod,應(yīng)該為Pod設(shè)置多個(gè)維度的label。如下:
"release" : "stable", "release" : "canary"
"environment" : "dev", "environment" : "qa", "environment" : "production"
"tier" : "frontend", "tier" : "backend", "tier" : "cache"
"partition" : "customerA", "partition" : "customerB"
"track" : "daily", "track" : "weekly"
舉例,當(dāng)你在RC的yaml文件中定義了該RC的selector中的label為app:my-web,那么這個(gè)RC就會(huì)去關(guān)注Pod-->metadata-->labeks中l(wèi)abel為app:my-web的Pod。修改了對(duì)應(yīng)Pod的Label,就會(huì)使Pod脫離RC的控制。同樣,在RC運(yùn)行正常的時(shí)候,若試圖繼續(xù)創(chuàng)建同樣Label的Pod,是創(chuàng)建不出來(lái)的。因?yàn)镽C認(rèn)為副本數(shù)已經(jīng)正常了,再多起的話(huà)會(huì)被RC刪掉的。
- 彈性伸縮
彈性伸縮是指適應(yīng)負(fù)載變化,以彈性可伸縮的方式提供資源。反映到Kubernetes中,指的是可根據(jù)負(fù)載的高低動(dòng)態(tài)調(diào)整Pod的副本數(shù)量。調(diào)整Pod的副本數(shù)是通過(guò)修改RC中Pod的副本是來(lái)實(shí)現(xiàn)的,示例命令如下:
擴(kuò)容Pod的副本數(shù)目到10
$ kubectl scale relicationcontroller yourRcName --replicas=10
縮容Pod的副本數(shù)目到1
$ kubectl scale relicationcontroller yourRcName --replicas=1
- 滾動(dòng)升級(jí)
滾動(dòng)升級(jí)是一種平滑過(guò)渡的升級(jí)方式,通過(guò)逐步替換的策略,保證整體系統(tǒng)的穩(wěn)定,在初始升級(jí)的時(shí)候就可以及時(shí)發(fā)現(xiàn)、調(diào)整問(wèn)題,以保證問(wèn)題影響度不會(huì)擴(kuò)大。Kubernetes中滾動(dòng)升級(jí)的命令如下:
$ kubectl rolling-update my-rcName-v1 -f my-rcName-v2-rc.yaml --update-period=10s
升級(jí)開(kāi)始后,首先依據(jù)提供的定義文件創(chuàng)建V2版本的RC,然后每隔10s(--update-period=10s)逐步的增加V2版本的Pod副本數(shù),逐步減少V1版本Pod的副本數(shù)。升級(jí)完成之后,刪除V1版本的RC,保留V2版本的RC,及實(shí)現(xiàn)滾動(dòng)升級(jí)。
升級(jí)過(guò)程中,發(fā)生了錯(cuò)誤中途退出時(shí),可以選擇繼續(xù)升級(jí)。Kubernetes能夠智能的判斷升級(jí)中斷之前的狀態(tài),然后緊接著繼續(xù)執(zhí)行升級(jí)。當(dāng)然,也可以進(jìn)行回退,命令如下:
$ kubectl rolling-update my-rcName-v1 -f my-rcName-v2-rc.yaml --update-period=10s --rollback
回退的方式實(shí)際就是升級(jí)的逆操作,逐步增加V1.0版本Pod的副本數(shù),逐步減少V2版本Pod的副本數(shù)。
- 新一代副本控制器replica set
這里所說(shuō)的replica set,可以被認(rèn)為 是“升級(jí)版”的Replication Controller。也就是說(shuō)。replica set也是用于保證與label selector匹配的pod數(shù)量維持在期望狀態(tài)。區(qū)別在于,replica set引入了對(duì)基于子集的selector查詢(xún)條件,而Replication Controller僅支持基于值相等的selector條件查詢(xún)。這是目前從用戶(hù)角度肴,兩者唯一的顯著差異。 社區(qū)引入這一API的初衷是用于取代vl中的Replication Controller,也就是說(shuō).當(dāng)v1版本被廢棄時(shí),Replication Controller就完成了它的歷史使命,而由replica set來(lái)接管其工作。雖然replica set可以被單獨(dú)使用,但是目前它多被Deployment用于進(jìn)行pod的創(chuàng)建、更新與刪除。Deployment在滾動(dòng)更新等方面提供了很多非常有用的功能,關(guān)于DeplOymCn的更多信息,讀者們可以在后續(xù)小節(jié)中獲得。
- Kubernetes為Pod設(shè)計(jì)了一套獨(dú)特的網(wǎng)絡(luò)配置,包括:為每個(gè)Pod分配一個(gè)IP地址,使用Pod名作為容器間通信的主機(jī)名等。另外,不建議在Kubernetes的一個(gè)Pod內(nèi)運(yùn)行相同應(yīng)用的多個(gè)實(shí)例。
5.1、Pod之間的通信:同一個(gè)Node內(nèi)。
- 通過(guò)Veth連接在同一個(gè)docker0網(wǎng)橋上,它們的IP地址都是從docker0的網(wǎng)橋上動(dòng)態(tài)獲取的,它們和網(wǎng)橋本身的IP3是同一個(gè)網(wǎng)絡(luò)段的。
5.2、不同Node上的Pod之間的通信。
- 對(duì)docker0的IP地址做統(tǒng)一的規(guī)劃;對(duì)Pod的IP地址做統(tǒng)一的規(guī)劃;
5.3、Pod到Service之間的通信。
- Service的虛擬IP通過(guò)每個(gè)Node上的kube-proxy映射到不同的Pod上,暫時(shí)只支持輪詢(xún)。
5.4、外部到內(nèi)部的訪(fǎng)問(wèn)
- NodePort、LoadBalancer、Ingress三種方式。
5.5 、nodePort
- nodePort
外部機(jī)器可訪(fǎng)問(wèn)的端口。
比如一個(gè)Web應(yīng)用需要被其他用戶(hù)訪(fǎng)問(wèn),那么需要配置`type=NodePort`,而且配置`nodePort=30001`,那么其他機(jī)器就可以通過(guò)瀏覽器訪(fǎng)問(wèn)`scheme://node:30001`訪(fǎng)問(wèn)到該服務(wù),例如`http://node:30001`。
例如MySQL數(shù)據(jù)庫(kù)可能不需要被外界訪(fǎng)問(wèn),只需被內(nèi)部服務(wù)訪(fǎng)問(wèn),那么不必設(shè)置NodePort
- targetPort
容器的端口(最根本的端口入口),與制作容器時(shí)暴露的端口一致(DockerFile中EXPOSE),例如docker.io官方的nginx暴露的是80端口。
- port
kubernetes中的服務(wù)之間訪(fǎng)問(wèn)的端口,盡管mysql容器暴露了3306端口,但是集群內(nèi)其他容器需要通過(guò)33306端口訪(fǎng)問(wèn)該服務(wù),外部機(jī)器不能訪(fǎng)問(wèn)mysql服務(wù),因?yàn)樗麤](méi)有配置NodePort類(lèi)型
- 舉例
# web
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
ports:
- port: 30080
targetPort: 80
nodePort: 30001
selector:
name: nginx-pod
# mysql
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
ports:
- port: 33306
targetPort: 3306
selector:
name: mysql-pod
5.6. LoadBalancer
如果云服務(wù)商支持外接負(fù)載均衡器,則可以通過(guò)spec.type=LoadBalaner定義Service,同時(shí)需要制定負(fù)載均衡器的IP地址。使用這種類(lèi)型需要指定Service的nodePort和clusterIP。例如:
apiVersion: v1
kind: Service
metadata: {
"kind" "Service",
"apiVersion": "v1",
"metadata": {
"name": "my-service"
},
"spec": {
"type": "LoadBalaner",
"clusterIP": "10.0.171.239",
"selector": {
"app": "MyApp"
},
"ports": [
{
"protocol": "TCP",
"port": 80,
"targetPort": 9376,
"nodePort": 30061
}
],
},
"status": {
"loadBalancer": {
"ingress": [
{
"ip": "146.148.47.155"
}
]
}
}
}
在這個(gè)例子中,status.loadBalancer.ingress.ip設(shè)置的146.146.47.155為云服務(wù)商提供的負(fù)載均衡器的IP地址。
之后,對(duì)該Service的訪(fǎng)問(wèn)請(qǐng)求將會(huì)通過(guò)LoadBalancer轉(zhuǎn)發(fā)到后端Pod上去,負(fù)載分發(fā)的實(shí)現(xiàn)方式則依賴(lài)于云服務(wù)上提供的LoadBalancer的實(shí)現(xiàn)機(jī)制。
六、Node 概念
Node通常是物理機(jī)、虛擬機(jī)或者云服務(wù)商提供的資源,并不是由Kubernetes創(chuàng)建的。我們說(shuō)Kubernetes創(chuàng)建一個(gè)Node,僅僅表示Kubernetes在系統(tǒng)內(nèi)部創(chuàng)建了一個(gè)Node對(duì)象,創(chuàng)建后即會(huì)對(duì)其進(jìn)行一系列健康檢查,包括是否可以連通、服務(wù)是否正確啟動(dòng)、是否可以創(chuàng)建Pod等。如果檢查未能通過(guò),則該Node將會(huì)在集群中被標(biāo)記為不可用(Not Ready)。
七、Label 概念
Label機(jī)制是K8S中一個(gè)重要設(shè)計(jì),通過(guò)Label進(jìn)行對(duì)象弱關(guān)聯(lián),靈活地分類(lèi)和選擇不同服務(wù)或業(yè)務(wù),讓用戶(hù)根據(jù)自己特定的組織結(jié)構(gòu)以松耦合方式進(jìn)行服務(wù)部署。
Label是一對(duì)KV,對(duì)用戶(hù)而言非常有意義的,但對(duì)K8S本身而言沒(méi)有直接意義的。Label可以在創(chuàng)建對(duì)象時(shí)指定,也可以在后期修改,每個(gè)對(duì)象可以擁有多個(gè)標(biāo)簽,但key值必須是唯一的。
Label可隨意定義,但建議可讀性,比如設(shè)置Pod的應(yīng)用名稱(chēng)和版本號(hào)等。另外Lable是不具有唯一性的,為了更準(zhǔn)確標(biāo)識(shí)資源對(duì)象,應(yīng)為資源對(duì)象設(shè)置多維度的label。如下:
"release" : "stable", "release" : "canary"
"environment" : "dev", "environment" : "qa", "environment" : "production"
"tier" : "frontend", "tier" : "backend", "tier" : "cache"
"partition" : "customerA", "partition" : "customerB"
"track" : "daily", "track" : "weekly"
語(yǔ)法和字符集
Label keys的語(yǔ)法
一個(gè)可選前綴+名稱(chēng),通過(guò)/來(lái)區(qū)分
名稱(chēng)部分是必須的,并且最多63個(gè)字符,開(kāi)始和結(jié)束的字符必須是字母或者數(shù)字,中間是字母數(shù)字和_、-、.。
前綴可選,如指定必須是個(gè)DNS子域,一系列的DNS label通過(guò).來(lái)劃分,長(zhǎng)度不超過(guò)253個(gè)字符,“/”來(lái)結(jié)尾。如前綴被省略了,這個(gè)Label的key被假定為對(duì)用戶(hù)私有的。系統(tǒng)組成部分(比如scheduler,controller-manager,apiserver,kubectl),必須要指定一個(gè)前綴,Kuberentes.io前綴是為K8S內(nèi)核部分保留的。
label value語(yǔ)法
長(zhǎng)度不超過(guò)63個(gè)字符。
可以為空
首位字符必須為字母數(shù)字字符
中間必須是橫線(xiàn)、_、.、數(shù)字、字母。
Label選擇器
label選擇器(selector)是K8S中核心的組織原語(yǔ),通過(guò)label選擇器,客戶(hù)端能方便辨識(shí)和選擇一組資源對(duì)象。API目前支持兩種選擇器:基于相等的和基于集合的,在使用時(shí)可以將多個(gè)Label進(jìn)行組合來(lái)選擇。
使用基于相等的選擇器時(shí),選擇器的所有鍵值和其他資源對(duì)象的label鍵值完全相同(包括數(shù)量,key和value),才能匹配。
name = redis-slave: 選擇所有包含Label中key="name"且value="redis-slave"的對(duì)象;
env != production: 選擇所有包括Label中的key="env"且value不等于"production"的對(duì)象。
而使用基于集合的label選擇器,只要選擇器部分鍵值匹配其他資源對(duì)象的label,就算匹配。選擇器可以由一個(gè)以上條件(KV鍵值)組成,在多個(gè)條件的情況下,所有條件都必須滿(mǎn)足。
name in (redis-master, redis-slave): 選擇所有包含Label中的key="name"且value="redis-master"或"redis-slave"的對(duì)象;
name not in (php-frontend): 選擇所有包含Label中的key="name"且value不等于"php-frontend"的對(duì)象。
在某些對(duì)象需要對(duì)另一些對(duì)象進(jìn)行選擇時(shí),可以將多個(gè)Label Selector進(jìn)行組合,使用逗號(hào)","進(jìn)行分隔即可。基于等式的LabelSelector和基于集合的Label Selector可以任意組合。例如:
name=redis-slave,env!=production
name not in (php-frontend),env!=production
更新資源類(lèi)型的Label
Label作為用戶(hù)可靈活定義的對(duì)象屬性,在已創(chuàng)建的對(duì)象上,仍然可以隨時(shí)通過(guò)kubectl label命令對(duì)其進(jìn)行增加、修改、刪除等操作。 例如,我們要給已創(chuàng)建的Pod“redis-master-bobr0”添加一個(gè)標(biāo)簽role=backend:
kubectl label pod redis-master-bobr0 role=backend
kubectl get pods -L role
NAME READY STATUS RESTARTS AGE ROLE
redis-master-bobr0 1/1 Running 0 3m backend
刪除一個(gè)Label,只需在命令行最后指定Label的key名并與一個(gè)減號(hào)相連即可:
kubectl label pod redis-master-bobr0 role-
修改一個(gè)Label的值,需要加上--overwrite參數(shù):
kubectl label pod redis-master-bobr0 role=master –overwrite
八、Service(服務(wù))
在Kubernetes的世界里,雖然每個(gè)Pod都會(huì)被分配一個(gè)單獨(dú)的IP地址,這個(gè)IP地址會(huì)隨時(shí)Pod的銷(xiāo)毀而消失。這就引出一個(gè)問(wèn)題:如果有一組Pod組成一個(gè)集群來(lái)提供服務(wù),那么如何來(lái)訪(fǎng)問(wèn)它們呢?
Kubernetes的Service(服務(wù))就是用來(lái)解決這個(gè)問(wèn)題的核心概念。
一個(gè)Service可以看作一組提供相同服務(wù)的Pod的對(duì)外訪(fǎng)問(wèn)接口。Service作用于哪些Pod是通過(guò)Label Selector來(lái)定義的。
8.1. 對(duì)Service的定義
對(duì)Service的定義同樣使用Yaml或Json格式的配置文件來(lái)完成。以redis-slave服務(wù)的定義為例:
apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
ports:
- port: 6379
selector:
name: redis-slave
通過(guò)該定義,Kubernetes將會(huì)創(chuàng)建一個(gè)名為"redis-slave"的服務(wù),并在6379端口上監(jiān)聽(tīng)。spec.selector的定義表示該Service將包含所有具有"name=redis-slave"的Label的Pod。
在Pod正常啟動(dòng)后,系統(tǒng)將會(huì)根據(jù)Service的定義創(chuàng)建出與Pod對(duì)應(yīng)的Endpoint(端點(diǎn))對(duì)象,以建立起Service與后端Pod的對(duì)應(yīng)關(guān)系。隨著Pod的創(chuàng)建、銷(xiāo)毀,Endpoint對(duì)象也將被更新。Endpoint對(duì)象主要有Pod的IP地址和容器所需監(jiān)聽(tīng)的端口號(hào)組成。
8.2. Pod的IP地址和Service的Cluster IP地址
Pod的IP地址是Docker Daemon根據(jù)docker0網(wǎng)橋的IP地址段進(jìn)行分配的,但Service的Cluster IP地址是Kubernetes系統(tǒng)中的虛擬IP地址,由系統(tǒng)動(dòng)態(tài)分配。Service的Cluster IP地址相對(duì)于Pod的IP地址來(lái)說(shuō)相對(duì)穩(wěn)定,Service被創(chuàng)建時(shí)即被分配一個(gè)IP地址,在銷(xiāo)毀該Service之前,這個(gè)IP地址都不會(huì)再變化了。而Pod在Kubernetes集群中生命周期較短,可能被ReplicationContrller銷(xiāo)毀、再次創(chuàng)建,新創(chuàng)建的Pod將會(huì)分配一個(gè)新的IP地址。
8.3. 外部訪(fǎng)問(wèn)Service
由于Service對(duì)象在Cluster IP Range池中分配到的IP只能在內(nèi)部訪(fǎng)問(wèn),所以其他Pod都可以無(wú)障礙地訪(fǎng)問(wèn)到它。如果這個(gè)Service作為前端服務(wù),準(zhǔn)備為集群外的客戶(hù)端提供服務(wù),我們就需要給這個(gè)服務(wù)提供公共IP了。
Kubernetes支持對(duì)外提供服務(wù)的Service的type定義:NodePort和LoadBalancer(見(jiàn)上文)和另外的Ingress
- Ingress,是一種HTTP方式的路由轉(zhuǎn)發(fā)機(jī)制,由Ingress Controller和HTTP代理服務(wù)器組合而成。Ingress Controller實(shí)時(shí)監(jiān)控Kubernetes API,實(shí)時(shí)更新HTTP代理服務(wù)器的轉(zhuǎn)發(fā)規(guī)則。HTTP代理服務(wù)器有GCE Load-Balancer、HaProxy、Nginx等開(kāi)源方案。。
8.4 service 自發(fā)現(xiàn)機(jī)制
Kubernetes中有一個(gè)很重要的服務(wù)自發(fā)現(xiàn)特性。一旦一個(gè)service被創(chuàng)建,該service的service IP和service port等信息都可以被注入到pod中供它們使用。Kubernetes主要支持兩種service發(fā)現(xiàn) 機(jī)制:環(huán)境變量和DNS。
環(huán)境變量方式
Kubernetes創(chuàng)建Pod時(shí)會(huì)自動(dòng)添加所有可用的service環(huán)境變量到該P(yáng)od中,如有需要.這些環(huán)境變量就被注入Pod內(nèi)的容器里。需要注意的是,環(huán)境變量的注入只發(fā)送在Pod創(chuàng)建時(shí),且不會(huì)被自動(dòng)更新。這個(gè)特點(diǎn)暗含了service和訪(fǎng)問(wèn)該service的Pod的創(chuàng)建時(shí)間的先后順序,即任何想要訪(fǎng)問(wèn)service的pod都需要在service已經(jīng)存在后創(chuàng)建,否則與service相關(guān)的環(huán)境變量就無(wú)法注入該P(yáng)od的容器中,這樣先創(chuàng)建的容器就無(wú)法發(fā)現(xiàn)后創(chuàng)建的service。
DNS方式
Kubernetes集群現(xiàn)在支持增加一個(gè)可選的組件——DNS服務(wù)器。這個(gè)DNS服務(wù)器使用Kubernetes的watchAPI,不間斷的監(jiān)測(cè)新的service的創(chuàng)建并為每個(gè)service新建一個(gè)DNS記錄。如果DNS在整個(gè)集群范圍內(nèi)都可用,那么所有的Pod都能夠自動(dòng)解析service的域名。
8.5 多個(gè)service如何避免地址和端口沖突
此處設(shè)計(jì)思想是,Kubernetes通過(guò)為每個(gè)service分配一個(gè)唯一的ClusterIP,所以當(dāng)使用ClusterIP:port的組合訪(fǎng)問(wèn)一個(gè)service的時(shí)候,不管port是什么,這個(gè)組合是一定不會(huì)發(fā)生重復(fù)的。另一方面,kube-proxy為每個(gè)service真正打開(kāi)的是一個(gè)絕對(duì)不會(huì)重復(fù)的隨機(jī)端口,用戶(hù)在service描述文件中指定的訪(fǎng)問(wèn)端口會(huì)被映射到這個(gè)隨機(jī)端口上。這就是為什么用戶(hù)可以在創(chuàng)建service時(shí)隨意指定訪(fǎng)問(wèn)端口。
8.6 service目前存在的不足
Kubernetes使用iptables和kube-proxy解析service的人口地址,在中小規(guī)模的集群中運(yùn)行良好,但是當(dāng)service的數(shù)量超過(guò)一定規(guī)模時(shí),仍然有一些小問(wèn)題。首當(dāng)其沖的便是service環(huán)境變量泛濫,以及service與使用service的pod兩者創(chuàng)建時(shí)間先后的制約關(guān)系。目前來(lái)看,很多使用者在使用Kubernetes時(shí)往往會(huì)開(kāi)發(fā)一套自己的Router組件來(lái)替代service,以便更好地掌控和定制這部分功能。
九、Deployment
Kubernetes提供了一種更加簡(jiǎn)單的更新RC和Pod的機(jī)制,叫做Deployment。通過(guò)在Deployment中描述你所期望的集群狀態(tài),Deployment Controller會(huì)將現(xiàn)在的集群狀態(tài)在一個(gè)可控的速度下逐步更新成你所期望的集群狀態(tài)。Deployment主要職責(zé)同樣是為了保證pod的數(shù)量和健康,90%的功能與Replication Controller完全一樣,可以看做新一代的Replication Controller。但是,它又具備了Replication Controller之外的新特性:
- 有Replication Controller全部功能:Deployment繼承了上面描述的Replication Controller全部功能。
- 事件和狀態(tài)查看:可以查看Deployment的升級(jí)詳細(xì)進(jìn)度和狀態(tài)。
- 回滾:當(dāng)升級(jí)pod鏡像或者相關(guān)參數(shù)的時(shí)候發(fā)現(xiàn)問(wèn)題,可以使用回滾操作回滾到上一個(gè)穩(wěn)定的版本或者指定的版本。
- 版本記錄: 每一次對(duì)Deployment的操作,都能保存下來(lái),給予后續(xù)可能的回滾使用。
- 暫停和啟動(dòng):對(duì)于每一次升級(jí),都能夠隨時(shí)暫停和啟動(dòng)。
- 多種升級(jí)方案:Recreate----刪除所有已存在的pod,重新創(chuàng)建新的; RollingUpdate----滾動(dòng)升級(jí),逐步替換的策略,同時(shí)滾動(dòng)升級(jí)時(shí),支持更多的附加參數(shù),例如設(shè)置最大不可用pod數(shù)量,最小升級(jí)間隔時(shí)間等等。
9.1 滾動(dòng)升級(jí)Deployment
相比于RC,Deployment直接使用kubectl edit deployment/deploymentName 或者kubectl set方法就可以直接升級(jí)(原理是Pod的template發(fā)生變化,例如更新label、更新鏡像版本等操作會(huì)觸發(fā)Deployment的滾動(dòng)升級(jí))。操作示例——首先 我們同樣定義一個(gè)nginx-deploy-v1.yaml的文件,副本數(shù)量為2:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
創(chuàng)建deployment:
$ kubectl create -f nginx-deploy-v1.yaml --record
deployment "nginx-deployment" created
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 0 0 0 1s
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 3 3 3 18s
正常之后,將nginx的版本進(jìn)行升級(jí),從1.7升級(jí)到1.9。第一種方法,直接set鏡像:
$ kubectl set image deployment/nginx-deployment2 nginx=nginx:1.9
deployment "nginx-deployment2" image updated
第二種方法,直接edit:
$ kubectl edit deployment/nginx-deployment
deployment "nginx-deployment2" edited
查看Deployment的變更信息(以下信息得以保存,是創(chuàng)建時(shí)候加的“--record”這個(gè)選項(xiàng)起的作用):
$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment":
REVISION CHANGE-CAUSE
1 kubectl create -f docs/user-guide/nginx-deployment.yaml --record
2 kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.91
$ kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" revision 2
Labels: app=nginx
pod-template-hash=1159050644
Annotations: kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
Containers:
nginx:
Image: nginx:1.9.1
Port: 80/TCP
QoS Tier:
cpu: BestEffort
memory: BestEffort
Environment Variables: <none>
No volumes.
最后介紹下Deployment的一些基礎(chǔ)命令。
$ kubectl describe deployments #查詢(xún)?cè)敿?xì)信息,獲取升級(jí)進(jìn)度
$ kubectl rollout pause deployment/nginx-deployment2 #暫停升級(jí)
$ kubectl rollout resume deployment/nginx-deployment2 #繼續(xù)升級(jí)
$ kubectl rollout undo deployment/nginx-deployment2 #升級(jí)回滾
$ kubectl scale deployment nginx-deployment --replicas 10 #彈性伸縮Pod數(shù)量
關(guān)于多重升級(jí),舉例,當(dāng)你創(chuàng)建了一個(gè)nginx1.7的Deployment,要求副本數(shù)量為5之后,Deployment Controller會(huì)逐步的將5個(gè)1.7的Pod啟動(dòng)起來(lái);當(dāng)啟動(dòng)到3個(gè)的時(shí)候,你又發(fā)出更新Deployment中Nginx到1.9的命令;這時(shí)Deployment Controller會(huì)立即將已啟動(dòng)的3個(gè)1.7Pod殺掉,然后逐步啟動(dòng)1.9的Pod。Deployment Controller不會(huì)等到1.7的Pod都啟動(dòng)完成之后,再依次殺掉1.7,啟動(dòng)1.9。
十、Volume
在Docker的設(shè)計(jì)實(shí)現(xiàn)中,容器中的數(shù)據(jù)是臨時(shí)的,即當(dāng)容器被銷(xiāo)毀時(shí),其中的數(shù)據(jù)將會(huì)丟失。如果需要持久化數(shù)據(jù),需要使用Docker數(shù)據(jù)卷掛載宿主機(jī)上的文件或者目錄到容器中。在Kubernetes中,當(dāng)Pod重建的時(shí)候,數(shù)據(jù)是會(huì)丟失的,Kubernetes也是通過(guò)數(shù)據(jù)卷掛載來(lái)提供Pod數(shù)據(jù)的持久化的。Kubernetes數(shù)據(jù)卷是對(duì)Docker數(shù)據(jù)卷的擴(kuò)展,Kubernetes數(shù)據(jù)卷是Pod級(jí)別的,可以用來(lái)實(shí)現(xiàn)Pod中容器的文件共享。目前,Kubernetes支持的數(shù)據(jù)卷類(lèi)型如下:
1) EmptyDir
2) HostPath
3) GCE Persistent Disk
4) AWS Elastic Block Store
5) NFS
6) iSCSI
7) Flocker
8) GlusterFS
9) RBD
10) Git Repo
11) Secret
12) Persistent Volume Claim
13) Downward API
10.1 本地?cái)?shù)據(jù)卷
EmptyDir、HostPath這兩種類(lèi)型的數(shù)據(jù)卷,只能最用于本地文件系統(tǒng)。本地?cái)?shù)據(jù)卷中的數(shù)據(jù)只會(huì)存在于一臺(tái)機(jī)器上,所以當(dāng)Pod發(fā)生遷移的時(shí)候,數(shù)據(jù)便會(huì)丟失。該類(lèi)型Volume的用途是:Pod中容器間的文件共享、共享宿主機(jī)的文件系統(tǒng)。
10.1.1 EmptyDir
如果Pod配置了EmptyDir數(shù)據(jù)卷,在Pod的生命周期內(nèi)都會(huì)存在,當(dāng)Pod被分配到 Node上的時(shí)候,會(huì)在Node上創(chuàng)建EmptyDir數(shù)據(jù)卷,并掛載到Pod的容器中。只要Pod 存在,EmptyDir數(shù)據(jù)卷都會(huì)存在(容器刪除不會(huì)導(dǎo)致EmptyDir數(shù)據(jù)卷丟失數(shù)據(jù)),但是如果Pod的生命周期終結(jié)(Pod被刪除),EmptyDir數(shù)據(jù)卷也會(huì)被刪除,并且永久丟失。
EmptyDir數(shù)據(jù)卷非常適合實(shí)現(xiàn)Pod中容器的文件共享。Pod的設(shè)計(jì)提供了一個(gè)很好的容器組合的模型,容器之間各司其職,通過(guò)共享文件目錄來(lái)完成交互,比如可以通過(guò)一個(gè)專(zhuān)職日志收集容器,在每個(gè)Pod中和業(yè)務(wù)容器中進(jìn)行組合,來(lái)完成日志的收集和匯總。
10.1.2 HostPath
HostPath數(shù)據(jù)卷允許將容器宿主機(jī)上的文件系統(tǒng)掛載到Pod中。如果Pod需要使用宿主機(jī)上的某些文件,可以使用HostPath。
10.2 網(wǎng)絡(luò)數(shù)據(jù)卷
Kubernetes提供了很多類(lèi)型的數(shù)據(jù)卷以集成第三方的存儲(chǔ)系統(tǒng),包括一些非常流行的分布式文件系統(tǒng),也有在IaaS平臺(tái)上提供的存儲(chǔ)支持,這些存儲(chǔ)系統(tǒng)都是分布式的,通過(guò)網(wǎng)絡(luò)共享文件系統(tǒng),因此我們稱(chēng)這一類(lèi)數(shù)據(jù)卷為網(wǎng)絡(luò)數(shù)據(jù)卷。
網(wǎng)絡(luò)數(shù)據(jù)卷能夠滿(mǎn)足數(shù)據(jù)的持久化需求,Pod通過(guò)配置使用網(wǎng)絡(luò)數(shù)據(jù)卷,每次Pod創(chuàng)建的時(shí)候都會(huì)將存儲(chǔ)系統(tǒng)的遠(yuǎn)端文件目錄掛載到容器中,數(shù)據(jù)卷中的數(shù)據(jù)將被水久保存,即使Pod被刪除,只是除去掛載數(shù)據(jù)卷,數(shù)據(jù)卷中的數(shù)據(jù)仍然保存在存儲(chǔ)系統(tǒng)中,且當(dāng)新的Pod被創(chuàng)建的時(shí)候,仍是掛載同樣的數(shù)據(jù)卷。網(wǎng)絡(luò)數(shù)據(jù)卷包含以下幾種:NFS、iSCISI、GlusterFS、RBD(Ceph Block Device)、Flocker、AWS Elastic Block Store、GCE Persistent Disk 7.3 Persistent Volume和Persistent Volume Claim
理解每個(gè)存儲(chǔ)系統(tǒng)是一件復(fù)雜的事情,特別是對(duì)于普通用戶(hù)來(lái)說(shuō),有時(shí)候并不需要關(guān)心各種存儲(chǔ)實(shí)現(xiàn),只希望能夠安全可靠地存儲(chǔ)數(shù)據(jù)。Kubernetes中提供了Persistent Volume和Persistent Volume Claim機(jī)制,這是存儲(chǔ)消費(fèi)模式。Persistent Volume是由系統(tǒng)管理員配置創(chuàng)建的一個(gè)數(shù)據(jù)卷(目前支持HostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、iSCSI、GlusterFS、RBD),它代表了某一類(lèi)存儲(chǔ)插件實(shí)現(xiàn);而對(duì)于普通用戶(hù)來(lái)說(shuō),通過(guò)Persistent Volume Claim可請(qǐng)求并獲得合適的Persistent Volume,而無(wú)須感知后端的存儲(chǔ)實(shí)現(xiàn)。Persistent Volume和Persistent Volume Claim的關(guān)系其實(shí)類(lèi)似于Pod和Node,Pod消費(fèi)Node資源,Persistent Volume Claim則消費(fèi)Persistent Volume資源。Persistent Volume和Persistent Volume Claim相互關(guān)聯(lián),有著完整的生命周期管理:
1) 準(zhǔn)備:系統(tǒng)管理員規(guī)劃或創(chuàng)建一批Persistent Volume;
2) 綁定:用戶(hù)通過(guò)創(chuàng)建Persistent Volume Claim來(lái)聲明存儲(chǔ)請(qǐng)求,Kubernetes發(fā)現(xiàn)有存儲(chǔ)請(qǐng)求的時(shí)候,就去查找符合條件的Persistent Volume(最小滿(mǎn)足策略)。找到合適的就綁定上,找不到就一直處于等待狀態(tài);
3) 使用:創(chuàng)建Pod的時(shí)候使用Persistent Volume Claim;
4) 釋放:當(dāng)用戶(hù)刪除綁定在Persistent Volume上的Persistent Volume Claim時(shí),Persistent Volume進(jìn)入釋放狀態(tài),此時(shí)Persistent Volume中還殘留著上一個(gè)Persistent Volume Claim的數(shù)據(jù),狀態(tài)還不可用;
5) 回收:是否的Persistent Volume需要回收才能再次使用?;厥詹呗钥梢允侨斯さ囊部梢允荎ubernetes自動(dòng)進(jìn)行清理(僅支持NFS和HostPath)
7.4信息數(shù)據(jù)卷
Kubernetes中有一些數(shù)據(jù)卷,主要用來(lái)給容器傳遞配置信息,我們稱(chēng)之為信息數(shù)據(jù)卷,比如Secret(處理敏感配置信息,密碼、Token等)、Downward API(通過(guò)環(huán)境變量的方式告訴容器Pod的信息)、Git Repo(將Git倉(cāng)庫(kù)下載到Pod中),都是將Pod的信息以文件形式保存,然后以數(shù)據(jù)卷方式掛載到容器中,容器通過(guò)讀取文件獲取相應(yīng)的信息。
十一、Pet Sets/StatefulSet
K8s在1.3版本里發(fā)布了Alpha版的PetSet功能。在云原生應(yīng)用的體系里,有下面兩組近義詞;第一組是無(wú)狀態(tài)(stateless)、牲畜(cattle)、無(wú)名(nameless)、可丟棄(disposable);第二組是有狀態(tài)(stateful)、寵物(pet)、有名(having name)、不可丟棄(non-disposable)。RC和RS主要是控制提供無(wú)狀態(tài)服務(wù)的,其所控制的Pod的名字是隨機(jī)設(shè)置的,一個(gè)Pod出故障了就被丟棄掉,在另一個(gè)地方重啟一個(gè)新的Pod,名字變了、名字和啟動(dòng)在哪兒都不重要,重要的只是Pod總數(shù);而PetSet是用來(lái)控制有狀態(tài)服務(wù),PetSet中的每個(gè)Pod的名字都是事先確定的,不能更改。PetSet中Pod的名字的作用,是用來(lái)關(guān)聯(lián)與該P(yáng)od對(duì)應(yīng)的狀態(tài)。
對(duì)于RC和RS中的Pod,一般不掛載存儲(chǔ)或者掛載共享存儲(chǔ),保存的是所有Pod共享的狀態(tài),Pod像牲畜一樣沒(méi)有分別;對(duì)于PetSet中的Pod,每個(gè)Pod掛載自己獨(dú)立的存儲(chǔ),如果一個(gè)Pod出現(xiàn)故障,從其他節(jié)點(diǎn)啟動(dòng)一個(gè)同樣名字的Pod,要掛在上原來(lái)Pod的存儲(chǔ)繼續(xù)以它的狀態(tài)提供服務(wù)。
適合于PetSet的業(yè)務(wù)包括數(shù)據(jù)庫(kù)服務(wù)MySQL和PostgreSQL,集群化管理服務(wù)Zookeeper、etcd等有狀態(tài)服務(wù)。PetSet的另一種典型應(yīng)用場(chǎng)景是作為一種比普通容器更穩(wěn)定可靠的模擬虛擬機(jī)的機(jī)制。傳統(tǒng)的虛擬機(jī)正是一種有狀態(tài)的寵物,運(yùn)維人員需要不斷地維護(hù)它,容器剛開(kāi)始流行時(shí),我們用容器來(lái)模擬虛擬機(jī)使用,所有狀態(tài)都保存在容器里,而這已被證明是非常不安全、不可靠的。使用PetSet,Pod仍然可以通過(guò)漂移到不同節(jié)點(diǎn)提供高可用,而存儲(chǔ)也可以通過(guò)外掛的存儲(chǔ)來(lái)提供高可靠性,PetSet做的只是將確定的Pod與確定的存儲(chǔ)關(guān)聯(lián)起來(lái)保證狀態(tài)的連續(xù)性。
十二、ConfigMap
很多生產(chǎn)環(huán)境中的應(yīng)用程序配置較為復(fù)雜,可能需要多個(gè)config文件、命令行參數(shù)和環(huán)境變量的組合。并且,這些配置信息應(yīng)該從應(yīng)用程序鏡像中解耦出來(lái),以保證鏡像的可移植性以及配置信息不被泄露。社區(qū)引入ConfigMap這個(gè)API資源提供了將配置數(shù)據(jù)注入容器的方式來(lái)滿(mǎn)足這一需求。
ConfigMap包含了一系列的鍵值對(duì),用于存儲(chǔ)被Pod或者系統(tǒng)組件(如controller)訪(fǎng)問(wèn)的信息。這與secret的設(shè)計(jì)理念有異曲同工之妙,它們的主要區(qū)別在于ConfigMap通常不用于存儲(chǔ)敏感信息,而只存儲(chǔ)簡(jiǎn)單的文本信息。
十三、Horizontal Pod Autoscaler
自動(dòng)擴(kuò)展作為一個(gè)長(zhǎng)久的議題,一直為人們津津樂(lè)道。系統(tǒng)能夠根據(jù)負(fù)載的變化對(duì)計(jì)算資源的分配進(jìn)行自動(dòng)的擴(kuò)增或者收縮,無(wú)疑是一個(gè)非常吸引人的特征,它能夠最大可能地減少費(fèi)用或者其他代價(jià)(如電力損耗)。自動(dòng)擴(kuò)展主要分為兩種,其一為水平擴(kuò)展,針對(duì)于實(shí)例數(shù)目的增減;其二為垂直擴(kuò)展,即單個(gè)實(shí)例可以使用的資源的增減。Horizontal Pod Autoscaler(HPA)屬于前者。
13.1 Horizontal Pod Autoscaler如何工作
- Horizontal Pod Autoscaler的操作對(duì)象是Replication Controller、ReplicaSet或Deployment對(duì)應(yīng)的Pod,根據(jù)觀(guān)察到的CPU實(shí)際使用量與用戶(hù)的期望值進(jìn)行比對(duì),做出是否需要增減實(shí)例數(shù)量的決策。controller目前使用heapSter來(lái)檢測(cè)CPU使用量,檢測(cè)周期默認(rèn)是30秒。
13.2 Horizontal Pod Autoscaler的決策策略
- 在HPA Controller檢測(cè)到CPU的實(shí)際使用量之后,會(huì)求出當(dāng)前的CPU使用率(實(shí)際使用量與pod 請(qǐng)求量的比率)。然后,HPA Controller會(huì)通過(guò)調(diào)整副本數(shù)量使得CPU使用率盡量向期望值靠近.另外,考慮到自動(dòng)擴(kuò)展的決策可能需要一段時(shí)間才會(huì)生效,甚至在短時(shí)間內(nèi)會(huì)引入一些噪聲. 例如當(dāng)pod所需要的CPU負(fù)荷過(guò)大,從而運(yùn)行一個(gè)新的pod進(jìn)行分流,在創(chuàng)建的過(guò)程中,系統(tǒng)的CPU使用量可能會(huì)有一個(gè)攀升的過(guò)程。所以,在每一次作出決策后的一段時(shí)間內(nèi),將不再進(jìn)行擴(kuò)展決策。對(duì)于ScaleUp而言,這個(gè)時(shí)間段為3分鐘,Scaledown為5分鐘。再者HPA Controller允許一定范圍內(nèi)的CPU使用量的不穩(wěn)定,也就是說(shuō),只有當(dāng)aVg(CurrentPodConsumption/Target低于0.9或者高于1.1時(shí)才進(jìn)行實(shí)例調(diào)整,這也是出于維護(hù)系統(tǒng)穩(wěn)定性的考慮。
十四、 Namespace(命名空間)
Namespace(命名空間)是Kubernetes系統(tǒng)中的另一個(gè)非常重要的概念,通過(guò)將系統(tǒng)內(nèi)部的對(duì)象“分配”到不同的Namespace中,形成邏輯上分組的不同項(xiàng)目、小組或用戶(hù)組,便于不同的分組在共享使用整個(gè)集群的資源的同時(shí)還能被分別管理。
Kubernetes集群在啟動(dòng)后,會(huì)創(chuàng)建一個(gè)名為“default”的Namespace,通過(guò)Kubectl可以查看到。
使用Namespace來(lái)組織Kubernetes的各種對(duì)象,可以實(shí)現(xiàn)對(duì)用戶(hù)的分組,即“多租戶(hù)”管理。對(duì)不同的租戶(hù)還可以進(jìn)行單獨(dú)的資源配額設(shè)置和管理,使得整個(gè)集群的資源配置非常靈活、方便。
十五、Annotation(注解)
Annotation與Label類(lèi)似,也使用key/value鍵值對(duì)的形式進(jìn)行定義。Label具有嚴(yán)格的命名規(guī)則,它定義的是Kubernetes對(duì)象的元數(shù)據(jù)(Metadata),并且用于Label Selector。Annotation則是用戶(hù)任意定義的“附加”信息,以便于外部工具進(jìn)行查找。
用Annotation來(lái)記錄的信息包括:
- build信息、release信息、Docker鏡像信息等,例如時(shí)間戳、release id號(hào)、PR號(hào)、鏡像hash值、docker registry地址等;
- 日志庫(kù)、監(jiān)控庫(kù)、分析庫(kù)等資源庫(kù)的地址信息;
- 程序調(diào)試工具信息,例如工具名稱(chēng)、版本號(hào)等;
- 團(tuán)隊(duì)的聯(lián)系信息,例如電話(huà)號(hào)碼、負(fù)責(zé)人名稱(chēng)、網(wǎng)址等。