一、Pod介紹
1、Pod結(jié)構(gòu)
每個(gè)Pod中都可以包含一個(gè)或者多個(gè)容器,這些容器可以分為兩類:
1)用戶容器:用戶程序所在的容器,數(shù)量可多可少
2)根容器:Pause容器,由Kubernetes創(chuàng)建,這是每個(gè)Pod都會(huì)有的一個(gè)根容器,它的作用有兩個(gè):
- 可以以它為依據(jù),評估整個(gè)Pod的健康狀態(tài)
- 可以在根容器上設(shè)置Ip地址,其它容器都共享此Ip(Pod IP),以實(shí)現(xiàn)Pod內(nèi)部的網(wǎng)絡(luò)通信
注意上面說的是Pod內(nèi)部的通訊,Pod的之間的通訊采用虛擬二層網(wǎng)絡(luò)技術(shù)來實(shí)現(xiàn),我們當(dāng)前環(huán)境用的是Flannel
2、Pod的定義
下面是Pod的資源清單:(常用的)
apiVersion: v1 #必選,版本號,例如v1
kind: Pod #必選,資源類型,例如 Pod
metadata: #必選,元數(shù)據(jù)
name: string #必選,Pod名稱
namespace: string #Pod所屬的命名空間,默認(rèn)為"default"
labels: #自定義標(biāo)簽列表
- name: string
spec: #必選,Pod中容器的詳細(xì)定義
containers: #必選,Pod中容器列表
- name: string #必選,容器名稱
image: string #必選,容器的鏡像名稱
imagePullPolicy: [ Always|Never|IfNotPresent ] #獲取鏡像的策略
command: [string] #容器的啟動(dòng)命令列表,如不指定,使用打包時(shí)使用的啟動(dòng)命令
args: [string] #容器的啟動(dòng)命令參數(shù)列表
workingDir: string #容器的工作目錄
volumeMounts: #掛載到容器內(nèi)部的存儲(chǔ)卷配置
- name: string #引用pod定義的共享存儲(chǔ)卷的名稱,需用volumes[]部分定義的的卷名
mountPath: string #存儲(chǔ)卷在容器內(nèi)mount的絕對路徑,應(yīng)少于512字符
readOnly: boolean #是否為只讀模式
ports: #需要暴露的端口庫號列表
- name: string #端口的名稱
containerPort: int #容器需要監(jiān)聽的端口號
hostPort: int #容器所在主機(jī)需要監(jiān)聽的端口號,默認(rèn)與Container相同
protocol: string #端口協(xié)議,支持TCP和UDP,默認(rèn)TCP
env: #容器運(yùn)行前需設(shè)置的環(huán)境變量列表
- name: string #環(huán)境變量名稱
value: string #環(huán)境變量的值
resources: #資源限制和請求的設(shè)置
limits: #資源限制的設(shè)置
cpu: string #Cpu的限制,單位為core數(shù),將用于docker run --cpu-shares參數(shù)
memory: string #內(nèi)存限制,單位可以為Mib/Gib,將用于docker run --memory參數(shù)
requests: #資源請求的設(shè)置
cpu: string #Cpu請求,容器啟動(dòng)的初始可用數(shù)量
memory: string #內(nèi)存請求,容器啟動(dòng)的初始可用數(shù)量
lifecycle: #生命周期鉤子
postStart: #容器啟動(dòng)后立即執(zhí)行此鉤子,如果執(zhí)行失敗,會(huì)根據(jù)重啟策略進(jìn)行重啟
preStop: #容器終止前執(zhí)行此鉤子,無論結(jié)果如何,容器都會(huì)終止
livenessProbe: #對Pod內(nèi)各容器健康檢查的設(shè)置,當(dāng)探測無響應(yīng)幾次后將自動(dòng)重啟該容器
exec: #對Pod容器內(nèi)檢查方式設(shè)置為exec方式
command: [string] #exec方式需要制定的命令或腳本
httpGet: #對Pod內(nèi)個(gè)容器健康檢查方法設(shè)置為HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #對Pod內(nèi)個(gè)容器健康檢查方式設(shè)置為tcpSocket方式
port: number
initialDelaySeconds: 0 #容器啟動(dòng)完成后首次探測的時(shí)間,單位為秒
timeoutSeconds: 0 #對容器健康檢查探測等待響應(yīng)的超時(shí)時(shí)間,單位秒,默認(rèn)1秒
periodSeconds: 0 #對容器監(jiān)控檢查的定期探測時(shí)間設(shè)置,單位秒,默認(rèn)10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always | Never | OnFailure] #Pod的重啟策略
nodeName: <string> #設(shè)置NodeName表示將該P(yáng)od調(diào)度到指定到名稱的node節(jié)點(diǎn)上
nodeSelector: obeject #設(shè)置NodeSelector表示將該P(yáng)od調(diào)度到包含這個(gè)label的node上
imagePullSecrets: #Pull鏡像時(shí)使用的secret名稱,以key:secretkey格式指定
- name: string
hostNetwork: false #是否使用主機(jī)網(wǎng)絡(luò)模式,默認(rèn)為false,如果設(shè)置為true,表示使用宿主機(jī)網(wǎng)絡(luò)
volumes: #在該pod上定義共享存儲(chǔ)卷列表
- name: string #共享存儲(chǔ)卷名稱 (volumes類型有很多種)
emptyDir: {} #類型為emtyDir的存儲(chǔ)卷,與Pod同生命周期的一個(gè)臨時(shí)目錄。為空值
hostPath: string #類型為hostPath的存儲(chǔ)卷,表示掛載Pod所在宿主機(jī)的目錄
path: string #Pod所在宿主機(jī)的目錄,將被用于同期中mount的目錄
secret: #類型為secret的存儲(chǔ)卷,掛載集群與定義的secret對象到容器內(nèi)部
scretname: string
items:
- key: string
path: string
configMap: #類型為configMap的存儲(chǔ)卷,掛載預(yù)定義的configMap對象到容器內(nèi)部
name: string
items:
- key: string
path: string
配置項(xiàng)很多,可以通過指令來查看每種資源的可配置項(xiàng):
# 查看某種資源可以配置的一級屬性
kubectl explain 資源類型
# 查看屬性的子屬性
kubectl explain 資源類型.屬性
# explain:解釋
[root@k8s-master01 ~] kubectl explain pod
KIND: Pod
VERSION: v1
FIELDS:
apiVersion <string>
kind <string>
metadata <Object>
spec <Object>
status <Object>
[root@k8s-master01 ~] kubectl explain pod.metadata
KIND: Pod
VERSION: v1
RESOURCE: metadata <Object>
FIELDS:
annotations <map[string]string>
clusterName <string>
creationTimestamp <string>
deletionGracePeriodSeconds <integer>
deletionTimestamp <string>
finalizers <[]string>
generateName <string>
generation <integer>
labels <map[string]string>
managedFields <[]Object>
name <string>
namespace <string>
ownerReferences <[]Object>
resourceVersion <string>
selfLink <string>
uid <string>
Kubernetes中,基本所有資源的一級屬性都是一樣的,有五部分:
- apiVersion:版本,由kubernetes內(nèi)部定義,版本號必須可以用 kubectl api-versions 查詢到所有,explain查看當(dāng)前
- kind:類型,由kubernetes內(nèi)部定義,版本號必須可以用 kubectl api-resources 查詢到所有,explain查看當(dāng)前
- metadata:元數(shù)據(jù),主要是資源
標(biāo)識
和說明,常用的有name、namespace、labels等- spec:描述,這是配置中最重要的一部分,里面是對各種
資源配置
的詳細(xì)描述- status:狀態(tài)信息,里面的內(nèi)容不需要定義,由kubernetes
自動(dòng)生成
先重點(diǎn)看spec的常用子屬性:
- containers <Object[ ]> : 容器列表,用于定義容器的詳細(xì)信息
- nodeName: 根據(jù)nodeName的值將pod調(diào)度到指定的Node節(jié)點(diǎn)上,不指定則由schedule調(diào)度分配
- nodeSelector <map[]>: 根據(jù)NodeSelector中定義的信息選擇將該P(yáng)od調(diào)度到包含這些label的Node 上
- hostNetwork: 是否使用主機(jī)網(wǎng)絡(luò)模式,默認(rèn)為false,如果設(shè)置為true,表示使用宿主機(jī)網(wǎng)絡(luò),多個(gè)副本pod就端口沖突了,不建議
- volumes <Object[]> :存儲(chǔ)卷,用于定義Pod上面掛載的存儲(chǔ)信息
- restartPolicy :重啟策略,表示Pod在遇到故障的時(shí)候的處理策略
二、Pod配置:spec.containers
[root@k8s-master01 ~] kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 數(shù)組,代表可以有多個(gè)容器
FIELDS:
name <string> # 容器名稱
image <string> # 容器需要的鏡像地址
imagePullPolicy <string> # 鏡像拉取策略
command <[]string> # 容器的啟動(dòng)命令列表,如不指定,使用打包時(shí)使用的啟動(dòng)命令
args <[]string> # 容器的啟動(dòng)命令需要的參數(shù)列表
env <[]Object> # 容器環(huán)境變量的配置
ports <[]Object> # 容器需要暴露的端口號列表
resources <Object> # 資源限制和資源請求的設(shè)置
1、基本配置 name和image
創(chuàng)建個(gè)yaml文件pod-base.yaml 測試name和image:
apiVersion: v1
kind: Pod
metadata:
name: pod-base
namespace: dev
labels:
user: 9527
spec:
containers:
# - 即數(shù)組
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
以上即定義一個(gè)pod,包含兩個(gè)容器:
- nginx:用1.17.1版本的nginx鏡像創(chuàng)建,(nginx是一個(gè)輕量級web容器)
- busybox:用1.30版本的busybox鏡像創(chuàng)建,(busybox是一個(gè)小巧的linux命令集合)
[root@k8s-master01 pod] kubectl apply -f pod-base.yaml
pod/pod-base created
# READY 1/2 : 表示當(dāng)前Pod中有2個(gè)容器,其中1個(gè)準(zhǔn)備就緒,1個(gè)未就緒
# RESTARTS : 重啟次數(shù),因?yàn)橛?個(gè)容器故障了,Pod一直在重啟試圖恢復(fù)它
[root@k8s-master01 pod] kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
pod-base 1/2 Running 4 95s
2、鏡像拉取策略 imagePullpolicy
鏡像拉取策略有三種:
- Always:總是從遠(yuǎn)程倉庫拉取鏡像(一直遠(yuǎn)程下載)
- IfNotPresent:本地有則使用本地鏡像,本地沒有則從遠(yuǎn)程倉庫拉取鏡像(本地有就本地 本地沒遠(yuǎn)程下載)
- Never:只使用本地鏡像,從不去遠(yuǎn)程倉庫拉取,本地沒有就報(bào)錯(cuò) (一直使用本地)
關(guān)于策略默認(rèn)值:
- 若鏡像tag為具體版本號,則默認(rèn)策略是:IfNotPresent
- 若鏡像tag為latest(最終版本),則默認(rèn)always
創(chuàng)建pod-imagepullpolicy.yaml文件做測試:
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent # 用于設(shè)置鏡像拉取策略
- name: busybox
image: busybox:1.30
創(chuàng)建pod:
[root@k8s-master01 pod] kubectl create -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created
# 查看Pod詳情
# 此時(shí)明顯可以看到nginx鏡像有一步Pulling image "nginx:1.17.1"的過程
[root@k8s-master01 pod] kubectl describe pod pod-imagepullpolicy -n dev
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned dev/pod-imagePullPolicy to node1
Normal Pulling 32s kubelet, node1 Pulling image "nginx:1.17.1"
Normal Pulled 26s kubelet, node1 Successfully pulled image "nginx:1.17.1"
Normal Created 26s kubelet, node1 Created container nginx
Normal Started 25s kubelet, node1 Started container nginx
Normal Pulled 7s (x3 over 25s) kubelet, node1 Container image "busybox:1.30" already present on machine
Normal Created 7s (x3 over 25s) kubelet, node1 Created container busybox
Normal Started 7s (x3 over 25s) kubelet, node1 Started container busybox
3、啟動(dòng)命令 command
上面busybox容器一直沒有成功運(yùn)行,因?yàn)閎usybox并不是一個(gè)程序,而是類似于一個(gè)工具類的集合,kubernetes集群啟動(dòng)管理后,容器中就沒有進(jìn)程了,它會(huì)自動(dòng)關(guān)閉。解決方法就是讓其一直在運(yùn)行 ? command
command,用于在pod中的容器
初始化完畢之后運(yùn)行一個(gè)命令
創(chuàng)建pod-command.yaml文件做測試:
apiVersion: v1
kind: Pod
metadata:
name: pod-command
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
命令解釋:
"/bin/sh","-c", 使用sh執(zhí)行命令
touch /tmp/hello.txt; 創(chuàng)建一個(gè)/tmp/hello.txt 文件
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中寫入當(dāng)前時(shí)間
[root@k8s-master01 pod] kubectl create -f pod-command.yaml
pod/pod-command created
# 此時(shí)發(fā)現(xiàn)兩個(gè)pod都正常運(yùn)行了
[root@k8s-master01 pod] kubectl get pods pod-command -n dev
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Runing 0 2s
補(bǔ)充一個(gè)命令:進(jìn)入容器,在容器內(nèi)部執(zhí)行命令
kubectl exec pod名稱 -n 命名空間 -it -c 容器名稱 /bin/sh
# 進(jìn)入pod中的busybox容器,查看文件內(nèi)容
[root@k8s-master01 pod] kubectl exec pod-command -n dev -it -c busybox /bin/sh
/: tail -f /tmp/hello.txt
14:44:19
14:44:22
14:44:25
最后:kubernetes中的command、args兩項(xiàng)其實(shí)是實(shí)現(xiàn)覆蓋Dockerfile中ENTRYPOINT的功能
:
- 如果command和args均沒有寫,那么用Dockerfile的配置。
- 如果command寫了,但args沒有寫,那么Dockerfile默認(rèn)的配置會(huì)被忽略,執(zhí)行輸入的command
- 如果command沒寫,但args寫了,那么Dockerfile中配置的ENTRYPOINT的命令會(huì)被執(zhí)行,使用當(dāng)前args的參數(shù)
- 如果command和args都寫了,那么Dockerfile的配置被忽略,執(zhí)行command并追加上args參數(shù)
4、環(huán)境變量 env
env,環(huán)境變量,通過name-value的鍵值對,用于在pod中的容器設(shè)置環(huán)境變量。創(chuàng)建pod-env.yaml文件:
apiVersion: v1
kind: Pod
metadata:
name: pod-env
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]
env: # 設(shè)置環(huán)境變量列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
創(chuàng)建pod:
# 創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-env.yaml
pod/pod-env created
# 進(jìn)入容器,輸出環(huán)境變量
[root@k8s-master01 ~] kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ echo $username
admin
/ echo $password
123456
5、端口設(shè)置 ports
容器的端口設(shè)置,先看ports下的子屬性:
[root@k8s-master01 ~] kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:
name <string> # 端口名稱,如果指定,必須保證name在pod中是唯一的
containerPort<integer> # 容器要監(jiān)聽的端口(0<x<65536)
hostPort <integer> # 容器要在主機(jī)上公開的端口,如果設(shè)置,主機(jī)上只能運(yùn)行容器的一個(gè)副本(一般省略)
hostIP <string> # 要將外部端口綁定到的主機(jī)IP(一般省略)
protocol <string> # 端口協(xié)議。必須是UDP、TCP或SCTP。默認(rèn)為“TCP”。
關(guān)于hostPort:創(chuàng)建一個(gè)二副本的deploy,兩個(gè)pod的IP分別為1.1和1.2,此時(shí)使用podIP + 容器Port正常。但容器端口都是80,此時(shí)都想映射到主機(jī)端口,比如映射到主機(jī)80,就會(huì)沖突。
編寫一個(gè)測試案例,創(chuàng)建pod-ports.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 設(shè)置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
創(chuàng)建pod:
# 創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-ports.yaml
pod/pod-ports created
# 查看pod
# 在下面可以明顯看到配置信息
[root@k8s-master01 ~] kubectl get pod pod-ports -n dev -o yaml
......
spec:
containers:
- image: nginx:1.17.1
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: nginx-port
protocol: TCP
......
訪問容器中的程序需要使用的是PodIp:containerPort
6、資源配額 resources
容器中的程序要運(yùn)行,要占用一定資源,比如cpu和內(nèi)存等,如果不對某個(gè)容器的資源做限制,那么它就可能吃掉大量資源,導(dǎo)致其它容器無法運(yùn)行。因此kubernetes提供了對內(nèi)存和cpu的資源進(jìn)行配額的機(jī)制 ? resources:
resources.limits
:上限。用于限制運(yùn)行時(shí)容器的最大占用資源,當(dāng)容器占用資源超過limits時(shí)會(huì)被終止,并進(jìn)行重啟resources.requests
:下限。用于設(shè)置容器需要的最小資源,如果環(huán)境資源不夠,容器將無法啟動(dòng)
寫個(gè)測試配置文件pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-resources
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources: # 資源配額
limits: # 限制資源(上限)
cpu: "2" # CPU限制,單位是core數(shù)
memory: "10Gi" # 內(nèi)存限制
requests: # 請求資源(下限)
cpu: "1" # CPU限制,單位是core數(shù)
memory: "10Mi" # 內(nèi)存限制
關(guān)于單位:
- cpu:core數(shù),可以為整數(shù)或小數(shù)
- memory: 內(nèi)存大小,可以使用Gi、Mi、G、M等形式
# 運(yùn)行Pod
[root@k8s-master01 ~] kubectl create -f pod-resources.yaml
pod/pod-resources created
# 查看發(fā)現(xiàn)pod運(yùn)行正常
[root@k8s-master01 ~] kubectl get pod pod-resources -n dev
NAME READY STATUS RESTARTS AGE
pod-resources 1/1 Running 0 39s
# 接下來,停止Pod
[root@k8s-master01 ~] kubectl delete -f pod-resources.yaml
pod "pod-resources" deleted
停止pod后,更改內(nèi)存最低配額為10Gi(虛擬機(jī)配置不夠了)
# 編輯pod,修改resources.requests.memory的值為10Gi
[root@k8s-master01 ~] vim pod-resources.yaml
# 再次啟動(dòng)pod
[root@k8s-master01 ~] kubectl create -f pod-resources.yaml
pod/pod-resources created
# 查看Pod狀態(tài),發(fā)現(xiàn)Pod啟動(dòng)失敗
[root@k8s-master01 ~] kubectl get pod pod-resources -n dev -o wide
NAME READY STATUS RESTARTS AGE
pod-resources 0/1 Pending 0 20s
pod狀態(tài)為pending(待定、掛起):
# 查看pod詳情會(huì)發(fā)現(xiàn),如下提示
[root@k8s-master01 ~] kubectl describe pod pod-resources -n dev
......
Warning FailedScheduling 35s default-scheduler 0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 Insufficient memory.(內(nèi)存不足)
調(diào)度失?。]有節(jié)點(diǎn)滿足這個(gè)最低配置),Insufficient memory
.(內(nèi)存不足)
三、Pod的生命周期
pod從創(chuàng)建至終止,有這幾個(gè)階段:
1)pod創(chuàng)建過程
2)運(yùn)行初始化容器(init container)過程
3)運(yùn)行主容器(main container)
- 容器啟動(dòng)后鉤子(post start)、容器終止前鉤子(pre stop)
- 容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
4)pod終止過程
在整個(gè)生命周期中,Pod會(huì)出現(xiàn)5種狀態(tài)(相位):
- 掛起(Pending):apiserver已經(jīng)創(chuàng)建了pod資源對象,但它尚未被調(diào)度完成或者仍處于下載鏡像的過程中
- 運(yùn)行中(Running):pod已經(jīng)被調(diào)度至某節(jié)點(diǎn),并且所有容器都已經(jīng)被kubelet創(chuàng)建完成
- 成功(Succeeded):pod中的所有容器都已經(jīng)成功終止并且不會(huì)被重啟
- 失?。‵ailed):所有容器都已經(jīng)終止,但至少有一個(gè)容器終止失敗,即容器返回了非0值的退出狀態(tài)
- 未知(Unknown):apiserver無法正常獲取到pod對象的狀態(tài)信息,通常由網(wǎng)絡(luò)通信失敗所導(dǎo)致
1、創(chuàng)建和終止
Pod創(chuàng)建的過程
- 用戶執(zhí)行kubectl指令提交pod信息給apiServer
- apiServer生成pod信息,存入etcd,并返回確認(rèn)信息給客戶端
- apiServer開始反饋pod的變化信息,其它組件使用watch機(jī)制來跟蹤檢查apiServer上的變動(dòng)
- scheduler發(fā)現(xiàn)有新的pod要?jiǎng)?chuàng)建,開始為Pod分配主機(jī)并將結(jié)果信息更新至apiServer
- node節(jié)點(diǎn)上的kubelet發(fā)現(xiàn)有pod調(diào)度過來,嘗試調(diào)用docker啟動(dòng)容器,并將結(jié)果回送至apiServer
- apiServer將接收到的pod狀態(tài)信息存入etcd中
Pod終止過程
- 用戶向apiServer發(fā)送刪除pod命令
- apiServcer中的pod對象信息會(huì)隨著時(shí)間的推移而更新,在寬限期內(nèi)(默認(rèn)30s),pod被視為dead
- 將pod標(biāo)記為terminating狀態(tài)
- kubelet在監(jiān)控到pod對象轉(zhuǎn)為terminating狀態(tài)的同時(shí)啟動(dòng)pod關(guān)閉過程
- 端點(diǎn)控制器監(jiān)控到pod對象的關(guān)閉行為時(shí)將其從所有匹配到此端點(diǎn)的service資源的端點(diǎn)列表中移除
- 如果當(dāng)前pod對象定義了preStop鉤子處理器,則在其標(biāo)記為terminating后即會(huì)以同步的方式啟動(dòng)執(zhí)行
- pod對象中的容器進(jìn)程收到停止信號
- 寬限期結(jié)束后,若pod中還存在仍在運(yùn)行的進(jìn)程,那么pod對象會(huì)收到立即終止的信號
- kubelet請求apiServer將此pod資源的寬限期設(shè)置為0從而完成刪除操作,此時(shí)pod對于用戶已不可見
2、初始化容器
初始化容器是在pod的主容器啟動(dòng)之前要運(yùn)行的容器,主要做著容器啟動(dòng)前的前置工作。特點(diǎn)是:
- 初始化容器必須運(yùn)行完成直至結(jié)束,若某初始化容器運(yùn)行失敗,那么kubernetes需要重啟它直到成功完成
- 初始化容器必須
按照定義的順序執(zhí)行
,當(dāng)且僅當(dāng)前一個(gè)成功之后,后面的一個(gè)才能運(yùn)行
初始化容器的應(yīng)用場景有兩個(gè):
- 提供主容器鏡像中不具備的工具程序或自定義代碼
- 初始化容器要先于應(yīng)用容器串行啟動(dòng)并運(yùn)行完成,因此可用于
延后應(yīng)用容器的啟動(dòng)直至其依賴的條件得到滿足
案例:
假設(shè)要以主容器來運(yùn)行nginx,但是要求在運(yùn)行nginx之前先要能夠連接上mysql和redis所在服務(wù)器:
# 服務(wù)器的地址假設(shè)如下:
mysql: 192.168.90.14
redis: 192.168.90.15
創(chuàng)建pod-initcontainer.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
initContainers: # 注意初始化容器和主容器平級
- name: test-mysql
image: busybox:1.30 # 初始化容器的鏡像用busybox這個(gè)工具包
# ?。。。。?!
command: ['sh', '-c', 'until ping 192.168.90.14 -c 1 ; do echo waiting for mysql...; sleep 2; done;']
- name: test-redis
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.90.15 -c 1 ; do echo waiting for reids...; sleep 2; done;']
上面定義了兩個(gè)初始化容器,一個(gè)啟動(dòng)后ping MySQL,另一個(gè)ping Redis:
# 創(chuàng)建pod
[root@k8s-master01 ~] kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created
# 查看pod狀態(tài)
# 發(fā)現(xiàn)pod卡在初始化容器test-mysql創(chuàng)建后,后面的容器不會(huì)運(yùn)行
root@k8s-master01 ~] kubectl describe pod pod-initcontainer -n dev
........
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 49s default-scheduler Successfully assigned dev/pod-initcontainer to node1
Normal Pulled 48s kubelet, node1 Container image "busybox:1.30" already present on machine
Normal Created 48s kubelet, node1 Created container test-mysql
Normal Started 48s kubelet, node1 Started container test-mysql
-w動(dòng)態(tài)查看pod,和watch一個(gè)意思。
# 動(dòng)態(tài)查看pod
# init:0/2 即兩個(gè)初始化容器都卡住了
[root@k8s-master01 ~] kubectl get pods pod-initcontainer -n dev -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 15s
為當(dāng)前服務(wù)器新增兩個(gè)ip,使得能ping通那兩個(gè)IP,同時(shí)觀察pod的變化:
[root@k8s-master01 ~] ifconfig ens33:1 192.168.90.14 netmask 255.255.255.0 up
[root@k8s-master01 ~] ifconfig ens33:2 192.168.90.15 netmask 255.255.255.0 up
pod狀態(tài)的變化:
# 動(dòng)態(tài)查看pod
[root@k8s-master01 ~] kubectl get pods pod-initcontainer -n dev -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 15s
pod-initcontainer 0/1 Init:1/2 0 52s
pod-initcontainer 0/1 Init:1/2 0 53s
pod-initcontainer 0/1 PodInitializing 0 89s
pod-initcontainer 1/1 Running 0 90s
# 最后runing1/1即pod啟動(dòng)成功,副本一個(gè),也成功一個(gè)
3、鉤子函數(shù)
直白講就是一些特殊時(shí)機(jī),想Java靜態(tài)代碼塊一樣,是一種時(shí)機(jī)。在這里可以執(zhí)行用戶自己的行為代碼。鉤子函數(shù)有兩種:
-
post start
:即容器創(chuàng)建之后執(zhí)行,如果失敗了會(huì)重啟容器 -
pre stop
:即容器終止之前執(zhí)行,執(zhí)行完成之后容器將成功終止,在其執(zhí)行完之前會(huì)阻塞刪除容器的操作
鉤子處理器支持使用下面三種方式定義動(dòng)作:
- Exec命令:在容器內(nèi)執(zhí)行一次命令
……
lifecycle:
postStart:
exec:
command:
- cat
- /tmp/healthy
……
- TCPSocket:在當(dāng)前容器嘗試訪問指定的socket
……
lifecycle:
postStart:
tcpSocket:
port: 8080
……
- HTTPGet:在當(dāng)前容器中向某url發(fā)起http請求
……
lifecycle:
postStart:
httpGet:
path: / #URI地址
port: 80 #端口號
host: 192.168.5.3 #主機(jī)地址
scheme: HTTP #支持的協(xié)議,http或者h(yuǎn)ttps
……
以exec方式為例,創(chuàng)建pod-hook-exec.yaml文件:
apiVersion: v1
kind: Pod
metadata:
name: pod-hook-exec
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
lifecycle:
postStart:
exec: # 在容器啟動(dòng)的時(shí)候執(zhí)行一個(gè)命令,修改掉nginx的默認(rèn)首頁內(nèi)容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服務(wù)
command: ["/usr/sbin/nginx","-s","quit"]
啟動(dòng)pod
# 創(chuàng)建pod
[root@k8s-master01 ~] kubectl create -f pod-hook-exec.yaml
pod/pod-hook-exec created
# 查看pod,取podIP
[root@k8s-master01 ~] kubectl get pods pod-hook-exec -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-hook-exec 1/1 Running 0 29s 10.244.2.48 node2
# 訪問pod
[root@k8s-master01 ~] curl 10.244.2.48
postStart...
4、容器探測
直白講就是測測這個(gè)實(shí)例健康不,實(shí)例的狀態(tài)不符合預(yù)期,那么kubernetes就會(huì)把該問題實(shí)例" 摘除 ",不承擔(dān)業(yè)務(wù)流量。
探針有兩種:
-
liveness probes
:存活性探針,用于檢測應(yīng)用實(shí)例當(dāng)前是否處于正常運(yùn)行狀態(tài),不是則k8s會(huì)重啟容器 -
readiness probes
:就緒性探針,用于檢測應(yīng)用實(shí)例當(dāng)前是否可以接收請求,不能則k8s不會(huì)轉(zhuǎn)發(fā)流量
livenessProbe 決定是否重啟容器,readinessProbe 決定是否將請求轉(zhuǎn)發(fā)給容器
舉個(gè)形象例子:
公司來了一個(gè)高級后端開發(fā),剛來公司第一天,要熟悉代碼,裝環(huán)境等等。這個(gè)時(shí)候,他"存活",但不"就緒"!暫時(shí)不能給他分配工作任務(wù)。
這兩種探針都有三種實(shí)現(xiàn)方式:
- Exec命令:在容器內(nèi)執(zhí)行一次命令,如果命令執(zhí)行的退出碼為0,則認(rèn)為程序正常,否則不正常
……
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
……
- TCPSocket:將會(huì)嘗試訪問一個(gè)
用戶容器的端口
,如果能夠建立這條連接,則認(rèn)為程序正常,否則不正常
……
livenessProbe:
tcpSocket:
port: 8080
……
- HTTPGet:調(diào)用
容器內(nèi)
Web應(yīng)用的URL,如果返回的狀態(tài)碼在200和399之間,則認(rèn)為程序正常,否則不正常
……
livenessProbe:
httpGet:
path: / #URI地址
port: 80 #端口號
host: 127.0.0.1 #主機(jī)地址
scheme: HTTP #支持的協(xié)議,http或者h(yuǎn)ttps
……
探針效果演示:
方式一:liveness+Exec
創(chuàng)建pod-liveness-exec.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-exec
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
exec:
command: ["/bin/cat","/tmp/hello.txt"] # 執(zhí)行一個(gè)查看文件的命令
創(chuàng)建pod,發(fā)現(xiàn)它一直在重啟,狀態(tài)CrashLoopBackOff
# 創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
# 查看Pod詳情
[root@k8s-master01 ~] kubectl describe pods pod-liveness-exec -n dev
......
Normal Created 20s (x2 over 50s) kubelet, node1 Created container nginx
Normal Started 20s (x2 over 50s) kubelet, node1 Started container nginx
Normal Killing 20s kubelet, node1 Container nginx failed liveness probe, will be restarted
Warning Unhealthy 0s (x5 over 40s) kubelet, node1 Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory
# 觀察上面的信息就會(huì)發(fā)現(xiàn)nginx容器啟動(dòng)之后就進(jìn)行了健康檢查
# 檢查失敗之后,容器被kill掉,然后嘗試進(jìn)行重啟(這是重啟策略的作用,后面講解)
# 稍等一會(huì)之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~] kubectl get pods pod-liveness-exec -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 2 3m19s
# 當(dāng)然接下來,可以修改成一個(gè)存在的文件,cat指令執(zhí)行成功,則健康檢查通過,pod開始running
方式二:TCPSocket + liveness
創(chuàng)建pod-liveness-tcpsocket.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-tcpsocket
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
tcpSocket:
port: 8080 # 嘗試訪問用戶容器中的8080端口,而用戶容器我只開啟了80端口
創(chuàng)建pod,查看效果:
# 創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
# 查看Pod詳情
[root@k8s-master01 ~] kubectl describe pods pod-liveness-tcpsocket -n dev
......
Normal Scheduled 31s default-scheduler Successfully assigned dev/pod-liveness-tcpsocket to node2
Normal Pulled <invalid> kubelet, node2 Container image "nginx:1.17.1" already present on machine
Normal Created <invalid> kubelet, node2 Created container nginx
Normal Started <invalid> kubelet, node2 Started container nginx
Warning Unhealthy <invalid> (x2 over <invalid>) kubelet, node2 Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused
# 觀察上面的信息,發(fā)現(xiàn)嘗試訪問8080端口,但是失敗了
# 稍等一會(huì)之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~] kubectl get pods pod-liveness-tcpsocket -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 2 3m19s
# 當(dāng)然接下來,可以修改成一個(gè)可以訪問的端口,比如80,再試,結(jié)果就正常了......
注意TCPSocket是嘗試訪問一個(gè)用戶容器的端口
:
方式三:HTTPGet + liveness
創(chuàng)建pod-liveness-httpget.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet: # 其實(shí)就是訪問http://127.0.0.1:80/hello
scheme: HTTP #支持的協(xié)議,http或者h(yuǎn)ttps
port: 80 #端口號
path: /hello #URI地址
創(chuàng)建pod查看效果:
# 創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
# 查看Pod詳情
[root@k8s-master01 ~] kubectl describe pod pod-liveness-httpget -n dev
.......
Normal Pulled 6s (x3 over 64s) kubelet, node1 Container image "nginx:1.17.1" already present on machine
Normal Created 6s (x3 over 64s) kubelet, node1 Created container nginx
Normal Started 6s (x3 over 63s) kubelet, node1 Started container nginx
Warning Unhealthy 6s (x6 over 56s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 6s (x2 over 36s) kubelet, node1 Container nginx failed liveness probe, will be restarted
# 觀察上面信息,嘗試訪問路徑,但是未找到,出現(xiàn)404錯(cuò)誤
# 稍等一會(huì)之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~] kubectl get pod pod-liveness-httpget -n dev
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 5 3m17s
# 當(dāng)然接下來,可以修改成一個(gè)可以訪問的路徑path,比如/,再試,結(jié)果就正常了......
最后,關(guān)于容器探針的其他子屬性,如:容器啟動(dòng)后多久執(zhí)行一次探測
[root@k8s-master01 ~] kubectl explain pod.spec.containers.livenessProbe
FIELDS:
exec <Object>
tcpSocket <Object>
httpGet <Object>
initialDelaySeconds <integer> # 容器啟動(dòng)后等待多少秒執(zhí)行第一次探測
timeoutSeconds <integer> # 探測超時(shí)時(shí)間。默認(rèn)1秒,最小1秒
periodSeconds <integer> # 執(zhí)行探測的頻率。默認(rèn)是10秒,最小1秒
failureThreshold <integer> # 連續(xù)探測失敗多少次才被認(rèn)定為失敗。默認(rèn)是3。最小值是1
successThreshold <integer> # 連續(xù)探測成功多少次才被認(rèn)定為成功。默認(rèn)是1
舉例:
[root@k8s-master01 ~]# more pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-liveness-httpget
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /9527
initialDelaySeconds: 30 # 容器啟動(dòng)后30s開始探測
timeoutSeconds: 5 # 探測超時(shí)時(shí)間為5s
35秒后:
5、重啟策略
一旦容器探測出現(xiàn)了問題,kubernetes就會(huì)對容器所在的Pod進(jìn)行重啟,怎么重啟由重啟策略決定:
- Always :容器失效時(shí),自動(dòng)重啟該容器,這也是默認(rèn)值
- OnFailure : 容器終止運(yùn)行且退出碼不為0時(shí)重啟
- Never : 不論狀態(tài)為何,都不重啟該容器
注意:
重啟策略適用于pod對象中的所有容器,首次需要重啟的容器,將在其需要時(shí)立即進(jìn)行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時(shí)間后進(jìn)行
,且反復(fù)的重啟操作的延遲時(shí)長以此為10s、20s、40s、80s、160s和300s,300s是最大延遲時(shí)長,到300以后不再加時(shí)長。階梯增加重啟的前搖,不然本來就跑不起來,你頻繁重啟只會(huì)浪費(fèi)系統(tǒng)資源。
做驗(yàn)證:
apiVersion: v1
kind: Pod
metadata:
name: pod-restartpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
livenessProbe:
httpGet:
scheme: HTTP
port: 80
path: /hello
restartPolicy: Never # 設(shè)置重啟策略為Never
運(yùn)行pod:
# 創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-restartpolicy.yaml
pod/pod-restartpolicy created
# 查看Pod詳情,發(fā)現(xiàn)nginx容器失敗
[root@k8s-master01 ~] kubectl describe pods pod-restartpolicy -n dev
......
Warning Unhealthy 15s (x3 over 35s) kubelet, node1 Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 15s kubelet, node1 Container nginx failed liveness probe
# 多等一會(huì),再觀察pod的重啟次數(shù),發(fā)現(xiàn)一直是0,并未重啟
[root@k8s-master01 ~] kubectl get pods pod-restartpolicy -n dev
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Running 0 5min42s
四、Pod調(diào)度
在默認(rèn)情況下,一個(gè)Pod在哪個(gè)Node節(jié)點(diǎn)上運(yùn)行,是由Scheduler組件采用相應(yīng)的算法計(jì)算出來的,當(dāng)然也可進(jìn)行人為干預(yù),這和調(diào)度方式有關(guān):
- 自動(dòng)調(diào)度:運(yùn)行在哪個(gè)節(jié)點(diǎn)上完全由Scheduler經(jīng)過一系列的算法計(jì)算得出
- 定向調(diào)度:NodeName、NodeSelector
- 親和性調(diào)度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污點(diǎn)(容忍)調(diào)度:Taints、Toleration
1、定向調(diào)度
就是直接在pod的yaml文件中聲明一下去哪個(gè)節(jié)點(diǎn)(nodeName),或者去有哪個(gè)標(biāo)簽的節(jié)點(diǎn)(nodeSelector)。
這里直接跳過Scheduler的調(diào)度邏輯,強(qiáng)制調(diào)度,即使要調(diào)度的目標(biāo)Node不存在,也會(huì)向上面進(jìn)行調(diào)度,只不過pod運(yùn)行失敗而已。
# 創(chuàng)建一個(gè)pod-nodename.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 指定調(diào)度到node1節(jié)點(diǎn)上
# 集群中節(jié)點(diǎn)名稱可以kubectl get nodes
創(chuàng)建pod,查看效果:
#創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-nodename.yaml
pod/pod-nodename created
#查看Pod調(diào)度到NODE屬性,確實(shí)是調(diào)度到了node1節(jié)點(diǎn)上
[root@k8s-master01 ~] kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 1/1 Running 0 56s 10.244.1.87 node1 ......
# 接下來,刪除pod,修改nodeName的值為node3(并沒有node3節(jié)點(diǎn))
[root@k8s-master01 ~] kubectl delete -f pod-nodename.yaml
pod "pod-nodename" deleted
[root@k8s-master01 ~] vim pod-nodename.yaml
[root@k8s-master01 ~] kubectl create -f pod-nodename.yaml
pod/pod-nodename created
#再次查看,發(fā)現(xiàn)已經(jīng)向Node3節(jié)點(diǎn)調(diào)度,但是由于不存在node3節(jié)點(diǎn),所以pod無法正常運(yùn)行
[root@k8s-master01 ~] kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 0/1 Pending 0 6s <none> node3 ......
接下來測試NodeSelector,即將pod調(diào)度到添加了指定標(biāo)簽的node節(jié)點(diǎn)上。它是通過kubernetes的label-selector機(jī)制實(shí)現(xiàn)的,也就是說,在pod創(chuàng)建之前,會(huì)由scheduler使用MatchNodeSelector調(diào)度策略進(jìn)行l(wèi)abel匹配,找出目標(biāo)node,然后將pod調(diào)度到目標(biāo)節(jié)點(diǎn),該匹配規(guī)則是強(qiáng)制約束。
先為node節(jié)點(diǎn)添加標(biāo)簽
[root@k8s-master01 ~] kubectl label nodes node1 nodeenv=pro
node/node2 labeled
[root@k8s-master01 ~] kubectl label nodes node2 nodeenv=test
node/node2 labeled
創(chuàng)建一個(gè)pod-nodeselector.yaml文件:
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定調(diào)度到具有nodeenv=pro標(biāo)簽的節(jié)點(diǎn)上
創(chuàng)建pod,查看效果:
#創(chuàng)建Pod
[root@k8s-master01 ~] kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created
#查看Pod調(diào)度到NODE屬性,確實(shí)是調(diào)度到了node1節(jié)點(diǎn)上
[root@k8s-master01 ~] kubectl get pods pod-nodeselector -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeselector 1/1 Running 0 47s 10.244.1.87 node1 ......
# 接下來,刪除pod,修改nodeSelector的值為nodeenv: xxxx(不存在打有此標(biāo)簽的節(jié)點(diǎn))
[root@k8s-master01 ~] kubectl delete -f pod-nodeselector.yaml
pod "pod-nodeselector" deleted
[root@k8s-master01 ~] vim pod-nodeselector.yaml
[root@k8s-master01 ~] kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created
#再次查看,發(fā)現(xiàn)pod無法正常運(yùn)行,Node的值為none
[root@k8s-master01 ~] kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-nodeselector 0/1 Pending 0 2m20s <none> <none>
# 查看詳情,發(fā)現(xiàn)node selector匹配失敗的提示
[root@k8s-master01 ~] kubectl describe pods pod-nodeselector -n dev
.......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
2、親和性調(diào)度
形象的說,就是想去哪些節(jié)點(diǎn)、想去有哪個(gè)pod的節(jié)點(diǎn),不想去有哪個(gè)pod的節(jié)點(diǎn)。
就像分座位,想去哪幾個(gè)班、想去有某位同學(xué)的班級、不去有哪位同學(xué)的班級。
和定向調(diào)度的強(qiáng)制性相比,親和性調(diào)度更溫柔,使得調(diào)度更靈活了。親和性Affinity主要分三類:
- nodeAffinity(node親和性): 以node為目標(biāo),解決pod可以調(diào)度到哪些node的問題 ==>
想去哪幾個(gè)班
- podAffinity(pod親和性) : 以pod為目標(biāo),解決pod可以和哪些已存在的pod部署在同一個(gè)拓?fù)溆蛑械膯栴} ==>
想去有某位同學(xué)的班級
- podAntiAffinity(pod反親和性) : 以pod為目標(biāo),解決pod不能和哪些已存在pod部署在同一個(gè)拓?fù)溆蛑械膯栴} ==>
不去有哪位同學(xué)的班級
關(guān)于親和性和反親和性的使用場景:
親和性:
如果兩個(gè)pod需要頻繁交互,如mysql和tomcat,那就有必要利用親和性讓兩個(gè)應(yīng)用的盡可能的靠近(同一節(jié)點(diǎn)),這樣可以減少因網(wǎng)絡(luò)通信而帶來的性能損耗。
反親和性:
當(dāng)應(yīng)用的采用多副本部署時(shí),有必要采用反親和性讓各個(gè)應(yīng)用實(shí)例打散分布在各個(gè)node上,這樣可以提高服務(wù)的高可用性。(別一個(gè)節(jié)點(diǎn)掛了,某個(gè)服務(wù)的副本pod被一鍋端了。)
2.1 node親和性調(diào)度
查看NodeAffinity的配置項(xiàng):
[root@k8s-master01 ~]kubectl explain pod.spec.affinity.nodeAffinity
---------
requiredDuringSchedulingIgnoredDuringExecution Node節(jié)點(diǎn)必須滿足指定的所有規(guī)則才可以調(diào)度成功,相當(dāng)于硬限制
nodeSelectorTerms 節(jié)點(diǎn)選擇列表
matchFields 按節(jié)點(diǎn)字段列出的節(jié)點(diǎn)選擇器要求列表
matchExpressions 按節(jié)點(diǎn)標(biāo)簽列出的節(jié)點(diǎn)選擇器要求列表(推薦)
key 鍵
values 值
operat or 關(guān)系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
---------
preferredDuringSchedulingIgnoredDuringExecution 優(yōu)先調(diào)度到滿足指定的規(guī)則的Node,相當(dāng)于軟限制 (傾向)
preference 一個(gè)節(jié)點(diǎn)選擇器項(xiàng),與相應(yīng)的權(quán)重相關(guān)聯(lián)
matchFields 按節(jié)點(diǎn)字段列出的節(jié)點(diǎn)選擇器要求列表
matchExpressions 按節(jié)點(diǎn)標(biāo)簽列出的節(jié)點(diǎn)選擇器要求列表(推薦)
key 鍵
values 值
operator 關(guān)系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 傾向權(quán)重,在范圍1-100。
關(guān)于關(guān)系符的使用:
- matchExpressions:
- key: nodeenv # 匹配存在標(biāo)簽的key為nodeenv的節(jié)點(diǎn)
operator: Exists
- key: nodeenv # 匹配標(biāo)簽的key為nodeenv,且value是"xxx"或"yyy"的節(jié)點(diǎn)
operator: In
values: ["xxx","yyy"]
- key: nodeenv # 匹配標(biāo)簽的key為nodeenv,且value大于"xxx"的節(jié)點(diǎn)
operator: Gt
values: "xxx"
創(chuàng)建一個(gè)pod-nodeaffinity-required.yaml文件來舉例看看:
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設(shè)置
nodeAffinity: #設(shè)置node親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions: # 匹配key為nodeenv,值在["xxx","yyy"]中的標(biāo)簽的節(jié)點(diǎn)
- key: nodeenv
operator: In
values: ["xxx","yyy"]
啟動(dòng)pod,查看效果:
# 創(chuàng)建pod
[root@k8s-master01 ~] kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
# 查看pod狀態(tài) (運(yùn)行失?。?/span>
[root@k8s-master01 ~] kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeaffinity-required 0/1 Pending 0 16s <none> <none> ......
# 查看Pod的詳情,pending
# 發(fā)現(xiàn)調(diào)度失敗,提示node選擇失敗
[root@k8s-master01 ~] kubectl describe pod pod-nodeaffinity-required -n dev
......
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 3 node(s) didn't match node selector.
由于我只設(shè)置了硬限制,所以調(diào)度失敗,修改一下配置文件:
#停止pod
[root@k8s-master01 ~] kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deleted
# 修改文件,將values: ["xxx","yyy"]------> ["pro","yyy"]
[root@k8s-master01 ~] vim pod-nodeaffinity-required.yaml
# 再次啟動(dòng)
[root@k8s-master01 ~] kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
# 此時(shí)查看,發(fā)現(xiàn)調(diào)度成功,已經(jīng)將pod調(diào)度到了node1上
[root@k8s-master01 ~] kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodeaffinity-required 1/1 Running 0 11s 10.244.1.89 node1 ......
接下來測試使用節(jié)點(diǎn)親和性的軟限制,創(chuàng)建pod-nodeaffinity-preferred.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-preferred
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設(shè)置
nodeAffinity: #設(shè)置node親和性
preferredDuringSchedulingIgnoredDuringExecution: # 軟限制
- weight: 1
preference:
matchExpressions: # 優(yōu)先匹配env的值在["xxx","yyy"]中的標(biāo)簽(當(dāng)前集群沒有),匹配不到可考慮其他
- key: nodeenv
operator: In
values: ["xxx","yyy"]
創(chuàng)建pod查看:
# 創(chuàng)建pod
[root@k8s-master01 ~] kubectl create -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created
# 查看pod狀態(tài) (運(yùn)行成功)
[root@k8s-master01 ~] kubectl get pod pod-nodeaffinity-preferred -n dev
NAME READY STATUS RESTARTS AGE
pod-nodeaffinity-preferred 1/1 Running 0 40s
幾個(gè)注意點(diǎn):
NodeAffinity規(guī)則設(shè)置的注意事項(xiàng):
1 如果同時(shí)定義了nodeSelector和nodeAffinity,那么必須兩個(gè)條件都得到滿足,Pod才能運(yùn)行在指定的Node上
2 如果nodeAffinity指定了多個(gè)nodeSelectorTerms,那么只需要其中一個(gè)能夠匹配成功即可
3 如果一個(gè)nodeSelectorTerms中有多個(gè)matchExpressions ,則一個(gè)節(jié)點(diǎn)必須滿足所有的才能匹配成功
4 如果一個(gè)pod所在的Node在Pod運(yùn)行期間其標(biāo)簽發(fā)生了改變,不再符合該P(yáng)od的節(jié)點(diǎn)親和性需求,則系統(tǒng)將忽略此變化,不會(huì)二次調(diào)度
2.2 pod親和性調(diào)度
讓新創(chuàng)建的Pod跟參照pod在一個(gè)區(qū)域的功能,去有某同學(xué)的那個(gè)班級。
kubectl explain pod.spec.affinity.podAffinity
----------
requiredDuringSchedulingIgnoredDuringExecution 硬限制
namespaces 指定參照pod的namespace
topologyKey 指定調(diào)度作用域
labelSelector 標(biāo)簽選擇器
matchExpressions 按節(jié)點(diǎn)標(biāo)簽列出的節(jié)點(diǎn)選擇器要求列表(推薦)
key 鍵
values 值
operator 關(guān)系符 支持In, NotIn, Exists, DoesNotExist.
matchLabels 指多個(gè)matchExpressions映射的內(nèi)容
----------
preferredDuringSchedulingIgnoredDuringExecution 軟限制
podAffinityTerm 選項(xiàng)
namespaces
topologyKey
labelSelector
matchExpressions
key 鍵
values 值
operator
matchLabels
weight 傾向權(quán)重,在范圍1-100
topologyKey指定調(diào)度作用域,作用域即范圍,調(diào)度到和參照pod同一節(jié)點(diǎn)、同一網(wǎng)段還是同樣的操作系統(tǒng)里。(和你同學(xué)同一個(gè)班級?同一張課桌?同一個(gè)學(xué)校?
)
topologyKey取值:
kubernetes.io/hostname,那就是以Node節(jié)點(diǎn)為區(qū)分范圍
beta.kubernetes.io/os,則以Node節(jié)點(diǎn)的操作系統(tǒng)類型來區(qū)分
舉個(gè)例子說明pod親和性,先創(chuàng)建一個(gè)參照pod,并定向調(diào)度到node1
# pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
podenv: pro #設(shè)置標(biāo)簽
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 將目標(biāo)pod名確指定到node1上
# 啟動(dòng)目標(biāo)pod
[root@k8s-master01 ~] kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
# 查看pod狀況
[root@k8s-master01 ~] kubectl get pods pod-podaffinity-target -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 4s
創(chuàng)建待調(diào)度的pod,配置文件pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設(shè)置
podAffinity: #設(shè)置pod親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的標(biāo)簽
- key: podenv
operator: In
values: ["xxx","yyy"]
# 如果找到這種pod了,那就把這個(gè)pod調(diào)度到和匹配成功的pod的同一幾點(diǎn)上
topologyKey: kubernetes.io/hostname
上面配置表達(dá)的意思是:新Pod必須要與擁有標(biāo)簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上,硬限制。
# 啟動(dòng)pod
[root@k8s-master01 ~] kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
# 查看pod狀態(tài),發(fā)現(xiàn)未運(yùn)行
[root@k8s-master01 ~] kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 0/1 Pending 0 9s
# 查看詳細(xì)信息
[root@k8s-master01 ~] kubectl describe pods pod-podaffinity-required -n dev
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are available: 2 node(s) didn't match pod affinity rules, 1 node(s) had taints that the pod didn't tolerate.
調(diào)度失敗了,稍微修改一下匹配的label:
# 接下來修改 values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必須要與擁有標(biāo)簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
[root@k8s-master01 ~] vim pod-podaffinity-required.yaml
# 然后重新創(chuàng)建pod,查看效果
[root@k8s-master01 ~] kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" de leted
[root@k8s-master01 ~] kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
# 發(fā)現(xiàn)此時(shí)Pod運(yùn)行正常
[root@k8s-master01 ~] kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE LABELS
pod-podaffinity-required 1/1 Running 0 6s <none>
2.3 pod反親和性調(diào)度
PodAntiAffinity主要實(shí)現(xiàn)以運(yùn)行的Pod為參照,讓新創(chuàng)建的Pod跟參照pod不在一個(gè)區(qū)域中的功能。(不和誰一個(gè)班
)
舉個(gè)例子說明,繼續(xù)使用pod親和性中建的pod:
[root@k8s-master01 ~] kubectl get pods -n dev -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE LABELS
pod-podaffinity-required 1/1 Running 0 3m29s 10.244.1.38 node1 <none>
pod-podaffinity-target 1/1 Running 0 9m25s 10.244.1.37 node1 podenv=pro
創(chuàng)建pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #親和性設(shè)置
podAntiAffinity: #設(shè)置pod反親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的標(biāo)簽
- key: podenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
上面配置表達(dá)的意思是:新Pod必須要與擁有標(biāo)簽nodeenv=pro的pod不在同一Node上
:
# 創(chuàng)建pod
[root@k8s-master01 ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
# 查看pod
# 發(fā)現(xiàn)調(diào)度到了node2上
[root@k8s-master01 ~] kubectl get pods pod-podantiaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ..
pod-podantiaffinity-required 1/1 Running 0 30s 10.244.1.96 node2 ..
3、污點(diǎn)和容忍調(diào)度
前面的兩種調(diào)度都是在pod的角度,來說pod要去哪兒,不要去哪兒。接下來站在node的角度,來說允許哪些pod過來,不允許哪些pod過來。(前面是學(xué)生要轉(zhuǎn)校到哪個(gè)學(xué)校,現(xiàn)在是校長貼出告示說明不要哪些學(xué)生
)
3.1 污點(diǎn)(Taints)
Node被設(shè)置上污點(diǎn)之后就和Pod之間存在了一種相斥的關(guān)系,進(jìn)而拒絕Pod調(diào)度進(jìn)來,甚至可以將已經(jīng)存在的Pod驅(qū)逐出去。
污點(diǎn)的格式為:key=value:effect,
key和value是污點(diǎn)的標(biāo)簽,effect描述污點(diǎn)的作用,支持如下三個(gè)選項(xiàng):
- PreferNoSchedule:kubernetes將盡量避免把Pod調(diào)度到具有該污點(diǎn)的Node上,除非沒有其他節(jié)點(diǎn)可調(diào)度
- NoSchedule:kubernetes將不會(huì)把Pod調(diào)度到具有該污點(diǎn)的Node上,但不會(huì)影響當(dāng)前Node上已存在的Pod
- NoExecute:kubernetes將不會(huì)把Pod調(diào)度到具有該污點(diǎn)的Node上,同時(shí)也會(huì)將Node上已存在的Pod驅(qū)離
設(shè)置和去除污點(diǎn):
# 設(shè)置污點(diǎn)
kubectl taint nodes node1 key=value:effect
# 去除污點(diǎn)
kubectl taint nodes node1 key:effect-
# 去除所有污點(diǎn)
kubectl taint nodes node1 key-
接下來舉例子演示效果,先停掉node2節(jié)點(diǎn)(關(guān)機(jī)),只留master和node1,先看PreferNoSchedule:
# 為node1設(shè)置污點(diǎn)(PreferNoSchedule)
[root@k8s-master01 ~] kubectl taint nodes node1 tag=code9527:PreferNoSchedule
# 創(chuàng)建pod1
[root@k8s-master01 ~] kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~] kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 node1
僅有一個(gè)工作節(jié)點(diǎn),且該節(jié)點(diǎn)設(shè)置了污點(diǎn)PreferNoSchedule,但仍調(diào)度成功。
再看NoSchedule:
# 為node1設(shè)置污點(diǎn)(取消PreferNoSchedule,設(shè)置NoSchedule)
[root@k8s-master01 ~] kubectl taint nodes node1 tag:PreferNoSchedule-
[root@k8s-master01 ~] kubectl taint nodes node1 tag=code9527:NoSchedule
創(chuàng)建pod,發(fā)現(xiàn)pending,且調(diào)度失?。?/p>
# 創(chuàng)建pod2
[root@k8s-master01 ~] kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~] kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1-7665f7fd85-574h4 1/1 Running 0 2m24s 10.244.1.59 node1
taint2-544694789-6zmlf 0/1 Pending 0 21s <none> <none>
再看NoExecute:
# 為node1設(shè)置污點(diǎn)(取消NoSchedule,設(shè)置NoExecute)
[root@k8s-master01 ~] kubectl taint nodes node1 tag:NoSchedule-
[root@k8s-master01 ~] kubectl taint nodes node1 tag=code9527:NoExecute
可以看到已有的pod也被驅(qū)逐了:
# 創(chuàng)建pod3
[root@k8s-master01 ~] kubectl run taint3 --image=nginx:1.17.1 -n dev
# IP全為<none>
[root@k8s-master01 ~] kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
taint1-7665f7fd85-htkmp 0/1 Pending 0 35s <none> <none> <none>
taint2-544694789-bn7wb 0/1 Pending 0 35s <none> <none> <none>
taint3-6d78dbd749-tktkq 0/1 Pending 0 6s <none> <none> <none>
小提示:
使用kubeadm搭建的集群,默認(rèn)就會(huì)給master節(jié)點(diǎn)添加一個(gè)污點(diǎn)標(biāo)記,所以pod就不會(huì)調(diào)度到master節(jié)點(diǎn)上.
kubectl describe node master
# 查看一下
3.2 容忍(Toleration)
在node上添加污點(diǎn)用于拒絕pod調(diào)度上來,但是如果就是想將一個(gè)pod調(diào)度到一個(gè)有污點(diǎn)的node上去,這時(shí)就要使用到容忍。
污點(diǎn)就是拒絕,容忍就是忽略,Node通過污點(diǎn)拒絕pod調(diào)度上去,Pod通過容忍忽略拒絕
形象的說,污點(diǎn)就是,相親時(shí),你告訴相親對象,你這人脾氣大,對方說:沒事,我能接收。于是你們在一起了
。
創(chuàng)建pod-toleration.yaml做演示:
apiVersion: v1
kind: Pod
metadata:
name: pod-toleration
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
tolerations: # 添加容忍
- key: "tag" # 要容忍的污點(diǎn)的key
operator: "Equal" # 操作符
value: "code9527" # 容忍的污點(diǎn)的value
effect: "NoExecute" # 添加容忍的規(guī)則,這里必須和標(biāo)記的污點(diǎn)規(guī)則相同
上一小節(jié),已經(jīng)在node1節(jié)點(diǎn)上打上了NoExecute的污點(diǎn),此時(shí)pod是調(diào)度不上去的。啟動(dòng)pod看一下加了容忍以后的效果:
# 添加容忍之前的pod
[root@k8s-master01 ~] kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 0/1 Pending 0 3s <none> <none> <none>
# 添加容忍之后的pod
[root@k8s-master01 ~] kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED
pod-toleration 1/1 Running 0 3s 10.244.1.62 node1 <none>
最后看一下容忍的所有子屬性:
[root@k8s-master01 ~] kubectl explain pod.spec.tolerations
......
FIELDS:
key # 對應(yīng)著要容忍的污點(diǎn)的鍵,空意味著匹配所有的鍵
value # 對應(yīng)著要容忍的污點(diǎn)的值
operator # key-value的運(yùn)算符,支持Equal和Exists(默認(rèn)),Exists是對鍵進(jìn)行操作
effect # 對應(yīng)污點(diǎn)的effect,空意味著匹配所有影響
tolerationSeconds # 容忍時(shí)間,
# 當(dāng)effect為NoExecute時(shí)生效
# 表示pod在Node上的停留時(shí)間
注意最后這個(gè)tolerationSeconds容忍時(shí)間,NoExecute時(shí)生效,NoExcute本來就禁止往上調(diào)度。tolerationSeconds就是忍多久以后跑路。文章來源:http://www.zghlxwxcb.cn/news/detail-441508.html
到此,Pod篇結(jié)束,下來是各種Pod控制器?。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-441508.html
到了這里,關(guān)于【K8s】Pod一文詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!