1.1 Kubernetes是什么
- 首先,它是一個全新的基于容器技術(shù)的分布式架構(gòu)領(lǐng)先方案;
- 其次,Kubernetes是一個開放的開發(fā)平臺;
- 最后,Kubernetes是一個完備的分布式系統(tǒng)支撐平臺。
1.2 為什么要用Kubernetes
使用Kubernetes的理由很多,最根本的一個理由就是:IT從來都是一個由新技術(shù)驅(qū)動的行業(yè)。
使用Kubernetes所帶來的好處:
- 首先,最直接的感受就是我們可以“輕裝上陣”地開發(fā)復(fù)雜系統(tǒng)了;
- 其次,使用Kubernetes就是在全面擁抱微服務(wù)架構(gòu);
- 然后,我們的系統(tǒng)可以隨時隨地整體“搬遷”到公有云上;
- 最后,Kubernetes系統(tǒng)架構(gòu)具備了超強(qiáng)的橫向擴(kuò)容能力。
1.3 Kubernetes基本概念和術(shù)語
在Kubernetes中,Node、Pod、Replication Controller、Service等概念都可以看作一種資源對象,通過Kubernetes提供的Kubectl工具或者API調(diào)用進(jìn)行操作,并保存在etcd中。
1.3.1 Node(節(jié)點(diǎn))
Node(節(jié)點(diǎn))是Kubernetes集群中相對于Master而言的工作主機(jī),在較早的版本中也被稱為Minion。Node可以是一臺物理主機(jī),也可以是一臺虛擬機(jī)(VM)。在每個Node上運(yùn)行用于啟動和管理Pid的服務(wù)Kubelet,并能夠被Master管理。在Node上運(yùn)行的服務(wù)進(jìn)行包括Kubelet、kube-proxy和docker daemon。
Node信息如下:
- Node地址:主機(jī)的IP地址,或者Node ID。
- Node運(yùn)行狀態(tài):包括Pending、Running、Terminated三種狀態(tài)。
- Node Condition(條件):描述Running狀態(tài)Node的運(yùn)行條件,目前只有一種條件----Ready。Ready表示Node處于健康狀態(tài),可以接收從Master發(fā)來的創(chuàng)建Pod的指令。
- Node系統(tǒng)容量:描述Node可用的系統(tǒng)資源,包括CPU、內(nèi)存數(shù)量、最大可調(diào)度Pod數(shù)量等。
- 其他:Node的其他信息,包括實(shí)例的內(nèi)核版本號、Kubernetes版本號、Docker版本號、操作系統(tǒng)名稱等。
1. Node的管理
Node通常是物理機(jī)、虛擬機(jī)或者云服務(wù)商提供的資源,并不是由Kubernetes創(chuàng)建的。我們說Kubernetes創(chuàng)建一個Node,僅僅表示Kubernetes在系統(tǒng)內(nèi)部創(chuàng)建了一個Node對象,創(chuàng)建后即會對其進(jìn)行一系列健康檢查,包括是否可以連通、服務(wù)是否正確啟動、是否可以創(chuàng)建Pod等。如果檢查未能通過,則該Node將會在集群中被標(biāo)記為不可用(Not Ready)。
2. 使用Node Controller對Node進(jìn)行管理
Node Controller是Kubernetes Master中的一個組件,用于管理Node對象。它的兩個主要功能包括:集群范圍內(nèi)的Node信息同步,以及單個Node的生命周期管理。
Node信息同步可以通過kube-controller-manager的啟動參數(shù)--node-sync-period設(shè)置同步的時間周期。
3. Node的自注冊
當(dāng)Kubelet的--register-node參數(shù)被設(shè)置為true(默認(rèn)值即為true)時,Kubelet會向apiserver注冊自己。這也是Kubernetes推薦的Node管理方式。
Kubelet進(jìn)行自注冊的啟動參數(shù)如下:
- --apiservers=: apiserver地址;
- --kubeconfig=: 登錄apiserver所需憑據(jù)/證書的目錄;
- --cloud_provider=: 云服務(wù)商地址,用于獲取自身的metadata;
- --register-node=: 設(shè)置為true表示自動注冊到apiserver。
4. 手動管理Node
Kubernetes集群管理員也可以手工創(chuàng)建和修改Node對象。當(dāng)需要這樣操作時,先要將Kubelet啟動參數(shù)中的--register-node參數(shù)的值設(shè)置為false。這樣,在Node上的Kubelet就不會把自己注冊到apiserver中去了。
另外,Kubernetes提供了一種運(yùn)行時加入或者隔離某些Node的方法。具體操作請參考第四章。
1.3.2 Pod
Pod是Kubernetes的最基本操作單元,包含一個活多個緊密相關(guān)的容器,類似于豌豆莢的概念。一個Pod可以被一個容器化的環(huán)境看作應(yīng)用層的“邏輯宿主機(jī)”(Logical Host)。一個Pod中的多個容器應(yīng)用通常是緊耦合的。Pod在Node上被創(chuàng)建、啟動或者銷毀。
為什么Kubernetes使用Pod在容器之上再封裝一層呢?一個很重要的原因是,Docker容器之間的通信受到Docker網(wǎng)絡(luò)機(jī)制的限制。在Docker的世界中,一個容器需要link方式才能訪問另一個容器提供的服務(wù)(端口)。大量容器之間的link將是一個非常繁重的工作。通過Pod的概念將多個容器組合在一個虛擬的“主機(jī)”內(nèi),可以實(shí)現(xiàn)容器之間僅需要通過Localhost就能相互通信了。
一個Pod中的應(yīng)用容器共享同一組資源,如下所述:
- PID命名空間:Pod中的不同應(yīng)用程序可以看到其他應(yīng)用程序的進(jìn)程ID;
- 網(wǎng)絡(luò)命名空間:Pod中的多個容器能夠訪問同一個IP和端口范圍;
- IPC命名空間:Pod中的多個容器能夠使用SystemV IPC或者POSIX消息隊(duì)列進(jìn)行通信;
- UTS命名空間:Pod中的多個容器共享一個主機(jī)名;
- Volumes(共享存儲卷):Pod中的各個容器可以訪問在Pod級別定義的Volumes。
1. 對Pod的定義
對Pod的定義通過Yaml或Json格式的配置文件來完成。下面的配置文件將定義一個名為redis-slave的Pod,其中kind為Pod。在spec中主要包含了Containers(容器)的定義,可以定義多個容器。
apiVersion: v1
kind: Pod
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
containers:
- name: slave
image: kubeguide/guestbook-redis-slave
env:
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 6379
Pod的生命周期是通過Replication Controller來管理的。Pod的生命周期過程包括:通過模板進(jìn)行定義,然后分配到一個Node上運(yùn)行,在Pod所含容器運(yùn)行結(jié)束后Pod也結(jié)束。在整個過程中,Pod處于一下4種狀態(tài)之一:
- Pending:Pod定義正確,提交到Master,但其所包含的容器鏡像還未完成創(chuàng)建。通常Master對Pod進(jìn)行調(diào)度需要一些時間,之后Node對鏡像進(jìn)行下載也需要一些時間;
- Running:Pod已被分配到某個Node上,且其包含的所有容器鏡像都已經(jīng)創(chuàng)建完成,并成功運(yùn)行起來;
- Succeeded:Pod中所有容器都成功結(jié)束,并且不會被重啟,這是Pod的一種最終狀態(tài);
- Failed:Pod中所有容器都結(jié)束了,但至少一個容器是以失敗狀態(tài)結(jié)束的,這也是Pod的一種最終狀態(tài)。
Kubernetes為Pod設(shè)計(jì)了一套獨(dú)特的網(wǎng)絡(luò)配置,包括:為每個Pod分配一個IP地址,使用Pod名作為容器間通信的主機(jī)名等。關(guān)于Kubernetes網(wǎng)絡(luò)的設(shè)計(jì)原理將在第2章進(jìn)行詳細(xì)說明。
另外,不建議在Kubernetes的一個Pod內(nèi)運(yùn)行相同應(yīng)用的多個實(shí)例。
1.3.3 Label(標(biāo)簽)
Label是Kubernetes系統(tǒng)中的一個核心概念。Label以key/value鍵值對的形式附加到各種對象上,如Pod、Service、RC、Node等。Label定義了這些對象的可識別屬性,用來對它們進(jìn)行管理和選擇。Label可以在創(chuàng)建時附加到對象上,也可以在對象創(chuàng)建后通過API進(jìn)行管理。
在為對象定義好Label后,其他對象就可以使用Label Selector(選擇器)來定義其作用的對象了。
Label Selector的定義由多個逗號分隔的條件組成。
"labels": {
"key1": "value1",
"key2": "value2"
}
當(dāng)前有兩種Label Selector:基于等式的(Equality-based)和基于集合的(Set-based),在使用時可以將多個Label進(jìn)行組合來選擇。
基于等式的Label Selector使用等式類的表達(dá)式來進(jìn)行選擇:
- name = redis-slave: 選擇所有包含Label中key="name"且value="redis-slave"的對象;
- env != production: 選擇所有包括Label中的key="env"且value不等于"production"的對象。
基于集合的Label Selector使用集合操作的表達(dá)式來進(jìn)行選擇:
- name in (redis-master, redis-slave): 選擇所有包含Label中的key="name"且value="redis-master"或"redis-slave"的對象;
- name not in (php-frontend): 選擇所有包含Label中的key="name"且value不等于"php-frontend"的對象。
在某些對象需要對另一些對象進(jìn)行選擇時,可以將多個Label Selector進(jìn)行組合,使用逗號","進(jìn)行分隔即可。基于等式的LabelSelector和基于集合的Label Selector可以任意組合。例如:
name=redis-slave,env!=production
name not in (php-frontend),env!=production
1.3.4 Replication Controller(RC)
Replication Controller是Kubernetes系統(tǒng)中的核心概念,用于定義Pod副本的數(shù)量。在Master內(nèi),Controller Manager進(jìn)程通過RC的定義來完成Pod的創(chuàng)建、監(jiān)控、啟停等操作。
根據(jù)Replication Controller的定義,Kubernetes能夠確保在任意時刻都能運(yùn)行用于指定的Pod“副本”(Replica)數(shù)量。如果有過多的Pod副本在運(yùn)行,系統(tǒng)就會停掉一些Pod;如果運(yùn)行的Pod副本數(shù)量太少,系統(tǒng)就會再啟動一些Pod,總之,通過RC的定義,Kubernetes總是保證集群中運(yùn)行著用戶期望的副本數(shù)量。
同時,Kubernetes會對全部運(yùn)行的Pod進(jìn)行監(jiān)控和管理,如果有需要(例如某個Pod停止運(yùn)行),就會將Pod重啟命令提交給Node上的某個程序來完成(如Kubelet或Docker)。
可以說,通過對Replication Controller的使用,Kubernetes實(shí)現(xiàn)了應(yīng)用集群的高可用性,并大大減少了系統(tǒng)管理員在傳統(tǒng)IT環(huán)境中需要完成的許多手工運(yùn)維工作(如主機(jī)監(jiān)控腳本、應(yīng)用監(jiān)控腳本、故障恢復(fù)腳本等)。
對Replication Controller的定義使用Yaml或Json格式的配置文件來完成。以redis-slave為例,在配置文件中通過spec.template定義Pod的屬性(這部分定義與Pod的定義是一致的),設(shè)置spec.replicas=2來定義Pod副本的數(shù)量。
apiVersion: v1
kind: ReplicationController
metadata:
name: redis-slave
labels: redis-slave
name: redis-slave
spec:
replicas: 2
selector:
name: redis-slave
template:
metadata:
labels:
name: redis-slave
spec:
container:
- name: slave
image: kubeguide/guestbook-redis-slave
env:
- name: GET_HOSTS_FROM
value: env
ports:
- containerPort: 6379
通常,Kubernetes集群中不止一個Node,假設(shè)一個集群有3個Node,根據(jù)RC的定義,系統(tǒng)將可能在其中的兩個Node上創(chuàng)建Pod。
1.3.5 Service(服務(wù))
在Kubernetes的世界里,雖然每個Pod都會被分配一個單獨(dú)的IP地址,這個IP地址會隨時Pod的銷毀而消失。這就引出一個問題:如果有一組Pod組成一個集群來提供服務(wù),那么如何來訪問它們呢?
Kubernetes的Service(服務(wù))就是用來解決這個問題的核心概念。
一個Service可以看作一組提供相同服務(wù)的Pod的對外訪問接口。Service作用于哪些Pod是通過Label Selector來定義的。
1. 對Service的定義
對Service的定義同樣使用Yaml或Json格式的配置文件來完成。以redis-slave服務(wù)的定義為例:
apiVersion: v1
kind: Service
metadata:
name: redis-slave
labels:
name: redis-slave
spec:
ports:
- port: 6379
selector:
name: redis-slave
通過該定義,Kubernetes將會創(chuàng)建一個名為“redis-slave”的服務(wù),并在6379端口上監(jiān)聽。spec.selector的定義表示該Service將包含所有具有"name=redis-slave"的Label的Pod。
在Pod正常啟動后,系統(tǒng)將會根據(jù)Service的定義創(chuàng)建出與Pod對應(yīng)的Endpoint(端點(diǎn))對象,以建立起Service與后端Pod的對應(yīng)關(guān)系。隨著Pod的創(chuàng)建、銷毀,Endpoint對象也將被更新。Endpoint對象主要有Pod的IP地址和容器所需監(jiān)聽的端口號組成。
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)動態(tài)分配。Service的Cluster IP地址相對于Pod的IP地址來說相對穩(wěn)定,Service被創(chuàng)建時即被分配一個IP地址,在銷毀該Service之前,這個IP地址都不會再變化了。而Pod在Kubernetes集群中生命周期較短,可能被ReplicationContrller銷毀、再次創(chuàng)建,新創(chuàng)建的Pod將會分配一個新的IP地址。
3. 外部訪問Service
由于Service對象在Cluster IP Range池中分配到的IP只能在內(nèi)部訪問,所以其他Pod都可以無障礙地訪問到它。到如果這個Service作為前端服務(wù),準(zhǔn)備為集群外的客戶端提供服務(wù),我們就需要給這個服務(wù)提供公共IP了。
Kubernetes支持兩種對外提供服務(wù)的Service的type定義:NodePort和LoadBalancer。
1. NodePort
在定義Service時指定spec.type=NodePort,并指定spec.ports.nodePort的值,系統(tǒng)就會在Kubernetes集群中的每個Node上打開一個主機(jī)上的真實(shí)端口號。這樣,能夠訪問Node的客戶端都就能通過這個端口號訪問到內(nèi)部的Service了。
以php-frontend service的定義為例,nodePort=80,這樣,在每一個啟動了該php-frontend Pod的Node節(jié)點(diǎn)上,都會打開80端口。
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
name: frontend
spec:
type: NodePort
ports:
- port: 80
nodePort: 30001
selector:
name: frontend
2. LoadBalancer
如果云服務(wù)商支持外接負(fù)載均衡器,則可以通過spec.type=LoadBalaner定義Service,同時需要制定負(fù)載均衡器的IP地址。使用這種類型需要指定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"
}
]
}
}
}
在這個例子中,status.loadBalancer.ingress.ip設(shè)置的146.146.47.155為云服務(wù)商提供的負(fù)載均衡器的IP地址。
之后,對該Service的訪問請求將會通過LoadBalancer轉(zhuǎn)發(fā)到后端Pod上去,負(fù)載分發(fā)的實(shí)現(xiàn)方式則依賴于云服務(wù)上提供的LoadBalancer的實(shí)現(xiàn)機(jī)制。
1.3.6 Volume(存儲卷)
Volume是Pod中能夠被多個容器訪問的共享目錄。Kubernetes的Volume概念與Docker的Volume比較類似,但不完全相同。Kubernetes中的Volume與Pod生命周期相同,但與容器的生命周期不相關(guān)。當(dāng)容器終止或者重啟時,Volume中的數(shù)據(jù)也不會丟失。另外,Kubernetes支持多種類型的Volume,并且一個Pod可以同時使用任意多個Volume。
Kubernetes提供了非常豐富的Volume類型,下面逐一進(jìn)行說明。
- EmptyDir:一個EmptyDir Volume是在Pod分配到Node時創(chuàng)建的。從它的名稱就可以看出,它的初始內(nèi)容為空。在同一個Pod中所有容器可以讀和寫EmptyDir中的相同文件。當(dāng)Pod從Node上移除時,EmptyDir中的數(shù)據(jù)也會永久刪除。
- hostPath:在Pod上掛載宿主機(jī)上的文件或目錄。
- gcePersistentDisk:使用這種類型的Volume表示使用谷歌計(jì)算引擎(Google Compute Engine,GCE)上永久磁盤(Persistent Disk,PD)上的文件。與EmptyDir不同,PD上的內(nèi)容會永久保存,當(dāng)Pod被刪除時,PD只是被卸載(Unmount),但不會被刪除。需要注意的是,你需要先創(chuàng)建一個永久磁盤(PD)才能使用gcePersistentDisk。
- awsElasticBlockStore:與GCE類似,該類型的Volume使用Amazon提供的Amazon Web Service(AWS)的EBS Volume,并可以掛在到Pod中去。需要注意到是,需要首先創(chuàng)建一個EBS Volume才能使用awsElasticBlockStore。
- nfs:使用NFS(網(wǎng)絡(luò)文件系統(tǒng))提供的共享目錄掛載到Pod中。在系統(tǒng)中需要一個運(yùn)行中的NFS系統(tǒng)。
- iscsi:使用iSCSI存儲設(shè)備上的目錄掛載到Pod中。
- glusterfs:使用開源GlusterFS網(wǎng)絡(luò)文件系統(tǒng)的目錄掛載到Pod中。
- rbd:使用Linux塊設(shè)備共享存儲(Rados Block Device)掛載到Pod中。
- gitRepo:通過掛載一個空目錄,并從GIT庫clone一個git respository以供Pod使用。
- secret:一個secret volume用于為Pod提供加密的信息,你可以將定義在Kubernetes中的secret直接掛載為文件讓Pod訪問。secret volume是通過tmfs(內(nèi)存文件系統(tǒng))實(shí)現(xiàn)的,所以這種類型的volume總是不會持久化的。
- persistentVolumeClaim:從PV(PersistentVolume)中申請所需的空間,PV通常是一種網(wǎng)絡(luò)存儲,例如GCEPersistentDisk、AWSElasticBlockStore、NFS、iSCSI等。
1.3.7 Namespace(命名空間)
Namespace(命名空間)是Kubernetes系統(tǒng)中的另一個非常重要的概念,通過將系統(tǒng)內(nèi)部的對象“分配”到不同的Namespace中,形成邏輯上分組的不同項(xiàng)目、小組或用戶組,便于不同的分組在共享使用整個集群的資源的同時還能被分別管理。
Kubernetes集群在啟動后,會創(chuàng)建一個名為“default”的Namespace,通過Kubectl可以查看到。
使用Namespace來組織Kubernetes的各種對象,可以實(shí)現(xiàn)對用戶的分組,即“多租戶”管理。對不同的租戶還可以進(jìn)行單獨(dú)的資源配額設(shè)置和管理,使得整個集群的資源配置非常靈活、方便。
1.3.8 Annotation(注解)
Annotation與Label類似,也使用key/value鍵值對的形式進(jìn)行定義。Label具有嚴(yán)格的命名規(guī)則,它定義的是Kubernetes對象的元數(shù)據(jù)(Metadata),并且用于Label Selector。Annotation則是用戶任意定義的“附加”信息,以便于外部工具進(jìn)行查找。
用Annotation來記錄的信息包括:
- build信息、release信息、Docker鏡像信息等,例如時間戳、release id號、PR號、鏡像hash值、docker registry地址等;
- 日志庫、監(jiān)控庫、分析庫等資源庫的地址信息;
- 程序調(diào)試工具信息,例如工具名稱、版本號等;
- 團(tuán)隊(duì)的聯(lián)系信息,例如電話號碼、負(fù)責(zé)人名稱、網(wǎng)址等。?
1.3.9 小結(jié)
上述這些組件是Kubernetes系統(tǒng)的核心組件,它們共同構(gòu)成了Kubernetes系統(tǒng)的框架和計(jì)算模型。通過對它們進(jìn)行靈活組合,用戶就可以快速、方便地對容器集群進(jìn)行配置、創(chuàng)建和管理。
除了以上核心組件,在Kubernetes系統(tǒng)中還有許多可供配置的資源對象,例如LimitRange、ResourceQuota。另外,一些系統(tǒng)內(nèi)部使用的對象Binding、Event等請參考Kubernetes的API文檔。
1.4 Kubernetes總體架構(gòu)
Kubernetes集群由兩類節(jié)點(diǎn)組成:Master和Node。在Master上運(yùn)行etcd、API Server、Controller Manager和Scheduler四個組件,其中后三個組件構(gòu)成了Kubernetes的總控中心,負(fù)責(zé)對集群中所有資源進(jìn)行管理和調(diào)度。在每個Node上運(yùn)行Kubelet、Proxy和Docker Daemon三個組件,負(fù)責(zé)對本節(jié)點(diǎn)上的Pod的生命周期進(jìn)行管理,以及實(shí)現(xiàn)服務(wù)代理的功能。另外在所有節(jié)點(diǎn)上都可以運(yùn)行Kubectl命令行工具,它提供了Kubernetes的集群管理工具集。