如何配置CLion來編譯執行Qt專案
Pod介紹
Pod結構
每個Pod中都可以包含一個或者多個容器,這些容器可以分為兩類:
-
使用者程式所在的容器,數量可多可少
-
Pause容器,這是每個Pod都會有的一個根容器,它的作用有兩個:
-
可以以它為依據,評估整個Pod的健康狀態
-
可以在根容器上設定Ip地址,其它容器都此Ip(Pod IP),以實現Pod內部的網路通訊
這裡是Pod內部的通訊,Pod的之間的通訊採用虛擬二層網路技術來實現,當前環境用的是Flannel
-
Pod的yaml定義
下面是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,預設TCP env: #容器執行前需設定的環境變數列表 - 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、port path: string port: number host: string scheme: string HttpHeaders: - name: string value: string tcpSocket: #對Pod內個容器健康檢查方式設定為tcpSocket方式 port: number initialDelaySeconds: 0 #容器啟動完成後首次探測的時間,單位為秒 timeoutSeconds: 0 #對容器健康檢查探測等待響應的超時時間,單位秒,預設1秒 periodSeconds: 0 #對容器監控檢查的定期探測時間設定,單位秒,預設10秒一次 successThreshold: 0 failureThreshold: 0 securityContext: privileged: false restartPolicy: [Always | Never | OnFailure] #Pod的重啟策略 nodeName: <string> #設定NodeName表示將該Pod排程到指定到名稱的node節點上 nodeSelector: obeject #設定NodeSelector表示將該Pod排程到包含這個label的node上 imagePullSecrets: #Pull映象時使用的secret名稱,以key:secretkey格式指定 - name: string hostNetwork: 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: string path: string configMap: #型別為configMap的儲存卷,掛載預定義的configMap物件到容器內部 name: string items: - key: string path: string
# yaml配置命令提示
# 在這裡,可通過一個命令來檢視每種資源的可配置項
# kubectl explain 資源型別 檢視某種資源可以配置的一級屬性
# kubectl explain 資源型別.屬性 檢視屬性的子屬性
kubectl explain pod
kubectl explain pod.metadata
在kubernetes中基本所有資源的一級屬性都是一樣的,主要包含5部分:
- apiVersion
版本,由kubernetes內部定義,版本號必須可以用 kubectl api-versions 查詢到 - kind
型別,由kubernetes內部定義,版本號必須可以用 kubectl api-resources 查詢到 - metadata
- spec
- status
在上面的屬性中,spec是接下來研究的重點,繼續看下它的常見子屬性:
- containers <[]Object> 容器列表,用於定義容器的詳細資訊
- nodeName
根據nodeName的值將pod排程到指定的Node節點上 - nodeSelector <map[]> 根據NodeSelector中定義的資訊選擇將該Pod排程到包含這些label的Node 上
- hostNetwork
是否使用主機網路模式,預設為false,如果設定為true,表示使用宿主機網路 - volumes <[]Object> 儲存卷,用於定義Pod上面掛在的儲存資訊
- restartPolicy
重啟策略,表示Pod在遇到故障的時候的處理策略
Pod配置
本小節主要來研究pod.spec.containers
屬性,這也是pod配置中最為關鍵的一項配置。
[root@k8s-master01 ~]# 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> # 資源限制和資源請求的設定
基本配置
建立pod-base.yaml檔案,內容如下:
注意提前建立namespace,名稱為dev
apiVersion: v1
kind: Pod
metadata:
name: pod-base
namespace: dev
labels:
user: heima
spec:
containers:
- name: nginx
image: nginx:1.17.1
- name: busybox
image: busybox:1.30
上面定義了一個比較簡單Pod的配置,裡面有兩個容器:
- nginx:用1.17.1版本的nginx映象建立,(nginx是一個輕量級web容器)
- busybox:用1.30版本的busybox映象建立,(busybox是一個小巧的linux命令集合)
# 建立Pod
kubectl apply -f pod-base.yaml
# 檢視Pod狀況
# READY 1/2 : 表示當前Pod中有2個容器,其中1個準備就緒,1個未就緒
# RESTARTS : 重啟次數,因為有1個容器故障了,Pod一直在重啟試圖恢復它
kubectl get pod -n dev
# 可以通過describe檢視內部的詳情
# 此時已經執行起來了一個基本的Pod,雖然它暫時有問題
kubectl describe pod pod-base -n dev
映象拉取
建立pod-imagepullpolicy.yaml檔案,內容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-imagepullpolicy
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.2
imagePullPolicy: IfNotPresent # 用於設定映象拉取策略
- name: busybox
image: busybox:1.30
imagePullPolicy,用於設定映象拉取策略,kubernetes支援配置三種拉取策略:
- Always:總是從遠端倉庫拉取映象(一直遠端下載)
- IfNotPresent:本地有則使用本地映象,本地沒有則從遠端倉庫拉取映象(本地有就本地 本地沒遠端下載)
- Never:只使用本地映象,從不去遠端倉庫拉取,本地沒有就報錯 (一直使用本地)
預設值說明:
如果映象tag為具體版本號, 預設策略是:IfNotPresent
如果映象tag為:latest(最終版本) ,預設策略是always
# 建立Pod
kubectl create -f pod-imagepullpolicy.yaml
# 檢視Pod詳情
# 此時明顯可以看到nginx映象有一步Pulling image "nginx:1.17.1"的過程
kubectl describe pod pod-imagepullpolicy -n dev
啟動命令
在前面的案例中,一直有一個問題沒有解決,就是的busybox容器一直沒有成功執行,那麼到底是什麼原因導致這個容器的故障呢?
原來busybox並不是一個程式,而是類似於一個工具類的集合,kubernetes叢集啟動管理後,它會自動關閉。解決方法就是讓其一直在執行,這就用到了command配置。
建立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;"]
command,用於在pod中的容器初始化完畢之後執行一個命令。
稍微解釋下上面命令的意思:
"/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秒向檔案中寫入當前時間
# 建立Pod
kubectl create -f pod-command.yaml
# 檢視Pod狀態
# 此時發現兩個pod都正常運行了
kubectl get pods pod-command -n dev
# 進入pod中的busybox容器,檢視檔案內容
# 補充一個命令: kubectl exec pod名稱 -n 名稱空間 -it -c 容器名稱 /bin/sh 在容器內部執行命令
# 使用這個命令就可以進入某個容器的內部,然後進行相關操作了
# 比如,可以檢視txt檔案的內容
kubectl exec pod-command -n dev -it -c busybox /bin/sh
tail -f /tmp/hello.txt
特別說明:
通過上面發現command已經可以完成啟動命令和傳遞引數的功能,為什麼這裡還要提供一個args選項,用於傳遞引數呢?這其實跟docker有點關係,kubernetes中的command、args兩項其實是實現覆蓋Dockerfile中ENTRYPOINT的功能。
1 如果command和args均沒有寫,那麼用Dockerfile的配置。
2 如果command寫了,但args沒有寫,那麼Dockerfile預設的配置會被忽略,執行輸入的command
3 如果command沒寫,但args寫了,那麼Dockerfile中配置的ENTRYPOINT的命令會被執行,使用當前args的引數
4 如果command和args都寫了,那麼Dockerfile的配置被忽略,執行command並追加上args引數
環境變數
建立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: # 設定環境變數列表
- name: "username"
value: "admin"
- name: "password"
value: "123456"
env,環境變數,用於在pod中的容器設定環境變數。
# 建立Pod
kubectl create -f pod-env.yaml
# 進入容器,輸出環境變數
kubectl exec pod-env -n dev -c busybox -it /bin/sh
echo $username
echo $password
這種方式不是很推薦,推薦將這些配置單獨儲存在配置檔案中
埠設定
本小節來介紹容器的埠設定,也就是containers的ports選項。
首先看下ports支援的子選項:
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
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 設定容器暴露的埠列表
- name: nginx-port
containerPort: 80
protocol: TCP
# 建立Pod
kubectl create -f pod-ports.yaml
# 檢視pod
# 在下面可以明顯看到配置資訊
kubectl get pod pod-ports -n dev -o yaml
訪問容器中的程式需要使用的是Podip:containerPort
資源配額
容器中的程式要執行,肯定是要佔用一定資源的,比如cpu和記憶體等,如果不對某個容器的資源做限制,那麼它就可能吃掉大量資源,導致其它容器無法執行。針對這種情況,kubernetes提供了對記憶體和cpu的資源進行配額的機制,這種機制主要通過resources選項實現,他有兩個子選項:
- limits:用於限制執行時容器的最大佔用資源,當容器佔用資源超過limits時會被終止,並進行重啟
- requests :用於設定容器需要的最小資源,如果環境資源不夠,容器將無法啟動
可以通過上面兩個選項設定資源的上下限。
接下來,編寫一個測試案例,建立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數
memory: "10Gi" # 記憶體限制
requests: # 請求資源(下限)
cpu: "1" # CPU限制,單位是core數
memory: "10Mi" # 記憶體限制
在這對cpu和memory的單位做一個說明:
- cpu:core數,可以為整數或小數
- memory: 記憶體大小,可以使用Gi、Mi、G、M等形式
# 執行Pod
kubectl create -f pod-resources.yaml
# 檢視發現pod執行正常
kubectl get pod pod-resources -n dev
# 接下來,停止Pod
kubectl delete -f pod-resources.yaml
# 編輯pod,修改resources.requests.memory的值為10Gi
vi pod-resources.yaml
# 再次啟動pod
kubectl create -f pod-resources.yaml
# 檢視Pod狀態,發現Pod啟動失敗
kubectl get pod pod-resources -n dev -o wide
# 檢視pod詳情會發現,如下提示
kubectl describe pod pod-resources -n dev
......
Warning FailedScheduling 16s default-scheduler 0/3 nodes are available: 3 Insufficient memory.(記憶體不足)
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需要重啟它直到成功完成
- 初始化容器必須按照定義的順序執行,當且僅當前一個成功之後,後面的一個才能執行
初始化容器有很多的應用場景,下面列出的是最常見的幾個:
- 提供主容器映象中不具備的工具程式或自定義程式碼
- 初始化容器要先於應用容器序列啟動並執行完成,因此可用於延後應用容器的啟動直至其依賴的條件得到滿足
接下來做一個案例,模擬下面這個需求:
假設要以主容器來執行nginx,但是要求在執行nginx之前先要能夠連線上mysql和redis所在伺服器
為了簡化測試,事先規定好mysql(192.168.176.14)
和redis(192.168.176.15)
伺服器的地址
建立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
# ping通表示已經啟動
command: ['sh', '-c', 'until ping 192.168.176.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.176.15 -c 1 ; do echo waiting for reids...; sleep 2; done;']
命令
# 建立pod
kubectl create -f pod-initcontainer.yaml
# 檢視pod狀態
# 發現pod卡在啟動第一個初始化容器過程中,後面的容器不會執行
kubectl get pods pod-initcontainer -n dev
# 動態檢視pod
kubectl get pods pod-initcontainer -n dev -w
# 接下來新開一個shell,為當前伺服器新增兩個ip,觀察pod的變化
ifconfig ens33:1 192.168.176.14 netmask 255.255.255.0 up
ifconfig ens33:2 192.168.176.15 netmask 255.255.255.0 up
鉤子函式
鉤子函式能夠感知自身生命週期中的事件,並在相應的時刻到來時執行使用者指定的程式程式碼。
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.176.3 #主機地址 scheme: HTTP #支援的協議,http或者https ……
接下來,以exec方式為例,演示下鉤子函式的使用,建立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: # 在容器啟動的時候執行一個命令,修改掉nginx的預設首頁內容
command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服務
command: ["/usr/sbin/nginx","-s","quit"]
命令
# 建立pod
kubectl create -f pod-hook-exec.yaml
# 檢視pod
kubectl get pods pod-hook-exec -n dev -o wide
# 訪問pod
curl 10.244.2.23
容器探測
容器探測用於檢測容器中的應用例項是否正常工作,是保障業務可用性的一種傳統機制。如果經過探測,例項的狀態不符合預期,那麼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
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"] # 執行一個檢視檔案的命令
建立pod,觀察效果
# 建立Pod
kubectl create -f pod-liveness-exec.yaml
# 檢視Pod詳情
kubectl describe pods pod-liveness-exec -n dev
# 觀察上面的資訊就會發現nginx容器啟動之後就進行了健康檢查
# 檢查失敗之後,容器被kill掉,然後嘗試進行重啟(這是重啟策略的作用)
# 稍等一會之後,再觀察pod資訊,就可以看到RESTARTS不再是0,而是一直增長
kubectl get pods pod-liveness-exec -n dev
# 當然接下來,可以修改成一個存在的檔案,比如/tmp/hello.txt,再試,結果就正常了......
方式二:TCPSocket
建立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埠
建立pod,觀察效果
# 建立Pod
kubectl create -f pod-liveness-tcpsocket.yaml
# 檢視Pod詳情
kubectl describe pods pod-liveness-tcpsocket -n dev
# 觀察上面的資訊,發現嘗試訪問8080埠,但是失敗了
# 稍等一會之後,再觀察pod資訊,就可以看到RESTARTS不再是0,而是一直增長
kubectl get pods pod-liveness-tcpsocket -n dev
# 當然接下來,可以修改成一個可以訪問的埠,比如80,再試,結果就正常了......
方式三:HTTPGet
建立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: # 其實就是訪問http://127.0.0.1:80/hello
scheme: HTTP #支援的協議,http或者https
port: 80 #埠號
path: /hello #URI地址
建立pod,觀察效果
# 建立Pod
kubectl create -f pod-liveness-httpget.yaml
# 檢視Pod詳情
kubectl describe pod pod-liveness-httpget -n dev
# 觀察上面資訊,嘗試訪問路徑,但是未找到,出現404錯誤
# 稍等一會之後,再觀察pod資訊,就可以看到RESTARTS不再是0,而是一直增長
kubectl get pod pod-liveness-httpget -n dev
# 當然接下來,可以修改成一個可以訪問的路徑path,比如/,再試,結果就正常了......
至此,已經使用liveness Probe演示了三種探測方式,但是檢視livenessProbe的子屬性,會發現除了這三種方式,還有一些其他的配置,在這裡一併解釋下:
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。最小值是1
successThreshold <integer> # 連續探測成功多少次才被認定為成功。預設是1
下面稍微配置兩個,演示下效果即可:
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: /
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:
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 # 設定重啟策略為Never
執行Pod測試
# 建立Pod
kubectl create -f pod-restartpolicy.yaml
# 檢視Pod詳情,發現nginx容器失敗
kubectl describe pods pod-restartpolicy -n dev
# 多等一會,再觀察pod的重啟次數,發現一直是0,並未重啟
kubectl get pods pod-restartpolicy -n dev
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檔案
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 指定排程到node1節點上
#建立Pod
kubectl create -f pod-nodename.yaml
#檢視Pod排程到NODE屬性,確實是排程到了node1節點上
kubectl get pods pod-nodename -n dev -o wide
# 接下來,刪除pod,修改nodeName的值為node3(並沒有node3節點)
kubectl delete -f pod-nodename.yaml
vi pod-nodename.yaml
kubectl create -f pod-nodename.yaml
#再次檢視,發現已經向Node3節點排程,但是由於不存在node3節點,所以pod無法正常執行
kubectl get pods pod-nodename -n dev -o wide
NodeSelector
NodeSelector用於將pod排程到添加了指定標籤的node節點上。它是通過kubernetes的label-selector機制實現的,也就是說,在pod建立之前,會由scheduler使用MatchNodeSelector排程策略進行label匹配,找出目標node,然後將pod排程到目標節點,該匹配規則是強制約束。
接下來,實驗一下:
1 首先分別為node節點新增標籤
kubectl label nodes node1 nodeenv=pro
kubectl label nodes node2 nodeenv=test
2 建立一個pod-nodeselector.yaml檔案,並使用它建立Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定排程到具有nodeenv=pro標籤的節點上
#建立Pod
kubectl create -f pod-nodeselector.yaml
#檢視Pod排程到NODE屬性,確實是排程到了node1節點上
kubectl get pods pod-nodeselector -n dev -o wide
# 接下來,刪除pod,修改nodeSelector的值為nodeenv: xxxx(不存在打有此標籤的節點)
kubectl delete -f pod-nodeselector.yaml
vi pod-nodeselector.yaml
kubectl create -f pod-nodeselector.yaml
#再次檢視,發現pod無法正常執行,Node的值為none
kubectl get pods -n dev -o wide
# 檢視詳情,發現node selector匹配失敗的提示
kubectl describe pods pod-nodeselector -n dev
親和性排程
上面介紹了兩種定向排程的方式,使用起來非常方便,但是也有一定的問題,那就是如果沒有滿足條件的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.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node節點必須滿足指定的所有規則才可以,相當於硬限制
nodeSelectorTerms 節點選擇列表
matchFields 按節點欄位列出的節點選擇器要求列表
matchExpressions 按節點標籤列出的節點選擇器要求列表(推薦)
key 鍵
values 值
operat or 關係符 支援Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 優先排程到滿足指定的規則的Node,相當於軟限制 (傾向)
preference 一個節點選擇器項,與相應的權重相關聯
matchFields 按節點欄位列出的節點選擇器要求列表
matchExpressions 按節點標籤列出的節點選擇器要求列表(推薦)
key 鍵
values 值
operator 關係符 支援In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 傾向權重,在範圍1-100。
關係符的使用說明:
- matchExpressions:
- key: nodeenv # 匹配存在標籤的key為nodeenv的節點
operator: Exists
- key: nodeenv # 匹配標籤的key為nodeenv,且value是"xxx"或"yyy"的節點
operator: In
values: ["xxx","yyy"]
- key: nodeenv # 匹配標籤的key為nodeenv,且value大於"xxx"的節點
operator: Gt
values: "xxx"
requiredDuringSchedulingIgnoredDuringExecution
接下來首先演示一下requiredDuringSchedulingIgnoredDuringExecution
,
建立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: #親和性設定
nodeAffinity: #設定node親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
nodeSelectorTerms:
- matchExpressions: # 匹配env的值在["xxx","yyy"]中的標籤
- key: nodeenv
operator: In
values: ["xxx","yyy"]
# 建立pod
kubectl create -f pod-nodeaffinity-required.yaml
# 檢視pod狀態 (執行失敗)
kubectl get pods pod-nodeaffinity-required -n dev -o wide
# 檢視Pod的詳情
# 發現排程失敗,提示node選擇失敗
kubectl describe pod pod-nodeaffinity-required -n dev
#接下來,停止pod
kubectl delete -f pod-nodeaffinity-required.yaml
# 修改檔案,將values: ["xxx","yyy"]------> ["pro","yyy"]
vi pod-nodeaffinity-required.yaml
# 再次啟動
kubectl create -f pod-nodeaffinity-required.yaml
# 此時檢視,發現排程成功,已經將pod排程到了node1上
kubectl get pods pod-nodeaffinity-required -n dev -o wide
排程失敗
修改之後排程成功
requiredDuringSchedulingIgnoredDuringExecution
接下來再演示一下requiredDuringSchedulingIgnoredDuringExecution
,
建立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: #親和性設定
nodeAffinity: #設定node親和性
preferredDuringSchedulingIgnoredDuringExecution: # 軟限制
- weight: 1
preference:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的標籤(當前環境沒有)
- key: nodeenv
operator: In
values: ["xxx","yyy"]
# 建立pod
kubectl create -f pod-nodeaffinity-preferred.yaml
# 檢視pod狀態 (執行成功)
kubectl get pod pod-nodeaffinity-preferred -n dev
NodeAffinity規則設定的注意事項
NodeAffinity規則設定的注意事項:
1 如果同時定義了nodeSelector和nodeAffinity,那麼必須兩個條件都得到滿足,Pod才能執行在指定的Node上
2 如果nodeAffinity指定了多個nodeSelectorTerms,那麼只需要其中一個能夠匹配成功即可
3 如果一個nodeSelectorTerms中有多個matchExpressions ,則一個節點必須滿足所有的才能匹配成功
4 如果一個pod所在的Node在Pod執行期間其標籤發生了改變,不再符合該Pod的節點親和性需求,則系統將忽略此變化
PodAffinity
PodAffinity主要實現以執行的Pod為參照,實現讓新建立的Pod跟參照pod在一個區域的功能。
PodAffinity的可配置項
首先來看一下PodAffinity
的可配置項:
pod.spec.affinity.podAffinity
requiredDuringSchedulingIgnoredDuringExecution 硬限制
namespaces 指定參照pod的namespace
topologyKey 指定排程作用域
labelSelector 標籤選擇器
matchExpressions 按節點標籤列出的節點選擇器要求列表(推薦)
key 鍵
values 值
operator 關係符 支援In, NotIn, Exists, DoesNotExist.
matchLabels 指多個matchExpressions對映的內容
preferredDuringSchedulingIgnoredDuringExecution 軟限制
podAffinityTerm 選項
namespaces
topologyKey
labelSelector
matchExpressions
key 鍵
values 值
operator
matchLabels
weight 傾向權重,在範圍1-100
topologyKey指定排程時作用域
topologyKey用於指定排程時作用域,例如:
如果指定為kubernetes.io/hostname,那就是以Node節點為區分範圍
如果指定為beta.kubernetes.io/os,則以Node節點的作業系統型別來區分
requiredDuringSchedulingIgnoredDuringExecution
接下來,演示下requiredDuringSchedulingIgnoredDuringExecution
,
1)首先建立一個參照Pod,pod-podaffinity-target.yaml:
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
podenv: pro #設定標籤
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 將目標pod名確指定到node1上
# 啟動目標pod
kubectl create -f pod-podaffinity-target.yaml
# 檢視pod狀況
kubectl get pods pod-podaffinity-target -n dev
2)建立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: #親和性設定
podAffinity: #設定pod親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的標籤
- key: podenv
operator: In
values: ["xxx","yyy"]
topologyKey: kubernetes.io/hostname
上面配置表達的意思是:新Pod必須要與擁有標籤nodeenv=xxx或者nodeenv=yyy的pod在同一Node上,顯然現在沒有這樣pod,接下來,執行測試一下。
# 啟動pod
kubectl create -f pod-podaffinity-required.yaml
# 檢視pod狀態,發現未執行
kubectl get pods pod-podaffinity-required -n dev
# 檢視詳細資訊
kubectl describe pods pod-podaffinity-required -n dev
# 接下來修改 values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必須要與擁有標籤nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
vi pod-podaffinity-required.yaml
# 然後重新建立pod,檢視效果
kubectl delete -f pod-podaffinity-required.yaml
kubectl create -f pod-podaffinity-required.yaml
# 發現此時Pod執行正常
kubectl get pods pod-podaffinity-required -n dev
啟動失敗
檢視詳細原因
有一個汙點,其他兩個節點不符合條件
修改之後重新執行
關於PodAffinity
的 preferredDuringSchedulingIgnoredDuringExecution
,這裡不再演示,也是同理。
PodAntiAffinity
PodAntiAffinity主要實現以執行的Pod為參照,讓新建立的Pod跟參照pod不在一個區域中的功能。
它的配置方式和選項跟PodAffinty是一樣的,這裡不再做詳細解釋,直接做一個測試案例。
1)繼續使用上個案例中目標pod
kubectl get pods -n dev -o wide --show-labels
2)建立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: #親和性設定
podAntiAffinity: #設定pod親和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的標籤
- key: podenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
上面配置表達的意思是:新Pod必須要與擁有標籤nodeenv=pro的pod不在同一Node上,執行測試一下。
# 建立pod
kubectl create -f pod-podantiaffinity-required.yaml
# 檢視pod
# 發現排程到了node2上
kubectl get pods pod-podantiaffinity-required -n dev -o wide
汙點和容忍
汙點(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=makalo:PreferNoSchedule
;然後建立pod1( pod1 可以 )# 為node1設定汙點(PreferNoSchedule) kubectl taint nodes node1 tag=makalo:PreferNoSchedule # 建立pod1 kubectl run taint1 --image=nginx:1.17.1 -n dev kubectl get pods -n dev -o wide
-
修改為node1節點設定一個汙點:
tag=makalo:NoSchedule
;然後建立pod2( pod1 正常 pod2 失敗 )# 為node1設定汙點(取消PreferNoSchedule,設定NoSchedule) kubectl taint nodes node1 tag:PreferNoSchedule- kubectl taint nodes node1 tag=makalo:NoSchedule # 建立pod2 kubectl run taint2 --image=nginx:1.17.1 -n dev kubectl get pods -n dev -o wide
-
修改為node1節點設定一個汙點:
tag=makalo:NoExecute
;然後建立pod3 ( 3個pod都失敗 )
# 為node1設定汙點(取消NoSchedule,設定NoExecute)
kubectl taint nodes node1 tag:NoSchedule-
kubectl taint nodes node1 tag=makalo:NoExecute
# 建立pod3
kubectl run taint3 --image=nginx:1.17.1 -n dev
kubectl get pods -n dev -o wide
master節點預設汙點設定
# 使用kubeadm搭建的叢集,預設就會給master節點新增一個汙點NoSchedule標記,所以pod就不會排程到master節點上.
# 檢視master汙點
kubectl describe node master
容忍(Toleration
上面介紹了汙點的作用,我們可以在node上新增汙點用於拒絕pod排程上來,但是如果就是想將一個pod排程到一個有汙點的node上去,這時候應該怎麼做呢?這就要使用到容忍。
汙點就是拒絕,容忍就是忽略,Node通過汙點拒絕pod排程上去,Pod通過容忍忽略拒絕
下面先通過一個案例看下效果:
- 在上面,已經在node1節點上打上了
NoExecute
的汙點,此時pod是排程不上去的 - 這裡,可以通過給pod新增容忍,然後將其排程上去
建立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" # 要容忍的汙點的key
operator: "Equal" # 操作符 等於
value: "makalo" # 容忍的汙點的value
effect: "NoExecute" # 新增容忍的規則,這裡必須和標記的汙點規則相同
# 新增容忍之前的pod,注意上面的yaml不加容忍配置
kubectl create -f pod-toleration.yaml
kubectl get pods pod-toleration -n dev -o wide
# 新增容忍之後的pod,注意加上容忍配置
kubectl delete -f pod-toleration.yaml
vi pod-toleration.yaml
kubectl create -f pod-toleration.yaml
kubectl get pods pod-toleration -n dev -o wide
新增容忍之前
新增容忍之後
下面看一下容忍的詳細配置:
kubectl explain pod.spec.tolerations
......
FIELDS:
key # 對應著要容忍的汙點的鍵,空意味著匹配所有的鍵
value # 對應著要容忍的汙點的值
operator # key-value的運算子,支援Equal和Exists(預設)
effect # 對應汙點的effect,空意味著匹配所有影響
tolerationSeconds # 容忍時間, 當effect為NoExecute時生效,表示pod在Node上的停留時間