主頁 > 軟體設計 > k8s 配置的各種策略講解(鏡像拉取、資源配額、鉤子函式、容器探測、調度策略等等)

k8s 配置的各種策略講解(鏡像拉取、資源配額、鉤子函式、容器探測、調度策略等等)

2021-08-19 07:13:44 軟體設計

一、Pod

每個Pod中都可以包含一個或者多個容器,這些容器可以分為兩類:

  • 用戶程式所在的容器,數量可多可少

  • Pause容器,這是每個Pod都會有的一個根容器,它的作用有兩個:

    • 可以以它為依據,評估整個Pod的健康狀態

    • 可以在根容器上設定Ip地址,其它容器都此Ip(Pod IP),以實作Pod內部的網路通信

Pod內部的通訊采用的虛擬二層網路技術來實作,當前環境用的是Flannel

Pod的資源清單

apiVersion: v1     #必選,版本號,例如v1
kind: Pod         #必選,資源型別,例如 Pod
metadata:         #必選,元資料
  name: string     #必選,Pod名稱
  namespace: string  #Pod所屬的命名空間,默認為"default"
  labels:           #自定義標簽串列
    - name: string                 
spec:  #必選,Pod中容器的詳細定義
  containers:  #必選,Pod中容器串列
  - name: string   #必選,容器名稱
    image: string  #必選,容器的鏡像名稱
    imagePullPolicy: [ Always|Never|IfNotPresent ]  #獲取鏡像的策略 
    command: [string]   #容器的啟動命令串列,如不指定,使用打包時使用的啟動命令
    args: [string]      #容器的啟動命令引數串列
    workingDir: string  #容器的作業目錄
    volumeMounts:       #掛載到容器內部的存盤卷配置
    - name: string      #參考pod定義的共享存盤卷的名稱,需用volumes[]部分定義的的卷名
      mountPath: string #存盤卷在容器內mount的絕對路徑,應少于512字符
      readOnly: boolean #是否為只讀模式
    ports: #需要暴露的埠庫號串列
    - name: string        #埠的名稱
      containerPort: int  #容器需要監聽的埠號
      hostPort: int       #容器所在主機需要監聽的埠號,默認與Container相同
      protocol: string    #埠協議,支持TCP和UDP,默認TCP
    env:   #容器運行前需設定的環境變數串列
    - name: string  #環境變數名稱
      value: string #環境變數的值
    resources: #資源限制和請求的設定
      limits:  #資源限制的設定
        cpu: string     #Cpu的限制,單位為core數,將用于docker run --cpu-shares引數
        memory: string  #記憶體限制,單位可以為Mib/Gib,將用于docker run --memory引數
      requests: #資源請求的設定
        cpu: string    #Cpu請求,容器啟動的初始可用數量
        memory: string #記憶體請求,容器啟動的初始可用數量
    lifecycle: #生命周期鉤子
        postStart: #容器啟動后立即執行此鉤子,如果執行失敗,會根據重啟策略進行重啟
        preStop: #容器終止前執行此鉤子,無論結果如何,容器都會終止
    livenessProbe:  #對Pod內各容器健康檢查的設定,當探測無回應幾次后將自動重啟該容器
      exec:         #對Pod容器內檢查方式設定為exec方式
        command: [string]  #exec方式需要制定的命令或腳本
      httpGet:       #對Pod內個容器健康檢查方法設定為HttpGet,需要制定Path、port
        path: string
        port: number
        host: string
        scheme: string
        HttpHeaders:
        - name: string
          value: string
      tcpSocket:     #對Pod內個容器健康檢查方式設定為tcpSocket方式
         port: number
       initialDelaySeconds: 0       #容器啟動完成后首次探測的時間,單位為秒
       timeoutSeconds: 0          #對容器健康檢查探測等待回應的超時時間,單位秒,默認1秒
       periodSeconds: 0           #對容器監控檢查的定期探測時間設定,單位秒,默認10秒一次
       successThreshold: 0
       failureThreshold: 0
       securityContext:
         privileged: false
  restartPolicy: [Always | Never | OnFailure]  #Pod的重啟策略
  nodeName: <string> #設定NodeName表示將該Pod調度到指定到名稱的node節點上
  nodeSelector: obeject #設定NodeSelector表示將該Pod調度到包含這個label的node上
  imagePullSecrets: #Pull鏡像時使用的secret名稱,以key:secretkey格式指定
  - name: string
  hostNetwork: false   #是否使用主機網路模式,默認為false,如果設定為true,表示使用宿主機網路
  volumes:   #在該pod上定義共享存盤卷串列
  - name: string    #共享存盤卷名稱 (volumes型別有很多種)
    emptyDir: {}       #型別為emtyDir的存盤卷,與Pod同生命周期的一個臨時目錄,為空值
    hostPath: string   #型別為hostPath的存盤卷,表示掛載Pod所在宿主機的目錄
      path: string                #Pod所在宿主機的目錄,將被用于同期中mount的目錄
    secret:          #型別為secret的存盤卷,掛載集群與定義的secret物件到容器內部
      scretname: string  
      items:     
      - key: string
        path: string
    configMap:         #型別為configMap的存盤卷,掛載預定義的configMap物件到容器內部
      name: string
      items:
      - key: string
        path: string

可通過命令來查看每種資源的可配置項

#   kubectl explain 資源型別         查看某種資源可以配置的一級屬性
#   kubectl explain 資源型別.屬性     查看屬性的子屬性
[root@k8s-master01 ~]# kubectl explain pod
KIND:     Pod
VERSION:  v1
FIELDS:
   apiVersion   <string>
   kind <string>
   metadata     <Object>
   spec <Object>
   status       <Object>
[root@k8s-master01 ~]# kubectl explain pod.metadata
KIND:     Pod
VERSION:  v1
RESOURCE: metadata <Object>
FIELDS:
   annotations  <map[string]string>
   clusterName  <string>
   creationTimestamp    <string>
   deletionGracePeriodSeconds   <integer>
   deletionTimestamp    <string>
   finalizers   <[]string>
   generateName <string>
   generation   <integer>
   labels       <map[string]string>
   managedFields        <[]Object>
   name <string>
   namespace    <string>
   ownerReferences      <[]Object>
   resourceVersion      <string>
   selfLink     <string>
   uid  <string>

在kubernetes中基本所有資源的一級屬性都是一樣的,主要包含5部分:

  • apiVersion 版本,由kubernetes內部定義,版本號必須可以用 kubectl api-versions 查詢到
  • kind 型別,由kubernetes內部定義,版本號必須可以用 kubectl api-resources 查詢到
  • metadata 元資料,主要是資源標識和說明,常用的有name、namespace、labels等
  • spec 描述,這是配置中最重要的一部分,里面是對各種資源配置的詳細描述
  • status 狀態資訊,里面的內容不需要定義,由kubernetes自動生成

在上面的屬性中,spec是接下來研究的重點,繼續看下它的常見子屬性:

  • containers <[]Object> 容器串列,用于定義容器的詳細資訊
  • nodeName 根據nodeName的值將pod調度到指定的Node節點上
  • nodeSelector <map[]> 根據NodeSelector中定義的資訊選擇將該Pod調度到包含這些label的Node 上
  • hostNetwork 是否使用主機網路模式,默認為false,如果設定為true,表示使用宿主機網路
  • volumes <[]Object> 存盤卷,用于定義Pod上面掛在的存盤資訊
  • restartPolicy 重啟策略,表示Pod在遇到故障的時候的處理策略

基本配置

創建pod-base.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: pod-base
  namespace: dev
  labels:
    user: heima
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  - name: busybox
    image: busybox:1.30

上面定義了一個比較簡單Pod的配置,里面有兩個容器:

  • nginx:
  • busybox:用1.30版本的busybox鏡像創建,(busybox是一個小巧的linux命令集合)
 創建Pod
[root@k8s-master01 pod]# kubectl apply -f pod-base.yaml
pod/pod-base created

# 查看Pod狀況
# READY 1/2 : 表示當前Pod中有2個容器,其中1個準備就緒,1個未就緒
# RESTARTS  : 重啟次數,因為有1個容器故障了,Pod一直在重啟試圖恢復它
[root@k8s-master01 pod]# kubectl get pod -n dev
NAME       READY   STATUS    RESTARTS   AGE
pod-base   1/2     Running   4          95s

# 可以通過describe查看內部的詳情
# 此時已經運行起來了一個基本的Pod,雖然它暫時有問題
[root@k8s-master01 pod]# kubectl describe pod pod-base -n dev

二、鏡像拉取策略

imagePullPolicy,用于設定鏡像拉取策略,kubernetes支持配置三種拉取策略:

  • Always:總是從遠程倉庫拉取鏡像(一直遠程下載)
  • IfNotPresent:本地有則使用本地鏡像,本地沒有則從遠程倉庫拉取鏡像(本地有就本地 本地沒遠程下載)
  • Never:只使用本地鏡像,從不去遠程倉庫拉取,本地沒有就報錯 (一直使用本地)

創建pod-imagepullpolicy.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: pod-imagepullpolicy
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    imagePullPolicy: IfNotPresent # 用于設定鏡像拉取策略

說明:

如果鏡像tag為具體版本號, 默認策略是:IfNotPresent
如果鏡像tag為:latest(最終版本) ,默認策略是always

三、啟動命令 command

command,用于在pod中的容器初始化完畢之后運行一個命令,

在上面的案例中,一直有一個問題沒有解決,就是的busybox容器一直沒有成功運行,那么到底是什么原因導致這個容器的故障呢?

原來busybox并不是一個程式,而是類似于一個工具類的集合,kubernetes集群啟動管理后,它會自動關閉,解決方法就是讓其一直在運行,這就用到了command配置,

創建pod-command.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: pod-command
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]

上面命令的意思:
“/bin/sh”,"-c", 使用sh執行命令
touch /tmp/hello.txt; 創建一個/tmp/hello.txt 檔案
while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向檔案中寫入當前時間

進入pod容器內部執行命令

# kubectl exec  pod名稱 -n 命名空間 -it -c 容器名稱 /bin/sh  在容器內部執行命令
# 使用這個命令就可以進入某個容器的內部,然后進行相關操作了
# 比如,可以查看txt檔案的內容
[root@k8s-master01 pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
14:44:19
14:44:22
14:44:25

特別說明:
通過上面發現command已經可以完成啟動命令和傳遞引數的功能,為什么這里還要提供一個args選項,用于傳遞引數呢?這其實跟docker有點關系,kubernetes中的command、args兩項其實是實作覆寫Dockerfile中ENTRYPOINT的功能,
1 如果command和args均沒有寫,那么用Dockerfile的配置,
2 如果command寫了,但args沒有寫,那么Dockerfile默認的配置會被忽略,執行輸入的command
3 如果command沒寫,但args寫了,那么Dockerfile中配置的ENTRYPOINT的命令會被執行,使用當前args的引數
4 如果command和args都寫了,那么Dockerfile的配置被忽略,執行command并追加上args引數

四、環境變數

創建pod-env.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: pod-env
  namespace: dev
spec:
  containers:
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]
    env: # 設定環境變數串列
    - name: "username"
      value: "admin"
    - name: "password"
      value: "123456"
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-env.yaml
pod/pod-env created

# 進入容器,輸出環境變數
[root@k8s-master01 ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ # echo $password
123456

這種方式不是很推薦,推薦將這些配置單獨存盤在組態檔中,將在后面介紹,

五、埠設定

創建pod-ports.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-ports
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports: # 設定容器暴露的埠串列
    - name: nginx-port
      containerPort: 80
      protocol: TCP
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-ports.yaml
pod/pod-ports created

# 查看pod
# 在下面可以明顯看到配置資訊
[root@k8s-master01 ~]# kubectl get pod pod-ports -n dev -o yaml
......
spec:
  containers:
  - image: nginx:1.17.1
    imagePullPolicy: IfNotPresent
    name: nginx
    ports:
    - containerPort: 80
      name: nginx-port
      protocol: TCP
......

訪問容器中的程式需要使用的是Podip:containerPort

六、資源配額

容器中的程式要運行,肯定是要占用一定資源的,比如cpu和記憶體等,如果不對某個容器的資源做限制,那么它就可能吃掉大量資源,導致其它容器無法運行,針對這種情況,kubernetes提供了對記憶體和cpu的資源進行配額的機制,這種機制主要通過resources選項實作,他有兩個子選項:

  • limits:用于限制運行時容器的最大占用資源,當容器占用資源超過limits時會被終止,并進行重啟
  • requests :用于設定容器需要的最小資源,如果環境資源不夠,容器將無法啟動

創建pod-resources.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-resources
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    resources: # 資源配額
      limits:  # 限制資源(上限)
        cpu: "2" # CPU限制,單位是core數
        memory: "10Gi" # 記憶體限制
      requests: # 請求資源(下限)
        cpu: "1"  # CPU限制,單位是core數
        memory: "10Mi"  # 記憶體限制

cpu和memory的單位說明:

  • cpu:core數,可以為整數或小數
  • memory: 記憶體大小,可以使用Gi、Mi、G、M等形式
# 運行Pod
[root@k8s-master01 ~]# kubectl create  -f pod-resources.yaml
pod/pod-resources created

# 查看發現pod運行正常
[root@k8s-master01 ~]# kubectl get pod pod-resources -n dev
NAME            READY   STATUS    RESTARTS   AGE  
pod-resources   1/1     Running   0          39s   

# 接下來,停止Pod
[root@k8s-master01 ~]# kubectl delete  -f pod-resources.yaml
pod "pod-resources" deleted

# 編輯pod,修改resources.requests.memory的值為10Gi
[root@k8s-master01 ~]# vim pod-resources.yaml

# 再次啟動pod
[root@k8s-master01 ~]# kubectl create  -f pod-resources.yaml
pod/pod-resources created

# 查看Pod狀態,發現Pod啟動失敗
[root@k8s-master01 ~]# kubectl get pod pod-resources -n dev -o wide
NAME            READY   STATUS    RESTARTS   AGE          
pod-resources   0/1     Pending   0          20s    

# 查看pod詳情會發現,如下提示
[root@k8s-master01 ~]# kubectl describe pod pod-resources -n dev
......
Warning  FailedScheduling  35s   default-scheduler  0/3 nodes are available: 1 node(s) had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 Insufficient memory.(記憶體不足)

七、初始化容器

我們一般將pod物件從創建至終的這段時間范圍稱為pod的生命周期,它主要包含下面的程序:

  • pod創建程序
  • 運行初始化容器(init container)程序
  • 運行主容器(main container)
    • 容器啟動后鉤子(post start)、容器終止前鉤子(pre stop)
    • 容器的存活性探測(liveness probe)、就緒性探測(readiness probe)
  • pod終止程序

初始化容器是在pod的主容器啟動之前要運行的容器,主要是做一些主容器的前置作業,它具有兩大特征:

  1. 初始化容器必須運行完成直至結束,若某初始化容器運行失敗,那么kubernetes需要重啟它直到成功完成
  2. 初始化容器必須按照定義的順序執行,當且僅當前一個成功之后,后面的一個才能運行

初始化容器有很多的應用場景,下面列出的是最常見的幾個:

  • 提供主容器鏡像中不具備的工具程式或自定義代碼
  • 初始化容器要先于應用容器串行啟動并運行完成,因此可用于延后應用容器的啟動直至其依賴的條件得到滿足

接下來做一個案例,模擬下面這個需求:

假設要以主容器來運行nginx,但是要求在運行nginx之前先要能夠連接上mysql和redis所在服務器

為了簡化測驗,事先規定好mysql(192.168.5.4)和redis(192.168.5.5)服務器的地址

創建pod-initcontainer.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-initcontainer
  namespace: dev
spec:
  containers:
  - name: main-container
    image: nginx:1.17.1
    ports: 
    - name: nginx-port
      containerPort: 80
  initContainers:
  - name: test-mysql
    image: busybox:1.30
    command: ['sh', '-c', 'until ping 192.168.5.14 -c 1 ; do echo waiting for mysql...; sleep 2; done;']
  - name: test-redis
    image: busybox:1.30
    command: ['sh', '-c', 'until ping 192.168.5.15 -c 1 ; do echo waiting for reids...; sleep 2; done;']
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-initcontainer.yaml
pod/pod-initcontainer created

# 查看pod狀態
# 發現pod卡在啟動第一個初始化容器程序中,后面的容器不會運行
root@k8s-master01 ~]# kubectl describe pod  pod-initcontainer -n dev
........
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  49s   default-scheduler  Successfully assigned dev/pod-initcontainer to node1
  Normal  Pulled     48s   kubelet, node1     Container image "busybox:1.30" already present on machine
  Normal  Created    48s   kubelet, node1     Created container test-mysql
  Normal  Started    48s   kubelet, node1     Started container test-mysql

# 動態查看pod
[root@k8s-master01 ~]# kubectl get pods pod-initcontainer -n dev -w
NAME                             READY   STATUS     RESTARTS   AGE
pod-initcontainer                0/1     Init:0/2   0          15s
pod-initcontainer                0/1     Init:1/2   0          52s
pod-initcontainer                0/1     Init:1/2   0          53s
pod-initcontainer                0/1     PodInitializing   0          89s
pod-initcontainer                1/1     Running           0          90s

# 接下來新開一個shell,為當前服務器新增兩個ip,觀察pod的變化
[root@k8s-master01 ~]# ifconfig ens33:1 192.168.5.14 netmask 255.255.255.0 up
[root@k8s-master01 ~]# ifconfig ens33:2 192.168.5.15 netmask 255.255.255.0 up

八、鉤子函式

鉤子函式能夠感知自身生命周期中的事件,并在相應的時刻到來時運行用戶指定的程式代碼,
kubernetes在主容器的啟動之后和停止之前提供了兩個鉤子函式:

  • post start:容器創建之后執行,如果失敗了會重啟容器
  • pre stop :容器終止之前執行,執行完成之后容器將成功終止,在其完成之前會阻塞洗掉容器的操作

鉤子處理器支持使用下面三種方式定義動作:

  • Exec命令:在容器內執行一次命令

    ……
      lifecycle:
        postStart: 
          exec:
            command:
            - cat
            - /tmp/healthy
    ……
    
  • TCPSocket:在當前容器嘗試訪問指定的socket

    ……      
      lifecycle:
        postStart:
          tcpSocket:
            port: 8080
    ……
    
  • HTTPGet:在當前容器中向某url發起http請求

    ……
      lifecycle:
        postStart:
          httpGet:
            path: / #URI地址
            port: 80 #埠號
            host: 192.168.5.3 #主機地址
            scheme: HTTP #支持的協議,http或者https
    ……
    

創建pod-hook-exec.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: pod-hook-exec
  namespace: dev
spec:
  containers:
  - name: main-container
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    lifecycle:
      postStart: 
        exec: # 在容器啟動的時候執行一個命令,修改掉nginx的默認首頁內容
          command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]
      preStop:
        exec: # 在容器停止之前停止nginx服務
          command: ["/usr/sbin/nginx","-s","quit"]
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-hook-exec.yaml
pod/pod-hook-exec created

# 查看pod
[root@k8s-master01 ~]# kubectl get pods  pod-hook-exec -n dev -o wide
NAME           READY   STATUS     RESTARTS   AGE    IP            NODE    
pod-hook-exec  1/1     Running    0          29s    10.244.2.48   node2   

# 訪問pod
[root@k8s-master01 ~]# curl 10.244.2.48
postStart...

九、容器探測

容器探測用于檢測容器中的應用實體是否正常作業,是保障業務可用性的一種傳統機制,如果經過探測,實體的狀態不符合預期,那么kubernetes就會把該問題實體" 摘除 ",不承擔業務流量,kubernetes提供了兩種探針來實作容器探測,分別是:

  • liveness probes:存活性探針,用于檢測應用實體當前是否處于正常運行狀態,如果不是,k8s會重啟容器
  • readiness probes:就緒性探針,用于檢測應用實體當前是否可以接收請求,如果不能,k8s不會轉發流量

livenessProbe 決定是否重啟容器,readinessProbe 決定是否將請求轉發給容器,

上面兩種探針目前均支持三種探測方式:

  • Exec命令:在容器內執行一次命令,如果命令執行的退出碼為0,則認為程式正常,否則不正常
……
  livenessProbe:
    exec:
      command:
      - cat
      - /tmp/healthy
……
  • TCPSocket:將會嘗試訪問一個用戶容器的埠,如果能夠建立這條連接,則認為程式正常,否則不正常
……      
  livenessProbe:
    tcpSocket:
      port: 8080
……
  • HTTPGet:呼叫容器內Web應用的URL,如果回傳的狀態碼在200和399之間,則認為程式正常,否則不正常
……
  livenessProbe:
    httpGet:
      path: / #URI地址
      port: 80 #埠號
      host: 127.0.0.1 #主機地址
      scheme: HTTP #支持的協議,http或者https
……

Exec

創建pod-liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-exec
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports: 
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      exec:
        command: ["/bin/cat","/tmp/hello.txt"] # 執行一個查看檔案的命令
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created

# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-exec -n dev
......
  Normal   Created    20s (x2 over 50s)  kubelet, node1     Created container nginx
  Normal   Started    20s (x2 over 50s)  kubelet, node1     Started container nginx
  Normal   Killing    20s                kubelet, node1     Container nginx failed liveness probe, will be restarted
  Warning  Unhealthy  0s (x5 over 40s)   kubelet, node1     Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory
  
# 觀察上面的資訊就會發現nginx容器啟動之后就進行了健康檢查
# 檢查失敗之后,容器被kill掉,然后嘗試進行重啟(這是重啟策略的作用,后面講解)
# 稍等一會之后,再觀察pod資訊,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pods pod-liveness-exec -n dev
NAME                READY   STATUS             RESTARTS   AGE
pod-liveness-exec   0/1     CrashLoopBackOff   2          3m19s

# 當然接下來,可以修改成一個存在的檔案,比如/tmp/hello.txt,再試,結果就正常了......

TCPSocket

創建pod-liveness-tcpsocket.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-tcpsocket
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports: 
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      tcpSocket:
        port: 8080 # 嘗試訪問8080埠
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created

# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-tcpsocket -n dev
......
  Normal   Scheduled  31s                            default-scheduler  Successfully assigned dev/pod-liveness-tcpsocket to node2
  Normal   Pulled     <invalid>                      kubelet, node2     Container image "nginx:1.17.1" already present on machine
  Normal   Created    <invalid>                      kubelet, node2     Created container nginx
  Normal   Started    <invalid>                      kubelet, node2     Started container nginx
  Warning  Unhealthy  <invalid> (x2 over <invalid>)  kubelet, node2     Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused
  
# 觀察上面的資訊,發現嘗試訪問8080埠,但是失敗了
# 稍等一會之后,再觀察pod資訊,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pods pod-liveness-tcpsocket  -n dev
NAME                     READY   STATUS             RESTARTS   AGE
pod-liveness-tcpsocket   0/1     CrashLoopBackOff   2          3m19s

# 當然接下來,可以修改成一個可以訪問的埠,比如80,再試,結果就正常了......

HTTPGet

創建pod-liveness-httpget.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-httpget
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      httpGet:  # 其實就是訪問http://127.0.0.1:80/hello  
        scheme: HTTP #支持的協議,http或者https
        port: 80 #埠號
        path: /hello #URI地址
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created

# 查看Pod詳情
[root@k8s-master01 ~]# kubectl describe pod pod-liveness-httpget -n dev
.......
  Normal   Pulled     6s (x3 over 64s)  kubelet, node1     Container image "nginx:1.17.1" already present on machine
  Normal   Created    6s (x3 over 64s)  kubelet, node1     Created container nginx
  Normal   Started    6s (x3 over 63s)  kubelet, node1     Started container nginx
  Warning  Unhealthy  6s (x6 over 56s)  kubelet, node1     Liveness probe failed: HTTP probe failed with statuscode: 404
  Normal   Killing    6s (x2 over 36s)  kubelet, node1     Container nginx failed liveness probe, will be restarted
  
# 觀察上面資訊,嘗試訪問路徑,但是未找到,出現404錯誤
# 稍等一會之后,再觀察pod資訊,就可以看到RESTARTS不再是0,而是一直增長
[root@k8s-master01 ~]# kubectl get pod pod-liveness-httpget -n dev
NAME                   READY   STATUS    RESTARTS   AGE
pod-liveness-httpget   1/1     Running   5          3m17s

# 當然接下來,可以修改成一個可以訪問的路徑path,比如/,再試,結果就正常了......

livenessProbe下可選擇的引數

[root@k8s-master01 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:
   exec <Object>  
   tcpSocket    <Object>
   httpGet      <Object>
   initialDelaySeconds  <integer>  # 容器啟動后等待多少秒執行第一次探測
   timeoutSeconds       <integer>  # 探測超時時間,默認1秒,最小1秒
   periodSeconds        <integer>  # 執行探測的頻率,默認是10秒,最小1秒
   failureThreshold     <integer>  # 連續探測失敗多少次才被認定為失敗,默認是3,最小值是1
   successThreshold     <integer>  # 連續探測成功多少次才被認定為成功,默認是1
[root@k8s-master01 ~]# more pod-liveness-httpget.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-httpget
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      httpGet:
        scheme: HTTP
        port: 80 
        path: /
      initialDelaySeconds: 30 # 容器啟動后30s開始探測
      timeoutSeconds: 5 # 探測超時時間為5s

十、重啟策略

在上一節中,一旦容器探測出現了問題,kubernetes就會對容器所在的Pod進行重啟,其實這是由pod的重啟策略決定的,pod的重啟策略有 3 種,分別如下:

  • Always :容器失效時,自動重啟該容器,這也是默認值,
  • OnFailure : 容器終止運行且退出碼不為0時重啟
  • Never : 不論狀態為何,都不重啟該容器

重啟策略適用于pod物件中的所有容器,首次需要重啟的容器,將在其需要時立即進行重啟,隨后再次需要重啟的操作將由kubelet延遲一段時間后進行,且反復的重啟操作的延遲時長以此為10s、20s、40s、80s、160s和300s,300s是最大延遲時長,

創建pod-restartpolicy.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-restartpolicy
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    ports:
    - name: nginx-port
      containerPort: 80
    livenessProbe:
      httpGet:
        scheme: HTTP
        port: 80
        path: /hello
  restartPolicy: Never # 設定重啟策略為Never
# 創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-restartpolicy.yaml
pod/pod-restartpolicy created

# 查看Pod詳情,發現nginx容器失敗
[root@k8s-master01 ~]# kubectl  describe pods pod-restartpolicy  -n dev
......
  Warning  Unhealthy  15s (x3 over 35s)  kubelet, node1     Liveness probe failed: HTTP probe failed with statuscode: 404
  Normal   Killing    15s                kubelet, node1     Container nginx failed liveness probe
  
# 多等一會,再觀察pod的重啟次數,發現一直是0,并未重啟   
[root@k8s-master01 ~]# kubectl  get pods pod-restartpolicy -n dev
NAME                   READY   STATUS    RESTARTS   AGE
pod-restartpolicy      0/1     Running   0          5min42s

十一、Pod調度

在默認情況下,一個Pod在哪個Node節點上運行,是由Scheduler組件采用相應的演算法計算出來的,這個程序是不受人工控制的,但是在實際使用中,這并不滿足的需求,因為很多情況下,我們想控制某些Pod到達某些節點上,那么應該怎么做呢?這就要求了解kubernetes對Pod的調度規則,kubernetes提供了四大類調度方式:

  • 自動調度:運行在哪個節點上完全由Scheduler經過一系列的演算法計算得出
  • 定向調度:NodeName、NodeSelector
  • 親和性調度:NodeAffinity、PodAffinity、PodAntiAffinity
  • 污點(容忍)調度:Taints、Toleration

定向調度

定向調度,指的是利用在pod上宣告nodeName或者nodeSelector,以此將Pod調度到期望的node節點上,注意,這里的調度是強制的,這就意味著即使要調度的目標Node不存在,也會向上面進行調度,只不過pod運行失敗而已,

NodeName
NodeName用于強制約束將Pod調度到指定的Name的Node節點上,這種方式,其實是直接跳過Scheduler的調度邏輯,直接將Pod調度到指定名稱的節點,
創建一個pod-nodename.yaml檔案

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeName: node1 # 指定調度到node1節點上
#創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created

#查看Pod調度到NODE屬性,確實是調度到了node1節點上
[root@k8s-master01 ~]# kubectl get pods pod-nodename -n dev -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP            NODE      ......
pod-nodename   1/1     Running   0          56s   10.244.1.87   node1     ......   

# 接下來,洗掉pod,修改nodeName的值為node3(并沒有node3節點)
[root@k8s-master01 ~]# kubectl delete -f pod-nodename.yaml
pod "pod-nodename" deleted
[root@k8s-master01 ~]# vim pod-nodename.yaml
[root@k8s-master01 ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created

#再次查看,發現已經向Node3節點調度,但是由于不存在node3節點,所以pod無法正常運行
[root@k8s-master01 ~]# kubectl get pods pod-nodename -n dev -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP       NODE    ......
pod-nodename   0/1     Pending   0          6s    <none>   node3   ......    

NodeSelector
NodeSelector用于將pod調度到添加了指定標簽的node節點上,它是通過kubernetes的label-selector機制實作的,也就是說,在pod創建之前,會由scheduler使用MatchNodeSelector調度策略進行label匹配,找出目標node,然后將pod調度到目標節點,該匹配規則是強制約束,

1 首先分別為node節點添加標簽

[root@k8s-master01 ~]# kubectl label nodes node1 nodeenv=pro
node/node2 labeled
[root@k8s-master01 ~]# kubectl label nodes node2 nodeenv=test
node/node2 labeled

2 創建一個pod-nodeselector.yaml檔案,并使用它創建Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeSelector: 
    nodeenv: pro # 指定調度到具有nodeenv=pro標簽的節點上
#創建Pod
[root@k8s-master01 ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created

#查看Pod調度到NODE屬性,確實是調度到了node1節點上
[root@k8s-master01 ~]# kubectl get pods pod-nodeselector -n dev -o wide
NAME               READY   STATUS    RESTARTS   AGE     IP          NODE    ......
pod-nodeselector   1/1     Running   0          47s   10.244.1.87   node1   ......

# 接下來,洗掉pod,修改nodeSelector的值為nodeenv: xxxx(不存在打有此標簽的節點)
[root@k8s-master01 ~]# kubectl delete -f pod-nodeselector.yaml
pod "pod-nodeselector" deleted
[root@k8s-master01 ~]# vim pod-nodeselector.yaml
[root@k8s-master01 ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created

#再次查看,發現pod無法正常運行,Node的值為none
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME               READY   STATUS    RESTARTS   AGE     IP       NODE    
pod-nodeselector   0/1     Pending   0          2m20s   <none>   <none>

# 查看詳情,發現node selector匹配失敗的提示
[root@k8s-master01 ~]# kubectl describe pods pod-nodeselector -n dev
.......
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.

十二、親和性調度

上面介紹了兩種定向調度的方式,使用起來非常方便,但是也有一定的問題,那就是如果沒有滿足條件的Node,那么Pod將不會被運行,即使在集群中還有可用Node串列也不行,這就限制了它的使用場景,

基于上面的問題,kubernetes還提供了一種親和性調度(Affinity),它在NodeSelector的基礎之上的進行了擴展,可以通過配置的形式,實作優先選擇滿足條件的Node進行調度,如果沒有,也可以調度到不滿足條件的節點上,使調度更加靈活,

Affinity主要分為三類:

  • nodeAffinity(node親和性): 以node為目標,解決pod可以調度到哪些node的問題
  • podAffinity(pod親和性) : 以pod為目標,解決pod可以和哪些已存在的pod部署在同一個拓撲域中的問題
  • podAntiAffinity(pod反親和性) : 以pod為目標,解決pod不能和哪些已存在pod部署在同一個拓撲域中的問題

關于親和性(反親和性)使用場景的說明:

親和性:如果兩個應用頻繁互動,那就有必要利用親和性讓兩個應用的盡可能的靠近,這樣可以減少因網路通信而帶來的性能損耗,

反親和性:當應用的采用多副本部署時,有必要采用反親和性讓各個應用實體打散分布在各個node上,這樣可以提高服務的高可用性,

NodeAffinity

先來看一下NodeAffinity的可配置項:

pod.spec.affinity.nodeAffinity
  requiredDuringSchedulingIgnoredDuringExecution  Node節點必須滿足指定的所有規則才可以,相當于硬限制
    nodeSelectorTerms  節點選擇串列
      matchFields   按節點欄位列出的節點選擇器要求串列
      matchExpressions   按節點標簽列出的節點選擇器要求串列(推薦)
        key    鍵
        values 值
        operator 關系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
  preferredDuringSchedulingIgnoredDuringExecution 優先調度到滿足指定的規則的Node,相當于軟限制 (傾向)
    preference   一個節點選擇器項,與相應的權重相關聯
      matchFields   按節點欄位列出的節點選擇器要求串列
      matchExpressions   按節點標簽列出的節點選擇器要求串列(推薦)
        key    鍵
        values 值
        operator 關系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
	weight 傾向權重,在范圍1-100,
關系符的使用說明:

- matchExpressions:
  - key: nodeenv              # 匹配存在標簽的key為nodeenv的節點
    operator: Exists
  - key: nodeenv              # 匹配標簽的key為nodeenv,且value是"xxx"或"yyy"的節點
    operator: In
    values: ["xxx","yyy"]
  - key: nodeenv              # 匹配標簽的key為nodeenv,且value大于"xxx"的節點
    operator: Gt
    values: "xxx"

接下來首先演示一下requiredDuringSchedulingIgnoredDuringExecution ,

創建pod-nodeaffinity-required.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:  #親和性設定
    nodeAffinity: #設定node親和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
        nodeSelectorTerms:
        - matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽
          - key: nodeenv
            operator: In
            values: ["xxx","yyy"]
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created

# 查看pod狀態 (運行失敗)
[root@k8s-master01 ~]# kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP       NODE    ...... 
pod-nodeaffinity-required   0/1     Pending   0          16s   <none>   <none>  ......

# 查看Pod的詳情
# 發現調度失敗,提示node選擇失敗
[root@k8s-master01 ~]# kubectl describe pod pod-nodeaffinity-required -n dev
......
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 node(s) didn't match node selector.

#接下來,停止pod
[root@k8s-master01 ~]# kubectl delete -f pod-nodeaffinity-required.yaml
pod "pod-nodeaffinity-required" deleted

# 修改檔案,將values: ["xxx","yyy"]------> ["pro","yyy"]
[root@k8s-master01 ~]# vim pod-nodeaffinity-required.yaml

# 再次啟動
[root@k8s-master01 ~]# kubectl create -f pod-nodeaffinity-required.yaml
pod/pod-nodeaffinity-required created

# 此時查看,發現調度成功,已經將pod調度到了node1上
[root@k8s-master01 ~]# kubectl get pods pod-nodeaffinity-required -n dev -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE  ...... 
pod-nodeaffinity-required   1/1     Running   0          11s   10.244.1.89   node1 ......

接下來再演示一下requiredDuringSchedulingIgnoredDuringExecution ,

創建pod-nodeaffinity-preferred.yaml

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeaffinity-preferred
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:  #親和性設定
    nodeAffinity: #設定node親和性
      preferredDuringSchedulingIgnoredDuringExecution: # 軟限制
      - weight: 1
        preference:
          matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽(當前環境沒有)
          - key: nodeenv
            operator: In
            values: ["xxx","yyy"]
# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-nodeaffinity-preferred.yaml
pod/pod-nodeaffinity-preferred created

# 查看pod狀態 (運行成功)
[root@k8s-master01 ~]# kubectl get pod pod-nodeaffinity-preferred -n dev
NAME                         READY   STATUS    RESTARTS   AGE
pod-nodeaffinity-preferred   1/1     Running   0          40s
NodeAffinity規則設定的注意事項:
    1 如果同時定義了nodeSelector和nodeAffinity,那么必須兩個條件都得到滿足,Pod才能運行在指定的Node上
    2 如果nodeAffinity指定了多個nodeSelectorTerms,那么只需要其中一個能夠匹配成功即可
    3 如果一個nodeSelectorTerms中有多個matchExpressions ,則一個節點必須滿足所有的才能匹配成功
    4 如果一個pod所在的Node在Pod運行期間其標簽發生了改變,不再符合該Pod的節點親和性需求,則系統將忽略此變化

PodAffinit

PodAffinity主要實作以運行的Pod為參照,實作讓新創建的Pod跟參照pod在一個區域的功能,
首先來看一下PodAffinity的可配置項:

pod.spec.affinity.podAffinity
  requiredDuringSchedulingIgnoredDuringExecution  硬限制
    namespaces       指定參照pod的namespace
    topologyKey      指定調度作用域
    labelSelector    標簽選擇器
      matchExpressions  按節點標簽列出的節點選擇器要求串列(推薦)
        key    鍵
        values 值
        operator 關系符 支持In, NotIn, Exists, DoesNotExist.
      matchLabels    指多個matchExpressions映射的內容
  preferredDuringSchedulingIgnoredDuringExecution 軟限制
    podAffinityTerm  選項
      namespaces      
      topologyKey
      labelSelector
        matchExpressions  
          key    鍵
          values 值
          operator
        matchLabels 
    weight 傾向權重,在范圍1-100
topologyKey用于指定調度時作用域,例如:
    如果指定為kubernetes.io/hostname,那就是以Node節點為區分范圍
	如果指定為beta.kubernetes.io/os,則以Node節點的作業系統型別來區分

接下來,演示下requiredDuringSchedulingIgnoredDuringExecution,

1)首先創建一個參照Pod,pod-podaffinity-target.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-target
  namespace: dev
  labels:
    podenv: pro #設定標簽
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  nodeName: node1 # 將目標pod名確指定到node1上
# 啟動目標pod
[root@k8s-master01 ~]# kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created

# 查看pod狀況
[root@k8s-master01 ~]# kubectl get pods  pod-podaffinity-target -n dev
NAME                     READY   STATUS    RESTARTS   AGE
pod-podaffinity-target   1/1     Running   0          4s

2)創建pod-podaffinity-required.yaml,內容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-podaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:  #親和性設定
    podAffinity: #設定pod親和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
      - labelSelector:
          matchExpressions: # 匹配env的值在["xxx","yyy"]中的標簽
          - key: podenv
            operator: In
            values: ["xxx","yyy"]
        topologyKey: kubernetes.io/hostname

上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上,顯然現在沒有這樣pod,接下來,運行測驗一下,

# 啟動pod
[root@k8s-master01 ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created

# 查看pod狀態,發現未運行
[root@k8s-master01 ~]# kubectl get pods pod-podaffinity-required -n dev
NAME                       READY   STATUS    RESTARTS   AGE
pod-podaffinity-required   0/1     Pending   0          9s

# 查看詳細資訊
[root@k8s-master01 ~]# kubectl describe pods pod-podaffinity-required  -n dev
......
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 2 node(s) didn't match pod affinity rules, 1 node(s) had taints that the pod didn't tolerate.

# 接下來修改  values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必須要與擁有標簽nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
[root@k8s-master01 ~]# vim pod-podaffinity-required.yaml

# 然后重新創建pod,查看效果
[root@k8s-master01 ~]# kubectl delete -f  pod-podaffinity-required.yaml
pod "pod-podaffinity-required" deleted
[root@k8s-master01 ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created

# 發現此時Pod運行正常
[root@k8s-master01 ~]# kubectl get pods pod-podaffinity-required -n dev
NAME                       READY   STATUS    RESTARTS   AGE   LABELS
pod-podaffinity-required   1/1     Running   0          6s    <none>

關于PodAffinitypreferredDuringSchedulingIgnoredDuringExecution,這里不再演示,

PodAntiAffinity

PodAntiAffinity主要實作以運行的Pod為參照,讓新創建的Pod跟參照pod不在一個區域中的功能,

它的配置方式和選項跟PodAffinty是一樣的,這里不再做詳細解釋,直接做一個測驗案例,

1)繼續使用上個案例中目標pod

[root@k8s-master01 ~]# kubectl get pods -n dev -o wide --show-labels
NAME                     READY   STATUS    RESTARTS   AGE     IP            NODE    LABELS
pod-podaffinity-required 1/1     Running   0          3m29s   10.244.1.38   node1   <none>     
pod-podaffinity-target   1/1     Running   0          9m25s   10.244.1.37   node1   podenv=pro

2)創建pod-podantiaffinity-required.yaml,內容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-podantiaffinity-required
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  affinity:  #親和性設定
    podAntiAffinity: #設定pod親和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
      - labelSelector:
          matchExpressions: # 匹配podenv的值在["pro"]中的標簽
          - key: podenv
            operator: In
            values: ["pro"]
        topologyKey: kubernetes.io/hostname

上面配置表達的意思是:新Pod必須要與擁有標簽nodeenv=pro的pod不在同一Node上,運行測驗一下,

# 創建pod
[root@k8s-master01 ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created

# 查看pod
# 發現調度到了node2上
[root@k8s-master01 ~]# kubectl get pods pod-podantiaffinity-required -n dev -o wide
NAME                           READY   STATUS    RESTARTS   AGE   IP            NODE   .. 
pod-podantiaffinity-required   1/1     Running   0          30s   10.244.1.96   node2  ..

十三、污點和容忍

污點(Taints)

前面的調度方式都是站在Pod的角度上,通過在Pod上添加屬性,來確定Pod是否要調度到指定的Node上,其實我們也可以站在Node的角度上,通過在Node上添加污點屬性,來決定是否允許Pod調度過來,

Node被設定上污點之后就和Pod之間存在了一種相斥的關系,進而拒絕Pod調度進來,甚至可以將已經存在的Pod驅逐出去,

污點的格式為:key=value:effect, key和value是污點的標簽,effect描述污點的作用,支持如下三個選項:

  • PreferNoSchedule:kubernetes將盡量避免把Pod調度到具有該污點的Node上,除非沒有其他節點可調度
  • NoSchedule:kubernetes將不會把Pod調度到具有該污點的Node上,但不會影響當前Node上已存在的Pod
  • NoExecute:kubernetes將不會把Pod調度到具有該污點的Node上,同時也會將Node上已存在的Pod驅離

在這里插入圖片描述
使用kubectl設定和去除污點的命令示例如下:

# 設定污點
kubectl taint nodes node1 key=value:effect

# 去除污點
kubectl taint nodes node1 key:effect-

# 去除所有污點
kubectl taint nodes node1 key-

接下來,演示下污點的效果:

  1. 準備節點node1(為了演示效果更加明顯,暫時停止node2節點)
  2. 為node1節點設定一個污點: tag=heima:PreferNoSchedule;然后創建pod1( pod1 可以 )
  3. 修改為node1節點設定一個污點: tag=heima:NoSchedule;然后創建pod2( pod1 正常 pod2 失敗 )
  4. 修改為node1節點設定一個污點: tag=heima:NoExecute;然后創建pod3 ( 3個pod都失敗 )
# 為node1設定污點(PreferNoSchedule)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:PreferNoSchedule

# 創建pod1
[root@k8s-master01 ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP           NODE   
taint1-7665f7fd85-574h4   1/1     Running   0          2m24s   10.244.1.59   node1    

# 為node1設定污點(取消PreferNoSchedule,設定NoSchedule)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag:PreferNoSchedule-
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:NoSchedule

# 創建pod2
[root@k8s-master01 ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods taint2 -n dev -o wide
NAME                      READY   STATUS    RESTARTS   AGE     IP            NODE
taint1-7665f7fd85-574h4   1/1     Running   0          2m24s   10.244.1.59   node1 
taint2-544694789-6zmlf    0/1     Pending   0          21s     <none>        <none>   

# 為node1設定污點(取消NoSchedule,設定NoExecute)
[root@k8s-master01 ~]# kubectl taint nodes node1 tag:NoSchedule-
[root@k8s-master01 ~]# kubectl taint nodes node1 tag=heima:NoExecute

# 創建pod3
[root@k8s-master01 ~]# kubectl run taint3 --image=nginx:1.17.1 -n dev
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME                      READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED 
taint1-7665f7fd85-htkmp   0/1     Pending   0          35s   <none>   <none>   <none>    
taint2-544694789-bn7wb    0/1     Pending   0          35s   <none>   <none>   <none>     
taint3-6d78dbd749-tktkq   0/1     Pending   0          6s    <none>   <none>   <none>     

小提示:
使用kubeadm搭建的集群,默認就會給master節點添加一個污點標記,所以pod就不會調度到master節點上.

容忍(Toleration)

上面介紹了污點的作用,我們可以在node上添加污點用于拒絕pod調度上來,但是如果就是想將一個pod調度到一個有污點的node上去,這時候應該怎么做呢?這就要使用到容忍
在這里插入圖片描述

污點就是拒絕,容忍就是忽略,Node通過污點拒絕pod調度上去,Pod通過容忍忽略拒絕

面先通過一個案例看下效果:

  1. 上一小節,已經在node1節點上打上了NoExecute的污點,此時pod是調度不上去的
  2. 本小節,可以通過給pod添加容忍,然后將其調度上去

創建pod-toleration.yaml,內容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
  tolerations:      # 添加容忍
  - key: "tag"        # 要容忍的污點的key
    operator: "Equal" # 運算子
    value: "heima"    # 容忍的污點的value
    effect: "NoExecute"   # 添加容忍的規則,這里必須和標記的污點規則相同
# 添加容忍之前的pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED 
pod-toleration   0/1     Pending   0          3s    <none>   <none>   <none>           

# 添加容忍之后的pod
[root@k8s-master01 ~]# kubectl get pods -n dev -o wide
NAME             READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED
pod-toleration   1/1     Running   0          3s    10.244.1.62   node1   <none>        

下面是容忍的詳細配置:

[root@k8s-master01 ~]# kubectl explain pod.spec.tolerations
......
FIELDS:
   key       # 對應著要容忍的污點的鍵,空意味著匹配所有的鍵
   value     # 對應著要容忍的污點的值
   operator  # key-value的運算子,支持Equal和Exists(默認)
   effect    # 對應污點的effect,空意味著匹配所有影響
   tolerationSeconds   # 容忍時間, 當effect為NoExecute時生效,表示pod在Node上的停留時間

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/294672.html

標籤:其他

上一篇:凝思linux上keepalived+nginx實作高可用和負載均衡Tcp服務自動主從機切換

下一篇:OpenResty介紹

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more