1. 程式人生 > 其它 >五、kubernetes的資源物件之pod基礎

五、kubernetes的資源物件之pod基礎

一、應用容器與Pod資源

叢集環境

OS: 
root@harbor:~# cat /etc/issue
Ubuntu 20.04.2 LTS \n \l

root@harbor:~# uname -r
5.4.0-81-generic

IP分配:
172.168.33.201 harbor.ywx.net  k8s-deploy
172.168.33.202 haproxy01
172.168.33.203 haproxy02
172.168.33.204 ecd01
172.168.33.205 ecd02
172.168.33.206 ecd03
172.168.33.207 k8s-master01
172.168
.33.208 k8s-master02 172.168.33.209 k8s-master03 172.168.33.210 k8s-node01 172.168.33.211 k8s-node02 172.168.33.212 k8s-node03 VIP: 172.168.33.50 api-server的VIP 172.168.33.51 172.168.33.52 172.168.33.53 Kubernetes: v1.21.5 root@k8s-master01:/usr/local/src# kubectl get nodes NAME STATUS ROLES AGE VERSION
172.168.33.207 Ready,SchedulingDisabled master 7d23h v1.21.5 172.168.33.208 Ready,SchedulingDisabled master 7d23h v1.21.5 172.168.33.209 Ready,SchedulingDisabled master 7d23h v1.21.5 172.168.33.210 Ready node 7d23h v1.21.5 172.168.33.211 Ready node 7d23h v1.21.5
172.168.33.212 Ready node 7d23h v1.21.5

kubernetes叢集部署:

https://www.cnblogs.com/yaokaka/p/15308917.html

kubernetes叢集升級:

https://www.cnblogs.com/yaokaka/p/15335719.html

1、什麼是pod

Kubernetes是分散式運行於多個主機之上、負責執行及管理應用程式的“雲作業系統”或“雲原生應用作業系統”,它將Pod作為執行應用的最小化元件和基礎排程單元。因而Pod是Kubernetes資源物件模型中可由使用者建立或部署的最小化應用元件,而大多數其他API資源基本都是負責支撐和擴充套件Pod功能的元件。

https://kubernetes.io/zh/docs/concepts/workloads/pods/

Pod 是可以在 Kubernetes 中建立和管理的、最小的可部署的計算單元。

Pod (就像在鯨魚莢或者豌豆莢中)是一組(一個或多個) 容器; 這些容器共享儲存、網路、以及怎樣執行這些容器的宣告。 Pod 中的容器總是並置(colocated)的並且一同排程,在共享的上下文中執行。 Pod 所建模的是特定於應用的“邏輯主機”,其中包含一個或多個應用容器, 這些容器是相對緊密的耦合在一起的。 在非雲環境中,在相同的物理機或虛擬機器上執行的應用類似於 在同一邏輯主機上執行的雲應用。

說明: 除了 Docker 之外,Kubernetes 支援 很多其他容器執行時, Docker 是最有名的執行時, 使用 Docker 的術語來描述 Pod 會很有幫助。

Pod 的共享上下文包括一組 Linux 名字空間、控制組(cgroup)和可能一些其他的隔離 方面,即用來隔離 Docker 容器的技術。 在 Pod 的上下文中,每個獨立的應用可能會進一步實施隔離。

就 Docker 概念的術語而言,Pod 類似於共享名字空間和檔案系統卷的一組 Docker 容器。

簡單來說:

Pod是Kubernetes建立和管理的最小單元,一個Pod由一個容器或多個容器組成,這些容器共享儲存、網路。

2、Pod資源基礎

現代應用容器技術用來執行單個程序(包括子程序),它在容器中PID名稱空間中的程序號為1(及在容器中為父程序),可直接接收並處理訊號,因而該程序終止也將導致容器終止並退出。這種設計使得容器與內部程序具有共同的生命週期,更容易發現和判定故障,也更利於對單個應用程式按需求進行擴容和縮容。單程序容器可將應用日誌直接送往標準輸出(STDOUT)和標準錯誤(STDERR),有利於讓容器引擎及容器編排工具獲取、儲存和管理。

單程序模型的應用容器顯然不利於有IPC通訊等需求的多個具有強耦合關係的程序間的協同,除非在組織這類容器時人為地讓它們運行於同一核心之上共享IPC名稱空間。於是,跨多個主機執行的容器編排系統需要能夠描述這種容器間的強耦合關係,並確保它們始終能夠被排程至叢集中的同一個節點之上。

配置容器執行時工具(例如Docker CLI)來控制容器組內各容器之間的共享級別:首先建立一個基礎容器作為父容器,而後使用必要的命令選項來建立與父容器共享指定環境的新容器,並管理好這些容器的生命週期即可。Kubernetes使用名為pause的容器作為Pod中所有容器的父容器來支撐這種構想,因而也被稱為Pod的基礎架構容器。若使用者為Pod啟用了PID名稱空間共享功能,pause容器還能夠作為同一Pod的各容器的1號PID程序以回收殭屍程序。不過,Kubernetes預設不會為Pod內的各容器共享PID名稱空間,它依賴於使用者的顯式定。

Pod內的容器共享PID、Network、IPC和UTS名稱空間

PID名稱空間
不同使用者的程序就是通過pid名稱空間隔離開的,且不同名稱空間中可以有相同pid。所有的 LXC程序在Docker中的父程序為Docker程序,每個LXC程序具有不同的名稱空間。同時由 允許巢狀,因此可以很方便的實現巢狀的Docker容器。

Network名稱空間
有了pid名稱空間,每個名稱空間中的pid能夠相互隔離,但是網路埠還是共享host的埠。網路隔離是通過net名稱空間實現的,每個net名稱空間有獨立的網路裝置,IP地址,    路由表,/proc/net目錄。這樣每個容器的網路就能隔離開來。Docker預設採用veth的方式,將容器中的虛擬網絡卡同host上的一個Docker網橋docker0連線在一起。

IPC名稱空間
容器中程序互動還是採用了Linux常見的程序間互動方法(interprocess communication - IPC), 包括訊號量、訊息佇列和共享記憶體等。然而同VM不同的是,容器的程序間互動實際上還是host上具有相同pid名稱空間中的程序間互動,因此需要在IPC資源申請時加入名稱空間資訊,每個IPC資源有一個唯一的32位id。


UTS    名稱空間
UTS("UNIX Time-sharing System")名稱空間允許每個容器擁有獨立的hostname和domain name,使其在網路上可以被視作一個獨立的節點而非主機上的一個程序。

同一Pod中,這些共享PID、IPC、Network和UTS名稱空間的容器彼此間可通過IPC通訊,共享使用主機名和網路介面、IP地址、埠和路由等各種網路資源,因而各容器程序能夠通過lo網路介面通訊且不能使用相同的網路套接字地址。儘管可以把Pod類比為物理機或虛擬機器,但一個Pod內通常僅應該執行具有強耦合關係的容器,否則除了pause以外,只應該存在單個容器,或者只存在單個主容器和一個以上的輔助類容器(例如服務網格中的Sidecar容器等),這也更符合單程序應用容器的設計初衷。

例如:業務應用和資料庫系統就應該分別放在不同的Pod中,雖然它們之間存在通訊,但並不是強耦合關係。將其分別放入不同的Pod中,兩個不同的Pod會被排程到不同的Node節點,能更好的利用分佈於叢集中的計算資源和儲存資源。

3、Pod中容器的模式

1)單容器模式

單容器模式為:將應用程式封裝為應用容器執行。

2)單節點多容器模式

單節點多容器模式:是指跨容器的設計模式,其目的是在單個主機之上同時執行多個共生關係的容器,因而容器管理系統需要將它們作為一個原子單位進行統一排程。Kubernetes編排系統設計的Pod概念就是這個設計模式的實現之一。一個Pod中的所有容器會被排程到同一個node節點上。

若多個容器間存在強耦合關係,它們具有完全相同的生命週期,或者必須運行於同一節點之上時,通常應該將它們置於同一個Pod中,較常見的情況是為主容器並行執行一個助理式管理程序。單節點多容器模式的常見實現有Sidecar(邊車)、介面卡(Adapter)、大使(Ambassador)、初始化(Initializer)容器模式等。

4、Pod的生命週期

Pod物件從建立開始至終止退出之間的時間稱為其生命週期,這段時間裡的某個時間點,Pod會處於某個特定的執行階段或相位(phase),以概括描述其在生命週期中所處的位置。Kubernetes為Pod資源嚴格定義了5種相位,並將特定Pod物件的當前相位儲存在其內部的子物件PodStatus的phase欄位上,因而它總是應該處於其生命程序中以下幾個相位之一。

▪Pending:API Server建立了Pod資源物件並已存入etcd中,但它尚未被排程完成,或仍處於從倉庫中下載容器映象的過程中。
▪Running:Pod已經被排程至某節點,所有容器都已經被kubelet建立完成,且至少有一個容器處於啟動、重啟或執行過程中。
▪Succeeded:Pod中的所有容器都已經成功終止且不會再重啟。
▪Failed:所有容器都已經終止,但至少有一個容器終止失敗,即容器以非0狀態碼退出或已經被系統終止。
▪Unknown:API Server無法正常獲取到Pod物件的狀態資訊,通常是由於其無法與所在工作節點的kubelet通訊所致。

需要注意的是,階段僅是對Pod物件生命週期執行階段的概括性描述,而非Pod或內部容器狀態的綜合彙總,因此Pod物件的status欄位中的狀態值未必一定是可用的相位,它也有可能是Pod的某個錯誤狀態,例如CrashLoopBackOff或Error等。 Pod資源的核心職責是執行和維護稱為主容器的應用程式容器,在其整個生命週期之中的多種可選行為也是圍繞更好地實現該功能而進行。其中,初始化容器(init container)是常用的Pod環境初始化方式,健康狀態檢測(startupProbe、livenessProbe和readinessProbe)為編排工具提供了監測容器執行狀態的程式設計介面,而事件鉤子(preStop和postStart)則賦予了應用容器讀取來自編排工具上自定義事件的機制。儘管健康狀態檢測也可歸入較為重要的操作環節,但這其中僅建立和執行主容器是必要任務,其他都可根據需要在建立Pod物件時按需定義。

一個Pod物件生命週期的執行步驟

1)在啟動包括初始化容器在內的任何容器之前先建立pause基礎容器,它初始化Pod環境併為後續加入的容器提供共享的名稱空間。
2)按順序以序列方式執行使用者定義的各個初始化容器進行Pod環境初始化;任何一個初始化容器執行失敗都將導致Pod建立失敗,並按其restartPolicy的策略進行處理,預設為重啟。
3)待所有初始化容器成功完成後,啟動應用程式容器,多容器Pod環境中,此步驟會並行啟動所有應用容器,例如主容器和Sidecar容器,它們各自按其定義展開其生命週期;本步驟及後面的幾個步驟都將以主容器為例進行說明;容器啟動的那一刻會同時執行主容器上定義的PostStart鉤子事件,該步驟失敗將導致相關容器被重啟。
4)執行容器啟動健康狀態監測(startupProbe),判定容器是否啟動成功;該步驟失敗,同樣參照restartPolicy定義的策略進行處理;未定義時,預設狀態為Success。
5)容器啟動成功後,定期進行存活狀態監測(liveness)和就緒狀態監測(readiness);存活狀態監測失敗將導致容器重啟,而就緒狀態監測失敗會使得該容器從其所屬的Service物件的可用端點列表中移除。
6)終止Pod物件時,會先執行preStop鉤子事件,並在寬限期(terminationGrace-PeriodSeconds)結束後終止主容器,寬限期預設為30秒。

5、pod的建立過程

1)使用者通過kubectl或其他API客戶端提交Pod Spec給API Server。
2)API Server嘗試著將Pod物件的相關資訊存入etcd中,待寫入操作執行完成,API Server即會返回確認資訊至客戶端。
3)Scheduler(排程器)通過其watcher監測到API Server建立了新的Pod物件,於是為該Pod物件挑選一個工作節點並將結果資訊更新至API Server。
4)排程結果資訊由API Server更新至etcd儲存系統,並同步給Scheduler。
5)相應節點的kubelet監測到由排程器綁定於本節點的Pod後會讀取其配置資訊,並由本地容器執行時建立相應的容器啟動Pod物件後將結果回存至API Server。
6)API Server將kubelet發來的Pod狀態資訊存入etcd系統,並將確認資訊傳送至相應的kubelet。

6、pod終止的過程

1)使用者傳送刪除Pod物件的命令。
2)API伺服器中的Pod物件會隨著時間的推移而更新,在寬限期內(預設為30秒),Pod被視為dead。
3)將Pod標記為Terminating狀態。
4)(與第3步同時執行)kubelet在監控到Pod物件轉為Terminating狀態的同時啟動Pod關閉過程。
5)(與第3步同時執行)端點控制器監控到Pod物件的關閉行為時將其從所有匹配到此端點的Service資源的端點列表中移除。
6)如果當前Pod物件定義了preStop鉤子控制代碼,在其標記為terminating後即會以同步方式啟動執行;如若寬限期結束後,preStop仍未執行完,則重新執行第2步並額外獲取一個時長為2秒的小寬限期。
7)Pod物件中的容器程序收到TERM訊號。
8)寬限期結束後,若存在任何一個仍在執行的程序,Pod物件即會收到SIGKILL訊號。
9)Kubelet請求API Server將此Pod資源的寬限期設定為0從而完成刪除操作,它變得對使用者不再可見。
預設情況下,所有刪除操作的寬限期都是30秒,不過kubectl delete命令可以使用--grace-period=<seconds>選項自定義其時長,使用0值則表示直接強制刪除指定的資源,不過此時需要同時為命令使用--force選項。

7、pod的管理命令

建立Pod:
kubectl apply -f pod.yaml
或者使用命令kubectl run nginx --image=nginx
檢視Pod:
kubectl get pods
kubectl describe pod <Pod名稱>
檢視日誌:
kubectl logs <Pod名稱> [-c CONTAINER]
kubectl logs <Pod名稱> [-c CONTAINER] -f
進入容器終端:
kubectl exec <Pod名稱> [-c CONTAINER] --bash
刪除Pod:
kubectl delete <Pod名稱>

二、在Pod中執行應用

Pod資源中可同時存在初始化容器、應用容器和臨時容器3種類型的容器,不過建立並執行一個具體的Pod物件時,僅有應用容器是必選項,並且可以僅為其定義單個容器。

1、單容器Pod資源

配置清單格式

apiVersion: v1  #api server版本
kind: Pod  #資源型別
metadata: #元資料
  name: ... #pod的名稱,在同一個名稱空間下必須唯一
  namespaces: ....  #pod所在的名稱空間,預設為default名稱空間
spec: #容器規劃
  containers: #定義容器,它是一個列表,可以定義一個或多個容器
  - name: ... #容器名稱,同一個Pod中名稱必須唯一
    image: ... #建立容器時使用的映象
    imagePullPolicy: ... #容器映象拉取策略(Alway/IfNotPresent/Never)
    env: #定義容器的環境變數
       #從pod屬性中獲取變數
       - name: MY_NODE_NAME
         valueFrom:
           fieldRef:
             fieldPath: spec.nodeName
       #自定義變數
       - name: ABC
         value: "123456"

image雖為可選欄位,這只是為方便更高級別的管理類資源(例如Deployment等)能覆蓋它以實現某種高階管理功能而設定,對於非控制器管理的自主式Pod(或稱為裸Pod)來說並不能省略該欄位。下面是一個Pod資源清單示例檔案,它僅指定了執行一個由ikubernetes/demoapp:v1.0映象啟動的主容器demo,該Pod物件位於default名稱空間。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: default
spec:
  containers:
  - name: test-pod
    #image: ikubernetes/demoapp:v1.0
    image: harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    imagePullPolicy: IfNotPresent

執行上面的配置清單pod-demo.yaml

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl apply -f pod.yml 
pod/test-pod created

該Pod物件由排程器繫結至特定工作節點後,由相應的kubelet負責建立和維護,實時狀態也將同步給API Server並由其儲存至etcd中。Pod建立並嘗試啟動的過程中,可能會經歷Pending、ContainerCreating、Running等多種不同的狀態,若Pod可正常啟動,則kubectl get pods/POD命令輸出欄位中的狀態(STATUS)則顯示為Running,如下面的命令及結果所示,使用預設的名稱空間時,其中的-n選項及引數default可以省略。

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl describe pod test-pod -n default
Name:         test-pod
Namespace:    default
Priority:     0
Node:         172.168.33.212/172.168.33.212  #node節點的地址
Start Time:   Sat, 02 Oct 2021 09:19:43 +0800
Labels:       <none>
Annotations:  <none>
Status:       Running
IP:           172.20.135.133  #容器的ip地址
IPs:
  IP:  172.20.135.133
Containers:
  test-pod:
    Container ID:   docker://93b8116a2e18a31500de7bdf2203777e52495caba690bf8a977353b8b91999be
    Image:          harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    Image ID:       docker-pullable://harbor.ywx.net/k8s-baseimages/demoapp@sha256:6698b205eb18fb0171398927f3a35fe27676c6bf5757ef57a35a4b055badf2c3
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 02 Oct 2021 09:19:51 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-f5q6n (ro)
......
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  #pod被排程到了172.168.33.212
  Normal  Scheduled  15s   default-scheduler  Successfully assigned default/test-pod to 172.168.33.212
  #拉取映象
  Normal  Pulling    15s   kubelet            Pulling image "harbor.ywx.net/k8s-baseimages/demoapp:v1.0"
  Normal  Pulled     9s    kubelet            Successfully pulled image "harbor.ywx.net/k8s-baseimages/demoapp:v1.0" in 5.621972341s
  #啟動容器
  Normal  Created    8s    kubelet            Created container test-pod
  Normal  Started    8s    kubelet            Started container test-pod

該pod已經被排程到了名為k8s-node03的node節點上,叢集分配的ip為 172.20.135.133

對Pod中執行著的主容器的服務發起訪問請求。映象demoapp預設運行了一個Web服務程式,該服務監聽TCP協議的80埠,映象可通過“/”、/hostname、/user-agent、/livez、/readyz和/configs等路徑服務於客戶端的請求。例如,下面的命令先獲取到Pod的IP地址,而後對其支援的Web資源路徑/和/user-agent分別發出了一個訪問請求:

root@k8s-master01:/apps/k8s-yaml/pod-case# demoIP=$(kubectl get pods test-pod -o jsonpath={.status.podIP})
root@k8s-master01:/apps/k8s-yaml/pod-case# curl $demoIP
iKubernetes demoapp v1.0 !! ClientIP: 172.20.32.128, ServerName: test-pod, ServerIP: 172.20.135.133!

root@k8s-master01:/apps/k8s-yaml/pod-case# curl -s http://$demoIP/user-agent
User-Agent: curl/7.68.0
root@k8s-master01:/apps/k8s-yaml/pod-case# curl -s http://$demoIP/hostname
ServerName: test-pod


root@k8s-master01:/apps/k8s-yaml/pod-case# curl  172.20.135.133
iKubernetes demoapp v1.0 !! ClientIP: 172.20.32.128, ServerName: test-pod, ServerIP: 172.20.135.133!
#測試該容器的健康狀態(該容器的健康狀態必須先做進容器中)
root@k8s-master01:/apps/k8s-yaml/pod-case# curl -s http:// 172.20.135.133/livez
OK

2、容器映象拉取策略

Kubernetes系統支援使用者自定義容器映象檔案的獲取策略,例如在網路資源較為緊張時可以禁止從倉庫中獲取映象檔案,或者不允許使用工作節點本地映象等。容器的imagePullPolicy欄位用於為其指定映象獲取策略,它的可用值包括如下幾個。

▪Always:每次啟動Pod時都要從指定的倉庫下載映象。
▪IfNotPresent:僅本地映象缺失時方才從目標倉庫下載映象。
▪Never:禁止從倉庫下載映象,僅使用本地映象。

對於標籤為latest的映象檔案,其預設的映象獲取策略為Always,其他標籤的映象,預設策略則為IfNotPresent。需要注意的是,從私有倉庫中下載映象時通常需要事先到Registry伺服器認證後才能進行。認證過程要麼需要在相關節點上互動式執行docker login命令,要麼將認證資訊定義為專有的Secret資源,並配置Pod通過imagePullSecretes欄位呼叫此認證資訊完成。

3、刪除Pod

刪除Pod物件則使用kubectl delete命令。

▪命令式命令:kubectl delete pods/NAME。
▪命令式物件配置:kubectl delete -f FILENAME。
若刪除後Pod一直處於Terminating狀態,則可再一次執行刪除命令,並同時使用--force和--grace-period=0選項進行強制刪除。

4、獲取Pod與容器狀態

資源建立或執行過程中偶爾會因故出現異常,此時使用者需要充分獲取相關的狀態及配置資訊以便確定問題所在。另外,在對資源物件進行建立或修改完成後,也需要通過其詳細的狀態資訊來了解操作成功與否。kubectl有多個子命令,用於從不同角度顯示物件的狀態資訊,這些資訊有助於使用者瞭解物件的執行狀態、屬性詳情等。

▪kubectl describe:顯示資源的詳情,包括執行狀態、事件等資訊,但不同的資源型別輸出內容不盡相同。
▪kubectl logs:檢視Pod物件中容器輸出到控制檯的日誌資訊;當Pod中執行有多個容器時,需要使用選項-c指定容器名稱。
▪kubectl exec:在Pod物件某容器內執行指定的程式,其功能類似於docker exec命令,可用於瞭解容器各方面的相關資訊或執行必需的設定操作等,具體功能取決於容器內可用的程式。

1)列印Pod物件的狀態

kubectl describe pods/NAME -n NAMESPACE命令可列印Pod物件的詳細描述資訊,包括events和controllers等關係的子物件等,它會輸出許多欄位,不同的需求場景中,使用者可能會關注不同維度的輸出,但Priority、Status、Containers和Events等欄位通常是重點關注的目標欄位,各級別的多數輸出欄位基本都可以見名知義。

也可以通過kubectl get pods/POD -o yaml|json命令的status欄位來了解Pod的狀態詳情,它儲存有Pod物件的當前狀態。如下命令顯示了pod-demo的狀態資訊。

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl get pods test-pod -n default -o yaml
......
status:
  conditions:
  - lastProbeTime: null  #上次進行探測的時間戳。
    lastTransitionTime: "2021-10-02T01:19:43Z"  #Pod上次發生狀態轉換的時間戳。
    status: "True" #是否為狀態資訊,可取值有True、False和Unknown。
    type: Initialized  #所有的初始化容器都已經成功啟動
  - lastProbeTime: null
    lastTransitionTime: "2021-10-02T01:19:52Z"
    status: "True"
    type: Ready #表示已經就緒,可服務客戶端請求
  - lastProbeTime: null
    lastTransitionTime: "2021-10-02T01:19:52Z"
    status: "True"
    type: ContainersReady #則表示所有容器均已就緒。
  - lastProbeTime: null
    lastTransitionTime: "2021-10-02T01:19:43Z"
    status: "True"
    type: PodScheduled #表示已經與節點繫結
  containerStatuses: #容器級別的狀態資訊
    #容器ID
  - containerID: docker://93b8116a2e18a31500de7bdf2203777e52495caba690bf8a977353b8b91999be
    #映象名稱
    image: harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    #映象ID
    imageID: docker-pullable://harbor.ywx.net/k8s-baseimages/demoapp@sha256:6698b205eb18fb0171398927f3a35fe27676c6bf5757ef57a35a4b055badf2c3
    lastState: {} #前一次的狀態
    name: test-pod
    ready: true #是否就緒
    restartCount: 0 #容器重啟次數
    started: true
    state: #當前狀態
      running:
        startedAt: "2021-10-02T01:19:51Z" #容器啟動時間
  hostIP: 172.168.33.212 #排程的node節點的ip
  phase: Running #Pod當前的相位
  podIP: 172.20.135.133 #當前pod的ip地址
  podIPs: #該Pod的所有IP
  - ip: 172.20.135.133
  qosClass: BestEffort #QOS型別
  startTime: "2021-10-02T01:19:43Z"

上面的命令結果中,conditions欄位是一個稱為PodConditions的陣列,它記錄了Pod所處的“境況”或者“條件”,其中的每個陣列元素都可能由如下6個欄位組成。

▪lastProbeTime:上次進行Pod探測時的時間戳。
▪lastTransitionTime:Pod上次發生狀態轉換的時間戳。
▪message:上次狀態轉換相關的易讀格式資訊。
▪reason:上次狀態轉換原因,用駝峰格式的單個單詞表示。
▪status:是否為狀態資訊,可取值有True、False和Unknown。
▪type:境況的型別或名稱,有4個固定值;PodScheduled表示已經與節點繫結;Ready表示已經就緒,可服務客戶端請求;Initialized表示所有的初始化容器都已經成功啟動;ContainersReady則表示所有容器均已就緒。

containerStatuses欄位描述了Pod中各容器的相關狀態資訊,包括容器ID、映象和映象ID、上一次的狀態、名稱、啟動與否、就緒與否、重啟次數和狀態等。隨著系統的執行,該Pod物件的狀態可能會因各種原因發生變動,例如程式自身bug導致的故障、工作節點資源耗盡引起的驅逐等,使用者可根據需要隨時請求檢視這些資訊,甚至將其納入監控系統中進行實時監控和告警。

2)檢視容器日誌

規範化組織的應用容器一般僅執行單個應用程式,其日誌資訊均通過標準輸出和標準錯誤輸出直接列印至控制檯,kubectl logs POD [-c CONTAINER]命令可直接獲取並列印這些日誌,不過,若Pod物件中僅執行有一個容器,則可以省略-c選項及容器名稱。

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl logs test-pod -n default
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
172.20.32.128 - - [02/Oct/2021 03:52:56] "GET / HTTP/1.1" 200 -
172.20.32.128 - - [02/Oct/2021 03:53:26] "GET /user-agent HTTP/1.1" 200 -
172.20.32.128 - - [02/Oct/2021 03:53:30] "GET /hostname HTTP/1.1" 200 -
172.20.32.128 - - [02/Oct/2021 03:54:00] "GET / HTTP/1.1" 200 -
172.20.32.128 - - [02/Oct/2021 03:54:14] "GET /livez HTTP/1.1" 200 -

需要注意的是,日誌檢視命令僅能用於列印存在於Kubernetes系統上的Pod中容器的日誌,對於已經刪除的Pod物件,其容器日誌資訊將無從獲取。日誌資訊是用於輔助使用者獲取容器中應用程式執行狀態的最有效途徑之一,也是重要的排錯手段,因此通常要使用集中式的日誌伺服器統一收集儲存各Pod物件中容器的日誌資訊。

3)在容器中額外執行其他程式

執行著非互動式程序的容器中預設執行的唯一程序及其子程序啟動後,容器即進入獨立、隔離的執行狀態。對容器內各種詳情的瞭解需要穿透容器邊界進入其中執行其他應用程式進行,kubectl exec可以讓使用者在Pod的某容器中執行使用者所需要的任何存在於容器中的程式。在kubectl logs獲取的資訊不夠全面時,此命令可以通過在Pod中執行其他指定的命令(前提是容器中存在此程式)來輔助使用者獲取更多資訊。一個更便捷的使用介面是直接互動式執行容器中的某shell程式。例如,直接檢視Pod中的容器執行的程序:

#檢視容器中的程序(容器必須可以執行該命令的)
root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl exec test-pod -- ps aux
PID   USER     TIME  COMMAND
    1 root      0:02 python3 /usr/local/bin/demo.py
   17 root      0:00 ps aux
   
#檢視容器地址(容器必須可以執行該命令的)
root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl exec test-pod -- ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
4: eth0@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 46:4e:78:58:12:19 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.20.135.133/32 scope global eth0
       valid_lft forever preferred_lft forever

如果Pod物件中執行時有多個容器,執行程式時還需要使用-c <container_name>選項指定執行程式的容器名稱。

有時候需要開啟容器的互動式shell介面以方便多次執行命令,為kubectl exec命令額外使用-it選項,並指定執行映象中可用的shell程式就能進入互動式介面。如下示例中,命令後的斜體部分表示在容器的互動式介面中執行的命令。

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl exec -it test-pod -- /bin/sh
[root@test-pod /]# hostname
test-pod
[root@test-pod /]# netstat -antlp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1/python3 

5、自定義容器應用與引數

容器映象啟動容器時執行的預設應用程式由其Dockerfile檔案中的ENTRYPOINT指令進行定義,傳遞給程式的引數則通過CMD指令設定,ETRYPOINT指令不存在時,CMD可同時指定程式及其引數。例如,要了解映象harbor.ywx.net/k8s-baseimages/demoapp:v1.0中定義的ENTRYPOINT和CMD,可以在任何存在此映象的節點上執行類似如下命令來獲取:

root@k8s-node03:~# docker inspect harbor.ywx.net/k8s-baseimages/demoapp:v1.0 -f {{.Config.Entrypoint}}
[/bin/sh -c python3 /usr/local/bin/demo.py]

root@k8s-node03:~# docker inspect harbor.ywx.net/k8s-baseimages/demoapp:v1.0 -f {{.Config.Cmd}}
[]

Pod配置中,spec.containers[].command欄位可在容器上指定非映象預設執行的應用程式,且可同時使用spec.containers[].args欄位進行引數傳遞,它們將覆蓋映象中預設定義的引數。若定義了args欄位,該欄位值將作為引數傳遞給映象中預設指定執行的應用程式;而僅定義了command欄位時,其值將覆蓋映象中定義的程式及引數。下面的資源配置清單儲存在pod-demo-with-cmd-and-args.yaml檔案中,它把映象harbor.ywx.net/k8s-baseimages/demoapp:v1.0的預設應用程式修改為/bin/sh -c,引數定義為python3 /usr/local/bin/demo.py -p 8080,其中的-p選項可修改服務監聽的埠為指定的自定義埠。

apiVersion: v1
kind: Pod
metadata:
  name: pod-demo-with-cmd-and-arags
  namespace: default
spec:
  containers:
  - name: demo
    #image: ikubernetes/demoapp:v1.0
    image: harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    #修改容器執行的埠為8080
    command: ["/bin/sh","-c"]
    args: ["python3 /usr/local/bin/demo.py -p 8080"]

下面將上述清單中定義的Pod物件建立到叢集上,驗證其監聽的埠是否從預設的80變為了指定的8080

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl apply -f pod-demo-with-cmd-and-arags.yaml 
pod/pod-demo-with-cmd-and-arags created

NAME                          READY   STATUS    RESTARTS   AGE     IP               NODE             NOMINATED NODE   READINESS GATES
pod-demo-with-cmd-and-arags   1/1     Running   0          37s     172.20.135.134   172.168.33.212   <none>           <none>
test-pod                      1/1     Running   0          4h25m   172.20.135.133   172.168.33.212   <none>           <none>
     <none>
     
#80埠已經無法訪問
root@k8s-master01:~# curl 172.20.135.134
curl: (7) Failed to connect to 172.20.135.134 port 80: Connection refused

#8080埠可以訪問
root@k8s-master01:~# curl 172.20.135.134:8080
iKubernetes demoapp v1.0 !! ClientIP: 172.20.32.128, ServerName: pod-demo-with-cmd-and-arags, ServerIP: 172.20.135.134!

root@k8s-master01:~# kubectl exec pod-demo-with-cmd-and-arags -- netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN    

可以通過修改yaml配置檔案中cmd或entrypoint來修改容器的啟動命令和引數

6、容器的環境變數env

定義args引數,也是向容器中應用程式傳遞配置資訊的常用方式之一,對於非雲原生的應用程式,這幾乎是最簡單的配置方式了。另一個常用配置方式是使用環境變數。

非容器化的傳統管理方式中,複雜應用程式的配置資訊多數由配置檔案進行指定,使用者藉助簡單的文字編輯器完成配置管理。然而,對於容器隔離出的環境中的應用程式,使用者就不得不穿透容器邊界在容器內進行配置編輯並進行過載,複雜且低效。於是,由環境變數在容器啟動時傳遞配置資訊就成了一種受到青睞的方式。

注意
這種方式需要應用程式支援通過環境變數進行配置,否則使用者要在製作Docker映象時通過entrypoint指令碼完成環境變數到程式配置檔案的同步。

向Pod物件中容器環境變數傳遞資料的方法有兩種:env和envFrom。

通過環境變數的配置容器化應用時,需要在容器配置段中巢狀使用env欄位,它的值是一個由環境變數構成的列表。每個環境變數通常由name和value欄位構成。

▪name <string>:環境變數的名稱,必選欄位。
▪value <string>:傳遞給環境變數的值,通過$(VAR_NAME)引用,逃逸格式為$$(VAR_NAME)預設值為空。
▪envFrom: 
    1)fieldRef:是從pod屬性中獲取變數,僅僅支援一下屬性
 metadata.name,metadata.namespace,metadata.labels['<KEY>'],metadata.annotations['<KEY>'],spec.nodeName,spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs
    2)configMapKeyRef:從configmap中獲取變數
    3)resourceFieldRef: 從容器資源中獲取變數,僅僅支援requests和limits的cpu跟memory
    only resources limits and requests(limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu,requests.memory and requests.ephemeral-storage)
    4) secretKeyRef:從secret中獲取變數    

示例中使用映象demoapp中的應用伺服器支援通過HOST與PORT環境變數分別獲取監聽的地址和埠,它們的預設值分別為0.0.0.0和80,下面的配置儲存在清單檔案pod-using-env.yaml中,它分別為HOST和PORT兩個環境變數傳遞了一個不同的值,以改變容器監聽的地址和埠。定義一個MY_NODE_NAME從pod的屬性中獲取nodeName。定義一個LIMIT_CPU從資源中獲取limits.cpu。

apiVersion: v1
kind: Pod
metadata:
  name: pod-using-env
  namespace: default
spec:
  containers:
  - name: demo
    #image: ikubernetes/demoapp:v1.0
    image: harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    env:
    #自定義的變數
    - name: HOST
      value: "127.0.0.1"
    - name: PORT
      value: "8080"
    #從pod屬性中獲取變數
    - name: MY_NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    #從從容器資源中獲取變數
    - name: LIMIT_CPU
      valueFrom:
        resourceFieldRef:
          resource: limits.cpu       

下面將清單檔案中定義的Pod物件建立至叢集中,並檢視應用程式監聽的地址和埠來驗證配置結果:

root@k8s-master01:~# kubectl get pod -n default -o wide|grep pod-using-env
pod-using-env   1/1    Running   0      4m     172.20.58.195    172.168.33.211   <none>     <none>


root@k8s-master01:~# kubectl exec pod-using-env -- netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN    

root@k8s-master01:~# kubectl exec -it pod-using-env -- /bin/sh
root@k8s-master01:~# kubectl exec -it pod-using-env -- /bin/sh
[root@pod-using-env /]# echo $HOST
127.0.0.1
[root@pod-using-env /]# echo $PORT
8080
[root@pod-using-env /]# echo $MY_NODE_NAME
172.168.33.211
[root@pod-using-env /]# echo $LIMIT_CPU
1
[root@pod-using-env /]#

無論它們是否真正被用到,傳遞給容器的環境變數都會直接注入容器的shell環境中,使用printenv一類的命令就能在容器中獲取到所有環境變數的列表。

root@k8s-master01:~# kubectl exec pod-using-env -- printenv|egrep -w "HOST|PORT|MY_NODE_NAME|LIMIT_CPU"
LIMIT_CPU=1
HOST=127.0.0.1
PORT=8080
MY_NODE_NAME=172.168.33.211

三、暴露容器服務

Pod物件的IP地址僅在叢集內可達,它們無法直接接收來自叢集外部客戶端的請求流量,儘管它們的服務可達性不受工作節點邊界的約束,但依然受制於叢集邊界。不考慮通過Service資源進行服務暴露的情況下,服務於叢集外部的客戶端的常用方式有兩種:一種是在其執行的節點上進行埠對映,由節點IP和選定的協議埠向Pod內的應用容器進行DNAT轉發;另一種是讓Pod共享其所在的工作節點的網路名稱空間,應用程序將直接監聽工作節點IP地址和協議埠。

1)容器埠對映

Pod的IP地址處於同一網路平面,無論是否為容器指定了要暴露的埠都不會影響叢集中其他節點之上的Pod客戶端對其進行訪問,這意味著,任何在非本地迴環介面lo上監聽的埠都可直接通過Pod網路被請求。從這個角度來說,容器埠只是資訊性資料,它僅為叢集使用者提供了一個快速瞭解相關Pod物件的可訪問埠的途徑,但顯式指定容器的服務埠可額外為其賦予一個名稱以方便按名稱呼叫。定義容器埠的ports欄位的值是一個列表,由一到多個埠物件組成,它的常用巢狀欄位有如下幾個。

▪containerPort <integer>:必選欄位,指定在Pod物件的IP地址上暴露的容器埠,有效範圍為(0,65536);使用時,需要指定為容器應用程式需要監聽的埠。
▪name <string>:當前埠的名稱標識,必須符合IANA_SVC_NAME規範且在當前Pod內要具有唯一性;此埠名可被Service資源按名呼叫。
▪protocol <string>:埠相關的協議,其值僅支援TCP、SCTP或UDP三者之一,預設為TCP。

需要藉助於Pod所在節點將容器服務暴露至叢集外部時,還需要使用hostIP與hostPort兩個欄位來指定佔用的工作節點地址和埠。如圖所示的Pod A與Pod C可分別通過各自所在節點上指定的hostIP和hostPort服務於客戶端請求。

通過hostIP和hostPort暴露容器服務

▪hostPort <integer>:主機埠,它將接收到的請求通過NAT機制轉發至由container-Port欄位指定的容器埠。
▪hostIP <string>:主機埠要繫結的主機IP,預設為主機之上所有可用的IP地址;該欄位通常使用預設值。

下面的資源配置清單示例(pod-using-hostport.yaml)中定義的demo容器指定了要暴露容器上TCP協議的80埠,並將之命名為http,該容器可通過工作節點的10080埠接入叢集外部客戶端的請求。

apiVersion: v1
kind: Pod
metadata:
  name: pod-using-hostport
  namespace: default
spec:
  containers:
  - name: demo
    #image: ikubernetes/demoapp:v1.0
    image: harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    imagePullPolicy: IfNotPresent
    ports: #定義port
    #port的名稱
    - name: http
      #容器使用的埠
      containerPort: 80
      #協議
      protocol: TCP
      #把pod所在的node節點的10080對映為80埠
      hostPort: 10080

在叢集中建立配置清單中定義的Pod物件後,需獲取其被排程至的目標節點,例如下面第二個命令結果中的k8s-node03/172.168.32.206,而後從叢集外部向該節點的10080埠發起Web請求進行訪問測試:

[root@k8s-master01 apps]# kubectl apply -f pod-using-hostport.yaml 
pod/pod-using-hostport created
#檢視pod-using-hostport在那個node節點
[root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl get pod -n default -o wide | grep pod-using-hostport
pod-using-hostport            1/1     Running   0          3m38s   172.20.85.194    172.168.33.210   <none>           <none>

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl describe pods pod-using-hostport |grep "^Node"
Node:         172.168.33.210/172.168.33.210
Node-Selectors:              <none>


#通過node01/172.168.33.210:10080可以訪問容器服務
root@k8s-master01:/apps/k8s-yaml/pod-case# curl 172.168.33.210:10080
iKubernetes demoapp v1.0 !! ClientIP: 172.168.33.207, ServerName: pod-using-hostport, ServerIP: 172.20.85.194!

注意,hostPort與NodePort型別的Service物件暴露埠的方式不同,NodePort是通過所有節點暴露容器服務,而hostPort則能經由Pod物件所在節點的IP地址進行。但Pod物件繫結的工作節點都由排程器根據其排程機制確定,除非人為地指示排程器將其繫結到指定的工作節點,否則多數情況下其目標節點都難以預測。

2)配置Pod使用節點網路

同一個Pod物件的各容器運行於一個獨立、隔離的Network、UTS和IPC名稱空間中,共享同一個網路協議棧及相關的網路裝置,但也有些特殊的Pod物件需要運行於所在節點的名稱空間中,執行系統級的管理任務(例如檢視和操作節點的網路資源甚至是網路裝置等),或藉助節點網路資源向叢集外客戶端提供服務等。

由kubeadm部署的Kubernetes叢集中的kube-apiserver、kube-controller-manager、kube-scheduler,以及kube-proxy和kube-flannel等通常都是第二種型別的Pod物件。網路名稱空間是Pod級別的屬性,使用者配置的Pod物件,僅需要設定其spec.hostNetwork的屬性為true即可建立共享節點網路名稱空間的Pod物件,如下面儲存在pod-using-hostnetwork.yaml檔案中的配置清單所示。

apiVersion: v1
kind: Pod
metadata:
  name: pod-using-hostnetwork
  namespace: default
spec:
  containers:
  - name: pod-using-hostnetwork
    #image: ikubernetes/demoapp:v1.0
    image: harbor.ywx.net/k8s-baseimages/demoapp:v1.0
    imagePullPolicy: IfNotPresent
  hostNetwork: true

將上面配置清單中定義的pod-using-hostnetwork創建於叢集上,並檢視主機名稱或網路介面的相關屬性資訊以驗證它是否能共享使用工作節點的網路名稱空間。

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl apply -f pod-using-hostnetwork.yaml 
pod/pod-using-hostnetwork created

#檢視pod的ip
root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl get pod -n default -o wide |grep pod-using-hostnetwork
pod-using-hostnetwork         1/1     Running   0          98s     172.168.33.212   172.168.33.212   <none>           <none>

#檢視pod坐在的node節點ip
root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl describe pod pod-using-hostnetwork |grep "^Node"
Node:         172.168.33.212/172.168.33.212
Node-Selectors:              <none>
#hostnetwork中pod的ip為所在node節點的ip

root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl exec pod-using-hostnetwork -- ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:50:56:85:0B:19  
          inet addr:172.168.33.212  Bcast:172.168.255.255  Mask:255.255.0.0
          inet6 addr: fe80::250:56ff:fe85:b19/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1656196 errors:0 dropped:708 overruns:0 frame:0
          TX packets:938938 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1135275631 (1.0 GiB)  TX bytes:73820731 (70.4 MiB)


##hostnetwork中pod的hostname為所在node節點的hostname
root@k8s-master01:/apps/k8s-yaml/pod-case# kubectl exec pod-using-hostnetwork -- hostname
k8s-node03

#訪問node節點ip就可以訪問pod
root@k8s-master01:/apps/k8s-yaml/pod-case#  curl 172.168.33.212
iKubernetes demoapp v1.0 !! ClientIP: 172.168.33.207, ServerName: k8s-node03, ServerIP: 172.168.33.212!

表示該Pod已然共享了其所在節點的UTS名稱空間,以及Network和IPC名稱空間。這意味著,Pod物件中執行容器化應用可在其所在的工作節點的IP地址之上監聽,這可以通過直接向k8s-node03節點發起請求。

與容器埠對映存在的同樣問題是,使用者無法事先預知Pod物件會排程至哪個節點,除非事先指示排程器將Pod繫結至固定的目標節點之上。

四、Pod的重啟策略

Pod物件的應用容器因程式崩潰、啟動狀態檢測失敗、存活狀態檢測失敗或容器申請超出限制的資源等原因都可能導致其被終止,此時是否應該重啟則取決於Pod上的restartPolicy(重啟策略)欄位的定義,該欄位支援以下取值。

1)Always:無論因何原因、以何種方式終止,kubelet都將重啟該Pod,此為預設設定。
2)OnFailure:僅在Pod物件以非0方式退出時才將其重啟。
3)Never:不再重啟該Pod。

需要注意的是,restartPolicy適用於Pod物件中的所有容器,而且它僅用於控制在同一個節點上重新啟動Pod物件的相關容器。首次需要重啟的容器,其重啟操作會立即進行,而再次重啟操作將由kubelet延遲一段時間後進行,反覆的重啟操作的延遲時長依次為10秒、20秒、40秒、80秒、160秒和300秒,300秒是最大延遲時長。 事實上,一旦繫結到一個節點,Pod物件將永遠不會被重新繫結到另一個節點,它要麼被重啟,要麼被終止,直到節點故障、被刪除或被驅逐。

五、資源需求與資源限制

容器在執行時具有多個維度,例如記憶體佔用、CPU佔用和其他資源的消耗等。每個容器都應該宣告其資源需求,並將該資訊傳遞給管理平臺。這些資源需求資訊會在CPU、記憶體、網路、磁碟等維度對平臺執行排程、自動擴充套件和容量管理等方面影響編排工具的決策。

1、資源需求與限制

在Kubernetes上,可由容器或Pod請求與消費的“資源”主要是指CPU和記憶體(RAM),它可統稱為計算資源,另一種資源是事關可用儲存卷空間的儲存資源。

相比較而言,CPU屬於可壓縮型資源,即資源額度可按需彈性變化,而記憶體(當前)則是不可壓縮型資源,對其執行壓縮操作可能會導致某種程度的問題,例如程序崩潰等。目前,資源隔離仍屬於容器級別,CPU和記憶體資源的配置主要在Pod物件中的容器上進行,並且每個資源存在的需求和限制兩種型別。為了表述方便,人們通常把資源配置稱作Pod資源的需求和限制,只不過它是指Pod內所有容器上的某種型別資源的請求與限制總和。

▪資源需求:定義需要系統預留給該容器使用的資源最小可用值,容器執行時可能用不到這些額度的資源,但用到時必須確保有相應數量的資源可用。
▪資源限制:定義該容器可以申請使用的資源最大可用值,超出該額度的資源使用請求將被拒絕;顯然,該限制需要大於等於requests的值,但系統在某項資源緊張時,會從容器回收超出request值的那部分。

在Kubernetes系統上,1個單位的CPU相當於虛擬機器上的1顆虛擬CPU(vCPU)或物理機上的一個超執行緒(Hyperthread,或稱為一個邏輯CPU),它支援分數計量方式,一個核心(1 core)相當於1000個微核心(millicores,以下簡稱為m),因此500m相當於是0.5個核心,即1/2個核心。記憶體的計量方式與日常使用方式相同,預設單位是位元組,也可以使用E、P、T、G、M和K為單位字尾,或Ei、Pi、Ti、Gi、Mi和Ki形式的單位字尾。

2、容器資源需求

的配置清單示例(resource-requests-demo.yaml)中的自主式Pod要求為stress容器確保128MiB的記憶體及1/5個CPU核心(200m)資源可用。Pod執行stress-ng映象啟動一個程序(-m 1)進行記憶體效能壓力測試,滿載測試時stress容器也會盡可能多地佔用CPU資源,另外再啟動一個專用的CPU壓力測試程序(-c 1)。stress-ng是一個多功能系統壓力測試具,master/worker模型,master為主程序,負載生成和控制子程序,worker是負責執行各類特定測試的子程序,例如測試CPU的子程序,以及測試RAM的子程序等。

apiVersion: v1
kind: Pod
metadata:
  name: stress-pod
spec:
  containers:
  - name: stress
    #image: ikubernetes/stress-ng
    image: harbor.ywx.net/k8s-baseimages/stress-ng
    command: ["/usr/bin/stress-ng", "-m 1", "-c 1", "-metrics-brief"]
    resources:
      requests: #定義容器的最少使用資源
        memory: "128Mi"
        cpu: "200m"

上面的配置清單中,stress容器請求使用的CPU資源大小為200m,這意味著一個CPU核心足以確保其以期望的最快方式執行。另外,配置清單中期望使用的記憶體大小為128MiB,不過其執行時未必真的會用到這麼多。考慮到記憶體為非壓縮型資源,當超出時存在因OOM被殺死的可能性,於是請求值是其理想中使用的記憶體空間上限。

叢集中的每個節點都擁有定量的CPU和記憶體資源,排程器將Pod繫結至節點時,僅計算資源餘量可滿足該Pod物件需求量的節點才能作為該Pod執行的可用目標節點。也就是說,Kubernetes的排程器會根據容器的requests屬性定義的資源需求量來判定哪些節點可接收並執行相關的Pod物件,而對於一個節點的資源來說,每執行一個Pod物件,該Pod物件上所有容器requests屬性定義的請求量都要給予預留,直到節點資源被繫結的所有Pod物件瓜分完畢為止。

3、容器資源限制

容器為保證其可用的最少資源量,並不限制可用資源上限,因此對應用程式自身Bug等多種原因導致的系統資源被長時間佔用無計可施,這就需要通過資源限制功能為容器定義資源的最大可用量。一旦定義資源限制,分配資源時,可壓縮型資源CPU的控制閥可自由調節,容器程序也就無法獲得超出其CPU配額的可用值。但是,若程序申請使用超出limits屬性定義的記憶體資源時,該程序將可能被殺死。不過,該程序隨後仍可能會被其控制程序重啟,例如,當Pod物件的重啟策略為Always或OnFailure時,或者容器程序存在有監視和管理功能的父程序等。

配置清單檔案(resource-limits-demo.yaml)中定義使用simmemleak映象執行一個Pod物件,它模擬記憶體洩漏操作不斷地申請使用記憶體資源,直到超出limits屬性中memory欄位設定的值而被殺死。

apiVersion: v1
kind: Pod
metadata:
  name: memleak-pod
  namespace: default
  labels:
    app: memleak
spec:
  containers:
  - name: simmemleak
    image: ikubernetes/simmemleak
    imagePullPolicy: IfNotPresent
    resources:
      requests: #定義容器的最小使用資源
        memory: "64Mi"
        cpu: "1"
      limits: #定義容器的最大使用資源
        memory: "128Mi"
        cpu: "1.2"

OOMKilled表示容器因記憶體耗盡而被終止,因此為limits屬性中的memory設定一個合理值至關重要。與資源需求不同的是,資源限制並不影響Pod物件的排程結果,即一個節點上的所有Pod物件的資源限制數量之和可以大於節點擁有的資源量,即支援資源的過載使用(overcommitted)。不過,這麼一來,一旦記憶體資源耗盡,幾乎必然地會有容器因OOMKilled而終止。 另外需要說明的是,Kubernetes僅會確保Pod物件獲得它們請求的CPU時間額度,它們能否取得額外(throttled)的CPU時間,則取決於其他正在執行作業的CPU資源佔用情況。例如對於總數為1000m的CPU資源來說,容器A請求使用200m,容器B請求使用500m,在不超出它們各自最大限額的前下,則餘下的300m在雙方都需要時會以2 : 5(200m : 500m)的方式進行配置。

注意:

容器可用資源受限於requests和limits屬性中的定義,但容器中可見的資源量依然是節點級別的可用總量。

六、Pod服務質量類別

Kubernetes允許節點的Pod物件過載使用資源,這意味著節點無法同時滿足繫結其上的所有Pod物件以資源滿載的方式執行。因而在記憶體資源緊缺的情況下,應該以何種次序終止哪些Pod物件就變成了問題。事實上,Kubernetes無法自行對此做出決策,它需要藉助於Pod物件的服務質量和優先順序等完成判定。根據Pod物件的requests和limits屬性,Kubernetes把Pod物件歸類到BestEffort、Burstable和Guaranteed這3個服務質量類別(Quality of Service,QoS)類別下。

▪Guaranteed:Pod物件為其每個容器都設定了CPU資源需求和資源限制,且二者具有相同值;同時為每個容器都設定了記憶體資需求和記憶體限制,且二者具有相同值。這類Pod物件具有最高級別服務質量。
▪Burstable:至少有一個容器設定了CPU或記憶體資源的requests屬性,但不滿足Guaranteed類別的設定要求,這類Pod物件具有中等級別服務質量。
▪BestEffort:不為任何一個容器設定requests或limits屬性,這類Pod物件可獲得的服務質量為最低級別。

一旦記憶體資源緊缺,BestEffort類別的容器將首當其衝地被終止,因為系統不為其提供任何級別的資源保證,但換來的好處是,它們能夠做到儘可能多地佔用資源。若此時系統上已然不存任何BestEffort類別的容器,則接下來將輪到Burstable類別的Pod被終止。Guaranteed類別的容器擁有最高優先順序,它們不會被殺死,除非其記憶體資源需求超限,或者OOM時沒有其他更低優先順序的Pod物件存在。 每個執行狀態的容器都有其OOM評分,評分越高越優先被殺死。OOM評分主要根據兩個維度進行計算:由服務質量類別繼承而來的預設分值,以及容器的可用記憶體資源比例,而同等類別的Pod物件的預設分值相同。下面的程式碼片段取自pkg/kubelet/qos/policy.go原始碼檔案,它們定義的是各種類別的Pod物件的OOM調節(Adjust)分值,即預設分值。其中,Guaranteed類別Pod資源的Adjust分值為–998,而BestEffort類別的預設分值為1000,Burstable類別的Pod資源的Adjust分值經由相應的演算法計算得出。

const (
    PodInfraOOMAdj        int = -998
    KubeletOOMScoreAdj    int = -999
    DockerOOMScoreAdj     int = -999
    KubeProxyOOMScoreAdj  int = -999
    guaranteedOOMScoreAdj int = -998
    besteffortOOMScoreAdj int = 1000
)

因此,同等級別優先順序的Pod資源在OOM時,與自身的requests屬性相比,其記憶體佔用比例最大的Pod物件將先被殺死。例如,下圖中的同屬於Burstable類別的Pod A將先於Pod B被殺死,雖然其記憶體用量小,但與自身的requests值相比,它的佔用比例為95%,要大於Pod B的80%。

需要特別說明的是,OOM是記憶體耗盡時的處理機制,與可壓縮型資源CPU無關,因此CPU資源的需求無法得到保證時,Pod物件僅僅是暫時獲取不到相應的資源來執行而已。

I have a dream so I study hard!!!