(五)Kubernetes Pod狀態和生命周期管理


什么是Pod

Podkubernetes中你可以創建和部署的最小也是最簡的單位。Pod代表着集群中運行的進程。

Pod中封裝着應用的容器(有的情況下是好幾個容器),存儲、獨立的網絡IP,管理容器如何運行的策略選項。Pod代表着部署的一個單位:kubernetes中應用的一個實例,可能由一個或者多個容器組合在一起共享資源。

Dockerkubernetes中最常用的容器運行時,但是Pod也支持其他容器運行時。

Kubernetes集群中Pod有如下兩種方式:

  • 一個Pod中運行一個容器。“每個Pod中一個容器”的模式是最常見的用法;在這種使用方式中,你可以把Pod想象成單個容器的封裝,Kubernetes管理的是Pod而不是直接管理容器。

  • 在一個Pod中同時運行多個容器。一個Pod也可以同時封裝幾個需要緊密耦合互相協作的容器,它們之間共享資源。這些在同一個Pod中的容器可以互相協作成為一個service單位——一個容器共享文件,另一個“sidecar”容器來更新這些文件。Pod將這些容器的存儲資源作為一個實體來管理。

Pod中共享的環境包括Linuxnamespacecgroup和其他可能的隔絕環境,這一點跟Docker容器一致。在Pod的環境中,每個容器可能還有更小的子隔離環境。

Pod中的容器共享IP地址和端口號,它們之間可以通過localhost互相發現。它們之間可以通過進程間通信,例如SystemV信號或者POSIX共享內存。不同Pod之間的容器具有不同的IP地址,不能直接通過IPC通信。

Pod中的容器也有訪問共享volume的權限,這些volume會被定義成pod的一部分並掛載到應用容器的文件系統中。

就像每個應用容器,pod被認為是臨時(非持久的)實體。在Pod的生命周期中討論過,pod被創建后,被分配一個唯一的ID(UID),調度到節點上,並一致維持期望的狀態直到被終結(根據重啟策略)或者被刪除。如果node死掉了,分配到了這個node上的pod,在經過一個超時時間后會被重新調度到其他node節點上。一個給定的pod(如UID定義的)不會被“重新調度”到新的節點上,而是被一個同樣的pod取代,如果期望的話甚至可以是相同的名字,但是會有一個新的UID

Pod中如何管理多個容器

Pod中可以同時運行多個進程(作為容器運行)協同工作。同一個Pod中的容器會自動的分配到同一個node上。同一個Pod中的容器共享資源、網絡環境和依賴,它們總是被同時調度。

注意在一個Pod中同時運行多個容器是一種比較高級的用法。只有當你的容器需要緊密配合協作的時候才考慮用這種模式。例如,你有一個容器作為web服務器運行,需要用到共享的volume,有另一個“sidecar”容器來從遠端獲取資源更新這些文件,如下圖所示:

Pod中可以共享兩種資源:網絡和存儲

網絡:每個pod都會被分配一個唯一的IP地址。Pod中的所有容器共享網絡空間,包括IP地址和端口。Pod內部的容器可以使用localhost互相通信。Pod中的容器與外界通信時,必須分配共享網絡資源(例如使用宿主機的端口映射)。

存儲:可以為一個Pod指定多個共享的VolumePod中的所有容器都可以訪問共享的volumeVolume也可以用來持久化Pod中的存儲資源,以防容器重啟后文件丟失。

使用Pod

你很少會直接在kubernetes中創建單個Pod。因為Pod的生命周期是短暫的,用后即焚的實體。當Pod被創建后(不論是由你直接創建還是被其它Controller),都會被Kubernetes調度到集群的Node上。直到Pod的進程終止、被刪掉、因為缺少資源而被驅逐、或者Node故障之前這個Pod都會一直保持在那個Node上。

注意:重啟Pod中的容器跟重啟Pod不是一回事。Pod只提供容器的運行環境並保持容器的運行狀態,重啟容器不會造成Pod重啟。

Pod不會自愈。如果Pod運行的Node故障,或者是調度器本身故障,這個Pod就會被刪除。同樣的,如果Pod所在Node缺少資源或者Pod處於維護狀態,Pod也會被驅逐。Kubernetes使用更高級的稱為Controller的抽象層,來管理Pod實例。雖然可以直接使用Pod,但是在Kubernetes中通常是使用Controller來管理Pod的。

Controller可以創建和管理多個Pod,提供副本管理、滾動升級和集群級別的自愈能力。例如,如果一個Node故障,Controller就能自動將該節點上的Pod調度到其他健康的Node上。

Pod對象的生命周期

官方文檔

中文文檔

Pod對象自從其創建開始至其終止退出的時間范圍稱為其生命周期。在這段時間中,Pod會處於多種不同的狀態,並執行一些操作;其中,創建主容器(main container)為必需的操作,其他可選的操作還包括運行初始化容器(init container)、容器啟動后鈎子(post start hook)、容器的存活性探測(liveness probe)、就緒性探測(readiness probe)以及容器終止前鈎子(pre stop hook)等,這些操作是否執行則取決於Pod的定義。如下圖所示:

Pod phase

Podstatus字段是一個PodStatus的對象,PodStatus中有一個phase字段。

無論是手動創建還是通過Deployment等控制器創建,Pod對象總是應該處於其生命進程中以下幾個相位(phase)之一。

  • 掛起(Pending):API Server創建了pod資源對象已存入etcd中,但它尚未被調度完成,或者仍處於從倉庫下載鏡像的過程中。

  • 運行中(Running):Pod已經被調度至某節點,並且所有容器都已經被kubelet創建完成。

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

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

  • 未知(Unknown):Api Server無法正常獲取到Pod對象的狀態信息,通常是由於無法與所在工作節點的kubelet通信所致。

Pod的創建過程

Podkubernetes的基礎單元,理解它的創建過程對於了解系統運作大有裨益。如下圖描述了一個Pod資源對象的典型創建過程。

  1. 用戶通過kubectl或其他API客戶端提交了Pod SpecAPI Server

  2. API Server嘗試着將Pod對象的相關信息存入etcd中,待寫入操作執行完成,API Server即會返回確認信息至客戶端。

  3. API Server開始反映etcd中的狀態變化。

  4. 所有的kubernetes組件均使用“watch”機制來跟蹤檢查API Server上的相關的變動。

  5. kube-scheduler(調度器)通過其“watcher”覺察到API Server創建了新的Pod對象但尚未綁定至任何工作節點。

  6. kube-schedulerPod對象挑選一個工作節點並將結果信息更新至API Server

  7. 調度結果信息由API Server更新至etcd存儲系統,而且API Server也開始反映此Pod對象的調度結果。

  8. Pod被調度到的目標工作節點上的kubelet嘗試在當前節點上調用Docker啟動容器,並將容器的結果狀態返回送至API Server

  9. API ServerPod狀態信息存入etcd系統中。

  10. etcd確認寫入操作成功完成后,API Server將確認信息發送至相關的kubelet,事件將通過它被接受。

Pod生命周期中的重要行為

1)初始化容器

初始化容器(init container)即應用程序的主容器啟動之前要運行的容器,常用於為主容器執行一些預置操作,它們具有兩種典型特征。

1)初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么kubernetes需要重啟它直到成功完成。(注意:如果podspec.restartPolicy字段值為“Never”,那么運行失敗的初始化容器不會被重啟。)

2)每個初始化容器都必須按定義的順序串行運行。

2)容器探測

容器探測(container probe)是Pod對象生命周期中的一項重要的日常任務,它是kubelet對容器周期性執行的健康狀態診斷,診斷操作由容器的處理器(handler)進行定義。Kubernetes支持三種處理器用於Pod探測:

  • ExecAction:在容器內執行指定命令,並根據其返回的狀態碼進行診斷的操作稱為Exec探測,狀態碼為0表示成功,否則即為不健康狀態。

  • TCPSocketAction:通過與容器的某TCP端口嘗試建立連接進行診斷,端口能夠成功打開即為正常,否則為不健康狀態。

  • HTTPGetAction:通過向容器IP地址的某指定端口的指定path發起HTTP GET請求進行診斷,響應碼為2xx3xx時即為成功,否則為失敗。

任何一種探測方式都可能存在三種結果:“Success”(成功)“Failure”(失敗)“Unknown”(未知),只有success表示成功通過檢測。

容器探測分為兩種類型:

  • 存活性探測(livenessProbe):用於判定容器是否處於“運行”(Running)狀態;一旦此類檢測未通過,kubelet將殺死容器並根據重啟策略(restartPolicy)決定是否將其重啟;未定義存活檢測的容器的默認狀態為“Success”。

  • 就緒性探測(readinessProbe):用於判斷容器是否准備就緒並可對外提供服務;未通過檢測的容器意味着其尚未准備就緒,端點控制器(如Service對象)會將其IP從所有匹配到此Pod對象的Service對象的端點列表中移除;檢測通過之后,會再將其IP添加至端點列表中。

什么時候使用存活(liveness)和就緒(readiness)探針?

如果容器中的進程能夠在遇到問題或不健康的情況下自行崩潰,則不一定需要存活探針,kubelet將根據PodrestartPolicy自動執行正確的操作。

如果希望容器在探測失敗時被殺死並重新啟動,那么請指定一個存活探針,並指定restartPolicyAlwaysOnFailure

如果要僅在探測成功時才開始向Pod發送流量,請指定就緒探針。在這種情況下,就緒探針可能與存活探針相同,但是spec中的就緒探針的存在意味着Pod將在沒有接收到任何流量的情況下啟動,並且只有在探針探測成功才開始接收流量。

如果希望容器能夠自行維護,可以指定一個就緒探針,該探針檢查與存活探針不同的端點。

注意:如果只想在Pod被刪除時能夠排除請求,則不一定需要使用就緒探針;在刪除Pod時,Pod會自動將自身置於未完成狀態,無論就緒探針是否存在。當等待Pod中的容器停止時,Pod仍處於未完成狀態。

容器的重啟策略

PodSpec中有一個restartPolicy字段,可能的值為AlwaysOnFailureNever。默認為AlwaysrestartPolicy適用於Pod中的所有容器。而且它僅用於控制在同一節點上重新啟動Pod對象的相關容器。首次需要重啟的容器,將在其需要時立即進行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時間后進行,且反復的重啟操作的延遲時長依次為10秒、20秒、40秒... 300秒是最大延遲時長。事實上,一旦綁定到一個節點,Pod對象將永遠不會被重新綁定到另一個節點,它要么被重啟,要么終止,直到節點發生故障或被刪除。

  • Always:但凡Pod對象終止就將其重啟,默認值

  • OnFailure:僅在Pod對象出現錯誤時方才將其重啟

  • Never:從不重啟

Pod存活性探測示例

設置exec探針示例

[root@k8s-master ~]# vim manfests/liveness-exec.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
  labels:
    test: liveness-exec
spec:
  containers:
  - name: liveness-exec-container
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 3600"]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]
      initialDelaySeconds: 1
      periodSeconds: 3

[root@k8s-master ~]# kubectl create -f manfests/liveness-exec.yaml    #創建pod
pod/liveness-exec-pod created
[root@k8s-master ~]# kubectl get pods   #查看pod
NAME                READY   STATUS    RESTARTS   AGE
liveness-exec-pod   1/1     Running   0          6s

#等待一段時間后再次查看其狀態
[root@k8s-master ~]# kubectl get pods
NAME                READY   STATUS    RESTARTS   AGE
liveness-exec-pod   1/1     Running   2          2m46s

上面的資源清單中定義了一個pod對象,基於busybox鏡像啟動一個運行“touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 3600"命令的容器,此命令在容器啟動時創建了/tmp/healthy文件,並於60秒之后將其刪除。存活性探針運行”test -e /tmp/healthy"命令檢查/tmp/healthy文件的存在性,若文件存在則返回狀態碼0,表示成功通過測試。在60秒內使用“kubectl describe pods/liveness-exec-pod”查看其詳細信息,其存活性探測不會出現錯誤。而超過60秒之后,再執行該命令查看詳細信息,可以發現存活性探測出現了故障,並且還可通過“kubectl get pods"查看該pod的重啟的相關信息。

設置HTTP探針示例

基於HTTP的探測(HTTPGetAction)向目標容器發起一個HTTP請求,根據其響應碼進行結果判定,響應碼如2xx3xx時表示測試通過。通過該命令”# kubectl explain pod.spec.containers.livenessProbe.httpGet“查看httpGet定義的字段

host    <string>:請求的主機地址,默認為Pod IP,也可以在httpHeaders中使用“Host:”來定義。
httpHeaders    <[]Object>:自定義的請求報文首部。
port    <string>:請求的端口,必選字段。
path    <string>:請求的HTTP資源路徑,即URL path。
scheme    <string>:建立連接使用的協議,僅可為HTTP或HTTPS,默認為HTTP。
[root@k8s-master ~]# vim manfests/liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-http
  namespace: default
  labels:
    test: liveness
spec:
  containers:
    - name: liveness-http-demo
      image: nginx:1.12
      imagePullPolicy: IfNotPresent
      ports:
      - name: http
        containerPort: 80
      lifecycle:
        postStart:
          exec:
            command: ["/bin/sh", "-c", "echo Healthz > /usr/share/nginx/html/healthz"]
      livenessProbe:
        httpGet:
          path: /healthz
          port: http
          scheme: HTTP
[root@k8s-master ~]# kubectl create -f manfests/liveness-httpget.yaml    #創建pod
pod/liveness-http created
[root@k8s-master ~]# kubectl get pods    #查看pod
NAME            READY   STATUS    RESTARTS   AGE
liveness-http   1/1     Running   0          7s

[root@k8s-master ~]# kubectl describe pods/liveness-http    #查看liveness-http詳細信息
......
Containers:
  liveness-http-demo:
......
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 09 Sep 2019 15:43:29 +0800
    Ready:          True
    Restart Count:  0
......

上面清單中定義的httpGet測試中,通過lifecycle中的postStart hook創建了一個專用於httpGet測試的頁面文件healthz,請求的資源路徑為"/healthz",地址默認為Pod IP,端口使用了容器中頂一個端口名稱http,這也是明確了為容器指明要暴露的端口的用途之一。並查看健康狀態檢測相關的信息,健康狀態檢測正常時,容器也將正常運行。下面通過“kubectl exec”命令進入容器刪除由postStart hook創建的測試頁面healthz。再次查看容器狀態

[root@k8s-master ~]# kubectl exec pods/liveness-http -it -- /bin/sh    #進入到上面創建的pod中
# rm -rf /usr/share/nginx/html/healthz   #刪除healthz測試頁面
# 

[root@k8s-master ~]# kubectl get pods 
NAME            READY   STATUS    RESTARTS   AGE
liveness-http   1/1     Running   1          10m

[root@k8s-master ~]# kubectl describe pods/liveness-http
......
Containers:
  liveness-http-demo:
......
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 09 Sep 2019 15:53:04 +0800
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Mon, 09 Sep 2019 15:43:29 +0800
      Finished:     Mon, 09 Sep 2019 15:53:03 +0800
    Ready:          True
    Restart Count:  1
......

通過上面測試可以看出,當發起http請求失敗后,容器將被殺掉后進行了重新構建。

設置TCP探針

基於TCP的存活性探測(TCPSocketAction)用於向容器的特定端口發起TCP請求並建立連接進行結果判定,連接建立成功即為通過檢測。相比較來說,它比基於HTTP的探測要更高效、更節約資源,但精確度略低,畢竟連接建立成功未必意味着頁面資源可用。通過該命令”# kubectl explain pod.spec.containers.livenessProbe.tcpSocket“查看tcpSocket定義的字段

host    <string>:請求連接的目標IP地址,默認為Pod IP
port    <string>:請求連接的目標端口,必選字段
[root@k8s-master ~]# vim manfests/liveness-tcp.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-tcp-pod
  namespace: default
  labels:
    test: liveness-tcp
spec:
  containers:
  - name: liveness-tcp-demo
    image: nginx:1.12
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: http

上面清單中定義的tcpSocket測試中,通過向容器的80端口發起請求,如果端口正常,則表明正常運行。

livenessProbe行為屬性

[root@k8s-master ~]# kubectl explain pods.spec.containers.livenessProbe
KIND:     Pod
VERSION:  v1

RESOURCE: livenessProbe <Object>

exec   command 的方式探測,例如 ps 一個進程是否存在

failureThreshold    探測幾次失敗 才算失敗, 默認是連續三次

initialDelaySeconds  初始化延遲探測,即容器啟動多久之后再開始探測,默認為0秒

periodSeconds  每隔多久探測一次,默認是10秒

successThreshold  處於失敗狀態時,探測操作至少連續多少次的成功才算通過檢測,默認為1秒

timeoutSeconds  存活性探測的超時時長,默認為1秒

httpGet   http請求探測

tcpSocket    端口探測

Pod就緒性探測示例

Pod對象啟動后,容器應用通常需要一段時間才能完成其初始化過程,例如加載配置或數據,甚至有些程序需要運行某類的預熱過程,若在這個階段完成之前即接入客戶端的請求,勢必會等待太久。因此,這時候就用到了就緒性探測(readinessProbe)。

與存活性探測機制類似,就緒性探測是用來判斷容器就緒與否的周期性(默認周期為10秒鍾)操作,它用於探測容器是否已經初始化完成並可服務於客戶端請求,探測操作返回”success“狀態時,即為傳遞容器已經”就緒“的信號。

就緒性探測也支持ExecHTTPGetTCPSocket三種探測方式,且各自的定義機制也都相同。但與存活性探測觸發的操作不同的是,探測失敗時,就緒探測不會殺死或重啟容器以保證其健康性,而是通知其尚未就緒,並觸發依賴於其就緒狀態的操作(例如,從Service對象中移除此Pod對象)以確保不會有客戶端請求接入此Pod對象。

這里只是示例http探針示例,不論是httpGet還是exec還是tcpSocket和存活性探針類似。

設置HTTP探針示例

#終端1:
[root@k8s-master ~]# vim manfests/readiness-httpget.yaml  #編輯readiness-httpget測試pod的yaml文件
apiVersion: v1
kind: Pod
metadata:
  name: readiness-http
  namespace: default
  labels:
    test: readiness-http
spec:
  containers:
  - name: readiness-http-demo
    image: nginx:1.12
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    readinessProbe:
      httpGet:
        path: /index.html
        port: http
        scheme: HTTP
[root@k8s-master ~]# kubectl create -f manfests/readiness-httpget.yaml    #創建pod
pod/readiness-http created
[root@k8s-master ~]# kubectl get pods      查看pod狀態
NAME               READY   STATUS    RESTARTS   AGE
liveness-tcp-pod   1/1     Running   1          7d18h
readiness-http     1/1     Running   0          7s


#新打開一個終端2進入到容器里面
[root@k8s-master ~]# kubectl exec pods/readiness-http -it -- /bin/sh    #進入上面創建的pod
# rm -f /usr/share/nginx/html/index.html     #刪除nginx的主頁面文件
# ls /usr/share/nginx/html
50x.html
# 


#回到終端1上面查看pod狀態
[root@k8s-master ~]# kubectl get pods     #查看pod狀態
NAME               READY   STATUS    RESTARTS   AGE
liveness-tcp-pod   1/1     Running   1          7d18h
readiness-http     0/1     Running   0          2m44s

通過上面測試可以看出,當我們刪除了nginx主頁文件后,readinessProbe發起的測試就會失敗,此時我們再查看pod的狀態會發現並不會將pod刪除重新啟動,只是在READY字段可以看出,當前的Pod處於未就緒狀態。

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM