Kubernetes 之資源清單

K8S 中所有的內容都抽象爲了資源,資源實例化之後就叫做對象。

在 Kubernetes 系統中,Kubernetes 對象是持久化的實體,Kubernetes 使用這些實體去表示整個集羣的狀態。特別地,它們描述瞭如下信息:

Kubernetes 對象是 “目標性記錄” —— 一旦創建對象,Kubernetes 系統將持續工作以確保對象存在。通過創建對象,本質上是在告知 Kubernetes 系統,所需要的集羣工作負載看起來是什麼樣子的,這就是 Kubernetes 集羣的期望狀態。

對象資源的分類

根據不同的級別,可以將 Kubernetes 中的資源進行多種分類。

Kubernetes 是一個可移植的、可擴展的開源平臺,用於管理容器化的工作負載和服務,可促進聲明式配置和自動化。Kubernetes 擁有一個龐大且快速增長的生態系統。Kubernetes 的服務、支持和工具廣泛可用。以下列舉的內容都是 Kubernetes 中的 Object,這些對象都可以在 yaml 文件中作爲一種 API 類型來配置。

常用字段的解釋

下面就需要我們熟悉,如何使用 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 分配一個唯一的 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。

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 就緒之前運行完成。

如果 Pod 內設置的 Init 容器運行失敗了,那麼 Kubernetes 就會不斷地重啓該 Pod,直到 Init 容器成功爲止。然而,如果 Pod 對應的重啓策略 restartPolicy 的值爲 Never,它不會重新啓動。如果爲一個 Pod 指定了多個 Init 容器,這些容器會按順序逐個運行。每個 Init 容器必須運行成功,下一個才能夠運行。當所有的 Init 容器運行完成時,Kubernetes 纔會爲 Pod 初始化應用容器並像平常一樣運行。

因爲 Init 容器具有與應用容器分離的單獨鏡像,其啓動相關代碼具有如下優勢:

# 等待一個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 服務。

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"]
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 的 status 定義在 PodStatus 對象中,其中有一個 phase 字段。Pod 的運行階段 (phase) 中其生命週期中的簡單宏觀概述。該階段並不是對容器或 Pod 的綜合彙總,也不是爲了做爲綜合狀態機。下面是 phase 可能的值:

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,有三種類型的處理程序:

Kubelet 可以選擇是否執行在容器上運行的三種探針執行和做出反應:

檢測探針 - 就緒檢測

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

檢測探針 - 存活檢測

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
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
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 中只有一個容器並且正在運行,容器成功退出

Pod 中只有一個容器並且正在運行,容器退出失敗

Pod 中有兩個容器並且正在運行,容器 1 退出失敗

Pod 中只有一個容器並處於運行狀態,容器運行時內存超出限制

Pod 正在運行,磁盤故障

Pod 正在運行,其節點被分段

_作者: Escape _

鏈接: https://www.escapelife.site/posts/49d1a23c.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ziIU37gMywrirpU8B3foiQ