- 容器與Pod的關系
- Sidecar pattern(邊車模式)
- 管理Pod物件的容器
- 定義鏡像的獲取策略
- 暴露埠
- 自定義運行的容器化應用
- 環境變數
- 標簽與標簽選擇器
- 標簽的管理
- 標簽選擇器
- 資源注解
- Pod物件的生命周期
- Phase
- Pod的創建程序
- Pod生命周期中的重要行為
- 用于初始化的容器
- 生命周期鉤子函式
- 容器的重啟策略
- Pod的終止程序
- Pod存活性探測
- exec
- httpGet
- tcpSocket
- 存活性探測行為屬性
- Pod就緒性探測
- 資源需求及資源限制
- 資源需求
- 資源限制
- Pod的服務質量類別
Pod是Kubernetes系統的基礎單元,是資源物件模型中可由用戶創建或部署的最小組件,也是在Kubernetes系統上運行容器化應用的資源物件,
容器與Pod的關系
Docker推薦采用單容器單行程的方式運行,但由于容器間的隔離機制,各容器行程間又無法實作IPC(Inter-Process Communication)通信,這就導致功能相關的容器之間通信困難,比如主容器與負責日志收集的容器之間的通信,而Pod資源抽象正是用來解決此類問題的組件,Pod物件是一組容器的集合,這些容器共享Network、UTS(UNIX Time-sharing System)及IPC名稱空間,因此具有相同的域名、主機名和網路介面,并可通過IPC直接通信,
為一個Pod物件中的各容器提供網路名稱空間等共享機制的是底層基礎容器pause,
盡管Pod支持運行多個容器,但作為最佳實踐,除非多個行程之間具有密切的關系,否則都應該將其構建到多個Pod中,這樣多個Pod可被調度至多個不同的主機運行,提高了資源利用率,也便于規模的伸縮,
Sidecar pattern(邊車模式)
多個行程之間具有密切的關系時,一般按照邊車模型來組織多個容器,邊車即為Pod的主應用容器提供協同的輔助應用容器,典型的應用場景是將主應用容器中的日志使用agent收集至日志服務器中時,可以將agent運行為輔助應用容器,
管理Pod物件的容器
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可以自定義鏡像的獲取策略,
- Always:鏡像標簽為“latest”或鏡像不存在時總是從指定的倉庫中獲取鏡像
- IfNotPresent:僅當本地鏡像缺失時才從目標倉庫下載鏡像
- Never:禁止從倉庫下載鏡像,即僅使用本地鏡像
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目前支持兩個選擇器:基于
- equality-based,可用運算子有“=”“==”和“! =”三種,前兩種等價
- set-based,支持in、notin和exists三種運算子,此外還有可以只指定KEY來篩選所有存在此鍵名標簽的資源,!KEY則篩選所有不存在此鍵名標簽的資源
使用標簽選擇器時遵循以下邏輯: - 同時指定的多個選擇器之間的邏輯關系為“與”操作
- 使用空值的標簽選擇器意味著每個資源物件都將被選中
- 空的標簽選擇器將無法選出任何資源,
Kubernetes的諸多資源物件必須以標簽選擇器的方式關聯到Pod資源物件,例如Service、Deployment和ReplicaSet型別的資源等,可以在spec欄位通過嵌套的“selector”欄位來指定選擇器,有兩種方式:
- matchLabels:通過直接給定鍵值對來指定標簽選擇器
- matchExpressions:基于運算式指定的標簽選擇器串列,每個選擇器都形如“{key:KEY_NAME, operator: OPERATOR,values: [VALUE1, VALUE2, …]}”,選擇器串列間為“邏輯與”關系;使用In或NotIn運算子時,其values不強制要求為非空的字串串列,而使用Exists或DostNotExist時,其values必須為空,
格式舉例:
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物件總是應該處于其生命行程中以下幾個Phase(階段)之一:
- Pending:API Server創建了Pod資源物件并已存入etcd中,但它尚未被調度完成,或者仍處于從倉庫下載鏡像的程序中,?
- Running:Pod已經被調度至某節點,并且所有容器都已經被kubelet創建完成,?
- Succeeded:Pod中的所有容器都已經成功終止并且不會被重啟,?
- Failed:所有容器都已經終止,但至少有一個容器終止失敗,即容器回傳了非0值的退出狀態或已經被系統終止,?
- Unknown:API Server無法正常獲取到Pod物件的狀態資訊,通常是由于其無法與所在作業節點的kubelet通信所致,
Pod的創建程序
Pod的創建程序是指Pod自身及其主容器及其輔助容器創建的程序,

- 用戶通過kubectl或其他API客戶端提交PodSpec給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啟動容器,并將容器的結果狀態回送至API Server,
- API Server將Pod狀態資訊存入etcd系統中,
- 在etcd確認寫入操作成功完成后,API Server將確認資訊發送至相關的kubelet,事件將通過它被接受,
Pod生命周期中的重要行為
除了創建應用容器(主容器及其輔助容器)之外,用戶還可以為Pod物件定義其生命周期中的多種行為,如用于初始化的容器、存活性探測及就緒性探測等
用于初始化的容器
用于初始化的容器(init container)是應用程式的主容器啟動之前要運行的容器,常用于為主容器執行一些預置操作,典型的應用如:
- 用于運行特定的工具程式,出于安全等方面的原因,這些程式不適于包含在主容器鏡像中,
- 提供主容器鏡像中不具備的工具程式或自定義代碼,
- 為容器鏡像的構建和部署人員提供了分離、獨立作業的途徑,使得他們不必協同起來制作單個鏡像檔案,
- 初始化容器和主容器處于不同的檔案系統視圖中,因此可以分別安全地使用敏感資料,例如Secrets資源,
- 初始化容器要先于應用容器串行啟動并運行完成,因此可用于延后應用容器的啟動直至其依賴的條件得到滿足,
在資源清單中通過initContainers欄位定義:
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v2
initContainers:
- name: init-something
image: busybox
command: ['sh', '-c', 'sleep 10']
生命周期鉤子函式
Kubernetes為容器提供了兩種生命周期鉤子:
- postStart:在容器創建完成之后立即運行,但是Kubernetes無法確保它一定會在容器中的ENTRYPOINT之前運行,
- preStop:在容器終止操作之前立即運行,它以同步的方式呼叫,因此在其完成之前會阻塞洗掉容器的操作,
鉤子函式的實作方式有“Exec”和“HTTP”兩種,前一種在鉤子事件觸發時直接在當前容器中運行由用戶定義的命令,后一種則是在當前容器中向某URL發起HTTP請求,鉤子函式定義在容器的spec.lifecycle欄位,
容器的重啟策略
容器程式發生崩潰或容器申請超出限制的資源等原因都可能會導致Pod物件的終止,此時是否應該重建該Pod物件則取決于其重啟策略(restartPolicy)屬性的定義,
- Always:只要Pod物件終止就將其重啟,此為默認設定,
- OnFailure:僅在Pod物件出現錯誤時方才將其重啟,
- Never:從不重啟,
容器在重啟失敗后,之后的重啟將有一段時間的延遲,且延遲時間越來越長,依次為10秒、20秒、40秒、80秒、160秒、300秒,
Pod的終止程序

- 用戶發送洗掉Pod物件的命令,
- API服務器中的Pod物件會隨著時間的推移而更新,在寬限期內(默認為30秒),Pod被視為“dead”,
- 將Pod標記為“Terminating”狀態,
- (與第3步同時運行)kubelet在監控到Pod物件轉為“Terminating”狀態的同時啟動Pod關閉程序,
- (與第3步同時運行)端點控制器監控到Pod物件的關閉行為時將其從所有匹配到此端點的Service資源的端點串列中移除,
- 如果當前Pod物件定義了preStop鉤子處理器,則在其標記為“terminating”后即會以同步的方式啟動執行;如若寬限期結束后,preStop仍未執行結束,則第2步會被重新執行并額外獲取一個時長為2秒的小寬限期,
- Pod物件中的容器行程收到TERM信號,
- 寬限期結束后,若存在任何一個仍在運行的行程,那么Pod物件即會收到SIGKILL信號,
- Kubelet請求API Server將此Pod資源的寬限期設定為0從而完成洗掉操作,它變得對用戶不再可見,
如果在等待行程終止的程序中,kubelet或容器管理器發生了重啟,那么終止操作會重新獲得一個滿額的洗掉寬限期并重新執行洗掉操作,
Pod存活性探測
kubelet可基于存活性探測判定何時需要重啟一個容器,可通過spec.containers.livenessProbe定義,支持三種探測方法:
- exec
- httpGet
- tcpSocket
exec
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
httpGet方式是向目標容器發起一個HTTP GET請求,根據其回應碼進行結果判定,2xx或3xx時表示檢測通過,
可配置欄位有:
- host,請求的主機地址,默認為Pod IP,也可以在httpHeaders中使用“Host:”來定義,
- port,請求的埠,必選欄位,
- httpHeaders,自定義的請求報文頭,
- path,請求的HTTP資源路徑,
- scheme:建立連接使用的協議,僅可為HTTP或HTTPS,默認為HTTP,
示例
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資源應該以輕量化的方式在內部對應用程式的各關鍵組件進行全面檢測以確保它們可正常向客戶端提供完整的服務,
tcpSocket
基于TCP的存活性探測用于向容器的特定埠發起TCP請求并嘗試建立連接,連接建立成功即為通過檢測,相比較來說,它比基于HTTP的探測要更高效、更節約資源,但精準度較低,
可配置欄位有:
- host,請求連接的目標IP地址,默認為Pod IP,
- port,請求連接的目標埠,必選欄位,
舉例:
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
- initialDelaySeconds,存活性探測延遲時長,即容器啟動多久之后再開始第一次探測操作,顯示為delay屬性,默認為0秒,整型
- timeoutSeconds,存活性探測的超時時長,顯示為timeout屬性,默認為1s,整型,最小1s
- periodSeconds,存活性探測的頻度,顯示為period屬性,整型,默認為10s,最小值為1s;過高的頻率會對Pod物件帶來較大的額外開銷,而過低的頻率又會使得對錯誤的反應不及時
- successThreshold,處于失敗狀態時,探測操作至少連續多少次的成功才被認為是通過檢測,顯示為#success屬性,默認值為1,最小值也為1,整型
- failureThreshold:處于成功狀態時,探測操作至少連續多少次的失敗才被視為是檢測不通過,顯示為#failure屬性,默認值為3,最小值為1,整型,
另外,liveness檢測僅對當前服務有效,比如但后端服務(如資料庫或快取服務)導致故障時,重啟當前服務并不能解決問題,但它卻會被一次次重啟,直到后端服務恢復正常為止,
Pod就緒性探測
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中的容器上進行,支持兩種屬性:
- requests,定義了其請求的確保可用值,即容器運行可能用不到這些額度的資源,但用到時必須要確保有如此多的資源可用;
- limits,限制資源可用的最大值
在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殺死,
Pod的服務質量類別
Kubernetes允許節點資源對limits的過載使用,這意味著節點無法同時滿足其上的所有Pod物件以資源滿載的方式運行,于是就需要確定Pod物件的優先級,在記憶體資源緊缺時,先終止低優先級的Pod物件,
Pod物件的優先級是根據requests和limits屬性確定的,分為三個級別或QoS(Quality of Service):
- Guaranteed,Pod中所有容器對所有資源型別都定義了Limits和Requests,而且Limits值等于Requests值且不為0,Requests值未定義時默認等于Limits,優先級最高,
- BestEffort,沒有為任何一個容器設定requests或limits屬性的Pod資源屬于這一類,優先級最低,
- Burstable,不為Guaranteed和BestEffort時,優先級中等,
以上只適用于記憶體資源緊缺時,CPU資源無法得到滿足時,Pod僅僅是暫時獲取不到相應的資源而已,
學習資料
《Kubernetes實戰進階》 馬永亮著
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/273557.html
標籤:其他
上一篇:vs建立資源檔案,無用ID洗掉
