1. 程式人生 > 其它 >k8s(3):pod控制器和service詳解

k8s(3):pod控制器和service詳解

1. Pod控制器詳解

1.1 Pod控制器介紹

Pod是kubernetes的最小管理單元,在kubernetes中,按照pod的建立方式可以將其分為兩類:

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

什麼是Pod控制器

​Pod控制器是管理pod的中間層,使用Pod控制器之後,只需要告訴Pod控制器,想要多少個什麼樣的Pod就可以了,它會創建出滿足條件的Pod並確保每一個Pod資源處於使用者期望的目標狀態。如果Pod資源在執行中出現故障,它會基於指定策略重新編排Pod。

在kubernetes中,有很多型別的pod控制器,每種都有自己的適合的場景,常見的有下面這些:

  • ReplicationController:比較原始的pod控制器,已經被廢棄,由ReplicaSet替代
  • ReplicaSet:保證副本數量一直維持在期望值,並支援pod數量擴縮容,映象版本升級
  • Deployment:通過控制ReplicaSet來控制Pod,並支援滾動升級、回退版本
  • Horizontal Pod Autoscaler:可以根據叢集負載自動水平調整Pod的數量,實現削峰填谷
  • DaemonSet:在叢集中的指定Node上執行且僅執行一個副本,一般用於守護程序類的任務
  • Job:它創建出來的pod只要完成任務就立即退出,不需要重啟或重建,用於執行一次性任務
  • Cronjob:它建立的Pod負責週期性任務控制,不需要持續後臺執行
  • StatefulSet:管理有狀態應用

1.2 ReplicaSet(RS)

ReplicaSet的主要作用是保證一定數量的pod正常執行,它會持續監聽這些Pod的執行狀態,一旦Pod發生故障,就會重啟或重建。同時它還支援對pod數量的擴縮容和映象版本的升降級。

ReplicaSet的資源清單檔案:

apiVersion: apps/v1 # 版本號
kind: ReplicaSet # 型別       
metadata: # 元資料
  name: # rs名稱 
  namespace: # 所屬名稱空間 
  labels: #標籤
    controller: rs
spec: # 詳情描述
  replicas: 3 # 副本數量
  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

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

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

建立ReplicaSet
建立pc-replicaset.yaml檔案,內容如下:

apiVersion: apps/v1
kind: ReplicaSet   
metadata:
  name: pc-replicaset
  namespace: dev
spec:
  replicas: 3
  selector: 
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
# 建立rs
[root@master ~]# kubectl create -f pc-replicaset.yaml
replicaset.apps/pc-replicaset created

# 檢視rs
# DESIRED:期望副本數量  
# CURRENT:當前副本數量  
# READY:已經準備好提供服務的副本數量
[root@master ~]# kubectl get rs pc-replicaset -n dev -o wide
NAME          DESIRED   CURRENT READY AGE   CONTAINERS   IMAGES             SELECTOR
pc-replicaset 3         3       3     22s   nginx        nginx:1.17.1       app=nginx-pod

# 檢視當前控制器創建出來的pod
# 這裡發現控制器創建出來的pod的名稱是在控制器名稱後面拼接了-xxxxx隨機碼
[root@master ~]# kubectl get pod -n dev
NAME                          READY   STATUS    RESTARTS   AGE
pc-replicaset-6vmvt   1/1     Running   0          54s
pc-replicaset-fmb8f   1/1     Running   0          54s
pc-replicaset-snrk2   1/1     Running   0          54s

擴縮容

# 編輯rs的副本數量,修改spec:replicas: 6即可
[root@master ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited

# 檢視pod
[root@master ~]# kubectl get pods -n dev
NAME                          READY   STATUS    RESTARTS   AGE
pc-replicaset-6vmvt   1/1     Running   0          114m
pc-replicaset-cftnp   1/1     Running   0          10s
pc-replicaset-fjlm6   1/1     Running   0          10s
pc-replicaset-fmb8f   1/1     Running   0          114m
pc-replicaset-s2whj   1/1     Running   0          10s
pc-replicaset-snrk2   1/1     Running   0          114m

# 當然也可以直接使用命令實現
# 使用scale命令實現擴縮容, 後面--replicas=n直接指定目標數量即可
[root@master ~]# kubectl scale rs pc-replicaset --replicas=2 -n dev
replicaset.apps/pc-replicaset scaled

# 命令執行完畢,立即檢視,發現已經有4個開始準備退出了
[root@master ~]# kubectl get pods -n dev
NAME                       READY   STATUS        RESTARTS   AGE
pc-replicaset-6vmvt   0/1     Terminating   0          118m
pc-replicaset-cftnp   0/1     Terminating   0          4m17s
pc-replicaset-fjlm6   0/1     Terminating   0          4m17s
pc-replicaset-fmb8f   1/1     Running       0          118m
pc-replicaset-s2whj   0/1     Terminating   0          4m17s
pc-replicaset-snrk2   1/1     Running       0          118m

#稍等片刻,就只剩下2個了
[root@master ~]# kubectl get pods -n dev
NAME                       READY   STATUS    RESTARTS   AGE
pc-replicaset-fmb8f   1/1     Running   0          119m
pc-replicaset-snrk2   1/1     Running   0          119m

映象升級

# 編輯rs的容器映象 - image: nginx:1.17.2
[root@master ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited

# 再次檢視,發現映象版本已經變更了
[root@master ~]# kubectl get rs -n dev -o wide
NAME                DESIRED  CURRENT   READY   AGE    CONTAINERS   IMAGES        ...
pc-replicaset       2        2         2       140m   nginx         nginx:1.17.2  ...

# 同樣的道理,也可以使用命令完成這個工作
# kubectl set image rs rs名稱 容器=映象版本 -n namespace
[root@master ~]# kubectl set image rs pc-replicaset nginx=nginx:1.17.1  -n dev
replicaset.apps/pc-replicaset image updated

# 再次檢視,發現映象版本已經變更了
[root@master ~]# kubectl get rs -n dev -o wide
NAME                 DESIRED  CURRENT   READY   AGE    CONTAINERS   IMAGES            ...
pc-replicaset        2        2         2       145m   nginx        nginx:1.17.1 ... 

刪除ReplicaSet

# 使用kubectl delete命令會刪除此RS以及它管理的Pod
# 在kubernetes刪除RS前,會將RS的replicasclear調整為0,等待所有的Pod被刪除後,在執行RS物件的刪除
[root@master ~]# kubectl delete rs pc-replicaset -n dev
replicaset.apps "pc-replicaset" deleted
[root@master ~]# kubectl get pod -n dev -o wide
No resources found in dev namespace.

# 如果希望僅僅刪除RS物件(保留Pod),可以使用kubectl delete命令時新增--cascade=false選項(不推薦)。
[root@master ~]# kubectl delete rs pc-replicaset -n dev --cascade=false
replicaset.apps "pc-replicaset" deleted
[root@master ~]# kubectl get pods -n dev
NAME                  READY   STATUS    RESTARTS   AGE
pc-replicaset-cl82j   1/1     Running   0          75s
pc-replicaset-dslhb   1/1     Running   0          75s

# 也可以使用yaml直接刪除(推薦)
[root@master ~]# kubectl delete -f pc-replicaset.yaml
replicaset.apps "pc-replicaset" deleted

1.3 Deployment(Deploy)

為了更好的解決服務編排的問題,kubernetes在V1.2版本開始,引入了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 # 保留歷史版本
  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

建立pc-deployment.yaml,內容如下:

apiVersion: apps/v1
kind: Deployment      
metadata:
  name: pc-deployment
  namespace: dev
spec: 
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
# 建立deployment
[root@master ~]# kubectl create -f pc-deployment.yaml --record=true
deployment.apps/pc-deployment created

# 檢視deployment
# UP-TO-DATE 最新版本的pod的數量
# AVAILABLE  當前可用的pod的數量
[root@master ~]# kubectl get deploy pc-deployment -n dev
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   3/3     3            3           15s

# 檢視rs
# 發現rs的名稱是在原來deployment的名字後面添加了一個10位數的隨機串
[root@master ~]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6696798b78   3         3         3       23s

# 檢視pod
[root@master ~]# kubectl get pods -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6696798b78-d2c8n   1/1     Running   0          107s
pc-deployment-6696798b78-smpvp   1/1     Running   0          107s
pc-deployment-6696798b78-wvjd8   1/1     Running   0          107s

擴縮容

# 變更副本數量為5個
[root@master ~]# kubectl scale deploy pc-deployment --replicas=5  -n dev
deployment.apps/pc-deployment scaled

# 檢視deployment
[root@master ~]# kubectl get deploy pc-deployment -n dev
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   5/5     5            5           2m

# 檢視pod
[root@master ~]#  kubectl get pods -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6696798b78-d2c8n   1/1     Running   0          4m19s
pc-deployment-6696798b78-jxmdq   1/1     Running   0          94s
pc-deployment-6696798b78-mktqv   1/1     Running   0          93s
pc-deployment-6696798b78-smpvp   1/1     Running   0          4m19s
pc-deployment-6696798b78-wvjd8   1/1     Running   0          4m19s

# 編輯deployment的副本數量,修改spec:replicas: 4即可
[root@master ~]# kubectl edit deploy pc-deployment -n dev
deployment.apps/pc-deployment edited

# 檢視pod
[root@master ~]# kubectl get pods -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6696798b78-d2c8n   1/1     Running   0          5m23s
pc-deployment-6696798b78-jxmdq   1/1     Running   0          2m38s
pc-deployment-6696798b78-smpvp   1/1     Running   0          5m23s
pc-deployment-6696798b78-wvjd8   1/1     Running   0          5m23s

映象更新
deployment支援兩種更新策略:重建更新滾動更新,可以通過strategy指定策略型別,支援兩個屬性:

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

重建更新

  1. 編輯pc-deployment.yaml,在spec節點下新增更新策略
spec:
  strategy: # 策略
    type: Recreate # 重建更新
  1. 建立deploy進行驗證
# 變更映象
[root@master ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n dev
deployment.apps/pc-deployment image updated

# 觀察升級過程
[root@master ~]#  kubectl get pods -n dev -w
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-5d89bdfbf9-65qcw   1/1     Running   0          31s
pc-deployment-5d89bdfbf9-w5nzv   1/1     Running   0          31s
pc-deployment-5d89bdfbf9-xpt7w   1/1     Running   0          31s

pc-deployment-5d89bdfbf9-xpt7w   1/1     Terminating   0          41s
pc-deployment-5d89bdfbf9-65qcw   1/1     Terminating   0          41s
pc-deployment-5d89bdfbf9-w5nzv   1/1     Terminating   0          41s

pc-deployment-675d469f8b-grn8z   0/1     Pending       0          0s
pc-deployment-675d469f8b-hbl4v   0/1     Pending       0          0s
pc-deployment-675d469f8b-67nz2   0/1     Pending       0          0s

pc-deployment-675d469f8b-grn8z   0/1     ContainerCreating   0          0s
pc-deployment-675d469f8b-hbl4v   0/1     ContainerCreating   0          0s
pc-deployment-675d469f8b-67nz2   0/1     ContainerCreating   0          0s

pc-deployment-675d469f8b-grn8z   1/1     Running             0          1s
pc-deployment-675d469f8b-67nz2   1/1     Running             0          1s
pc-deployment-675d469f8b-hbl4v   1/1     Running             0          2s

滾動更新

  1. 編輯pc-deployment.yaml,在spec節點下新增更新策略
spec:
  strategy: # 策略
    type: RollingUpdate # 滾動更新策略
    rollingUpdate:
      maxSurge: 25% 
      maxUnavailable: 25%
  1. 建立deploy進行驗證
# 變更映象
[root@master ~]# kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev
deployment.apps/pc-deployment image updated

# 觀察升級過程
[root@master ~]# kubectl get pods -n dev -w
NAME                           READY   STATUS    RESTARTS   AGE
pc-deployment-c848d767-8rbzt   1/1     Running   0          31m
pc-deployment-c848d767-h4p68   1/1     Running   0          31m
pc-deployment-c848d767-hlmz4   1/1     Running   0          31m
pc-deployment-c848d767-rrqcn   1/1     Running   0          31m

pc-deployment-966bf7f44-226rx   0/1     Pending             0          0s
pc-deployment-966bf7f44-226rx   0/1     ContainerCreating   0          0s
pc-deployment-966bf7f44-226rx   1/1     Running             0          1s
pc-deployment-c848d767-h4p68    0/1     Terminating         0          34m

pc-deployment-966bf7f44-cnd44   0/1     Pending             0          0s
pc-deployment-966bf7f44-cnd44   0/1     ContainerCreating   0          0s
pc-deployment-966bf7f44-cnd44   1/1     Running             0          2s
pc-deployment-c848d767-hlmz4    0/1     Terminating         0          34m

pc-deployment-966bf7f44-px48p   0/1     Pending             0          0s
pc-deployment-966bf7f44-px48p   0/1     ContainerCreating   0          0s
pc-deployment-966bf7f44-px48p   1/1     Running             0          0s
pc-deployment-c848d767-8rbzt    0/1     Terminating         0          34m

pc-deployment-966bf7f44-dkmqp   0/1     Pending             0          0s
pc-deployment-966bf7f44-dkmqp   0/1     ContainerCreating   0          0s
pc-deployment-966bf7f44-dkmqp   1/1     Running             0          2s
pc-deployment-c848d767-rrqcn    0/1     Terminating         0          34m

# 至此,新版本的pod建立完畢,就版本的pod銷燬完畢
# 中間過程是滾動進行的,也就是邊銷燬邊建立

滾動更新的過程:

映象更新中rs的變化:

# 檢視rs,發現原來的rs的依舊存在,只是pod數量變為了0,而後又新產生了一個rs,pod數量為4
# 其實這就是deployment能夠進行版本回退的奧妙所在,後面會詳細解釋
[root@master ~]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6696798b78   0         0         0       7m37s
pc-deployment-6696798b11   0         0         0       5m37s
pc-deployment-c848d76789   4         4         4       72s

版本回退

deployment支援版本升級過程中的暫停、繼續功能以及版本回退等諸多功能,下面具體來看.
kubectl rollout: 版本升級相關功能,支援下面的選項:

  • status:顯示當前升級狀態
  • history:顯示升級歷史記錄
  • pause:暫停版本升級過程
  • resume:繼續已經暫停的版本升級過程
  • restart:重啟版本升級過程
  • undo:回滾到上一級版本(可以使用--to-revision回滾到指定版本)
# 檢視當前升級版本的狀態
[root@master ~]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out

# 檢視升級歷史記錄
[root@master ~]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment
REVISION  CHANGE-CAUSE
1         kubectl create --filename=pc-deployment.yaml --record=true
2         kubectl create --filename=pc-deployment.yaml --record=true
3         kubectl create --filename=pc-deployment.yaml --record=true
# 可以發現有三次版本記錄,說明完成過兩次升級

# 版本回滾
# 這裡直接使用--to-revision=1回滾到了1版本, 如果省略這個選項,就是回退到上個版本,就是2版本
[root@master ~]# kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
deployment.apps/pc-deployment rolled back

# 檢視發現,通過nginx映象版本可以發現到了第一版
[root@master ~]# kubectl get deploy -n dev -o wide
NAME            READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         
pc-deployment   4/4     4            4           74m   nginx        nginx:1.17.1   

# 檢視rs,發現第一個rs中有4個pod執行,後面兩個版本的rs中pod為執行
# 其實deployment之所以可是實現版本的回滾,就是通過記錄下歷史rs來實現的,
# 一旦想回滾到哪個版本,只需要將當前版本pod數量降為0,然後將回滾版本的pod提升為目標數量就可以了
[root@master ~]# kubectl get rs -n dev
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6696798b78   4         4         4       78m
pc-deployment-966bf7f44    0         0         0       37m
pc-deployment-c848d767     0         0         0       71m

金絲雀釋出
Deployment控制器支援控制更新過程中的控制,如“暫停(pause)”或“繼續(resume)”更新操作。
比如有一批新的Pod資源建立完成後立即暫停更新過程,此時,僅存在一部分新版本的應用,主體部分還是舊的版本。然後,再篩選一小部分的使用者請求路由到新版本的Pod應用,繼續觀察能否穩定地按期望的方式執行。確定沒問題之後再繼續完成餘下的Pod資源滾動更新,否則立即回滾更新操作。這就是所謂的金絲雀釋出。

# 更新deployment的版本,並配置暫停deployment
[root@master ~]#  kubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment  -n dev
deployment.apps/pc-deployment image updated
deployment.apps/pc-deployment paused

#觀察更新狀態
[root@master ~]# kubectl rollout status deploy pc-deployment -n dev 
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 4 new replicas have been updated...

# 監控更新的過程,可以看到已經新增了一個資源,但是並未按照預期的狀態去刪除一箇舊的資源,就是因為使用了pause暫停命令

[root@master ~]# kubectl get rs -n dev -o wide
NAME                       DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         
pc-deployment-5d89bdfbf9   3         3         3       19m     nginx        nginx:1.17.1   
pc-deployment-675d469f8b   0         0         0       14m     nginx        nginx:1.17.2   
pc-deployment-6c9f56fcfb   2         2         2       3m16s   nginx        nginx:1.17.4   
[root@master ~]# kubectl get pods -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-5d89bdfbf9-rj8sq   1/1     Running   0          7m33s
pc-deployment-5d89bdfbf9-ttwgg   1/1     Running   0          7m35s
pc-deployment-5d89bdfbf9-v4wvc   1/1     Running   0          7m34s
pc-deployment-6c9f56fcfb-996rt   1/1     Running   0          3m31s
pc-deployment-6c9f56fcfb-j2gtj   1/1     Running   0          3m31s

# 確保更新的pod沒問題了,繼續更新
[root@master ~]# kubectl rollout resume deploy pc-deployment -n dev
deployment.apps/pc-deployment resumed

# 檢視最後的更新情況
[root@master ~]# kubectl get rs -n dev -o wide
NAME                       DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES         
pc-deployment-5d89bdfbf9   0         0         0       21m     nginx        nginx:1.17.1   
pc-deployment-675d469f8b   0         0         0       16m     nginx        nginx:1.17.2   
pc-deployment-6c9f56fcfb   4         4         4       5m11s   nginx        nginx:1.17.4   

[root@master ~]# kubectl get pods -n dev
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6c9f56fcfb-7bfwh   1/1     Running   0          37s
pc-deployment-6c9f56fcfb-996rt   1/1     Running   0          5m27s
pc-deployment-6c9f56fcfb-j2gtj   1/1     Running   0          5m27s
pc-deployment-6c9f56fcfb-rf84v   1/1     Running   0          37s

刪除Deployment

# 刪除deployment,其下的rs和pod也將被刪除
[root@master ~]# kubectl delete -f pc-deployment.yaml
deployment.apps "pc-deployment" deleted

1.4 Horizontal Pod Autoscaler(HPA)

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

接下來,我們來做一個實驗

1 安裝metrics-server

metrics-server可以用來收集叢集中的資源使用情況

# 安裝git
[root@master ~]# yum install git -y
# 獲取metrics-server, 注意使用的版本
[root@master ~]# git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server
# 修改deployment, 注意修改的是映象和初始化引數
[root@master ~]# cd /root/metrics-server/deploy/1.8+/
[root@master 1.8+]# vim metrics-server-deployment.yaml
按圖中新增下面選項
hostNetwork: true
image: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6
args:
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP
# 安裝metrics-server
[root@master 1.8+]# kubectl apply -f ./

# 檢視pod執行情況
[root@master 1.8+]# kubectl get pod -n kube-system
metrics-server-6b976979db-2xwbj   1/1     Running   0          90s

# 使用kubectl top node 檢視資源使用情況
[root@master 1.8+]# kubectl top node
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
master   98m          4%     1067Mi          62%
node1    27m          1%     727Mi           42%
node2    34m          1%     800Mi           46%
[root@master 1.8+]# kubectl top pod -n kube-system
NAME                              CPU(cores)   MEMORY(bytes)
coredns-6955765f44-7ptsb          3m           9Mi
coredns-6955765f44-vcwr5          3m           8Mi
etcd-master                       14m          145Mi
...
# 至此,metrics-server安裝完成

2 準備deployment和servie

為了操作簡單,直接使用命令

# 建立deployment 
[root@master 1.8+]# kubectl run nginx --image=nginx:latest --requests=cpu=100m -n dev
# 建立service
[root@master 1.8+]# kubectl expose deployment nginx --type=NodePort --port=80 -n dev

# 檢視
[root@master 1.8+]# kubectl get deployment,pod,svc -n dev
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           47s

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-7df9756ccc-bh8dr   1/1     Running   0          47s

NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/nginx   NodePort   10.109.57.248   <none>        80:31136/TCP   35s

3 部署HPA

建立pc-hpa.yaml

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: pc-hpa
  namespace: dev
spec:
  minReplicas: 1  #最小pod數量
  maxReplicas: 10 #最大pod數量
  targetCPUUtilizationPercentage: 3 # CPU使用率指標
  scaleTargetRef:   # 指定要控制的nginx資訊
    apiVersion: apps/v1
    kind: Deployment  
    name: nginx  
# 建立hpa
[root@master 1.8+]# kubectl create -f pc-hpa.yaml
horizontalpodautoscaler.autoscaling/pc-hpa created

# 檢視hpa
[root@master 1.8+]# kubectl get hpa -n dev
NAME     REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
pc-hpa   Deployment/nginx   0%/3%     1         10        1          62s

4 測試

使用壓測工具對service地址192.168.109.100:31136進行壓測,然後通過控制檯檢視hpa和pod的變化

hpa變化

[root@master ~]# kubectl get hpa -n dev -w
NAME     REFERENCE          TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
pc-hpa   Deployment/nginx   0%/3%     1         10        1          4m11s
pc-hpa   Deployment/nginx   0%/3%     1         10        1          5m19s
pc-hpa   Deployment/nginx   22%/3%    1         10        1          6m50s
pc-hpa   Deployment/nginx   22%/3%    1         10        4          7m5s
pc-hpa   Deployment/nginx   22%/3%    1         10        8          7m21s
pc-hpa   Deployment/nginx   6%/3%     1         10        8          7m51s
pc-hpa   Deployment/nginx   0%/3%     1         10        8          9m6s
pc-hpa   Deployment/nginx   0%/3%     1         10        8          13m
pc-hpa   Deployment/nginx   0%/3%     1         10        1          14m

deployment變化

[root@master ~]# kubectl get deployment -n dev -w
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           11m
nginx   1/4     1            1           13m
nginx   1/4     1            1           13m
nginx   1/4     1            1           13m
nginx   1/4     4            1           13m
nginx   1/8     4            1           14m
nginx   1/8     4            1           14m
nginx   1/8     4            1           14m
nginx   1/8     8            1           14m
nginx   2/8     8            2           14m
nginx   3/8     8            3           14m
nginx   4/8     8            4           14m
nginx   5/8     8            5           14m
nginx   6/8     8            6           14m
nginx   7/8     8            7           14m
nginx   8/8     8            8           15m
nginx   8/1     8            8           20m
nginx   8/1     8            8           20m
nginx   1/1     1            1           20m

pod變化

[root@master ~]# kubectl get pods -n dev -w
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7df9756ccc-bh8dr   1/1     Running   0          11m
nginx-7df9756ccc-cpgrv   0/1     Pending   0          0s
nginx-7df9756ccc-8zhwk   0/1     Pending   0          0s
nginx-7df9756ccc-rr9bn   0/1     Pending   0          0s
nginx-7df9756ccc-cpgrv   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-8zhwk   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-rr9bn   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-m9gsj   0/1     Pending             0          0s
nginx-7df9756ccc-g56qb   0/1     Pending             0          0s
nginx-7df9756ccc-sl9c6   0/1     Pending             0          0s
nginx-7df9756ccc-fgst7   0/1     Pending             0          0s
nginx-7df9756ccc-g56qb   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-m9gsj   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-sl9c6   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-fgst7   0/1     ContainerCreating   0          0s
nginx-7df9756ccc-8zhwk   1/1     Running             0          19s
nginx-7df9756ccc-rr9bn   1/1     Running             0          30s
nginx-7df9756ccc-m9gsj   1/1     Running             0          21s
nginx-7df9756ccc-cpgrv   1/1     Running             0          47s
nginx-7df9756ccc-sl9c6   1/1     Running             0          33s
nginx-7df9756ccc-g56qb   1/1     Running             0          48s
nginx-7df9756ccc-fgst7   1/1     Running             0          66s
nginx-7df9756ccc-fgst7   1/1     Terminating         0          6m50s
nginx-7df9756ccc-8zhwk   1/1     Terminating         0          7m5s
nginx-7df9756ccc-cpgrv   1/1     Terminating         0          7m5s
nginx-7df9756ccc-g56qb   1/1     Terminating         0          6m50s
nginx-7df9756ccc-rr9bn   1/1     Terminating         0          7m5s
nginx-7df9756ccc-m9gsj   1/1     Terminating         0          6m50s
nginx-7df9756ccc-sl9c6   1/1     Terminating         0          6m50s

1.5 DaemonSet(DS)

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

DaemonSet控制器的特點:

  • 每當向叢集中新增一個節點時,指定的 Pod 副本也將新增到該節點上
  • 當節點從叢集中移除時,Pod 也就被垃圾回收了

下面先來看下DaemonSet的資源清單檔案

apiVersion: apps/v1 # 版本號
kind: DaemonSet # 型別       
metadata: # 元資料
  name: # rs名稱 
  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

建立pc-daemonset.yaml,內容如下:

apiVersion: apps/v1
kind: DaemonSet      
metadata:
  name: pc-daemonset
  namespace: dev
spec: 
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
# 建立daemonset
[root@master ~]# kubectl create -f  pc-daemonset.yaml
daemonset.apps/pc-daemonset created

# 檢視daemonset
[root@master ~]#  kubectl get ds -n dev -o wide
NAME        DESIRED  CURRENT  READY  UP-TO-DATE  AVAILABLE   AGE   CONTAINERS   IMAGES         
pc-daemonset   2        2        2      2           2        24s   nginx        nginx:1.17.1   

# 檢視pod,發現在每個Node上都執行一個pod
[root@master ~]#  kubectl get pods -n dev -o wide
NAME                 READY   STATUS    RESTARTS   AGE   IP            NODE    
pc-daemonset-9bck8   1/1     Running   0          37s   10.244.1.43   node1     
pc-daemonset-k224w   1/1     Running   0          37s   10.244.2.74   node2      

# 刪除daemonset
[root@master ~]# kubectl delete -f pc-daemonset.yaml
daemonset.apps "pc-daemonset" deleted

1.6 Job

Job,主要用於負責批量處理(一次要處理指定數量任務)短暫的一次性(每個任務僅執行一次就結束)任務。Job特點如下:

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

Job的資源清單檔案:

apiVersion: batch/v1 # 版本號
kind: Job # 型別       
metadata: # 元資料
  name: # rs名稱 
  namespace: # 所屬名稱空間 
  labels: #標籤
    controller: job
spec: # 詳情描述
  completions: 1 # 指定job需要成功執行Pods的次數。預設值: 1
  parallelism: 1 # 指定job在任一時刻應該併發執行Pods的數量。預設值: 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 2;done"]
關於重啟策略設定的說明:
    如果指定為OnFailure,則job會在pod出現故障時重啟容器,而不是建立pod,failed次數不變
    如果指定為Never,則job會在pod出現故障時建立新的pod,並且故障pod不會消失,也不會重啟,failed次數加1
    如果指定為Always的話,就意味著一直重啟,意味著job任務會重複去執行了,當然不對,所以不能設定為Always

建立pc-job.yaml,內容如下:

apiVersion: batch/v1
kind: Job      
metadata:
  name: pc-job
  namespace: dev
spec:
  manualSelector: true
  selector:
    matchLabels:
      app: 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 3;done"]
# 建立job
[root@master ~]# kubectl create -f pc-job.yaml
job.batch/pc-job created

# 檢視job
[root@master ~]# kubectl get job -n dev -o wide  -w
NAME     COMPLETIONS   DURATION   AGE   CONTAINERS   IMAGES         SELECTOR
pc-job   0/1           21s        21s   counter      busybox:1.30   app=counter-pod
pc-job   1/1           31s        79s   counter      busybox:1.30   app=counter-pod

# 通過觀察pod狀態可以看到,pod在執行完畢任務後,就會變成Completed狀態
[root@master ~]# kubectl get pods -n dev -w
NAME           READY   STATUS     RESTARTS      AGE
pc-job-rxg96   1/1     Running     0            29s
pc-job-rxg96   0/1     Completed   0            33s

# 接下來,調整下pod執行的總數量和並行數量 即:在spec下設定下面兩個選項
#  completions: 6 # 指定job需要成功執行Pods的次數為6
#  parallelism: 3 # 指定job併發執行Pods的數量為3
#  然後重新執行job,觀察效果,此時會發現,job會每次執行3個pod,總共執行了6個pod
[root@master ~]# kubectl get pods -n dev -w
NAME           READY   STATUS    RESTARTS   AGE
pc-job-684ft   1/1     Running   0          5s
pc-job-jhj49   1/1     Running   0          5s
pc-job-pfcvh   1/1     Running   0          5s
pc-job-684ft   0/1     Completed   0          11s
pc-job-v7rhr   0/1     Pending     0          0s
pc-job-v7rhr   0/1     Pending     0          0s
pc-job-v7rhr   0/1     ContainerCreating   0          0s
pc-job-jhj49   0/1     Completed           0          11s
pc-job-fhwf7   0/1     Pending             0          0s
pc-job-fhwf7   0/1     Pending             0          0s
pc-job-pfcvh   0/1     Completed           0          11s
pc-job-5vg2j   0/1     Pending             0          0s
pc-job-fhwf7   0/1     ContainerCreating   0          0s
pc-job-5vg2j   0/1     Pending             0          0s
pc-job-5vg2j   0/1     ContainerCreating   0          0s
pc-job-fhwf7   1/1     Running             0          2s
pc-job-v7rhr   1/1     Running             0          2s
pc-job-5vg2j   1/1     Running             0          3s
pc-job-fhwf7   0/1     Completed           0          12s
pc-job-v7rhr   0/1     Completed           0          12s
pc-job-5vg2j   0/1     Completed           0          12s

# 刪除job
[root@master ~]# kubectl delete -f pc-job.yaml
job.batch "pc-job" deleted

1.7 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: 替換,取消當前正在執行的作業並用新作業替換它

建立pc-cronjob.yaml,內容如下:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: pc-cronjob
  namespace: dev
  labels:
    controller: cronjob
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    metadata:
    spec:
      template:
        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 3;done"]
# 建立cronjob
[root@master ~]# kubectl create -f pc-cronjob.yaml
cronjob.batch/pc-cronjob created

# 檢視cronjob
[root@master ~]# kubectl get cronjobs -n dev
NAME         SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
pc-cronjob   */1 * * * *   False     0        <none>          6s

# 檢視job
[root@master ~]# kubectl get jobs -n dev
NAME                    COMPLETIONS   DURATION   AGE
pc-cronjob-1592587800   1/1           28s        3m26s
pc-cronjob-1592587860   1/1           28s        2m26s
pc-cronjob-1592587920   1/1           28s        86s

# 檢視pod
[root@master ~]# kubectl get pods -n dev
pc-cronjob-1592587800-x4tsm   0/1     Completed   0          2m24s
pc-cronjob-1592587860-r5gv4   0/1     Completed   0          84s
pc-cronjob-1592587920-9dxxq   1/1     Running     0          24s


# 刪除cronjob
[root@master ~]# kubectl  delete -f pc-cronjob.yaml
cronjob.batch "pc-cronjob" deleted

2 Service詳解

2.1 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資訊轉換成對應的訪問規則

# 10.97.97.97:80 是service提供的訪問入口
# 當訪問這個入口的時候,可以發現後面有三個pod的服務在等待呼叫,
# kube-proxy會基於rr(輪詢)的策略,將請求分發到其中一個pod上去
# 這個規則會同時在叢集內的所有節點上都生成,所以在任何一個節點上訪問都可以。
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

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

userspace 模式
userspace模式下,kube-proxy會為每一個Service建立一個監聽埠,發向Cluster IP的請求被Iptables規則重定向到kube-proxy監聽的埠上,kube-proxy根據LB演算法選擇一個提供服務的Pod並和其建立連結,以將請求轉發到Pod上。
​該模式下,kube-proxy充當了一個四層負責均衡器的角色。由於kube-proxy執行在userspace中,在進行轉發處理時會增加核心和使用者空間之間的資料拷貝,雖然比較穩定,但是效率比較低。

iptables 模式
iptables模式下,kube-proxy為service後端的每個Pod建立對應的iptables規則,直接將發向Cluster IP的請求重定向到一個Pod IP。
​該模式下kube-proxy不承擔四層負責均衡器的角色,只負責建立iptables規則。該模式的優點是較userspace模式效率更高,但不能提供靈活的LB策略,當後端Pod不可用時也無法進行重試。

ipvs 模式
ipvs模式和iptables類似,kube-proxy監控Pod的變化並建立相應的ipvs規則。ipvs相對iptables轉發效率更高。除此以外,ipvs支援更多的LB演算法。

# 此模式必須安裝ipvs核心模組,否則會降級為iptables
# 開啟ipvs
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

2.2 Service型別

Service的資源清單檔案:

kind: Service  # 資源型別
apiVersion: v1  # 資源版本
metadata: # 元資料
  name: service # 資源名稱
  namespace: dev # 名稱空間
spec: # 描述
  selector: # 標籤選擇器,用於確定當前service代理哪些pod
    app: nginx
  type: # Service型別,指定service的訪問方式
  clusterIP:  # 虛擬服務的ip地址
  sessionAffinity: # session親和性,支援ClientIP、None兩個選項
  ports: # 埠資訊
    - protocol: TCP 
      port: 3017  # service埠
      targetPort: 5003 # pod埠
      nodePort: 31122 # 主機埠
  • ClusterIP:預設值,它是Kubernetes系統自動分配的虛擬IP,只能在叢集內部訪問
  • NodePort:將Service通過指定的Node上的埠暴露給外部,通過此方法,就可以在叢集外部訪問服務
  • LoadBalancer:使用外接負載均衡器完成到服務的負載分發,注意此模式需要外部雲環境支援
  • ExternalName: 把叢集外部的服務引入叢集內部,直接使用

2.3 Service使用

2.3.1 實驗環境準備

在使用service之前,首先利用Deployment創建出3個pod,注意要為pod設定app=nginx-pod的標籤

建立deployment.yaml,內容如下:

apiVersion: apps/v1
kind: Deployment      
metadata:
  name: pc-deployment
  namespace: dev
spec: 
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
[root@master ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created

# 檢視pod詳情
[root@master ~]# kubectl get pods -n dev -o wide --show-labels
NAME                             READY   STATUS     IP            NODE     LABELS
pc-deployment-66cb59b984-8p84h   1/1     Running    10.244.1.40   node1    app=nginx-pod
pc-deployment-66cb59b984-vx8vx   1/1     Running    10.244.2.33   node2    app=nginx-pod
pc-deployment-66cb59b984-wnncx   1/1     Running    10.244.1.39   node1    app=nginx-pod

# 為了方便後面的測試,修改下三臺nginx的index.html頁面(三臺修改的IP地址不一致)
# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
# echo "10.244.1.40" > /usr/share/nginx/html/index.html

#修改完畢之後,訪問測試
[root@master ~]# curl 10.244.1.40
10.244.1.40
[root@master ~]# curl 10.244.2.33
10.244.2.33
[root@master ~]# curl 10.244.1.39
10.244.1.39

2.3.2 ClusterIP型別的Service

建立service-clusterip.yaml檔案

apiVersion: v1
kind: Service
metadata:
  name: service-clusterip
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: 10.97.97.97 # service的ip地址,如果不寫,預設會生成一個
  type: ClusterIP
  ports:
  - port: 80  # Service埠       
    targetPort: 80 # pod埠
# 建立service
[root@master ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created

# 檢視service
[root@master ~]# kubectl get svc -n dev -o wide
NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-clusterip   ClusterIP   10.97.97.97   <none>        80/TCP    13s   app=nginx-pod

# 檢視service的詳細資訊
# 在這裡有一個Endpoints列表,裡面就是當前service可以負載到的服務入口
[root@master ~]# kubectl describe svc service-clusterip -n dev
Name:              service-clusterip
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                10.97.97.97
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>

# 檢視ipvs的對映規則
[root@master ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 訪問10.97.97.97:80觀察效果
[root@master ~]# curl 10.97.97.97:80
10.244.2.33

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

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

  • 如果不定義,預設使用kube-proxy的策略,比如隨機、輪詢
  • 基於客戶端地址的會話保持模式,即來自同一個客戶端發起的所有請求都會轉發到固定的一個Pod上,此模式可以使在spec中新增sessionAffinity:ClientIP選項
# 檢視ipvs的對映規則【rr 輪詢】
[root@master ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 迴圈訪問測試
[root@master ~]# while true;do curl 10.97.97.97:80; sleep 5; done;
10.244.1.40
10.244.1.39
10.244.2.33
10.244.1.40
10.244.1.39
10.244.2.33

# 修改分發策略----sessionAffinity:ClientIP

# 檢視ipvs規則【persistent 代表持久】
[root@master ~]# ipvsadm -Ln
TCP  10.97.97.97:80 rr persistent 10800
  -> 10.244.1.39:80               Masq    1      0          0
  -> 10.244.1.40:80               Masq    1      0          0
  -> 10.244.2.33:80               Masq    1      0          0

# 迴圈訪問測試
[root@master ~]# while true;do curl 10.97.97.97; sleep 5; done;
10.244.2.33
10.244.2.33
10.244.2.33
  
# 刪除service
[root@master ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip" deleted

2.3.3 HeadLiness型別的Service

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

建立service-headliness.yaml

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    
    targetPort: 80
# 建立service
[root@master ~]# kubectl create -f service-headliness.yaml
service/service-headliness created

# 獲取service, 發現CLUSTER-IP未分配
[root@master ~]# kubectl get svc service-headliness -n dev -o wide
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE   SELECTOR
service-headliness   ClusterIP   None         <none>        80/TCP    11s   app=nginx-pod

# 檢視service詳情
[root@master ~]# kubectl describe svc service-headliness  -n dev
Name:              service-headliness
Namespace:         dev
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx-pod
Type:              ClusterIP
IP:                None
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity:  None
Events:            <none>

# 檢視域名的解析情況
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local

[root@master ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33

2.3.4 NodePort型別的Service

在之前的樣例中,建立的Service的ip地址只有叢集內部才可以訪問,如果希望將Service暴露給叢集外部使用,那麼就要使用到另外一種型別的Service,稱為NodePort型別。NodePort的工作原理其實就是將service的埠對映到Node的一個埠上,然後就可以通過NodeIp:NodePort來訪問service了。

建立service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: service-nodeport
  namespace: dev
spec:
  selector:
    app: nginx-pod
  type: NodePort # service型別
  ports:
  - port: 80
    nodePort: 30002 # 指定繫結的node的埠(預設的取值範圍是:30000-32767), 如果不指定,會預設分配
    targetPort: 80
# 建立service
[root@master ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created

# 檢視service
[root@master ~]# kubectl get svc -n dev -o wide
NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)       SELECTOR
service-nodeport   NodePort   10.105.64.191   <none>        80:30002/TCP  app=nginx-pod

# 接下來可以通過電腦主機的瀏覽器去訪問叢集中任意一個nodeip的30002埠,即可訪問到pod

2.3.5 LoadBalancer型別的Service

LoadBalancer和NodePort很相似,目的都是向外部暴露一個埠,區別在於LoadBalancer會在叢集的外部再來做一個負載均衡裝置,而這個裝置需要外部環境支援的,外部服務傳送到這個裝置上的請求,會被裝置負載之後轉發到叢集中。

2.3.6 ExternalName型別的Service

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

apiVersion: v1
kind: Service
metadata:
  name: service-externalname
  namespace: dev
spec:
  type: ExternalName # service型別
  externalName: www.baidu.com  #改成ip地址也可以
# 建立service
[root@master ~]# kubectl  create -f service-externalname.yaml
service/service-externalname created

# 域名解析
[root@master ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com.          30      IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       30      IN      A       39.156.66.18
www.a.shifen.com.       30      IN      A       39.156.66.14

2.4 Ingress介紹

在前面課程中已經提到,Service對叢集之外暴露服務的主要方式有兩種:NotePort和LoadBalancer,但是這兩種方式,都有一定的缺點:

  • NodePort方式的缺點是會佔用很多叢集機器的埠,那麼當叢集服務變多的時候,這個缺點就愈發明顯
  • LB方式的缺點是每個service需要一個LB,浪費、麻煩,並且需要kubernetes之外裝置的支援

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

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

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

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

  1. 使用者編寫Ingress規則,說明哪個域名對應kubernetes叢集中的哪個Service
  2. Ingress控制器動態感知Ingress服務規則的變化,然後生成一段對應的Nginx反向代理配置
  3. Ingress控制器會將生成的Nginx配置寫入到一個執行著的Nginx服務中,並動態更新
  4. 到此為止,其實真正在工作的就是一個Nginx了,內部配置了使用者定義的請求轉發規則

2.5 Ingress使用

2.5.1 環境準備

搭建ingress環境

# 建立資料夾
[root@master ~]# mkdir ingress-controller
[root@master ~]# cd ingress-controller/

# 獲取ingress-nginx,本次案例使用的是0.30版本
[root@master ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
[root@master ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml

# 修改mandatory.yaml檔案中的倉庫
# 修改quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 為quay-mirror.qiniu.com/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 建立ingress-nginx
[root@master ingress-controller]# kubectl apply -f ./

# 檢視ingress-nginx
[root@master ingress-controller]# kubectl get pod -n ingress-nginx
NAME                                           READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-controller-fbf967dd5-4qpbp   1/1     Running   0          12h

# 檢視service
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.98.75.163   <none>        80:32240/TCP,443:31335/TCP   11h

準備service和pod

為了後面的實驗比較方便,建立如下圖所示的模型

建立tomcat-nginx.yaml

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

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat-pod
  template:
    metadata:
      labels:
        app: tomcat-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.5-jre10-slim
        ports:
        - containerPort: 8080

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: dev
spec:
  selector:
    app: tomcat-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080

# 建立
[root@master ~]# kubectl create -f tomcat-nginx.yaml

# 檢視
[root@master ~]# kubectl get svc -n dev
NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
nginx-service    ClusterIP   None         <none>        80/TCP     48s
tomcat-service   ClusterIP   None         <none>        8080/TCP   48s

2.5.2 Http代理

建立ingress-http.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
spec:
  rules:
  - host: nginx.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080

# 建立
[root@master ~]# kubectl create -f ingress-http.yaml
ingress.extensions/ingress-http created

# 檢視
[root@master ~]# kubectl get ing ingress-http -n dev
NAME           HOSTS                                  ADDRESS   PORTS   AGE
ingress-http   nginx.itheima.com,tomcat.itheima.com             80      22s

# 檢視詳情
[root@master ~]# kubectl describe ing ingress-http  -n dev
...
Rules:
Host                Path  Backends
----                ----  --------
nginx.itheima.com   / nginx-service:80 (10.244.1.96:80,10.244.1.97:80,10.244.2.112:80)
tomcat.itheima.com  / tomcat-service:8080(10.244.1.94:8080,10.244.1.95:8080,10.244.2.111:8080)
...

# 接下來,在本地電腦上配置host檔案,解析上面的兩個域名到192.168.109.100(master)上
# 然後,就可以分別訪問tomcat.itheima.com:32240  和  nginx.itheima.com:32240 檢視效果了

2.5.3 Https代理

建立證書

# 生成證書
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima.com"

# 建立金鑰
kubectl create secret tls tls-secret --key tls.key --cert tls.crt

建立ingress-https.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-https
  namespace: dev
spec:
  tls:
    - hosts:
      - nginx.itheima.com
      - tomcat.itheima.com
      secretName: tls-secret # 指定祕鑰
  rules:
  - host: nginx.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-service
          servicePort: 80
  - host: tomcat.itheima.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-service
          servicePort: 8080
# 建立
[root@master ~]# kubectl create -f ingress-https.yaml
ingress.extensions/ingress-https created

# 檢視
[root@master ~]# kubectl get ing ingress-https -n dev
NAME            HOSTS                                  ADDRESS         PORTS     AGE
ingress-https   nginx.itheima.com,tomcat.itheima.com   10.104.184.38   80, 443   2m42s

# 檢視詳情
[root@master ~]# kubectl describe ing ingress-https -n dev
...
TLS:
  tls-secret terminates nginx.itheima.com,tomcat.itheima.com
Rules:
Host              Path Backends
----              ---- --------
nginx.itheima.com  /  nginx-service:80 (10.244.1.97:80,10.244.1.98:80,10.244.2.119:80)
tomcat.itheima.com /  tomcat-service:8080(10.244.1.99:8080,10.244.2.117:8080,10.244.2.120:8080)
...

# 下面可以通過瀏覽器訪問https://nginx.itheima.com:31335 和 https://tomcat.itheima.com:31335來查看了