kubernetes基礎知識 核心功能 架構


 一、提供一套Minikube集群測試環境

1、Minikube集群測試環境服務器地址
https://kubernetes.io/docs/tutorials/hello-minikube/

二、k8s核心功能:應用部署、訪問、Scale Up/Down 以及滾動更新

1、部署應用

命令:
kubectl run kubernetes-bootcamp \
      --image=docker.io/jocatalin/kubernetes-bootcamp:v1 \
      --port=8080

通過 kubectl run 部署了一個應用,命名為 kubernetes-bootcamp。

Docker 鏡像通過 --image 指定。

--port 設置應用對外服務的端口。

結果:
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/kubernetes-bootcamp created

3、Kubernetes 重要術語 

a、Pod 是容器的集合,通常會將緊密相關的一組容器放到一個 Pod 中,同一個 Pod 中的所有容器共享 IP 地址和 Port 空間,也就是說它們在一個 network namespace 中。Pod 是 Kubernetes 調度的最小單位,同一 Pod 中的容器始終被一起調度。

 b、deployment可以理解為應用。

查看當前的pod

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-6c5cfd894b-698g2   1/1     Running   0          28s

訪問應用

默認情況下,所有 Pod 只能在集群內部訪問。對於上面這個例子,要訪問應用只能直接訪問容器的 8080 端口。為了能夠從外部訪問應用,我們需要將容器的 8080 端口映射到節點的端口。

 執行如下命令:

kubectl expose deployment/kubernetes-bootcamp \
      --type="NodePort" \
      --port 8080

查看應用被映射到節點的哪個端口

$ kubectl get services
NAME                  TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
kubernetes            ClusterIP   10.96.0.1     <none>        443/TCP          2m43s
kubernetes-bootcamp   NodePort    10.105.35.4   <none>        8080:30207/TCP   33s

kubernetes-bootcamp 是我們應用的 service,8080 端口已經映射到 host01 的 32320 端口,端口號是隨機分配的。

訪問應用命令:

$ hostname
minikube

$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-698g2 | v=1

Scale 應用

默認情況下應用只會運行一個副本,可以通過 kubectl get deployments查看副本數。

$  kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1/1     1            1           5m1s

將副本數增加到 3 個

$ kubectl scale deployments/kubernetes-bootcamp --replicas=3
deployment.extensions/kubernetes-bootcamp scaled

$ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   3/3     3            3           6m4s

查看當前pod也增加到3個

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-6c5cfd894b-698g2   1/1     Running   0          7m49s
kubernetes-bootcamp-6c5cfd894b-9d6sn   1/1     Running   0          2m3s
kubernetes-bootcamp-6c5cfd894b-tcp94   1/1     Running   0          2m3s

通過 curl 訪問應用,可以看到每次請求發送到不同的 Pod,三個副本輪詢處理,這樣就實現了負載均衡。

$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-9d6sn | v=1
$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-9d6sn | v=1
$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-tcp94 | v=1
$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-tcp94 | v=1
$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-698g2 | v=1
#備注,已經實現了負載均衡功能。

通過scale down命令刪除副本

$ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   3/3     3            3           11m

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-6c5cfd894b-698g2   1/1     Running   0          12m
kubernetes-bootcamp-6c5cfd894b-9d6sn   1/1     Running   0          6m19s
kubernetes-bootcamp-6c5cfd894b-tcp94   1/1     Running   0          6m19s

#通過scale down刪除一個副本
$ kubectl scale deployments/kubernetes-bootcamp --replicas=2

deployment.extensions/kubernetes-bootcamp scaled
$ kubectl get pods
NAME                                   READY   STATUS        RESTARTS   AGE
kubernetes-bootcamp-6c5cfd894b-698g2   1/1     Running       0          13m
kubernetes-bootcamp-6c5cfd894b-9d6sn   1/1     Running       0          7m42s
kubernetes-bootcamp-6c5cfd894b-tcp94   1/1     Terminating   0          7m42s  #其中一個副本被刪除了

滾動更新

當前應用使用的 image 版本為 v1,執行如下命令將其升級到 v2:

$ kubectl get pods  #這里需要等20秒左右,才能更新完成
NAME                                   READY   STATUS        RESTARTS   AGE
kubernetes-bootcamp-5bf4d5689b-km8dq   1/1     Running       0          39s
kubernetes-bootcamp-5bf4d5689b-wlrmn   1/1     Running       0          38s
kubernetes-bootcamp-6c5cfd894b-698g2   0/1     Terminating   0          15m
kubernetes-bootcamp-6c5cfd894b-9d6sn   0/1     Terminating   0          9m41s

$ kubectl get pods
NAME                                   READY   STATUS    RESTARTS   AGE
kubernetes-bootcamp-5bf4d5689b-km8dq   1/1     Running   0          42s
kubernetes-bootcamp-5bf4d5689b-wlrmn   1/1     Running   0          41s

通過 kubectl get pods 可以觀察滾動更新的過程:v1 的 Pod 被逐個刪除,同時啟動了新的 v2 Pod。更新完成后訪問新版本應用。

$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5bf4d5689b-wlrmn | v=2

$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5bf4d5689b-km8dq | v=2

現在是V2版本,需要進行回退成V1版本

$ kubectl rollout undo deployments/kubernetes-bootcamp
deployment.extensions/kubernetes-bootcamp rolled back

$ kubectl get pods
NAME                                   READY   STATUS        RESTARTS   AGE
kubernetes-bootcamp-5bf4d5689b-km8dq   1/1     Terminating   0          4m43s
kubernetes-bootcamp-5bf4d5689b-wlrmn   1/1     Terminating   0          4m42s
kubernetes-bootcamp-6c5cfd894b-mrcn5   1/1     Running       0          10s
kubernetes-bootcamp-6c5cfd894b-nz2jx   1/1     Running       0          9s

$ curl minikube:30207
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6c5cfd894b-qw8dq | v=1

4、Kubernetes 集群的重要概念

Cluster 
Cluster 是計算、存儲和網絡資源的集合,Kubernetes 利用這些資源運行各種基於容器的應用。

Master 
Master 是 Cluster 的大腦,它的主要職責是調度,即決定將應用放在哪里運行。Master 運行 Linux 操作系統,可以是物理機或者虛擬機。為了實現高可用,可以運行多個 Master。

Node 
Node 的職責是運行容器應用。Node 由 Master 管理,Node 負責監控並匯報容器的狀態,並根據 Master 的要求管理容器的生命周期。Node 運行在 Linux 操作系統,可以是物理機或者是虛擬機。

Pod 
Pod 是 Kubernetes 的最小工作單元。每個 Pod 包含一個或多個容器。Pod 中的容器會作為一個整體被 Master 調度到一個 Node 上運行。

Kubernetes 引入 Pod 主要基於下面兩個目的:

可管理性。
有些容器天生就是需要緊密聯系,一起工作。Pod 提供了比容器更高層次的抽象,將它們封裝到一個部署單元中。Kubernetes 以 Pod 為最小單位進行調度、擴展、共享資源、管理生命周期。

通信和資源共享。
Pod 中的所有容器使用同一個網絡 namespace,即相同的 IP 地址和 Port 空間。它們可以直接用 localhost 通信。同樣的,這些容器可以共享存儲,當 Kubernetes 掛載 volume 到 Pod,本質上是將 volume 掛載到 Pod 中的每一個容器。

Pods 有兩種使用方式:
運行單一容器。
one-container-per-Pod 是 Kubernetes 最常見的模型,這種情況下,只是將單個容器簡單封裝成 Pod。即便是只有一個容器,Kubernetes 管理的也是 Pod 而不是直接管理容器。

運行多個容器。
但問題在於:哪些容器應該放到一個 Pod 中? 
答案是:這些容器聯系必須 非常緊密,而且需要 直接共享資源。

Controller 
Kubernetes 通常不會直接創建 Pod,而是通過 Controller 來管理 Pod 的。Controller 中定義了 Pod 的部署特性,比如有幾個副本,在什么樣的 Node 上運行等。為了滿足不同的業務場景,Kubernetes 提供了多種 Controller,包括 Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等,我們逐一討論。

Deployment 是最常用的 Controller,比如前面在線教程中就是通過創建 Deployment 來部署應用的。Deployment 可以管理 Pod 的多個副本,並確保 Pod 按照期望的狀態運行。

ReplicaSet 實現了 Pod 的多副本管理。使用 Deployment 時會自動創建 ReplicaSet,也就是說 Deployment 是通過 ReplicaSet 來管理 Pod 的多個副本,我們通常不需要直接使用 ReplicaSet。

DaemonSet 用於每個 Node 最多只運行一個 Pod 副本的場景。正如其名稱所揭示的,DaemonSet 通常用於運行 daemon。

StatefuleSet 能夠保證 Pod 的每個副本在整個生命周期中名稱是不變的。而其他 Controller 不提供這個功能,當某個 Pod 發生故障需要刪除並重新啟動時,Pod 的名稱會發生變化。同時 StatefuleSet 會保證副本按照固定的順序啟動、更新或者刪除。

Job 用於運行結束就刪除的應用。而其他 Controller 中的 Pod 通常是長期持續運行。

Service 
Deployment 可以部署多個副本,每個 Pod 都有自己的 IP,外界如何訪問這些副本呢?

通過 Pod 的 IP 嗎?
要知道 Pod 很可能會被頻繁地銷毀和重啟,它們的 IP 會發生變化,用 IP 來訪問不太現實。

答案是 Service。
Kubernetes Service 定義了外界訪問一組特定 Pod 的方式。Service 有自己的 IP 和端口,Service 為 Pod 提供了負載均衡。

Kubernetes 運行容器(Pod)與訪問容器(Pod)這兩項任務分別由 Controller 和 Service 執行。

Namespace

如果有多個用戶或項目組使用同一個 Kubernetes Cluster,如何將他們創建的 Controller、Pod 等資源分開呢?

答案就是 Namespace。
Namespace 可以將一個物理的 Cluster 邏輯上划分成多個虛擬 Cluster,每個 Cluster 就是一個 Namespace。不同 Namespace 里的資源是完全隔離的。

Kubernetes 默認創建了兩個 Namespace。
$ kubectl get namespace
NAME          STATUS   AGE
default       Active   26m
kube-public   Active   26m
kube-system   Active   26m

default -- 創建資源時如果不指定,將被放到這個 Namespace 中。

kube-system -- Kubernetes 自己創建的系統資源將放到這個 Namespace 中。

5、Kubernetes Cluste

Kubernetes Cluster 由 Master 和 Node 組成,節點上運行着若干 Kubernetes 服務。

#k8s-master  1個節點

#k8s-node1、k8s-node2  多個node節點

所有的節點都已經 Ready,Kubernetes Cluster 創建成功。

Master 節點

Master 是 Kubernetes Cluster 的大腦,運行着如下 Daemon 服務:kube-apiserver、kube-scheduler、kube-controller-manager、etcd 和 Pod 網絡(例如 flannel)。

API Server(kube-apiserver)

API Server 提供 HTTP/HTTPS RESTful API,即 Kubernetes API。API Server 是 Kubernetes Cluster 的前端接口,各種客戶端工具(CLI 或 UI)以及 Kubernetes 其他組件可以通過它管理 Cluster 的各種資源。

Scheduler(kube-scheduler)

Scheduler 負責決定將 Pod 放在哪個 Node 上運行。Scheduler 在調度時會充分考慮 Cluster 的拓撲結構,當前各個節點的負載,以及應用對高可用、性能、數據親和性的需求。

Controller Manager(kube-controller-manager)

Controller Manager 負責管理 Cluster 各種資源,保證資源處於預期的狀態。Controller Manager 由多種 controller 組成,包括 replication controller、endpoints controller、namespace controller、serviceaccounts controller 等。

不同的 controller 管理不同的資源。例如 replication controller 管理 Deployment、StatefulSet、DaemonSet 的生命周期,namespace controller 管理 Namespace 資源。

etcd

etcd 負責保存 Kubernetes Cluster 的配置信息和各種資源的狀態信息。當數據發生變化時,etcd 會快速地通知 Kubernetes 相關組件。

Pod 網絡

Pod 要能夠相互通信,Kubernetes Cluster 必須部署 Pod 網絡,flannel 是其中一個可選方案。

Node節點

 Node 是 Pod 運行的地方,Kubernetes 支持 Docker、rkt 等容器 Runtime。 Node上運行的 Kubernetes 組件有 kubelet、kube-proxy 和 Pod 網絡(例如 flannel)。

kubelet

kubelet 是 Node 的 agent,當 Scheduler 確定在某個 Node 上運行 Pod 后,會將 Pod 的具體配置信息(image、volume 等)發送給該節點的 kubelet,kubelet 根據這些信息創建和運行容器,並向 Master 報告運行狀態。

kube-proxy

service 在邏輯上代表了后端的多個 Pod,外界通過 service 訪問 Pod。service 接收到的請求是如何轉發到 Pod 的呢?這就是 kube-proxy 要完成的工作。

每個 Node 都會運行 kube-proxy 服務,它負責將訪問 service 的 TCP/UPD 數據流轉發到后端的容器。如果有多個副本,kube-proxy 會實現負載均衡。

Pod 網絡

Pod 要能夠相互通信,Kubernetes Cluster 必須部署 Pod 網絡,flannel 是其中一個可選方案。

Kubernetes 組件本身也運行在 Pod 里,執行如下命令:

kubectl get pod --all-namespaces -o wide

 

三、kubernetes架構,演示各個組件之間是如何協作的。
2.1、建一個httpd的pod,有兩個副本
$ kubectl run httpd-app --image=httpd --replicas=2
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/httpd-app created

#查看deployment
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
httpd-app 0/2 2 0 10s
nginx-deployment 2/2 2 2 7m15s

#查看pod運行狀態
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpd-app-f9ccf4675-4dzk5 1/1 Running 0 6m6s 172.18.0.7 minikube <none> <none>
httpd-app-f9ccf4675-56ztr 1/1 Running 0 6m6s 172.18.0.6 minikube <none> <none>

2.2、部署過程

 

① kubectl 發送部署請求到 API Server。

② API Server 通知 Controller Manager 創建一個 deployment 資源。

③ Scheduler 執行調度任務,將兩個副本 Pod 分發到 k8s-node1 和 k8s-node2。

④ k8s-node1 和 k8s-node2 上的 kubelet 在各自的節點上創建並運行 Pod。

 

四、講解Deployment

Kubernetes 開發了 Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等多種 Controller

 示例:

 Deployment:

#將部署包含兩個副本的 Deployment nginx-deployment,容器的 image 為 nginx:1.7.9
kubectl run nginx-deployment --image=nginx:1.7.9 --replicas=2 kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead. #kubectl get deployment 命令可以查看 nginx-deployment 的狀態,輸出顯示兩個副本正常運行。 $ kubectl get deployment nginx-deployment NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 2/2 2 2 57s

#用kubectl describe deployment 了解更詳細的信息

$ kubectl describe deployment
Name:                   httpd-app
Namespace:              default
CreationTimestamp:      Fri, 10 May 2019 06:36:03 +0000
Labels:                 run=httpd-app
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               run=httpd-app
Replicas:               2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  run=httpd-app
  Containers:
   httpd-app:
    Image:        httpd
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   httpd-app-f9ccf4675 (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  15m   deployment-controller  Scaled up replica set httpd-app-f9ccf4675 to 2


Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Fri, 10 May 2019 06:28:58 +0000
Labels:                 run=nginx-deployment
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               run=nginx-deployment
Replicas:               2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  run=nginx-deployment
  Containers:
   nginx-deployment:
    Image:        nginx:1.7.9
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-578fb949d8 (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  22m   deployment-controller  Scaled up replica set nginx-deployment-578fb949d8 to 2

#備注:創建了一個 ReplicaSet nginx-deployment-1260880958Events 是 Deployment 的日志,記錄了 ReplicaSet 的啟動過程 

# Deployment 通過 ReplicaSet 來管理 Pod

$ kubectl get replicaset
NAME                          DESIRED   CURRENT   READY   AGE
httpd-app-f9ccf4675           2         2         2       18m
nginx-deployment-578fb949d8   2         2         2       25m

#查看副本詳細信息

$ kubectl describe replicaset
Name:           httpd-app-f9ccf4675
Namespace:      default
Selector:       pod-template-hash=f9ccf4675,run=httpd-app
Labels:         pod-template-hash=f9ccf4675
                run=httpd-app
Annotations:    deployment.kubernetes.io/desired-replicas: 2
                deployment.kubernetes.io/max-replicas: 3
                deployment.kubernetes.io/revision: 1
Controlled By:  Deployment/httpd-app
Replicas:       2 current / 2 desired
Pods Status:    2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  pod-template-hash=f9ccf4675
           run=httpd-app
  Containers:
   httpd-app:
    Image:        httpd
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  19m   replicaset-controller  Created pod: httpd-app-f9ccf4675-4dzk5
  Normal  SuccessfulCreate  19m   replicaset-controller  Created pod: httpd-app-f9ccf4675-56ztr


Name:           nginx-deployment-578fb949d8
Namespace:      default
Selector:       pod-template-hash=578fb949d8,run=nginx-deployment
Labels:         pod-template-hash=578fb949d8
                run=nginx-deployment
Annotations:    deployment.kubernetes.io/desired-replicas: 2
                deployment.kubernetes.io/max-replicas: 3
                deployment.kubernetes.io/revision: 1
Controlled By:  Deployment/nginx-deployment
Replicas:       2 current / 2 desired
Pods Status:    2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  pod-template-hash=578fb949d8
           run=nginx-deployment
  Containers:
   nginx-deployment:
    Image:        nginx:1.7.9
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  26m   replicaset-controller  Created pod: nginx-deployment-578fb949d8-56bfz
  Normal  SuccessfulCreate  26m   replicaset-controller  Created pod: nginx-deployment-578fb949d8-fbdvd

#總結創建過程

1、用戶通過 kubectl 創建 Deployment。
2、Deployment 創建 ReplicaSet。
3、ReplicaSet 創建 Pod。

  

備注:從上圖也可以看出,對象的命名方式是:子對象的名字 = 父對象名字 + 隨機字符串或數字

 

五、通過 kubectl run 創建的 Deployment

 Kubernetes 支持兩種方式創建資源:

1. 用 kubectl 命令直接創建,比如:

kubectl run nginx-deployment --image=nginx:1.7.9 --replicas=2

2、在命令行中通過參數指定資源的屬性。

 通過配置文件和 kubectl apply 創建。

cat nginx.yml

① apiVersion 是當前配置格式的版本。
② kind 是要創建的資源類型,這里是 Deployment
③ metadata 是該資源的元數據,name 是必需的元數據項。
④ spec 部分是該 Deployment 的規格說明。
⑤ replicas 指明副本數量,默認為 1。
⑥ template 定義 Pod 的模板,這是配置文件的重要部分。
⑦ metadata 定義 Pod 的元數據,至少要定義一個 label。label 的 key 和 value 可以任意指定。
⑧ spec 描述 Pod 的規格,此部分定義 Pod 中每一個容器的屬性,name 和 image 是必需的。

執行命令創建:

 

通過 kubectl get 查看 nginx-deployment 的各種資源:

Deployment、ReplicaSet、Pod 都已經就緒。如果要刪除這些資源,執行 kubectl delete deployment nginx-deployment 或者 kubectl delete -f nginx.yml

 總結:

a、用戶命令的方式,適合臨時測試

b、用配置文件的方式:

  1. 配置文件描述了 What,即應用最終要達到的狀態。

  2. 配置文件提供了創建資源的模板,能夠重復部署。

  3. 可以像管理代碼一樣管理部署。

  4. 適合正式的、跨環境的、規模化部署。

  5. 這種方式要求熟悉配置文件的語法,有一定難度。

后面我們都將采用配置文件的方式,大家需要盡快熟悉和掌握。

 

 六、伸縮(Scale Up/Down)是指在線增加或減少 Pod 的副本數。

Deployment nginx-deployment 初始是兩個副本。

k8s-node1 和 k8s-node2 上各跑了一個副本。現在修改 nginx.yml,將副本改成 5 個。

再次執行 kubectl apply

三個新副本被創建並調度到 k8s-node1 和 k8s-node2 上。

出於安全考慮,默認配置下 Kubernetes 不會將 Pod 調度到 Master 節點。如果希望將 k8s-master 也當作 Node 使用,可以執行如下命令:

kubectl taint node k8s-master node-role.kubernetes.io/master-

如果要恢復 Master Only 狀態,執行如下命令:

kubectl taint node k8s-master node-role.kubernetes.io/master="":NoSchedule

接下來修改配置文件,將副本數減少為 3 個,重新執行 kubectl apply

可以看到兩個副本被刪除,最終保留了 3 個副本。

 

七、模擬 k8s-node2 故障,關閉該節點。

我們有 3 個 nginx 副本分別運行在 k8s-node1 和 k8s-node2 上。現在模擬 k8s-node2 故障,關閉該節點。

等待一段時間,Kubernetes 會檢查到 k8s-node2 不可用,將 k8s-node2 上的 Pod 標記為 Unknown 狀態,並在 k8s-node1 上新創建兩個 Pod,維持總副本數為 3。

當 k8s-node2 恢復后,Unknown 的 Pod 會被刪除,不過已經運行的 Pod 不會重新調度回 k8s-node2。

刪除 nginx-deployment

 

八、label功能介紹

默認配置下,Scheduler 會將 Pod 調度到所有可用的 Node。不過有些情況我們希望將 Pod 部署到指定的 Node,比如將有大量磁盤 I/O 的 Pod 部署到配置了 SSD 的 Node;或者 Pod 需要 GPU,需要運行在配置了 GPU 的節點上。

Kubernetes 是通過 label 來實現這個功能的。

label 是 key-value 對,各種資源都可以設置 label,靈活添加各種自定義屬性。比如執行如下命令標注 k8s-node1 是配置了 SSD 的節點。

kubectl label node k8s-node1 disktype=ssd

然后通過 kubectl get node --show-labels 查看節點的 label。

disktype=ssd 已經成功添加到 k8s-node1,除了 disktype,Node 還有幾個 Kubernetes 自己維護的 label。

有了 disktype 這個自定義 label,接下來就可以指定將 Pod 部署到 k8s-node1。編輯 nginx.yml:

在 Pod 模板的 spec 里通過 nodeSelector 指定將此 Pod 部署到具有 label disktype=ssd 的 Node 上。

部署 Deployment 並查看 Pod 的運行節點:

全部 6 個副本都運行在 k8s-node1 上,符合我們的預期。

要刪除 label disktype,執行如下命令:

kubectl label node k8s-node1 disktype-

 - 即刪除。

不過此時 Pod 並不會重新部署,依然在 k8s-node1 上運行。

除非在 nginx.yml 中刪除 nodeSelector 設置,然后通過 kubectl apply 重新部署。

Kubernetes 會刪除之前的 Pod 並調度和運行新的 Pod。

Deployment 部署的副本 Pod 會分布在各個 Node 上,每個 Node 都可能運行好幾個副本。DaemonSet 的不同之處在於:每個 Node 上最多只能運行一個副本。

DaemonSet 的典型應用場景有:

  1. 在集群的每個節點上運行存儲 Daemon,比如 glusterd 或 ceph。

  2. 在每個節點上運行日志收集 Daemon,比如 flunentd 或 logstash。

  3. 在每個節點上運行監控 Daemon,比如 Prometheus Node Exporter 或 collectd。

其實 Kubernetes 自己就在用 DaemonSet 運行系統組件。執行如下命令:

kubectl get daemonset --namespace=kube-system


DaemonSet kube-flannel-ds 和 kube-proxy 分別負責在每個節點上運行 flannel 和 kube-proxy 組件。

因為 flannel 和 kube-proxy 屬於系統組件,需要在命令行中通過 --namespace=kube-system 指定 namespace kube-system。如果不指定則只返回默認 namespace default 中的資源。

 

九、本節詳細分析兩個 k8s 自己的 DaemonSet:kube-flannel-ds 和 kube-proxy 。

kube-flannel-ds

下面我們通過分析 kube-flannel-ds 來學習 DaemonSet。

還記得之前是如何部署 flannel 網絡的嗎?我們執行了如下兩個命令:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

flannel 的 DaemonSet 就定義在 kube-flannel.yml 中:

注:配置文件的完整內容要復雜些,為了更好地學習 DaemonSet,這里只保留了最重要的內容。

① DaemonSet 配置文件的語法和結構與 Deployment 幾乎完全一樣,只是將 kind 設為 DaemonSet

② hostNetwork 指定 Pod 直接使用的是 Node 的網絡,相當於 docker run --network=host。考慮到 flannel 需要為集群提供網絡連接,這個要求是合理的。

③ containers 定義了運行 flannel 服務的兩個容器。

我們再來分析另一個 DaemonSet kube-proxy

kube-proxy

由於無法拿到 kube-proxy 的 YAML 文件,只能運行如下命令查看其配置:

kubectl edit daemonset kube-proxy --namespace=kube-system

同樣為了便於理解,這里只保留了最重要的信息。

① kind: DaemonSet 指定這是一個 DaemonSet 類型的資源。

② containers 定義了 kube-proxy 的容器。

③ status 是當前 DaemonSet 的運行時狀態,這個部分是 kubectl edit特有的。其實 Kubernetes 集群中每個當前運行的資源都可以通過 kubectl edit 查看其配置和運行狀態,比如 kubectl edit deployment nginx-deployment

 

十、如何運行自己的 DaemonSet

以 Prometheus Node Exporter 為例演示如何運行自己的 DaemonSet。

Prometheus 是流行的系統監控方案,Node Exporter 是 Prometheus 的 agent,以 Daemon 的形式運行在每個被監控節點上。

如果是直接在 Docker 中運行 Node Exporter 容器,命令為:

docker run -d \
-v "/proc:/host/proc" \
-v "/sys:/host/sys" \
-v "/:/rootfs" \
--net=host \ prom/node-exporter \
--path.procfs /host/proc \
--path.sysfs /host/sys \
--collector.filesystem.ignored-mount-points "^/(sys|proc|dev|host|etc)($|/)"

將其轉換為 DaemonSet 的 YAML 配置文件 node_exporter.yml:

① 直接使用 Host 的網絡。
② 設置容器啟動命令。
③ 通過 Volume 將 Host 路徑 /proc/sys 和 / 映射到容器中。我們將在后面詳細討論 Volume。

執行 kubectl apply -f node_exporter.yml

DaemonSet node-exporter-daemonset 部署成功,k8s-node1 和 k8s-node2 上分別運行了一個 node exporter Pod。

 

十一、容器按照持續運行的時間進行分類

容器按照持續運行的時間可分為兩類:服務類容器和工作類容器。

服務類容器通常持續提供服務,需要一直運行,比如 http server,daemon 等。工作類容器則是一次性任務,比如批處理程序,完成后容器就退出。

Kubernetes 的 Deployment、ReplicaSet 和 DaemonSet 都用於管理服務類容器;對於工作類容器,我們用 Job。

先看一個簡單的 Job 配置文件 myjob.yml:

① batch/v1 是當前 Job 的 apiVersion

② 指明當前資源的類型為 Job

③ restartPolicy 指定什么情況下需要重啟容器。對於 Job,只能設置為 Never 或者 OnFailure。對於其他 controller(比如 Deployment)可以設置為 Always 。

通過 kubectl apply -f myjob.yml 啟動 Job。

kubectl get job 查看 Job 的狀態:

DESIRED 和 SUCCESSFUL 都為 1,表示按照預期啟動了一個 Pod,並且已經成功執行。kubectl get pod 查看 Pod 的狀態:

因為 Pod 執行完畢后容器已經退出,需要用 --show-all 才能查看 Completed 狀態的 Pod。

kubectl logs 可以查看 Pod 的標准輸出:

 

十一、Job執行失敗的解決方法

如果Job執行失敗了會怎么樣呢?

修改 myjob.yml,故意引入一個錯誤:

646.png

先刪除之前的 Job:

648.png

如果將 restartPolicy 設置為 OnFailure 會怎么樣?下面我們實踐一下,修改 myjob.yml 后重新啟動。

運行新的 Job 並查看狀態:

647.png

當前 SUCCESSFUL 的 Pod 數量為 0,查看 Pod 的狀態:

649.png

可以看到有多個 Pod,狀態均不正常。kubectl describe pod 查看某個 Pod 的啟動日志:

650.png

日志顯示沒有可執行程序,符合我們的預期。

下面解釋一個現象:為什么 kubectl get pod 會看到這么多個失敗的 Pod?

原因是:當第一個 Pod 啟動時,容器失敗退出,根據 restartPolicy: Never,此失敗容器不會被重啟,但 Job DESIRED 的 Pod 是 1,目前 SUCCESSFUL 為 0,不滿足,所以 Job controller 會啟動新的 Pod,直到 SUCCESSFUL 為 1。對於我們這個例子,SUCCESSFUL 永遠也到不了 1,所以 Job controller 會一直創建新的 Pod。為了終止這個行為,只能刪除 Job。

648.png

如果將 restartPolicy 設置為 OnFailure 會怎么樣?下面我們實踐一下,修改 myjob.yml 后重新啟動。

651.png

Job 的 SUCCESSFUL Pod 數量還是為 0,看看 Pod 的情況:

652.png

這里只有一個 Pod,不過 RESTARTS 為 3,而且不斷增加,說明 OnFailure 生效,容器失敗后會自動重啟。

 

十二、提高 Job 執行效率的方法。

我們希望能同時運行多個 Pod,提高 Job 的執行效率。這個可以通過 parallelism 設置。

這里我們將並行的 Pod 數量設置為 2,實踐一下:

Job 一共啟動了兩個 Pod,而且 AGE 相同,可見是並行運行的。

我們還可以通過 completions 設置 Job 成功完成 Pod 的總數:

上面配置的含義是:每次運行兩個 Pod,直到總共有 6 個 Pod 成功完成。實踐一下:

DESIRED 和 SUCCESSFUL 均為 6,符合預期。如果不指定 completions 和 parallelism,默認值均為 1

上面的例子只是為了演示 Job 的並行特性,實際用途不大。不過現實中確實存在很多需要並行處理的場景。比如批處理程序,每個副本(Pod)都會從任務池中讀取任務並執行,副本越多,執行時間就越短,效率就越高。這種類似的場景都可以用 Job 來實現。

 

十三、如何定時執行 Job

Linux 中有 cron 程序定時執行任務,Kubernetes 的 CronJob 提供了類似的功能,可以定時執行 Job。CronJob 配置文件示例如下:

① batch/v2alpha1 是當前 CronJob 的 apiVersion

② 指明當前資源的類型為 CronJob

③ schedule 指定什么時候運行 Job,其格式與 Linux cron 一致。這里 */1 * * * * 的含義是每一分鍾啟動一次。

④ jobTemplate 定義 Job 的模板,格式與前面 Job 一致。

接下來通過 kubectl apply 創建 CronJob。

失敗了。這是因為 Kubernetes 默認沒有 enable CronJob 功能,需要在 kube-apiserver 中加入這個功能。方法很簡單,修改 kube-apiserver 的配置文件 /etc/kubernetes/manifests/kube-apiserver.yaml:

kube-apiserver 本身也是個 Pod,在啟動參數中加上 --runtime-config=batch/v2alpha1=true 即可。

然后重啟 kubelet 服務:

systemctl restart kubelet.service

kubelet 會重啟 kube-apiserver Pod。通過 kubectl api-versions 確認 kube-apiserver 現在已經支持 batch/v2alpha1

再次創建CronJob:

這次成功了。通過 kubectl get cronjob 查看 CronJob 的狀態:

等待幾分鍾,然后通過 kubectl get jobs 查看 Job 的執行情況:

可以看到每隔一分鍾就會啟動一個 Job。執行 kubectl logs 可查看某個 Job 的運行日志:

小結

運行容器化應用是 Kubernetes 最重要的核心功能。為滿足不同的業務需要,Kubernetes 提供了多種 Controller,包括 Deployment、DaemonSet、Job、CronJob 等。本章我們通過實踐詳細學習了這些 Controller,並討論了它們的特性和應用場景。

 

十四、講解 Service

我們不應該期望 Kubernetes Pod 是健壯的,而是要假設 Pod 中的容器很可能因為各種原因發生故障而死掉。Deployment 等 controller 會通過動態創建和銷毀 Pod 來保證應用整體的健壯性。換句話說,Pod 是脆弱的,但應用是健壯的。

每個 Pod 都有自己的 IP 地址。當 controller 用新 Pod 替代發生故障的 Pod 時,新 Pod 會分配到新的 IP 地址。這樣就產生了一個問題:

如果一組 Pod 對外提供服務(比如 HTTP),它們的 IP 很有可能發生變化,那么客戶端如何找到並訪問這個服務呢?

Kubernetes 給出的解決方案是 Service。

創建 Service

Kubernetes Service 從邏輯上代表了一組 Pod,具體是哪些 Pod 則是由 label 來挑選。Service 有自己 IP,而且這個 IP 是不變的。客戶端只需要訪問 Service 的 IP,Kubernetes 則負責建立和維護 Service 與 Pod 的映射關系。無論后端 Pod 如何變化,對客戶端不會有任何影響,因為 Service 沒有變。

來看個例子,創建下面的這個 Deployment:

我們啟動了三個 Pod,運行 httpd 鏡像,label 是 run: httpd,Service 將會用這個 label 來挑選 Pod。

Pod 分配了各自的 IP,這些 IP 只能被 Kubernetes Cluster 中的容器和節點訪問。

接下來創建 Service,其配置文件如下:

① v1 是 Service 的 apiVersion

② 指明當前資源的類型為 Service

③ Service 的名字為 httpd-svc

④ selector 指明挑選那些 label 為 run: httpd 的 Pod 作為 Service 的后端。

⑤ 將 Service 的 8080 端口映射到 Pod 的 80 端口,使用 TCP 協議。

執行 kubectl apply 創建 Service httpd-svc

httpd-svc 分配到一個 CLUSTER-IP 10.99.229.179。可以通過該 IP 訪問后端的 httpd Pod。

根據前面的端口映射,這里要使用 8080 端口。另外,除了我們創建的 httpd-svc,還有一個 Service kubernetes,Cluster 內部通過這個 Service 訪問 kubernetes API Server。

通過 kubectl describe 可以查看 httpd-svc 與 Pod 的對應關系。

Endpoints 羅列了三個 Pod 的 IP 和端口。我們知道 Pod 的 IP 是在容器中配置的,那么 Service 的 Cluster IP 又是配置在哪里的呢?CLUSTER-IP 又是如何映射到 Pod IP 的呢?

 

十五、Service IP 原理

Service Cluster IP 是一個虛擬 IP,是由 Kubernetes 節點上的 iptables 規則管理的。

可以通過 iptables-save 命令打印出當前節點的 iptables 規則,因為輸出較多,這里只截取與 httpd-svc Cluster IP 10.99.229.179 相關的信息:

這兩條規則的含義是:

  1. 如果 Cluster 內的 Pod(源地址來自 10.244.0.0/16)要訪問 httpd-svc,則允許。

  2. 其他源地址訪問 httpd-svc,跳轉到規則 KUBE-SVC-RL3JAE4GN7VOGDGP

KUBE-SVC-RL3JAE4GN7VOGDGP 規則如下:

  1. 1/3 的概率跳轉到規則 KUBE-SEP-C5KB52P4BBJQ35PH

  2. 1/3 的概率(剩下 2/3 的一半)跳轉到規則 KUBE-SEP-HGVKQQZZCF7RV4IT

  3. 1/3 的概率跳轉到規則 KUBE-SEP-XE25WGVXLHEIRVO5

上面三個跳轉的規則如下:

即將請求分別轉發到后端的三個 Pod。通過上面的分析,我們得到如下結論:

iptables 將訪問 Service 的流量轉發到后端 Pod,而且使用類似輪詢的負載均衡策略。

另外需要補充一點:Cluster 的每一個節點都配置了相同的 iptables 規則,這樣就確保了整個 Cluster 都能夠通過 Service 的 Cluster IP 訪問 Service。

除了直接通過 Cluster IP 訪問到 Service,DNS 是更加便捷的方式。

 

十六、DNS訪問Service

在 Cluster 中,除了可以通過 Cluster IP 訪問 Service,Kubernetes 還提供了更為方便的 DNS 訪問。

kubeadm 部署時會默認安裝 kube-dns 組件。

kube-dns 是一個 DNS 服務器。每當有新的 Service 被創建,kube-dns 會添加該 Service 的 DNS 記錄。Cluster 中的 Pod 可以通過 <SERVICE_NAME>.<NAMESPACE_NAME> 訪問 Service。

比如可以用 httpd-svc.default 訪問 Service httpd-svc

如上所示,我們在一個臨時的 busybox Pod 中驗證了 DNS 的有效性。另外,由於這個 Pod 與 httpd-svc 同屬於 default namespace,可以省略 default 直接用 httpd-svc 訪問 Service。

用 nslookup 查看 httpd-svc 的 DNS 的信息。

DNS 服務器是 kube-dns.kube-system.svc.cluster.local,這實際上就是 kube-dns 組件,它本身是部署在 kube-system namespace 中的一個 Service。

httpd-svc.default.svc.cluster.local 是 httpd-svc 的完整域名。

如果要訪問其他 namespace 中的 Service,就必須帶上 namesapce 了。kubectl get namespace 查看已有的 namespace。

在 kube-public 中部署 Service httpd2-svc,配置如下:

通過 namespace: kube-public 指定資源所屬的 namespace。多個資源可以在一個 YAML 文件中定義,用 --- 分割。執行 kubectl apply 創建資源:

查看 kube-public 的 Service:

在 busybox Pod 中訪問 httpd2-svc

因為屬於不同的 namespace,必須使用 httpd2-svc.kube-public 才能訪問到。

Kubernetes 集群內部可以通過 Cluster IP 和 DNS 訪問 Service。

 

十七、外網如何訪問Service

除了 Cluster 內部可以訪問 Service,很多情況我們也希望應用的 Service 能夠暴露給 Cluster 外部。Kubernetes 提供了多種類型的 Service,默認是 ClusterIP。

ClusterIP 
Service 通過 Cluster 內部的 IP 對外提供服務,只有 Cluster 內的節點和 Pod 可訪問,這是默認的 Service 類型,前面實驗中的 Service 都是 ClusterIP。

NodePort 
Service 通過 Cluster 節點的靜態端口對外提供服務。Cluster 外部可以通過 <NodeIP>:<NodePort> 訪問 Service。

LoadBalancer 
Service 利用 cloud provider 特有的 load balancer 對外提供服務,cloud provider 負責將 load balancer 的流量導向 Service。目前支持的 cloud provider 有 GCP、AWS、Azur 等。

下面我們來實踐 NodePort,Service httpd-svc 的配置文件修改如下:

添加 type: NodePort,重新創建 httpd-svc

Kubernetes 依然會為 httpd-svc 分配一個 ClusterIP,不同的是:

  1. EXTERNAL-IP 為 nodes,表示可通過 Cluster 每個節點自身的 IP 訪問 Service。

  2. PORT(S) 為 8080:323128080 是 ClusterIP 監聽的端口,32312 則是節點上監聽的端口。Kubernetes 會從 30000-32767 中分配一個可用的端口,每個節點都會監聽此端口並將請求轉發給 Service。

下面測試 NodePort 是否正常工作。

通過三個節點 IP + 32312 端口都能夠訪問 httpd-svc

接下來我們深入探討一個問題:Kubernetes 是如何將 <NodeIP>:<NodePort> 映射到 Pod 的呢?

與 ClusterIP 一樣,也是借助了 iptables。與 ClusterIP 相比,每個節點的 iptables 中都增加了下面兩條規則:

規則的含義是:訪問當前節點 32312 端口的請求會應用規則 KUBE-SVC-RL3JAE4GN7VOGDGP,內容為:

其作用就是負載均衡到每一個 Pod。

NodePort 默認是的隨機選擇,不過我們可以用 nodePort 指定某個特定端口。

現在配置文件中就有三個 Port 了:
nodePort 是節點上監聽的端口。
port 是 ClusterIP 上監聽的端口。
targetPort 是 Pod 監聽的端口。

最終,Node 和 ClusterIP 在各自端口上接收到的請求都會通過 iptables 轉發到 Pod 的 targetPort

應用新的 nodePort 並驗證:

nodePort: 30000 已經生效了。

小結

本章我們討論訪問應用的機制 Service,學習了如何創建 Service;Service 的三種類型 ClusterIP、NodePort 和 LoadBalancer,以及它們各自的適用場景。

 

十八、Rolling Update

滾動更新是一次只更新一小部分副本,成功后,再更新更多的副本,最終完成所有副本的更新。滾動更新的最大的好處是零停機,整個更新過程始終有副本在運行,從而保證了業務的連續性。

下面我們部署三副本應用,初始鏡像為 httpd:2.2.31,然后將其更新到 httpd:2.2.32。

httpd:2.2.31 的配置文件如下:

通過 kubectl apply 部署。

部署過程如下:

  1. 創建 Deployment httpd

  2. 創建 ReplicaSet httpd-551879778

  3. 創建三個 Pod

  4. 當前鏡像為 httpd:2.2.31

將配置文件中 httpd:2.2.31 替換為 httpd:2.2.32,再次執行 kubectl apply

我們發現了如下變化:

  1. Deployment httpd 的鏡像更新為 httpd:2.2.32

  2. 新創建了 ReplicaSet httpd-1276601241,鏡像為 httpd:2.2.32,並且管理了三個新的 Pod。

  3. 之前的 ReplicaSet httpd-551879778 里面已經沒有任何 Pod。

結論是:ReplicaSet httpd-551879778 的三個 httpd:2.2.31 Pod 已經被 ReplicaSet httpd-1276601241 的三個 httpd:2.2.32 Pod 替換了。

具體過程可以通過 kubectl describe deployment httpd 查看。

每次只更新替換一個 Pod:

  1. ReplicaSet httpd-1276601241 增加一個 Pod,總數為 1。

  2. ReplicaSet httpd-551879778 減少一個 Pod,總數為 2。

  3. ReplicaSet httpd-1276601241 增加一個 Pod,總數為 2。

  4. ReplicaSet httpd-551879778 減少一個 Pod,總數為 1。

  5. ReplicaSet httpd-1276601241 增加一個 Pod,總數為 3。

  6. ReplicaSet httpd-551879778 減少一個 Pod,總數為 0。

每次替換的 Pod 數量是可以定制的。Kubernetes 提供了兩個參數 maxSurge 和 maxUnavailable 來精細控制 Pod 的替換數量,我們將在后面結合 Health Check 特性一起討論。

 

十九、回滾

kubectl apply 每次更新應用時 Kubernetes 都會記錄下當前的配置,保存為一個 revision(版次),這樣就可以回滾到某個特定 revision。

默認配置下,Kubernetes 只會保留最近的幾個 revision,可以在 Deployment 配置文件中通過 revisionHistoryLimit 屬性增加 revision 數量。

下面實踐回滾功能。應用有如下三個配置文件 httpd.v1.ymlhttpd.v2.yml 和 httpd.v3.yml,分別對應不同的 httpd 鏡像 2.4.162.4.17 和 2.4.18

通過 kubectl apply 部署並更新應用:

--record 的作用是將當前命令記錄到 revision 記錄中,這樣我們就可以知道每個 revison 對應的是哪個配置文件。通過 kubectl rollout history deployment httpd 查看 revison 歷史記錄。

CHANGE-CAUSE 就是 --record 的結果。如果要回滾到某個版本,比如 revision 1,可以執行命令 kubectl rollout undo deployment httpd --to-revision=1

此時,revison 歷史記錄也會發生相應變化。

revison 1 變成了 revison 4。不過我們可以通過 CHANGE-CAUSE 知道每個 revison 的具體含義。所以一定要在執行 kubectl apply 時加上 --record參數。

 

二十、Health Check(健康檢查)

強大的自愈能力是 Kubernetes 這類容器編排引擎的一個重要特性。自愈的默認實現方式是自動重啟發生故障的容器。除此之外,用戶還可以利用 Liveness 和 Readiness 探測機制設置更精細的健康檢查,進而實現如下需求:

  1. 零停機部署。

  2. 避免部署無效的鏡像。

  3. 更加安全的滾動升級。

下面通過實踐學習 Kubernetes 的 Health Check 功能。

默認的健康檢查

我們首先學習 Kubernetes 默認的健康檢查機制:

每個容器啟動時都會執行一個進程,此進程由 Dockerfile 的 CMD 或 ENTRYPOINT 指定。如果進程退出時返回碼非零,則認為容器發生故障,Kubernetes 就會根據 restartPolicy 重啟容器。

下面我們模擬一個容器發生故障的場景,Pod 配置文件如下:

Pod 的 restartPolicy 設置為 OnFailure,默認為 Always

sleep 10; exit 1 模擬容器啟動 10 秒后發生故障。

執行 kubectl apply 創建 Pod,命名為 healthcheck

過幾分鍾查看 Pod 的狀態:

可看到容器當前已經重啟了 3 次。

在上面的例子中,容器進程返回值非零,Kubernetes 則認為容器發生故障,需要重啟。但有不少情況是發生了故障,但進程並不會退出。比如訪問 Web 服務器時顯示 500 內部錯誤,可能是系統超載,也可能是資源死鎖,此時 httpd 進程並沒有異常退出,在這種情況下重啟容器可能是最直接最有效的解決方案,那我們如何利用 Health Check 機制來處理這類場景呢?

 

二十一、Liveness 探測

Liveness 探測讓用戶可以自定義判斷容器是否健康的條件。如果探測失敗,Kubernetes 就會重啟容器。

還是舉例說明,創建如下 Pod:

啟動進程首先創建文件 /tmp/healthy,30 秒后刪除,在我們的設定中,如果 /tmp/healthy 文件存在,則認為容器處於正常狀態,反正則發生故障。

livenessProbe 部分定義如何執行 Liveness 探測:

  1. 探測的方法是:通過 cat 命令檢查 /tmp/healthy 文件是否存在。如果命令執行成功,返回值為零,Kubernetes 則認為本次 Liveness 探測成功;如果命令返回值非零,本次 Liveness 探測失敗。

  2. initialDelaySeconds: 10 指定容器啟動 10 之后開始執行 Liveness 探測,我們一般會根據應用啟動的准備時間來設置。比如某個應用正常啟動要花 30 秒,那么 initialDelaySeconds 的值就應該大於 30。

  3. periodSeconds: 5 指定每 5 秒執行一次 Liveness 探測。Kubernetes 如果連續執行 3 次 Liveness 探測均失敗,則會殺掉並重啟容器。

下面創建 Pod liveness

從配置文件可知,最開始的 30 秒,/tmp/healthy 存在,cat 命令返回 0,Liveness 探測成功,這段時間 kubectl describe pod liveness 的 Events部分會顯示正常的日志。

35 秒之后,日志會顯示 /tmp/healthy 已經不存在,Liveness 探測失敗。再過幾十秒,幾次探測都失敗后,容器會被重啟。

 

二十二、Readiness 探測

除了 Liveness 探測,Kubernetes Health Check 機制還包括 Readiness 探測。

用戶通過 Liveness 探測可以告訴 Kubernetes 什么時候通過重啟容器實現自愈;Readiness 探測則是告訴 Kubernetes 什么時候可以將容器加入到 Service 負載均衡池中,對外提供服務。

Readiness 探測的配置語法與 Liveness 探測完全一樣,下面是個例子:

這個配置文件只是將前面例子中的 liveness 替換為了 readiness,我們看看有什么不同的效果。

Pod readiness 的 READY 狀態經歷了如下變化:

  1. 剛被創建時,READY 狀態為不可用。

  2. 15 秒后(initialDelaySeconds + periodSeconds),第一次進行 Readiness 探測並成功返回,設置 READY 為可用。

  3. 30 秒后,/tmp/healthy 被刪除,連續 3 次 Readiness 探測均失敗后,READY 被設置為不可用。

通過 kubectl describe pod readiness 也可以看到 Readiness 探測失敗的日志。

下面對 Liveness 探測和 Readiness 探測做個比較:

  1. Liveness 探測和 Readiness 探測是兩種 Health Check 機制,如果不特意配置,Kubernetes 將對兩種探測采取相同的默認行為,即通過判斷容器啟動進程的返回值是否為零來判斷探測是否成功。

  2. 兩種探測的配置方法完全一樣,支持的配置參數也一樣。不同之處在於探測失敗后的行為:Liveness 探測是重啟容器;Readiness 探測則是將容器設置為不可用,不接收 Service 轉發的請求。

  3. Liveness 探測和 Readiness 探測是獨立執行的,二者之間沒有依賴,所以可以單獨使用,也可以同時使用。用 Liveness 探測判斷容器是否需要重啟以實現自愈;用 Readiness 探測判斷容器是否已經准備好對外提供服務。

二十三、在 Scale Up 中使用 Health Check

對於多副本應用,當執行 Scale Up 操作時,新副本會作為 backend 被添加到 Service 的負載均衡中,與已有副本一起處理客戶的請求。考慮到應用啟動通常都需要一個准備階段,比如加載緩存數據,連接數據庫等,從容器啟動到正真能夠提供服務是需要一段時間的。我們可以通過 Readiness 探測判斷容器是否就緒,避免將請求發送到還沒有 ready 的 backend。

下面是示例應用的配置文件。

重點關注 readinessProbe 部分。這里我們使用了不同於 exec 的另一種探測方法 -- httpGet。Kubernetes 對於該方法探測成功的判斷條件是 http 請求的返回代碼在 200-400 之間。

schema 指定協議,支持 HTTP(默認值)和 HTTPS
path 指定訪問路徑。
port 指定端口。

上面配置的作用是:

  1. 容器啟動 10 秒之后開始探測。

  2. 如果 http://[container_ip]:8080/healthy 返回代碼不是 200-400,表示容器沒有就緒,不接收 Service web-svc 的請求。

  3. 每隔 5 秒再探測一次。

  4. 直到返回代碼為 200-400,表明容器已經就緒,然后將其加入到 web-svc 的負責均衡中,開始處理客戶請求。

  5. 探測會繼續以 5 秒的間隔執行,如果連續發生 3 次失敗,容器又會從負載均衡中移除,直到下次探測成功重新加入。

對於 http://[container_ip]:8080/healthy,應用則可以實現自己的判斷邏輯,比如檢查所依賴的數據庫是否就緒,示例代碼如下:

① 定義 /healthy 的處理函數。

② 連接數據庫並執行測試 SQL。

③ 測試成功,正常返回,代碼 200。

④ 測試失敗,返回錯誤代碼 503。

⑤ 在 8080 端口監聽。

對於生產環境中重要的應用都建議配置 Health Check,保證處理客戶請求的容器都是准備就緒的 Service backend。

以上是 Health Check 在 Scale Up 中的應用。

 

二十四、在Rolling Update 中使用Health Check 

上一節討論了 Health Check 在 Scale Up 中的應用,Health Check 另一個重要的應用場景是 Rolling Update。試想一下下面的情況:

現有一個正常運行的多副本應用,接下來對應用進行更新(比如使用更高版本的 image),Kubernetes 會啟動新副本,然后發生了如下事件:

  1. 正常情況下新副本需要 10 秒鍾完成准備工作,在此之前無法響應業務請求。

  2. 但由於人為配置錯誤,副本始終無法完成准備工作(比如無法連接后端數據庫)。

先別繼續往下看,現在請花一分鍾思考這個問題:如果沒有配置 Health Check,會出現怎樣的情況?

因為新副本本身沒有異常退出,默認的 Health Check 機制會認為容器已經就緒,進而會逐步用新副本替換現有副本,其結果就是:當所有舊副本都被替換后,整個應用將無法處理請求,無法對外提供服務。如果這是發生在重要的生產系統上,后果會非常嚴重。

如果正確配置了 Health Check,新副本只有通過了 Readiness 探測,才會被添加到 Service;如果沒有通過探測,現有副本不會被全部替換,業務仍然正常進行。

下面通過例子來實踐 Health Check 在 Rolling Update 中的應用。

用如下配置文件 app.v1.yml 模擬一個 10 副本的應用:

10 秒后副本能夠通過 Readiness 探測。

接下來滾動更新應用,配置文件 app.v2.yml 如下:

很顯然,由於新副本中不存在 /tmp/healthy,是無法通過 Readiness 探測的。驗證如下:

這個截圖包含了大量的信息,值得我們詳細分析。

先關注 kubectl get pod 輸出:

  1. 從 Pod 的 AGE 欄可判斷,最后 5 個 Pod 是新副本,目前處於 NOT READY 狀態。

  2. 舊副本從最初 10 個減少到 8 個。

再來看 kubectl get deployment app 的輸出:

  1. DESIRED 10 表示期望的狀態是 10 個 READY 的副本。

  2. CURRENT 13 表示當前副本的總數:即 8 個舊副本 + 5 個新副本。

  3. UP-TO-DATE 5 表示當前已經完成更新的副本數:即 5 個新副本。

  4. AVAILABLE 8 表示當前處於 READY 狀態的副本數:即 8個舊副本。

在我們的設定中,新副本始終都無法通過 Readiness 探測,所以這個狀態會一直保持下去。

上面我們模擬了一個滾動更新失敗的場景。不過幸運的是:Health Check 幫我們屏蔽了有缺陷的副本,同時保留了大部分舊副本,業務沒有因更新失敗受到影響。

接下來我們要回答:為什么新創建的副本數是 5 個,同時只銷毀了 2 個舊副本?

原因是:滾動更新通過參數 maxSurge 和 maxUnavailable 來控制副本替換的數量。

maxSurge

此參數控制滾動更新過程中副本總數的超過 DESIRED 的上限。maxSurge 可以是具體的整數(比如 3),也可以是百分百,向上取整。maxSurge 默認值為 25%。

在上面的例子中,DESIRED 為 10,那么副本總數的最大值為:
roundUp(10 + 10 * 25%) = 13

所以我們看到 CURRENT 就是 13。

maxUnavailable

此參數控制滾動更新過程中,不可用的副本相占 DESIRED 的最大比例。 maxUnavailable 可以是具體的整數(比如 3),也可以是百分百,向下取整。maxUnavailable 默認值為 25%。

在上面的例子中,DESIRED 為 10,那么可用的副本數至少要為:
10 - roundDown(10 * 25%) = 8

所以我們看到 AVAILABLE 就是 8。

maxSurge 值越大,初始創建的新副本數量就越多;maxUnavailable 值越大,初始銷毀的舊副本數量就越多。

理想情況下,我們這個案例滾動更新的過程應該是這樣的:

  1. 首先創建 3 個新副本使副本總數達到 13 個。

  2. 然后銷毀 2 個舊副本使可用的副本數降到 8 個。

  3. 當這 2 個舊副本成功銷毀后,可再創建 2 個新副本,使副本總數保持為 13 個。

  4. 當新副本通過 Readiness 探測后,會使可用副本數增加,超過 8。

  5. 進而可以繼續銷毀更多的舊副本,使可用副本數回到 8。

  6. 舊副本的銷毀使副本總數低於 13,這樣就允許創建更多的新副本。

  7. 這個過程會持續進行,最終所有的舊副本都會被新副本替換,滾動更新完成。

而我們的實際情況是在第 4 步就卡住了,新副本無法通過 Readiness 探測。這個過程可以在 kubectl describe deployment app 的日志部分查看。

如果滾動更新失敗,可以通過 kubectl rollout undo 回滾到上一個版本。

如果要定制 maxSurge 和 maxUnavailable,可以如下配置:

小結

本章我們討論了 Kubernetes 健康檢查的兩種機制:Liveness 探測和 Readiness 探測,並實踐了健康檢查在 Scale Up 和 Rolling Update 場景中的應用。

 

二十五、Kubernetes 如何管理存儲資源 

首先我們會學習 Volume,以及 Kubernetes 如何通過 Volume 為集群中的容器提供存儲;然后我們會實踐幾種常用的 Volume 類型並理解它們各自的應用場景;最后,我們會討論 Kubernetes 如何通過 Persistent Volume 和 Persistent Volume Claim 分離集群管理員與集群用戶的職責,並實踐 Volume 的靜態供給和動態供給。

Volume

本節我們討論 Kubernetes 的存儲模型 Volume,學習如何將各種持久化存儲映射到容器。

我們經常會說:容器和 Pod 是短暫的。
其含義是它們的生命周期可能很短,會被頻繁地銷毀和創建。容器銷毀時,保存在容器內部文件系統中的數據都會被清除。

為了持久化保存容器的數據,可以使用 Kubernetes Volume。

Volume 的生命周期獨立於容器,Pod 中的容器可能被銷毀和重建,但 Volume 會被保留。

本質上,Kubernetes Volume 是一個目錄,這一點與 Docker Volume 類似。當 Volume 被 mount 到 Pod,Pod 中的所有容器都可以訪問這個 Volume。Kubernetes Volume 也支持多種 backend 類型,包括 emptyDir、hostPath、GCE Persistent Disk、AWS Elastic Block Store、NFS、Ceph 等,完整列表可參考 https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes

Volume 提供了對各種 backend 的抽象,容器在使用 Volume 讀寫數據的時候不需要關心數據到底是存放在本地節點的文件系統中呢還是雲硬盤上。對它來說,所有類型的 Volume 都只是一個目錄。

我們將從最簡單的 emptyDir 開始學習 Kubernetes Volume。

emptyDir

emptyDir 是最基礎的 Volume 類型。正如其名字所示,一個 emptyDir Volume 是 Host 上的一個空目錄。

emptyDir Volume 對於容器來說是持久的,對於 Pod 則不是。當 Pod 從節點刪除時,Volume 的內容也會被刪除。但如果只是容器被銷毀而 Pod 還在,則 Volume 不受影響。

也就是說:emptyDir Volume 的生命周期與 Pod 一致。

Pod 中的所有容器都可以共享 Volume,它們可以指定各自的 mount 路徑。下面通過例子來實踐 emptyDir,配置文件如下:

這里我們模擬了一個 producer-consumer 場景。Pod 有兩個容器 producer和 consumer,它們共享一個 Volume。producer 負責往 Volume 中寫數據,consumer 則是從 Volume 讀取數據。

① 文件最底部 volumes 定義了一個 emptyDir 類型的 Volume shared-volume

② producer 容器將 shared-volume mount 到 /producer_dir 目錄。

③ producer 通過 echo 將數據寫到文件 hello 里。

④ consumer 容器將 shared-volume mount 到 /consumer_dir 目錄。

⑤ consumer 通過 cat 從文件 hello 讀數據。

執行如下命令創建 Pod:

kubectl logs 顯示容器 consumer 成功讀到了 producer 寫入的數據,驗證了兩個容器共享 emptyDir Volume。

因為 emptyDir 是 Docker Host 文件系統里的目錄,其效果相當於執行了 docker run -v /producer_dir 和 docker run -v /consumer_dir。通過 docker inspect 查看容器的詳細配置信息,我們發現兩個容器都 mount 了同一個目錄:

這里 /var/lib/kubelet/pods/3e6100eb-a97a-11e7-8f72-0800274451ad/volumes/kubernetes.io~empty-dir/shared-volume 就是 emptyDir 在 Host 上的真正路徑。

emptyDir 是 Host 上創建的臨時目錄,其優點是能夠方便地為 Pod 中的容器提供共享存儲,不需要額外的配置。但它不具備持久性,如果 Pod 不存在了,emptyDir 也就沒有了。根據這個特性,emptyDir 特別適合 Pod 中的容器需要臨時共享存儲空間的場景,比如前面的生產者消費者用例。

 

二十六、 hostPath Volume

hostPath Volume 的作用是將 Docker Host 文件系統中已經存在的目錄 mount 給 Pod 的容器。大部分應用都不會使用 hostPath Volume,因為這實際上增加了 Pod 與節點的耦合,限制了 Pod 的使用。不過那些需要訪問 Kubernetes 或 Docker 內部數據(配置文件和二進制庫)的應用則需要使用 hostPath。

比如 kube-apiserver 和 kube-controller-manager 就是這樣的應用,通過

kubectl edit --namespace=kube-system pod kube-apiserver-k8s-master

查看 kube-apiserver Pod 的配置,下面是 Volume 的相關部分:

這里定義了三個 hostPath volume k8scerts 和 pki,分別對應 Host 目錄 /etc/kubernetes/etc/ssl/certs 和 /etc/pki

如果 Pod 被銷毀了,hostPath 對應的目錄也還會被保留,從這點看,hostPath 的持久性比 emptyDir 強。不過一旦 Host 崩潰,hostPath 也就沒法訪問了。

 

二十七、 外部 Storage Provider

 

如果 Kubernetes 部署在諸如 AWS、GCE、Azure 等公有雲上,可以直接使用雲硬盤作為 Volume,下面是 AWS Elastic Block Store 的例子:

要在 Pod 中使用 ESB volume,必須先在 AWS 中創建,然后通過 volume-id 引用。其他雲硬盤的使用方法可參考各公有雲廠商的官方文檔。

Kubernetes Volume 也可以使用主流的分布式存,比如 Ceph、GlusterFS 等,下面是 Ceph 的例子:

Ceph 文件系統的 /some/path/in/side/cephfs 目錄被 mount 到容器路徑 /test-ceph。

相對於 emptyDir 和 hostPath,這些 Volume 類型的最大特點就是不依賴 Kubernetes。Volume 的底層基礎設施由獨立的存儲系統管理,與 Kubernetes 集群是分離的。數據被持久化后,即使整個 Kubernetes 崩潰也不會受損。

當然,運維這樣的存儲系統通常不是項簡單的工作,特別是對可靠性、高可用和擴展性有較高要求時。

Volume 提供了非常好的數據持久化方案,不過在可管理性上還有不足。

 

二十八、PV & PVC

Volume 提供了非常好的數據持久化方案,不過在可管理性上還有不足。

拿前面 AWS EBS 的例子來說,要使用 Volume,Pod 必須事先知道如下信息:

  1. 當前 Volume 來自 AWS EBS。

  2. EBS Volume 已經提前創建,並且知道確切的 volume-id。

Pod 通常是由應用的開發人員維護,而 Volume 則通常是由存儲系統的管理員維護。開發人員要獲得上面的信息:

  1. 要么詢問管理員。

  2. 要么自己就是管理員。

這樣就帶來一個管理上的問題:應用開發人員和系統管理員的職責耦合在一起了。如果系統規模較小或者對於開發環境這樣的情況還可以接受。但當集群規模變大,特別是對於生成環境,考慮到效率和安全性,這就成了必須要解決的問題。

Kubernetes 給出的解決方案是 PersistentVolume 和 PersistentVolumeClaim。

PersistentVolume (PV) 是外部存儲系統中的一塊存儲空間,由管理員創建和維護。與 Volume 一樣,PV 具有持久性,生命周期獨立於 Pod。

PersistentVolumeClaim (PVC) 是對 PV 的申請 (Claim)。PVC 通常由普通用戶創建和維護。需要為 Pod 分配存儲資源時,用戶可以創建一個 PVC,指明存儲資源的容量大小和訪問模式(比如只讀)等信息,Kubernetes 會查找並提供滿足條件的 PV。

有了 PersistentVolumeClaim,用戶只需要告訴 Kubernetes 需要什么樣的存儲資源,而不必關心真正的空間從哪里分配,如何訪問等底層細節信息。這些 Storage Provider 的底層信息交給管理員來處理,只有管理員才應該關心創建 PersistentVolume 的細節信息。

Kubernetes 支持多種類型的 PersistentVolume,比如 AWS EBS、Ceph、NFS 等,完整列表請參考 https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes

 

二十八、NFS PersistentVolume

本節通過 NFS 實踐。

作為准備工作,我們已經在 k8s-master 節點上搭建了一個 NFS 服務器,目錄為 /nfsdata

下面創建一個 PV mypv1,配置文件 nfs-pv1.yml 如下:

① capacity 指定 PV 的容量為 1G。

② accessModes 指定訪問模式為 ReadWriteOnce,支持的訪問模式有:
ReadWriteOnce – PV 能以 read-write 模式 mount 到單個節點。
ReadOnlyMany – PV 能以 read-only 模式 mount 到多個節點。
ReadWriteMany – PV 能以 read-write 模式 mount 到多個節點。

③ persistentVolumeReclaimPolicy 指定當 PV 的回收策略為 Recycle,支持的策略有:
Retain – 需要管理員手工回收。
Recycle – 清除 PV 中的數據,效果相當於執行 rm -rf /thevolume/*
Delete – 刪除 Storage Provider 上的對應存儲資源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

④ storageClassName 指定 PV 的 class 為 nfs。相當於為 PV 設置了一個分類,PVC 可以指定 class 申請相應 class 的 PV。

⑤ 指定 PV 在 NFS 服務器上對應的目錄。

創建 mypv1

STATUS 為 Available,表示 mypv1 就緒,可以被 PVC 申請。

接下來創建 PVC mypvc1,配置文件 nfs-pvc1.yml 如下:

PVC 就很簡單了,只需要指定 PV 的容量,訪問模式和 class。

創建 mypvc1

從 kubectl get pvc 和 kubectl get pv 的輸出可以看到 mypvc1 已經 Bound 到 mypv1,申請成功。

接下來就可以在 Pod 中使用存儲了,Pod 配置文件 pod1.yml 如下:

與使用普通 Volume 的格式類似,在 volumes 中通過 persistentVolumeClaim 指定使用 mypvc1 申請的 Volume。

創建 mypod1

驗證 PV 是否可用:

可見,在 Pod 中創建的文件 /mydata/hello 確實已經保存到了 NFS 服務器目錄 /nfsdata/pv1 中。

如果不再需要使用 PV,可用刪除 PVC 回收 PV。

 

二十九、回收PV

當 PV 不再需要時,可通過刪除 PVC 回收。

當 PV 不再需要時,可通過刪除 PVC 回收。

當 PVC mypvc1 被刪除后,我們發現 Kubernetes 啟動了一個新 Pod recycler-for-mypv1,這個 Pod 的作用就是清除 PV mypv1 的數據。此時 mypv1 的狀態為 Released,表示已經解除了與 mypvc1 的 Bound,正在清除數據,不過此時還不可用。

當數據清除完畢,mypv1 的狀態重新變為 Available,此時則可以被新的 PVC 申請。

/nfsdata/pv1 中的 hello 文件已經被刪除了。

因為 PV 的回收策略設置為 Recycle,所以數據會被清除,但這可能不是我們想要的結果。如果我們希望保留數據,可以將策略設置為 Retain

通過 kubectl apply 更新 PV:

回收策略已經變為 Retain,通過下面步驟驗證其效果:

① 重新創建 mypvc1

② 在 mypv1 中創建文件 hello

③ mypv1 狀態變為 Released

④ Kubernetes 並沒有啟動 Pod recycler-for-mypv1

⑤ PV 中的數據被完整保留。

雖然 mypv1 中的數據得到了保留,但其 PV 狀態會一直處於 Released,不能被其他 PVC 申請。為了重新使用存儲資源,可以刪除並重新創建 mypv1。刪除操作只是刪除了 PV 對象,存儲空間中的數據並不會被刪除。

新建的 mypv1 狀態為 Available,已經可以被 PVC 申請。

PV 還支持 Delete 的回收策略,會刪除 PV 在 Storage Provider 上對應存儲空間。NFS 的 PV 不支持 Delete,支持 Delete 的 Provider 有 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

 

三十、PV 動態供給

前面的例子中,我們提前創建了 PV,然后通過 PVC 申請 PV 並在 Pod 中使用,這種方式叫做靜態供給(Static Provision)。

與之對應的是動態供給(Dynamical Provision),即如果沒有滿足 PVC 條件的 PV,會動態創建 PV。相比靜態供給,動態供給有明顯的優勢:不需要提前創建 PV,減少了管理員的工作量,效率高。

動態供給是通過 StorageClass 實現的,StorageClass 定義了如何創建 PV,下面是兩個例子。

StorageClass standard

StorageClass slow

這兩個 StorageClass 都會動態創建 AWS EBS,不同在於 standard 創建的是 gp2 類型的 EBS,而 slow 創建的是 io1 類型的 EBS。不同類型的 EBS 支持的參數可參考 AWS 官方文檔。

StorageClass 支持 Delete 和 Retain 兩種 reclaimPolicy,默認是 Delete

與之前一樣,PVC 在申請 PV 時,只需要指定 StorageClass 和容量以及訪問模式,比如:

除了 AWS EBS,Kubernetes 支持其他多種動態供給 PV 的 Provisioner,完整列表請參考 https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

 

三十一、MySQL 如何使用 PV 和 PVC

本節演示如何為 MySQL 數據庫提供持久化存儲,步驟為:

  1. 創建 PV 和 PVC。

  2. 部署 MySQL。

  3. 向 MySQL 添加數據。

  4. 模擬節點宕機故障,Kubernetes 將 MySQL 自動遷移到其他節點。

  5. 驗證數據一致性。

首先創建 PV 和 PVC,配置如下:

mysql-pv.yml

mysql-pvc.yml

創建 mysql-pv 和 mysql-pvc

接下來部署 MySQL,配置文件如下:

PVC mysql-pvc Bound 的 PV mysql-pv 將被 mount 到 MySQL 的數據目錄 var/lib/mysql

MySQL 被部署到 k8s-node2,下面通過客戶端訪問 Service mysql

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword


更新數據庫:

① 切換到數據庫 mysql。

② 創建數據庫表 my_id。

③ 插入一條數據。

④ 確認數據已經寫入。

關閉 k8s-node2,模擬節點宕機故障。

一段時間后,Kubernetes 將 MySQL 遷移到 k8s-node1

驗證數據的一致性:

MySQL 服務恢復,數據也完好無損。

小結

本章我們討論了 Kubernetes 如何管理存儲資源。

emptyDir 和 hostPath 類型的 Volume 很方便,但可持久性不強,Kubernetes 支持多種外部存儲系統的 Volume。

PV 和 PVC 分離了管理員和普通用戶的職責,更適合生產環境。我們還學習了如何通過 StorageClass 實現更高效的動態供給。

最后,我們演示了如何在 MySQL 中使用 PersistentVolume 實現數據持久性。

 

三十二、用k8s管理機密信息 (例始用戶和密碼,不直接寫在鏡像中,這樣不安全,通過Secret方案解決這個問題。)

應用啟動過程中可能需要一些敏感信息,比如訪問數據庫的用戶名密碼或者秘鑰。將這些信息直接保存在容器鏡像中顯然不妥,Kubernetes 提供的解決方案是 Secret。

Secret 會以密文的方式存儲數據,避免了直接在配置文件中保存敏感信息。Secret 會以 Volume 的形式被 mount 到 Pod,容器可通過文件的方式使用 Secret 中的敏感數據;此外,容器也可以環境變量的方式使用這些數據。

Secret 可通過命令行或 YAML 創建。比如希望 Secret 中包含如下信息:

  1. 用戶名 admin

  2. 密碼 123456

創建 Secret

有四種方法創建 Secret:

1. 通過 --from-literal

kubectl create secret generic mysecret --from-literal=username=admin --from-literal=password=123456

每個 --from-literal 對應一個信息條目。

2. 通過 --from-file

echo -n admin > ./username
echo -n 123456 > ./password
kubectl create secret generic mysecret --from-file=./username --from-file=./password

每個文件內容對應一個信息條目。

3. 通過 --from-env-file

cat << EOF > env.txt
username=admin
password=123456
EOF
kubectl create secret generic mysecret --from-env-file=env.txt

文件 env.txt 中每行 Key=Value 對應一個信息條目。

4. 通過 YAML 配置文件:

文件中的敏感數據必須是通過 base64 編碼后的結果。

執行 kubectl apply 創建 Secret:

 

 三十三、查看Secret (查看用戶和密碼)

可以通過 kubectl get secret 查看存在的 secret。

顯示有兩個數據條目,kubectl describe secret 查看條目的 Key:

如果還想查看 Value,可以用 kubectl edit secret mysecret

然后通過 base64 將 Value 反編碼:

下節學習如何在 Pod 中使用 Secret。

 

三十四、volume方式使用Secret

Pod 可以通過 Volume 或者環境變量的方式使用 Secret,今天先學習 Volume 方式。

Pod 的配置文件如下所示:

① 定義 volume foo,來源為 secret mysecret

② 將 foo mount 到容器路徑 /etc/foo,可指定讀寫權限為 readOnly

創建 Pod 並在容器中讀取 Secret:

767.png

可以看到,Kubernetes 會在指定的路徑 /etc/foo 下為每條敏感數據創建一個文件,文件名就是數據條目的 Key,這里是 /etc/foo/username 和 /etc/foo/password,Value 則以明文存放在文件中。

我們也可以自定義存放數據的文件名,比如將配置文件改為:

這時數據將分別存放在 /etc/foo/my-group/my-username 和 /etc/foo/my-group/my-password 中。

以 Volume 方式使用的 Secret 支持動態更新:Secret 更新后,容器中的數據也會更新。

將 password 更新為 abcdef,base64 編碼為 YWJjZGVm

更新 Secret。

幾秒鍾或,新的 password 會同步到容器。

以上是通過 Volume 使用 Secret。

 

三十五、通過環境變量使用 Secret

通過 Volume 使用 Secret,容器必須從文件讀取數據,會稍顯麻煩,Kubernetes 還支持通過環境變量使用 Secret。

Pod 配置文件示例如下:

創建 Pod 並讀取 Secret。

通過環境變量 SECRET_USERNAME 和 SECRET_PASSWORD 成功讀取到 Secret 的數據。

需要注意的是,環境變量讀取 Secret 很方便,但無法支撐 Secret 動態更新。

Secret 可以為 Pod 提供密碼、Token、私鑰等敏感數據;對於一些非敏感數據,比如應用的配置信息,則可以用 ConfigMap。

 

三十六、用 ConfigMap 管理配置

 

Secret 可以為 Pod 提供密碼、Token、私鑰等敏感數據;對於一些非敏感數據,比如應用的配置信息,則可以用 ConfigMap。

ConfigMap 的創建和使用方式與 Secret 非常類似,主要的不同是數據以明文的形式存放。

與 Secret 一樣,ConfigMap 也支持四種創建方式:

1. 通過 --from-literal

kubectl create configmap myconfigmap --from-literal=config1=xxx --from-literal=config2=yyy

每個 --from-literal 對應一個信息條目。

2. 通過 --from-file

echo -n xxx > ./config1
echo -n yyy > ./config2
kubectl create configmap myconfigmap --from-file=./config1 --from-file=./config2

每個文件內容對應一個信息條目。

3. 通過 --from-env-file

cat << EOF > env.txt
config1=xxx
config2=yyy
EOF
kubectl create configmap myconfigmap --from-env-file=env.txt

文件 env.txt 中每行 Key=Value 對應一個信息條目。

4. 通過 YAML 配置文件:
 
文件中的數據直接以明文輸入。

與 Secret 一樣,Pod 也可以通過 Volume 或者環境變量的方式使用 Secret。

Volume 方式:

環境變量方式:

大多數情況下,配置信息都以文件形式提供,所以在創建 ConfigMap 時通常采用 --from-file 或 YAML 方式,讀取 ConfigMap 時通常采用 Volume 方式。

比如給 Pod 傳遞如何記錄日志的配置信息:

可以采用 --from-file 形式,則將其保存在文件 logging.conf 中,然后執行命令:

kubectl create configmap myconfigmap --from-file=./logging.conf

如果采用 YAML 配置文件,其內容則為:

注意別漏寫了 Key logging.conf 后面的 | 符號。

創建並查看 ConfigMap:

在 Pod 中使用此 ConfigMap,配置文件為:

① 在 volume 中指定存放配置信息的文件相對路徑為 myapp/logging.conf

② 將 volume mount 到容器的 /etc 目錄。

創建 Pod 並讀取配置信息:

配置信息已經保存到 /etc/myapp/logging.conf 文件中。與 Secret 一樣,Volume 形式的 ConfigMap 也支持動態更新,留給大家自己實踐。

小結

本章我們學習了如何向 Pod 傳遞配置信息。如果信息需要加密,可使用 Secret;如果是一般的配置信息,則可使用 ConfigMap。

Secret 和 ConfigMap 支持四種定義方法。Pod 在使用它們時,可以選擇 Volume 方式或環境變量方式,不過只有 Volume 方式支持動態更新。

 

 三十七、why Helm?

本章我們將學習 Helm,Kubernetes 的包管理器。

每個成功的軟件平台都有一個優秀的打包系統,比如 Debian、Ubuntu 的 apt,Redhat、Centos 的 yum。而 Helm 則是 Kubernetes 上的包管理器。

本章我們將討論為什么需要 Helm,它的架構和組件,以及如何使用 Helm。

Why Helm

Helm 到底解決了什么問題?為什么 Kubernetes 需要 Helm?

答案是:Kubernetes 能夠很好地組織和編排容器,但它缺少一個更高層次的應用打包工具,而 Helm 就是來干這件事的。

先來看個例子。
比如對於一個 MySQL 服務, Kubernetes 需要部署下面這些對象:

  1. Service,讓外界能夠訪問到 MySQL。

  2. Secret,定義 MySQL 的密碼。

  3. PersistentVolumeClaim,為 MySQL 申請持久化存儲空間。

  4. Deployment,部署 MySQL Pod,並使用上面的這些支持對象。

我們可以將上面這些配置保存到對象各自的文件中,或者集中寫進一個配置文件,然后通過 kubectl apply -f 部署。

到目前為止,Kubernetes 對服務的部署支持得都挺好,如果應用只由一個或幾個這樣的服務組成,上面的部署方式完全足夠了。

但是,如果我們開發的是微服務架構的應用,組成應用的服務可能多達十個甚至幾十上百個,這種組織和管理應用的方式就不好使了:

  1. 很難管理、編輯和維護如此多的服務。每個服務都有若干配置,缺乏一個更高層次的工具將這些配置組織起來。

  2. 不容易將這些服務作為一個整體統一發布。部署人員需要首先理解應用都包含哪些服務,然后按照邏輯順序依次執行 kubectl apply。即缺少一種工具來定義應用與服務,以及服務與服務之間的依賴關系。

  3. 不能高效地共享和重用服務。比如兩個應用都要用到 MySQL 服務,但配置的參數不一樣,這兩個應用只能分別拷貝一套標准的 MySQL 配置文件,修改后通過 kubectl apply 部署。也就是說不支持參數化配置和多環境部署。

  4. 不支持應用級別的版本管理。雖然可以通過 kubectl rollout undo 進行回滾,但這只能針對單個 Deployment,不支持整個應用的回滾。

  5. 不支持對部署的應用狀態進行驗證。比如是否能通過預定義的賬號訪問 MySQL。雖然 Kubernetes 有健康檢查,但那是針對單個容器,我們需要應用(服務)級別的健康檢查。

Helm 能夠解決上面這些問題,Helm 幫助 Kubernetes 成為微服務架構應用理想的部署平台。

 

 三十八、Helm 架構

在實踐之前,我們先來看看 Helm 的架構。

Helm 有兩個重要的概念:chart 和 release。

chart 是創建一個應用的信息集合,包括各種 Kubernetes 對象的配置模板、參數定義、依賴關系、文檔說明等。chart 是應用部署的自包含邏輯單元。可以將 chart 想象成 apt、yum 中的軟件安裝包。

release 是 chart 的運行實例,代表了一個正在運行的應用。當 chart 被安裝到 Kubernetes 集群,就生成一個 release。chart 能夠多次安裝到同一個集群,每次安裝都是一個 release。

Helm 是包管理工具,這里的包就是指的 chart。Helm 能夠:

  1. 從零創建新 chart。

  2. 與存儲 chart 的倉庫交互,拉取、保存和更新 chart。

  3. 在 Kubernetes 集群中安裝和卸載 release。

  4. 更新、回滾和測試 release。

Helm 包含兩個組件:Helm 客戶端 和 Tiller 服務器。

Helm 客戶端是終端用戶使用的命令行工具,用戶可以:

  1. 在本地開發 chart。

  2. 管理 chart 倉庫。

  3. 與 Tiller 服務器交互。

  4. 在遠程 Kubernetes 集群上安裝 chart。

  5. 查看 release 信息。

  6. 升級或卸載已有的 release。

Tiller 服務器運行在 Kubernetes 集群中,它會處理 Helm 客戶端的請求,與 Kubernetes API Server 交互。Tiller 服務器負責:

  1. 監聽來自 Helm 客戶端的請求。

  2. 通過 chart 構建 release。

  3. 在 Kubernetes 中安裝 chart,並跟蹤 release 的狀態。

  4. 通過 API Server 升級或卸載已有的 release。

簡單的講:Helm 客戶端負責管理 chart;Tiller 服務器負責管理 release。

 

三十九、部署 Helm

 

本節我們將安裝和部署 Helm 客戶端和 Tiller 服務器。

Helm 客戶端

通常,我們將 Helm 客戶端安裝在能夠執行 kubectl 命令的節點上,只需要下面一條命令:

curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash

執行 helm version 驗證。

目前只能查看到客戶端的版本,服務器還沒有安裝。

helm 有很多子命令和參數,為了提高使用命令行的效率,通常建議安裝 helm 的 bash 命令補全腳本,方法如下:

helm completion bash > .helmrcecho "source .helmrc" >> .bashrc

重新登錄后就可以通過 Tab 鍵補全 helm 子命令和參數了。

Tiller 服務器

Tiller 服務器安裝非常簡單,只需要執行 helm init

Tiller 本身也是作為容器化應用運行在 Kubernetes Cluster 中的:

可以看到 Tiller 的 Service、Deployment 和 Pod。

現在, helm version 已經能夠查看到服務器的版本信息了。

Helm 部署完畢,下一節開始使用 Helm。

 

 四十、使用Helm

Helm 安裝成功后,可執行 helm search 查看當前可安裝的 chart。

這個列表很長,這里只截取了一部分。大家不禁會問,這些 chart 都是從哪里來的?

前面說過,Helm 可以像 apt 和 yum 管理軟件包一樣管理 chart。apt 和 yum 的軟件包存放在倉庫中,同樣的,Helm 也有倉庫。

Helm 安裝時已經默認配置好了兩個倉庫:stable 和 localstable 是官方倉庫,local 是用戶存放自己開發的 chart 的本地倉庫。

helm search 會顯示 chart 位於哪個倉庫,比如 local/cool-chart 和 stable/acs-engine-autoscaler

用戶可以通過 helm repo add 添加更多的倉庫,比如企業的私有倉庫,倉庫的管理和維護方法請參考官網文檔 https://docs.helm.sh

與 apt 和 yum 一樣,helm 也支持關鍵字搜索:

包括 DESCRIPTION 在內的所有信息,只要跟關鍵字匹配,都會顯示在結果列表中。

安裝 chart 也很簡單,執行如下命令可以安裝 MySQL。

helm install stable/mysql

如果看到如下報錯,通常是因為 Tiller 服務器的權限不足。

執行如下命名添加權限:

kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

然后再次執行

helm install stable/mysql

輸出分為三部分:

① chart 本次部署的描述信息:

NAME 是 release 的名字,因為我們沒用 -n 參數指定,Helm 隨機生成了一個,這里是 fun-zorse

NAMESPACE 是 release 部署的 namespace,默認是 default,也可以通過 --namespace 指定。

STATUS 為 DEPLOYED,表示已經將 chart 部署到集群。

② 當前 release 包含的資源:Service、Deployment、Secret 和 PersistentVolumeClaim,其名字都是 fun-zorse-mysql,命名的格式為 ReleasName-ChartName

③ NOTES 部分顯示的是 release 的使用方法。比如如何訪問 Service,如何獲取數據庫密碼,以及如何連接數據庫等。

通過 kubectl get 可以查看組成 release 的各個對象:

因為我們還沒有准備 PersistentVolume,當前 release 還不可用。

helm list 顯示已經部署的 release,helm delete 可以刪除 release。

Helm 的使用方法像極了 apt 和 yum,用 Helm 來管理 Kubernetes 應用非常方便。

 

 四十一、Helm目錄結構

 

chart 是 Helm 的應用打包格式。chart 由一系列文件組成,這些文件描述了 Kubernetes 部署應用時所需要的資源,比如 Service、Deployment、PersistentVolumeClaim、Secret、ConfigMap 等。

單個的 chart 可以非常簡單,只用於部署一個服務,比如 Memcached;chart 也可以很復雜,部署整個應用,比如包含 HTTP Servers、 Database、消息中間件、cache 等。

chart 將這些文件放置在預定義的目錄結構中,通常整個 chart 被打成 tar 包,而且標注上版本信息,便於 Helm 部署。

下面我們將詳細討論 chart 的目錄結構以及包含的各類文件。

chart 目錄結構

以前面 MySQL chart 為例。一旦安裝了某個 chart,我們就可以在 ~/.helm/cache/archive 中找到 chart 的 tar 包。

解壓后,MySQL chart 目錄結構如下:

目錄名就是 chart 的名字(不帶版本信息),這里是 mysql,包含如下內容:

Chart.yaml 
YAML 文件,描述 chart 的概要信息。

name 和 version 是必填項,其他都是可選。

README.md 
Markdown 格式的 README 文件,相當於 chart 的使用文檔,此文件為可選。

LICENSE 
文本文件,描述 chart 的許可信息,此文件為可選。

requirements.yaml 
chart 可能依賴其他的 chart,這些依賴關系可通過 requirements.yaml 指定,比如:

在安裝過程中,依賴的 chart 也會被一起安裝。

values.yaml 
chart 支持在安裝的時根據參數進行定制化配置,而 values.yaml 則提供了這些配置參數的默認值。

templates 目錄 
各類 Kubernetes 資源的配置模板都放置在這里。Helm 會將 values.yaml 中的參數值注入到模板中生成標准的 YAML 配置文件。

模板是 chart 最重要的部分,也是 Helm 最強大的地方。模板增加了應用部署的靈活性,能夠適用不同的環境,我們后面會詳細討論。

templates/NOTES.txt 
chart 的簡易使用文檔,chart 安裝成功后會顯示此文檔內容。

與模板一樣,可以在 NOTE.txt 中插入配置參數,Helm 會動態注入參數值。

 

四十二、chart 模板

 

Helm 通過模板創建 Kubernetes 能夠理解的 YAML 格式的資源配置文件,我們將通過例子來學習如何使用模板。

以 templates/secrets.yaml 為例:

從結構看,文件的內容非常像 Secret 配置,只是大部分屬性值變成了{{ xxx }}。這些 {{ xxx }} 實際上是模板的語法。Helm 采用了 Go 語言的模板來編寫 chart。Go 模板非常強大,支持變量、對象、函數、流控制等功能。下面我們通過解析 templates/secrets.yaml 快速學習模板。

① {{ template "mysql.fullname" . }} 定義 Secret 的 name
關鍵字 template 的作用是引用一個子模板 mysql.fullname。這個子模板是在 templates/_helpers.tpl 文件中定義的。

這個定義還是很復雜的,因為它用到了模板語言中的對象、函數、流控制等概念。現在看不懂沒關系,這里我們學習的重點是:如果存在一些信息多個模板都會用到,則可在 templates/_helpers.tpl 中將其定義為子模板,然后通過 templates 函數引用。

這里 mysql.fullname 是由 release 與 chart 二者名字拼接組成。

根據 chart 的最佳實踐,所有資源的名稱都應該保持一致,對於我們這個 chart,無論 Secret 還是 Deployment、PersistentVolumeClaim、Service,它們的名字都是子模板 mysql.fullname 的值。

② Chart 和 Release 是 Helm 預定義的對象,每個對象都有自己的屬性,可以在模板中使用。如果使用下面命令安裝 chart:

helm install stable/mysql -n my

那么:
{{ .Chart.Name }} 的值為 mysql
{{ .Chart.Version }} 的值為 0.3.0
{{ .Release.Name }} 的值為 my
{{ .Release.Service }} 始終取值為 Tiller
{{ template "mysql.fullname" . }} 計算結果為 my-mysql

③ 這里指定 mysql-root-password 的值,不過使用了 if-else 的流控制,其邏輯為:
如果 .Values.mysqlRootPassword 有值,則對其進行 base64 編碼;否則隨機生成一個 10 位的字符串並編碼。

Values 也是預定義的對象,代表的是 values.yaml 文件。而 .Values.mysqlRootPassword 則是 values.yaml 中定義的 mysqlRootPassword 參數:

因為 mysqlRootPassword 被注釋掉了,沒有賦值,所以邏輯判斷會走 else,即隨機生成密碼。

randAlphaNumb64encquote 都是 Go 模板語言支持的函數,函數之間可以通過管道 | 連接。{{ randAlphaNum 10 | b64enc | quote }} 的作用是首先隨機產生一個長度為 10 的字符串,然后將其 base64 編碼,最后兩邊加上雙引號。

templates/secrets.yaml 這個例子展示了 chart 模板主要的功能,我們最大的收獲應該是:模板將 chart 參數化了,通過 values.yaml 可以靈活定制應用。

無論多復雜的應用,用戶都可以用 Go 模板語言編寫出 chart。無非是使用到更多的函數、對象和流控制。對於初學者,我的建議是盡量參考官方的 chart。根據二八定律,這些 chart 已經覆蓋了絕大部分情況,而且采用了最佳實踐。如何遇到不懂的函數、對象和其他語法,可參考官網文檔 https://docs.helm.sh

有了上面 chart 結構和模板的知識后,下節我們將重新實踐一次 MySQL chart,相信會有更多收獲。

 

 四十三、再次實踐 MySQL chart 

 

學習了 chart 結構和模板的知識后,現在重新實踐一次 MySQL chart,相信會有更多收獲。

chart 安裝前的准備

作為准備工作,安裝之前需要先清楚 chart 的使用方法。這些信息通常記錄在 values.yaml 和 README.md 中。除了下載源文件查看,執行 helm inspect values 可能是更方便的方法。

輸出的實際上是 values.yaml 的內容。閱讀注釋就可以知道 MySQL chart 支持哪些參數,安裝之前需要做哪些准備。其中有一部分是關於存儲的:

chart 定義了一個 PersistentVolumeClaim,申請 8G 的 PersistentVolume。由於我們的實驗環境不支持動態供給,所以得預先創建好相應的 PV,其配置文件 mysql-pv.yml 內容為:

創建 PV mysql-pv

接下來就可以安裝 chart 了。

定制化安裝 chart

除了接受 values.yaml 的默認值,我們還可以定制化 chart,比如設置 mysqlRootPassword

Helm 有兩種方式傳遞配置參數:

  1. 指定自己的 values 文件。
    通常的做法是首先通過 helm inspect values mysql > myvalues.yaml生成 values 文件,然后設置 mysqlRootPassword,之后執行 helm install --values=myvalues.yaml mysql

  2. 通過 --set 直接傳入參數值,比如:

mysqlRootPassword 設置為 abc123。另外,-n 設置 release 為 my,各類資源的名稱即為my-mysql

通過 helm list 和 helm status 可以查看 chart 的最新狀態。

PVC 已經 Bound,Deployment 也 AVAILABLE

升級和回滾 release

release 發布后可以執行 helm upgrade 對其升級,通過 --values 或 --set應用新的配置。比如將當前的 MySQL 版本升級到 5.7.15:

等待一些時間,升級成功。

helm history 可以查看 release 所有的版本。通過 helm rollback 可以回滾到任何版本。

回滾成功,MySQL 恢復到 5.7.14。

到這里,相信大家已經會使用 chart 了。下一節我們學習如何開發自己的 chart。

 

四十四、開發自己的chart

 

Kubernetes 給我們提供了大量官方 chart,不過要部署微服務應用,還是需要開發自己的 chart,下面就來實踐這個主題。

創建 chart

執行 helm create mychart 的命令創建 chart mychart

Helm 會幫我們創建目錄 mychart,並生成了各類 chart 文件。這樣我們就可以在此基礎上開發自己的 chart 了。

新建的 chart 默認包含一個 nginx 應用示例,values.yaml 內容如下:

開發時建議大家參考官方 chart 中的模板、values.yaml、Chart.yaml,里面包含了大量最佳實踐和最常用的函數、流控制,這里就不一一展開了。

調試 chart

只要是程序就會有 bug,chart 也不例外。Helm 提供了 debug 的工具:helm lint 和 helm install --dry-run --debug

helm lint 會檢測 chart 的語法,報告錯誤以及給出建議。

比如我們故意在 values.yaml 的第 8 行漏掉了一個 :

helm lint mychart 會指出這個語法錯誤。

mychart 目錄被作為參數傳遞給 helm lint。錯誤修復后則能通過檢測。

helm install --dry-run --debug 會模擬安裝 chart,並輸出每個模板生成的 YAML 內容。

我們可以檢視這些輸出,判斷是否與預期相符。

同樣,mychart 目錄作為參數傳遞給 helm install --dry-run --debug

 

四十五、管理和安裝chart

安裝 chart

當我們覺得准備就緒,就可以安裝 chart,Helm 支持四種安裝方法:

  1. 安裝倉庫中的 chart,例如:helm install stable/nginx

  2. 通過 tar 包安裝,例如:helm install ./nginx-1.2.3.tgz

  3. 通過 chart 本地目錄安裝,例如:helm install ./nginx

  4. 通過 URL 安裝,例如:helm install https://example.com/charts/nginx-1.2.3.tgz

這里我們使用本地目錄安裝:

當 chart 部署到 Kubernetes 集群,便可以對其進行更為全面的測試。

將 chart 添加到倉庫

chart 通過測試后可以將其添加到倉庫,團隊其他成員就能夠使用。任何 HTTP Server 都可以用作 chart 倉庫,下面演示在 k8s-node1192.168.56.106 上搭建倉庫。

  1. 在 k8s-node1 上啟動一個 httpd 容器。

  2. 通過 helm package 將 mychart 打包。

  3. 執行 helm repo index 生成倉庫的 index 文件。
     
    Helm 會掃描 myrepo 目錄中的所有 tgz 包並生成 index.yaml--url指定的是新倉庫的訪問路徑。新生成的 index.yaml 記錄了當前倉庫中所有 chart 的信息:
     
    當前只有 mychart 這一個 chart。

  4. 將 mychart-0.1.0.tgz 和 index.yaml 上傳到 k8s-node1 的 /var/www/charts 目錄。

  5. 通過 helm repo add 將新倉庫添加到 Helm。
     
    倉庫命名為 newrepo,Helm 會從倉庫下載 index.yaml。

  6. 現在已經可以 repo search 到 mychart 了。
     
    除了 newrepo/mychart,這里還有一個 local/mychart。這是因為在執行第 2 步打包操作的同時,mychart 也被同步到了 local 的倉庫。

  7. 已經可以直接從新倉庫安裝 mychart 了。

  8. 如果以后倉庫添加了新的 chart,需要用 helm repo update 更新本地的 index。
     
    這個操作相當於 Ubutun 的 apt-get update

小結

本章我們學習了 Kubernetes 包管理器 Helm。

Helm 讓我們能夠像 apt 管理 deb 包那樣安裝、部署、升級和刪除容器化應用。

Helm 由客戶端和 Tiller 服務器組成。客戶端負責管理 chart,服務器負責管理 release。

chart 是 Helm 的應用打包格式,它由一組文件和目錄構成。其中最重要的是模板,模板中定義了 Kubernetes 各類資源的配置信息,Helm 在部署時通過 values.yaml 實例化模板。

Helm 允許用戶開發自己的 chart,並為用戶提供了調試工具。用戶可以搭建自己的 chart 倉庫,在團隊中共享 chart。

Helm 幫助用戶在 Kubernetes 上高效地運行和管理微服務架構應用,Helm 非常重要。

 

 四十六、 Kubernetes 網絡模型

 

本節我們討論 Kubernetes 網絡這個重要主題。

Kubernetes 作為編排引擎管理着分布在不同節點上的容器和 Pod。Pod、Service、外部組件之間需要一種可靠的方式找到彼此並進行通信,Kubernetes 網絡則負責提供這個保障。本章包括如下內容:

  1. Kubernetes 網絡模型

  2. 各種網絡方案

  3. Network Policy

Kubernetes 網絡模型

Kubernetes 采用的是基於扁平地址空間的網絡模型,集群中的每個 Pod 都有自己的 IP 地址,Pod 之間不需要配置 NAT 就能直接通信。另外,同一個 Pod 中的容器共享 Pod 的 IP,能夠通過 localhost 通信。

這種網絡模型對應用開發者和管理員相當友好,應用可以非常方便地從傳統網絡遷移到 Kubernetes。每個 Pod 可被看作是一個個獨立的系統,而 Pod 中的容器則可被看做同一系統中的不同進程。

下面討論在這個網絡模型下集群中的各種實體如何通信。知識點前面都已經涉及,這里可當做復習和總結。

Pod 內容器之間的通信

當 Pod 被調度到某個節點,Pod 中的所有容器都在這個節點上運行,這些容器共享相同的本地文件系統、IPC 和網絡命名空間。

不同 Pod 之間不存在端口沖突的問題,因為每個 Pod 都有自己的 IP 地址。當某個容器使用 localhost 時,意味着使用的是容器所屬 Pod 的地址空間。

比如 Pod A 有兩個容器 container-A1 和 container-A2,container-A1 在端口 1234 上監聽,當 container-A2 連接到 localhost:1234,實際上就是在訪問 container-A1。這不會與同一個節點上的 Pod B 沖突,即使 Pod B 中的容器 container-B1 也在監聽 1234 端口。

Pod 之間的通信

Pod 的 IP 是集群可見的,即集群中的任何其他 Pod 和節點都可以通過 IP 直接與 Pod 通信,這種通信不需要借助任何的網絡地址轉換、隧道或代理技術。Pod 內部和外部使用的是同一個 IP,這也意味着標准的命名服務和發現機制,比如 DNS 可以直接使用。

Pod 與 Service 的通信

Pod 間可以直接通過 IP 地址通信,但前提是 Pod 得知道對方的 IP。在 Kubernetes 集群中, Pod 可能會頻繁的銷毀和創建,也就是說 Pod 的 IP 不是固定的。為了解決這個問題,Service 提供了訪問 Pod 的抽象層。無論后端的 Pod 如何變化,Service 都作為穩定的前端對外提供服務。同時,Service 還提供了高可用和負載均衡功能,Service 負責將請求轉發給正確的 Pod。

外部訪問

無論是 Pod 的 IP 還是 Service 的 Cluster IP,它們只能在 Kubernetes 集群中可見,對集群之外的世界,這些 IP 都是私有的。

Kubernetes 提供了兩種方式讓外界能夠與 Pod 通信:

  1. NodePort
    Service 通過 Cluster 節點的靜態端口對外提供服務。外部可以通過 <NodeIP>:<NodePort> 訪問 Service。

  2. LoadBalancer
    Service 利用 cloud provider 提供的 load balancer 對外提供服務,cloud provider 負責將 load balancer 的流量導向 Service。目前支持的 cloud provider 有 GCP、AWS、Azur 等。

以上就是 Kubernetes 網絡模型的相關討論。

 

 四十七、k8s各種網絡方案

 

網絡模型有了,如何實現呢?

為了保證網絡方案的標准化、擴展性和靈活性,Kubernetes 采用了 Container Networking Interface(CNI)規范。

CNI 是由 CoreOS 提出的容器網絡規范,它使用了插件(Plugin)模型創建容器的網絡棧。

CNI 的優點是支持多種容器 runtime,不僅僅是 Docker。CNI 的插件模型支持不同組織和公司開發的第三方插件,這對運維人員來說很有吸引力,可以靈活選擇適合的網絡方案。

目前已有多種支持 Kubernetes 的網絡方案,比如 Flannel、Calico、Canal、Weave Net 等。因為它們都實現了 CNI 規范,用戶無論選擇哪種方案,得到的網絡模型都一樣,即每個 Pod 都有獨立的 IP,可以直接通信。區別在於不同方案的底層實現不同,有的采用基於 VxLAN 的 Overlay 實現,有的則是 Underlay,性能上有區別。再有就是是否支持 Network Policy。

 

 四十八、Network Policy

Network Policy 是 Kubernetes 的一種資源。Network Policy 通過 Label 選擇 Pod,並指定其他 Pod 或外界如何與這些 Pod 通信。

默認情況下,所有 Pod 是非隔離的,即任何來源的網絡流量都能夠訪問 Pod,沒有任何限制。當為 Pod 定義了 Network Policy,只有 Policy 允許的流量才能訪問 Pod。

不過,不是所有的 Kubernetes 網絡方案都支持 Network Policy。比如 Flannel 就不支持,Calico 是支持的。我們接下來將用 Canal 來演示 Network Policy。Canal 這個開源項目很有意思,它用 Flannel 實現 Kubernetes 集群網絡,同時又用 Calico 實現 Network Policy。

部署 Canal

部署 Canal 與部署其他 Kubernetes 網絡方案非常類似,都是在執行了 kubeadm init 初始化 Kubernetes 集群之后通過 kubectl apply 安裝相應的網絡方案。也就是說,沒有太好的辦法直接切換使用不同的網絡方案,基本上只能重新創建集群。

要銷毀當前集群,最簡單的方法是在每個節點上執行 kubeadm reset。然后就可以按照我們在前面 “部署 Kubernetes Cluster” 一章的 “初始化 Master” 小節中的方法初始化集群。

kubeadm init --apiserver-advertise-address 192.168.56.105 --pod-network-cidr=10.244.0.0/16

然后按照文檔 https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ 安裝 Canal。文檔列出了各種網絡方案的安裝方法:

執行如下命令部署 Canal

kubectl apply -f https://raw.githubusercontent.com/projectcalico/canal/master/k8s-install/1.7/rbac.yaml
kubectl apply -f https://raw.githubusercontent.com/projectcalico/canal/master/k8s-install/1.7/canal.yaml

部署成功后,可以查看到 Canal 相關組件:

Canal 作為 DaemonSet 部署到每個節點,地屬於 kube-system 這個 namespace。

Canal 准備就緒,下節我們將實踐 Network Policy。

 

 四十九、實踐Network Policy

為了演示 Network Policy,我們先部署一個 httpd 應用,其配置文件 httpd.yaml 為:

httpd 有三個副本,通過 NodePort 類型的 Service 對外提供服務。部署應用:

當前沒有定義任何 Network Policy,驗證應用可以被訪問:

  1. 啟動一個 busybox Pod,可以訪問 Service,也可以 Ping 到副本 Pod。

  2. 集群節點可以訪問 Service, 也可以 Ping 到副本 Pod。

  3. 集群外(192.168.56.1)可以訪問 Service。

現在創建如下 Network Policy:

① 定義將此 Network Policy 中的訪問規則應用於 label 為 run: httpd 的 Pod,即 httpd 應用的三個副本 Pod。

② ingress 中定義只有 label 為 access: "true" 的 Pod 才能訪問應用。

③ 只能訪問 80 端口。

通過 kubectl apply 創建 Network Policy。

驗證 Network Policy 的有效性:

  1. busybox Pod 已經不能訪問 Service。

    如果 Pod 添加了 label access: "true" 就能訪問到應用,但 Ping 已經被禁止。

  2. 集群節點已經不能訪問 Service, 也 Ping 不到副本 Pod。

  3. 集群外(192.168.56.1)已經不能訪問 Service。

如果希望讓集群節點和集群外(192.168.56.1)也能夠訪問到應用,可以對 Network Policy 做如下修改:

應用新的 Network Policy:

現在,集群節點和集群外(192.168.56.1)已經能夠訪問了:

除了通過 ingress 限制進入的流量,也可以用 egress 限制外出的流量。大家可以參考官網相關文檔和示例,這里就不贅述了。

小結

Kubernetes 采用的是扁平化的網絡模型,每個 Pod 都有自己的 IP,並且可以直接通信。

CNI 規范使得 Kubernetes 可以靈活選擇多種 Plugin 實現集群網絡。

Network Policy 則賦予了 Kubernetes 強大的網絡訪問控制機制。

 

五十、Kubernetes Dashboard

前面章節 Kubernetes 所有的操作我們都是通過命令行工具 kubectl 完成的。為了提供更豐富的用戶體驗,Kubernetes 還開發了一個基於 Web 的 Dashboard,用戶可以用 Kubernetes Dashboard 部署容器化的應用、監控應用的狀態、執行故障排查任務以及管理 Kubernetes 各種資源。

在 Kubernetes Dashboard 中可以查看集群中應用的運行狀態,也能夠創建和修改各種 Kubernetes 資源,比如 Deployment、Job、DaemonSet 等。用戶可以 Scale Up/Down Deployment、執行 Rolling Update、重啟某個 Pod 或者通過向導部署新的應用。Dashboard 能顯示集群中各種資源的狀態以及日志信息。

可以說,Kubernetes Dashboard 提供了 kubectl 的絕大部分功能,大家可以根據情況進行選擇。

安裝

Kubernetes 默認沒有部署 Dashboard,可通過如下命令安裝:

kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

Dashboard 會在 kube-system namespace 中創建自己的 Deployment 和 Service。

因為 Service 是 ClusterIP 類型,為了方便使用,我們可通過 kubectl --namespace=kube-system edit service kubernetes-dashboard 修改成 NodePort 類型。

保存修改,此時已經為 Service 分配了端口 31614

通過瀏覽器訪問 Dashboard https://192.168.56.105:31614/,登錄界面如下:

配置登錄權限

Dashboard 支持 Kubeconfig 和 Token 兩種認證方式,為了簡化配置,我們通過配置文件 dashboard-admin.yaml 為 Dashboard 默認用戶賦予 admin 權限。

執行 kubectl apply 使之生效。

現在直接點擊登錄頁面的 SKIP 就可以進入 Dashboard 了。

下一節我們來使用 Kubernetes Dashboard。

 

五十一、使用Dashboard

Dashboard 界面結構

Dashboard 的界面很簡潔,分為三個大的區域。

  1. 頂部操作區

    在這里用戶可以搜索集群中的資源、創建資源或退出。

  2. 左邊導航菜單
    通過導航菜單可以查看和管理集群中的各種資源。菜單項按照資源的層級分為兩類:
    Cluster 級別的資源 
     
    Namespace 級別的資源 
     
    默認顯示的是 default Namespace,可以進行切換:

  3. 中間主體區
    在導航菜單中點擊了某類資源,中間主體區就會顯示該資源所有實例,比如點擊 Pods

典型使用場景

接下來我們介紹幾個 Dashboard 的典型使用場景。

部署 Deployment

點擊頂部操作區的 + CREATE 按鈕。

用戶可以直接輸入要部署應用的名字、鏡像、副本數等信息;也可以上傳 YAML 配置文件。如果是上傳配置文件,則可以創建任意類型的資源,不僅僅是 Deployment。

在線操作

對於每種資源,都可以點擊  按鈕執行各種操作。

比如點擊 View/edit YAML 可直接修改資源的配置,保存后立即生效,其效果與 kubectl edit 一樣。

查看資源詳細信息

點擊某個資源實例的名字,可以查看到詳細信息,其效果與 kubectl describe 一樣。

查看 Pod 日志

在 Pod 及其父資源(DaemonSet、ReplicaSet 等)頁面點擊  按鈕,可以查看 Pod 的日志,其效果與 kubectl logs 一樣。

Kubernetes Dashboard 界面設計友好,自解釋性強,可以看作 GUI 版的 kubectl,更多功能留給大家自己探索。

小結

本章介紹了Kubernetes Dashboard 的安裝和使用方法。Dashboard能完成日常管理的大部分工作,可以作為命令行工具 kubectl 的有益補充。

 

五十二、用Weave Scope監控集群

 

創建 Kubernetes 集群並部署容器化應用只是第一步。一旦集群運行起來,我們需要確保一起正常,所有必要組件就位並各司其職,有足夠的資源滿足應用的需求。Kubernetes 是一個復雜系統,運維團隊需要有一套工具幫助他們獲知集群的實時狀態,並為故障排查提供及時和准確的數據支持。

本章重點討論 Kubernetes 常用的監控方案,下一章會討論日志管理。

Weave Scope

Weave Scope 是 Docker 和 Kubernetes 可視化監控工具。Scope 提供了至上而下的集群基礎設施和應用的完整視圖,用戶可以輕松對分布式的容器化應用進行實時監控和問題診斷。

安裝 Scope

安裝 Scope 的方法很簡單,執行如下命令:

kubectl apply --namespace kube-system -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')"

部署成功后,有如下相關組件:

  1. DaemonSet weave-scope-agent,集群每個節點上都會運行的 scope agent 程序,負責收集數據。

  2. Deployment weave-scope-app,scope 應用,從 agent 獲取數據,通過 Web UI 展示並與用戶交互。

  3. Service weave-scope-app,默認是 ClusterIP 類型,為了方便已通過 kubectl edit 修改為 NodePort

使用 Scope

瀏覽器訪問 http://192.168.56.106:30693/,Scope 默認顯示當前所有的 Controller(Deployment、DaemonSet 等)。

拓撲結構

Scope 會自動構建應用和集群的邏輯拓撲。比如點擊頂部 PODS,會顯示所有 Pod 以及 Pod 之間的依賴關系。

點擊 HOSTS,會顯示各個節點之間的關系。

實時資源監控

可以在 Scope 中查看資源的 CPU 和內存使用情況。

支持的資源有 Host、Pod 和 Container。

在線操作

Scope 還提供了便捷的在線操作功能,比如選中某個 Host,點擊 >_ 按鈕可以直接在瀏覽器中打開節點的命令行終端:

點擊 Deployment 的 + 可以執行 Scale Up 操作:

可以查看 Pod 的日志:

可以 attach、restart、stop 容器,以及直接在 Scope 中排查問題:

強大的搜索功能

Scope 支持關鍵字搜索和定位資源。

還可以進行條件搜索,比如查找和定位 MEMORY > 100M 的 Pod。

Weave Scope 界面極其友好,操作簡潔流暢,更多功能留給大家去探索。

 

五十三、用Heapster監控集群

Heapster 是 Kubernetes 原生的集群監控方案。Heapster 以 Pod 的形式運行,它會自動發現集群節點、從節點上的 Kubelet 獲取監控數據。Kubelet 則是從節點上的 cAdvisor 收集數據。

Heapster 將數據按照 Pod 進行分組,將它們存儲到預先配置的 backend 並進行可視化展示。Heapster 當前支持的 backend 有 InfluxDB(通過 Grafana 展示),Google Cloud Monitoring 等。Heapster 的整體架構如下圖所示:

下面我們將實踐由 Heapster、InfluxDB 和 Grafana 組成的監控方案。Kubelet 和 cAdvisor 是 Kubernetes 的自帶組件,無需額外部署。

部署

Heapster 本身是一個 Kubernetes 應用,部署方法很簡單,運行如下命令:

git clone https://github.com/kubernetes/heapster.git
kubectl apply -f heapster/deploy/kube-config/influxdb/
kubectl apply -f heapster/deploy/kube-config/rbac/heapster-rbac.yaml

Heapster 相關資源如下:

為便與訪問,已通過 kubectl edit 將 Service monitoring-grafana 的類型修改為 NodePort

使用

瀏覽器打開 Grafana 的 Web UI:http://192.168.56.105:32314/

Heapster 已經預先配置好了 Grafana 的 DataSource 和 Dashboard。

點擊左上角 Home 菜單,可以看到預定義的兩個 Dashboard Cluster 和 Pods

點擊 Cluster,可以查看集群中節點的 CPU、內存、網絡和磁盤的使用情況。

在左上角可以切換查看不同節點的數據。

切換到 Pods Dashboard,可以查看 Pod 的監控數據,包括單個 Pod 的 CPU、內存、網絡和磁盤使用情況。

在左上角可以切換到不同 Namespace 的 Pod。

Heapster 預定義的 Dashboard 很直觀也很簡單。如有必要,可以在 Grafana 中定義自己的 Dashboard 滿足特定的業務需求。

 

五十四、Prometheus Operator

 

前面我們介紹了 Kubernetes 的兩種監控方案 Weave Scope 和 Heapster,它們主要的監控對象是 Node 和 Pod。這些數據對 Kubernetes 運維人員是必須的,但還不夠。我們通常還希望監控集群本身的運行狀態,比如 Kubernetes 的 API Server、Scheduler、Controller Manager 等管理組件是否正常工作,負荷是否過大等?

本節我們將學習監控方案 Prometheus Operator,它能回答上面這些問題。

Prometheus Operator 是 CoreOS 開發的基於 Prometheus 的 Kubernetes 監控方案,也可能是目前功能最全面的開源方案。我們先通過截圖了解一下它能干什么。

Prometheus Operator 通過 Grafana 展示監控數據,預定義了一系列的 Dashboard:

可以監控 Kubernetes 集群的整體健康狀態:

整個集群的資源使用情況:

Kubernetes 各個管理組件的狀態:

節點的資源使用情況:

Deployment 的運行狀態:

Pod 的運行狀態:

這些 Dashboard 展示了從集群到 Pod 的運行狀況,能夠幫助用戶更好地運維 Kubernetes。而且 Prometheus Operator 迭代非常快,相信會繼續開發出更多更好的功能,所以值得我們花些時間學習和實踐。

通過上面這些內容相信對 Prometheus Operator 有了些感性的認識,下一節我們將討論 Prometheus Operator 的架構。

 

 五十五、 Prometheus Operator 的架構

本節討論 Prometheus Operator 的架構。
因為 Prometheus Operator 是基於 Prometheus 的,我們需要先了解一下 Prometheus。

Prometheus 架構

Prometheus 是一個非常優秀的監控工具。准確的說,應該是監控方案。Prometheus 提供了數據搜集、存儲、處理、可視化和告警一套完整的解決方案。Prometheus 的架構如下圖所示:

官網上的原始架構圖比上面這張要復雜一些,為了避免注意力分散,這里只保留了最重要的組件。

Prometheus Server

Prometheus Server 負責從 Exporter 拉取和存儲監控數據,並提供一套靈活的查詢語言(PromQL)供用戶使用。

Exporter

Exporter 負責收集目標對象(host, container...)的性能數據,並通過 HTTP 接口供 Prometheus Server 獲取。

可視化組件

監控數據的可視化展現對於監控方案至關重要。以前 Prometheus 自己開發了一套工具,不過后來廢棄了,因為開源社區出現了更為優秀的產品 Grafana。Grafana 能夠與 Prometheus 無縫集成,提供完美的數據展示能力。

Alertmanager

用戶可以定義基於監控數據的告警規則,規則會觸發告警。一旦 Alermanager 收到告警,會通過預定義的方式發出告警通知。支持的方式包括 Email、PagerDuty、Webhook 等.

Prometheus Operator 架構

Prometheus Operator 的目標是盡可能簡化在 Kubernetes 中部署和維護 Prometheus 的工作。其架構如下圖所示:

圖上的每一個對象都是 Kubernetes 中運行的資源。

Operator

Operator 即 Prometheus Operator,在 Kubernetes 中以 Deployment 運行。其職責是部署和管理 Prometheus Server,根據 ServiceMonitor 動態更新 Prometheus Server 的監控對象。

Prometheus Server

Prometheus Server 會作為 Kubernetes 應用部署到集群中。為了更好地在 Kubernetes 中管理 Prometheus,CoreOS 的開發人員專門定義了一個命名為 Prometheus 類型的 Kubernetes 定制化資源。我們可以把 Prometheus看作是一種特殊的 Deployment,它的用途就是專門部署 Prometheus Server。

Service

這里的 Service 就是 Cluster 中的 Service 資源,也是 Prometheus 要監控的對象,在 Prometheus 中叫做 Target。每個監控對象都有一個對應的 Service。比如要監控 Kubernetes Scheduler,就得有一個與 Scheduler 對應的 Service。當然,Kubernetes 集群默認是沒有這個 Service 的,Prometheus Operator 會負責創建。

ServiceMonitor

Operator 能夠動態更新 Prometheus 的 Target 列表,ServiceMonitor 就是 Target 的抽象。比如想監控 Kubernetes Scheduler,用戶可以創建一個與 Scheduler Service 相映射的 ServiceMonitor 對象。Operator 則會發現這個新的 ServiceMonitor,並將 Scheduler 的 Target 添加到 Prometheus 的監控列表中。

ServiceMonitor 也是 Prometheus Operator 專門開發的一種 Kubernetes 定制化資源類型。

Alertmanager

除了 Prometheus 和 ServiceMonitor,Alertmanager 是 Operator 開發的第三種 Kubernetes 定制化資源。我們可以把 Alertmanager 看作是一種特殊的 Deployment,它的用途就是專門部署 Alertmanager 組件。

 

五十六、部署 Prometheus Operator

本節在實踐時使用的是 Prometheus Operator 版本 v0.14.0。由於項目開發迭代速度很快,部署方法可能會更新,必要時請參考官方文檔。

下載最新源碼

git clone https://github.com/coreos/prometheus-operator.git
cd prometheus-operator

為方便管理,創建一個單獨的 Namespace monitoring,Prometheus Operator 相關的組件都會部署到這個 Namespace。

kubectl create namespace monitoring

安裝 Prometheus Operator Deployment

helm install --name prometheus-operator --set rbacEnable=true --namespace=monitoring helm/prometheus-operator

Prometheus Operator 所有的組件都打包成 Helm Chart,安裝部署非常方便。如果對 Helm 不熟悉,可以參考前面相關章節。

安裝 Prometheus、Alertmanager 和 Grafana

helm install --name prometheus --set serviceMonitorsSelector.app=prometheus --set ruleSelector.app=prometheus --namespace=monitoring helm/prometheus
helm install --name alertmanager --namespace=monitoring helm/alertmanager
helm install --name grafana --namespace=monitoring helm/grafana

可以通過 kubectl get prometheus 查看 Prometheus 類型的資源。

為了方便訪問 Prometheus Server,這里已經將 Service 類型通過 kubectl edit 改為 NodePort。

同樣可以查看 Alertmanager 和 Grafana 的相關資源。

Service 類型也都已經改為 NodePort。

安裝 kube-prometheus

kube-prometheus 是一個 Helm Chart,打包了監控 Kubernetes 需要的所有 Exporter 和 ServiceMonitor。

helm install --name kube-prometheus --namespace=monitoring helm/kube-prometheus

每個 Exporter 會對應一個 Service,為 Pormetheus 提供 Kubernetes 集群的各類監控數據。

每個 Service 對應一個 ServiceMonitor,組成 Pormetheus 的 Target 列表。

如下是與 Prometheus Operator 相關的所有 Pod。

我們注意到有些 Exporter 沒有運行 Pod,這是因為像 API Server、Scheduler、Kubelet 等 Kubernetes 內部組件原生就支持 Prometheus,只需要定義 Service 就能直接從預定義端口獲取監控數據。

瀏覽器打開 Pormetheus 的 Web UI http://192.168.56.105:30413/targets

所有 Target 的狀態都是 UP

安裝 Alert 規則

Prometheus Operator 提供了默認的 Alertmanager 告警規則,通過如下命令安裝。

sed -ie 's/role: prometheus-rulefiles/app: prometheus/g' contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml
sed -ie 's/prometheus: k8s/prometheus: prometheus/g' contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml
sed -ie 's/job=\"kube-controller-manager/job=\"kube-prometheus-exporter-kube-controller-manager/g' contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml
sed -ie 's/job=\"apiserver/job=\"kube-prometheus-exporter-kube-api/g' contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml
sed -ie 's/job=\"kube-scheduler/job=\"kube-prometheus-exporter-kube-scheduler/g' contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml
sed -ie 's/job=\"node-exporter/job=\"kube-prometheus-exporter-node/g' contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml
kubectl apply -n monitoring -f contrib/kube-prometheus/manifests/prometheus/prometheus-k8s-rules.yaml

安裝 Grafana Dashboard

Prometheus Operator 定義了顯示監控數據的默認 Dashboard,通過如下命令安裝。

sed -ie 's/grafana-dashboards-0/grafana-grafana/g' contrib/kube-prometheus/manifests/grafana/grafana-dashboards.yaml
sed -ie 's/prometheus-k8s.monitoring/prometheus-prometheus.monitoring/g' contrib/kube-prometheus/manifests/grafana/grafana-dashboards.yaml
kubectl apply -n monitoring -f contrib/kube-prometheus/manifests/grafana/grafana-dashboards.yaml

打開 Grafana 的 Web UI http://192.168.56.105:32342/

Grafana 的 DataSource 和 Dashboard 已自動配置。點擊 Home 就可以使用我們在最開始討論過的那些 Dashboard 了。

小結

本章我們實踐了三種 Kubernetes 監控方案。

Weave Scope 可以展示集群和應用的完整視圖。其出色的交互性讓用戶能夠輕松對容器化應用進行實時監控和問題診斷。

Heapster 是 Kubernetes 原生的集群監控方案。預定義的 Dashboard 能夠從 Cluster 和 Pods 兩個層次監控 Kubernetes。

Prometheus Operator 可能是目前功能最全面的 Kubernetes 開源監控方案。除了能夠監控 Node 和 Pod,還支持集群的各種管理組件,比如 API Server、Scheduler、Controller Manager 等。

Kubernetes 監控是一個快速發展的領域。隨着 Kubernetes 的普及,一定會涌現出更多的優秀方案。

 

 五十七、Kubernetes 集群日志管理

Kubernetes 開發了一個 Elasticsearch 附加組件來實現集群的日志管理。這是一個 Elasticsearch、Fluentd 和 Kibana 的組合。Elasticsearch 是一個搜索引擎,負責存儲日志並提供查詢接口;Fluentd 負責從 Kubernetes 搜集日志並發送給 Elasticsearch;Kibana 提供了一個 Web GUI,用戶可以瀏覽和搜索存儲在 Elasticsearch 中的日志。

部署

Elasticsearch 附加組件本身會作為 Kubernetes 的應用在集群里運行,其 YAML 配置文件可從 https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/fluentd-elasticsearch 獲取。

可將這些 YAML 文件下載到本地目錄,比如 addons ,通過 kubectl apply -f addons/ 部署。

這里有一點需要注意:后面我們會通過 NodePort 訪問 Kibana,需要注釋掉 kibana-deployment.yaml 中的環境變量 SERVER_BASEPATH,否則無法訪問。

所有的資源都部署在 kube-system Namespace 里。

DaemonSet fluentd-es 從每個節點收集日志,然后發送給 Elasticsearch。

Elasticsearch 以 StatefulSet 資源運行,並通過 Service elasticsearch-logging 對外提供接口。這里已經將 Service 的類型通過 kubectl edit 修改為 NodePort。

可通過 http://192.168.56.106:32607/ 驗證 Elasticsearch 已正常工作。

Kibana 以 Deployment 資源運行,用戶可通過 Service kibana-logging 訪問其 Web GUI。這里已經將 Service 的類型修改為 NodePort。

通過 http://192.168.56.106:30319/ 訪問 Kibana。

Kibana 會顯示 Index Pattern 創建頁面。直接點擊 Create,Kibana 會自動完成后續配置。

這時,點擊左上角的 Discover 就可以查看和檢索 Kubernetes 日志了。

Kubernetes 日志管理系統已經就緒,用戶可以根據需要創建自己的 Dashboard,具體方法可參考 Kibana 官方文檔。

小結

Elasticsearch 附加組件本身會作為 Kubernetes 的應用在集群里運行,以實現集群的日志管理。它是一個 Elasticsearch、Fluentd 和 Kibana 的組合。
Elasticsearch 是一個搜索引擎,負責存儲日志並提供查詢接口。
Fluentd 負責從 Kubernetes 搜集日志並發送給 Elasticsearch。
Kibana 提供了一個 Web GUI,用戶可以瀏覽和搜索存儲在 Elasticsearch 中的日志。

寫在最后

作為 Kubernetes 的實戰教程,我們已經到了該收尾的地方。

本教程涵蓋了 Kubernetes 最最重要的技術:集群架構、容器化應用部署、Scale Up/Down、滾動更新、監控檢查、集群網絡、數據管理、監控和日志管理,通過大量的實驗探討了 Kubernetes 的運行機制。

這個教程的目標是使讀者能夠掌握實施和管理 Kubernetes 的必需技能,能夠真正將 Kubernetes 用起來。

為了達到這個目標,每一章都設計了大量的實踐操作環節,通過截圖和日志幫助讀者理解各個技術要點,同時為讀者自己實踐 Kubernetes 提供詳盡的參考。

本教程對讀者應該會有兩個作用:

  1. 初學者可以按照章節順序系統地學習 Kubernetes,並通過教程中的實驗掌握 Kubernetes 的理論知識和實操技能。

  2. 有經驗的運維人員可以將本教程當做參考材料,在實際工作中有針對性地查看相關知識點。

希望讀者們能夠通過本教程打下扎實基礎,能夠從容地運維 Kubernetes ,並結合所在公司和組織的實際需求搭建出實用的容器管理平台。

其它參考:

https://www.cnblogs.com/CloudMan6/tag/Kubernetes/default.html?page=3

 

 Prometheus Operator 的架構


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM