资深专家深度剖析Kubernetes API Server第2章(共3章)
歡迎來(lái)到深入學(xué)習(xí)Kubernetes API?Server的系列文章的第二部分。在上一部分中我們對(duì)APIserver總體,相關(guān)術(shù)語(yǔ)及request請(qǐng)求流進(jìn)行探討說(shuō)明。在本部分文章中,我們主要聚焦于探究如何對(duì)Kubernetes?對(duì)象的狀態(tài)以一種可靠,持久的方式進(jìn)行管理。之前的文章中提到過(guò)API Server自身是無(wú)狀態(tài)的,并且它是唯一能夠與分布式存儲(chǔ)etcd直接通信的組件。
etcd的簡(jiǎn)要說(shuō)明
在*nix操作系統(tǒng)中,我們一般使用/etc來(lái)存儲(chǔ)相關(guān)配置數(shù)據(jù)。實(shí)際上etcd的名字就是由此發(fā)展而來(lái),在etc后面加上個(gè)”d”表示”distributed”分布式。任何分布式系統(tǒng)都需要有像etcd這樣能夠存儲(chǔ)系統(tǒng)數(shù)據(jù)的東西,使其能夠以一致和可靠的方式檢索相關(guān)數(shù)據(jù)。為了能實(shí)現(xiàn)分布式的數(shù)據(jù)訪問(wèn),etcd使用Raft?協(xié)議。從概念上講,etcd支持的數(shù)據(jù)模型是鍵值(key-value)存儲(chǔ)。在etcd2中,各個(gè)key是以層次結(jié)構(gòu)存在,而在etcd3中這個(gè)就變成了遍布模型,但同時(shí)也保持了層次結(jié)構(gòu)方式的兼容性。
使用容器化版本的etcd,我們可以創(chuàng)建上面的樹(shù),然后按如下方式檢索它:
$ docker run --rm -d -p 2379:2379 \
?--name test-etcd3 quay.io/coreos/etcd:v3.1.0 /usr/local/bin/etcd \
?--advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379
$ curl localhost:2379/v2/keys/foo -XPUT -d value="some value"
$ curl localhost:2379/v2/keys/bar/this -XPUT -d value=42
$ curl localhost:2379/v2/keys/bar/that -XPUT -d value=take
$ http localhost:2379/v2/keys/?recursive=true
HTTP/1.1 200 OK
Content-Length: 327
Content-Type: application/json
Date: Tue, 06 Jun 2017 12:28:28 GMT
X-Etcd-Cluster-Id: 10e5e39849dab251
X-Etcd-Index: 6
X-Raft-Index: 7
X-Raft-Term: 2
{
????"action": "get",
????"node": {
????????"dir": true,
????????"nodes": [
????????????{
????????????????"createdIndex": 4,
????????????????"key": "/foo",
????????????????"modifiedIndex": 4,
????????????????"value": "some value"
????????????},
????????????{
????????????????"createdIndex": 5,
????????????????"dir": true,
????????????????"key": "/bar",
????????????????"modifiedIndex": 5,
????????????????"nodes": [
????????????????????{
????????????????????????"createdIndex": 5,
????????????????????????"key": "/bar/this",
????????????????????????"modifiedIndex": 5,
????????????????????????"value": "42"
????????????????????},
????????????????????{
????????????????????????"createdIndex": 6,
????????????????????????"key": "/bar/that",
????????????????????????"modifiedIndex": 6,
????????????????????????"value": "take"
????????????????????}
????????????????]
????????????}
????????]
????}
}
現(xiàn)在我們已經(jīng)大致了解了etcd是如何工作的,接下去我們繼續(xù)討論etcd在Kubernetes是如何被使用的。
集群中的etcd
在Kubernetes中,etcd是控制平面中的一耳光獨(dú)立組成部分。在Kubernetes1.5.2版本之前,我們使用的是etcd2版本,而在Kubernetes1.5.2版本之后我們就轉(zhuǎn)向使用etcd3版本了。值得注意的是在Kubernetes1.5.x版本中etcd依舊使用的是v2的API模型,之后這將開(kāi)始變?yōu)関3的API模型,包括使用的數(shù)據(jù)模型。站在開(kāi)發(fā)者角度而言這個(gè)似乎沒(méi)什么直接影響,因?yàn)锳PI Server與存儲(chǔ)之前是抽象交互,而并不關(guān)心后端存儲(chǔ)的實(shí)現(xiàn)是etcd v2還是v3。但是如果是站在集群管理員的角度來(lái)看,還是需要知道etcd使用的是哪個(gè)版本,因?yàn)榧汗芾韱T需要日常對(duì)數(shù)據(jù)進(jìn)行一些備份,恢復(fù)的維護(hù)操作。
你可以API Server的相關(guān)啟動(dòng)項(xiàng)中配置使用etcd的方式,API Server的etcd相關(guān)啟動(dòng)項(xiàng)參數(shù)如下所示:
$ kube-apiserver -h
...
--etcd-cafile string ??SSL Certificate Authority file used to secure etcd communication.
--etcd-certfile string SSL certification file used to secure etcd communication.
--etcd-keyfile string ?SSL key file used to secure etcd communication.
...
--etcd-quorum-read ????If true, enable quorum read.
--etcd-servers ????????List of etcd servers to connect with (scheme://ip:port) …
...
Kubernetes存儲(chǔ)在etcd中的數(shù)據(jù),是以JSON字符串或Protocol Buffers?格式存儲(chǔ)。下面我們來(lái)看一個(gè)具體的例子:在apiserver-sandbox的命名空間中創(chuàng)建一個(gè)webserver的pod。然后我們使用etcdctl工具來(lái)查看相關(guān)etcd(在本環(huán)節(jié)中etcd版本為3.1.0)數(shù)據(jù)。
$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
??name: webserver
spec:
??containers:
??- name: nginx
????image: tomaskral/nonroot-nginx
????ports:
????- containerPort: 80
$ kubectl create -f pod.yaml
$ etcdctl ls /
/kubernetes.io
/openshift.io
$ etcdctl get /kubernetes.io/pods/apiserver-sandbox/webserver
{
??"kind": "Pod",
??"apiVersion": "v1",
??"metadata": {
????"name": "webserver",
...
下面我們來(lái)看一下這個(gè)pod對(duì)象是如何最終存儲(chǔ)到etcd中,通過(guò)kubectl create -f pod.yaml的方式。下圖描繪了這個(gè)總體流程:
1.客戶端(比如kubectl)提供一個(gè)理想狀態(tài)的對(duì)象,比如以YAML格式,v1版本提供。
2.Kubectl將YAML轉(zhuǎn)換為JSON格式,并發(fā)送。
3.對(duì)應(yīng)同類型對(duì)象的不同版本,API Server執(zhí)行無(wú)損耗轉(zhuǎn)換。對(duì)于老版本中不存在的字段則存儲(chǔ)在annotations中。
4.API Server將接受到的對(duì)象轉(zhuǎn)換為規(guī)范存儲(chǔ)版本,這個(gè)版本由API Server指定,一般是最新的穩(wěn)定版本,比如v1。
5.最后將對(duì)象通過(guò)JSON 或protobuf方式解析為一個(gè)value,通過(guò)一個(gè)特定的key存入etcd當(dāng)中。
我們可以通過(guò)配置?kube-apiserver的啟動(dòng)參數(shù)--storage-media-type來(lái)決定想要序列化數(shù)據(jù)存入etcd的格式,默認(rèn)情況下為application/vnd.kubernetes.protobuf格式。我們也可以通過(guò)配置--storage-versions啟動(dòng)參數(shù),來(lái)確定存入etcd的每個(gè)群組Group對(duì)象的默認(rèn)版本號(hào)。
現(xiàn)在讓我們來(lái)看看無(wú)損轉(zhuǎn)換是如何進(jìn)行的,我們將使用Kubernetes?對(duì)象Horizontal Pod Autoscaling?(HPA)來(lái)列舉說(shuō)明。HPA顧名思義是指通過(guò)監(jiān)控資源的使用情況結(jié)合ReplicationController控制Pod的伸縮。
首先我們期待一個(gè)API代理(以便于我們能夠在本地直接訪問(wèn)它),并啟動(dòng)ReplicationController,以及HPA?。
$ kubectl proxy --port=8080 &
$ kubectl create -f https://raw.githubusercontent.com/mhausenblas/kbe/master/specs/rcs/rc.yaml
kubectl autoscale rc rcex --min=2 --max=5 --cpu-percent=80
kubectl get hpa/rcex -o yaml
現(xiàn)在,你能夠使用httpie?——當(dāng)然你也能夠使用curl的方式——向API server?請(qǐng)求獲取HPA對(duì)象使用當(dāng)前的穩(wěn)定版本(autoscaling/v1),或者使用之前的版本(extensions/v1beta1),獲取的兩個(gè)版本的區(qū)別如下所示:
$ http localhost:8080/apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex > hpa-v1beta1.json
$ http localhost:8080/apis/autoscaling/v1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex > hpa-v1.json
$ diff -u hpa-v1beta1.json hpa-v1.json
{
??"kind": "HorizontalPodAutoscaler",
- ?"apiVersion": "extensions/v1beta1",
+ ?"apiVersion": "autoscaling/v1",
??"metadata": {
????"name": "rcex",
????"namespace": "api-server-deepdive",
- ???"selfLink": "/apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex",
+ ???"selfLink": "/apis/autoscaling/v1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex",
????"uid": "ad7efe42-50ed-11e7-9882-5254009543f6",
????"resourceVersion": "267762",
????"creationTimestamp": "2017-06-14T10:39:00Z"
??},
??"spec": {
- ???"scaleRef": {
+ ???"scaleTargetRef": {
??????"kind": "ReplicationController",
??????"name": "rcex",
- ?????"apiVersion": "v1",
- ?????"subresource": "scale"
+ ?????"apiVersion": "v1"
????},
????"minReplicas": 2,
????"maxReplicas": 5,
- ???"cpuUtilization": {
- ?????"targetPercentage": 80
- ???}
+ ???"targetCPUUtilizationPercentage": 80
我們能夠看到HorizontalPodAutoscale的版本從v1beta1變?yōu)榱藇1。API server能夠在不同的版本之前無(wú)損耗轉(zhuǎn)換,不論在etcd中實(shí)際存的是哪個(gè)版本。
在了解整個(gè)存儲(chǔ)流程之后,我們下面來(lái)探究一下API server如何將數(shù)據(jù)進(jìn)行編碼,解碼存入etcd中以JSON或protobuf的方式,同時(shí)也考慮到etcd的版本。
API Server將所有已知的Kubernetes對(duì)象類型保存在名為Scheme的Go類型注冊(cè)表(registry)中。在此注冊(cè)表中,定義每種了Kubernetes對(duì)象的類型以及如何轉(zhuǎn)換它們,如何創(chuàng)建新對(duì)象,以及如何將對(duì)象編碼和解碼為JSON或protobuf。
當(dāng)API Server從客戶端接收到一個(gè)對(duì)象時(shí),比如kubectl,通過(guò)HTTP路徑,能夠知道這個(gè)對(duì)象的具體版本號(hào)。首先會(huì)為這個(gè)對(duì)象使用對(duì)應(yīng)的版本Scheme創(chuàng)建一個(gè)空對(duì)象,然后通過(guò)JSON或protobuf將HTTP傳過(guò)來(lái)的對(duì)象內(nèi)容進(jìn)行解碼轉(zhuǎn)換。解碼完成后創(chuàng)建對(duì)象,存入etcd中。
在API中可能會(huì)有很多版本,如果要支持每個(gè)版本之間的直接轉(zhuǎn)換,這樣往往處理起來(lái)比較麻煩。比如某個(gè)API下面有三個(gè)版本,那么它就要支持一個(gè)版本到另兩個(gè)版本的直接轉(zhuǎn)換(比如v1 ??v1alpha1, v1 ??v1beta1, v1beta1 ??v1alpha1)。為了避免這個(gè)問(wèn)題,在API server中有一個(gè)特別的“internal”版本。當(dāng)兩個(gè)版本之間需要轉(zhuǎn)換時(shí),先轉(zhuǎn)換為internal版本,再轉(zhuǎn)換為相應(yīng)轉(zhuǎn)換的版本。這樣的話,每個(gè)版本只要支持能夠轉(zhuǎn)換為internal版本,那么就能夠與其它任何版本進(jìn)行間接的轉(zhuǎn)換。所以一個(gè)對(duì)象先轉(zhuǎn)換為internal版本,然后在轉(zhuǎn)換為穩(wěn)定的v1版本,然后在存入etcd中。
v1beta1 ??internal ??v1
在轉(zhuǎn)換的第一步中,如果某些字段用戶沒(méi)有賦值指定,那么這些會(huì)被賦為一個(gè)默認(rèn)值。比如在v1beta1?中肯定沒(méi)有在v1版本新增的一個(gè)字段。在這種情況下,用戶肯定無(wú)法在v1beta1?版本為這個(gè)字段賦值。這時(shí)候,在轉(zhuǎn)換的第一步中,我們會(huì)為這個(gè)字段賦一個(gè)默認(rèn)值以生成一個(gè)有效的internal。
校驗(yàn)及準(zhǔn)入
在轉(zhuǎn)換過(guò)程中有兩個(gè)重要的步驟,如下圖所示:
v1beta1 ??internal ?????| ???????????| ??????v1 ???json/yaml ??etcd
?????????????????????admission ???validation
準(zhǔn)入和校驗(yàn)是創(chuàng)建和更新對(duì)象存入etcd之前必須通過(guò)的步驟。它們的一些規(guī)則如下所示:
1.準(zhǔn)入(Admission):查看集群中的一些約束條件是否允許創(chuàng)建或更新此對(duì)象,并根據(jù)此集群的相關(guān)配置為對(duì)象設(shè)置一些默認(rèn)值。在Kubernetes有很多這種約束條件,下面列舉一些例子:
NamespaceLifecycle:如果命名空間不存在,則拒絕該命名空間下的所有傳入請(qǐng)求。
LimitRanger:強(qiáng)制限制命名空間中資源的使用率。
ServiceAccount:為pod創(chuàng)建?service account?。
DefaultStorageClass:如果用戶沒(méi)有為PersistentVolumeClaims賦值,那么將它設(shè)置為一個(gè)默認(rèn)值。
ResourceQuota:對(duì)群集上的當(dāng)前用戶強(qiáng)制執(zhí)行配額約束,如果配額不足,可能會(huì)拒絕請(qǐng)求。
2.校驗(yàn)(Validation):檢查傳入對(duì)象(在創(chuàng)建和更新期間)是否格式是否合法以及相關(guān)值是否有效。比如:
1)檢查必填字段是否已填。
2)檢查字符串格式是否正確(比如只允許小寫形式)。
3)是否有些字段存在沖突(比如,有兩個(gè)容器的名字一樣)。
校驗(yàn)(Validation)并不關(guān)心其它類型的對(duì)象實(shí)例,換言之,它只關(guān)心每個(gè)對(duì)象的靜態(tài)檢查,無(wú)關(guān)集群配置。
準(zhǔn)入(Admission)可以用flag?--admission-control=來(lái)啟動(dòng)或禁用。它們中的大多數(shù)可以有集群管理配置。此外,在Kubernetes 1.7中可以用webhook機(jī)制來(lái)擴(kuò)展準(zhǔn)入機(jī)制,使用控制器來(lái)實(shí)現(xiàn)對(duì)對(duì)象的傳統(tǒng)的校驗(yàn)。
遷移存儲(chǔ)對(duì)象
關(guān)于存儲(chǔ)對(duì)象遷移的最后說(shuō)明:當(dāng)Kubernetes需要升級(jí)到新的版本時(shí),根據(jù)每個(gè)版本的相關(guān)文檔步驟備份相關(guān)集群的數(shù)據(jù)是至關(guān)重要的。這一方面是由于etcd2到etcd3的轉(zhuǎn)變,另一方面是由于Kubernetes?對(duì)象的Kind及version的不斷發(fā)展。
在etcd中,每個(gè)對(duì)象是首選存儲(chǔ)版本(preferred storage version)存在的。但是,隨著時(shí)間的推移,etcd存儲(chǔ)中的對(duì)象可能以一個(gè)非常老的版本存在。如果在將來(lái)某個(gè)時(shí)間這個(gè)對(duì)象版本被廢棄了,那么將無(wú)法再解碼它的protobuf?或JSON。因此,在集群升級(jí)之前需要重寫,遷移這些數(shù)據(jù)。下面這些資料能夠?qū)ersion切換提供一些幫助:
請(qǐng)參閱集群管理文檔升級(jí)API版本部分。Upgrading to a different API version
下一次,在深入學(xué)習(xí)Kubernetes APIServer的第三部分中,我們將討論如何使用Custom Resource Definitions擴(kuò)展和自定義API資源。
轉(zhuǎn)載于:https://blog.51cto.com/13955983/2171662
總結(jié)
以上是生活随笔為你收集整理的资深专家深度剖析Kubernetes API Server第2章(共3章)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring RestTemplate中
- 下一篇: i/o传输数据打印换行符的方法