Pod是Kubernetes系統的基礎單元,是資源對象模型中可由用戶建立或部署的最小組件,也是在Kubernetes系統上運行容器化應用的資源對象。html
Docker推薦採用單容器單進程的方式運行,但因爲容器間的隔離機制,各容器進程間又沒法實現IPC(Inter-Process Communication)通訊。這就致使功能相關的容器之間通訊困難,好比主容器與負責日誌收集的容器之間的通訊。而Pod資源抽象正是用來解決此類問題的組件,Pod對象是一組容器的集合,這些容器共享Network、UTS(UNIX Time-sharing System)及IPC名稱空間,所以具備相同的域名、主機名和網絡接口,並可經過IPC直接通訊。
爲一個Pod對象中的各容器提供網絡名稱空間等共享機制的是底層基礎容器pause。
儘管Pod支持運行多個容器,但做爲最佳實踐,除非多個進程之間具備密切的關係,不然都應該將其構建到多個Pod中,這樣多個Pod可被調度至多個不一樣的主機運行,提升了資源利用率,也便於規模的伸縮。nginx
多個進程之間具備密切的關係時,通常按照邊車模型來組織多個容器,邊車即爲Pod的主應用容器提供協同的輔助應用容器,典型的應用場景是將主應用容器中的日誌使用agent收集至日誌服務器中時,能夠將agent運行爲輔助應用容器。redis
Pod的配置清單舉例:數據庫
apiVersion: v1 kind: Pod metadata: name: pod-example spec: containers: - name: myapp image: ikubernetes/myapp:v2
其中spec字段下,containers爲及其子字段name爲必選項,image在手動場景Pod時必選,在但在被高級別管理資源如Deployment控制時可選,由於這個字段可能會被覆蓋。後端
Pod的核心功能是運行容器,而 經過image.imagePullPolicy能夠自定義鏡像的獲取策略。api
spec: containers: - name: myapp image: ikubernetes/myapp:v2 imagePullPolicy: Always
對於標籤爲「latest」的鏡像文件,其默認的鏡像獲取策略即爲「Always」,而對於其餘標籤的鏡像,其默認策略則爲「IfNotPresent」。緩存
在Pod中暴露端口與爲Docker容器暴露端口的意義不同:
在Docker的網絡模型中,使用默認網絡的容器化應用需經過NAT機制將其「暴露」(expose)到外部網絡中才能被其餘節點之上的容器客戶端所訪問;
而在K8S中,各Pod的IP地址已經處於同一網絡平面,不管是否爲容器暴露端口,都不會影響集羣中其餘節點之上的Pod客戶端對其進行訪問,因此暴露的端口只是信息性數據,並且顯式指定容器端口也方便調用。安全
spec: containers: - name: myapp image: ikubernetes/myapp:v2 ports: - name: http containerPort: 80 protocol: TCP
這裏的配置指定暴露容器上的TCP端口80,並將其命名爲http。
Pod對象的IP地址僅在當前集羣內可達,它們沒法直接接收來自集羣外部客戶端的請求流量,儘管它們的服務可達性不受工做節點邊界的約束,但依然受制於集羣邊界。如何讓集羣外部訪問到Pod對象,將在後面學習。服務器
command字段可以指定不一樣於鏡像默認運行的應用程序,而且能夠同時使用args字段進行參數傳遞,它們將覆蓋鏡像中的默認定義。不過,若是僅爲容器定義了args字段,那麼它將做爲參數傳遞給鏡像中默認指定運行的應用程序;若是僅爲容器定義了command字段,那麼它將覆蓋鏡像中定義的程序及參數,並以無參數方式運行應用程序。網絡
spec: containers: - name: myapp image: ikubernetes/myapp:v2 imagePullPolicy: Never command: ["/bin/sh"] args: ["-c", "while true; do sleep 30; done"]
環境變量也是向容器化應用傳遞配置的一種方式,向Pod對象中的容器環境變量傳遞數據的方法有兩種:env和envFrom,這裏只介紹第一種方式,第二種方式將在介紹ConfigMap和Secret資源時進行說明。環境變量一般由name和value字段構成。
spec: containers: - name: myapp image: ikubernetes/myapp:v2 env: - name: REDIS_HOST value: do.macOS - name: LOG_LEVEL value: info
標籤選擇器能夠對附帶標籤的資源對象進行挑選,並進行所須要的操做。一個對象可擁有不止一個標籤,而同一個標籤也可被添加至多個資源之上。
能夠爲資源附加多個不一樣緯度的標籤以實現靈活的資源分組管理功能,例如,版本標籤、環境標籤、分層架構標籤等,用於交叉標識同一個資源所屬的不一樣版本、環境及架構層級等。
定義標籤示例:
apiVersion: v1 kind: Pod metadata: name: pod-example labels: env: qa tier: frontend
資源建立後,在kubectl get pods
命令中添加--show-labels
選項就可顯示lables信息。
-L <key1>, <key2>
選項可增長對應的列信息。
直接管理活動對象的標籤:
kubectl label pods/pod-example release=beta
爲pod-example添加了release=beta,若是要修改已經存在的減值對,須要添加--overwrite
選項。
標籤選擇器用於表達標籤的查詢條件或選擇標準,Kubernetes API目前支持兩個選擇器:基於
Kubernetes的諸多資源對象必須以標籤選擇器的方式關聯到Pod資源對象,例如Service、Deployment和ReplicaSet類型的資源等,能夠在spec字段經過嵌套的「selector」字段來指定選擇器,有兩種方式:
selector: matchLabels: component: redis matchExpressions: - {key: tier, operator: In, values: [cache]} - {key: environment, operator: Exists, values:}
標籤以外,Pod與其餘各類資源還能使用資源註解(annotation),也是鍵值類型的數據,不過它不能用於標籤及挑選Kubernetes對象,僅可用於爲資源提供「元數據」信息。另外,註解中的元數據不受字符數量的限制,能夠爲結構化或非結構化形式,並且對字符類型也沒有限制。
Annotation中放置構建、發行或鏡像相關的信息,指向日誌、監控、分析或審計倉庫的地址,或者由客戶端庫或工具程序生成的用於調試目的的信息:如名稱、版本、構建信息等信息。
查看資源註解
使用kubectl get -o yaml
和kubectl describe
命令均能顯示資源的註解信息。
kubectl describe pods pod-example | grep "Annotations"
管理資源註解
在配置清單中定義annotations:
apiVersion: v1 kind: Pod metadata: name: pod-example annotations: created-by: "cluster admin"
追加annotations:
kubectl annotate pods pod-example created-by2="admin"
Pod的生命週期如圖:
Pod對象老是應該處於其生命進程中如下幾個Phase(階段)之一:
Pod的建立過程是指Pod自身及其主容器及其輔助容器建立的過程。
除了建立應用容器(主容器及其輔助容器)以外,用戶還能夠爲Pod對象定義其生命週期中的多種行爲,如用於初始化的容器、存活性探測及就緒性探測等
用於初始化的容器(init container)是應用程序的主容器啓動以前要運行的容器,經常使用於爲主容器執行一些預置操做,典型的應用如:
在資源清單中經過initContainers字段定義:
spec: containers: - name: myapp image: ikubernetes/myapp:v2 initContainers: - name: init-something image: busybox command: ['sh', '-c', 'sleep 10']
Kubernetes爲容器提供了兩種生命週期鉤子:
鉤子函數的實現方式有「Exec」和「HTTP」兩種,前一種在鉤子事件觸發時直接在當前容器中運行由用戶定義的命令,後一種則是在當前容器中向某URL發起HTTP請求。鉤子函數定義在容器的spec.lifecycle字段。
容器程序發生崩潰或容器申請超出限制的資源等緣由均可能會致使Pod對象的終止,此時是否應該重建該Pod對象則取決於其重啓策略(restartPolicy)屬性的定義。
容器在重啓失敗後,以後的重啓將有一段時間的延遲,且延遲時間愈來愈長,依次爲10秒、20秒、40秒、80秒、160秒、300秒。
若是在等待進程終止的過程當中,kubelet或容器管理器發生了重啓,那麼終止操做會從新得到一個滿額的刪除寬限期並從新執行刪除操做。
kubelet可基於存活性探測斷定什麼時候須要重啓一個容器。可經過spec.containers.livenessProbe定義,支持三種探測方法:
exec類型的探針經過在目標容器中執行由用戶自定義的命令來斷定容器的健康狀態,若命令狀態返回值爲0則表示「成功」經過檢測,其它值均爲「失敗」狀態。它只有一個可用屬性「command」,用於指定要執行的命令,示例:
apiVersion: v1 kind: Pod metadata: name: liveness-exec-demo labels: test: liveness-exec-demo 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"]
這段配置清單基於busybox鏡像啓動一個容器,並執行args定義的命令,此命令在容器啓動時建立/tmp/healthy文件,並於60秒以後將其刪除。存活性探針運行「test -e/tmp/healthy」命令檢查/tmp/healthy文件的存在性,若文件存在則返回狀態碼0,表示成功經過測試。
因此60秒後使用describe命令能夠看到容器被重啓的event。
httpGet方式是向目標容器發起一個HTTP GET請求,根據其響應碼進行結果斷定,2xx或3xx時表示檢測經過。
可配置字段有:
示例
apiVersion: v1 kind: Pod metadata: name: liveness-http-demo labels: test: liveness-http-demo 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/healthz"] livenessProbe: httpGet: path: /healthz port: http scheme: HTTP
這個配置清單經過postStart hook建立了一個專用於httpGet測試的頁面文件healthz。而爲httpGet探測指定的路徑爲「/healthz」,地址默認爲Pod IP,端口使用了容器中定義的端口名稱http。
啓動容器後健康檢查是正常的,但執行以下命令刪除healthz頁面後,可在event中看到Container liveness-http-demo failed liveness probe, will be restarted
。
kubectl exec liveness-http-demo rm /usr/share/nginx/html/healthz
通常應爲HTTP探測操做定義專用的URL路徑,此URL路徑對應的Web資源應該以輕量化的方式在內部對應用程序的各關鍵組件進行全面檢測以確保它們可正常向客戶端提供完整的服務。
基於TCP的存活性探測用於向容器的特定端口發起TCP請求並嘗試創建鏈接,鏈接創建成功即爲經過檢測。相比較來講,它比基於HTTP的探測要更高效、更節約資源,但精準度較低。
可配置字段有:
舉例:
spec: containers: - name: liveness-tcp-demo image: nginx:1.12-alpine livenessProbe: tcpSocket: port: 80
對於配置了liveness的pod,經過describe命令能夠看到相似這樣的信息,有delay、timeout等配置,因爲以前沒有指定因此都爲默認值:
Liveness: tcp-socket :80 delay=0s timeout=1s period=10s #success=1 #failure=3
另外,liveness檢測僅對當前服務有效,好比但後端服務(如數據庫或緩存服務)致使故障時,重啓當前服務並不能解決問題,但它卻會被一次次重啓,直到後端服務恢復正常爲止。
Pod對象啓動後,容器應用一般須要一段時間才能完成其初始化過程,例如加載配置或數據,甚至有些程序還須要預熱的過程。所以應該避免在Pod對象啓動後當即讓其處理客戶端請求,而是等待容器初始化工做執行完成並轉爲Ready狀態,尤爲是存在其餘提供相同服務的Pod對象的場景更是如此。
就緒性探測是用來判斷容器就緒與否的週期性操做,探測操做返回「success」狀態時,就認爲容器已經就緒。
與liveness探測相似,它也支持三種方式,但定義時使用的屬性名爲readinessProbe。
舉例:
apiVersion: v1 kind: Pod metadata: name: readiness-tcp-demo labels: test: readiness-tcp-demo spec: containers: - name: readiness-tcp-demo image: nginx:1.12-alpine readinessProbe: tcpSocket: port: 80
未定義就緒性探測的Pod對象在Pod進入「Running」狀態後將當即就緒。生產實踐中,必須爲須要時間進行初始化容器以及關鍵性Pod資源中的容器定義就緒性探測。
K8S中可由容器或Pod請求或消費的「計算資源」是指CPU和內存,其中CPU屬於可壓縮(compressible)型資源,可按需收縮,而內存則是不可壓縮型資源,對其執行收縮操做可能會致使沒法預知的問題。
目前資源隔離屬於容器級別,因此CPU和內存資源的配置須要在Pod中的容器上進行,支持兩種屬性:
在K8S中,1個單位的CPU至關於虛擬機上的1顆虛擬CPU(vCPU)或物理機上的一個超線程(Hyperthread,或稱爲一個邏輯CPU),它支持分數計量方式,一個核心(1 core)至關於1000個微核心(millicores),所以500m至關因而0.5個核心。內存的計量方式與平常使用方式相同,默認單位是字節,也可使用E(Ei)、P(Pi)、T(Ti)、G(Gi)、M(Mi)和K(Ki)做爲單位後綴。
apiVersion: v1 kind: Pod metadata: name: stress-demo spec: containers: - name: stress-demo image: ikubernetes/stress-ng command: ["/usr/bin/stress-ng", "-m 1", "-c 1", "--metrics-brief"] resources: requests: memory: "128Mi" cpu: "200m"
以上的配置清單定義了容器的資源需求爲128M內存、200m(0.2)個CPU核心。它運行stress-ng(一個多功能系統壓力測工具)鏡像啓動一個進程(-m 1)進行內存性能壓力測試,再啓動一個專用的CPU壓力測試進程(-c 1)。
而後使用kubectl exec stress-demo -- top
命令來查看資源的使用狀況,在個人電腦(6核,內存16G)上顯示的內存佔用爲262m,CPU佔用2*17%(約等於2/6,由於兩個測試線程分佈於兩個CPU核心以滿載的方式運行),都遠高於requests中定義的值,這是由於當前資源充裕,一旦
資源緊張時,節點僅保證容器有五分之一個CPU核心可用,對於有着6個核心的節點來講,它的佔用率約爲3.33%,多佔用的資源會被壓縮。內存爲非可壓縮型資源,因此此Pod在內存資源緊張時可能會因OOM被殺死(killed)。
若是沒有定義requests,那麼在CPU資源緊張時,可能會被其它Pod壓縮至極低的水平,甚至會達到Pod不可以被調度運行的境地,而不可壓縮型的內存資源,則可能因OOM致使進程被殺死。所以在Kubernetes系統上運行關鍵型業務相關的Pod時必須使用requests屬性爲容器定義資源的確保可用量。
集羣中的每一個節點擁有的CPU和內存資源是固定的,Kubernetes的調度器在調度Pod時,會根據容器的requests屬性來斷定哪些節點可接收運行當前的Pod資源,而對於一個節點的資源來講,每運行一個Pod對象,其requests中定義的請求量都要被預留,直到給全部Pod對象分配完爲止。
經過定義資源需求能夠保證容器的最少資源量,若是要限制容器使用資源的上限,則須要定義資源限制。
若是定義了資源限制,則容器進程沒法得到超出其CPU配額的可用時間,而進程申請分配超出其limits定義的內存資源時,它將被OOM killer殺死。
Kubernetes容許節點資源對limits的過載使用,這意味着節點沒法同時知足其上的全部Pod對象以資源滿載的方式運行。因而就須要肯定Pod對象的優先級,在內存資源緊缺時,先終止低優先級的Pod對象。
Pod對象的優先級是根據requests和limits屬性肯定的,分爲三個級別或QoS(Quality of Service):
以上只適用於內存資源緊缺時,CPU資源沒法獲得知足時,Pod僅僅是暫時獲取不到相應的資源而已。
《Kubernetes實戰進階》 馬永亮著