KUBERNETES之Kubernetes Workloads(Kubernetes 作業管理)三

2.Controller

Kubernetes通常不會直接創建Pod,而是通過Controller來管理Pod。Controller中定義了Pod的部署特性,比如有幾個副本、在什麼樣的Node上運行等,爲了滿足不通的業務場景,Kubernetes提供了多種Controller。

 

2.1 控制器的實現

確保攜帶了 app=nginx 標籤的 Pod 的個數,永遠等於 spec.replicas 指定的個數,即 2 個。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

測試
在這裏插入圖片描述

 

2.1.1 Deployment的實現
  • Deployment 控制器從 Etcd 中獲取到所有攜帶了“app: nginx”標籤的 Pod,然後統計它們的數量,這就是實際狀態;

  • Deployment 對象的 Replicas 字段的值就是期望狀態;

  • Deployment 控制器將兩個狀態做比較,然後根據比較結果,確定是創建 Pod,還是刪除已有的 Pod

控制循環(control loop)

go僞代碼描述

for {
  實際狀態 := 獲取集羣中對象X的實際狀態(Actual State)
  期望狀態 := 獲取集羣中對象X的期望狀態(Desired State)
  if 實際狀態 == 期望狀態{
    什麼都不做
  } else {
    執行編排動作,將實際狀態調整爲期望狀態
  }
}

 

2.1.2 總結

在這裏插入圖片描述

  • 很多不同類型的容器編排功能,比如 StatefulSet、DaemonSet 等等,它們無一例外地都有這樣一個甚至多個控制器的存在,並遵循控制循環(control loop)的流程,完成各自的編排邏輯。
  • 跟 Deployment 相似,這些控制循環最後的執行結果,要麼就是創建、更新一些 Pod(或者其他的 API 對象、資源),要麼就是刪除一些已經存在的 Pod(或者其他的 API 對象、資源)。
  • 也正是在這個統一的編排框架下,不同的控制器可以在具體執行過程中,設計不同的業務邏輯,從而達到不同的編排效果。

 

2.2 ReplicaSet(容器副本集)

實現Pod的多副本管理

 

2.2.1 結構

一個 ReplicaSet 對象,其實就是由副本數目的定義和一個 Pod 模板組成的。不難發現,它的定義其實是 Deployment 的一個子集。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-set
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9

 

2.3 Deployment(常規作業發佈)

可以管理多個ReplicaSet,並確保Pod按照期望的狀態運行

 

2.3.1 結構

在這裏插入圖片描述

實現

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

測試

在這裏插入圖片描述

 

2.3.2 水平擴展/收縮
  • 指令方式擴展
kubectl scale deployment nginx-deployment --replicas=4

測試

在這裏插入圖片描述

收縮

在這裏插入圖片描述

  • 修改yaml文件方式擴展

修改yaml文件中的replicas: 3,,,,改成replicas: 4

kubectl apply -f nginx-deployment.yaml 

測試

在這裏插入圖片描述

 

2.3.3 Rolling update(自動的滾動更新)

將一個集羣中正在運行的多個 Pod 版本,交替地逐一升級的過程,就是“滾動更新”

實現過程

在這裏插入圖片描述

 

1.創建deployment

kubectl create -f nginx-deployment.yaml --record

 

2.檢查狀態

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         0         0            0           1s
  • DESIRED:用戶期望的 Pod 副本個數(spec.replicas 的值)
  • CURRENT:當前處於 Running 狀態的 Pod 的個數
  • UP-TO-DATE:當前處於最新版本的 Pod 的個數,所謂最新版本指的是 Pod 的 Spec 部分與 Deployment 裏 Pod 模板裏定義的完全一致
  • AVAILABLE:當前已經可用的 Pod 的個數,即:既是 Running 狀態,又是最新版本,並且已經處於 Ready(健康檢查正確)狀態的 Pod 的個數
  • kubectl rollout status deployment/nginx-deployment
  • kubectl get rs
  • 相比之下,Deployment 只是在 ReplicaSet 的基礎上,添加了 UP-TO-DATE 這個跟版本有關的狀態字段

 

3.修改pod模板,觸發滾動更新

修改方式

修改方式

第一種:kubectl edit
第二種:kubectl set image
kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1

測試

在這裏插入圖片描述

 

4.查看滾動更新過程

kubectl describe deployment nginx-deployment
  • 新 ReplicaSet 管理的 Pod 副本數,從 0 個變成 1 個,再變成 2 個,最後變成 3 個。而舊的 ReplicaSet 管理的 Pod 副本數則從 3 個變成 2 個,再變成 1 個,最後變成 0 個。這樣,就完成了這一組 Pod 的版本升級過程。

  • 默認離線pod的比例爲Desired指的25%

在這裏插入圖片描述

 

2.3.4 pause、resume(可控的更新流程)

對 Deployment 進行的每一次更新操作,都會生成一個新的 ReplicaSet 對象,是不是有些多餘,甚至浪費資源呢?

  • 解決方案
kubectl rollout pause deployment/nginx-deployment
kubectl rollout resume deployment/nginx-deployment

結果

[root@kubernetes01 ~]# kubectl get deployment -o wide
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES        SELECTOR
nginx-deployment   3         4         1            3           62m   nginx        nginx:1.7.1   app=nginx
[root@kubernetes01 ~]# kubectl rollout pause deployment/nginx-deployment
[root@kubernetes01 ~]# kubectl get deployment -o wide
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES        SELECTOR
nginx-deployment   3         3         3            3           79m   nginx        nginx:1.7.1   app=nginx

這裏會發現 CURRENT少了一個

  • 說明

1.這個 kubectl rollout pause 的作用,是讓這個 Deployment 進入了一個“暫停”狀態。所以接下來,你就可以隨意使用 kubectl edit 或者 kubectl set image 指令,修改這個 Deployment 的內容了。

2.由於此時 Deployment 正處於“暫停”狀態,所以我們對 Deployment 的所有修改,都不會觸發新的“滾動更新”,也不會創建新的 ReplicaSet。

3.而等到我們對 Deployment 修改操作都完成之後,只需要再執行一條 kubectl rollout resume 指令,就可以把這個 Deployment“恢復”回來

 

2.3.5 Rollback(版本回滾)

回滾過程

1.修改版本

kubectl set image deployment/nginx-deployment nginx=nginx:1.8.1

2.查看rs信息

kubectl get rs -o wide

測試

在這裏插入圖片描述
手速快的話還可以看到兩個版本的更新的過程

會發現兩個版本之間是一點一點更新的

 

3.回滾到上一個版本

kubectl rollout undo deployment/nginx-deployment

測試

在這裏插入圖片描述

知識點:

查看變更版本

kubectl rollout history deployment/nginx-deployment

測試

在這裏插入圖片描述

回退到指定版本

$ kubectl rollout undo deployment/nginx-deployment --to-revision=2

限制版本歷史記錄

Deployment對象的spec.revisionHistoryLimit

 

2.3.6 其他發佈方式

1.Canary Deployment(金絲雀發佈)

金絲雀部署:優先發布一臺或少量機器升級,等驗證無誤後再更新其他機器。優點是用戶影響範圍小,不足之處是要額外控制如何做自動更新。

  • 準備好部署各個階段的工件,包括:構建工件,測試腳本,配置文件和部署清單文件。
  • 從負載均衡列表中移除掉“金絲雀”服務器。
  • 升級“金絲雀”應用(排掉原有流量並進行部署)。
  • 對應用進行自動化測試。
  • 將“金絲雀”服務器重新添加到負載均衡列表中(連通性和健康檢查)。
  • 如果“金絲雀”在線使用測試成功,升級剩餘的其他服務器。(否則就回滾)

2.Blue-Green Deployment(藍綠髮布)
藍綠部署:2組機器,藍代表當前的V1版本,綠代表已經升級完成的V2版本。通過LB將流量全部導入V2完成升級部署。優點是切換快速,缺點是影響全部用戶。

  • 部署版本1的應用(一開始的狀態)
  • 所有外部請求的流量都打到這個版本上。
  • 部署版本2的應用
    版本2的代碼與版本1不同(新功能、Bug修復等)。
  • 將流量從版本1切換到版本2。
  • 如版本2測試正常,就刪除版本1正在使用的資源(例如實例),從此正式用版本2。

 

2.4 StatefulSet(有狀態任務)

無狀態的應用pod都是完全一樣,沒有順序,沒有關係
有狀態的應用保存拓撲狀態和存儲狀態

 

2.4.1 Topology State(拓撲狀態)

應用的多個實例之間不是完全對等的關係。這些應用實例,必須按照某些順序啓動,使用Headless Service維持Pod的拓撲狀態

1.知識儲備

Service 訪問方式

  • VIP

(Virtual IP,即:虛擬 IP)當我訪問 10.0.23.1 這個 Service 的 IP 地址時,10.0.23.1 其實就是一個 VIP,它會把請求轉發到該 Service 所代理的某一個 Pod 上。

  • DNS

  • 區別在於,Headless Service 不需要分配一個 VIP,而是可以直接以 DNS 記錄的方式解析出被代理 Pod 的 IP 地址。

    • Normal Service

      • 訪問“my-svc.my-namespace.svc.cluster.local”解析到的,正是 my-svc 這個 Service 的 VIP

      • 例子
        apiVersion: v1
        kind: Service
        metadata:
          name: nginx
          labels:
            app: nginx
        spec:
          ports:
          - port: 80
            name: web
          clusterIP: 10.96.0.2
          selector:
            app: nginx
        
    • Headless Service

      • 訪問“my-svc.my-namespace.svc.cluster.local”解析到的,直接就是 my-svc 代理的某一個 Pod 的 IP 地址。

      • 實戰

      • headless.yaml
        
        apiVersion: v1
        kind: Service
        metadata:
          name: nginx
          labels:
            app: nginx
        spec:
          ports:
          - port: 80
            name: web
          clusterIP: None
          selector:
            app: nginx
        
      • 創建service

      • Pod DNS名稱格式:...svc.cluster.local

      • kube apply -f headless.yaml

      • 這個“可解析身份”,只要你知道了一個 Pod 的名字,以及它對應的 Service 的名字,你就可以非常確定地通過這條 DNS 記錄訪問到 Pod 的 IP 地址。

Normal Service測試

在這裏插入圖片描述

Headless Service測試

在這裏插入圖片描述

 
2.實戰

stateful.yaml

比起Deployment多了一個serviceName: "nginx"字段,這個字段的作用,就是告訴 StatefulSet 控制器,在執行控制循環(Control Loop)的時候,請使用 nginx 這個 Headless Service 來保證 Pod 的“可解析身份”。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.1
        ports:
        - containerPort: 80
          name: web

總共運行兩個yaml文件(headless.yaml、stateful.yaml)

headless.yaml文件是dns服務,解析

stateful.yaml文件是StatefulSet類型兩個pod

  • 監控pod創建過程
kubectl apply -f stateful.yaml
kubect get pod -w 

測試

在這裏插入圖片描述

web-1

在這裏插入圖片描述

  • 查看pod的主機名
kubectl exec web-0 -- sh -c hostname
  • 查看pod的hosts
kubectl exec -it  web-0 -- sh 

測試

這裏重啓了一下stateful.yaml,所以ip變了。

在這裏插入圖片描述

  • 創建一個一次性的pod訪問應用
kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh 
nslookup web-0.nginx
nslookup web-1.nginx

測試

在這裏插入圖片描述

  • 刪除兩個pod
kubectl delete pod -l app=nginx
  • 監控pod恢復過程
kubect get pod -w 

測試

在這裏插入圖片描述

再次查看dns映射關係

  • 總結

Kubernetes 成功地將 Pod 的拓撲狀態(比如:哪個節點先啓動,哪個節點後啓動),按照 Pod 的“名字 + 編號”的方式固定了下來。

 

2.4.2 Storage State(存儲狀態)

應用的多個實例分別綁定了不同的存儲數據。

1.知識儲備

Kubernetes 項目引入了一組叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 對象,大大降低了用戶聲明和使用持久化 Volume 的門檻。

https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes

  • Ceph基礎

Ceph是一個分佈式存儲系統,誕生於2004年,最早致力於開發下一代高性能分佈式文件系統的項目。隨着雲計算的發展,ceph乘上了OpenStack的春風,進而成爲了開源社區受關注較高的項目之一。

    • 優勢

    • CRUSH算法

      ceph摒棄了傳統的集中式存儲元數據尋址的方案,轉而使用CRUSH算法完成數據的尋址操作。CRUSH在一致性哈希基礎上很好的考慮了容災域的隔離,能夠實現各類負載的副本放置規則,例如跨機房、機架感知等。Crush算法有相當強大的擴展性,理論上支持數千個存儲節點。

    • 高可用

      Ceph中的數據副本數量可以由管理員自行定義,並可以通過CRUSH算法指定副本的物理存儲位置以分隔故障域,支持數據強一致性; ceph可以忍受多種故障場景並自動嘗試並行修復。

    • 高擴展性

      Ceph不同於swift,客戶端所有的讀寫操作都要經過代理節點。一旦集羣併發量增大時,代理節點很容易成爲單點瓶頸。Ceph本身並沒有主控節點,擴展起來比較容易,並且理論上,它的性能會隨着磁盤數量的增加而線性增長。

    • 特性豐富

      Ceph支持三種調用接口:對象存儲,塊存儲,文件系統掛載。三種方式可以一同使用。在國內一些公司的雲環境中,通常會採用ceph作爲openstack,k8s的唯一後端存儲來提升數據轉發效率。

    • 基本組件

在這裏插入圖片描述

  • RADOS

Ceph的底層是RADOS,RADOS本身也是分佈式存儲系統,CEPH所有的存儲功能都是基於RADOS實現。

  • RADOS GateWay

RADOS GW是一個提供與Amazon S3和Swift兼容的RESTful API的gateway,以供相應的對象存儲應用開發使用。

  • RBD

RBD則提供了一個標準的塊設備接口,常用於在虛擬化的場景下爲虛擬機創建volume。

  • CEPHFS

CEPHFS提供了POSIX接口,用戶可直接通過客戶端掛載使用。它是內核態的程序,所以無需調用用戶空間的librados庫。它通過內核中的net模塊來與Rados進行交互。需要與MDS配合。

    • 基礎架構

在這裏插入圖片描述

Ceph主要有三個基本進程:mon、osd、mds

  • Osd

    • 用於集羣中所有數據與對象的存儲。處理集羣數據的複製、恢復、回填、再均衡。並向其他osd守護進程發送心跳,然後向Mon提供一些監控信息。

    • 當Ceph存儲集羣設定數據有兩個副本時(一共存兩份),則至少需要兩個OSD守護進程即兩個OSD節點,集羣才能達到active+clean狀態。

    • 數據存儲過程

      無論使用哪種存儲方式(對象、塊、掛載),存儲的數據都會被切分成對象(Objects)。Objects size大小可以由管理員調整,通常爲2M或4M。每個對象都會有一個唯一的OID,由ino與ono生成,雖然這些名詞看上去很複雜,其實相當簡單。ino即是文件的File ID,用於在全局唯一標示每一個文件,而ono則是分片的編號。比如:一個文件FileID爲A,它被切成了兩個對象,一個對象編號0,另一個編號1,那麼這兩個文件的oid則爲A0與A1。Oid的好處是可以唯一標示每個不同的對象,並且存儲了對象與文件的從屬關係。由於ceph的所有數據都虛擬成了整齊劃一的對象,所以在讀寫時效率都會比較高。

      但是對象並不會直接存儲進OSD中,因爲對象的size很小,在一個大規模的集羣中可能有幾百到幾千萬個對象。這麼多對象光是遍歷尋址,速度都是很緩慢的;並且如果將對象直接通過某種固定映射的哈希算法映射到osd上,當這個osd損壞時,對象無法自動遷移至其他osd上面(因爲映射函數不允許)。爲了解決這些問題,ceph引入了歸置組的概念,即PG。

      PG是一個邏輯概念,我們linux系統中可以直接看到對象,但是無法直接看到PG。它在數據尋址時類似於數據庫中的索引:每個對象都會固定映射進一個PG中,所以當我們要尋找一個對象時,只需要先找到對象所屬的PG,然後遍歷這個PG就可以了,無需遍歷所有對象。而且在數據遷移時,也是以PG作爲基本單位進行遷移,ceph不會直接操作對象。

      對象時如何映射進PG的?還記得OID麼?首先使用靜態hash函數對OID做hash取出特徵碼,用特徵碼與PG的數量去模,得到的序號則是PGID。由於這種設計方式,PG的數量多寡直接決定了數據分佈的均勻性,所以合理設置的PG數量可以很好的提升CEPH集羣的性能並使數據均勻分佈。

      最後PG會根據管理員設置的副本數量進行復制,然後通過crush算法存儲到不同的OSD節點上(其實是把PG中的所有對象存儲到節點上),第一個osd節點即爲主節點,其餘均爲從節點。

存儲過程

在這裏插入圖片描述

PG placement group

Pool

Pool是管理員自定義的命名空間,像其他的命名空間一樣,用來隔離對象與PG。我們在調用API存儲即使用對象存儲時,需要指定對象要存儲進哪一個POOL中。除了隔離數據,我們也可以分別對不同的POOL設置不同的優化策略,比如副本數、數據清洗次數、數據塊及對象大小等。

在這裏插入圖片描述

數據讀寫流程

在這裏插入圖片描述

  • Monitor

監控整個集羣的狀態,維護集羣的cluster MAP二進制表,保證集羣數據的一致性。ClusterMAP描述了對象塊存儲的物理位置,以及一個將設備聚合到物理位置的桶列表。

Mon節點監控着整個ceph集羣的狀態信息,監聽於tcp的6789端口。每一個ceph集羣中至少要有一個Mon節點,官方推薦每個集羣至少部署三臺。Mon節點中保存了最新的版本集羣數據分佈圖(cluster map)的主副本。客戶端在使用時,需要掛載mon節點的6789端口,下載最新的cluster map,通過crush算法獲得集羣中各osd的IP地址,然後再與osd節點直接建立連接來傳輸數據。所以對於ceph來說,並不需要有集中式的主節點用於計算與尋址,客戶端分攤了這部分工作。而且客戶端也可以直接和osd通信,省去了中間代理服務器的額外開銷。

Mon節點之間使用Paxos算法來保持各節點cluster map的一致性;各mon節點的功能總體上是一樣的,相互間的關係可以被簡單理解爲主備關係。如果主mon節點損壞,其他mon存活節點超過半數時,集羣還可以正常運行。當故障mon節點恢復時,會主動向其他mon節點拉取最新的cluster map。

Mon節點並不會主動輪詢各個osd的當前狀態,相反,osd只有在一些特殊情況纔會上報自己的信息,平常只會簡單的發送心跳。特殊情況包括:1、新的OSD被加入集羣;2、某個OSD發現自身或其他OSD發生異常。Mon節點在收到這些上報信息時,則會更新cluster map信息並加以擴散。

cluster map信息是以異步且lazy的形式擴散的。monitor並不會在每一次cluster map版本更新後都將新版本廣播至全體OSD,而是在有OSD向自己上報信息時,將更新回覆給對方。類似的,各個OSD也是在和其他OSD通信時,如果發現對方的osd中持有的cluster map版本較低,則把自己更新的版本發送給對方。

  • MDS(可選)

爲Ceph文件系統提供元數據計算、緩存與同步。在ceph中,元數據也是存儲在osd節點中的,mds類似於元數據的代理緩存服務器。MDS進程並不是必須的進程,只有需要使用CEPHFS時,才需要配置MDS節點。

  • Pod(Ceph RBD Volume )
apiVersion: v1
kind: Pod
metadata:
  name: rbd
spec:
  containers:
    - image: kubernetes/pause
      name: rbd-rw
      volumeMounts:
      - name: rbdpd
        mountPath: /mnt/rbd
  volumes:
    - name: rbdpd
      rbd:
        monitors:
        - '10.16.154.78:6789'
        - '10.16.154.82:6789'
        - '10.16.154.83:6789'
        pool: kube
        image: foo
        fsType: ext4
        readOnly: true
        user: admin
        keyring: /etc/ceph/keyring
        imageformat: "2"
        imagefeatures: "layering"
  • PVC(Persistent Volume Claim)

    • 在PVC 對象裏,不需要任何關於 Volume 細節的字段,只有描述性的屬性和定義。

    • 應用

      定義PVC

      kind: PersistentVolumeClaim
      apiVersion: v1
      metadata:
        name: pv-claim
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
      

      在pod中聲明PVC

      apiVersion: v1
      kind: Pod
      metadata:
        name: pv-pod
      spec:
        containers:
          - name: pv-container
            image: nginx
            ports:
              - containerPort: 80
                name: "http-server"
            volumeMounts:
              - mountPath: "/usr/share/nginx/html"
                name: pv-storage
        volumes:
          - name: pv-storage
            persistentVolumeClaim:
              claimName: pv-claim
      
  • PV(Persistent Volume)

    • 定義PV

      kind: PersistentVolume
      apiVersion: v1
      metadata:
        name: pv-volume
        labels:
          type: local
      spec:
        capacity:
          storage: 10Gi
        accessModes:
          - ReadWriteOnce
        rbd:
          monitors:
          # 使用 kubectl get pods -n rook-ceph 查看 rook-ceph-mon- 開頭的 POD IP 即可得下面的列表
          - '10.16.154.78:6789'
          - '10.16.154.82:6789'
          - '10.16.154.83:6789'
          pool: kube
          image: foo
          fsType: ext4
          readOnly: true
          user: admin
          keyring: /etc/ceph/keyring
      

2.在Stateful中應用

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi

3.查看命令

kubectl get pvc -l app=nginx

4.問題

刪除pod,Volume中文件是否會丟失?不會

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章