Pod
為什么需要Pod
Pod印荔,是Kubernetes項目中的原子調(diào)度單位乙濒。容器就是未來云計算系統(tǒng)中的進程搏明;容器鏡像就是系統(tǒng)里的.exe安裝包肛响,Kubernetes就是操作系統(tǒng)。在一個真正的操作系統(tǒng)里麸拄,進程并不是“孤苦伶仃”地獨自運行派昧,而是以進程組的方式,“有原則”的組織在一起感帅。在kubernetes中斗锭,“進程組”所映射的概念就是Pod。Google的工程師發(fā)現(xiàn)失球,他們部署的應(yīng)用岖是,往往都存在著類似于“進程和進程組”的關(guān)系,也就是說這些應(yīng)用之間有著親密的協(xié)作關(guān)系实苞,使得他們必須部署在同一臺機器上豺撑。
Pod的實現(xiàn)原理
Pod只是一個邏輯概念。
Pod其實是一組共享了某些資源的容器黔牵,具體來說聪轿,Pod里的所有容器,共享的是同一個Network Namespace猾浦,并且可以聲明共享同一個Volume陆错。
在Kubernetes中,Pod的實現(xiàn)需要使用一個中間容器金赦,這個容器叫做Infra容器音瓷。在Pod中,Infra容器永遠是第一個被創(chuàng)建的容器夹抗,而其他用戶定義的容器绳慎,則通過Join Network Namespace的方式與Infra容器關(guān)聯(lián)在一起。
Infra容器使用的是一個非常特殊的鏡像漠烧,叫做:k8s.gcr.io/pause杏愤。這個鏡像是一個用匯編語言編寫的、永遠處于暫停狀態(tài)的容器已脓。在Infra容器“Hold住”NetworkNamespace之后珊楼,用戶容器就可以加入到Infra容器的NetworkNamespace中了。這也就意味著度液,對于Pod中的容器A和容器B:
- 它們可以直接使用localhost進行通信
- 它們看到的網(wǎng)絡(luò)設(shè)備和Infra容器看到的完全一樣
- 一個Pod只有一個IP地址厕宗,也就是這個Pod的Network Namespace對應(yīng)的IP地址
- 所有的網(wǎng)絡(luò)資源邓了,都是一個Pod一份,并且被該Pod內(nèi)的所有容器共享
- Pod的生命周期只和Infra容器一致媳瞪,與容器A和容器B無關(guān)
對于Pod共享Volume來說,Kubernetes只要把所有Volume定義設(shè)計在Pod層即可照宝。例如:
apiVersion: v1
kind: Pod
metadata:
name: two-containers
spec:
restartPolicy: Never #Pod的重啟策略蛇受。1.Always:容器失效時,kubelet自動重啟該容器厕鹃。2.OnFailure:容器終止運行且退出碼不為0時重啟兢仰。3.Never:無論狀態(tài)如何,kubelet都不重啟該容器剂碴。
volumes:
- name: shared-data
hostPath:
path : /data
containers:
- name: nginx-container
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
- name: debian-container
image: debian
volumeMounts:
- name: shared-data
mountPath: /pod-data
command: ["/bin/sh"]
args: ["-c","echo Hello from the debian container > /pod/data/index.html"]
Pod對象的生命周期
Pod生命周期變化主要體現(xiàn)在Pod API對象的Status部分把将。
- Pending,這個狀態(tài)意味著Pod的YAML文件已經(jīng)提交給了Kubernetes忆矛,API對象已經(jīng)被創(chuàng)建并被保存在etcd中察蹲。但是,這個Pod里有些容器因為某種原因不能被成功創(chuàng)建催训,比如洽议,調(diào)度不成功
- Running,這個狀態(tài)下漫拭,Pod已經(jīng)調(diào)度成功亚兄,和一個具體的節(jié)點綁定。它包含的容器都已經(jīng)創(chuàng)建成功采驻,并且至少有一個正在運行中审胚。
- Succeed,這個狀態(tài)意味著礼旅,Pod中所有容器都正常運行完畢膳叨,并且已經(jīng)退出了。
- Failed各淀,這個狀態(tài)下懒鉴,Pod里至少有一個容器以不正常的狀態(tài)(非0狀態(tài)碼)退出。這個狀態(tài)的出現(xiàn)意味著你要想辦法Debug這個容器的應(yīng)用碎浇,比如查看Pod的Events和日志
- Unknown临谱,這是一個異常狀態(tài),意味著Pod的狀態(tài)不能持續(xù)被kubelet匯報給kube-apiserver奴璃,這很可能是Master和kubelet間的通信出了問題悉默。
Pod核心字段
可以把Pod堪稱傳統(tǒng)環(huán)境中的“機器”,把容器看作運行在這個“機器”里的“用戶程序”苟穆。
凡是調(diào)度抄课、網(wǎng)絡(luò)唱星、存儲以及安全相關(guān)的屬性,都是Pod級別的跟磨。
NodeSelector
這是一個供用戶將Pod與Node進行綁定的字段间聊,用法如下:
apiVersion: v1
kind: Pod
...
sepc:
nodeSelector:
disktype: ssd
這樣一個配置后,這個Pod只能被調(diào)度到攜帶了“disktype:ssd”標(biāo)簽的節(jié)點上抵拘。
NodeName
一旦Pod這個字段被輔助哎榴,Kubernetes就會認為這個Pod已經(jīng)經(jīng)過了調(diào)度,調(diào)度的結(jié)果就是賦值的節(jié)點名字僵蛛。這個字段一般由調(diào)度器負責(zé)設(shè)置尚蝌,用戶也可以設(shè)置這個字段“騙過”調(diào)度器
HostAliases
定義了Pod的hosts文件(/etc/hosts)里的內(nèi)容。
apiVersion: v1
kind: Pod
...
spec:
hostAliases:
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
這個Pod啟動后充尉,/etc/hosts文件內(nèi)容將如下所示:
127.0.0.1 localhost
...
10.244.135.10 hostaliases-pod
10.1.2.3 foo.remote
10.1.2.3 bar.remote
在Kubernetes中如何過要設(shè)置hosts文件的內(nèi)容飘言,一定要通過這種方式,否則驼侠,如果直接修改hosts文件的話姿鸿,在Pod刪除重建之后,kubelet會自動覆蓋掉被修改的內(nèi)容倒源。
Containers
Image般妙、Command、workingDir相速、Ports以及VolumeMounts都是構(gòu)成kubernetes項目中container的主要字段碟渺。還有其他幾個屬性值需要你關(guān)注
ImagePullPolicy
定義了鏡像拉取的策略。
- Always:默認值突诬,即每次創(chuàng)建Pod都重新拉取一次鏡像苫拍。另外當(dāng)容器的鏡像類似于nginx,nginx:latest這樣的名字時旺隙,ImagePullPolicy也會被認為是Always绒极。
- Never:永遠不主動拉取這個鏡像
- IfNotPresent:只有在宿主機上不存在這個鏡像時才拉取
Lifecycle
定義的是Container Lifecycle Hooks。它的作用是在容器狀態(tài)發(fā)生變化的時候觸發(fā)一系列“鉤子”蔬捷。比如
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh","-c","echo Hello fomr the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
postStart垄提,指的是在容器啟動后,立刻執(zhí)行一個指定的操作周拐。postStart定義的操作铡俐,雖然是在Docker容器ENTRYPOINT執(zhí)行之后,但不嚴格保證順序妥粟,也就是說在postStart啟動時审丘,ENTRYPOINT有可能還沒結(jié)束。
preStop勾给,發(fā)生的時機滩报,是容器被殺死之前锅知。它會阻塞當(dāng)前的容器殺死流程,直到這個Hook定義的操作完成脓钾。
Projected Volume
ProjectVolume可以翻譯為投射數(shù)據(jù)卷售睹,Kubernetes中,有幾種特殊的Volume可训,它們存在的意義并不是為了存放容器中的數(shù)據(jù)侣姆,也不是用來進行容器和宿主機之間的數(shù)據(jù)交換,它們的作用是為容器提供預(yù)先定義好的數(shù)據(jù)沉噩。這些Volume中的信息好像是被Kubernetes“投射”進容器中的。
目前為止柱蟀,Kubernetedes支持的Projected Volume一共有四種:
- Secret
- ConfigMap
- Downward API
- ServiceAccountToken
Secret
secret可以幫你把Pod想要訪問的加密數(shù)據(jù)存放到Etcd中川蒙,然后,你就可以通過在Pod的容器里掛載Volume的方式长已,訪問到這些Secret保存的信息
Secret使用實例:
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-secret-volume
image: busybox
args:
- sleep
- "86400"
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
projected:
sources:
- secret:
name: user
- secret:
name: pass
上述Pod中畜眨,聲明掛載的Volume,是projected類型的术瓮。這個volume的數(shù)據(jù)來源則是名為user和pass的secret對象
secret創(chuàng)建命令:
kubectl create secret generic [NAME] [DATA] [TYPE] #TYPE默認為Opaque
kubectl create secret generic [NAME] --from-file=[KEY]=[VALUE的文件路徑]
kubectl create secret generic [NAME] --from-literal=[KEY]=[VALUE] --from-literal=[KEY]=[VALUE] ....
然后康聂,我們創(chuàng)建這兩個secret對象
kubectl create secret generic user --from=literal=username=admin
kubectl create secret generic pass --from=literal=password=hanjiaxv123
也可以使用文件創(chuàng)建
$ cat ./username.txt
admin
$ cat ./password.txt
hanjiaxv123
$ kubectl create secret generic user --from-file=./username.txt
$ kubectl create secret generic pass --from-file=./password.txt
接下來,我們創(chuàng)建這個pod胞四,然后進入pod恬汁。
[root@host1 ~]# kubectl create -f geektime/pod/test-projected-volume.yaml
pod/test-projected-volume created
[root@host1 ~]# kubectl exec -it test-projected-volume -- /bin/sh
/ # ls /projected-volume
pass username
/ # cd /projected-volume
/projected-volume # cat pass
hanjiaxv123/projected-volume # cat username
ConfigMap
ConfigMap與secret的區(qū)別在于,ConfigMap保存的是不需要加密的辜伟、應(yīng)用所需的配置信息氓侧。
ConfigMap用法幾乎和secret相同。
ConfigMap創(chuàng)建語法:
kubectl create configmap [NAME] [DATA]
指定文件:
kubectl create configmap kube-flannel-cfg --from-file=configure-pod-container/configmap/cni-conf.json -n kube-system
指定鍵值對:
kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm
ConfigMap使用
apiVersion: v1
kind: ConfigMap
metadta:
name: special-config
data:
special.how: very
special.type: charm
第一種导狡,用ConfigMap配置環(huán)境變量
apiVersion: v1
kind: Pod
metadata:
name: cm-env-test
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: ["/bin/sh","-c","env"]
env:
#使用special-config中的special.how定義環(huán)境變量
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
第二種约巷,使用ConfigMap管控命令行參數(shù)
apiVersion: v1
kind: Pod
metadata:
name: cm-cmd-test
spec:
containers:
- name: test-container
image: k8s.io.gcr/busybox
command: ["/bin/sh","-c","echo $(SPECIAL_LEVEL_KEY)"]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
第三種,使用ConfigMap掛載配置文件
apiVersion: v1
kind: Pod
metadata:
name: cm-volume-test
spec:
containers:
- name: test-container
image: k8s.io.gcr/busybox
command: ["/bin/sh","-c","ls /etc/config/"]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: special-config
Service Account
如果現(xiàn)在有一個Pod旱捧,我能否在這個Pod里安裝一個Kubernetes的Client独郎,這樣就可以從容器里直接訪問并操作這個Kubernetes的API了呢?
答案是可以枚赡。
首先氓癌,要解決API Server的授權(quán)問題
Service Account對象的作用,就是Kubernetes系統(tǒng)內(nèi)置的一種“服務(wù)賬號”贫橙,它是Kubernetes進行權(quán)限分配的對象顽铸。比如,Service Account A料皇,可以只被允許對Kubernetes API進行GET操作谓松,而Service Account B星压,則可以有Kubernetes API的所有操作權(quán)限。
這種Service Account的授權(quán)信息和文件鬼譬,實際上保存在它所綁定的一個特殊的Secret對象中娜膘,這種特殊的Service Account對象,就叫做ServiceAccountToken优质。任何運行在Kubernetes集群上的應(yīng)用竣贪,都必須使用這個ServiceAccountToken中保存的授權(quán)信息,才可以合法地訪問API Server巩螃。ServiceAccountToken只是一種特殊地Secret演怎。
Kubernetes已經(jīng)為你提供了一個默認“服務(wù)賬戶”,并且避乏,任何一個運行在Kubernetes里的Pod爷耀,都可以直接使用這個默認的Service Account,無需顯示地聲明掛載它拍皮。
查看一下集群中運行的Pod歹叮,就會發(fā)現(xiàn),每一個Pod铆帽,都已經(jīng)聲明了一個類型是Secret咆耿、名為default-token-xxxx的Volume,然后自動掛載在每一個容器的固定目錄上爹橱。
[root@host1 ~]# kubectl describe pods test-projected-volume
...
Volumes:
mysql-cred:
Type: Projected (a volume that contains injected data from multiple sources)
SecretName: user
SecretOptionalName: <nil>
SecretName: pass
SecretOptionalName: <nil>
default-token-j5k7m:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-j5k7m
Optional: false
....
Kubernetes在每個Pod創(chuàng)建的時候萨螺,自動在它的spec.volumes部分添加上了默認ServiceAccountToken的定義,然后自動給每個容器加上了對應(yīng)的volumeMounts字段愧驱。這個容器內(nèi)的路徑在Kubernetes里是固定的屑迂,即:/var/run/secrets/kubernetes.io/serviceaccount,這個Secret類型的Volume內(nèi)容如下所示:
/ # ls /var/run/secrets/kubernetes.io/serviceaccount/
ca.crt namespace token
你的應(yīng)用程序只要加載這些授權(quán)文件冯键,就可以訪問并操作Kubernetes API了惹盼。如果使用的是Kubernetes官方的Client包,還可以自動加載這個目錄下的文件惫确。
Pod的健康檢查機制
Liveness Probe是存活探針手报、Readiness是就緒探針。
一個程序在長時間運行后或者有bug的情況下改化,會進入不正常狀態(tài)掩蛤,Liveness會將容器內(nèi)的進程殺死,重啟整個容器/Pod陈肛,使得Pod恢復(fù)最初始的狀態(tài)揍鸟。readiness探測失敗后,Pod和容器并不會被刪除句旱,而是被標(biāo)記為特殊狀態(tài)阳藻,進入這個狀態(tài)后晰奖,會切斷上層流量到達該Pod。
探測方式
- httpGet 通過發(fā)送http Get請求返回200-399狀態(tài)碼則表示容器健康
- Exec 通過執(zhí)行命令檢查服務(wù)是否正常腥泥,命令返回值為0表示容器健康
- tcpSocket 通過容器的IP和PORT執(zhí)行TCP檢查匾南,如果可以建立TCP連接表示容器健康
探測結(jié)果
- Success Container通過了檢查
- Failure Container未能通過檢查
- Unknown 未能執(zhí)行檢查,不采取任何動作
重啟策略
- Always 總是重啟
- OnFailure 失敗才重啟
- Never 永遠不重啟
實例參考
exec方式:
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: k8s.gcr.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
httpGet
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: k8s.gcr.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders:
- name: Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
tcpSocket
apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: k8s.gcr.io/goproxy:0.1
ports:
- containerPort: 8080
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
參數(shù):
- initialDelaySeconds:Pod啟動后延遲多久進行檢查
- periodSeconds:檢查間隔事件蛔外,默認10S
- timeoutSeconds:探測的超時時間蛆楞,默認1S
- successThreshold:探測失敗后再次判斷成功的閾值,默認為1次
- failureThreshold:探測失敗的重試次數(shù)夹厌,默認3次
總結(jié)
Liveness(存活探針) | Readiness(就緒探針) | |
---|---|---|
介紹 | 用于判斷容器是否存活豹爹,即容器的狀態(tài)是否是Running,如果Liveness探針判斷容器不健康矛纹,則會觸發(fā)kubelet殺掉容器臂聋,并根據(jù)配置的策略判斷是否重啟容器,如果默認不配置Liveness探針崖技,則認為返回值默認為成功 | 用于判斷容器是否啟動完成,即Pod得Condition是否為Ready钟哥,如果探測結(jié)果不成功迎献,則會將Pod從Endpoint中移除,直至下次判斷成功腻贰,再將Pod掛回到Endpoint上 |
檢測失敗 | 殺掉Pod | 切斷上層流量到Pod |
使用場景 | 支持重新拉起的應(yīng)用 | 啟動后無法立即對外服務(wù)的應(yīng)用 |
參考: 阿里云大學(xué)云原生技術(shù)公開課 極客時間深入剖析Kubernetes