banner
云野阁

云野阁

闲云野鹤,八方逍遥

Kubernetes知識梳理-核心組件

pod#

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

  1. apiVersion <string> 版本,由 kubernetes 內部定義,版本號必須可以用 kubectl api-versions 查詢到
  2. kind <string>類型,由 kubernetes 內部定義,版本號必須可以用 kubectl api-resources 查詢到
  3. metadata <object>元數據,主要是資源標識和說明,常用的有 name、namespace、labelss 等
  4. spec <object>描述,這是配置中最重要的一部分,裡面是對各種資源配置的詳細描述
  5. status <object>狀態信息,裡面的內容不需要定義,由 kubernetes 自動生成

Pod 生命週期#

將 Pod 對象從創建到結束的時間範圍稱為 Pod 的生命週期。其生命週期的主要過程如下:

  1. pod 創建
  2. 運行初始化容器
  3. 運行主容器容器
    (1)啟動鉤子、終止鉤子
    (2)存活性探測、就緒性探測
  4. pod 終止

0

在整個生命週期中,P0d 會出現 5 種狀態,分別如下:

  1. 挂起 (Pending):apiserver 已經創建了 pod 資源對象,但它尚未被調度完成或者仍處於下載鏡像的過程中
  2. 運行中 (Running):pod 已經被調度至某節點,並且所有容器都已經被 kubelet 創建完成
  3. 成功 (Succeeded):pod 中的所有容器都已經成功終止並且不會被重啟
  4. 失敗 (Failed):所有容器都已經終止,但至少有一個容器終止失敗,即容器返回了非 0 值的退出狀態
  5. 未知 (Unknown):apiserver 無法正常獲取到 pod 對象的狀態信息,通常由網絡通信失敗所導致

pod 的創建過程#

通過 kubectl 將 pod 配置傳輸給 apiserver,apiserver 轉化 pod 信息並存入 etcd,再進行” 握手 “反饋。scheduler 監聽 apiserver 中 pod 信息的變化,使用算法為 pod 分配主機並更新 apiserver 的信息,對應 node 節點主機通過監聽更新後的信息,創建容器並更新信息至 apiserver,apiserver 將最終信息存入 etcd,至此 pod 創建完成。

  1. 用戶通過 kubectl 或其他 api 客戶端提交需要創建的 pod 信息給 apiServer
  2. apiServer 開始生成 pod 對象的信息,並將信息存入 etcd, 然後返回確認信息至客戶端
  3. apiServer 開始反映 etcd 中的 pod 對象的變化,其它組件使用 watch 機制來跟蹤檢查 apiServer 上的變動
  4. scheduler 發現有新的 pod 對象要創建,開始為 Pod 分配主機並將結果信息更新至 apiServer
  5. node 節點上的 kubelet 發現有 pod 調度過來,嘗試調用 docker 啟動容器,並將結果回送至 apiServer
  6. apiServer 將接收到的 pod 狀態信息存入 etcd 中

1

pod 的終止過程#

用戶發送刪除 pod 命令,apiserver 接受並更新信息,pod 狀態變為 terminating。kubelet 監聽收到後,啟動 pod 關閉指令,端點控制器監聽到 pod 關閉指令,刪除對應 service 資源,pod 停止運行,kubelet 請求 apiServer 將 pod 資源的寬限期設置為 0 從而完成刪除操作,apiserver 將最終信息存入 etcd,至此 pod 刪除完成。

  1. 用戶向 apiServer 發送刪除 pod 對象的命令
  2. apiServer 中的 pod 對象信息會隨著時間的推移而更新,在寬限期內 (默認 30s),pod 被視為 dead
  3. 將 pod 標記為 terminating 狀態
  4. kubelet 在監控到 pod 對象轉為 terminating 狀態的同時啟動 pod 關閉過程
  5. 端點控制器監控到 pod 對象的關閉行為時將其從所有匹配到此端點的 service 資源的端點列表中移除
  6. 如果當前 pod 對象定義了 preStop 鉤子處理器,則在其標記為 terminating 後即會以同步的方式啟動執行
  7. pod 對象中的容器進程收到停止信號
  8. 寬限期結束後,若 pod 中還存在仍在運行的進程,那麼 pod 對象會收到立即終止的信號
  9. kubelet 請求 apiServer 將此 pod 資源的寬限期設置為 0 從而完成刪除操作,此時 pod 對於用戶已不可見

初始化容器#

初始化容器是在 Pod 的主容器啟動之前要運行的容器,主要是做一些主容器的前置工作,它具有兩大特徵:
1、初始化容器必須運行完成直至結束,如果某個初始化容器運行失敗,那麼 kubernetes 需要重啟它直至成功完成。
2、初始化容器必須按照定義的順序執行,當且僅當前一個成功之後,後面的一个才能運行。
初始化容器有很多的應用場景,下面列出的是最常見的幾個:
1、提供主容器鏡像中不具備的工具程序或自定義代碼。
2、初始化容器要先於應用容器串行啟動並運行完成,因此可用於延後應用容器的啟動直至其依賴的條件得到滿足。
接下來做一個案例,模擬下面這個需求:
假設要以主容器來運行 Nginx,但是要求在運行 Nginx 之前要能夠連接上 MySQL 和 Redis 所在的服務器。
為了簡化測試,事先規定好 MySQL 和 Redis 所在的 IP 地址分別為 192.168.18.103 和 192.168.18.104(注意,這兩個 IP 都不能 ping 通,因為環境中沒有這兩個 IP)。
創建 pod-initcontainer.yaml 文件,內容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-initcontainer
  namespace: dev
  labels:
    user: xudaxian
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
      resources:
        limits:
          cpu: "2"
          memory: "10Gi"
        requests:
          cpu: "1"
          memory: "10Mi"
  initContainers: # 初始化容器配置
    - name: test-mysql
      image: busybox:1.30
      command: ["sh","-c","until ping 192.168.18.103 -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.18.104 -c 1;do echo waiting for redis ...;sleep 2;done;"]

執行命令後,test-mysql 未創建成功,之後的容器也無法創建。修改 ip 為可訪問 ip 後,重新執行命令,會按順序創建成功

鉤子函數#

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.109.100 #主機地址  
              scheme: HTTP #支持的協議,http或者https
  .......

容器探測#

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

  • liveness probes:存活性探測,用於檢測應用實例當前是否處於正常運行狀態,如果不是,k8s 會重啟容器。

  • readiness probes:就緒性探測,用於檢測應用實例是否可以接受請求,如果不能,k8s 不會轉發流量。

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

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

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

  • 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
……

重啟策略#

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

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

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

Pod 的調度#

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

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

定向調度#

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

nodeName#

nodeName 用於強制約束將 Pod 調度到指定的 name 的 Node 節點上。這種方式,其實是直接跳過 Scheduler 的調度邏輯,直接將 Pod 調度到指定名稱的節點。
創建一個 pod-nodename.yaml 文件,內容如下:

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodename
  namespace: dev
  labels:
    user: xudaxian
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeName: k8s-node1 # 指定調度到k8s-node1節點上
nodeSelector#

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

apiVersion: v1
kind: Pod
metadata:
  name: pod-nodeselector
  namespace: dev
spec:
  containers: # 容器配置
    - name: nginx
      image: nginx:1.17.1
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  nodeSelector:
    nodeenv: pro # 指定調度到具有nodeenv=pro的Node節點上

親和性調度#

雖然定向調度的兩種方式,使用起來非常方便,但是也有一定的問題,那就是如果沒有滿足條件的 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(node 親和性)#

查看 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
      imagePullPolicy: IfNotPresent
      ports:
        - name: nginx-port
          containerPort: 80
          protocol: TCP
  affinity: # 親和性配置
    nodeAffinity: # node親和性配置
      requiredDuringSchedulingIgnoredDuringExecution: # Node節點必須滿足指定的所有規則才可以,相當於硬規則,類似於定向調度
        nodeSelectorTerms: # 節點選擇列表
          - matchExpressions:
              - key: nodeenv # 匹配存在標籤的key為nodeenv的節點,並且value是"xxx"或"yyy"的節點
                operator: In
                values:
                  - "xxx"
                  - "yyy"

nodeAffinity 的注意事項:

  • 如果同時定義了 nodeSelector 和 nodeAffinity,那麼必須兩個條件都滿足,Pod 才能運行在指定的 Node 上。
  • 如果 nodeAffinity 指定了多個 nodeSelectorTerms,那麼只需要其中一個能夠匹配成功即可。
  • 如果一個 nodeSelectorTerms 中有多個 matchExpressions,則一個節點必須滿足所有的才能匹配成功。
  • 如果一個 Pod 所在的 Node 在 Pod 運行期間其標籤發生了改變,不再符合該 Pod 的 nodeAffinity 的要求,則系統將忽略此變化。
podAffinity(pod 親和性)#

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-1

topologyKey 用於指定調度的作用域,例如:

  • 如果指定為 kubernetes.io/hostname,那就是以 Node 節點為區分範圍。
  • 如果指定為 beta.kubernetes.io/os,則以 Node 節點的操作系統類型來區分。

演示 requiredDuringSchedulingIgnoredDuringExecution。
創建 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必須和擁有標籤podenv=xxx或者podenv=yyy的Pod在同一個Node上,顯然沒有這樣的Pod
              - key: podenv
                operator: In
                values:
                  - "xxx"
                  - "yyy"
          topologyKey: kubernetes.io/hostname
podAntiAffinity(pod 反親和性)#

podAntiAffinity 主要實現以運行的 Pod 為參照,讓新創建的 Pod 和參照的 Pod 不在一個區域的功能。
其配置方式和 podAffinity 一樣。

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:
              - key: podenv
                operator: In
                values:
                  - "pro"
          topologyKey: kubernetes.io/hostname

污點和容忍#

污點(Taints)#

前面的調度方式都是站在 Pod 的角度上,通過在 Pod 上添加屬性,來確定 Pod 是否要調度到指定的 Node 上,其實我們也可以站在 Node 的角度上,通過在 Node 上添加污點屬性,來決定是否運行 Pod 調度過來。
Node 被設置了污點之後就和 Pod 之間存在了一種相斥的關係,進而拒絕 Pod 調度進來,甚至可以將已經存在的 Pod 驅逐出去。
污點的格式為:key=value,key 和 value 是污點的標籤,effect 描述污點的作用,支持如下三個選項:

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

2

容忍(Toleration)#

上面介紹了污點的作用,我們可以在 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:
  • 容忍指定的污點,不考慮具體的 effect:
  • 容忍一切污點(慎用):
  tolerations: # 容忍
    - key: "tag" # 要容忍的污點的key
      operator: Exists # 操作符
      effect: NoExecute # 添加容忍的規則,這裡必須和標記的污點規則相同
  tolerations: # 容忍
    - key: "tag" # 要容忍的污點的key
      operator: Exists # 操作符
 tolerations: # 容忍
    - operator: Exists # 操作符

Pod 控制器#

在 kubernetes 中,按照 Pod 的創建方式可以將其分為兩類:

  • 自主式 Pod:kubernetes 直接創建出來的 Pod,這種 Pod 刪除後就沒有了,也不會重建。
  • 控制器創建 Pod:通過 Pod 控制器創建的 Pod,這種 Pod 刪除之後還會自動重建。

Pod 控制器:Pod 控制器是管理 Pod 的中間層,使用了 Pod 控制器之後,我們只需要告訴 Pod 控制器,想要多少個什麼樣的 Pod 就可以了,它就會創建出滿足條件的 Pod 並確保每一個 Pod 處於用戶期望的狀態,如果 Pod 在運行中出現故障,控制器會基於指定的策略重啟或重建 Pod。
在 kubernetes 中,有很多類型的 Pod 控制器,每種都有自己的適合的場景,常見的有下面這些:

  • ReplicationController:比較原始的 Pod 控制器,已經被廢棄,由 ReplicaSet 替代。
  • ReplicaSet:保證指定數量的 Pod 運行,並支持 Pod 數量變更,鏡像版本變更。
  • Deployment:通過控制 ReplicaSet 來控制 Pod,並支持滾動升級、版本回退。
  • Horizontal Pod Autoscaler:可以根據集群負載自動調整 Pod 的數量,實現削峰填谷。
  • DaemonSet:在集群中的指定 Node 上都運行一個副本,一般用於守護進程類的任務。
  • Job:它創建出來的 Pod 只要完成任務就立即退出,用於執行一次性任務。
  • CronJob:它創建的 Pod 會周期性的執行,用於執行周期性的任務。
  • StatefulSet:管理有狀態的應用。

ReplicaSet(RS)#

ReplicaSet 的主要作用是保證一定數量的 Pod 能夠正常運行,它會持續監聽這些 Pod 的運行狀態,一旦 Pod 發生故障,就會重啟或重建。同時它還支持對 Pod 數量的擴縮容。

3

ReplicaSet 的資源清單文件:

apiVersion: apps/v1 # 版本號 
kind: ReplicaSet # 類型 
metadata: # 元數據 
  name: # rs名稱
  namespace: # 所属命名空間 
  labels: #標籤 
    controller: rs 
spec: # 詳情描述 
  replicas: 3 # 副本數量 
  selector: # 選擇器,通過它指定該控制器管理哪些po
    matchLabels: # Labels匹配規則 
      app: nginx-pod 
    matchExpressions: # Expressions匹配規則 
      - {key: app, operator: In, values: [nginx-pod]} 
template: # 模板,當副本數量不足時,會根據下面的模板創建pod副本 
  metadata: 
    labels: 
      app: nginx-pod 
  spec: 
    containers: 
      - name: nginx 
        image: nginx:1.17.1 
        ports: 
        - containerPort: 80

在這裡,需要新了解的配置項就是 spec 下面幾個選項:

  • replicas:指定副本數量,其實就是當前 rs 創建出來的 Pod 的數量,默認為 1.
  • selector:選擇器,它的作用是建立 Pod 控制器和 Pod 之間的關聯關係,采用了 Label Selector 機制(在 Pod 模塊上定義 Label,在控制器上定義選擇器,就可以表明當前控制器能管理哪些 Pod 了)。
  • template:模板,就是當前控制器創建 Pod 所使用的模板,裡面其實就是前面學過的 Pod 的定義。

Deployment(Deploy)#

為了更好的解決服務編排的問題,kubernetes 在 v1.2 版本開始,引入了 Deployment 控制器。值得一提的是,Deployment 控制器並不直接管理 Pod,而是通過管理 ReplicaSet 來間接管理 Pod,即:Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。所以 Deployment 的功能比 ReplicaSet 強大。

Deployment 的主要功能如下:

  • 支持 ReplicaSet 的所有功能。
  • 支持發布的停止、繼續。
  • 支持版本滾動更新和版本回退。

Deployment 的資源清單:

apiVersion: apps/v1 # 版本號 
kind: Deployment # 類型 
metadata: # 元數據 
  name: # rs名稱 
  namespace: # 所屬命名空間 
  labels: #標籤 
    controller: deploy 
spec: # 詳情描述 
  replicas: 3 # 副本數量 
  revisionHistoryLimit: 3 # 保留歷史版本,默認為10 
  paused: false # 暫停部署,默認是false 
  progressDeadlineSeconds: 600 # 部署超時時間(s),默認是600 
  strategy: # 策略 
    type: RollingUpdate # 滾動更新策略 
    rollingUpdate: # 滾動更新 
      maxSurge: 30% # 最大額外可以存在的副本數,可以為百分比,也可以為整數 maxUnavailable: 30% # 最大不可用狀態的    Pod 的最大值,可以為百分比,也可以為整數 
  selector: # 選擇器,通過它指定該控制器管理哪些pod 
    matchLabels: # Labels匹配規則 
      app: nginx-pod 
    matchExpressions: # Expressions匹配規則 
      - {key: app, operator: In, values: [nginx-pod]} 
  template: # 模板,當副本數量不足時,會根據下面的模板創建pod副本 
    metadata: 
      labels: 
        app: nginx-pod 
    spec: 
      containers: 
      - name: nginx 
        image: nginx:1.17.1 
        ports: 
        - containerPort: 80

Deployment 支持兩種鏡像更新的策略:重建更新和滾動更新(默認),可以通過 strategy 選項進行配置。

strategy: 指定新的Pod替代舊的Pod的策略,支持兩個屬性
  type: 指定策略類型,支持兩種策略
    Recreate:在創建出新的Pod之前會先殺掉所有已經存在的Pod
    RollingUpdate:滾動更新,就是殺死一部分,就啟動一部分,在更新過程中,存在兩個版本的Pod
  rollingUpdate:當type為RollingUpdate的時候生效,用於為rollingUpdate設置參數,支持兩個屬性:
    maxUnavailable:用來指定在升級過程中不可用的Pod的最大數量,默認為25%。
    maxSurge: 用來指定在升級過程中可以超過期望的Pod的最大數量,默認為25%。

Deployment 支持版本升級過程中的暫停、繼續功能以及版本回退等諸多功能,下面具體來看:

# 版本升級相關功能
kubetl rollout 參數 deploy xx  # 支持下面的選擇
# status 顯示當前升級的狀態
# history 顯示升級歷史記錄
# pause 暫停版本升級過程
# resume 繼續已經暫停的版本升級過程
# restart 重啟版本升級過程
# undo 回滾到上一级版本 (可以使用--to-revision回滾到指定的版本)

deployment 之所以能夠實現版本的回退,就是通過記錄下歷史的 ReplicaSet 來實現的,一旦想回滾到那個版本,只需要將當前版本的 Pod 數量降為 0,然後將回退版本的 Pod 提升為目標數量即可。

金絲雀發布#

Deployment 支持更新過程中的控制,如暫停更新操作(pause)或繼續更新操作(resume)。
例如有一批新的 Pod 資源創建完成後立即暫停更新過程,此時,僅存在一部分新版本的應用,主體部分還是舊的版本。然後,再篩選一小部分的用戶請求到新版本的 Pod 應用,繼續觀察能夠穩定的按照期望的方式運行,如果沒有問題之後再繼續完成餘下的 Pod 資源的滾動更新,否則立即回滾操作。

Horizontal Pod Autoscaler(HPA)#

我們已經可以通過手動執行 kubectl scale 命令實現 Pod 的擴縮容,但是這顯然不符合 kubernetes 的定位目標–自動化和智能化。kubernetes 期望可以通過監測 Pod 的使用情況,實現 Pod 數量的自動調整,於是就產生了 HPA 這種控制器。
HPA 可以獲取每個 Pod 的利用率,然後和 HPA 中定義的指標進行對比,同時計算出需要伸縮的具體值,最後實現 Pod 的數量的調整。其實 HPA 和之前的 Deployment 一樣,也屬於一種 kubernetes 資源對象,它通過追蹤分析目標 Pod 的負載變化情況,來確定是否需要針對性的調整目標 Pod 的副本數。

5

若集群中沒有收集資源使用情況的程序,可選擇安裝 metrics-server

測試示例:

apiVersion: autoscaling/v1 # 版本號
kind: HorizontalPodAutoscaler # 類型
metadata: # 元數據
  name: pc-hpa # deployment的名稱
  namespace: dev # 命名類型
spec:
  minReplicas: 1 # 最小Pod數量
  maxReplicas: 10 # 最大Pod數量
  targetCPUUtilizationPercentage: 3 # CPU使用率指標
  scaleTargetRef:  # 指定要控制的Nginx的信息
    apiVersion: apps/v1
    kind: Deployment
    name: nginx

DaemonSet(DS)#

DaemonSet 類型的控制器可以保證集群中的每一台(或指定)節點上都運行一個副本,一般適用於日誌收集、節點監控等場景。也就是說,如果一個 Pod 提供的功能是節點級別的(每個節點都需要且只需要一個),那麼這類 Pod 就適合使用 DaemonSet 類型的控制器創建。

DaemonSet 控制器的特點:

  • 每向集群中添加一個節點的時候,指定的 Pod 副本也將添加到該節點上。
  • 當節點從集群中移除的時候,Pod 也會被垃圾回收。

DaemonSet 的資源清單:

apiVersion: apps/v1 # 版本號
kind: DaemonSet # 類型
metadata: # 元數據
  name: # 名稱
  namespace: #命名空間
  labels: #標籤
    controller: daemonset
spec: # 詳情描述
  revisionHistoryLimit: 3 # 保留歷史版本
  updateStrategy: # 更新策略
    type: RollingUpdate # 滾動更新策略
    rollingUpdate: # 滾動更新
      maxUnavailable: 1 # 最大不可用狀態的Pod的最大值,可用為百分比,也可以為整數
  selector: # 選擇器,通過它指定該控制器管理那些Pod
    matchLabels: # Labels匹配規則
      app: nginx-pod
    matchExpressions: # Expressions匹配規則
      - key: app
        operator: In
        values:
          - nginx-pod
  template: # 模板,當副本數量不足時,會根據下面的模板創建Pod模板
     metadata:
       labels:
         app: nginx-pod
     spec:
       containers:
         - name: nginx
           image: nginx:1.17.1
           ports:
             - containerPort: 80

Job#

Job 主要用於負責批量處理短暫的一次性任務。
Job 的特點:

  • 當 Job 創建的 Pod 執行成功結束時,Job 將記錄成功結束的 Pod 數量。
  • 當成功結束的 Pod 達到指定的數量時,Job 將完成執行。

Job 可以保證指定數量的 Pod 執行完成。

Job 的資源清單:

apiVersion: batch/v1 # 版本號
kind: Job # 類型
metadata: # 元數據
  name:  # 名稱
  namespace:  #命名空間
  labels: # 標籤
    controller: job
spec: # 詳情描述
  completions: 1 # 指定Job需要成功運行Pod的總次數,默認為1
  parallelism: 1 # 指定Job在任一時刻應該並發運行Pod的數量,默認為1
  activeDeadlineSeconds: 30 # 指定Job可以運行的時間期限,超過時間還沒結束,系統將會嘗試進行終止
  backoffLimit: 6 # 指定Job失敗後進行重試的次數,默認為6
  manualSelector: true # 是否可以使用selector選擇器選擇Pod,默認為false
  selector: # 選擇器,通過它指定該控制器管理那些Pod
    matchLabels: # Labels匹配規則
      app: counter-pod
    matchExpressions: # Expressions匹配規則
      - key: app
        operator: In
        values:
          - counter-pod
  template: # 模板,當副本數量不足時,會根據下面的模板創建Pod模板
     metadata:
       labels:
         app: counter-pod
     spec:
       restartPolicy: Never # 重啟策略只能設置為Never或OnFailure
       containers:
         - name: counter
           image: busybox:1.30
           command: ["/bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1;do echo $i;sleep 20;done"]

關於模板中的重啟策略的說明:

  • 如果設置為 OnFailure,則 Job 會在 Pod 出現故障的時候重啟容器,而不是創建 Pod,failed 次數不變。
  • 如果設置為 Never,則 Job 會在 Pod 出現故障的時候創建新的 Pod,並且故障 Pod 不會消失,也不會重啟,failed 次數 + 1。
  • 如果指定為 Always 的話,就意味著一直重啟,意味著 Pod 任務會重複執行,這和 Job 的定義衝突,所以不能設置為 Always。

CronJob(CJ)#

CronJob 控制器以 Job 控制器資源為其管控對象,並借助它管理 pod 資源對象,Job 控制器定義的作業任務在其控制器資源創建之後便會立即執行,但 CronJob 可以以類似於 Linux 操作系統的周期性任務作業計劃的方式控制其運行時間點重複運行的方式。也就是說,CronJob 可以在特定的時間點 (反復的) 去運行 job 任務

CronJob 的資源清單文件:

apiVersion: batch/v1beta1 # 版本號
kind: CronJob # 類型       
metadata: # 元數據
  name: # rs名稱 
  namespace: # 所屬命名空間 
  labels: #標籤
    controller: cronjob
spec: # 詳情描述
  schedule: # cron格式的作業調度運行時間點,用於控制任務在什麼時間執行
  concurrencyPolicy: # 並發執行策略,用於定義前一次作業運行尚未完成時是否以及如何運行後一次的作業
  failedJobHistoryLimit: # 為失敗的任務執行保留的歷史記錄數,默認為1
  successfulJobHistoryLimit: # 為成功的任務執行保留的歷史記錄數,默認為3
  startingDeadlineSeconds: # 啟動作業錯誤的超時時長
  jobTemplate: # job控制器模板,用於為cronjob控制器生成job對象;下面其實就是job的定義
    metadata:
    spec:
      completions: 1
      parallelism: 1
      activeDeadlineSeconds: 30
      backoffLimit: 6
      manualSelector: true
      selector:
        matchLabels:
          app: counter-pod
        matchExpressions: 規則
          - {key: app, operator: In, values: [counter-pod]}
      template:
        metadata:
          labels:
            app: counter-pod
        spec:
          restartPolicy: Never 
          containers:
          - name: counter
            image: busybox:1.30
            command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]
需要重點解釋的幾個選項:
schedule: cron表達式,用於指定任務的執行時間
    */1    *      *    *     *
    <分鐘> <小時> <日> <月份> <星期>

    分鐘 值從 0 到 59.
    小時 值從 0 到 23.
    日 值從 1 到 31.
    月 值從 1 到 12.
    星期 值從 0 到 6, 0 代表星期日
    多個時間可以用逗號隔開; 範圍可以用連字符給出;*可以作為通配符; /表示每...
concurrencyPolicy:
    Allow:   允許Jobs並發運行(默認)
    Forbid:  禁止並發運行,如果上一次運行尚未完成,則跳過下一次運行
    Replace: 替換,取消當前正在運行的作業並用新作業替換它

StatefulSet(有狀態)#

無狀態應用:

  • 認為 Pod 都是一樣的。
  • 沒有順序要求。
  • 不用考慮在哪個 Node 節點上運行。
  • 隨意進行伸縮和擴展。

有狀態應用:

  • 有順序的要求。
  • 認為每個 Pod 都是不一樣的。
  • 需要考慮在哪個 Node 節點上運行。
  • 需要按照順序進行伸縮和擴展。
  • 讓每個 Pod 都是獨立的,保持 Pod 啟動順序和唯一性。

StatefulSet 是 Kubernetes 提供的管理有狀態應用的負載管理控制器。
StatefulSet 部署需要 HeadLinessService(無頭服務)。

為什麼需要 HeadLinessService(無頭服務)?

  • 在用 Deployment 時,每一個 Pod 名稱是沒有順序的,是隨機字符串,因此是 Pod 名稱是無序的,但是在 StatefulSet 中要求必須是有序 ,每一個 Pod 不能被隨意取代,Pod 重建後 pod 名稱還是一樣的。
  • 而 Pod IP 是變化的,所以是以 Pod 名稱來識別。Pod 名稱是 Pod 唯一性的標識符,必須持久穩定有效。這時候要用到無頭服務,它可以給每個 Pod 一個唯一的名稱 。

StatefulSet 常用來部署 RabbitMQ 集群、Zookeeper 集群、MySQL 集群、Eureka 集群等。

演示示例:

apiVersion: v1
kind: Service
metadata:
  name: service-headliness
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None # 將clusterIP設置為None,即可創建headliness Service
  type: ClusterIP
  ports:
    - port: 80 # Service的端口
      targetPort: 80 # Pod的端口
...

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: pc-statefulset
  namespace: dev
spec:
  replicas: 3
  serviceName: service-headliness
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
        - name: nginx
          image: nginx:1.17.1
          ports:
            - containerPort: 80

Service#

在 kubernetes 中,Pod 是應用程序的載體,我們可以通過 Pod 的 IP 來訪問應用程序,但是 Pod 的 IP 地址不是固定的,這就意味著不方便直接采用 Pod 的 IP 對服務進行訪問。
為了解決這個問題,kubernetes 提供了 Service 資源,Service 會對提供同一個服務的多個 Pod 進行聚合,並且提供一個統一的入口地址,通過訪問 Service 的入口地址就能訪問到後面的 Pod 服務。

Service 在很多情況下只是一個概念,真正起作用的其實是 kube-proxy 服務進程,每個 Node 節點上都運行了一個 kube-proxy 的服務進程。當創建 Service 的時候會通過 API Server 向 etcd 寫入創建的 Service 的信息,而 kube-proxy 會基於監聽的機制發現這種 Service 的變化,然後它會將最新的 Service 信息轉換為對應的訪問規則。

9

kube-proxy 目前支持三種工作模式:

  • userspace 模式:

    • userspace 模式下,kube-proxy 會為每一個 Service 創建一個監聽端口,發向 Cluster IP 的請求被 iptables 規則重定向到 kube-proxy 監聽的端口上,kube-proxy 根據 LB 算法(負載均衡算法)選擇一個提供服務的 Pod 並和其建立連接,以便將請求轉發到 Pod 上。

    • 該模式下,kube-proxy 充當了一個四層負載均衡器的角色。由於 kube-proxy 運行在 userspace 中,在進行轉發處理的時候會增加內核和用戶空間之間的數據拷貝,雖然比較穩定,但是效率非常低下。

10

  • iptables 模式:

    • iptables 模式下,kube-proxy 為 Service 後端的每個 Pod 創建對應的 iptables 規則,直接將發向 Cluster IP 的請求重定向到一個 Pod 的 IP 上。

    • 該模式下 kube-proxy 不承擔四層負載均衡器的角色,只負責創建 iptables 規則。該模式的優點在於較 userspace 模式效率更高,但是不能提供靈活的 LB 策略,當後端 Pod 不可用的時候無法進行重試。

11

ipvs 模式:

  • ipvs 模式和 iptables 類似,kube-proxy 監控 Pod 的變化並創建相應的 ipvs 規則。ipvs 相對 iptables 轉發效率更高,除此之外,ipvs 支持更多的 LB(負載均衡)算法。

12

Service 類型#

Service 的資源清單:

apiVersion: v1 # 版本
kind: Service # 類型
metadata: # 元數據
  name: # 資源名稱
  namespace: # 命名空間
spec:
  selector: # 標籤選擇器,用於確定當前Service代理那些Pod
    app: nginx
  type: NodePort # Service的類型,指定Service的訪問方式
  clusterIP: # 虛擬服務的IP地址
  sessionAffinity: # session親和性,支持ClientIP、None兩個選項,默認值為None
  ports: # 端口信息
    - port: 8080 # Service端口
      protocol: TCP # 協議
      targetPort : # Pod端口
      nodePort:  # 主機端口

spec.type 的說明:

  • ClusterIP:默認值,它是 kubernetes 系統自動分配的虛擬 IP,只能在集群內部訪問。
  • NodePort:將 Service 通過指定的 Node 上的端口暴露給外部,通過此方法,就可以在集群外部訪問服務。
  • LoadBalancer:使用外接負載均衡器完成到服務的負載分發,注意此模式需要外部雲環境的支持。
  • ExternalName:把集群外部的服務引入集群內部,直接使用。

ClusterIP 類型的 Service#

Endpoint(實際中使用的不多)

  • Endpoint 是 kubernetes 中的一個資源對象,存儲在 etcd 中,用來記錄一個 service 對應的所有 Pod 的訪問地址,它是根據 service 配置文件中的 selector 描述產生的。
  • 一個 service 由一組 Pod 組成,這些 Pod 通過 Endpoints 暴露出來,Endpoints 是實現實際服務的端點集合。換言之,service 和 Pod 之間的聯繫是通過 Endpoints 實現的。

13

負載分發策略

對 Service 的訪問被分發到了後端的 Pod 上去,目前 kubernetes 提供了兩種負載分發策略:

  • 如果不定義,默認使用 kube-proxy 的策略,比如隨機、輪詢等。
  • 基於客戶端地址的會話保持模式,即來自同一個客戶端發起的所有請求都會轉發到固定的一个 Pod 上,這對於傳統基於 Session 的認證項目來說很友好,此模式可以在 spec 中添加 sessionAffinity: ClusterIP 選項。

HeadLiness 類型的 Service#

在某些場景中,開發人員可能不想使用 Service 提供的負載均衡功能,而希望自己來控制負載均衡策略,針對這種情況,kubernetes 提供了 HeadLinesss Service,這類 Service 不會分配 Cluster IP,如果想要訪問 Service,只能通過 Service 的域名進行查詢。

NodePort 類型的 Service#

在之前的案例中,創建的 Service 的 IP 地址只能在集群內部才可以訪問,如果希望 Service 暴露給集群外部使用,那麼就需要使用到另外一種類型的 Service,稱為 NodePort 類型的 Service。NodePort 的工作原理就是將 Service 的端口映射到 Node 的一個端口上,然後就可以通過 NodeIP來訪問 Service 了。

14

LoadBalancer 類型的 Service#

15

ExternalName 類型的 Service#

ExternalName 類型的 Service 用於引入集群外部的服務,它通過 externalName 屬性指定一個服務的地址,然後在集群內部訪問此 Service 就可以訪問到外部的服務了。

16

Ingress#

我們已經知道,Service 對集群之外暴露服務的主要方式有兩種:NodePort 和 LoadBalancer,但是這兩種方式,都有一定的缺點:

  • NodePort 方式的缺點是會佔用很多集群機器的端口,那麼當集群服務變多的時候,這個缺點就愈發明顯。
  • LoadBalancer 的缺點是每個 Service 都需要一個 LB,浪費,麻煩,並且需要 kubernetes 之外的設備的支持。

基於這種現狀,kubernetes 提供了 Ingress 資源對象,Ingress 只需要一個 NodePort 或者一個 LB 就可以滿足暴露多個 Service 的需求,工作機制大致如下圖所示:

17

實際上,Ingress 相當於一個七層的負載均衡器,是 kubernetes 對反向代理的一個抽象,它的工作原理類似於 Nginx,可以理解為 Ingress 裡面建立了諸多映射規則,Ingress Controller 通過監聽這些配置規則並轉化為 Nginx 的反向代理配置,然後對外提供服務。

  • Ingress:kubernetes 中的一個對象,作用是定義請求如何轉發到 Service 的規則。
  • Ingress Controller:具體實現反向代理及負載均衡的程序,對 Ingress 定義的規則進行解析,根據配置的規則來實現請求轉發,實現的方式有很多,比如 Nginx,Contour,Haproxy 等。

Ingress(以 Nginx)的工作原理如下:

  • 用戶編寫 Ingress 規則,說明那個域名對應 kubernetes 集群中的那個 Service。
  • Ingress 控制器動態感知 Ingress 服務規則的變化,然後生成一段對應的 Nginx 的反向代理配置。
  • Ingress 控制器會將生成的 Nginx 配置寫入到一個運行著的 Nginx 服務中,並動態更新。
  • 到此為止,其實真正在工作的就是一個 Nginx 了,內部配置了用戶定義的請求規則。

18

Ingress 支持 Http 代理和 Https 代理

數據存儲#

在前面已經提到,容器的生命週期可能很短,會被頻繁地創建和銷毀。那么容器在銷毀時,保存在容器中的數據也會被清除。這種結果對用戶來說,在某些情況下是不樂意看到的。為了持久化保存容器的數據,kubernetes 引入了 Volume 的概念。

Volume 是 Pod 中能夠被多個容器訪問的共享目錄,它被定義在 Pod 上,然後被一個 Pod 裡的多個容器掛載到具體的文件目錄下,kubernetes 通過 Volume 實現同一個 Pod 中不同容器之間的數據共享以及數據的持久化存儲。Volume 的生命容器不與 Pod 中單個容器的生命週期相關,當容器終止或者重啟時,Volume 中的數據也不會丟失。

kubernetes 的 Volume 支持多種類型,比較常見的有下面幾個:

  • 簡單存儲:EmptyDir、HostPath、NFS
  • 高級存儲:PV、PVC
  • 配置存儲:ConfigMap、Secret

基本存儲#

EmptyDir#

EmptyDir 是最基礎的 Volume 類型,一個 EmptyDir 就是 Host 上的一個空目錄。

EmptyDir 是在 Pod 被分配到 Node 時創建的,它的初始內容為空,並且無須指定宿主機上對應的目錄文件,因為 kubernetes 會自動分配一個目錄,當 Pod 銷毀時, EmptyDir 中的數據也會被永久刪除。 EmptyDir 用途如下:

  • 臨時空間,例如用於某些應用程序運行時所需的臨時目錄,且無須永久保留
  • 一個容器需要從另一個容器中獲取數據的目錄(多容器共享目錄)

接下來,通過一個容器之間文件共享的案例來使用一下 EmptyDir。

在一個 Pod 中準備兩個容器 nginx 和 busybox,然後聲明一個 Volume 分別掛在到兩個容器的目錄中,然後 nginx 容器負責向 Volume 中寫日誌,busybox 中通過命令將日誌內容讀到控制台。

19

HostPath#

上節課提到,EmptyDir 中數據不會被持久化,它會隨著 Pod 的結束而銷毀,如果想簡單的將數據持久化到主機中,可以選擇 HostPath。

HostPath 就是將 Node 主機中一個實際目錄掛在到 Pod 中,以供容器使用,這樣的設計就可以保證 Pod 銷毀了,但是數據依據可以存在於 Node 主機上。

20

NFS#

HostPath 可以解決數據持久化的問題,但是一旦 Node 節點故障了,Pod 如果轉移到了別的節點,又會出現問題了,此時需要準備單獨的網絡存儲系統,比較常用的用 NFS、CIFS。

NFS 是一個網絡文件存儲系統,可以搭建一台 NFS 服務器,然後將 Pod 中的存儲直接連接到 NFS 系統上,這樣的話,無論 Pod 在節點上怎麼轉移,只要 Node 跟 NFS 的對接沒問題,數據就可以成功訪問。

21

高級存儲#

前面已經學習了使用 NFS 提供存儲,此時就要求用戶會搭建 NFS 系統,並且會在 yaml 配置 nfs。由於 kubernetes 支持的存儲系統有很多,要求客戶全都掌握,顯然不現實。為了能夠屏蔽底層存儲實現的細節,方便用戶使用, kubernetes 引入 PV 和 PVC 兩種資源對象。

  • PV(Persistent Volume)是持久化卷的意思,是對底層的共享存儲的一種抽象。一般情況下 PV 由 kubernetes 管理員進行創建和配置,它與底層具體的共享存儲技術有關,並通過插件完成與共享存儲的對接。
  • PVC(Persistent Volume Claim)是持久卷聲明的意思,是用戶對存儲需求的一種聲明。換句話說,PVC 其實就是用戶向 kubernetes 系統發出的一種資源需求申請。

22

使用了 PV 和 PVC 之後,工作可以得到進一步的細分:

  • 存儲:存儲工程師維護
  • PV: kubernetes 管理員維護
  • PVC:kubernetes 用戶維護

PV#

PV 是存儲資源的抽象,下面是資源清單文件:

apiVersion: v1  
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存儲類型,與底層真正存儲對應
  capacity:  # 存儲能力,目前只支持存儲空間的設置
    storage: 2Gi
  accessModes:  # 訪問模式
  storageClassName: # 存儲類別
  persistentVolumeReclaimPolicy: # 回收策略

PV 的關鍵配置參數說明:

  • 存儲類型

    底層實際存儲的類型,kubernetes 支持多種存儲類型,每種存儲類型的配置都有所差異

  • 存儲能力(capacity)

目前只支持存儲空間的設置 (storage=1Gi),不過未來可能會加入 IOPS、吞吐量等指標的配置

  • 訪問模式(accessModes)

    用於描述用戶應用對存儲資源的訪問權限,訪問權限包括下面幾種方式:

    • ReadWriteOnce(RWO):讀寫權限,但是只能被單個節點掛載
    • ReadOnlyMany(ROX): 只讀權限,可以被多個節點掛載
    • ReadWriteMany(RWX):讀寫權限,可以被多個節點掛載

    需要注意的是,底層不同的存儲類型可能支持的訪問模式不同

  • 回收策略(persistentVolumeReclaimPolicy)

    當 PV 不再被使用了之後,對其的處理方式。目前支持三種策略:

    • Retain (保留) 保留數據,需要管理員手工清理數據
    • Recycle(回收) 清除 PV 中的數據,效果相當於執行 rm -rf /thevolume/*
    • Delete (刪除) 與 PV 相連的後端存儲完成 volume 的刪除操作,當然這常見於雲服務商的存儲服務

    需要注意的是,底層不同的存儲類型可能支持的回收策略不同

  • 存儲類別

    PV 可以通過 storageClassName 參數指定一個存儲類別

    • 具有特定類別的 PV 只能與請求了該類別的 PVC 進行綁定
    • 未設定類別的 PV 則只能與不請求任何類別的 PVC 進行綁定
  • 狀態(status)

    一個 PV 的生命週期中,可能會處於 4 中不同的階段:

    • Available(可用): 表示可用狀態,還未被任何 PVC 綁定
    • Bound(已綁定): 表示 PV 已經被 PVC 綁定
    • Released(已釋放): 表示 PVC 被刪除,但是資源還未被集群重新聲明
    • Failed(失敗): 表示該 PV 的自動回收失敗

PVC#

PVC 是資源的申請,用來聲明對存儲空間、訪問模式、存儲類別需求信息。下面是資源清單文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
  namespace: dev
spec:
  accessModes: # 訪問模式
  selector: # 采用標籤對PV選擇
  storageClassName: # 存儲類別
  resources: # 請求空間
    requests:
      storage: 5Gi

PVC 的關鍵配置參數說明:

  • 訪問模式(accessModes)

用於描述用戶應用對存儲資源的訪問權限

  • 選擇條件(selector)

    通過 Label Selector 的設置,可使 PVC 對於系統中已存在的 PV 進行篩選

  • 存儲類別(storageClassName)

    PVC 在定義時可以設置需要的後端存儲的類別,只有設置了該 class 的 pv 才能被系統選出

  • 資源請求(Resources )

    描述對存儲資源的請求

生命週期#

PVC 和 PV 是一一對應的,PV 和 PVC 之間的相互作用遵循以下生命週期:

  • 資源供應:管理員手動創建底層存儲和 PV

  • 資源綁定:用戶創建 PVC,kubernetes 負責根據 PVC 的聲明去尋找 PV,並綁定

    在用戶定義好 PVC 之後,系統將根據 PVC 對存儲資源的請求在已存在的 PV 中選擇一個滿足條件的

    • 一旦找到,就將該 PV 與用戶定義的 PVC 進行綁定,用戶的應用就可以使用這個 PVC 了
    • 如果找不到,PVC 則會無限期處於 Pending 狀態,直到等到系統管理員創建了一個符合其要求的 PV

    PV 一旦綁定到某個 PVC 上,就會被這個 PVC 獨占,不能再與其他 PVC 進行綁定了

  • 資源使用:用戶可在 pod 中像 volume 一樣使用 p

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。