Kubernetes學習之路(十一)之Pod狀態和生命週期管理

1、什麼是Pod?

Pod是kubernetes中你能夠建立和部署的最小也是最簡的單位。一個Pod表明着集羣中運行的一個進程。html

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

在Kubrenetes集羣中Pod有以下兩種使用方式:nginx

  • 一個Pod中運行一個容器。「每一個Pod中一個容器」的模式是最多見的用法;在這種使用方式中,你能夠把Pod想象成是單個容器的封裝,kuberentes管理的是Pod而不是直接管理容器。
  • 在一個Pod中同時運行多個容器。一個Pod中也能夠同時封裝幾個須要緊密耦合互相協做的容器,它們之間共享資源。這些在同一個Pod中的容器能夠互相協做成爲一個service單位——一個容器共享文件,另外一個「sidecar」容器來更新這些文件。Pod將這些容器的存儲資源做爲一個實體來管理。

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

Pod中的容器共享IP地址和端口號,它們之間能夠經過localhost互相發現。它們之間能夠經過進程間通訊,須要明白的是同一個Pod下的容器是經過lo網卡進行通訊。例如SystemV信號或者POSIX共享內存。不一樣Pod之間的容器具備不一樣的IP地址,不能直接經過IPC通訊。docker

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

就像每一個應用容器,pod被認爲是臨時實體。在Pod的生命週期中,pod被建立後,被分配一個惟一的ID(UID),調度到節點上,並一致維持指望的狀態直到被終結(根據重啓策略)或者被刪除。若是node死掉了,分配到了這個node上的pod,在通過一個超時時間後會被從新調度到其餘node節點上。一個給定的pod(如UID定義的)不會被「從新調度」到新的節點上,而是被一個一樣的pod取代,若是指望的話甚至能夠是相同的名字,可是會有一個新的UID(查看replication controller獲取詳情)。api

2、Pod中如何管理多個容器?

Pod中能夠同時運行多個進程(做爲容器運行)協同工做。同一個Pod中的容器會自動的分配到同一個 node 上。同一個Pod中的容器共享資源、網絡環境和依賴,它們老是被同時調度。數組

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

Pod中能夠共享兩種資源:網絡和存儲。服務器

  •  網絡:

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

  • 存儲:

  能夠Pod指定多個共享的Volume。Pod中的全部容器均可以訪問共享的volume。Volume也能夠用來持久化Pod中的存儲資源,以防容器重啓後文件丟失。

 3、使用Pod

一般把Pod分爲兩類:

  • 自主式Pod :這種Pod自己是不能自我修復的,當Pod被建立後(不管是由你直接建立仍是被其餘Controller),都會被Kuberentes調度到集羣的Node上。直到Pod的進程終止、被刪掉、由於缺乏資源而被驅逐、或者Node故障以前這個Pod都會一直保持在那個Node上。Pod不會自愈。若是Pod運行的Node故障,或者是調度器自己故障,這個Pod就會被刪除。一樣的,若是Pod所在Node缺乏資源或者Pod處於維護狀態,Pod也會被驅逐。
  • 控制器管理的Pod:Kubernetes使用更高級的稱爲Controller的抽象層,來管理Pod實例。Controller能夠建立和管理多個Pod,提供副本管理、滾動升級和集羣級別的自愈能力。例如,若是一個Node故障,Controller就能自動將該節點上的Pod調度到其餘健康的Node上。雖然能夠直接使用Pod,可是在Kubernetes中一般是使用Controller來管理Pod的。以下圖:

每一個Pod都有一個特殊的被稱爲「根容器」的Pause 容器。 Pause容器對應的鏡像屬於Kubernetes平臺的一部分,除了Pause容器,每一個Pod還包含一個或者多個緊密相關的用戶業務容器。

Kubernetes設計這樣的Pod概念和特殊組成結構有什麼用意?????

緣由一:在一組容器做爲一個單元的狀況下,難以對總體的容器簡單地進行判斷及有效地進行行動。好比,一個容器死亡了,此時是算總體掛了麼?那麼引入與業務無關的Pause容器做爲Pod的根容器,以它的狀態表明着整個容器組的狀態,這樣就能夠解決該問題。

緣由二:Pod裏的多個業務容器共享Pause容器的IP,共享Pause容器掛載的Volume,這樣簡化了業務容器之間的通訊問題,也解決了容器之間的文件共享問題。

4、Pod的持久性和終止

(1)Pod的持久性

Pod在設計支持就不是做爲持久化實體的。在調度失敗、節點故障、缺乏資源或者節點維護的狀態下都會死掉會被驅逐

一般,用戶不須要手動直接建立Pod,而是應該使用controller(例如Deployments),即便是在建立單個Pod的狀況下。Controller能夠提供集羣級別的自愈功能、複製和升級管理。

(2)Pod的終止

由於Pod做爲在集羣的節點上運行的進程,因此在再也不須要的時候可以優雅的終止掉是十分必要的(比起使用發送KILL信號這種暴力的方式)。用戶須要可以放鬆刪除請求,而且知道它們什麼時候會被終止,是否被正確的刪除。用戶想終止程序時發送刪除pod的請求,在pod能夠被強制刪除前會有一個寬限期,會發送一個TERM請求到每一個容器的主進程。一旦超時,將向主進程發送KILL信號並從API server中刪除。若是kubelet或者container manager在等待進程終止的過程當中重啓,在重啓後仍然會重試完整的寬限期。

示例流程以下:

  1. 用戶發送刪除pod的命令,默認寬限期是30秒;
  2. 在Pod超過該寬限期後API server就會更新Pod的狀態爲「dead」;
  3. 在客戶端命令行上顯示的Pod狀態爲「terminating」;
  4. 跟第三步同時,當kubelet發現pod被標記爲「terminating」狀態時,開始中止pod進程:
    1. 若是在pod中定義了preStop hook,在中止pod前會被調用。若是在寬限期事後,preStop hook依然在運行,第二步會再增長2秒的寬限期;
    2. 向Pod中的進程發送TERM信號;
  5. 跟第三步同時,該Pod將從該service的端點列表中刪除,再也不是replication controller的一部分。關閉的慢的pod將繼續處理load balancer轉發的流量;
  6. 過了寬限期後,將向Pod中依然運行的進程發送SIGKILL信號而殺掉進程。
  7. Kublete會在API server中完成Pod的的刪除,經過將優雅週期設置爲0(當即刪除)。Pod在API中消失,而且在客戶端也不可見。

刪除寬限期默認是30秒。 kubectl delete命令支持 —grace-period=<seconds> 選項,容許用戶設置本身的寬限期。若是設置爲0將強制刪除pod。在kubectl>=1.5版本的命令中,你必須同時使用 --force 和 --grace-period=0 來強制刪除pod。

Pod的強制刪除是經過在集羣和etcd中將其定義爲刪除狀態。當執行強制刪除命令時,API server不會等待該pod所運行在節點上的kubelet確認,就會當即將該pod從API server中移除,這時就能夠建立跟原pod同名的pod了。這時,在節點上的pod會被當即設置爲terminating狀態,不過在被強制刪除以前依然有一小段優雅刪除週期。 

 5、Pause容器

 Pause容器,又叫Infra容器。咱們檢查node節點的時候會發現每一個node上都運行了不少的pause容器,例如以下。

[root@k8s-node01 ~]# docker ps |grep pause
0cbf85d4af9e    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_myapp-848b5b879b-ksgnv_default_0af41a40-a771-11e8-84d2-000c2972dc1f_0
d6e4d77960a7    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_myapp-848b5b879b-5f69p_default_09bc0ba1-a771-11e8-84d2-000c2972dc1f_0
5f7777c55d2a    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_kube-flannel-ds-pgpr7_kube-system_23dc27e3-a5af-11e8-84d2-000c2972dc1f_1
8e56ef2564c2    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_client2_default_17dad486-a769-11e8-84d2-000c2972dc1f_1
7815c0d69e99    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_nginx-deploy-5b595999-872c7_default_7e9df9f3-a6b6-11e8-84d2-000c2972dc1f_2
b4e806fa7083    k8s.gcr.io/pause:3.1   "/pause"     7 days ago  Up 7 days   k8s_POD_kube-proxy-vxckf_kube-system_23dc0141-a5af-11e8-84d2-000c2972dc1f_2

kubernetes中的pause容器主要爲每一個業務容器提供如下功能:

  • 在pod中擔任Linux命名空間共享的基礎;
  • 啓用pid命名空間,開啓init進程。

如圖:

 

[root@k8s-node01 ~]# docker run -d --name pause -p 8880:80 k8s.gcr.io/pause:3.1
d3057ceb54bc6565d28ded2c33ad2042010be73d76117775c130984c3718d609
[root@k8s-node01 ~]# cat <<EOF >> nginx.conf
> error_log stderr;
> events { worker_connections  1024; }
> http {
>     access_log /dev/stdout combined;
>     server {
>         listen 80 default_server;
>         server_name example.com www.example.com;
>         location / {
>             proxy_pass http://127.0.0.1:2368;
>         }
>     }
> }
> EOF
[root@k8s-node01 ~]# docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
d04f848b7386109085ee350ebb81103e4efc7df8e48da18404efb9712f926082
[root@k8s-node01 ~]#  docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
332c86a722f71680b76b3072e85228a8d8e9608456c653edd214f06c2a77f112

如今訪問http://192.168.56.12:8880/就能夠看到ghost博客的界面了。

解析

pause容器將內部的80端口映射到宿主機的8880端口,pause容器在宿主機上設置好了網絡namespace後,nginx容器加入到該網絡namespace中,咱們看到nginx容器啓動的時候指定了--net=container:pause,ghost容器一樣加入到了該網絡namespace中,這樣三個容器就共享了網絡,互相之間就可使用localhost直接通訊,--ipc=contianer:pause --pid=container:pause就是三個容器處於同一個namespace中,init進程爲pause,這時咱們進入到ghost容器中查看進程狀況。

[root@k8s-node01 ~]# docker exec -it ghost /bin/bash
root@d3057ceb54bc:/var/lib/ghost# ps axu 
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0   1012     4 ?        Ss   03:48   0:00 /pause
root          6  0.0  0.0  32472   780 ?        Ss   03:53   0:00 nginx: master process nginx -g daemon off;
systemd+     11  0.0  0.1  32932  1700 ?        S    03:53   0:00 nginx: worker process
node         12  0.4  7.5 1259816 74868 ?       Ssl  04:00   0:07 node current/index.js
root         77  0.6  0.1  20240  1896 pts/0    Ss   04:29   0:00 /bin/bash
root         82  0.0  0.1  17496  1156 pts/0    R+   04:29   0:00 ps axu

在ghost容器中同時能夠看到pause和nginx容器的進程,而且pause容器的PID是1。而在kubernetes中容器的PID=1的進程即爲容器自己的業務進程。

6、init容器 

Pod 可以具備多個容器,應用運行在容器裏面,可是它也可能有一個或多個先於應用容器啓動的 Init 容器。

Init 容器與普通的容器很是像,除了以下兩點:

  • Init 容器老是運行到成功完成爲止。
  • 每一個 Init 容器都必須在下一個 Init 容器啓動以前成功完成。

若是 Pod 的 Init 容器失敗,Kubernetes 會不斷地重啓該 Pod,直到 Init 容器成功爲止。然而,若是 Pod 對應的 restartPolicy 爲 Never,它不會從新啓動。

7、Pod的生命週期

(1)Pod phase(Pod的相位)

Pod 的 status 在信息保存在 PodStatus 中定義,其中有一個 phase 字段。

Pod 的相位(phase)是 Pod 在其生命週期中的簡單宏觀概述。該階段並非對容器或 Pod 的綜合彙總,也不是爲了作爲綜合狀態機。

Pod 相位的數量和含義是嚴格指定的。除了本文檔中列舉的狀態外,不該該再假定 Pod 有其餘的 phase值。

下面是 phase 可能的值:

  • 掛起(Pending):API Server建立了Pod資源對象並已經存入了etcd中,可是它並未被調度完成,或者仍然處於從倉庫下載鏡像的過程當中。
  • 運行中(Running):Pod已經被調度到某節點之上,而且全部容器都已經被kubelet建立完成。
  • 成功(Succeeded):Pod 中的全部容器都被成功終止,而且不會再重啓。
  • 失敗(Failed):Pod 中的全部容器都已終止了,而且至少有一個容器是由於失敗終止。也就是說,容器以非0狀態退出或者被系統終止。
  • 未知(Unknown):由於某些緣由沒法取得 Pod 的狀態,一般是由於與 Pod 所在主機通訊失敗。

下圖是Pod的生命週期示意圖,從圖中能夠看到Pod狀態的變化。

(2)Pod的建立過程

Pod是Kubernetes的基礎單元,瞭解其建立的過程,更有助於理解系統的運做。

①用戶經過kubectl或其餘API客戶端提交Pod Spec給API Server。

②API Server嘗試將Pod對象的相關信息存儲到etcd中,等待寫入操做完成,API Server返回確認信息到客戶端。

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

④全部的Kubernetes組件經過"watch"機制跟蹤檢查API Server上的相關信息變更。

⑤kube-scheduler(調度器)經過其"watcher"檢測到API Server建立了新的Pod對象可是沒有綁定到任何工做節點。

⑥kube-scheduler爲Pod對象挑選一個工做節點並將結果信息更新到API Server。

⑦調度結果新消息由API Server更新到etcd,而且API Server也開始反饋該Pod對象的調度結果。

⑧Pod被調度到目標工做節點上的kubelet嘗試在當前節點上調用docker engine進行啓動容器,並將容器的狀態結果返回到API Server。

⑨API Server將Pod信息存儲到etcd系統中。

⑩在etcd確認寫入操做完成,API Server將確認信息發送到相關的kubelet。

(3)Pod的狀態

Pod 有一個 PodStatus 對象,其中包含一個 PodCondition 數組。 PodCondition 數組的每一個元素都有一個 type 字段和一個 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized 和 Unschedulable。status 字段是一個字符串,可能的值有 True、False 和 Unknown。

(4)Pod存活性探測

在pod生命週期中能夠作的一些事情。主容器啓動前能夠完成初始化容器,初始化容器能夠有多個,他們是串行執行的,執行完成後就推出了,在主程序剛剛啓動的時候能夠指定一個post start 主程序啓動開始後執行一些操做,在主程序結束前能夠指定一個 pre stop 表示主程序結束前執行的一些操做。在程序啓動後能夠作兩類檢測 liveness probe(存活性探測) 和 readness probe(就緒性探測)。以下圖:

 

探針是由 kubelet 對容器執行的按期診斷。要執行診斷,kubelet 調用由容器實現的Handler。其存活性探測的方法有如下三種:

  • ExecAction:在容器內執行指定命令。若是命令退出時返回碼爲 0 則認爲診斷成功。
  • TCPSocketAction:對指定端口上的容器的 IP 地址進行 TCP 檢查。若是端口打開,則診斷被認爲是成功的。
  • HTTPGetAction:對指定的端口和路徑上的容器的 IP 地址執行 HTTP Get 請求。若是響應的狀態碼大於等於200 且小於 400,則診斷被認爲是成功的。

設置exec探針舉例:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-exec
  name: liveness-exec
spec:
  containers:
  - name: liveness-exec-demo
    image: busybox
    args: ["/bin/sh","-c","touch /tmp/healthy;sleep 60;rm -rf /tmp/healthy;"sleep 600]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/healthy"]

上面的資源清單中定義了一個Pod 對象, 基於 busybox 鏡像 啓動 一個 運行「 touch/ tmp/ healthy; sleep 60; rm- rf/ tmp/ healthy; sleep 600」 命令 的 容器, 此 命令 在 容器 啓動 時 建立/ tmp/ healthy 文件, 並於 60 秒 以後 將其 刪除。 存活 性 探針 運行「 test -e/ tmp/ healthy」 命令 檢查/ tmp/healthy 文件 的 存在 性, 若 文件 存在 則 返回 狀態 碼 0, 表示 成功 經過 測試。

設置HTTP探針舉例:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-http
  name: liveness-http
spec:
  containers:
  - name: liveness-http-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh","-c","echo healthy > /usr/share/nginx/html/healthy"]
    livenessProbe:
      httpGet:
        path: /healthy
        port: http
        scheme: HTTP

上面 清單 文件 中 定義 的 httpGet 測試 中, 請求 的 資源 路徑 爲「/ healthy」, 地址 默認 爲 Pod IP, 端口 使用 了 容器 中 定義 的 端口 名稱 HTTP, 這也 是 明確 爲 容器 指明 要 暴露 的 端口 的 用途 之一。

設置TCP探針舉例:

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness-tcp
  name: liveness-tcp
spec:
  containers:
  - name: liveness-tcp-demo
    image: nginx:1.12-alpine
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: http

上面的資源清單文件,向Pod IP的80/tcp端口發起鏈接請求,並根據鏈接創建的狀態判斷Pod存活狀態。

每次探測都將得到如下三種結果之一:

  • 成功:容器經過了診斷。
  • 失敗:容器未經過診斷。
  • 未知:診斷失敗,所以不會採起任何行動。

Kubelet 能夠選擇是否執行在容器上運行的兩種探針執行和作出反應:

  • livenessProbe指示容器是否正在運行。若是存活探測失敗,則 kubelet 會殺死容器,而且容器將受到其 重啓策略 的影響。若是容器不提供存活探針,則默認狀態爲 Success
  • readinessProbe指示容器是否準備好服務請求。若是就緒探測失敗,端點控制器將從與 Pod 匹配的全部 Service 的端點中刪除該 Pod 的 IP 地址。初始延遲以前的就緒狀態默認爲 Failure。若是容器不提供就緒探針,則默認狀態爲 Success

(5)livenessProbe和readinessProbe使用場景

若是容器中的進程可以在遇到問題或不健康的狀況下自行崩潰,則不必定須要存活探針; kubelet 將根據 Pod 的restartPolicy 自動執行正確的操做。

若是但願容器在探測失敗時被殺死並從新啓動,那麼請指定一個存活探針,並指定restartPolicy 爲 Always 或 OnFailure。

若是要僅在探測成功時纔開始向 Pod 發送流量,請指定就緒探針。在這種狀況下,就緒探針可能與存活探針相同,可是 spec 中的就緒探針的存在乎味着 Pod 將在沒有接收到任何流量的狀況下啓動,而且只有在探針探測成功後纔開始接收流量。

若是您但願容器可以自行維護,您能夠指定一個就緒探針,該探針檢查與存活探針不一樣的端點。

請注意,若是您只想在 Pod 被刪除時可以排除請求,則不必定須要使用就緒探針;在刪除 Pod 時,Pod 會自動將自身置於未完成狀態,不管就緒探針是否存在。當等待 Pod 中的容器中止時,Pod 仍處於未完成狀態。

(6)Pod的重啓策略

PodSpec 中有一個 restartPolicy 字段,可能的值爲 Always、OnFailure 和 Never。默認爲 Always。 restartPolicy 適用於 Pod 中的全部容器。restartPolicy 僅指經過同一節點上的 kubelet 從新啓動容器。失敗的容器由 kubelet 以五分鐘爲上限的指數退避延遲(10秒,20秒,40秒...)從新啓動,並在成功執行十分鐘後重置。pod一旦綁定到一個節點,Pod 將永遠不會從新綁定到另外一個節點。

(7)Pod的生命

通常來講,Pod 不會消失,直到人爲銷燬他們。這多是一我的或控制器。這個規則的惟一例外是成功或失敗的 phase 超過一段時間(由 master 肯定)的Pod將過時並被自動銷燬。

有三種可用的控制器:

  • 使用 Job 運行預期會終止的 Pod,例如批量計算。Job 僅適用於重啓策略爲 OnFailure 或 Never 的 Pod。
  • 對預期不會終止的 Pod 使用 ReplicationControllerReplicaSet 和 Deployment ,例如 Web 服務器。 ReplicationController 僅適用於具備 restartPolicy 爲 Always 的 Pod。
  • 提供特定於機器的系統服務,使用 DaemonSet 爲每臺機器運行一個 Pod 。

全部這三種類型的控制器都包含一個 PodTemplate。建議建立適當的控制器,讓它們來建立 Pod,而不是直接本身建立 Pod。這是由於單獨的 Pod 在機器故障的狀況下沒有辦法自動復原,而控制器卻能夠。

若是節點死亡或與集羣的其他部分斷開鏈接,則 Kubernetes 將應用一個策略將丟失節點上的全部 Pod 的 phase 設置爲 Failed。

 (8)livenessProbe解析

[root@k8s-master ~]# kubectl explain pod.spec.containers.livenessProbe

KIND:     Pod
VERSION:  v1

RESOURCE: livenessProbe <Object>

exec  command 的方式探測 例如 ps 一個進程
failureThreshold 探測幾回失敗 纔算失敗 默認是連續三次
periodSeconds 每次的多長時間探測一次 默認10s
timeoutSeconds 探測超市的秒數 默認1s
initialDelaySeconds 初始化延遲探測,第一次探測的時候,由於主程序未必啓動完成
tcpSocket 檢測端口的探測
httpGet http請求探測

舉個例子:定義一個liveness的pod資源類型,基礎鏡像爲busybox,在busybox這個容器啓動後會執行建立/tmp/test的文件啊,並刪除,而後等待3600秒。隨後定義了存活性探測,方式是以exec的方式執行命令判斷/tmp/test是否存在,存在即表示存活,不存在則表示容器已經掛了。

[root@k8s-master ~]# vim liveness.yaml

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

 (9)資源需求和資源限制

 在Docker的範疇內,咱們知道能夠對運行的容器進行請求或消耗的資源進行限制。而在Kubernetes中,也有一樣的機制,容器或Pod能夠進行申請和消耗的資源就是CPU和內存。CPU屬於可壓縮型資源,即資源的額度能夠按照需求進行收縮。而內存屬於不可壓縮型資源,對內存的收縮可能會致使沒法預知的問題。

資源的隔離目前是屬於容器級別,CPU和內存資源的配置須要Pod中的容器spec字段下進行定義。其具體字段,可使用"requests"進行定義請求的確保資源可用量。也就是說容器的運行可能用不到這樣的資源量,可是必須確保有這麼多的資源供給。而"limits"是用於限制資源可用的最大值,屬於硬限制。

在Kubernetes中,1個單位的CPU至關於虛擬機的1顆虛擬CPU(vCPU)或者是物理機上一個超線程的CPU,它支持分數計量方式,一個核心(1core)至關於1000個微核心(millicores),所以500m至關因而0.5個核心,即二分之一個核心。內存的計量方式也是同樣的,默認的單位是字節,也可使用E、P、T、G、M和K做爲單位後綴,或者是Ei、Pi、Ti、Gi、Mi、Ki等形式單位後綴。 

資源需求舉例:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx
    resources:
      requests:
        memory: "128Mi"
        cpu: "200m"

上面的配置清單中,nginx請求的CPU資源大小爲200m,這意味着一個CPU核心足以知足nginx以最快的方式運行,其中對內存的指望可用大小爲128Mi,實際運行時不必定會用到這麼多的資源。考慮到內存的資源類型,在超出指定大小運行時存在會被OOM killer殺死的可能性,因而該請求值屬於理想中使用的內存上限。

資源限制舉例:

容器的資源需求只是可以確保容器運行時所須要的最少資源量,可是並不會限制其可用的資源上限。當應用程序存在Bug時,也有可能會致使系統資源被長期佔用的狀況,這就須要另一個limits屬性對容器進行定義資源使用的最大可用量。CPU是屬於可壓縮資源,能夠進行自由地調節。而內存屬於硬限制性資源,當進程申請分配超過limit屬性定義的內存大小時,該Pod將會被OOM killer殺死。以下:

[root@k8s-master ~]# vim memleak-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: memleak-pod
  labels:
    app: memleak
spec:
  containers:
  - name: simmemleak
    image: saadali/simmemleak
    resources:
      requests:
        memory: "64Mi"
        cpu: "1"
      limits:
        memory: "64Mi"
        cpu: "1"

[root@k8s-master ~]# kubectl apply -f memleak-pod.yaml 
pod/memleak-pod created
[root@k8s-master ~]# kubectl get pods -l app=memleak
NAME          READY     STATUS      RESTARTS   AGE
memleak-pod   0/1       OOMKilled   2          12s
[root@k8s-master ~]# kubectl get pods -l app=memleak
NAME          READY     STATUS             RESTARTS   AGE
memleak-pod   0/1       CrashLoopBackOff   2          28s

Pod資源默認的重啓策略爲Always,在memleak由於內存限制而終止會當即重啓,此時該Pod會被OOM killer殺死,在屢次重複由於內存資源耗盡重啓會觸發Kunernetes系統的重啓延遲,每次重啓的時間會不斷拉長,後面看到的Pod的狀態一般爲"CrashLoopBackOff"。

 這裏還須要明確的是,在一個Kubernetes集羣上,運行的Pod衆多,那麼當節點都沒法知足多個Pod對象的資源使用時,是按照什麼樣的順序去終止這些Pod對象呢??

Kubernetes是沒法自行去判斷的,須要藉助於Pod對象的優先級進行斷定終止Pod的優先問題。根據Pod對象的requests和limits屬性,Kubernetes將Pod對象分爲三個服務質量類別:

  • Guaranteed:每一個容器都爲CPU和內存資源設置了相同的requests和limits屬性的Pod都會自動歸屬於該類別,屬於最高優先級。
  • Burstable:至少有一個容器設置了CPU或內存資源的requests屬性,單不知足Guaranteed類別要求的資源歸於該類別,屬於中等優先級。
  • BestEffort:未對任何容器設置requests屬性和limits屬性的Pod資源,自動歸於該類別,屬於最低級別。

 

顧名思義,最低級別,死得越快!

相關文章
相關標籤/搜索