1. 程式人生 > >K8s 二、(1、kubeadm部署Kubernetes叢集)

K8s 二、(1、kubeadm部署Kubernetes叢集)

準備工作

  1. 滿足安裝 Docker 專案所需的要求,比如 64 位的 Linux 作業系統、3.10 及以上的核心版本;

  2. x86 或者 ARM 架構均可;

  3. 機器之間網路互通,這是將來容器之間網路互通的前提;

  4. 有外網訪問許可權,因為需要拉取映象;

  5. 能夠訪問到gcr.io、quay.io這兩個 docker registry,因為有小部分映象需要在這裡拉取;

  6. 單機可用資源建議 2 核 CPU、8 GB 記憶體或以上,再小的話問題也不大,但是能排程的 Pod 數量就比較有限了;

  7. 30 GB 或以上的可用磁碟空間,這主要是留給 Docker 映象和日誌檔案用的。

在本次部署中,我準備的機器配置如下:

  1. 2 核 CPU、 7.5 GB 記憶體;

  2. 30 GB 磁碟;

  3. CentOS7.4;

  4. 內網互通;

  5. 外網訪問許可權不受限制。

然後,我再和你介紹一下今天實踐的目標:

  1. 在所有節點上安裝 Docker 和 kubeadm;

  2. 部署 Kubernetes Master;

  3. 部署容器網路外掛;

  4. 部署 Kubernetes Worker;

  5. 部署 Dashboard 視覺化外掛;

  6. 部署容器儲存外掛。

安裝 kubeadm 和 Docker

 curl -s https://
packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - cat <<EOF > /etc/apt/sources.list.d/kubernetes.list deb http://apt.kubernetes.io/ kubernetes-xenial main EOF yum update yum install -y docker.io kubeadm

在上述安裝 kubeadm 的過程中,kubeadm 和 kubelet、kubectl、kubernetes-cni 這幾個二進位制檔案都會被自動安裝好。

部署 Kubernetes 的 Master 節點

這裡我編寫了一個給 kubeadm 用的 YAML 檔案(名叫:kubeadm.yaml):

apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
controllerManagerExtraArgs:
  horizontal-pod-autoscaler-use-rest-clients: "true"
  horizontal-pod-autoscaler-sync-period: "10s"
  node-monitor-grace-period: "10s"
apiServerExtraArgs:
  runtime-config: "api/all=true"
kubernetesVersion: "stable-1.11"

這個配置中,我給 kube-controller-manager 設定了:

horizontal-pod-autoscaler-use-rest-clients: "true"

這意味著,將來部署的 kube-controller-manager 能夠使用自定義資源(Custom Metrics)進行自動水平擴充套件。

其中,“stable-1.11”就是 kubeadm 幫我們部署的 Kubernetes 版本號,即:Kubernetes release 1.11 最新的穩定版,在我的環境下,它是 v1.11.1。你也可以直接指定這個版本,比如:kubernetesVersion: “v1.11.1”。

然後,我們只需要執行一句指令:

kubeadm init --config kubeadm.yaml

就可以完成 Kubernetes Master 的部署了,這個過程只需要幾分鐘。部署完成後,kubeadm 會生成一行指令:

kubeadm join 10.168.0.2:6443 --token 00bwbx.uvnaa2ewjflwu1ry --discovery-token-ca-cert-hash sha256:00eb62a2a6020f94132e3fe1ab721349bbcd3e9b94da9654cfe15f2985ebd711

這個 kubeadm join 命令,就是用來給這個 Master 節點新增更多工作節點(Worker)的命令。我們在後面部署 Worker 節點的時候馬上會用到它,所以找一個地方把這條命令記錄

 

此外,kubeadm 還會提示 第一次使用 Kubernetes 叢集所需要的配置命令:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

而需要這些配置命令的原因是:Kubernetes 叢集預設需要加密方式訪問。所以,這幾條命令,就是將剛剛部署生成的 Kubernetes 叢集的安全配置檔案,儲存到當前使用者的.kube 目錄下,kubectl 預設會使用這個目錄下的授權資訊訪問 Kubernetes 叢集。

如果不這麼做的話,我們每次都需要通過 export KUBECONFIG 環境變數告訴 kubectl 這個安全配置檔案的位置。

 

現在,就可以使用 kubectl get 命令來檢視當前唯一一個節點的狀態了:

 

kubectl get nodes

NAME      STATUS     ROLES     AGE       VERSION
master    NotReady   master    1d        v1.11.1

可以看到,這個 get 指令輸出的結果裡,Master 節點的狀態是 NotReady,這是為什麼呢?

在除錯 Kubernetes 叢集時,最重要的手段就是用 kubectl describe 來檢視這個節點(Node)物件的詳細資訊、狀態和事件(Event),檢視一下

kubectl describe node master

...
Conditions:
...

Ready   False ... KubeletNotReady  runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady 

通過 kubectl describe 指令的輸出,可以看到 NodeNotReady 的原因在於,尚未部署任何網路外掛。

另外,還可以通過 kubectl 檢查這個節點上各個系統 Pod 的狀態,其中,kube-system 是 Kubernetes 專案預留的系統 Pod 的工作空間(Namepsace,注意它並不是 Linux Namespace,它只是 Kubernetes 劃分不同工作空間的單位):

kubectl get pods -n kube-system

NAME               READY   STATUS   RESTARTS  AGE
coredns-78fcdf6894-j9s52     0/1    Pending  0     1h
coredns-78fcdf6894-jm4wf     0/1    Pending  0     1h
etcd-master           1/1    Running  0     2s
kube-apiserver-master      1/1    Running  0     1s
kube-controller-manager-master  0/1    Pending  0     1s
kube-proxy-xbd47         1/1    NodeLost  0     1h
kube-scheduler-master      1/1    Running  0     1s

可以看到,CoreDNS、kube-controller-manager 等依賴於網路的 Pod 都處於 Pending 狀態,即排程失敗。這當然是符合預期的:因為這個 Master 節點的網路尚未就緒。

部署網路外掛

在 Kubernetes 專案“一切皆容器”的設計理念指導下,部署網路外掛非常簡單,只需要執行一句 kubectl apply 指令,以 Weave 為例:

kubectl apply -f https://git.io/weave-kube-1.6

部署完成後,我們可以通過 kubectl get 重新檢查 Pod 的狀態:

 kubectl get pods -n kube-system

NAME                             READY     STATUS    RESTARTS   AGE
coredns-78fcdf6894-j9s52         1/1       Running   0          1d
coredns-78fcdf6894-jm4wf         1/1       Running   0          1d
etcd-master                      1/1       Running   0          9s
kube-apiserver-master            1/1       Running   0          9s
kube-controller-manager-master   1/1       Running   0          9s
kube-proxy-xbd47                 1/1       Running   0          1d
kube-scheduler-master            1/1       Running   0          9s
weave-net-cmk27                  2/2       Running   0          

 

可以看到,所有的系統 Pod 都成功啟動了,而剛剛部署的 Weave 網路外掛則在 kube-system 下面新建了一個名叫 weave-net-cmk27 的 Pod,一般來說,這些 Pod 就是容器網路外掛在每個節點上的控制組件。

至此,Kubernetes 的 Master 節點就部署完成了。如果你只需要一個單節點的 Kubernetes,現在你就可以使用了。不過,在預設情況下,Kubernetes 的 Master 節點是不能執行使用者 Pod 的,所以還需要額外做一個小操作。在本篇的最後部分,我會介紹到它。

部署 Kubernetes 的 Worker 節點

Kubernetes 的 Worker 節點跟 Master 節點幾乎是相同的,它們執行著的都是一個 kubelet 元件。唯一的區別在於,在 kubeadm init 的過程中,kubelet 啟動後,Master 節點上還會自動執行 kube-apiserver、kube-scheduler、kube-controller-manger 這三個系統 Pod。

所以,相比之下,部署 Worker 節點反而是最簡單的,只需要兩步即可完成。

第一步,在所有 Worker 節點上執行“安裝 kubeadm 和 Docker”一節的所有步驟。

第二步,執行部署 Master 節點時生成的 kubeadm join 指令:

kubeadm join 10.168.0.2:6443 --token 00bwbx.uvnaa2ewjflwu1ry --discovery-token-ca-cert-hash sha256:00eb62a2a6020f94132e3fe1ab721349bbcd3e9b94da9654cfe15f2985ebd711

通過 Taint/Toleration 調整 Master 執行 Pod 的策略

預設情況下 Master 節點是不允許執行使用者 Pod 的。而 Kubernetes 做到這一點,依靠的是 Kubernetes 的 Taint/Toleration 機制。

它的原理非常簡單:一旦某個節點被加上了一個 Taint,即被“打上了汙點”,那麼所有 Pod 就都不能在這個節點上執行,因為 Kubernetes 的 Pod 都有“潔癖”。

除非,有個別的 Pod 宣告自己能“容忍”這個“汙點”,即聲明瞭 Toleration,它才可以在這個節點上執行。

其中,為節點打上“汙點”(Taint)的命令是:

kubectl taint nodes node1 foo=bar:NoSchedule

這時,該 node1 節點上就會增加一個鍵值對格式的 Taint,即:foo=bar:NoSchedule。其中值裡面的 NoSchedule,意味著這個 Taint 只會在排程新 Pod 時產生作用,而不會影響已經在 node1 上執行的 Pod,哪怕它們沒有 Toleration。

那麼 Pod 又如何宣告 Toleration 呢?

我們只要在 Pod 的.yaml 檔案中的 spec 部分,加入 tolerations 欄位即可:

apiVersion: v1
kind: Pod
...
spec:
  tolerations:
  - key: "foo"
    operator: "Equal"
    value: "bar"
    effect: "NoSchedule"

這個 Toleration 的含義是,這個 Pod 能“容忍”所有鍵值對為 foo=bar 的 Taint( operator: “Equal”,“等於”操作)。

現在回到我們已經搭建的叢集上來。這時,如果你通過 kubectl describe 檢查一下 Master 節點的 Taint 欄位,就會有所發現了:

 kubectl describe node master

Name:               master
Roles:              master
Taints:             node-role.kubernetes.io/master:NoSchedule

可以看到,Master 節點預設被加上了node-role.kubernetes.io/master:NoSchedule這樣一個“汙點”,其中“鍵”是node-role.kubernetes.io/master,而沒有提供“值”。

此時,你就需要像下面這樣用“Exists”操作符(operator: “Exists”,“存在”即可)來說明,該 Pod 能夠容忍所有以 foo 為鍵的 Taint,才能讓這個 Pod 執行在該 Master 節點上

apiVersion: v1
kind: Pod
...
spec:
  tolerations:
  - key: "foo"
    operator: "Exists"
    effect: "NoSchedule

當然,如果你就是想要一個單節點的 Kubernetes,刪除這個 Taint 才是正確的選擇:

$ kubectl taint nodes --all node-role.kubernetes.io/master-

如上所示,我們在“node-role.kubernetes.io/master”這個鍵後面加上了一個短橫線“-”,這個格式就意味著移除所有以“node-role.kubernetes.io/master”為鍵的 Taint。

到了這一步,一個基本完整的 Kubernetes 叢集就部署完畢了。是不是很簡單呢

部署 Dashboard 視覺化外掛

在 Kubernetes 社群中,有一個很受歡迎的 Dashboard 專案,它可以給使用者提供一個視覺化的 Web 介面來檢視當前叢集的各種資訊。毫不意外,它的部署也相當簡單:

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

部署完成之後,我們就可以檢視 Dashboard 對應的 Pod 的狀態了:

kubectl get pods -n kube-system

kubernetes-dashboard-6948bdb78-f67xk   1/1       Running   0          1m

需要注意的是,由於 Dashboard 是一個 Web Server,很多人經常會在自己的公有云上無意地暴露 Dashboard 的埠,從而造成安全隱患。所以,1.7 版本之後的 Dashboard 專案部署完成後,預設只能通過 Proxy 的方式在本地訪問。具體的操作,你可以檢視 Dashboard 專案的官方文件

而如果你想從叢集外訪問這個 Dashboard 的話,就需要用到 Ingress。

 

 

部署容器儲存外掛

接下來,完成這個 Kubernetes 叢集的最後一塊拼圖:容器持久化儲存。

我在前面介紹容器原理時已經提到過,很多時候我們需要用資料卷(Volume)把外面宿主機上的目錄或者檔案掛載進容器的 Mount Namespace 中,從而達到容器和宿主機共享這些目錄或者檔案的目的。容器裡的應用,也就可以在這些資料卷中新建和寫入檔案。

可是,如果你在某一臺機器上啟動的一個容器,顯然無法看到其他機器上的容器在它們的資料卷裡寫入的檔案。這是容器最典型的特徵之一:無狀態。

而容器的持久化儲存,就是用來儲存容器儲存狀態的重要手段:儲存外掛會在容器裡掛載一個基於網路或者其他機制的遠端資料卷,使得在容器裡建立的檔案,實際上是儲存在遠端儲存伺服器上,或者以分散式的方式儲存在多個節點上,而與當前宿主機沒有任何繫結關係。這樣,無論你在其他哪個宿主機上啟動新的容器,都可以請求掛載指定的持久化儲存卷,從而訪問到資料卷裡儲存的內容。這就是“持久化”的含義。

由於 Kubernetes 本身的鬆耦合設計,絕大多數儲存專案,比如 Ceph、GlusterFS、NFS 等,都可以為 Kubernetes 提供持久化儲存能力。在這次的部署實戰中,我會選擇部署一個很重要的 Kubernetes 儲存外掛專案:Rook。

Rook 專案是一個基於 Ceph 的 Kubernetes 儲存外掛(它後期也在加入對更多儲存實現的支援)。不過,不同於對 Ceph 的簡單封裝,Rook 在自己的實現中加入了水平擴充套件、遷移、災難備份、監控等大量的企業級功能,使得這個專案變成了一個完整的、生產級別可用的容器儲存外掛。

得益於容器化技術,用兩條指令,Rook 就可以把複雜的 Ceph 儲存後端部署起來

kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/operator.yaml

 kubectl apply -f https://raw.githubusercontent.com/rook/rook/master/cluster/examples/kubernetes/ceph/cluster.yaml

在部署完成後,你就可以看到 Rook 專案會將自己的 Pod 放置在由它自己管理的兩個 Namespace 當中:

$ kubectl get pods -n rook-ceph-system
NAME                                  READY     STATUS    RESTARTS   AGE
rook-ceph-agent-7cv62                 1/1       Running   0          15s
rook-ceph-operator-78d498c68c-7fj72   1/1       Running   0          44s
rook-discover-2ctcv                   1/1       Running   0          15s

$ kubectl get pods -n rook-ceph
NAME                   READY     STATUS    RESTARTS   AGE
rook-ceph-mon0-kxnzh   1/1       Running   0          13s
rook-ceph-mon1-7dn2t   1/1       Running   0          2s

這樣,一個基於 Rook 持久化儲存叢集就以容器的方式執行起來了,而接下來在 Kubernetes 專案上建立的所有 Pod 就能夠通過 Persistent Volume(PV)和 Persistent Volume Claim(PVC)的方式,在容器裡掛載由 Ceph 提供的資料捲了。

而 Rook 專案,則會負責這些資料卷的生命週期管理、災難備份等運維工作。關於這些容器持久化儲存的知識,我會在後續章節中專門講解。

為什麼我要選擇 Rook 專案呢?

其實,是因為這個專案很有前途。

如果你去研究一下 Rook 專案的實現,就會發現它巧妙地依賴了 Kubernetes 提供的編排能力,合理的使用了很多諸如 Operator、CRD 等重要的擴充套件特性(這些特性我都會在後面的文章中逐一講解到)。這使得 Rook 專案,成為了目前社群中基於 Kubernetes API 構建的最完善也最成熟的容器儲存外掛。

這個叢集的部署過程並不像傳說中那麼繁瑣,這主要得益於:

  1. kubeadm 專案大大簡化了部署 Kubernetes 的準備工作,尤其是配置檔案、證書、二進位制檔案的準備和製作,以及叢集版本管理等操作,都被 kubeadm 接管了。

  2. Kubernetes 本身“一切皆容器”的設計思想,加上良好的可擴充套件機制,使得外掛的部署非常簡便。

上述思想,也是開發和使用 Kubernetes 的重要指導思想,即:基於 Kubernetes 開展工作時,你一定要優先考慮這兩個問題:

  1. 我的工作是不是可以容器化?

  2. 我的工作是不是可以藉助 Kubernetes API 和可擴充套件機制來完成?

而一旦這項工作能夠基於 Kubernetes 實現容器化,就很有可能像上面的部署過程一樣,大幅簡化原本複雜的運維工作。對於時間寶貴的技術人員來說,這個變化的重要性是不言而喻的。