1. 程式人生 > 實用技巧 >K8S-POD詳解

K8S-POD詳解

Pod是最小的部署單元,也是後面經常配置的地方,本章節帶你熟悉Pod中常見資源配置及引數。

也就是YAML這部分:

  ...
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: a13552821243/java-demo:latest
        imagePullPolicy: Always
        name: java

6.1 Pod介紹

  • 最小部署單元

  • 一組容器的集合

  • 一個Pod中的容器共享網路名稱空間

  • Pod是短暫的

6.2 Pod存在的意義

Pod為親密性應用而存在。

親密性應用場景:

  • 兩個應用之間發生檔案互動

  • 兩個應用需要通過127.0.0.1或者socket通訊

  • 兩個應用需要發生頻繁的呼叫

6.3 Pod實現機制與設計模式

Pod本身是一個邏輯概念,沒有具體存在,那究竟是怎麼實現的呢?

眾所周知,容器之間是通過Namespace隔離的,Pod要想解決上述應用場景,那麼就要讓Pod裡的容器之間高效共享。

具體分為兩個部分:網路和儲存

  • 共享網路

kubernetes的解法是這樣的:會在每個Pod裡先啟動一個infra container小容器,然後讓其他的容器連線進來這個網路名稱空間,然後其他容器看到的網路試圖就完全一樣了,即網路裝置、IP地址、Mac地址等,這就是解決網路共享問題。在Pod的IP地址就是infra container的IP地址。

  • 共享儲存

比如有兩個容器,一個是nginx,另一個是普通的容器,普通容器要想訪問nginx裡的檔案,就需要nginx容器將共享目錄通過volume掛載出來,然後讓普通容器掛載的這個volume,最後大家看到這個共享目錄的內容一樣。

例如:

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: write
    image: centos
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"]
    volumeMounts:
      - name: data
        mountPath: /data

  - name: read
    image: centos
    command: ["bash","-c","tail -f /data/hello"]
    volumeMounts:
      - name: data
        mountPath: /data
  
  volumes:
  - name: data
    emptyDir: {}

上述示例中有兩個容器,write容器負責提供資料,read消費資料,通過資料卷將寫入資料的目錄和讀取資料的目錄都放到了該卷中,這樣每個容器都能看到該目錄。

驗證:


kubectl apply -f pod.yaml
kubectl logs my-pod -c read -f

在Pod中容器分為以下幾個型別:

  • Infrastructure Container:基礎容器,維護整個Pod網路空間,對使用者不可見

  • InitContainers:初始化容器,先於業務容器開始執行,一般用於業務容器的初始化工作

  • Containers:業務容器,具體跑應用程式的映象

6.4 映象拉取策略

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: java
      image: a13552821243/java-demo
      imagePullPolicy: IfNotPresent

imagePullPolicy 欄位有三個可選值:

  • IfNotPresent:映象在宿主機上不存在時才拉取

  • Always:預設值,每次建立 Pod 都會重新拉取一次映象

  • Never: Pod 永遠不會主動拉取這個映象

如果拉取公開的映象,直接按照上述示例即可,但要拉取私有的映象,是必須認證映象倉庫才可以,即docker login,而在K8S叢集中會有多個Node,顯然這種方式是很不放方便的!為了解決這個問題,K8s 實現了自動拉取映象的功能。 以secret方式儲存到K8S中,然後傳給kubelet。

6.5 資源限制

Pod資源配額有兩種:

  • 申請配額:排程時使用,參考是否有節點滿足該配置

    spec.containers[].resources.limits.cpu

    spec.containers[].resources.limits.memory

  • 限制配額:容器能使用的最大配置

    spec.containers[].resources.requests.cpu

    spec.containers[].resources.requests.memory

示例:

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
  - name: java
    image: a13552821243/java-demo
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

其中cpu值比較抽象,可以這麼理解:

1核=1000m

1.5核=1500m

那上面限制配置就是1核的二分之一(500m),即該容器最大使用半核CPU。

該值也可以寫成浮點數,更容易理解:

半核=0.5

1核=1

1.5核=1.5

6.6 重啟策略

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: java
      image: a13552821243java-demo
    restartPolicy: Always

restartPolicy欄位有三個可選值:

  • Always:當容器終止退出後,總是重啟容器,預設策略。

  • OnFailure:當容器異常退出(退出狀態碼非0)時,才重啟容器。適於job

  • Never:當容器終止退出,從不重啟容器。適於job

6.7 健康檢查

預設情況下,kubelet 根據容器狀態作為健康依據,但不能容器中應用程式狀態,例如程式假死。這就會導致無法提供服務,丟失流量。因此引入健康檢查機制確保容器健康存活。

健康檢查有兩種型別:

  • livenessProbe

    如果檢查失敗,將殺死容器,根據Pod的restartPolicy來操作。

  • readinessProbe

    如果檢查失敗,Kubernetes會把Pod從service endpoints中剔除。

這兩種型別支援三種檢查方法:

Probe支援以下三種檢查方法:

  • httpGet

    傳送HTTP請求,返回200-400範圍狀態碼為成功。

  • exec

    執行Shell命令返回狀態碼是0為成功。

  • tcpSocket

    發起TCP Socket建立成功。

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 60
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

上述示例:啟動容器第一件事建立檔案,停止30s,刪除該檔案,再停止60s,確保容器還在執行中。

驗證現象:容器啟動正常,30s後異常,會restartPolicy策略自動重建,容器繼續正常,反覆現象。

6.8 排程策略

先看下建立一個Pod的工作流程: create pod -> apiserver -> write etcd -> scheduler -> bind pod to node -> write etcd -> kubelet( apiserver get pod) -> dcoekr api,create container -> apiserver -> update pod status to etcd -> kubectl get pods

Pod根據排程器預設演算法將Pod分配到合適的節點上,一般是比較空閒的節點。但有些情況我們希望將Pod分配到指定節點,該怎麼做呢?

這裡給你介紹排程策略:nodeName、nodeSelector和汙點

1、nodeName

nodeName用於將Pod排程到指定的Node名稱上。

例如:下面示例會繞過排程系統,直接分配到k8s-node1節點。

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox
  namespace: default
spec:
  nodeName: k8s-node1
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "baidu.com"
kubectl label nodes k8s-node1 team=a
kubectl label nodes k8s-node2 team=b

2、nodeSelector

nodeSelector用於將Pod排程到匹配Label的Node上。

先給規劃node用途,然後打標籤,例如將兩臺node劃分給不同團隊使用:

後在建立Pod只會被排程到含有team=a標籤的節點上。

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  nodeSelector: 
    team: b
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "baidu.com"

**3、taint(汙點)與tolerations(容忍)**  

汙點應用場景:節點獨佔,例如具有特殊硬體裝置的節點,如GPU

設定汙點命令:
kubectl taint node [node] key=value[effect]

其中[effect] 可取值:

  • NoSchedule :一定不能被排程。

  • PreferNoSchedule:儘量不要排程。

  • NoExecute:不僅不會排程,還會驅逐Node上已有的Pod。

示例:

先給節點設定汙點,說明這個節點不是誰都可以排程過來的:

kubectl taint node k8s-node1 abc=123:NoSchedule

檢視汙點:

kubectl describe node k8s-node1 |grep Taints

然後在建立Pod只有聲明瞭容忍汙點(tolerations),才允許被排程到abc=123汙點節點上。


apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox3
  namespace: default
spec:
  tolerations:
  - key: "abc"
    operator: "Equal"
    value: "123"
    effect: "NoSchedule"
  containers:
  - image: busybox
    name: bs
    command:
    - "ping"
    - "baidu.com"

如果不配置容忍汙點,則永遠不會排程到k8s-node1。

去掉汙點:

kubectl taint node [node] key:[effect]-
kubectl taint node k8s-node1 abc:NoSchedule-

6.9 故障排查

# 檢視事件,可用於大部分資源
kubectl describe TYPE/NAME    
# 如果pod啟動失敗,先檢視日誌
kubectl logs TYPE/NAME [-c CONTAINER]  
# 進入到容器中debug
kubectl exec POD [-c CONTAINER] -- COMMAND [args...]