Kubernetes 之資源清單
K8S 中所有的內容都抽象爲了資源,資源實例化之後就叫做對象。
在 Kubernetes 系統中,Kubernetes 對象是持久化的實體,Kubernetes 使用這些實體去表示整個集羣的狀態。特別地,它們描述瞭如下信息:
-
哪些容器化應用在運行,以及在哪個 Node 上
-
可以被應用使用的資源
-
關於應用運行時表現的策略,比如重啓策略、升級策略,以及容錯策略
Kubernetes 對象是 “目標性記錄” —— 一旦創建對象,Kubernetes 系統將持續工作以確保對象存在。通過創建對象,本質上是在告知 Kubernetes 系統,所需要的集羣工作負載看起來是什麼樣子的,這就是 Kubernetes 集羣的期望狀態。
對象資源的分類
根據不同的級別,可以將 Kubernetes 中的資源進行多種分類。
Kubernetes 是一個可移植的、可擴展的開源平臺,用於管理容器化的工作負載和服務,可促進聲明式配置和自動化。Kubernetes 擁有一個龐大且快速增長的生態系統。Kubernetes 的服務、支持和工具廣泛可用。以下列舉的內容都是 Kubernetes 中的 Object,這些對象都可以在 yaml 文件中作爲一種 API 類型來配置。
-
工作負載型資源
-
Pod、ReplicaSet、Deployment、StatefulSet、DaemonSet、Job、CronJob
-
服務發現及負載均衡型資源
-
Service、Ingress
-
配置與存儲型資源
-
Volume、CSI
-
特殊類型的存儲卷
-
ConfigMap、Secret、DownwardAPI
-
集羣級別資源
-
Namespace、Node、Role、ClusterRole、RoleBinding、ClusterRoleBinding
-
元數據型資源
-
HPA、PodTemplate、LimitRange
常用字段的解釋
下面就需要我們熟悉,如何使用 yaml 文件來描述 Kubernetes 對象。
當創建 Kubernetes 對象時,必須提供對象的規約,用來描述該對象的期望狀態,以及關於對象的一些基本信息,例如名稱。當使用 Kubernetes API 創建對象時,或者直接創建或者基於 kubectl,API 請求必須在請求體中包含 JSON 格式的信息。大多數情況下,需要在 .yaml 文件中爲 kubectl 提供這些信息。kubectl 在發起 API 請求時,將這些信息轉換成 JSON 格式。
大體的 .yaml 文件格式如下所示,其中包含的都是配置 yaml 文件啓動服務必須或者建議配置的字段。
apiVersion: group/api-version # 版本信息
kind: # 資源類別
metadata: # 資源元數據
name: # 元數據對應的名稱
namespace: # 元數據所屬的命名空間
lables: # 對資源打上便籤供後續使用
annotations: # 主要目的是方便用戶閱讀查找
spec: # 定義期望狀態
status: # 設置當前狀態
這裏有一個 .yaml 示例文件,展示了 Kubernetes Deployment 的必需字段和對象規約。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: app-test
lables:
apps: nginx
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
使用類似於上面的 .yaml 文件來創建 Deployment,一種方式是使用 kubectl 命令行接口中的 kubectl apply 命令, 將 .yaml 文件作爲參數。
# 創建Deployment服務
kubectl apply -f ./deployment.yaml --record
Pod 的相關知識
瞭解和使用 Pod,才能掌握其應用範圍和邊界。
什麼是 Pod
Pod 是 Kubernetes 應用程序的基本執行單元,即它是 Kubernetes 對象模型中創建或部署的最小和最簡單的單元。簡單的,我們可以理解爲 Pod 是在集羣上運行的進程。Pod 封裝了應用程序容器或者在某些情況下封裝多個容器、存儲資源、唯一網絡 IP 以及控制容器應該如何運行的選項。其中,Docker 是 Kubernetes Pod 中最常用的容器運行時,但 Pod 也能支持其他的容器運行時。Pod 的兩個主要用途如下所示:
-
運行單個容器的 Pod —— 支持多容器的微服務實例
-
每個 Pod 一個容器的模型是最常見的使用情況,在這種情況下,可以將 Pod 看作單個容器的包裝器,並且 Kubernetes 直接管理 Pod,而不是容器。
-
運行多個協同工作的容器的 Pod —— 基於多容器微服務模型的分佈式應用模型
-
Pod 可能封裝由多個緊密耦合且需要共享資源的共處容器組成的應用程序,Pod 將這些容器和存儲資源打包爲一個可管理的實體。
-
分佈式系統工具包:容器組合的模式 —— 有狀態服務水平擴展
-
即將多個服務同時封裝到同一個 Pod 中,對外提供服務,而不再需要用戶手動安裝其他服務或者工具,真正做到即時即用的便捷效果。
-
容器設計模式
-
雲原生應用運行的環境都是複雜的分佈式環境,在這種情況下,一些有用的設計模式可以起到四兩撥千斤的作用。目前 K8s 社區推出的容器設計模式主要分爲三大類:單容器管理模式、單節點多容器模式、多節點多容器模式。
Pod 中的容器被自動的安排到集羣中的同一物理或虛擬機上,並可以一起進行調度。容器可以共享資源和依賴、彼此通信、協調何時以及何種方式終止它們。Pod 提供了兩種共享資源:網絡 和 存儲。
網絡
每個 Pod 分配一個唯一的 IP 地址。Pod 中的每個容器共享網絡命名空間,包括 IP 地址和網絡端口。Pod 內的容器 可以使用 localhost 互相通信。當 Pod 中的容器與 Pod 之外 的實體通信時,它們必須協調如何使用共享的網絡資源,例如端口。
存儲
一個 Pod 可以指定一組共享存儲卷。Pod 中的所有容器都可以訪問共享卷,允許這些容器共享數據。卷還允許 Pod 中的持久數據保留下來,以防其中的容器需要重新啓動。
# 通過定義清單文件創建Pod
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox
command: ["sh", "-c", "echo Hello Kubernetes! && sleep 3600"]
# 通過定義清單文件創建Pod
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
spec:
containers:
- name: myapp-1
image: hub.escapelife.site/library/nginx-test:v1
- name: busybox-1
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
Pod 的使用
我們很少會直接在 kubernetes 中創建單個 Pod。因爲 Pod 的生命週期是短暫的,用後即焚的實體。當 Pod 被創建後,都會被 Kubernetes 調度到集羣的 Node 上。直到 Pod 的進程終止、被刪掉、因爲缺少資源而被驅逐、或者 Node 故障之前這個 Pod 都會一直保持在那個 Node 上。
我們需要知道 Pod 本身是不會自愈修復的。如果 Pod 運行的 Node 故障或者是調度器本身故障,這個 Pod 就會被刪除。同樣的,如果 Pod 所在 Node 因爲缺少資源或者 Pod 處於維護狀態,那麼 Pod 也就會被自動驅逐掉。Kubernetes 使用更高級的稱爲 Controller 的抽象層,來管理 Pod 實例。雖然可以直接使用 Pod,但是在 Kubernetes 中通常是使用 Controller 來管理 Pod 的。Controller 可以創建和管理多個 Pod,提供副本管理、滾動升級和集羣級別的自愈能力。
需要注意的是,重啓 Pod 中的容器跟重啓 Pod 不是一回事。Pod 只提供容器的運行環境並保持容器的運行狀態,重啓容器不會造成 Pod 重啓。
Kubernetes 使用了一個更高級的稱爲 控制器 的抽象,由它處理相對可丟棄的 Pod 實例的管理工作。因此,雖然可以直接使用 Pod,但在 Kubernetes 中,更爲常見的是使用控制器管理 Pod。
-
Deployment
-
StatefulSet
-
DaemonSet
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
Pod 的生命週期
Pod 的生命週期纔是 Kubernetest 資源清單中的重中之重!
Kubernetes 中的基本組件 kube-controller-manager 就是用來控制 Pod 的狀態和生命週期的,在瞭解各種 controller 之前我們有必要先了解下 Pod 本身和其生命週期。想要深入理解 Pod 的實現原理,最好最快的辦法就是從 Pod 的生命週期入手,通過理解 Pod 創建、重啓和刪除的原理我們最終就能夠系統地掌握 Pod 的生命週期與核心原理。
瞭解 Init 容器
我們知道 Pod 可以包含多個容器,應用運行在這些容器裏面,同時 Pod 也可以有一個或多個先於應用容器啓動的 Init 容器。Init 容器與普通的容器非常像,但是有兩點不同之處。與普通容器的不同之處在於,Init 容器支持應用容器的全部字段和特性,包括資源限制、數據卷和安全設置,但是不支持 Readiness Probe,因爲它們必須在 Pod 就緒之前運行完成。
-
其一就是,Init 容器總是運行到完成爲止。
-
其二就是,每個 Init 容器都必須在下一個 Init 容器啓動之前成功完成纔會繼續運行。
如果 Pod 內設置的 Init 容器運行失敗了,那麼 Kubernetes 就會不斷地重啓該 Pod,直到 Init 容器成功爲止。然而,如果 Pod 對應的重啓策略 restartPolicy 的值爲 Never,它不會重新啓動。如果爲一個 Pod 指定了多個 Init 容器,這些容器會按順序逐個運行。每個 Init 容器必須運行成功,下一個才能夠運行。當所有的 Init 容器運行完成時,Kubernetes 纔會爲 Pod 初始化應用容器並像平常一樣運行。
因爲 Init 容器具有與應用容器分離的單獨鏡像,其啓動相關代碼具有如下優勢:
-
Init 容器可以包含一些安裝過程中應用容器中不存在的實用工具或個性化代碼。例如,沒有必要僅爲了在安裝過程中使用類似 sed、 awk、 python 或 dig 這樣的工具而去 FROM 一個鏡像來生成一個新的鏡像。
-
Init 容器可以安全地運行這些工具,避免這些工具導致應用鏡像的安全性降低。應用鏡像的創建者和部署者可以各自獨立工作,而沒有必要聯合構建一個單獨的應用鏡像。
-
Init 容器能以不同於 Pod 內應用容器的文件系統視圖運行。因此,Init 容器可具有訪問 Secrets 的權限,而應用容器不能夠訪問。
-
由於 Init 容器必須在應用容器啓動之前運行完成,因此 Init 容器提供了一種機制來阻塞或延遲應用容器的啓動,直到滿足了一組先決條件。一旦前置條件滿足,Pod 內的所有的應用容器會並行啓動。
# 等待一個Service完成創建,通過類似如下shell命令
for i in {1..100}; do sleep 1; if dig myservice; then exit 0; fi; exit 1
# 在啓動應用容器之前等一段時間,使用類似命令
sleep 60
使用 Init 容器
下面的例子定義了一個具有 2 個 Init 容器的簡單 Pod。第一個等待 myservice 啓動,第二個等待 mydb 啓動。一旦這兩個 Init 容器都啓動完成,Pod 將啓動 spec 區域中的應用容器。下面的準備的 yaml 文件展示了 mydb 和 myservice 兩個 Pod 對應的 Service 服務。
- Pod
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command:
- "sh"
- "-c"
- "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"
- name: init-mydb
image: busybox:1.28
command:
- "sh"
- "-c"
- "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
- Service
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
要啓動這個 Pod,可以執行如下命令,這可以看到對應的啓動過程,但是我們會發現其狀態一直停留在 init 的階段沒有就緒。通過日誌我們發現,是因爲 init 容器沒有檢測到 myservice 和 mydb 的 service 啓動導致的。
# 啓動Pod
$ kubectl apply -f myapp.yaml
pod/myapp-pod created
# 檢查Pod狀態
$ kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
# 如需更詳細的信息
$ kubectl describe -f myapp.yaml
# 如需查看Pod內Init容器的日誌
$ kubectl logs myapp-pod -c init-myservice
$ kubectl logs myapp-pod -c init-mydb
創建 mydb 和 myservice 的 service 之後,這樣將能看到這些 Init 容器 執行完畢,隨後 my-app 的 Pod 轉移進入 Running 狀態且 myapp-pod 被創建了。需要注意的是,如果 Pod 重啓,所有 Init 容器必須重新執行。
# 創建mydb和myservice
$ kubectl create -f services.yaml
service "myservice" created
service "mydb" created
# 查看Pod狀態
$ kubectl get -f myapp.yaml
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
狀態和策略
- Pod 的各種狀態
Pod 的 status 定義在 PodStatus 對象中,其中有一個 phase 字段。Pod 的運行階段 (phase) 中其生命週期中的簡單宏觀概述。該階段並不是對容器或 Pod 的綜合彙總,也不是爲了做爲綜合狀態機。下面是 phase 可能的值:
- Pod 的重啓策略
Pod 重啓策略 RestartPolicy 應用於 Pod 內的所有容器,並且僅在 Pod 所處的 Node 上由 kubelet 進行判斷和重啓操作。當某個容器異常退出或者健康檢查失敗時,kubelet 將根據 RestartPolicy 的設置來進行相應的操作。Pod 的重啓策略包括 Always、OnFailure 和 Never 三種,默認值爲 Always。
kubelet 重啓失效容器的時間間隔以 sync-frequency 乘以 2n 來計算,例如 1、2、4 等,最長延時 5min,並且在成功重啓後的 10min 後重置該時間。
容器探針的類型
探針是由 kubelet 對容器執行的定期診斷,主要是爲了保證我們在使用容器探針來幫助我們檢測和保證 Pod 中的服務正常運行。要執行診斷,kubelet 調用由容器實現的 Handler,有三種類型的處理程序:
-
ExecAction
-
在容器內執行指定命令。如果命令退出時返回碼爲 0 則認爲診斷成功。
-
TCPSocketAction
-
對指定端口上的容器的 IP 地址進行 TCP 檢查。如果端口打開,則診斷被認爲是成功的。
-
HTTPGetAction
-
對指定的端口和路徑上的容器的 IP 地址執行 HTTP Get 請求。如果響應的狀態碼大於等於 200 且小於 400,則診斷被認爲是成功的。
Kubelet 可以選擇是否執行在容器上運行的三種探針執行和做出反應:
-
livenessProbe
-
指示容器是否正在運行。如果存活探測失敗,則 kubelet 會殺死容器,並且容器將受到其 重啓策略 的影響。如果容器不提供存活探針,則默認狀態爲 Success。
-
readinessProbe
-
指示容器是否準備好服務請求。如果就緒探測失敗,端點控制器將從與 Pod 匹配的所有 Service 的端點中刪除該 Pod 的 IP 地址。初始延遲之前的就緒狀態默認爲 Failure。如果容器不提供就緒探針,則默認狀態爲 Success。
-
startupProbe
-
指示容器中的應用是否已經啓動。如果提供了啓動探測 (startup probe),則禁用所有其他探測,直到它成功爲止。如果啓動探測失敗,kubelet 將殺死容器,容器服從其重啓策略進行重啓。如果容器沒有提供啓動探測,則默認狀態爲成功 Success。
檢測探針 - 就緒檢測
- readinessProbe-httpget
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: escape/nginx-test:v1
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 80
path: /index1.html
initialDelaySeconds: 1
periodSeconds: 3
檢測探針 - 存活檢測
- livenessProbe-exec
apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: busybox
imagePullPolicy: IfNotPresent
command:
- "/bin/sh"
- "-c"
- "touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600 "
livenessProbe:
exec:
command: ["test", "-e", "/tmp/live"]
initialDelaySeconds: 1
periodSeconds: 3
- livenessProbe-tcp
apiVersion: v1
kind: Pod
metadata:
name: probe-tcp
namespace: default
spec:
containers:
- name: nginx
image: escape/nginx-test:v1
livenessProbe:
initialDelaySeconds: 5
timeoutSeconds: 1
tcpSocket:
port: 80
- livenessProbe-httpget
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-httpget-container
image: escape/nginx-test:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
timeoutSeconds: 10
啓動退出動作
我們可以設置,在 Pod 啓動和停止的時候執行某些操作。
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command:
- "/bin/sh"
- "-c"
- "echo Hello from the postStart handler > /usr/share/message"
preStop:
exec:
command:
- "/bin/sh"
- "-c"
- "echo Hello from the poststop handler > /usr/share/message"
Pod 的狀態示例
Pod 中只有一個容器並且正在運行,容器成功退出
-
記錄事件完成
-
如果 restartPolicy 爲:
-
Always:重啓容器;Pod phase 仍爲 Running
-
OnFailure:Pod phase 變成 Succeeded
-
Never:Pod phase 變成 Succeeded
Pod 中只有一個容器並且正在運行,容器退出失敗
-
記錄失敗事件
-
如果 restartPolicy 爲:
-
Always:重啓容器;Pod phase 仍爲 Running
-
OnFailure:重啓容器;Pod phase 仍爲 Running
-
Never:Pod phase 變成 Failed
Pod 中有兩個容器並且正在運行,容器 1 退出失敗
-
記錄失敗事件
-
如果 restartPolicy 爲:
-
Always:重啓容器;Pod phase 仍爲 Running
-
OnFailure:重啓容器;Pod phase 仍爲 Running
-
Never:不重啓容器;Pod phase 仍爲 Running
-
如果有容器 1 沒有處於運行狀態,並且容器 2 退出:
-
Always:重啓容器;Pod phase 仍爲 Running
-
OnFailure:重啓容器;Pod phase 仍爲 Running
-
Never:Pod phase 變成 Failed
-
記錄失敗事件
-
如果 restartPolicy 爲:
Pod 中只有一個容器並處於運行狀態,容器運行時內存超出限制
-
容器以失敗狀態終止
-
記錄 OOM 事件
-
如果 restartPolicy 爲 :
-
Always:重啓容器;Pod phase 仍爲 Running
-
OnFailure:重啓容器;Pod phase 仍爲 Running
-
Never: 記錄失敗事件;Pod phase 仍爲 Failed
Pod 正在運行,磁盤故障
-
殺掉所有容器。記錄適當事件
-
Pod phase 變成 Failed
-
如果使用控制器來運行,Pod 將在別處重建
Pod 正在運行,其節點被分段
-
節點控制器等待直到超時
-
節點控制器將 Pod phase 設置爲 Failed
-
如果是用控制器來運行,Pod 將在別處重建
_作者: Escape _
鏈接: https://www.escapelife.site/posts/49d1a23c.html
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ziIU37gMywrirpU8B3foiQ