Kubernetes初露鋒芒(Pod詳解)

一、Pod的介紹

1.一、Pod的結構

每一個Pod中都包含一個或多個容器,這些容器能夠分爲兩類:html

  • 用戶程序所在的容器,數量可多可少。
  • Pause容器,這是每一個Pod都會有一個根容器,它的做用有兩個:
    • 能夠以它爲依據,評估整個Pod的健康情況。
    • 能夠在根容器上設置IP地址,其餘容器共享次IP(Pod的IP),以實現Pod內部的網絡通訊(這裏是Pod內部的通信,Pod之間的通信採用虛擬二層網絡技術來實現,咱們當前環境使用的是Flannel)。

1.二、Pod定義

Pod的資源清單:node

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
  • 查看每種資源的可配置項mysql

    # 查看某種資源能夠配置的一級配置
    kubectl explain 資源類型
    
    # 查看pod的一級配置
    kubectl explain pod
    
    KIND:     Pod
    VERSION:  v1
    
    DESCRIPTION:
         Pod is a collection of containers that can run on a host. This resource is
         created by clients and scheduled onto hosts.
    
    FIELDS:
       apiVersion	<string>
         APIVersion defines the versioned schema of this representation of an
         object. Servers should convert recognized schemas to the latest internal
         value, and may reject unrecognized values. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
    
       kind	<string>
         Kind is a string value representing the REST resource this object
         represents. Servers may infer this from the endpoint the client submits
         requests to. Cannot be updated. In CamelCase. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
    
       metadata	<Object>
         Standard object's metadata. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
    
       spec	<Object>
         Specification of the desired behavior of the pod. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
    
       status	<Object>
         Most recently observed status of the pod. This data may not be up to date.
         Populated by the system. Read-only. More info:
         https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
  • 查看資源配置項的子配置項linux

    kubectl explain 資源類型.屬性
    
    # 查看pod的metadata
    kubectl explain pod.metadata

說明nginx

在Kubernetes中基本全部資源的一級屬性都是同樣的 ,主要包含五個部分:git

  • apiVersion : 版本,由Kubernetes內部定義,版本號必須用kubectl api-versions查詢。
  • kind : 類型,由Kubernetes內部定義,類型必須用kubectl api-resources查詢。
  • metadata : 元數據,主要是資源標示和說明,經常使用的有name,namespace,labels等。
  • spec :描述,這是配置中最重要的一部分,裏面是對各類資源配置的詳細描述。
  • status :狀態信息,裏面的內容不須要定義,由Kubernetes自動生成。

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

  • containers <[]Object>:容器列表,用於定義容器的詳細信息。redis

  • nodeName :根據nodeName的值將Pod調度到指定的Node節點上。 算法

  • nodeSelector <map[]> :根據NodeSelector中定義的信息選擇該Pod調度到包含這些Label的Node上。sql

  • hostNetwork :是否使用主機網絡模式,默認爲false,若是設置爲true,表示使用宿主機網絡。

  • volumes <[]Object> :存儲卷,用於定義Pod上面掛載的存儲信息。

    restartPolicy :重啓策略,表示Pod在遇到故障的時候的處理策略。

二、Pod的配置

本小節主要來研究pod.spec.containers屬性,這也是Pod配置中最爲關鍵的一項配置。

# 查看pod.spec.containers的可選配置項
kubectl explain pod.spec.containers

# 返回的重要屬性
KIND:     Pod
VERSION:  v1
RESOURCE: containers <[]Object>   # 數組,表明能夠有多個容器FIELDS:
  name  <string>     # 容器名稱
  image <string>     # 容器須要的鏡像地址
  imagePullPolicy  <string> # 鏡像拉取策略 
  command  <[]string> # 容器的啓動命令列表,如不指定,使用打包時使用的啓動命令
  args   <[]string> # 容器的啓動命令須要的參數列表 
  env    <[]Object> # 容器環境變量的配置
  ports  <[]Object>  # 容器須要暴露的端口號列表
  resources <Object> # 資源限制和資源請求的設置

2.一、基本配置

  • 建立pod_base.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-base  # 此處不能使用_
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
        - name: busybox # 容器名稱
          image: busybox:1.30  # 容器須要的鏡像地址

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

    • nginx:用的是1.17.1版本的nginx鏡像建立(nginx是一個輕量級的web容器)。

    • busybox:用的是1.30版本的busybox鏡像建立(busybox是一個小巧的linux命令集合)。

  • 建立Pod以及查看pod狀態:

    # 建立pod
    kubectl apply -f pod_base.yaml
    
    # 查看Pod
    kubectl get pods pod-base -n dev
    NAME       READY   STATUS             RESTARTS   AGE
    pod-base   1/2     ImagePullBackOff   1          2m2s
    
    # 咱們發現,當前Pod中有兩個容器,可是準備就緒只有一個。
    # 再次查看,發現重試了3次
    kubectl get pods pod-base -n dev
    NAME       READY   STATUS             RESTARTS   AGE
    pod-base   1/2     CrashLoopBackOff   3          3m2s
    
    # 查看內部詳情
    kubectl describe pods pod-base -n dev
    
    # 咱們只關心Events部分,
    Events:
      Type     Reason     Age                From               Message
      ----     ------     ----               ----               -------
      Normal   Scheduled  <unknown>          default-scheduler  Successfully assigned dev/pod-base to node2
      Normal   Pulled     75s                kubelet, node2     Container image "nginx:1.17.1" already present on machine
      Normal   Created    73s                kubelet, node2     Created container nginx
      Normal   Started    73s                kubelet, node2     Started container nginx
      Normal   Pulling    73s                kubelet, node2     Pulling image "busybox:1.30"
      Normal   Pulled     49s                kubelet, node2     Successfully pulled image "busybox:1.30"
      Normal   Pulled     25s (x2 over 47s)  kubelet, node2     Container image "busybox:1.30" already present on machine
      Normal   Created    24s (x3 over 49s)  kubelet, node2     Created container busybox
      Normal   Started    24s (x3 over 48s)  kubelet, node2     Started container busybox
      Warning  BackOff    11s (x4 over 41s)  kubelet, node2     Back-off restarting failed container
    # 發現啓動busybox失敗了。

2.二、鏡像拉取策略

imagePullPolicy用於設置鏡像拉取的策略,Kubernetes支持配置三種拉取策略:

​ 一、Always: 老是從遠程倉庫拉取鏡像(一直遠程下載)

二、IfNotPresent: 本地有則使用本地鏡像,本地沒有則從遠程倉庫拉取鏡像(本地有就用本地,本地沒有就使用遠程下載)。

三、Never:只使用本地鏡像,從不去遠程倉庫拉取,本地沒有就報錯(一直使用本地,沒有就報錯)

默認值說明:

若是鏡像tag爲具體版本號,默認策略是IfNotPresent;

若是鏡像tag爲latest,默認策略是Always。

  • 建立pod_imagepullpolicy.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-imagepullpolicy
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: Always  # 設置鏡像的拉取策略
        - name: busybox # 容器名稱
          image: busybox:1.30  # 容器須要的鏡像地址

2.三、啓動命令

在前面的案例中,一直有一個問題沒有解決,就是busybox容器一直沒有運行成功,那麼究竟是什麼緣由致使這個容器的故障呢?

原來busybox並非一個程序,而是相似於一個工具類的集合,Kubernetes集羣啓動管理後,它會自動關閉,解決方法就是讓其一直在運行,這就用到了command的配置。

  • 建立pod_command.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-command
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: Always  # 設置鏡像的拉取策略
        - 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;]
          
          
    # 補充說明:
    # command:用於在Pod中的容器初始化完畢以後執行一個命令
    # "/bin/sh","-c":使用sh執行命令
    # touch /tmp/hello.txt:建立一個/tmp/hello.txt的文件
    # while ture;do /bin/echo $(date +%T) >> /tmp/hello.txt:sleep 3;done:每隔三秒,向文件寫入當前時間
  • 進入Pod中的容器

    kubectl exec -it pod名稱 -n 命名空間 -c 容器名稱 /bin/sh
    
    kubectl exec -it pod-command -n dev -c busybox /bin/sh
    
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
    / # tail -f tmp/hello.txt
    10:02:15
    10:02:18
    10:02:21
    10:02:24
    10:02:27
    10:02:30
    10:02:33
    10:02:36
    ...

特別說明

​ 經過上面發現command已經能夠完成啓動命令和傳遞參數的功能,爲何還要提供一個args選項,用於傳遞參數?其實和Docker有點關係,Kubernetes中的command和args兩個參數實際上是爲了實現覆蓋Dockerfile中的ENTRYPOINT的功能:

1 、若是command和args均沒有寫,那麼用Dockerfile的配置。

2 、若是command寫了,可是args沒有寫,那麼Dockerfile默認的配置會被忽略,執行注入的command。

​ 三、若是command沒有寫,可是args寫了,那麼Dockerfile中配置的ENTRYPOINT命令會被執行,使用當前args的參數。

​ 四、若是command和args都寫了,那麼Dockerfile中的配置會被忽略,執行command並追加上args參數。

2.四、環境變量(不推薦)

env:環境變量,用於在Pod中的容器設置環境變量。

  • 建立pod_env.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-command
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: Always  # 設置鏡像的拉取策略
        - 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;"]
          env:
            - name: "username"
              value: "admin"
            - name: "password"
              value: "123456"
  • 進入容器,輸出環境變量

    kubectl exec -it pod-env -n dev -c busybox -it /bin/sh
    
    kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
    / # echo $username
    admin
    / # echo $password
    123456

2.五、端口設置

  • 查看posts支持的子選項

    kubectl explain pod.spec.containers.ports
    
    KIND:     Pod
    VERSION:  v1
    RESOURCE: ports <[]Object>
    FIELDS:
      name <string> # 端口名稱,若是指定,必須保證name在pod中是惟一的
      containerPort <integer> # 容器要監聽的端口(0<x<65536)
      hostPort <integer> # 容器要在主機上公開的端口,若是設置,主機上只能運行容器的一個副本(通常省略)
      hostIP <string>  # 要將外部端口綁定到的主機IP(通常省略)
      protocol <string>  # 端口協議。必須是UDP、TCP或SCTP。默認爲「TCP」
    
    #----------------------------------------------------------------
    KIND:     Pod
    VERSION:  v1
    
    RESOURCE: ports <[]Object>
    
    DESCRIPTION:
         List of ports to expose from the container. Exposing a port here gives the
         system additional information about the network connections a container
         uses, but is primarily informational. Not specifying a port here DOES NOT
         prevent that port from being exposed. Any port which is listening on the
         default "0.0.0.0" address inside a container will be accessible from the
         network. Cannot be updated.
    
         ContainerPort represents a network port in a single container.
    
    FIELDS:
       containerPort	<integer> -required-
         Number of port to expose on the pod's IP address. This must be a valid port
         number, 0 < x < 65536.
    
       hostIP	<string>
         What host IP to bind the external port to.
    
       hostPort	<integer>
         Number of port to expose on the host. If specified, this must be a valid
         port number, 0 < x < 65536. If HostNetwork is specified, this must match
         ContainerPort. Most containers do not need this.
    
       name	<string>
         If specified, this must be an IANA_SVC_NAME and unique within the pod. Each
         named port in a pod must have a unique name. Name for the port that can be
         referred to by services.
    
       protocol	<string>
         Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP".
  • 建立pod_ports.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-ports
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports: 
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議

    訪問Pod中的容器用PodIP:ContainerPort

2.六、資源配額

容器中的程序要運行,確定會佔用必定的資源,好比CPU和內存等,若是不對某個容器的資源作限制,那麼它就可能吃掉大量的資源,致使其餘的容器沒法運行。針對這種狀況,Kubernetes提供了對內存和CPU的資源進行配額的機制,這種機制主要經過resources選項實現,它有兩個子選項:

​ 1 、limits:用於限制運行的容器的最大佔用資源,當容器佔用資源超過limits時會被終止,並進行重啓。

​ 二、requests:用於設置容器須要的最小資源,若是環境資源不夠,容器將沒法啓動。

能夠經過上面的兩個選項設置資源的上下限。

  • 建立pod_resoures.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-ports
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
          resources: # 資源配額
            limits: # 限制資源的上限
              cpu: "2"  # CPU限制,單位是core數
              memory: "10Gi"  # 內存限制
            requests: # 限制資源的下限
              cpu: "1"  
              memory: "10Mi"

    這裏對cpu和memory的單位作一個說明:

    • CPU:core數,能夠爲整數或小數
    • memory:內存大小,可使用Gi,Mi,G,M等形式
  • 啓動並查看狀態

    kubectl create -f pod_resoures.yaml 
    > pod/pod-resoures created
    
    kubectl get pods pod-resoures -n dev
    
    NAME           READY   STATUS    RESTARTS   AGE
    pod-resoures   1/1     Running   0          20s

    發現正常啓動,接下來咱們修改「requests」中的memory的值爲10Gi。

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-ports
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
          resources: # 資源配額
            limits: # 限制資源的上限
              cpu: "2"  # CPU限制,單位是core數
              memory: "10Gi"  # 內存限制
            requests: # 限制資源的下限
              cpu: "1"  
              memory: "10Gi"

    從新啓動並查看

    # 刪除原來的pod
    kubectl delete -f pod_resoures.yaml
    # 啓動
    kubectl apply -f pod_resoures.yaml 
    
    # 查看
    kubectl get pods pod-resoures -n dev
    NAME           READY   STATUS    RESTARTS   AGE
    pod-resoures   0/1     Pending   0          18s
    
    # 查看詳情
    kubectl describe pods pod-resoures
    
    # 咱們發現提示內存不足
    Events:
      Type     Reason            Age        From               Message
      ----     ------            ----       ----               -------
      Warning  FailedScheduling  <unknown>  default-scheduler  0/3 nodes are available: 3 Insufficient memory.

三、Pod的生命週期

咱們通常將Pod對象從建立到終止的這段時間範圍稱爲Pod的生命週期,它主要包含下面的過程:

​ 1)Pod建立過程;

​ 2)運行初始化容器(init container)過程;

​ 3)運行主容器(main container):

​ ① 容器啓動後鉤子(post start)、容器終止前鉤子(pre stop)

​ ②容器存活性探測(liveness probe)、就緒性探測(readiness probe)

​ 4) Pod終止過程。

在整個生命週期中,Pod會出現5中狀態(相位),分別以下:

  • 掛起(Pending):API Server已經建立了Pod資源對象,但它還沒有被調度完成或仍處於下載鏡像的過程當中。
  • 運行中(Running):Pod已經被調度到某節點,而且全部容器已經被Kubectl建立完成。
  • 成功(Succeeded):Pod中的全部容器都已經成功終止而且不會被重啓。
  • 失敗(Failed):全部容器都已經終止,但至少有一個容器終止失敗,即容器返回了非0值的退出狀態。
  • 未知(Unknown):API Server沒法正常獲取到Pod對象的狀態信息,一般因爲網絡通訊失敗所致使。

3.一、Pod的建立過程

​ 一、用戶經過kubectl或其餘的api客戶端提交須要建立的Pod信息給API Server

​ 二、API Server開始生成Pod對象的信息,並將信息存入ETCD,而後返回確認信息到客戶端。

​ 三、API Server開始反映ETCD中Pod對象的變化,其餘組件使用watch機制來跟蹤檢查API Server上的變更。

​ 四、Scheduler發現有新的Pod對象要建立,開始爲Pod分配主機並將結果信息更新至API Server

​ 五、Node節點上的kubectl發現有Pod調度過來,嘗試調度Docker啓動容器,並將結果返回給API Server

​ 六、API Server將接收到的Pod狀態信息存入到ETCD中。

3.二、Pod的終止過程

一、用戶向API Server發送刪除Pod對象的命令。

​ 二、API Server中的Pod對象信息會隨着時間的推移而更新,在寬限期內(30s),Pod被視爲dead。

​ 三、將Pod標記爲terminating狀態。

​ 四、kubectl在監控到Pod對象轉爲terminating狀態的同時啓動Pod關閉過程。

​ 五、端點控制器監控到Pod對象的關閉行爲時,將其從全部匹配到此端點的service資源的端點列表中移除。

​ 六、若是當前Pod對象定義了Pre Stop鉤子處理器,則在其標記爲terminating後會以同步的方式啓動執行。

​ 七、Pod對象中的容器進程收到中止信號。

​ 八、寬限期結束後,若是Pod中還存在運行的進程,那麼Pod對象會收到當即終止的信號。

​ 九、kubectl請求API Server將此Pod資源的寬限期設置爲0從而完成刪除操做,此時Pod對於用戶來講已經不可用了。

3.三、初始化容器

初始化容器是在Pod的主容器啓動以前要運行的容器,主要是作一些主容器的前置工做,它具備兩大特徵:

​ 一、初始化容器必須運行完成直至結束,若是某個初始化容器運行失敗,那麼Kubernetes須要重啓它直至成功完成。

​ 二、初始化容器必須按照定義的順序執行,當且僅當前一個成功以後,後面一個才能運行。

初始化容器有不少應用場景,下面列出的是最多見的幾種:

​ 一、提供主容器鏡像中不具有的工具程序或自定義代碼。

​ 二、初始化容器要鹹魚應用容器串行啓動並運行完成;所以可用於延後應用容器的啓動直至其依賴的條件獲得知足。

案例

假設要以主容器來運行Nginx,可是要求在運行Nginx以前要可以鏈接上MySQL和Redis所在的服務器。

爲了簡化測試,實現規定好MySQL和Redis所在的IP地址分別爲192.168.209.120,192.168.209.121(注意這兩個IP都不能ping通)

  • 建立pod_initcontainer.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-initcontainers
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
          resources: # 資源配額
            limits: # 限制資源的上限
              cpu: "2"  # CPU限制,單位是core數
              memory: "10Gi"  # 內存限制
            requests: # 限制資源的下限
              cpu: "1"
              memory: "10Mi"
      initContainers: # 初始化容器配置
        - name: test-mysql
          image: busybox:1.30
          command: ["sh","-c","until ping 192.168.209.120 -c 1;do echo waiting for mysql ...;sleep 2;done;"]
          securityContext:
            privileged: true # 使用特權模式運行
        - name: test-redis
          image: busybox:1.30
          command: ["sh","-c","until ping 192.168.209.121 -c 1;do echo waiting for redis ...;sleep 2; done;"]
  • 啓動並查看狀態

    # 啓動
    kubectl create -f pod_initcontainers.yaml
    
    # 查看狀態,發現沒有準備就緒
    kubectl get pods pod-initcontainers -n dev
    NAME                 READY   STATUS     RESTARTS   AGE
    pod-initcontainers   0/1     Init:0/2   0          74s
  • 開啓動態查看狀態併爲網卡添加IP

    # 動態查看pod
    kubectl get pod pod-initcontainers -n dev -w
    
    NAME                 READY   STATUS     RESTARTS   AGE
    pod-initcontainers   0/1     Init:0/2   0          4m30s
    
    # 新開一個shell窗口,執行給master主機添加ip
    ifconfig ens32:1 192.168.209.120 netmask 255.255.255.0 up
    ifconfig ens32:2 192.168.209.121 netmask 255.255.255.0 up
    
    # 切回原來的窗口,發現pod已經準備就緒
    NAME                 READY   STATUS     RESTARTS   AGE
    pod-initcontainers   0/1     Init:0/2   0          4m30s
    pod-initcontainers   0/1     Init:1/2   0          6m40s
    pod-initcontainers   0/1     Init:1/2   0          6m41s
    pod-initcontainers   0/1     PodInitializing   0          6m53s
    pod-initcontainers   1/1     Running           0          6m54s

3.四、鉤子函數

鉤子函數可以感知自身生命週期中的事件,並在相應的時刻到來時運行用戶指定的程序代碼。

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:/ # url地址
                    port:80  # 端口號
                    host:192.168.209.128  # 主機地址
                    scheme: HTTP  # 支持的協議,http或https
    ......

示例

接下來,以exec方式爲例,演示下鉤子函數的使用,建立pod_hook_exec.yaml文件

apiVersion: v1
kind: Pod
metadata:
  name: pod-hook-exec
  namespace: dev
  labels:
    user: Negan
spec:
  containers:
    - name: nginx # 容器名稱
      image: nginx:1.17.1  # 容器須要的鏡像地址
      imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
      ports:
        - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
          containerPort: 80 # 容器要監聽的端口
          protocol: TCP # 端口協議
      resources: # 資源配額
        limits: # 限制資源的上限
          cpu: "2"  # CPU限制,單位是core數
          memory: "10Gi"  # 內存限制
        requests: # 限制資源的下限
          cpu: "1"
          memory: "10Mi"
      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,查看pod,以及訪問Pod

# 建立
kubectl create -f pod_hook_exec.yaml 
> pod/pod-hook-exec created

# 查看
kubectl get pods pod-hook-exec -n dev -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-hook-exec   1/1     Running   0          38s   10.244.1.14   node1   <none>           <none>

# 訪問
curl 10.244.1.14
postStart ...

3.五、容器探測

容器探測用於檢測容器中的應用實例是否正常工做,是保障業務可用性的一種傳統機制。若是通過探測,實例的狀態不符合預期,那麼Kubernetes就會把該問題實例「摘除」,不承擔業務流量。Kubernetes提供了兩種探針來實現容器探測,分別是:

​ 1) 、liveness probes:存活性探測,用於檢測應用實例當前是否處於正常運行狀態,若是不是,k8s會重啓容器。

​ 2)、readiness probes:就緒性探測,用於檢測應用實例是否能夠接受請求,若是不能,k8s不會轉發流量。

說明:

livenessProbe:存活性探測,決定是否重啓容器。

readinessProbe:就緒性探測,決定是否將請求轉發給容器。

k8s在1.16版本以後新增了startupProbe探針,用於判斷容器內應用程序是否已經啓動。若是配置了startupProbe探針,就會先禁止其餘的探針,直到startupProbe探針成功爲止,一旦成功將再也不進行探測。

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

  • exec:在容器內執行一次命令,若是命令執行的退出碼爲0,則認爲程序正常,不然不正常。

    ......
        livenessProbe:
            postStart:
                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
......
3.5.一、exec方式
  • 建立pod_liveness_exec.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-liveness-exec
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
          livenessProbe: # 聲明週期配置
            exec: # 在容器啓動以後,執行一條命令,修改Nginx的首頁內容
              command: ["/bin/cat","/tmp/hello.txt"] # 執行一個查看文件的命令,必須失敗,由於根本沒有這個文件
  • 建立Pod並查看Pod詳情

    # 建立pod
    kubectl create -f pod_liveness_exec.yaml
    
    # 查看pod
    kubectl get pods pod-liveness-exec -n dev
    
    # 發現RESTARTS不爲0
    NAME                READY   STATUS    RESTARTS   AGE
    pod-liveness-exec   1/1     Running   2          67s
    
    # 查看pod詳情
    kubectl describe pods pod-liveness-exec -n dev
    
    Events:
      Type     Reason     Age                  From               Message
      ----     ------     ----                 ----               -------
      Normal   Scheduled  <unknown>            default-scheduler  Successfully assigned dev/pod-liveness-exec to node1
      Normal   Killing    28s (x3 over 88s)    kubelet, node1     Container nginx failed liveness probe, will be restarted
      Normal   Pulled     27s (x4 over 115s)   kubelet, node1     Container image "nginx:1.17.1" already present on machine
      Normal   Created    27s (x4 over 114s)   kubelet, node1     Created container nginx
      Normal   Started    27s (x4 over 114s)   kubelet, node1     Started container nginx
      Warning  Unhealthy  18s (x10 over 108s)  kubelet, node1     Liveness probe failed: /bin/cat: /tmp/hello.txt: No such file or directory

    觀察上面的信息就會發現nginx容器啓動以後就進行了健康檢查。

    檢查失敗以後,容器被kill掉,而後嘗試進行重啓,這是重啓策略的做用。

    稍等一會以後,再觀察Pod的信息,就會看到RESTARTS再也不是0,而是一直增加。

  • 刪除上面建立的Pod,並修改command執行的命令,使其正常。

    # 刪除
    kubectl delete -f pod_liveness_exec.yaml
    
    # 修改yaml文件
    command: ["/bin/ls","/tmp"] # 查看tmp目錄
    
    # 建立
    kubectl create -f pod_liveness_exec.yaml 
    
    # 查看
    kubectl get pods pod-liveness-exec -n dev
    NAME                READY   STATUS    RESTARTS   AGE
    pod-liveness-exec   1/1     Running   0          16s
3.5.二、tcpSocket方式
  • 建立pod_liveness_tcpsocket.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-liveness-socket
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
          livenessProbe: # 聲明週期配置
            tcpSocket:
              port: 8080 # 嘗試訪問8080端口,失敗,Pod中只有一個Nginx,監聽80
  • 建立和查看

    # 建立
    kubectl create -f pod_liveness_tcpsocket.yaml 
    
    # 查看
    get pods pod-liveness-tcpsocket -n dev
    NAME                     READY   STATUS             RESTARTS   AGE
    pod-liveness-tcpsocket   0/1     CrashLoopBackOff   4          2m39s
3.5.三、httpGet方式

此處只給出yaml文件,其餘操做和上面相似,此處再也不贅述。

apiVersion: v1
kind: Pod
metadata:
  name: pod-liveness-httpGet
  namespace: dev
  labels:
    user: Negan
spec:
  containers:
    - name: nginx # 容器名稱
      image: nginx:1.17.1  # 容器須要的鏡像地址
      imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
      ports:
        - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
          containerPort: 80 # 容器要監聽的端口
          protocol: TCP # 端口協議
      livenessProbe: # 聲明週期配置
        httpGet: 
          port: 80
          scheme: HTTP
          path: /hello
          host: 127.0.0.1
3.5.四、容器探測的補充

上面已經使用了livenessProbe演示了三種探測方式,可是查看livenessProbe的子屬性,會發現除了這三種方式,還有一些其餘的配置。

kubectl explain pod.spec.containers.livenessProbe

KIND:     Pod
VERSION:  v1

RESOURCE: livenessProbe <Object>

DESCRIPTION:
     Periodic probe of container liveness. Container will be restarted if the
     probe fails. Cannot be updated. More info:
     https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes

     Probe describes a health check to be performed against a container to
     determine whether it is alive or ready to receive traffic.

FIELDS:
   exec	<Object>
     One and only one of the following should be specified. Exec specifies the
     action to take.

   failureThreshold	<integer> 
   # 連續探測失敗多少次才被認定爲失敗。默認是3。最小值是1
     Minimum consecutive failures for the probe to be considered failed after
     having succeeded. Defaults to 3. Minimum value is 1.

   httpGet	<Object>
     HTTPGet specifies the http request to perform.

   initialDelaySeconds	<integer>
     Number of seconds after the container has started before liveness probes
     are initiated. More info:
     https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes

   periodSeconds	<integer>
     How often (in seconds) to perform the probe. Default to 10 seconds. Minimum
     value is 1.

   successThreshold	<integer>
     Minimum consecutive successes for the probe to be considered successful
     after having failed. Defaults to 1. Must be 1 for liveness and startup.
     Minimum value is 1.

   tcpSocket	<Object>
     TCPSocket specifies an action involving a TCP port. TCP hooks not yet
     supported

   timeoutSeconds	<integer>
     Number of seconds after which the probe times out. Defaults to 1 second.
     Minimum value is 1. More info:
     https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes


initialDelaySeconds    # 容器啓動後等待多少秒執行第一次探測
timeoutSeconds      # 探測超時時間。默認1秒,最小1秒
periodSeconds       # 執行探測的頻率。默認是10秒,最小1秒
failureThreshold    # 連續探測失敗多少次才被認定爲失敗。默認是3。最小值是1
successThreshold    # 連續探測成功多少次才被認定爲成功。默認是1

3.六、重啓策略

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

​ 1) 、Always:容器失效時,自動重啓該容器,默認值。

​ 2)、OnFailure:容器終止運行且退出碼不爲0時重啓。

​ 3)、Never:不論狀態如何,都不重啓該容器。

重啓策略適用於Pod對象中的全部容器,首次須要重啓的容器,將在其須要的時候當即進行重啓,隨後再次重啓的操做將由kubelet延遲一段時間後進行,且反覆的重啓操做的延遲時長以此爲10s、20s、40s、80s、160s和300s,300s是最大的延遲時長。

  • 建立pod_restart_policy.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-restart-policy
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
          livenessProbe: # 聲明週期配置
            httpGet:
              port: 80
              scheme: HTTP
              path: /hello
              host: 127.0.0.1
      restartPolicy: Never # 重啓策略
  • 建立,查看狀態

    kubectl create -f pod_restart_policy.yaml 
    
    kubectl describe pod pod-restart-policy -n dev
    
    Events:
      Type     Reason     Age                From               Message
      ----     ------     ----               ----               -------
      Normal   Scheduled  <unknown>          default-scheduler  Successfully assigned dev/pod-restart-policy to node2
      Normal   Pulled     73s                kubelet, node2     Container image "nginx:1.17.1" already present on machine
      Normal   Created    73s                kubelet, node2     Created container nginx
      Normal   Started    73s                kubelet, node2     Started container nginx
      Warning  Unhealthy  50s (x3 over 70s)  kubelet, node2     Liveness probe failed: Get http://127.0.0.1:80/hello: dial tcp 127.0.0.1:80: connect: connection refused
      Normal   Killing    50s                kubelet, node2     Stopping container nginx 
      # 咱們發現容器探測失敗後,直接中止容器,並無選擇重啓

四、調度

在默認狀況下,一個Pod在哪一個Node節點上運行,是由Scheduler組件採用相應的算法計算出來的,這個過程是不受人工控制的。可是在實際使用中,這並不知足需求,由於不少狀況下,咱們想控制某些Pod到達某些節點上,那麼應該怎麼作?這就要求瞭解Kubernetes對Pod的調度規則,Kubernetes提供了四大類調度方式。

  • 自動調度:運行在哪一個Node節點上徹底由Scheduler通過一系列的算法計算得出。

  • 定向調度:NodeName、NodeSelector。

  • 親和性調度:NodeAffinity、PodAffinity、PodAntiAffinity。

  • 污點(容忍)調度:Taints、Toleration。

4.一、定向調度

定向調度,指的是利用在Pod上聲明的nodeName或nodeSelector,以此將Pod調度到指望的Node節點上。注意,這裏的調度是強制的,這就意味着即便要調度的目標Node不存在,也會向上面進行調度,只不過Pod運行失敗而已。

4.1.一、nodeName

nodeName用於強制約束將Pod調度到指定的name的Node節點上。這種方式,實際上是直接跳過Scheduler的調度邏輯,直接將Pod調度到指定名稱的節點。

  • 建立一個pod-nodename.yaml文件,內容以下:

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodename
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
      nodeName: node1  # 指定調度到node1上
  • 建立和查看

    # 建立pod
    kubectl create -f pod_nodename.yaml
    
    # 查看
    kubectl get pod pod-nodename -n dev -o wide
    NAME           READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
    pod-nodename   1/1     Running   0          27s   10.244.1.18   node1   <none>           <none>

固然咱們也能夠將pod調度到不存在node上,pod確定也是不能正常運行。

# 刪除pod
kubectl delete -f pod_nodename.yaml

# 修改pod_nodename.yaml
# 將nodeName:node1 修改成node3 (node3不存在)

# 建立和查看狀態
kubectl create -f pod_nodename.yaml

kubectl get pods pod-nodename -n dev -o wide

# 咱們發現pod的狀態時掛起狀態
NAME           READY   STATUS    RESTARTS   AGE   IP       NODE    NOMINATED NODE   READINESS GATES
pod-nodename   0/1     Pending   0          6s    <none>   node3   <none>           <none>
4.1.二、nodeSelector

nodeSelector用於將Pod調度到添加了指定標籤的Node節點上,它是經過Kubernetes的label-selector機制實現的,換言之,在Pod建立以前,會由Scheduler使用MatchNodeSelector調度策略進行label匹配,找出目標node,而後將Pod調度到目標節點,該匹配規則是強制約束。

  • 首先給node節點添加標籤

    kubectl label node node1 env=pro # 給node1添加標籤爲env=pro
    kubectl label node node2 env=debug # 給node2添加標籤爲env=debug
    
    # 能夠看到咱們已經添加上了相應的標籤
    kubectl get nodes --show-labels
    NAME     STATUS   ROLES    AGE   VERSION   LABELS
    master   Ready    master   2d    v1.18.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/master=
    node1    Ready    <none>   2d    v1.18.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=pro,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
    node2    Ready    <none>   2d    v1.18.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,env=debug,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
  • 建立pod_nodeselector.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeselector
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
      nodeSelector:
        env: debug
  • 查看

    kubectl get pod pod-nodeselector -n dev -o wide
    
    # 發現成功調度到node2上
    NAME               READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
    pod-nodeselector   1/1     Running   0          21s   10.244.2.20   node2   <none>           <none>

一樣,咱們如要調度要不存在的標籤上,會發生什麼狀況:

# 刪除pod
kubectl delete -f pod_nodeselector.yaml

# 修改pod_nodeselector.yaml文件
# 將debug改成debug1 

# 建立和查看
kubectl get pod pod-nodeselector -n dev -o wide
# pod的狀態爲掛起狀態
NAME               READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
pod-nodeselector   0/1     Pending   0          24s   <none>   <none>   <none>           <none>

4.二、親和性調度

雖然定向調度的兩種方式,使用起來很是方便,可是也有必定的問題,那就是若是沒有知足條件的Node,那麼Pod將不會被運行,即便在集羣中還有可用的Node列表也不行,這就限制了它的使用場景。

基於上面的問題,Kubernetes還提供了一種親和性調度(Affinity)。它在nodeSelector的基礎之上進行了擴展,能夠經過配置的形式,實現優先選擇知足條件的Node進行調度,若是沒有,也能夠調度到不知足條件的節點上,使得調度更加靈活。

Affinity主要分爲三類:

  • nodeAffinity(node親和性):以Node爲目標,解決Pod能夠調度到那些Node的問題。

  • podAffinity(pod親和性):以Pod爲目標,解決Pod能夠和那些已存在的Pod部署在同一個拓撲域中的問題。

  • podAntiAffinity(pod反親和性):以Pod爲目標,解決Pod不能和那些已經存在的Pod部署在同一拓撲域中的問題。

關於親和性和反親和性的使用場景說明:

親和性:若是兩個應用頻繁交互,那麼就有必要利用親和性讓兩個應用盡量的靠近,這樣能夠較少因網絡通訊而帶來的性能損耗。

反親和性:當應用採用多副本部署的時候,那麼就有必要利用反親和性讓各個應用實例打散分佈在各個Node上,這樣能夠提升服務的高可用性。

4.2.一、nodeAffinity

查看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: env # 匹配存在標籤的key爲env的節點
	  operator: Exists   
	- key: env # 匹配標籤的key爲env,且value是"xxx"或"yyy"的節點
	  operator: In    
      values: ["xxx","yyy"]
    - key: env # 匹配標籤的key爲env,且value大於"xxx"的節點
      operator: Gt   
      values: "xxx"

下面演示requiredDuringSchedulingIgnoredDuringExecution

  • 建立pod_nodeaffinity_required.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeselector
      namespace: dev
      labels:
        user: Negan
    spec:
      containers:
        - name: nginx # 容器名稱
          image: nginx:1.17.1  # 容器須要的鏡像地址
          imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
          ports:
            - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
              containerPort: 80 # 容器要監聽的端口
              protocol: TCP # 端口協議
      affinity: # 親和性設置
        nodeAffinity: # node親和性
          requiredDuringSchedulingIgnoredDuringExecution: # node節點必須知足全部規則
            nodeSelectorTerms: # 節點選擇列表
              - matchExpressions:
                  - key: env
                    operator: In
                    values: ["xxx","yyy"] # 須要知足其中一個
  • 查看狀態

    kubectl get pods pod-nodeaffinity-required -n dev -o wide
    
    # 咱們發現STATUS爲Pending,這也不難解釋,咱們的標籤env對應的值沒有xxx和yyy
    NAME                        READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
    pod-nodeaffinity-required   0/1     Pending   0          24s   <none>   <none>   <none>           <none>
  • 使其正常調度

    # 刪除pod
    kuebctl delete -f pod_nodeaffinity_required.yaml 
    
    # 修改yaml文件,將「pro」添加到values列表中
    
    # 建立
    kubectl create -f pod_nodeaffinity_required.yaml 
    
    # 查看
    kubectl get pods pod-nodeaffinity-required -n dev -o wide
    # 發現成功調度
    NAME                        READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
    pod-nodeaffinity-required   1/1     Running   0          12s   10.244.1.19   node1   <none>           <none>

下面演示preferredDuringSchedulingIgnoredDuringExecution :

  • 建立pod_nodeaffinity_preferred.yaml文件

    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-nodeaffinity-preferred
      namespace: dev
    spec:
      containers: # 容器配置
        - name: nginx
          image: nginx:1.17.1
          imagePullPolicy: IfNotPresent
          ports:
            - name: nginx-port
              containerPort: 80
              protocol: TCP
      affinity: # 親和性配置
        nodeAffinity: # node親和性配置
          preferredDuringSchedulingIgnoredDuringExecution: # 優先調度到知足指定的規則的Node,至關於軟限制 (傾向)
            - preference: # 一個節點選擇器項,與相應的權重相關聯
                matchExpressions:
                  - key: nodeenv
                    operator: In
                    values:
                      - "xxx"
                      - "yyy"
              weight: 1
  • 查看

    kubectl get pods pod-nodeaffinity-preferred -n dev -o wide
    
    # 發現即便沒有知足條件,pod也被正常調度了。
    NAME                         READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
    pod-nodeaffinity-preferred   1/1     Running   0          57s   10.244.2.21   node2   <none>           <none>

注意

若是同時定義了nodeSelector和nodeAffinity,那麼必須兩個條件都知足,Pod才能運行在指定的Node上。

若是nodeAffinity指定了多個nodeSelectorTerms,那麼只須要其中一個可以匹配成功便可。

若是一個nodeSelectorTerms中有多個matchExpressions,則一個節點必須知足全部的才能匹配成功。

若是一個Pod所在的Node在Pod運行期間其標籤發生了改變,再也不符合該Pod的nodeAffinity的要求,則系統將忽略此變化。

4.2.二、podAffinity

podAffinity主要實現以運行的Pod爲參照,實現讓新建立的Pod和參照的Pod在一個區域的功能。

查看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 **

  • 建立參照的pod

    • pod-podaffinity-target.yaml文件

      apiVersion: v1
      kind: Pod
      metadata:
        name: pod-podaffinity-target
        namespace: dev
        labels:
          user: Negan
      spec:
        containers:
          - name: nginx # 容器名稱
            image: nginx:1.17.1  # 容器須要的鏡像地址
            imagePullPolicy: IfNotPresent  # 設置鏡像的拉取策略
            ports:
              - name: nginx-port  # 端口名稱,若是執行,必須保證name在Pod中是惟一的
                containerPort: 80 # 容器要監聽的端口
                protocol: TCP # 端口協議
        nodeName: node1 # 將pod定向調度到node1節點上
    • 建立pod並查看狀態

      kubectl get pods pod-podaffinity-target -n dev -o wide
      NAME                     READY   STATUS    RESTARTS   AGE    IP            NODE    NOMINATED NODE   READINESS GATES
      pod-podaffinity-target   1/1     Running   0          2m5s   10.244.1.20   node1   <none>           <none>
  • 建立目標pod,將其調度到參照pod所在的節點

    • 編寫pod-podaffinity-requred.yaml文件

      apiVersion: v1
      kind: Pod
      metadata:
        name: pod-podaffinity-requred
        namespace: dev
      spec:
        containers: # 容器配置
          - name: nginx
            image: nginx:1.17.1
            imagePullPolicy: IfNotPresent
            ports:
              - name: nginx-port
                containerPort: 80
                protocol: TCP
        affinity: # 親和性配置
          podAffinity: # Pod親和性
            requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
              - labelSelector:
                  matchExpressions: # 該Pod必須和擁有標籤name=Negan或者name=yyy的Pod在同一個Node上
                    - key: user
                      operator: In
                      values:
                        - "Negan"
                        - "yyy"
                topologyKey: kubernetes.io/hostname
    • 建立並查看狀態

      kubectl get pods pod-podaffinity-requred -n dev -o wide
      NAME                      READY   STATUS              RESTARTS   AGE   IP       NODE    NOMINATED NODE   READINESS GATES
      pod-podaffinity-requred   1/1     ContainerCreating   0          5s    <none>   node1  <none>           <none>
4.2.三、podAntiAffinity

podAntiAffinity主要實現以運行的Pod爲參照,讓新建立的Pod和參照的Pod不在一個區域的功能。

其配置方式和podAffinity同樣,此處不作詳細解釋。

這裏咱們繼續使用上面的參照pod作演示。

  • 建立pod-podantiaffinity-requred.yaml文件,內容以下:
apiVersion: v1
kind: Pod
metadata:
  name: pod-podantiaffinity-requred
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 親和性配置
    podAntiAffinity: # Pod反親和性
      requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
        - labelSelector:
            matchExpressions: # 該Pod必須和擁有標籤name=Negan或者name=yyy的Pod在同一個Node上
              - key: user
                operator: In
                values:
                  - "Negan"
                  - "yyy"
          topologyKey: kubernetes.io/hostname
  • 查看狀態
kubectl get pods pod-podantiaffinity-requred -n dev -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
pod-podantiaffinity-requred   1/1     Running   0          26s   10.244.2.23   node2   <none>           <none>

4.三、污點和容忍

4.3.一、污點

前面的調度方式都是站在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 taint node xxx key=value:effect
    • 去除污點

      kubectl taint node xxx key:effect-
    • 去除全部污點

      kubectl taint node xxx key-
    • 查看指定節點上的污點

      kubectl describe node 節點名稱
  • 演示污點效果

    爲了演示方便,暫時中止node2節點。

    • 爲node1設置不一樣的污點

      # 爲Node1設置污點
      kubectl taint node node1 tag=Negan:PreferNoSchedule
      
      # 建立一個pod1
      kubectl run pod1 --image=nginx:1.17.1 -n dev
      
      # 查看pod狀態
      kubectl get pod pod1 -n dev
      
      NAME   READY   STATUS    RESTARTS   AGE
      pod1   1/1     Running   0          21m
      
      # 給node1更換污點NoSchedule
      kubectl taint node node1 tag:PreferNoSchedule-
      kubectl taint node node1 tag=Negan:NoSchedule
      
      # 建立pod2
      kubectl run pod2 --image=nginx:1.17.1 -n dev
      
      # 查看狀態,發現不能正常運行
      kubectl get pods pod2 -n dev
      NAME   READY   STATUS    RESTARTS   AGE
      pod2   0/1     Pending   0          29s
      
      # 給node1更換污點NoExecute
      kubectl taint node node1 tag:NoSchedule-
      kubectl taint node node1 tag=Negan:NoExecute
      
      # 建立node3,發現不能正常運行,同時正常運行的pod1和pod2被驅逐了
      kubectl run pod3 --image=nginx:1.17.1 -n dev

說明

使用kubeadm搭建的集羣,默認就會給Master節點添加一個污點標記,因此Pod就不會調度到Master節點上

4.3.二、容忍

上面介紹了污點的做用,咱們能夠在Node上添加污點用來拒絕Pod調度上來,可是若是就是想讓一個Pod調度到一個有污點的Node上去,這時候應該怎麼作?這就須要使用到容忍。

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

容忍詳細配置:

kubectl explain pod.spec.tolerations
......
FIELDS:
  key       # 對應着要容忍的污點的鍵,空意味着匹配全部的鍵
  value     # 對應着要容忍的污點的值
  operator  # key-value的運算符,支持Equal和Exists(默認)
  effect    # 對應污點的effect,空意味着匹配全部影響
  tolerationSeconds   # 容忍時間, 當effect爲NoExecute時生效,表示pod在Node上的停留時間

當operator爲Equal的時候,若是Node節點有多個Taint,那麼Pod每一個Taint都須要容忍才能部署上去。

當operator爲Exists的時候,有以下的三種寫法:

  • 容忍指定的污點,污點帶有指定的effect:

    tolerations: # 容忍
        - key: "tag" # 要容忍的污點的key
          operator: Exists # 操做符
          effect: NoExecute # 添加容忍的規則,這裏必須和標記的污點規則相同
  • 容忍指定的污點,不考慮具體的effect:

    tolerations: # 容忍
        - key: "tag" # 要容忍的污點的key
          operator: Exists # 操做符
  • 容忍一切污點(慎用):

    tolerations: # 容忍
        - operator: Exists # 操做符

在上面的污點示例中,已經給node1打上了NoExecute的污點,此時任何Pod是調度不上去的,能夠經過在Pod中添加容忍,將Pod調度上去。

建立pod-toleration.yaml文件,內容以下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-toleration
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  tolerations: # 容忍
    - key: "tag" # 要容忍的污點的key
      operator: Equal # 操做符
      value: "Negan" # 要容忍的污點的value
      effect: NoExecute # 添加容忍的規則,這裏必須和標記的污點規則相同

建立和查看pod

kubectl get pods pod-toleration -n dev
NAME             READY   STATUS    RESTARTS   AGE
pod-toleration   1/1     Running   0          47s
相關文章
相關標籤/搜索