文章目錄
- Kubernetes之Pod詳解
- 一、Pod介紹
- pod結構
- pod定義
- 二、Pod配置
- pod基本配置
- 鏡像拉取策略
- 啟動命令
- 環境變量
- 端口設置
- 資源配額
- 三、Pod生命周期
- 創建和終止
- 初始化容器
- 鉤子函數
- 容器探測
- 重啟策略
- 四、Pod調度
- 定向調度
- NodeName
- NodeSelector
- 親和性調度
- NodeAffinity
- PodAffinity
- PodAntiAffinity
- 五、污點和容忍
- 污點(Taints)
- 容忍(Toleration)
Kubernetes之Pod詳解
一、Pod介紹
pod結構
Pod 是 kubernetes 中最小的資源管理組件,Pod 也是最小化運行容器化應用的資源對象。一個 Pod 代表著集群中運行的一個進程。kubernetes 中其他大多數組件都是圍繞著 Pod 來進行支撐和擴展 Pod 功能的,例如用于管理 Pod 運行的 StatefulSet 和 Deployment 等控制器對象,用于暴露 Pod 應用的 Service 和 Ingress 對象,為 Pod 提供存儲的 PersistentVolume 存儲資源對象等。
每個Pod中都可以包含一個或者多個容器,這些容器可以分為兩類:
-
用戶程序所在的容器,數量可多可少
-
Pause容器,這是每個Pod都會有的一個根容器,它的作用有兩個:
可以以它為依據,評估整個Pod的健康狀態
可以在根容器上設置Ip地址,其它容器都此Ip(Pod IP),以實現Pod內部的網路通信
這里是Pod內部的通訊,Pod的之間的通訊采用虛擬二層網絡技術來實現,我們當前環境用的是Flannel
pod定義
下面是Pod的資源清單:
apiVersion: v1 #必選,版本號,例如v1
kind: Pod #必選,資源類型,例如 Pod
metadata: #必選,元數據name: string #必選,Pod名稱namespace: string #Pod所屬的命名空間,默認為"default"labels: #自定義標簽列表- name: string
spec: #必選,Pod中容器的詳細定義containers: #必選,Pod中容器列表- name: string #必選,容器名稱image: string #必選,容器的鏡像名稱imagePullPolicy: [ Always|Never|IfNotPresent ] #獲取鏡像的策略 command: [string] #容器的啟動命令列表,如不指定,使用打包時使用的啟動命令args: [string] #容器的啟動命令參數列表workingDir: string #容器的工作目錄volumeMounts: #掛載到容器內部的存儲卷配置- name: string #引用pod定義的共享存儲卷的名稱,需用volumes[]部分定義的的卷名mountPath: string #存儲卷在容器內mount的絕對路徑,應少于512字符readOnly: boolean #是否為只讀模式ports: #需要暴露的端口庫號列表- name: string #端口的名稱containerPort: int #容器需要監聽的端口號hostPort: int #容器所在主機需要監聽的端口號,默認與Container相同protocol: string #端口協議,支持TCP和UDP,默認TCPenv: #容器運行前需設置的環境變量列表- name: string #環境變量名稱value: string #環境變量的值resources: #資源限制和請求的設置limits: #資源限制的設置cpu: string #Cpu的限制,單位為core數,將用于docker run --cpu-shares參數memory: string #內存限制,單位可以為Mib/Gib,將用于docker run --memory參數requests: #資源請求的設置cpu: string #Cpu請求,容器啟動的初始可用數量memory: string #內存請求,容器啟動的初始可用數量lifecycle: #生命周期鉤子postStart: #容器啟動后立即執行此鉤子,如果執行失敗,會根據重啟策略進行重啟preStop: #容器終止前執行此鉤子,無論結果如何,容器都會終止livenessProbe: #對Pod內各容器健康檢查的設置,當探測無響應幾次后將自動重啟該容器exec: #對Pod容器內檢查方式設置為exec方式command: [string] #exec方式需要制定的命令或腳本httpGet: #對Pod內個容器健康檢查方法設置為HttpGet,需要制定Path、portpath: stringport: numberhost: stringscheme: stringHttpHeaders:- name: stringvalue: stringtcpSocket: #對Pod內個容器健康檢查方式設置為tcpSocket方式port: numberinitialDelaySeconds: 0 #容器啟動完成后首次探測的時間,單位為秒timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,默認1秒periodSeconds: 0 #對容器監控檢查的定期探測時間設置,單位秒,默認10秒一次successThreshold: 0failureThreshold: 0securityContext:privileged: falserestartPolicy: [Always | Never | OnFailure] #Pod的重啟策略nodeName: <string> #設置NodeName表示將該Pod調度到指定到名稱的node節點上nodeSelector: obeject #設置NodeSelector表示將該Pod調度到包含這個label的node上imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定- name: stringhostNetwork: false #是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡volumes: #在該pod上定義共享存儲卷列表- name: string #共享存儲卷名稱 (volumes類型有很多種)emptyDir: {} #類型為emtyDir的存儲卷,與Pod同生命周期的一個臨時目錄。為空值hostPath: string #類型為hostPath的存儲卷,表示掛載Pod所在宿主機的目錄path: string #Pod所在宿主機的目錄,將被用于同期中mount的目錄secret: #類型為secret的存儲卷,掛載集群與定義的secret對象到容器內部scretname: string items: - key: stringpath: stringconfigMap: #類型為configMap的存儲卷,掛載預定義的configMap對象到容器內部name: stringitems:- key: stringpath: string
#小提示:
# 在這里,可通過一個命令來查看每種資源的可配置項
# kubectl explain 資源類型 查看某種資源可以配置的一級屬性
# kubectl explain 資源類型.屬性 查看屬性的子屬性[root@master ~]# kubectl explain pod
KIND: Pod
VERSION: v1
FIELDS:apiVersion <string>kind <string>metadata <Object>spec <Object>status <Object>[root@master ~]# 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中基本所有資源的一級屬性都是一樣的,主要包含5部分:
- apiVersion 版本,由kubernetes內部定義,版本號必須可以用 kubectl api-versions 查詢到
- kind 類型,由kubernetes內部定義,版本號必須可以用 kubectl api-resources 查詢到
- metadata 元數據,主要是資源標識和說明,常用的有name、namespace、labels等
- spec 描述,這是配置中最重要的一部分,里面是對各種資源配置的詳細描述
- status 狀態信息,里面的內容不需要定義,由kubernetes自動生成
在上面的屬性中,spec是接下來研究的重點,繼續看下它的常見子屬性:
- containers <[]Object> 容器列表,用于定義容器的詳細信息
- nodeName 根據nodeName的值將pod調度到指定的Node節點上
- nodeSelector 根據NodeSelector中定義的信息選擇將該Pod調度到包含這些label的Node 上
- hostNetwork 是否使用主機網絡模式,默認為false,如果設置為true,表示使用宿主機網絡
- volumes <[]Object> 存儲卷,用于定義Pod上面掛在的存儲信息
- restartPolicy 重啟策略,表示Pod在遇到故障的時候的處理策略
二、Pod配置
這一節主要是pod.spec.containers
的屬性,這也是pod配置中最為關鍵的一項配置。
[root@master ~]# kubectl explain pod.spec.containers
KIND: Pod
VERSION: v1
RESOURCE: containers <[]Object> # 數組,代表可以有多個容器
FIELDS:name <string> # 容器名稱image <string> # 容器需要的鏡像地址imagePullPolicy <string> # 鏡像拉取策略 command <[]string> # 容器的啟動命令列表,如不指定,使用打包時使用的啟動命令args <[]string> # 容器的啟動命令需要的參數列表env <[]Object> # 容器環境變量的配置ports <[]Object> # 容器需要暴露的端口號列表resources <Object> # 資源限制和資源請求的設置
先創建一個名稱空間用于實驗使用
//創建一個名為lcwanf的名稱空間
[root@master ~]# kubectl create namespace lcwanf
namespace/lcwanf created
[root@master ~]# kubectl get namespace
NAME STATUS AGE
default Active 4d22h
kube-flannel Active 4d21h
kube-node-lease Active 4d22h
kube-public Active 4d22h
kube-system Active 4d22h
lcwanf Active 3s #用于實驗用的名稱空間
[root@master ~]#
pod基本配置
創建一個pod-httpd.yaml文件,內容如下
//編寫資源文件
[root@master ~]# vim pod-httpd1.yaml
[root@master ~]# cat pod-httpd1.yaml
apiVersion: v1
kind: Pod
metadata:name: httpd1namespace: lcwanflabels:user: wanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.2- name: tomcatimage: lcwanf/tomcat-meinv:v1.0
[root@master ~]#
上面定義了一個簡單的pod配置,里面有兩個容器:
- httpd:用的是我自己制作的鏡像,上傳在我的docker官方鏡像倉庫
- tomcat:也是用的我自己制作的鏡像,上傳在我的docker官方鏡像倉庫
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-httpd1.yaml
pod/httpd1 created
[root@master ~]# //查看pod狀況
# READY 2/2 : 表示當前Pod中有2個容器,2個都準備就緒
# RESTARTS : 重啟次數,此時為0;如果容器故障了,Pod會一直重啟故障容器試圖恢復它
[root@master ~]# kubectl get -f pod-httpd1.yaml
NAME READY STATUS RESTARTS AGE
httpd1 2/2 Running 0 2m
[root@master ~]# # 可以通過describe查看內部的詳情
//查看pod詳細信息
[root@master ~]# kubectl describe pod httpd1 -n lcwanf
(省略)
鏡像拉取策略
imagePullPolicy,用于設置鏡像拉取策略,kubernetes支持配置三種拉取策略:
- Always:總是從遠程倉庫拉取鏡像(一直遠程下載)
- IfNotPresent:本地有則使用本地鏡像,本地沒有則從遠程倉庫拉取鏡像(本地有就本地 本地沒遠程下載)
- Never:只使用本地鏡像,從不去遠程倉庫拉取,本地沒有就報錯 (一直使用本地)
默認值說明:
如果鏡像tag為具體版本號, 默認策略是:IfNotPresent
如果鏡像tag為:latest(最終版本) ,默認策略是always
創建應該pod-imagepullpolicy.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-imagepullpolicy.yaml
[root@master ~]# cat pod-imagepullpolicy.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.2imagePullPolicy: Never # 用于設置鏡像拉取策略- name: tomcatimage: lcwanf/tomcat-meinv:v1.0
[root@master ~]#
上面定義了一個新的pod,其中httpd這個容器的鏡像拉取規則是Never(只使用本地鏡像,從不去遠程倉庫拉取,本地沒有就報錯 :一直使用本地)
創建資源并查看
//創建之前,先修改pod-imagepullpolicy.yaml文件里面的這個內容把httpd這個容器的鏡像的版本從lcwanf/apache:v0.2改為lcwanf/apache:v0.1因為前面的操作拉取過lcwanf/apache:v0.2這個版本的鏡像,所以本地是有這個鏡像的換成lcwanf/apache:v0.1后,本地肯定是沒有這個鏡像的,再加上配置了拉取鏡像規則為Never,只使用本地鏡像,所以這個httpd的容器是運行不起來的
[root@master ~]# vim pod-imagepullpolicy.yaml
[root@master ~]# cat pod-imagepullpolicy.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1 //版本已經修改imagePullPolicy: Never- name: tomcatimage: lcwanf/tomcat-meinv:v1.0
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created//查看資源
--READY為1/2,證明確實有一個容器運行失敗
[root@master ~]# kubectl get -f pod-imagepullpolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-imagepullpolicy 1/2 ErrImageNeverPull 0 3s//查看詳細信息,發現確實是httpd這個容器運行失敗報錯顯示:容器鏡像“lcwanf/apache:v0.1”不存在,pull策略為Never
[root@master ~]# kubectl describe -f pod-imagepullpolicy.yaml
(省略)
Warning(省略)Container image "lcwanf/apache:v0.1" is not present with pull policy of Never//刪除創建的資源
[root@master ~]# kubectl delete -f pod-imagepullpolicy.yaml
pod "pod-imagepullpolicy" deleted
[root@master ~]# //修改拉取鏡像策略為Always,我的docker官方鏡像倉庫里面是有lcwanf/apache:v0.1這個版本的所以會去自動拉取
[root@master ~]# vim pod-imagepullpolicy.yaml
[root@master ~]# cat pod-imagepullpolicy.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1imagePullPolicy: Always #改為Always:總是從遠程倉庫拉取鏡像- name: tomcatimage: lcwanf/tomcat-meinv:v1.0
[root@master ~]# //重新創建資源
[root@master ~]# kubectl apply -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created
[root@master ~]# //查看資源,正常運行
[root@master ~]# kubectl get -f pod-imagepullpolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-imagepullpolicy 2/2 Running 0 23s
[root@master ~]#
啟動命令
command,用于在pod中的容器初始化完畢之后運行一個命令。
創建一個pod-command.yaml文件,不定義啟動命令,內容如下:
//編寫資源文件,沒有定義啟動命令
[root@master ~]# vim pod-command.yaml
[root@master ~]# cat pod-command.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-commandnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1- name: busyboximage: busybox:1.30
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-command.yaml
pod/pod-command created//查看資源,會發現一直有一個容器運行失敗,通過詳細查看發現是busybox這個容器有問題
[root@master ~]# kubectl get -f pod-command.yaml
NAME READY STATUS RESTARTS AGE
pod-command 1/2 CrashLoopBackOff 2 (12s ago) 40s
[root@master ~]# //刪除此資源
是什么原因導致這個容器的故障呢?原來busybox并不是一個程序,而是類似于一個工具類的集合,kubernetes集群啟動管理后,它會自動關閉。解決方法就是讓其一直在運行,這就用到了command配置。
修改pod-command.yaml文件,給busybox容器定義啟動命令,內容如下:
//修改資源文件
[root@master ~]# vim pod-command.yaml
[root@master ~]# cat pod-command.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-commandnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
[root@master ~]#
解釋下上面命令的意思:
“/bin/sh”,“-c”, 使用sh執行命令
touch /tmp/hello.txt; 創建一個/tmp/hello.txt 文件
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中寫入當前時間
//創建資源
[root@master ~]# kubectl apply -f pod-command.yaml
pod/pod-command created
[root@master ~]# //查看資源,兩個容器都正常運行了
[root@master ~]# kubectl get -f pod-command.yaml
NAME READY STATUS RESTARTS AGE
pod-command 2/2 Running 0 25s
[root@master ~]# //進入busybox這個容器里面去,查看命令運行情況
# 補充一個命令: kubectl exec pod名稱 -n 命名空間 -it -c 容器名稱 /bin/sh 在容器內部執行命令
# 使用這個命令就可以進入某個容器的內部,然后進行相關操作了
# 比如,可以查看txt文件的內容
[root@master ~]# kubectl exec pod-command -n lcwanf -it -c busybox /bin/sh / -- tail -f /tmp/hello.txt
08:07:50
08:07:53
08:07:56
08:07:59
08:08:02
08:08:05
08:08:08
08:08:11
08:08:14
08:08:17
(省略)
//命令一直在運行
特別說明:通過上面發現command已經可以完成啟動命令和傳遞參數的功能,為什么這里還要提供一個args選項,用于傳遞參數呢?這其實跟docker有點關系,kubernetes中的command、args兩項其實是實現覆蓋Dockerfile中ENTRYPOINT的功能。1 如果command和args均沒有寫,那么用Dockerfile的配置。2 如果command寫了,但args沒有寫,那么Dockerfile默認的配置會被忽略,執行輸入的command3 如果command沒寫,但args寫了,那么Dockerfile中配置的ENTRYPOINT的命令會被執行,使用當前args的參數4 如果command和args都寫了,那么Dockerfile的配置被忽略,執行command并追加上args參數
環境變量
env,環境變量,用于在pod中的容器設置環境變量。
創建一個pod-env.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-env.yaml
[root@master ~]# cat pod-env.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-envnamespace: lcwanf
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]env: # 設置環境變量列表- name: "username"value: "admin"- name: "password"value: "123456"
[root@master ~]#
定義了兩個環境變量:
username:admin
password:123456
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-env.yaml
pod/pod-env created
[root@master ~]# //查看資源
[root@master ~]# kubectl get -f pod-env.yaml
NAME READY STATUS RESTARTS AGE
pod-env 1/1 Running 0 24s
[root@master ~]# //進入容器,輸出環境變量
[root@master ~]# kubectl exec pod-env -n lcwanf -c busybox -it /bin/sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # echo $username
admin
/ # echo $password
123456
/ # exit
[root@master ~]# //注意,這里有一個警告信息kubectl exec [POD] [COMMAND]...,意思是此命令語法將在以后的版本中刪除,現在不影響使用
這種方式不是很推薦,推薦將這些配置單獨存儲在配置文件中,這種方式將在后面介紹。
端口設置
容器的端口設置,也就是containers的ports選項。
[root@master ~]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:name <string> # 端口名稱,如果指定,必須保證name在pod中是唯一的 containerPort<integer> # 容器要監聽的端口(0<x<65536)hostPort <integer> # 容器要在主機上公開的端口,如果設置,主機上只能運行容器的一個副本(一般省略) hostIP <string> # 要將外部端口綁定到的主機IP(一般省略)protocol <string> # 端口協議。必須是UDP、TCP或SCTP。默認為“TCP”。
創建一個pod-ports.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-ports.yaml
[root@master ~]# cat pod-ports.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-portsnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports: # 設置容器暴露的端口列表- name: httpd-portcontainerPort: 80protocol: TCP
[root@master ~]#
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-ports.yaml
pod/pod-ports created
[root@master ~]# //查看
[root@master ~]# kubectl get -f pod-ports.yaml
NAME READY STATUS RESTARTS AGE
pod-ports 1/1 Running 0 17s
[root@master ~]# //以yaml格式查看
# 在下面可以明顯看到配置信息
[root@master ~]# kubectl get pod pod-ports -n lcwanf -o yaml
(省略)
spec:containers:- image: lcwanf/apache:v0.1imagePullPolicy: IfNotPresentname: httpdports:- containerPort: 80name: httpd-portprotocol: TCP
(省略)
訪問容器中的程序需要使用的是Podip:containerPort
資源配額
容器中的程序要運行,肯定是要占用一定資源的,比如cpu和內存等,如果不對某個容器的資源做限制,那么它就可能吃掉大量資源,導致其它容器無法運行。針對這種情況,kubernetes提供了對內存和cpu的資源進行配額的機制,這種機制主要通過resources選項實現,他有兩個子選項:
- limits:用于限制運行時容器的最大占用資源,當容器占用資源超過limits時會被終止,并進行重啟
- requests :用于設置容器需要的最小資源,如果環境資源不夠,容器將無法啟動
可以通過上面兩個選項設置資源的上下限。
創建一個pod-resources.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-resources.yaml
[root@master ~]# cat pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-resourcesnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1resources: # 資源配額limits: # 限制資源(上限)cpu: "2" # CPU限制,單位是core數memory: "10Gi" # 內存限制requests: # 請求資源(下限)cpu: "1" # CPU限制,單位是core數memory: "10Mi" # 內存限制
[root@master ~]#
在這對cpu和memory的單位做一個說明:
cpu:core數,可以為整數或小數
memory: 內存大小,可以使用Gi、Mi、G、M等形式
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-resources.yaml
pod/pod-resources created
[root@master ~]# //查看,運行是成功的
[root@master ~]# kubectl get -f pod-resources.yaml
NAME READY STATUS RESTARTS AGE
pod-resources 1/1 Running 0 18s
[root@master ~]# //編輯pod,修改resources.requests.memory的值為10Gi(內存下限為10Gi)我這臺實驗用的虛擬機的內存為4G,肯定不滿足,所以容器肯定起不來//先刪除資源
[root@master ~]# kubectl delete -f pod-resources.yaml
pod "pod-resources" deleted//修改資源文件的resources.requests.memory的值為10Gi
[root@master ~]# vim pod-resources.yaml
[root@master ~]# cat pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-resourcesnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1resources: limits: cpu: "2" memory: "10Gi" requests: cpu: "1" memory: "10Gi" #修改為10Gi
[root@master ~]# //再次創建資源
[root@master ~]# kubectl apply -f pod-resources.yaml
pod/pod-resources created
[root@master ~]# //查看容器,是Pending狀態,無法正常運行
[root@master ~]# kubectl get -f pod-resources.yaml
NAME READY STATUS RESTARTS AGE
pod-resources 0/1 Pending 0 14s
[root@master ~]# //查看pod詳情會發現,如下提示
[root@master ~]# kubectl describe -f pod-resources.yaml
(省略)
Warning FailedScheduling 60s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Insufficient memory. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod..
三、Pod生命周期
我們一般將pod對象從創建至終的這段時間范圍稱為pod的生命周期,它主要包含下面的過程:
- pod創建過程
- 運行初始化容器(init container)過程
- 運行主容器(main container)
- 容器啟動后鉤子(post start)、容器終止前鉤子(pre stop)
- 容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
- pod終止過程
在整個生命周期中,Pod會出現5種狀態(相位),分別如下:
- 掛起(Pending):apiserver已經創建了pod資源對象,但它尚未被調度完成或者仍處于下載鏡像的過程中
- 運行中(Running):pod已經被調度至某節點,并且所有容器都已經被kubelet創建完成
- 成功(Succeeded):pod中的所有容器都已經成功終止并且不會被重啟
- 失敗(Failed):所有容器都已經終止,但至少有一個容器終止失敗,即容器返回了非0值的退出狀態
- 未知(Unknown):apiserver無法正常獲取到pod對象的狀態信息,通常由網絡通信失敗所導致
創建和終止
pod的創建過程
- 用戶通過kubectl或其他api客戶端提交需要創建的pod信息給apiServer
- apiServer開始生成pod對象的信息,并將信息存入etcd,然后返回確認信息至客戶端
- apiServer開始反映etcd中的pod對象的變化,其它組件使用watch機制來跟蹤檢查apiServer上的變動
- scheduler發現有新的pod對象要創建,開始為Pod分配主機并將結果信息更新至apiServer
- node節點上的kubelet發現有pod調度過來,嘗試調用docker啟動容器,并將結果回送至apiServer
- apiServer將接收到的pod狀態信息存入etcd中
pod的終止過程
- 用戶向apiServer發送刪除pod對象的命令
- apiServcer中的pod對象信息會隨著時間的推移而更新,在寬限期內(默認30s),pod被視為dead
- 將pod標記為terminating狀態
- kubelet在監控到pod對象轉為terminating狀態的同時啟動pod關閉過程
- 端點控制器監控到pod對象的關閉行為時將其從所有匹配到此端點的service資源的端點列表中移除
- 如果當前pod對象定義了preStop鉤子處理器,則在其標記為terminating后即會以同步的方式啟動執行
- pod對象中的容器進程收到停止信號
- 寬限期結束后,若pod中還存在仍在運行的進程,那么pod對象會收到立即終止的信號
- kubelet請求apiServer將此pod資源的寬限期設置為0從而完成刪除操作,此時pod對于用戶已不可見
初始化容器
初始化容器是在pod的主容器啟動之前要運行的容器,主要是做一些主容器的前置工作,它具有兩大特征:
- 初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么kubernetes需要重啟它直到成功完成
- 初始化容器必須按照定義的順序執行,當且僅當前一個成功之后,后面的一個才能運行
初始化容器有很多的應用場景,下面列出的是最常見的幾個:
- 提供主容器鏡像中不具備的工具程序或自定義代碼
- 初始化容器要先于應用容器串行啟動并運行完成,因此可用于延后應用容器的啟動直至其依賴的條件得到滿足
接下來做一個案例,模擬下面這個需求:
假設要以主容器來運行httpd,但是要求在運行httpd之前先要能夠連接上mysql和redis所在服務器
為了簡化測試,事先規定好mysql(192.168.179.81)
和redis(192.168.179.82)
服務器的地址
創建一個pod-resources.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-initcontainer.yaml
[root@master ~]# cat pod-initcontainer.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-initcontainernamespace: lcwanf
spec:containers:- name: main-containerimage: lcwanf/apache:v0.1ports: - name: httpd-portcontainerPort: 80initContainers:- name: test-mysqlimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.179.81 -c 1 ; do echo waiting for mysql...; sleep 2; done;']- name: test-redisimage: busybox:1.30command: ['sh', '-c', 'until ping 192.168.179.82 -c 1 ; do echo waiting for reids...; sleep 2; done;']
[root@master ~]#
initContainers中的,test-mysql和test-redis這兩個初始化容器的cmd中要ping的IP地址現在是不存在的,所以這兩個容器是無法運行起來的。那么主容器httpd自然也不會運行起來
創建資源和查看
//創建資源
[root@master ~]# kubectl apply -f pod-initcontainer.yaml
pod/pod-initcontainer created
[root@master ~]# //查看資源
--沒有運行成功
[root@master ~]# kubectl get -f pod-initcontainer.yaml
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 21s
[root@master ~]# //詳細查看pod狀態
--發現pod卡在啟動第一個初始化容器過程中,后面的容器不會運行
[root@master ~]# kubectl describe pod pod-initcontainer -n lcwanf
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 72s default-scheduler Successfully assigned lcwanf/pod-initcontainer to node2Normal Pulled 71s kubelet Container image "busybox:1.30" already present on machineNormal Created 71s kubelet Created container test-mysqlNormal Started 71s kubelet Started container test-mysql//動態查看pod
--暫時沒有什么變化
[root@master ~]# kubectl get pods pod-initcontainer -n lcwanf -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 3m49s//然后新打開一個終端,給網卡添加上192.168.179.81和192.168.179.82這兩個IP
[root@master ~]# yum -y install net-tools
[root@master ~]# ifconfig ens160:1 192.168.179.81 netmask 255.255.255.0 up
[root@master ~]# ifconfig ens160:2 192.168.179.82 netmask 255.255.255.0 up//回到正在動態查看pod的終端繼續查看變化
--因為添加了對應的IP地址,所以兩個初始化容器可以正常運行,那么主容器也可以創建成功了
[root@master ~]# kubectl get pods pod-initcontainer -n lcwanf -w
NAME READY STATUS RESTARTS AGE
pod-initcontainer 0/1 Init:0/2 0 3m49s
pod-initcontainer 0/1 Init:1/2 0 19m
pod-initcontainer 0/1 PodInitializing 0 19m
pod-initcontainer 1/1 Running 0 19m//查看pod,正常運行
[root@master ~]# kubectl get -f pod-initcontainer.yaml
NAME READY STATUS RESTARTS AGE
pod-initcontainer 1/1 Running 0 20m
[root@master ~]#
鉤子函數
鉤子函數能夠感知自身生命周期中的事件,并在相應的時刻到來時運行用戶指定的程序代碼。
kubernetes在主容器的啟動之后和停止之前提供了兩個鉤子函數:
- post start:容器創建之后執行,如果失敗了會重啟容器
- pre stop :容器終止之前執行,執行完成之后容器將成功終止,在其完成之前會阻塞刪除容器的操作
鉤子處理器支持使用下面三種方式定義動作:
-
Exec命令:在容器內執行一次命令
…… lifecycle:postStart: exec:command:- cat- /tmp/healthy ……
-
TCPSocket:在當前容器嘗試訪問指定的socket
…… lifecycle:postStart:tcpSocket:port: 8080 ……
-
HTTPGet:在當前容器中向某url發起http請求
…… lifecycle:postStart:httpGet:path: / #URI地址port: 80 #端口號host: 192.168.179.15 #主機地址scheme: HTTP #支持的協議,http或者https ……
演示exec方式的鉤子函數的使用:
創建一個pod-hook-exec.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-hook-exec.yaml
[root@master ~]# cat pod-hook-exec.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-hook-execnamespace: lcwanf
spec:containers:- name: main-containerimage: lcwanf/apache:v0.1ports:- name: httpd-portcontainerPort: 80lifecycle:postStart: exec: # 在容器啟動的時候執行一個命令,修改掉httpd的默認首頁內容command: ["/bin/sh", "-c", "echo this is test > /usr/local/apache/htdocs/index.html"]preStop:exec: # 在容器停止之前停止nginx服務command: ["/usr/local/apache/bin/apachectl","stop"]
[root@master ~]#
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-hook-exec.yaml
pod/pod-hook-exec created
[root@master ~]# //查看pod
[root@master ~]# kubectl get -f pod-hook-exec.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-hook-exec 1/1 Running 0 28s 10.244.1.31 node1 <none> <none>
[root@master ~]# //訪問pod
--內容確實是修改后的
[root@master ~]# curl 10.244.1.31
this is test
[root@master ~]#
容器探測
容器探測用于檢測容器中的應用實例是否正常工作,是保障業務可用性的一種傳統機制。如果經過探測,實例的狀態不符合預期,那么kubernetes就會把該問題實例" 摘除 ",不承擔業務流量。kubernetes提供了兩種探針來實現容器探測,分別是:
- liveness probes:存活性探針,用于檢測應用實例當前是否處于正常運行狀態,如果不是,k8s會重啟容器
- readiness probes:就緒性探針,用于檢測應用實例當前是否可以接收請求,如果不能,k8s不會轉發流量
livenessProbe 決定是否重啟容器,readinessProbe 決定是否將請求轉發給容器。
上面兩種探針目前均支持三種探測方式:
-
Exec命令:在容器內執行一次命令,如果命令執行的退出碼為0,則認為程序正常,否則不正常
…… livenessProbe:exec:command:- cat- /tmp/healthy ……
-
TCPSocket:將會嘗試訪問一個用戶容器的端口,如果能夠建立這條連接,則認為程序正常,否則不正常
…… livenessProbe:tcpSocket:port: 8080 ……
-
HTTPGet:調用容器內Web應用的URL,如果返回的狀態碼在200和399之間,則認為程序正常,否則不正常
…… livenessProbe:httpGet:path: / #URI地址port: 80 #端口號host: 127.0.0.1 #主機地址scheme: HTTP #支持的協議,http或者https ……
以liveness probes為例,做幾個演示:
方式一:Exec
創建一個pod-liveness-exec.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-liveness-exec.yaml
[root@master ~]# cat pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-execnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports: - name: httpd-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/tmp/hello.txt"] # 執行一個查看文件的命令(此文件不存在)
[root@master ~]#
容器啟動時會執行查看/tmp/hello.txt這個文件,但是這個文件是不存在的,所以這條命令會失敗
那么這個容器也不會運行成功
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
[root@master ~]# //查看資源
--現在暫時是正常狀態,其實不是正常狀態
[root@master ~]# kubectl get -f pod-liveness-exec.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 1/1 Running 1 (8s ago) 38s//詳細查看
[root@master ~]# kubectl describe pods pod-liveness-exec -n lcwanf
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 21m default-scheduler Successfully assigned lcwanf/pod-liveness-exec to node1Normal Created 19m (x4 over 21m) kubelet Created container httpdNormal Started 19m (x4 over 21m) kubelet Started container httpdNormal Killing 19m (x3 over 20m) kubelet Container httpd failed liveness probe, will be restartedWarning Unhealthy 19m (x10 over 21m) kubelet Liveness probe failed: cat: /tmp/hello.txt: No such file or directoryWarning BackOff 6m12s (x54 over 18m) kubelet Back-off restarting failed container httpd in pod pod-liveness-exec_lcwanf(dcac7987-fef7-497c-afaf-8e609b1ce6aa)Normal Pulled 75s (x11 over 21m) kubelet Container image "lcwanf/apache:v0.1" already present on machine# 觀察上面的信息就會發現httpd容器啟動之后就進行了健康檢查
# 檢查失敗之后(要查看的文件是不存在的文件,所以檢查失敗)
# 容器被kill掉,然后嘗試進行重啟(這是重啟策略的作用,在下一小節)
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長//再次查看pod,容器運行失敗了
[root@master ~]# kubectl get pods pod-liveness-exec -n lcwanf
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 0/1 CrashLoopBackOff 11 (2m58s ago) 23m
[root@master ~]# //刪除資源
[root@master ~]# kubectl delete -f pod-liveness-exec.yaml## 修改成一個存在的文件,比如/usr/local/apache/htdocs/index.html,再試,結果就正常了......//修改資源文件
[root@master ~]# vim pod-liveness-exec.yaml
[root@master ~]# cat pod-liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-execnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports: - name: httpd-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/usr/local/apache/htdocs/index.html"] //修改為一個存在的文件
[root@master ~]# //重新創建資源
[root@master ~]# kubectl apply -f pod-liveness-exec.yaml
pod/pod-liveness-exec created
[root@master ~]# //查看資源
[root@master ~]# kubectl get -f pod-liveness-exec.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-exec 1/1 Running 0 14s
[root@master ~]# //詳細查看pod,沒有任何報錯
[root@master ~]# kubectl describe pods pod-liveness-exec -n lcwanf
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 39s default-scheduler Successfully assigned lcwanf/pod-liveness-exec to node1Normal Pulled 40s kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 40s kubelet Created container httpdNormal Started 40s kubelet Started container httpd
方式二:TCPSocket
創建一個pod-liveness-tcpsocket.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-liveness-tcpsocket.yaml
[root@master ~]# cat pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports: - name: httpd-portcontainerPort: 80livenessProbe:tcpSocket:port: 8080 # 嘗試訪問8080端口(錯誤的端口)
[root@master ~]#
容器啟動時會執行訪問8080,但是這個8080是錯誤的,所以這個訪問會失敗
那么這個容器也不會運行成功
創建資源并查看
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 65s default-scheduler Successfully assigned lcwanf/pod-liveness-tcpsocket to node2Normal Pulled 5s (x3 over 65s) kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 5s (x3 over 65s) kubelet Created container httpdNormal Started 5s (x3 over 64s) kubelet Started container httpdWarning Unhealthy 5s (x6 over 55s) kubelet Liveness probe failed: dial tcp 10.244.2.36:8080: connect: connection refusedNormal Killing 5s (x2 over 35s) kubelet Container httpd failed liveness probe, will be restarted
[root@master ~]# kubectl describe -f pod-liveness-tcpsocket.yaml//創建資源
[root@master ~]# kubectl apply -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
[root@master ~]# //查看資源
--現在暫時是正常狀態,其實不是正常狀態
[root@master ~]# kubectl get -f pod-liveness-tcpsocket.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 1/1 Running 0 25s
[root@master ~]# //詳細查看pod情況
[root@master ~]# kubectl describe -f pod-liveness-tcpsocket.yaml
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 65s default-scheduler Successfully assigned lcwanf/pod-liveness-tcpsocket to node2Normal Pulled 5s (x3 over 65s) kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 5s (x3 over 65s) kubelet Created container httpdNormal Started 5s (x3 over 64s) kubelet Started container httpdWarning Unhealthy 5s (x6 over 55s) kubelet Liveness probe failed: dial tcp 10.244.2.36:8080: connect: connection refusedNormal Killing 5s (x2 over 35s) kubelet Container httpd failed liveness probe, will be restarted
# 觀察上面的信息,發現嘗試訪問8080端口,但是失敗了
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長//再次查看pod,容器運行失敗了
[root@master ~]# kubectl get -f pod-liveness-tcpsocket.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 0/1 CrashLoopBackOff 4 (4s ago) 2m34s
[root@master ~]# //刪除資源
[root@master ~]# kubectl delete -f pod-liveness-tcpsocket.yaml
pod "pod-liveness-tcpsocket" deleted
[root@master ~]# ## 接下來,修改成一個可以訪問的端口,比如80,再試,結果就正常了......//修改資源文件,將測試端口修改為一個正確的端口
[root@master ~]# vim pod-liveness-tcpsocket.yaml
[root@master ~]# cat pod-liveness-tcpsocket.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports: - name: httpd-portcontainerPort: 80livenessProbe:tcpSocket:port: 80
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created
[root@master ~]# //查看pod
[root@master ~]# kubectl get -f pod-liveness-tcpsocket.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-tcpsocket 1/1 Running 0 19s
[root@master ~]# //詳細查看pod,沒有任何報錯
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 39s default-scheduler Successfully assigned lcwanf/pod-liveness-tcpsocket to node2Normal Pulled 39s kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 39s kubelet Created container httpdNormal Started 39s kubelet Started container httpd
[root@master ~]# kubectl describe -f pod-liveness-tcpsocket.yaml
方式三:HTTPGet
創建一個pod-liveness-httpget.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-liveness-httpget.yaml
[root@master ~]# cat pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports:- name: httpd-portcontainerPort: 80livenessProbe:httpGet: # 其實就是訪問http://127.0.0.1:80/hello scheme: HTTP #支持的協議,http或者httpsport: 80 #端口號path: /hello #URI地址(錯誤的URI地址)
[root@master ~]#
容器啟動時會執行訪問http://127.0.0.1:80/hello ,但是這個URI地址是錯誤的,所以這個訪問會失敗,那么這個容器也不會運行成功
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
[root@master ~]# //查看資源
--現在暫時是正常狀態,其實不是正常狀態
[root@master ~]# kubectl get -f pod-liveness-httpget.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 0 19s
[root@master ~]# //詳細查看pod
[root@master ~]# kubectl describe -f pod-liveness-httpget.yaml
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 46s default-scheduler Successfully assigned lcwanf/pod-liveness-httpget to node1Normal Pulled 16s (x2 over 46s) kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 16s (x2 over 46s) kubelet Created container httpdNormal Started 16s (x2 over 46s) kubelet Started container httpdNormal Killing 16s kubelet Container httpd failed liveness probe, will be restartedWarning Unhealthy 6s (x4 over 36s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404
[root@master ~]# # 觀察上面信息,嘗試訪問路徑,但是未找到,出現404錯誤
# 稍等一會之后,再觀察pod信息,就可以看到RESTARTS不再是0,而是一直增長//再次查看pod,容器運行失敗了
[root@master ~]# kubectl get -f pod-liveness-httpget.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 0/1 CrashLoopBackOff 4 (5s ago) 2m35s
[root@master ~]# //刪除資源
[root@master ~]# kubectl delete -f pod-liveness-httpget.yaml
pod "pod-liveness-httpget" deleted
[root@master ~]# ## 修改成一個可以訪問的路徑path,比如/,再試,結果就正常了......//修改資源文件,把URL地址改為正確的地址。
[root@master ~]# vim pod-liveness-httpget.yaml
[root@master ~]# cat pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports:- name: httpd-portcontainerPort: 80livenessProbe:httpGet: scheme: HTTPport: 80path: / #直接訪問根目錄
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created
[root@master ~]# //查看pod
[root@master ~]# kubectl get -f pod-liveness-httpget.yaml
NAME READY STATUS RESTARTS AGE
pod-liveness-httpget 1/1 Running 0 93s
[root@master ~]# //查看pod詳細信息,沒有任何報錯
[root@master ~]# kubectl describe -f pod-liveness-httpget.yaml
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 2m3s default-scheduler Successfully assigned lcwanf/pod-liveness-httpget to node2Normal Pulled 2m2s kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 2m2s kubelet Created container httpdNormal Started 2m2s kubelet Started container httpd
[root@master ~]#
擴展:
至此,已經使用liveness Probe演示了三種探測方式,但是查看livenessProbe的子屬性,會發現除了這三種方式,還有一些其他的配置,在這里一并解釋下:
[root@master ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:exec <Object> tcpSocket <Object>httpGet <Object>initialDelaySeconds <integer> # 容器啟動后等待多少秒執行第一次探測timeoutSeconds <integer> # 探測超時時間。默認1秒,最小1秒periodSeconds <integer> # 執行探測的頻率。默認是10秒,最小1秒failureThreshold <integer> # 連續探測失敗多少次才被認定為失敗。默認是3。最小值是1successThreshold <integer> # 連續探測成功多少次才被認定為成功。默認是1
下面稍微配置兩個,演示下效果即可:
[root@master ~]# more pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports:- name: httpd-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80 path: /initialDelaySeconds: 30 # 容器啟動后30s開始探測timeoutSeconds: 5 # 探測超時時間為5s
重啟策略
在上一節中,一旦容器探測出現了問題,kubernetes就會對容器所在的Pod進行重啟,其實這是由pod的重啟策略決定的,pod的重啟策略有 3 種,分別如下:
- Always :容器失效時,自動重啟該容器,這也是默認值。
- OnFailure : 容器終止運行且退出碼不為0時重啟
- Never : 不論狀態為何,都不重啟該容器
重啟策略適用于pod對象中的所有容器,首次需要重啟的容器,將在其需要時立即進行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時間后進行,且反復的重啟操作的延遲時長以此為10s、20s、40s、80s、160s和300s,300s是最大延遲時長。
創建一個pod-restartpolicy.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-restartpolicy.yaml
[root@master ~]# cat pod-restartpolicy.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-restartpolicynamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1ports:- name: httpd-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /hello # 錯誤的URL地址restartPolicy: Never # 設置重啟策略為Never
[root@master ~]#
探測規則中定義了錯誤的URL地址,所以容器會啟動失敗,pod會自己重啟嘗試修復
但是又因為定義了重啟策略為Never(從不),所以當容器啟動失敗時,不會重啟容器嘗試修復
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-restartpolicy.yaml
pod/pod-restartpolicy created
[root@master ~]# //查看pod,不是正常運行狀態;RESTARTS數值為0(重啟次數為0),不需要像前面一樣,要等很久才能看見非正常狀態
[root@master ~]# kubectl get -f pod-restartpolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 31s//查看pod詳細信息,容器啟動錯誤后,沒有重啟,而是直接Killing容器
[root@master ~]# kubectl describe -f pod-restartpolicy.yaml
(省略)
Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 5m52s default-scheduler Successfully assigned lcwanf/pod-restartpolicy to node1Normal Pulled 5m53s kubelet Container image "lcwanf/apache:v0.1" already present on machineNormal Created 5m53s kubelet Created container httpdNormal Started 5m53s kubelet Started container httpdWarning Unhealthy 5m23s (x3 over 5m43s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404Normal Killing 5m23s kubelet Stopping container httpd
[root@master ~]# //等待一段時間,重啟次數依然為0
[root@master ~]# kubectl get -f pod-restartpolicy.yaml
NAME READY STATUS RESTARTS AGE
pod-restartpolicy 0/1 Completed 0 9m42s
[root@master ~]#
四、Pod調度
在默認情況下,一個Pod在哪個Node節點上運行,是由Scheduler組件采用相應的算法計算出來的,這個過程是不受人工控制的。但是在實際使用中,這并不滿足的需求,因為很多情況下,我們想控制某些Pod到達某些節點上,那么應該怎么做呢?這就要求了解kubernetes對Pod的調度規則,kubernetes提供了四大類調度方式:
- 自動調度:運行在哪個節點上完全由Scheduler經過一系列的算法計算得出
- 定向調度:NodeName、NodeSelector
- 親和性調度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污點(容忍)調度:Taints、Toleration
定向調度
定向調度,指的是利用在pod上聲明nodeName或者nodeSelector,以此將Pod調度到期望的node節點上。注意,這里的調度是強制的,這就意味著即使要調度的目標Node不存在,也會向上面進行調度,只不過pod運行失敗而已。
NodeName
NodeName用于強制約束將Pod調度到指定的Name的Node節點上。這種方式,其實是直接跳過Scheduler的調度邏輯,直接將Pod調度到指定名稱的節點。示例如下:
創建一個pod-nodename.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-nodename.yaml
[root@master ~]# cat pod-nodename.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1nodeName: node2 //指定調度到node2節點上
[root@master ~]#
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-nodename.yaml
pod/pod-nodename created
[root@master ~]# //查看pod
--確實調度在node2上面
[root@master ~]# kubectl get -f pod-nodename.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 1/1 Running 0 4m4s 10.244.2.2 node2 <none> <none>
[root@master ~]# //刪除該pod,修改指定調度到node3上面去(node3不存在)
[root@master ~]# kubectl delete -f pod-nodename.yaml
pod "pod-nodename" deleted
[root@master ~]# [root@master ~]# vim pod-nodename.yaml
[root@master ~]# cat pod-nodename.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1nodeName: node3 //修改指定調度到node3上
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-nodename.yaml
pod/pod-nodename created
[root@master ~]#//查看pod
--由于指定的node3這個節點不存在,所以pod既不能成功創建出來,也不會被調度到其他節點,只會是Pending狀態
[root@master ~]# kubectl get -f pod-nodename.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodename 0/1 Pending 0 17s <none> node3 <none> <none>
[root@master ~]#
NodeSelector
NodeSelector用于將pod調度到添加了指定標簽的node節點上。它是通過kubernetes的label-selector機制實現的,也就是說,在pod創建之前,會由scheduler使用MatchNodeSelector調度策略進行label匹配,找出目標node,然后將pod調度到目標節點,該匹配規則是強制約束。示例如下:
分別為node節點添加標簽
//為node1打標簽
[root@master ~]# kubectl label nodes node1 nodeenv=pro
node/node1 labeled
[root@master ~]# //為node2打標簽
[root@master ~]# kubectl label nodes node2 nodeenv=test
node/node2 labeled
[root@master ~]#
創建一個pod-nodeselector.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-nodeselector.yaml
[root@master ~]# cat pod-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1nodeSelector: nodeenv: pro //指定調度到具有nodeenv=pro標簽的節點上
[root@master ~]#
前面已經為node1節點打上了nodeenv=pro的標簽,node2節點打上了nodeenv=test的標簽
資源文件里面寫的是指定調度到具有nodeenv=pro標簽的節點上,所以此pod會調度到node1上
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodeselector created
[root@master ~]# //查看pod
--確實調度到node1上了
[root@master ~]# kubectl get -f pod-nodeselector.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 1/1 Running 0 19s 10.244.1.44 node1 <none> <none>
[root@master ~]# //刪除該pod,修改nodeSelector的值為nodeenv: xxx (標簽xxx不存在)
[root@master ~]# kubectl delete -f pod-nodeselector.yaml
pod "pod-nodeselector" deleted
[root@master ~]# [root@master ~]# vim pod-nodeselector.yaml
[root@master ~]# cat pod-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1nodeSelector: nodeenv: xxx
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-nodeselector.yaml
pod/pod-nodeselector created
[root@master ~]#//查看pod
--由于標簽 nodeenv: xxx不存在,所以無法正確調度pod。處于Pending狀態
[root@master ~]# kubectl get -f pod-nodeselector.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeselector 0/1 Pending 0 18s <none> <none> <none> <none>
[root@master ~]#
親和性調度
上面兩種定向調度的方式,使用起來非常方便,但是也有一定的問題,那就是如果沒有滿足條件的Node,那么Pod將不會被運行,即使在集群中還有可用Node列表也不行,這就限制了它的使用場景。
基于上面的問題,kubernetes還提供了一種親和性調度(Affinity)。它在NodeSelector的基礎之上的進行了擴展,可以通過配置的形式,實現優先選擇滿足條件的Node進行調度,如果沒有,也可以調度到不滿足條件的節點上,使調度更加靈活。
Affinity主要分為三類:
- nodeAffinity(node親和性): 以node為目標,解決pod可以調度到哪些node的問題
- podAffinity(pod親和性) : 以pod為目標,解決pod可以和哪些已存在的pod部署在同一個拓撲域中的問題
- podAntiAffinity(pod反親和性) : 以pod為目標,解決pod不能和哪些已存在pod部署在同一個拓撲域中的問題
關于親和性(反親和性)使用場景的說明:
親和性:如果兩個應用頻繁交互,那就有必要利用親和性讓兩個應用的盡可能的靠近,這樣可以減少因網絡通信而帶來的性能損耗。
反親和性:當應用的采用多副本部署時,有必要采用反親和性讓各個應用實例打散分布在各個node上,這樣可以提高服務的高可用性。
NodeAffinity
首先來看一下NodeAffinity
的可配置項:
pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringExecution Node節點必須滿足指定的所有規則才可以,相當于硬限制nodeSelectorTerms 節點選擇列表matchFields 按節點字段列出的節點選擇器要求列表matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)key 鍵values 值operator 關系符 支持Exists, DoesNotExist, In, NotIn, Gt, LtpreferredDuringSchedulingIgnoredDuringExecution 優先調度到滿足指定的規則的Node,相當于軟限制 (傾向)preference 一個節點選擇器項,與相應的權重相關聯matchFields 按節點字段列出的節點選擇器要求列表matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)key 鍵values 值operator 關系符 支持In, NotIn, Exists, DoesNotExist, Gt, Ltweight 傾向權重,在范圍1-100。
關系符的使用說明:- matchExpressions:- key: nodeenv # 匹配存在標簽的key為nodeenv的節點operator: Exists- key: nodeenv # 匹配標簽的key為nodeenv,且value是"xxx"或"yyy"的節點operator: Invalues: ["xxx","yyy"]- key: nodeenv # 匹配標簽的key為nodeenv,且value大于"xxx"的節點operator: Gtvalues: "xxx"
示例requiredDuringSchedulingIgnoredDuringExecution
(硬限制)
創建一個pod-nodeaffinity-required.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-nodeaffinity-required.yaml
[root@master ~]# cat pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1affinity: #親和性設置nodeAffinity: #設置node親和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制nodeSelectorTerms:- matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽- key: nodeenvoperator: Invalues: ["xxx","yyy"]
[root@master ~]#
前面已經為node1節點打上了nodeenv=pro的標簽,node2節點打上了nodeenv=test的標簽
資源文件里面定義了硬限制:選擇nodeenv=xxx,或者nodeenv=yyy;但是這兩個標簽都是不存在的
所以此pod無法正常調度并運行
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
[root@master ~]# //查看pod
--由于定義選擇的標簽都是不存在的,并且是硬限制,所以pod是Pending狀態
[root@master ~]# kubectl get -f pod-nodeaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-required 0/1 Pending 0 38s <none> <none> <none> <none>
[root@master ~]# //刪除pod,修改其中一個標簽為存在的標簽:test。[root@master ~]# kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deleted
[root@master ~]# //創建pod
[root@master ~]# vim pod-nodeaffinity-required.yaml
[root@master ~]# cat pod-nodeaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms:- matchExpressions: - key: nodeenvoperator: Invalues: ["test","yyy"] //修改一個標簽為存在的標簽:test
[root@master ~]# //創建pod
[root@master ~]# kubectl apply -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created
[root@master ~]# //查看pod
--由于標簽nodeenv:test是可以選擇到的,所以pod被調度到對應的node2節點上了,pod正常運行
[root@master ~]# kubectl get -f pod-nodeaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-required 1/1 Running 0 17s 10.244.2.3 node2 <none> <none>
[root@master ~]#
示例preferredDuringSchedulingIgnoredDuringExecution
(軟限制)
創建一個pod-nodeaffinity-preferred.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-nodeaffinity-preferred.yaml
[root@master ~]# cat pod-nodeaffinity-preferred.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-preferrednamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1affinity: #親和性設置nodeAffinity: #設置node親和性preferredDuringSchedulingIgnoredDuringExecution: # 軟限制- weight: 1preference:matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽(當前環境沒有)- key: nodeenvoperator: Invalues: ["xxx","yyy"]
[root@master ~]#
前面已經為node1節點打上了nodeenv=pro的標簽,node2節點打上了nodeenv=test的標簽
資源文件里面定義了軟限制:選擇nodeenv=xxx,或者nodeenv=yyy;雖然這兩個標簽都是不存在的,但是由于定義的軟限制,所以這個pod會被調度其它適合運行的節點
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created
[root@master ~]# //查看pod,在node2上運行成功
[root@master ~]#
[root@master ~]# kubectl get -f pod-nodeaffinity-preferred.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-nodeaffinity-preferred 1/1 Running 0 63s 10.244.2.4 node2 <none> <none>
[root@master ~]#
NodeAffinity規則設置的注意事項:1 如果同時定義了nodeSelector和nodeAffinity,那么必須兩個條件都得到滿足,Pod才能運行在指定的Node上2 如果nodeAffinity指定了多個nodeSelectorTerms,那么只需要其中一個能夠匹配成功即可3 如果一個nodeSelectorTerms中有多個matchExpressions ,則一個節點必須滿足所有的才能匹配成功4 如果一個pod所在的Node在Pod運行期間其標簽發生了改變,不再符合該Pod的節點親和性需求,則系統將忽略此變化
PodAffinity
PodAffinity主要實現以運行的Pod為參照,實現讓新創建的Pod跟參照pod在一個區域的功能。
首先來看一下PodAffinity
的可配置項:
pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringExecution 硬限制namespaces 指定參照pod的namespacetopologyKey 指定調度作用域labelSelector 標簽選擇器matchExpressions 按節點標簽列出的節點選擇器要求列表(推薦)key 鍵values 值operator 關系符 支持In, NotIn, Exists, DoesNotExist.matchLabels 指多個matchExpressions映射的內容preferredDuringSchedulingIgnoredDuringExecution 軟限制podAffinityTerm 選項namespaces topologyKeylabelSelectormatchExpressions key 鍵values 值operatormatchLabels weight 傾向權重,在范圍1-100
topologyKey用于指定調度時作用域,例如:如果指定為kubernetes.io/hostname,那就是以Node節點為區分范圍如果指定為beta.kubernetes.io/os,則以Node節點的操作系統類型來區分
示例requiredDuringSchedulingIgnoredDuringExecution
(硬限制)
先創建一個用于參照的pod
//編寫資源文件,創建pod
[root@master ~]# vim pod-podaffinity-target.yaml
[root@master ~]# cat pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-targetnamespace: lcwanflabels:podenv: pro # 設置標簽
spec:containers:- name: httpdimage: lcwanf/apache:v0.1nodeName: node1 # 將目標pod名確指定到node1上
[root@master ~]# [root@master ~]# kubectl apply -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
[root@master ~]# [root@master ~]# kubectl get -f pod-podaffinity-target.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-target 1/1 Running 0 14s 10.244.1.45 node1 <none> <none>
[root@master ~]#
創建一個pod-podaffinity-required.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-podaffinity-required.yaml
[root@master ~]# cat pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1affinity: # 親和性設置podAffinity: # 設置pod親和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽- key: podenvoperator: Invalues: ["xxx","yyy"]topologyKey: kubernetes.io/hostname
[root@master ~]#
前面已經為node1節點打上了nodeenv=pro的標簽,node2節點打上了nodeenv=test的標簽
資源文件里定義了新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
可是現在沒有這樣的pod,所以不會運行成功
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
[root@master ~]# //查看pod
--容器無法正常運行,為Pending狀態
[root@master ~]# kubectl get -f pod-podaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-required 0/1 Pending 0 23s <none> <none> <none> <none>
[root@master ~]# //刪除pod,修改 values: ["xxx","yyy"]里面的xxx為pro
# 意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
[root@master ~]# kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" deleted
[root@master ~]# [root@master ~]# vim pod-podaffinity-required.yaml
[root@master ~]# cat pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector:matchExpressions: - key: podenvoperator: Invalues: ["pro","yyy"] //把xxx修改為protopologyKey: kubernetes.io/hostname
[root@master ~]# //創建資源
[root@master ~]# kubectl apply -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
[root@master ~]# //查看pod,運行成功。新pod和參照的pod都在node1節點上
[root@master ~]# kubectl get -f pod-podaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-required 1/1 Running 0 12s 10.244.1.46 node1 <none> <none>
[root@master ~]#
示例preferredDuringSchedulingIgnoredDuringExecution
(軟限制)
在此不做演示,只簡單口述:
PodAffinity的軟限制,是當定義一個新pod需要和一個參考pod在同一節點,但是這個條件無法滿足。就把這個新pod調度給一個合適的node節點。和NodeAffinity里面的軟限制示例一樣的
PodAntiAffinity
PodAntiAffinity主要實現以運行的Pod為參照,讓新創建的Pod跟參照pod不在一個區域中的功能。
它的配置方式和選項跟PodAffinty是一樣的。示例如下:
繼續使用上一個示例里面的參考pod
[root@master ~]# kubectl get -f pod-podaffinity-target.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podaffinity-target 1/1 Running 0 86m 10.244.1.45 node1 <none> <none>
[root@master ~]#
創建一個pod-podantiaffinity-required.yaml文件,內容如下:
//編寫資源文件
[root@master ~]# vim pod-podantiaffinity-required.yaml
[root@master ~]# cat pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: lcwanf
spec:containers:- name: httpdimage: lcwanf/apache:v0.1affinity: #親和性設置podAntiAffinity: #設置pod親和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配podenv的值在["pro"]中的標簽- key: podenvoperator: Invalues: ["pro"]topologyKey: kubernetes.io/hostname
[root@master ~]#
上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=pro的pod不在同一Node上。
創建資源并查看
//創建資源
[root@master ~]# kubectl apply -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
[root@master ~]# //查看pod
# 發現調度到了node2上
[root@master ~]# kubectl get -f pod-podantiaffinity-required.yaml -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod-podantiaffinity-required 1/1 Running 0 67s 10.244.2.5 node2 <none> <none>
[root@master ~]#
五、污點和容忍
污點(Taints)
前面的調度方式都是站在Pod的角度上,通過在Pod上添加屬性,來確定Pod是否要調度到指定的Node上,其實我們也可以站在Node的角度上,通過在Node上添加污點屬性,來決定是否允許Pod調度過來。
Node被設置上污點之后就和Pod之間存在了一種相斥的關系,進而拒絕Pod調度進來,甚至可以將已經存在的Pod驅逐出去。
污點的格式為:key=value:effect
, key和value是污點的標簽,effect描述污點的作用,支持如下三個選項:
- PreferNoSchedule:kubernetes將盡量避免把Pod調度到具有該污點的Node上,除非沒有其他節點可調度
- NoSchedule:kubernetes將不會把Pod調度到具有該污點的Node上,但不會影響當前Node上已存在的Pod
- NoExecute:kubernetes將不會把Pod調度到具有該污點的Node上,同時也會將Node上已存在的Pod驅離
使用kubectl設置和去除污點的命令示例如下:
# 設置污點
kubectl taint nodes node1 key=value:effect# 去除污點
kubectl taint nodes node1 key:effect-# 去除所有污點
kubectl taint nodes node1 key-
接下來,演示下污點的效果:
- 準備節點node1(為了演示效果更加明顯,暫時停止node2節點)
- 為node1節點設置一個污點:
tag=wanf:PreferNoSchedule
;然后創建pod1( pod1 可以 ) - 修改為node1節點設置一個污點:
tag=wanf:NoSchedule
;然后創建pod2( pod1 正常 pod2 失敗 ) - 修改為node1節點設置一個污點:
tag=wanf:NoExecute
;然后創建pod3 ( 3個pod都失敗 )
# 為node1設置污點(PreferNoSchedule)
[root@master ~]# kubectl taint nodes node1 tag=wanf:PreferNoSchedule# 創建pod1
[root@master ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@master ~]# 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 # 為node1設置污點(取消PreferNoSchedule,設置NoSchedule)
[root@master ~]# kubectl taint nodes node1 tag:PreferNoSchedule-
[root@master ~]# kubectl taint nodes node1 tag=wanf:NoSchedule# 創建pod2
[root@master ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@master ~]# kubectl get pods taint2 -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> # 為node1設置污點(取消NoSchedule,設置NoExecute)
[root@master ~]# kubectl taint nodes node1 tag:NoSchedule-
[root@master ~]# kubectl taint nodes node1 tag=wanf:NoExecute# 創建pod3
[root@master ~]# kubectl run taint3 --image=nginx:1.17.1 -n dev
[root@master ~]# 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搭建的集群,默認就會給master節點添加一個污點標記,所以pod就不會調度到master節點上.
容忍(Toleration)
上面介紹了污點的作用,我們可以在node上添加污點用于拒絕pod調度上來,但是如果就是想將一個pod調度到一個有污點的node上去,這時候應該怎么做呢?這就要使用到容忍。
污點就是拒絕,容忍就是忽略,Node通過污點拒絕pod調度上去,Pod通過容忍忽略拒絕
下面先通過一個案例看下效果:
- 上一小節,已經在node1節點上打上了
NoExecute
的污點,此時pod是調度不上去的 - 本小節,可以通過給pod添加容忍,然后將其調度上去
創建pod-toleration.yaml,內容如下
apiVersion: v1
kind: Pod
metadata:name: pod-tolerationnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1tolerations: # 添加容忍- key: "tag" # 要容忍的污點的keyoperator: "Equal" # 操作符value: "wanf" # 容忍的污點的valueeffect: "NoExecute" # 添加容忍的規則,這里必須和標記的污點規則相同
# 添加容忍之前的pod
[root@master ~]# 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@master ~]# 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@master ~]# kubectl explain pod.spec.tolerations
......
FIELDS:key # 對應著要容忍的污點的鍵,空意味著匹配所有的鍵value # 對應著要容忍的污點的值operator # key-value的運算符,支持Equal和Exists(默認)effect # 對應污點的effect,空意味著匹配所有影響tolerationSeconds # 容忍時間, 當effect為NoExecute時生效,表示pod在Node上的停留時間