k8s基本單位Pod

misakivv發表於2024-07-21

目錄
  • 一、概述
  • 二、Pod的基本操作
    • 1、建立Pod
      • 1.1、命令列方式啟動
      • 1.2、建立Deployment資源清單啟動
      • 1.3、直接建立Pod(資源清單)
      • 1.4、直接建立(kubectl run)
    • 2、查詢
      • 2.1、查詢指定名稱空間下所有的Pod
      • 2.2、查詢指定Pod的資訊
      • 2.3、對Pod狀態進行實時監控
      • 2.4、格式化輸出
      • 2.5、顯示詳細資訊(包括Events)
      • 2.6、檢視Pod的日誌資訊
      • 2.7、顯示Pod的對應標籤
    • 3、修改Pod
      • 3.1、修改已存在的Pod屬性(不是由控制器建立的)
      • 3.2、修改已存在的Pod屬性(由控制器建立的)
      • 3.3、edit線上編輯
    • 4、刪除Pod
      • 4.1、未經Deployment建立的Pod
      • 4.2、透過Deployment建立的Pod
  • 二、Pod的生命週期
    • 1、Pod的狀態
    • 2、重啟策略
    • 3、初始化容器
    • 4、pod hook
      • 4.1、兩種鉤子函式
      • 4.2、兩種實現方式
      • 4.3、PostStart鉤子函式
      • 4.4.、PreStop鉤子函式
  • 三、Pod健康檢查
    • 1、Kubernetes中三種主要的探針型別
      • 1.1、存活探針(Liveness Probes)
      • 1.2、 就緒探針(Readiness Probes)
      • 1.3、啟動探針(Startup Probes)
    • 2、探針配置引數
    • 3、探針狀態
    • 4、示例(存活探針--exec)
      • 4.1、編寫資源清單
      • 4.2、.spec.containers.args
      • 4.3、建立該pod
      • 4.4、檢視Pod的Event
    • 5、示例(存活探針--HTTP GET)
      • 5.1、編寫資源清單
  • 四、Pod資源配置
    • 1、CGroup 裡面對於 CPU 資源的單位換算
    • 2、建立容器的資源清單
      • 2.1、編寫資源清單
      • 2.2、建立該Pod
      • 2.3、檢視被排程到的節點
      • 2.4、檢視Pod裡的容器
      • 2.5、檢視主容器的資訊

一、概述

Pod 是 Kubernetes 中最小的排程和管理單元,它代表著叢集中執行的一個或多個容器例項。在一個 Pod 中,所有容器共享相同的網路名稱空間、程序名稱空間和儲存卷,因此它們可以互相通訊和共享資料。Pod 可以透過控制器進行建立、擴縮容和更新等操作。

img

二、Pod的基本操作

1、建立Pod

1.1、命令列方式啟動

kubectl create deploy (pod控制器名稱) [引數] 
kubectl create deploy nginx-deployment01 --image=nginx --port=8088 -n dev1
deployment.apps/nginx-deployment created

image-20240717224731570

–-image 指定Pod的映象

–port 指定埠

–n 指定namespace

1.2、建立Deployment資源清單啟動

nginx-deployment02.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment02
  namespace: dev2
  labels:
    chapter: first-app
    function: test
spec:
  selector:
     matchLabels:
       app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name : nginx02
        image: nginx
        ports:
        - containerPort: 8081
kubectl apply -f nginx-deployment02.yaml
deployment.apps/nginx-deployment02 created

image-20240717225057323

1.3、直接建立Pod(資源清單)

apiVersion: v1
kind: Pod
metadata:
  name: nginx03
  namespace: dev3
  labels:
    function: test
    app: nginx
spec:
  containers:
  - image: nginx:latest
    name: pod
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
kubectl apply -f pod-nginx03.yaml
pod/nginx03 created

image-20240717225557969

1.4、直接建立(kubectl run)

kubectl run pod nginx04 --image=nginx -n dev4

image-20240717225914446

2、查詢

2.1、查詢指定名稱空間下所有的Pod

kubectl get pod -n <namespace>

2.2、查詢指定Pod的資訊

kubectl get pods <pod_name> -n <namespace>

2.3、對Pod狀態進行實時監控

kubectl get pods -n <namespace> -w

2.4、格式化輸出

kubectl get pods -n <namespace> -o json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-as-json|jsonpath-file|custom-columns-file|custom-columns|wide

2.5、顯示詳細資訊(包括Events)

kubectl describe pods <pod_name> -n <namespace>

2.6、檢視Pod的日誌資訊

kubectl logs <pod_name> -n <namespace>

2.7、顯示Pod的對應標籤

kubectl get pod <pod_name> -n <namespace> --show-labels

3、修改Pod

3.1、修改已存在的Pod屬性(不是由控制器建立的)

kubectl replcae -f <createpods.yaml>

前提是Pod不是由更高階別的控制器(如Deployment)管理的。

  • 修改建立pod的資源清單

cp pod-nginx03.yaml replcae-pod-nginx03.yaml && vim replcae-pod-nginx03.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx03
  namespace: dev3
  labels:
    function: replace
    app: nginx
spec:
  containers:
  - image: nginx:latest
    name: pod
    command: ["sh", "-c", "echo 'Hello Kubernetes replaced!'; sleep 3600"]
    ports:
    - name: nginx-port
      containerPort: 80
      protocol: TCP
  • 重建Pod
kubectl replace -f replcae-pod-nginx03.yaml --force

image-20240717232252492

#Pod有很多屬性是無法修改的,如果一定要修改,需要加上 --force 引數,相當於重建 Pod

  • 檢視這個Pod的日誌
kubectl logs nginx03 -n dev3

image-20240717233130210

3.2、修改已存在的Pod屬性(由控制器建立的)

  • 檢視原本的Deployment資源清單
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment02
  namespace: dev2
  labels:
    chapter: first-app
    function: test
spec:
  selector:
     matchLabels:
       app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name : nginx02
        image: nginx
        ports:
        - containerPort: 8081
  • 修改後的Deployment資源清單
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment02
  namespace: dev2
  labels:
    chapter: first-app
    function: test
spec:
  selector:
     matchLabels:
       app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name : nginx02
        image: nginx
        ports:
        - containerPort: 8081
        command: ["sh", "-c", "echo 'Hello Kubernetes updated!'; sleep 3600"]
  • apply -f 更新Deployment
kubectl apply -f nginx-deployment02.yaml

image-20240717233952316

image-20240717234325312

3.3、edit線上編輯

kubectl edit nginx-deployment02-58759c87cb-dvg62 -n dev2

:wq儲存退出後也可以達到更新的效果

4、刪除Pod

4.1、未經Deployment建立的Pod

kubectl delete <pod_name> -n <namespace>

image-20240718232823297

4.2、透過Deployment建立的Pod

kubectl delete -f <createpods.yaml>

image-20240718234220144

二、Pod的生命週期

img

1、Pod的狀態

我們可以透過 kubectl explain pod.status 命令來了解關於 Pod 狀態的一些資訊

Pod 的狀態定義在 PodStatus 物件中,其中有一個 phase 欄位,下面是 phase 的可能取值:

  • 掛起(Pending):Pod 資訊已經提交給了叢集,但是還沒有被排程器排程到合適的節點或者 Pod 裡的映象正在下載

  • 執行中(Running):該 Pod 已經繫結到了一個節點上,Pod 中所有的容器都已被建立。至少有一個容器正在執行,或者正處於啟動或重啟狀態

  • 成功(Succeeded):Pod 中的所有容器都被成功終止,並且不會再重啟

  • 失敗(Failed):Pod 中的所有容器都已終止了,並且至少有一個容器是因為失敗終止。也就是說,容器以非0狀態退出或者被系統終止

  • 未知(Unknown):因為某些原因無法取得 Pod 的狀態,通常是因為與 Pod 所在主機通訊失敗導致的

除此之外,PodStatus 物件中還包含一個 PodCondition 的陣列,裡面包含的屬性有:

  • lastProbeTime:最後一次探測 Pod Condition 的時間戳。

  • lastTransitionTime:上次 Condition 從一種狀態轉換到另一種狀態的時間。

  • message:上次 Condition 狀態轉換的詳細描述。

  • reason:Condition 最後一次轉換的原因。

  • status:Condition 狀態型別,可以為 “True”, “False”, and “Unknown”.

  • type:Condition 型別,包括以下方面:

    • PodScheduled(Pod 已經被排程到其他 node 裡)
    • Ready(Pod 能夠提供服務請求,可以被新增到所有可匹配服務的負載平衡池中)
    • Initialized(所有的init containers已經啟動成功)
    • Unschedulable(排程程式現在無法排程 Pod,例如由於缺乏資源或其他限制)
    • ContainersReady(Pod 裡的所有容器都是 ready 狀態)

2、重啟策略

我們可以透過配置restartPolicy欄位來設定 Pod 中所有容器的重啟策略,其可能值為Always,OnFailure 和 Never,預設值為 Always。restartPolicy 僅指透過 kubelet 在同一節點上重新啟動容器。透過 kubelet 重新啟動的退出容器將以指數增加延遲(10s,20s,40s…)重新啟動,上限為 5 分鐘,並在成功執行 10 分鐘後重置。不同型別的的控制器可以控制 Pod 的重啟策略:

  • Job:適用於一次性任務如批次計算,任務結束後 Pod 會被此類控制器清除。Job 的重啟策略只能是"OnFailure"或者"Never"。

  • Replication Controller, ReplicaSet, or Deployment,此類控制器希望 Pod 一直執行下去,它們的重啟策略只能是"Always"。

  • DaemonSet:每個節點上啟動一個 Pod,很明顯此類控制器的重啟策略也應該是"Always"。

3、初始化容器

瞭解了 Pod 狀態後,首先來了解下 Pod 中最新啟動的 Init Container,也就是我們平時常說的初始化容器Init Container就是用來做初始化工作的容器,可以是一個或者多個,如果有多個的話,這些容器會按定義的順序依次執行。我們知道一個 Pod 裡面的所有容器是共享資料卷和Network Namespace 的,所以Init Container裡面產生的資料可以被主容器使用到。從上面的 Pod 生命週期的圖中可以看出初始化容器是獨立與主容器之外的,只有所有的初始化容器執行完之後,主容器才會被啟動。那麼初始化容器有哪些應用場景呢:

  • 等待其他模組 Ready:這個可以用來解決服務之間的依賴問題,比如我們有一個 Web 服務,該服務又依賴於另外一個資料庫服務,但是在我們啟動這個 Web 服務的時候我們並不能保證依賴的這個資料庫服務就已經啟動起來了,所以可能會出現一段時間內 Web 服務連線資料庫異常。要解決這個問題的話我們就可以在 Web 服務的 Pod 中使用一個 InitContainer,在這個初始化容器中去檢查資料庫是否已經準備好了,準備好了過後初始化容器就結束退出,然後我們主容器的 Web 服務才被啟動起來,這個時候去連線資料庫就不會有問題了。

  • 做初始化配置:比如叢集裡檢測所有已經存在的成員節點,為主容器準備好叢集的配置資訊,這樣主容器起來後就能用這個配置資訊加入叢集。

  • 其它場景:如將 Pod 註冊到一箇中央資料庫、配置中心等。

比如現在我們來實現一個功能,在 Nginx Pod 啟動之前去重新初始化首頁內容,如下所示的資源清單

init-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: init-demo
spec:
  volumes:
  - name: workdir
    emptyDir: {}
  initContainers:
  - name: install
    image: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://www.baidu.com
    volumeMounts:
    - name: workdir
      mountPath: "/work-dir"
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html

上面的資源清單中我們首先在 Pod 頂層宣告瞭一個名為 workdir 的 Volume,前面我們用了 hostPath 的模式,這裡我們使用的是 emptyDir{},這個是一個臨時的目錄,資料會儲存在 kubelet 的工作目錄下面,生命週期等同於 Pod 的生命週期。

然後我們定義了一個初始化容器,該容器會下載一個 html 檔案到 /work-dir 目錄下面,但是由於我們又將該目錄宣告掛載到了全域性的 Volume,同樣的主容器 nginx 也將目錄 /usr/share/nginx/html 宣告掛載到了全域性的 Volume,所以在主容器的該目錄下面會同步初始化容器中建立的 index.html 檔案。

  • 直接應用資源清單來建立上面的Pod
kubectl apply -f init-pod.yaml

image-20240718235416632

  • 建立完成後可以檢視該 Pod 的狀態
kubectl get pods -n default

image-20240718235558415

可以發現 Pod 現在的狀態處於 Init:0/1 狀態,意思就是現在第一個初始化容器還在執行過程中

  • 此時我們可以檢視 Pod 的詳細資訊
kubectl describe pods init-demo -n default
Name:         init-demo
Namespace:    default
Priority:     0
Node:         k8s-node2/192.168.112.60
Start Time:   Thu, 18 Jul 2024 23:53:56 +0800
Labels:       <none>
Annotations:  <none>
Status:       Pending
IP:           10.244.2.41
IPs:
  IP:  10.244.2.41
Init Containers:
  install:
    Container ID:  docker://d7ab75ce40d80daa676a547482ee32eba9f2488a9c791051fb5aa5ddcfbcc5b1
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:5acba83a746c7608ed544dc1533b87c737a0b0fb730301639a0179f9344b1678
    Port:          <none>
    Host Port:     <none>
    Command:
      wget
      -O
      /work-dir/index.html
      http://www.baidu.com
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Thu, 18 Jul 2024 23:56:21 +0800
      Finished:     Thu, 18 Jul 2024 23:56:24 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2kmc4 (ro)
      /work-dir from workdir (rw)
Containers:
  nginx:
    Container ID:
    Image:          nginx
    Image ID:
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Waiting
      Reason:       PodInitializing
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /usr/share/nginx/html from workdir (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-2kmc4 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             False
  ContainersReady   False
  PodScheduled      True
Volumes:
  workdir:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  kube-api-access-2kmc4:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason     Age                  From               Message
  ----     ------     ----                 ----               -------
  Normal   Scheduled  2m49s                default-scheduler  Successfully assigned default/init-demo to k8s-node2
  Warning  Failed     64s                  kubelet            Failed to pull image "busybox": rpc error: code = Unknown desc = Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: TLS handshake timeout
  Warning  Failed     64s                  kubelet            Error: ErrImagePull
  Normal   BackOff    63s                  kubelet            Back-off pulling image "busybox"
  Warning  Failed     63s                  kubelet            Error: ImagePullBackOff
  Normal   Pulling    51s (x2 over 2m49s)  kubelet            Pulling image "busybox"
  Normal   Pulled     25s                  kubelet            Successfully pulled image "busybox" in 26.135324609s
  Normal   Created    25s                  kubelet            Created container install
  Normal   Started    25s                  kubelet            Started container install
  Normal   Pulling    21s                  kubelet            Pulling image "nginx"

從上面的描述資訊裡面可以看到初始化容器已經啟動了,現在處於 Running 狀態,所以還需要稍等,到初始化容器執行完成後退出初始化容器會變成 Completed 狀態,然後才會啟動主容器。待到主容器也啟動完成後,Pod 就會變成Running 狀態

  • 然後我們去訪問下 Pod 主頁,驗證下是否有我們初始化容器中下載的頁面資訊:
kuebctl get pods -o wide

image-20240718235928033

 curl 10.244.2.41

image-20240719000021369

4、pod hook

我們知道 Pod 是 Kubernetes 叢集中的最小單元,而 Pod 是由容器組成的,所以在討論 Pod 的生命週期的時候我們可以先來討論下容器的生命週期。

實際上 Kubernetes 為我們的容器提供了生命週期的鉤子,就是我們說的Pod Hook,Pod Hook 是由 kubelet 發起的,當容器中的程序啟動前或者容器中的程序終止之前執行,這是包含在容器的生命週期之中。我們可以同時為 Pod 中的所有容器都配置 hook。

4.1、兩種鉤子函式

Kubernetes 為我們提供了兩種鉤子函式:

  • PostStart:這個鉤子在容器建立後立即執行。但是,並不能保證鉤子將在容器 ENTRYPOINT 之前執行,因為沒有引數傳遞給處理程式。主要用於資源部署、環境準備等。不過需要注意的是如果鉤子花費太長時間以至於不能執行或者掛起,容器將不能達到 running 狀態。

  • PreStop:這個鉤子在容器終止之前立即被呼叫。它是阻塞的,意味著它是同步的,所以它必須在刪除容器的呼叫發出之前完成。主要用於優雅關閉應用程式、通知其他系統等。如果鉤子在執行期間掛起,Pod 階段將停留在 running 狀態並且永不會達到 failed 狀態。

如果 PostStart 或者 PreStop 鉤子失敗, 它會殺死容器。所以我們應該讓鉤子函式儘可能的輕量。當然有些情況下,長時間執行命令是合理的, 比如在停止容器之前預先儲存狀態。

4.2、兩種實現方式

  • Exec - 用於執行一段特定的命令,不過要注意的是該命令消耗的資源會被計入容器。

  • HTTP - 對容器上的特定的端點執行 HTTP 請求。

4.3、PostStart鉤子函式

以下示例中,定義了一個 Nginx Pod,其中設定了 PostStart 鉤子函式,即在容器建立成功後,寫入一句話到 /usr/share/message 檔案中:

pod-poststart.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hook-demo1
spec:
  containers:
  - name: hook-demo1
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
  • 應用資源清單
kubectl apply -f pod-poststart.yaml
 
kubectl get pods
 
NAME                            READY   STATUS    RESTARTS   AGE
hook-demo1                      1/1     Running   0          43s
  • 建立成功後檢視容器中的 /usr/share/message 檔案是否內容正確

image-20240720225049900

4.4.、PreStop鉤子函式

當使用者請求刪除含有 Pod 的資源物件時(如 Deployment 等),K8S 為了讓應用程式優雅關閉(即讓應用程式完成正在處理的請求後,再關閉軟體),K8S 提供兩種資訊通知:

  • 預設:K8S 通知 node 執行docker stop命令,docker 會先向容器中 PID 為 1 的程序傳送系統訊號SIGTERM,然後等待容器中的應用程式終止執行,如果等待時間達到設定的超時時間,或者預設超時時間(30s),會繼續傳送SIGKILL的系統訊號強行 kill 掉程序

  • 使用 Pod 生命週期(利用PreStop回撥函式),它在傳送終止訊號之前執行預設所有的優雅退出時間都在30秒內。kubectl delete 命令支援 --grace-period=選項,這個選項允許使用者用他們自己指定的值覆蓋預設值。值'0'代表強制刪除 pod。 在 kubectl 1.5 及以上的版本里,執行強制刪除時必須同時指定 --force --grace-period=0。

強制刪除一個 pod 是從叢集狀態還有 etcd 裡立刻刪除這個 pod,只是當 Pod 被強制刪除時, APIServer 不會等待來自 Pod 所在節點上的 kubelet 的確認資訊:pod 已經被終止。在 API 裡 pod 會被立刻刪除,在節點上, pods 被設定成立刻終止後,在強行殺掉前還會有一個很小的寬限期。

以下示例中,定義了一個 Nginx Pod,其中設定了PreStop鉤子函式,即在容器退出之前,優雅的關閉 Nginx

pod-prestop.yaml

apiVersion: v1
kind: Pod
metadata:
  name: hook-demo2
spec:
  containers:
  - name: hook-demo2
    image: nginx
    lifecycle:
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]  # 優雅退出
 
---
apiVersion: v1
kind: Pod
metadata:
  name: hook-demo3
spec:
  volumes:
  - name: message
    hostPath:
      path: /tmp
  containers:
  - name: hook-demo2
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: message
      mountPath: /usr/share/
    lifecycle:
      preStop:
        exec:
          command: ['/bin/sh', '-c', 'echo Hello from the preStop Handler > /usr/share/message']

上面定義的兩個 Pod,一個是利用 preStop 來進行優雅刪除,另外一個是利用 preStop 來做一些資訊記錄的事情

  • 同樣直接應用資源清單建立上面的 Pod:
kubectl apply -f pod-prestop.yaml

image-20240720230525988

  • 檢視對應Pod狀態
kubectl get pods |grep ^hook

image-20240721102145735

建立完成後,我們可以直接刪除 hook-demo2 這個 Pod,在容器刪除之前會執行 preStop 裡面的優雅關閉命令,這個用法在後面我們的滾動更新的時候用來保證我們的應用零當機非常有用。

第二個 Pod 我們宣告瞭一個 hostPath 型別的 Volume,在容器裡面宣告掛載到了這個 Volume,所以當我們刪除 Pod,退出容器之前,在容器裡面輸出的資訊也會同樣的儲存到宿主機(一定要是 Pod 被排程到的目標節點)的 /tmp 目錄下面

  • 檢視 hook-demo3 這個 Pod 被排程的節點
kubectl describe pods hook-demo3

image-20240721102637516

可以看到這個 Pod 被排程到了 k8s-node1 這個節點上,我們可以先到該節點上檢視 /tmp 目錄下面目前沒有想要的內容:

ls /tmp/

image-20240721102915757

  • 刪除hook-demo3
kubectl delete pod hook-demo3 

image-20240721103029773

現在我們來刪除 hook-demo3 這個 Pod,安裝我們的設定在容器退出之前會執行 preStop 裡面的命令,也就是會往 message 檔案中輸出一些資訊

  • 回到被排程到的節點/tmp/目錄下檢視
ls /tmp

cat /tmp/message   

image-20240721103234815

另外 Hook 呼叫的日誌沒有暴露個給 Pod,所以只能透過 describe 命令來獲取,如果有錯誤將可以看到 FailedPostStartHook 或 FailedPreStopHook 這樣的 event。

三、Pod健康檢查

在Kubernetes中,Pod健康檢查是透過使用探針(probes)來實現的,這些探針用於確定Pod中的容器是否處於健康狀態。健康檢查機制允許Kubernetes系統在容器出現問題時做出響應,例如重啟容器或從服務路由中移除容器。

1、Kubernetes中三種主要的探針型別

1.1、存活探針(Liveness Probes)

存活探針用於檢查容器是否正在執行並響應。如果存活探針失敗,Kubernetes將根據Pod的restartPolicy重啟容器。這通常用於檢測應用程式的崩潰或陷入死鎖的情況。存活探針可以配置為以下幾種型別:

  • HTTP GET:對容器的 IP 地址上指定埠和路徑執行 HTTP GET 請求。如果響應的狀態碼大於等於 200 且小於 400,則診斷被認為是成功的。

  • TCP Socket:對容器的 IP 地址上的指定埠執行 TCP 檢查。如果埠開啟,則診斷被認為是成功的。 如果遠端系統(容器)在開啟連線後立即將其關閉,這算作是健康的。

  • Exec:在容器內執行指定命令。如果命令退出時返回碼為 0 則認為診斷成功。

  • gRPC:使用 gRPC 執行一個遠端過程呼叫。 目標應該實現 gRPC 健康檢查。 如果響應的狀態是 "SERVING",則認為診斷成功。

1.2、 就緒探針(Readiness Probes)

就緒探針用於確認容器是否準備好接收流量。如果就緒探針失敗,Kubernetes將不會把該Pod視為服務的活動例項,這意味著不會將網路流量路由到該Pod。一旦就緒探針開始成功,Pod將開始接收流量。就緒探針可以是以下幾種型別:

  • HTTP GET

  • TCP Socket

  • Exec

  • gRPC

1.3、啟動探針(Startup Probes)

啟動探針用於檢測容器是否已啟動並執行。在啟動探針成功之前,其他探針(如存活探針和就緒探針)不會執行。這有助於避免在容器初始化階段錯誤地重啟或標記容器未準備好。啟動探針同樣可以配置為:

  • HTTP GET

  • TCP Socket

  • Exec

  • gRPC

2、探針配置引數

探針配置包含幾個重要的引數:

  • initialDelaySeconds:探針開始執行前等待的秒數。

  • periodSeconds:探針執行之間的間隔秒數。

  • timeoutSeconds:探針執行超時秒數。

  • successThreshold:連續成功多少次後探針被認為是成功的。

  • failureThreshold:連續失敗多少次後探針被認為是失敗的。

3、探針狀態

探針的結果可以是:

  • Success:容器透過了探針的檢查。

  • Failure:容器沒有透過探針的檢查。

  • Unknown:探針執行失敗或未知狀態,此時不採取任何行動,kubelet會繼續檢查。

4、示例(存活探針--exec)

用 exec 執行命令的方式來檢測容器的存活

4.1、編寫資源清單

liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5
  • periodSeconds:表示讓 kubelet 每隔5秒執行一次存活探針,也就是每5秒執行一次上面的cat /tmp/healthy命令,如果命令執行成功了,將返回0,那麼 kubelet 就會認為當前這個容器是存活的,如果返回的是非0值,那麼 kubelet 就會把該容器殺掉然後重啟它。預設是10秒,最小1秒。
  • initialDelaySeconds:表示在第一次執行探針的時候要等待5秒,這樣能夠確保我們的容器能夠有足夠的時間啟動起來。大家可以想象下,如果你的第一次執行探針等候的時間太短,是不是很有可能容器還沒正常啟動起來,所以存活探針很可能始終都是失敗的,這樣就會無休止的重啟下去了

4.2、.spec.containers.args

我們在容器啟動的時候,執行了如下命令:

/bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"

意思是說在容器最開始的30秒內建立了一個/tmp/healthy檔案,在這30秒內執行cat /tmp/healthy命令都會返回一個成功的返回碼。30 秒後,我們刪除這個檔案,現在執行cat /tmp/healthy是不是就會失敗了(預設檢測失敗3次才認為失敗),所以這個時候就會重啟容器了。

4.3、建立該pod

kubectl apply -f liveness-exec.yaml

4.4、檢視Pod的Event

kubectl describe pod liveness-exec

image-20240721104206442

我們可以觀察到容器是正常啟動的,在隔一會兒,再檢視下 Pod 的 Event,在最下面有一條資訊顯示 liveness probe 失敗了,容器將要重啟。然後可以檢視到 Pod 的 RESTARTS 值加 1 了:

kubectl get pods|grep ^live

image-20240721104533530

5、示例(存活探針--HTTP GET)

5.1、編寫資源清單

apiVersion: v1
kind: Pod
metadata:
  name: liveness-http
spec:
  containers:
  - name: liveness
    image: cnych/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
        httpHeaders:
        - name: X-Custom-Header
          value: Awesome
      initialDelaySeconds: 3
      periodSeconds: 3

同樣的,根據 periodSeconds 屬性我們可以知道 kubelet 需要每隔3秒執行一次 liveness Probe,該探針將向容器中的 server 的 8080 埠傳送一個 HTTP GET 請求。如果 server 的 /healthz 路徑的 handler 返回一個成功的返回碼,kubelet 就會認定該容器是活著的並且很健康,如果返回失敗的返回碼,kubelet 將殺掉該容器並重啟它。initialDelaySeconds 指定kubelet 在該執行第一次探測之前需要等待3秒鐘。

通常來說,任何大於200小於400的狀態碼都會認定是成功的返回碼。其他返回碼都會被認為是失敗的返回碼。

四、Pod資源配置

實際上上面幾個步驟就是影響一個 Pod 生命週期的大的部分,但是還有一些細節也會在 Pod 的啟動過程進行設定,比如在容器啟動之前還會為當前的容器設定分配的 CPU、記憶體等資源,我們知道我們可以透過 CGroup 來對容器的資源進行限制,同樣的,在 Pod 中我們也可以直接配置某個容器的使用的 CPU 或者記憶體的上限。那麼 Pod 是如何來使用和控制這些資源的分配的呢?

首先對於 CPU,我們知道計算機裡 CPU 的資源是按“時間片”的方式來進行分配的,系統裡的每一個操作都需要 CPU 的處理,所以,哪個任務要是申請的 CPU 時間片越多,那麼它得到的 CPU 資源就越多。

1、CGroup 裡面對於 CPU 資源的單位換算

1 CPU =  1000 millicpu(1 Core = 1000m)
 
0.5 CPU = 500 millicpu (0.5 Core = 500m)

這裡的 m 就是毫、毫核的意思,Kubernetes 叢集中的每一個節點可以透過作業系統的命令來確認本節點的 CPU 核心數量,然後將這個數量乘以1000,得到的就是節點總 CPU 總毫數。比如一個節點有四核,那麼該節點的 CPU 總毫量為 4000m,如果你要使用0.5 core,則你要求的是 4000*0.5 = 2000m。在 Pod 裡面我們可以透過下面的兩個引數來現在和請求 CPU 資源:

  • spec.containers[].resources.limits.cpu:CPU 上限值,可以短暫超過,容器也不會被停止

  • spec.containers[].resources.requests.cpu:CPU請求值,Kubernetes 排程演算法裡的依據值,可以超過

這裡需要明白的是,如果resources.requests.cpu設定的值大於叢集裡每個節點的最大 CPU 核心數,那麼這個 Pod 將無法排程,因為沒有節點能滿足它。

到這裡應該明白了,requests 是用於叢集排程使用的資源,而 limits 才是真正的用於資源限制的配置,如果你需要保證的你應用優先順序很高,也就是資源吃緊的情況下最後再殺掉你的 Pod,那麼你就把你的 requests 和 limits 的值設定成一致。

2、建立容器的資源清單

2.1、編寫資源清單

pod-resource-demo1.yaml

apiVersion: v1
kind: Pod
metadata:
  name: resource-demo1
spec:
  containers:
  - name: resource-demo1
    image: nginx
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: 50Mi
        cpu: 50m
      limits:
        memory: 100Mi
        cpu: 100m

這裡,CPU 我們給的是 50m,也就是 0.05core,這 0.05 core 也就是佔了 1 CPU 裡的 5% 的資源時間。而限制資源是給的是 100m,但是需要注意的是 CPU 資源是可壓縮資源,也就是容器達到了這個設定的上限後,容器效能會下降,但是不會終止或退出。

2.2、建立該Pod

kubectl apply -f pod-resource-demo1.yaml

image-20240721001651739

2.3、檢視被排程到的節點

kubectl get pods resource-demo1 -o wide

image-20240721104655735

可以看到Pod被排程到k8s-node2這個節點上了

2.4、檢視Pod裡的容器

docker ps |grep resource-demo1

image-20240721110451935

其中第一個容器就是我們的主容器,第二容器是 Infra 容器

2.5、檢視主容器的資訊

docker inspect 78638fd35d88   

image-20240721111414882

實際上我們就可以看到這個容器的一些資源情況,Pod 上的資源配置最終也還是透過底層的容器執行時去控制 CGroup 來實現的,我們可以進入對應目錄檢視 CGroup 的配置,該目錄就是 CGroup 父級目錄,而 CGroup 是透過檔案系統來進行資源限制的,所以我們上面限制容器的資源就可以在該目錄下面反映出來:

截圖裡CgroupParent對應的目錄名

cd /sys/fs/cgroup/cpu/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-poda77b9886_5413_4368_8ee0_5e4c153c8d01.slice

cat cpu.cfs_quota_us 

image-20240721111818294

其中 cpu.cfs_quota_us 就是 CPU 的限制值,如果要檢視具體的容器的資源,我們也可以進入到容器目錄下面去檢視即可。

記憶體是可壓縮性資源,如果容器使用記憶體資源到達了上限,那麼會OOM,造成記憶體溢位,容器就會終止和退出。

相關文章