iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 26
0

今天來看一下 Kubernetes 的組成元件,然後在本機上用虛擬機器建立一個有多節點的 Kubernetes 叢集。

Kubernetes 組成元件

關於 Kubernetes 組成元件,請參考 https://kubernetes.io/docs/concepts/overview/components/ 的說明。

主元件

主元件 (master component) 組成了叢集的控制層,負責叢集中的整體性決策,例如 Pod 排程,以及偵測、回應叢集事件,例如在 replica 數量不足時產生新的 Pod。這些主元件通常都運行於同一台機器中,而這台機器通常不執行使用者的容器。

  1. kube-apiserver: 負責揭露 (expose) Kubernetes API,可視為 Kubernetes 控制層的前端。
  2. etcd: 用於儲存 Kubernetes 資料的鍵值儲存系統。
  3. kube-scheduler: 負責將新建立的 Pod 分派至不同的節點,考慮的因素包括資源需求、軟硬體或政策的限制、資料的在地性 (locality) 等等。
  4. kube-controller-manager: 負責執行以下的控制器
    • 節點 (node) controller:當節點有問題 (go down) 負責通知與回應。
    • 複本 (replication) 控制器:維持每個 Replication controller 的 Pod replica 數量。
    • 端點 (endpoint) 控制器:產生 Endpoint 物件,Endpoint 物件可以想成是用來結合 Pod 和 Service 的物件,可以用 kubectl get endpoints 來查看。不知道為什麼這個指令的 endpoints 只能使用複數形式。
    • 服務帳號 (service account) 及 token 控制器:建立預設的帳號、為新的命名空間建立新的 API 存取 token。
  5. cloud-controller-manager: 負責執行和底層雲端供應商互動的控制器。從文件中的敘述中看來好像是要替雲端供應商和 Kubernetes 提供一個中間層。

節點元件

節點元件 (node component) 則包含了 kubelet、kube-proxy 以及 container runtime (容器執行期)。

  1. kubelet: 負責管理 Pod 裡的容器,根據容器的 spec 定義,確保它們運行的健康狀態。
  2. kube-proxy: 處理 Kubernetes 服務的抽象化、主機上的網路規則及轉送 (forwarding) 等等。
  3. container runtime: 例如 Docker、rkt 等等實際用來執行容器操作的引擎。

Addons

addon 這個是附加元件或外掛程式的意思,在這裡是指實作叢集功能的 Pod 和 Service。在叢集的機器中使用 docker container ls 指令會看到很多容器在運行,這些容器中有些就是在執行這裡提到的 addon,一些常見的 addon 如下:

  1. DNS
  2. Web UI (dashboard)
  3. 容器資源監控 (container resource monitoring)
  4. 叢集層級 log (cluster-level logging)
    這些 addon 位在 kube-system 的命名空間之中。較詳細的 addon 清單可參考 https://kubernetes.io/docs/concepts/cluster-administration/addons/

使用 kubeadm 建立 Kubernetes 叢集

這幾天的操作都是透過 Minikube 來進行,它是一個單節點的 Kubernetes 叢集,但是只有一個節點的叢集是無法應付單點失效的狀況的。在雲端供應商的環境,像是 Azure、GCP 應該都有建立 Kubernetes 叢集的功能,只要幾個簡單按鍵就可以完成,但如果想自己建立一個多節點的 Kubernetes 叢集要怎麼做呢?接下來就透過 Kubernetes 提供的 kubeadm 工具來建立一個多節點的叢集。

之前在介紹 Ansible 的時候利用 Vagrant 建立了多台機器的環境,打算以這幾台機器為基礎,回憶一下當時建立了三台機器,在 Vagrantfile 中分別命名為 node1、node2 及 node3。這裡讓 node1 作為主節點。node2、node3 作為一般節點。在這三台機器上必須安裝的套件包括 Docker、kubectl、kubelet、kubeadm。來看一下文件中對 kubeadm 的介紹:它是一個提供 kubeadm initkubeadm join 等命令,作為建立 Kubernetes 節點的快速途徑 (fast paths) 的最佳實踐。其實之前學了 Ansible,所以應用所學的話應該用 Ansible 來做,除了安裝必要的套件,甚至(應該)可以用來處理初始叢集及加入叢集的工作,但在鐵人賽中有時間壓力沒辦法仔細測試,所以這邊就直接安裝,或者也可以用 Vagrant 的 provision 來幫忙完成安裝套件的任務。

Docker 和 Kubernetes 工具在安裝上應該沒有相依性,我是先安裝 Docker 再安裝 Kubernetes 相關工具。Docker 的安裝方式之前提過了,而 Kubernetes 相關套件的安裝說明,可參考 https://kubernetes.io/docs/setup/independent/install-kubeadm/。在 Ubuntu 環境的安裝指令如下:

$ apt-get update && apt-get install -y apt-transport-https curl
$ 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 https://apt.kubernetes.io/ kubernetes-xenial main
EOF
$ apt-get update
$ apt-get install -y kubelet kubeadm kubectl
$ apt-mark hold kubelet kubeadm kubectl

在 Vagrant 機器中,以 vagrant 帳號登入執行以上命令需要 sudo,但第三個指令可能無法直接加上 sudo(應該是因為重導又有 EOF 的關係),這個指令是要在 kubernetes.list 檔案中加入 deb https://apt.kubernetes.io/ kubernetes-xenial main 這串文字。安裝完畢後,就可以來建立叢集了。在安裝說明的最後有一個 configure cgroup driver used by kubelet on Master Node,因為這裡是使用 Docker 作為容器的 runtime,可以跳過這一段的設定。

接下來要設定 master node,在 node1 執行下列指令,結果輸出如下:

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=192.168.50.10

[init] using Kubernetes version: v1.12.2
[preflight] running pre-flight checks
[preflight/images] Pulling images required for setting up a Kubernetes cluster
.
.
.
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

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

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 192.168.50.10:6443 --token ffvofw.lk2e4prugfrsxqy2 --discovery-token-ca-cert-hash sha256:5d8916483d7b246fbcd81023d4433079984fead7c6cd8e8049e830d998a9643b

--pod-network-cidr 在設定 Kubernetes 叢集中的網路,10.244.0.0/16 這個值是等一下要安裝的網路附加元件中所指定的,而 --apiserver-advertise-address 則是指定 master node 主機 IP。執行要花一點時間,接下來按照輸出進行主節點的設定。

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

如果之前有建立過 Kubernetes 叢集而 $HOME/.kube 目錄存在的話,請將它刪除後再執行這三個指令,否則可能會有一些先前的設定 cache 導致連線認證的異常。剛才 kubeadm init 的輸出選項最後面有其他節點要加入此叢集的指令,請先記錄下來。

接下來要安裝網路附加元件,其實有蠻多選項的,請參考 https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network。這裡依照之前看過的教學選擇 flannel ,這也是應試目標能力中有提到要認識的。安裝指令如下:

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

關於 flannel 可參考官方 GitHub https://github.com/coreos/flannel 以及 https://blog.laputa.io/kubernetes-flannel-networking-6a1cb1f8ec7c 的說明。

現在應該可以用 kubectl get nodes 來確認叢集的節點狀況。

$ kubectl get nodes
NAME     STATUS   ROLES    AGE   VERSION
node-1   Ready    master   92m   v1.12.2

接下來來安裝 Kubenetes dashboard,第一天已經介紹過了,那時候是用 kubectl proxy 來將 Kubernetes API 代理到本機的 8001 port,但預設它只能在本機存取,它綁定的是 localhost。kubectl proxy 可以用 --address 指定要綁定那個位址,指定本機的 IP (192.168.50.10),但發現會無法存取,這好像是 Kubernetes 的預設,這些訊息只能在本機存取。所以這裡換一個方式,不要用 kubectl proxy

首先先安裝 Dashboard 的附加元件,一樣是使用 kubectl apply 指令,可參考 https://github.com/kubernetes/dashboard/wiki/Installation。這裡提供 recommended setup 以及 alternative setup 兩種方法,其中 recommended setup 必須使用 https,會需要一些憑證的設定,因為只是在本機上架設實驗環境,就用 http 的 alternative setup,安裝指令如下:

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

安裝好了之後來查看一下相關的 Deployment 和 Service,指令如下:

$ kubectl -n kube-system get deployments kubernetes-dashboard
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-dashboard   1         1         1            1           2m23s

$ kubectl -n kube-system get services kubernetes-dashboard
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes-dashboard   ClusterIP   10.106.116.30   <none>        80/TCP    2m30s

請注意這裡的指令加了 -n kube-system 參數,表示我們要查看的資源位於 kube-system 這個命名空間 (namespace) 之中。可以看到 kubernetes-dashboard 這個 Service 的 type 是 ClusterIP,要把它改成 NodePort。執行 kubectl -n kube-system edit services kubernetes-dashboard 後會出現文字編輯器,在下面的片段中,將 .spec.type 的值由 ClusterIP 改成 NodePort,至於要對應到本機的那個 port 可以讓 Kubernetes 來決定,或自己直接指定。

spec:
  clusterIP: 10.106.116.30
  ports:
  - port: 80
    protocol: TCP
    targetPort: 9090
  selector:
    k8s-app: kubernetes-dashboard
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}

完成之後,再用 kubectl get services 查看一下這個服務對應到那個 port。

$ kubectl -n kube-system get services kubernetes-dashboard
NAME                   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes-dashboard   NodePort   10.106.116.30   <none>        80:31983/TCP   43m

在我的機器上它對應到 31983 port,因此訪問 http://192.168.50.10:31983,應該會看到以下的畫面:

發現上面有黃色警告方塊,文字是這樣的

configmaps is forbidden: User "system:serviceaccount:kube-system:kubernetes-dashboard" cannot list resource "configmaps" in API group "" in the namespace "default"

看起來是權限的問題,參考 https://github.com/kubernetes/dashboard/wiki/Access-control,需要有一個足夠權限的角色,請建立 dashboard-admin.yaml,文字內容如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system

這裡出現了一個新的資源物件,稱為 ClusterRoleBinding,總之是一些和權限相關的設定,使用 kubectl create 來建立資源。

$ kubectl create -f dashboard-admin.yaml
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created

重新整理頁面,警告訊息應該就會消失了。在左邊的 menu 按一下叢集 -> 節點,可以看到目前這個叢集有一個節點。接下來要將 node2 和 node3 也加入這個節點。請注意 node1、node2、node3 是在 Vagrant 中給機器的名稱,實際上它們的 hostname 應該是 node-1、node-2 以及 node-3,是當初在 Vagrantfile 中的設定值。

先連進 node2,然後執行剛才 kubeadm init 中記錄下來的 kubeadm join 指令,例如:

$ kubeadm join 192.168.50.10:6443 --token ffvofw.lk2e4prugfrsxqy2 --discovery-token-ca-cert-hash sha256:5d8916483d7b246fbcd81023d4433079984fead7c6cd8e8049e830d998a9643b

完成後在 master node 以 kubectl get nodes 來查看新節點是否有加入叢集,或者透過 dashboard 頁面確認。都沒問題的話,dashboard 節點頁面應該類似這樣:
https://ithelp.ithome.com.tw/upload/images/20181110/20111953pZWMZoqDOg.png

至此為止應該已經使用 kubeadm 建立一個具有三個節點的 Kubernets 叢集,並安裝了 Dashbord 的附加元件,使得查看叢集狀態變得更加容易。

假設今天不再需要此叢集,那要如何結束它呢?必須在 master node 上先將加入此叢集的節點結束掉,請執行下列指令(都在 master node 上作):

$ kubectl drain <node-name> --delete-local-data --force --ignore-daemonsets
$ kubectl delete node <node-name>

應該是先依序執行 worker node,最後再執行 master node。接下來要在每一個節點 (worker node 和 master node 都要) 執行 sudo Kubeadm reset 指令,並將 master node 上 $HOME/.kube 目錄刪除。

結語

這幾天大概瞭解了 Kubernetes 的基本架構,以及其中的主要元件,包括 PodReplicaSetReplicationControllerDeploymentService 等等,也進行了幾項實作,包括利用 Minikube 建立單節點的叢集,以及利用 Vagrant 機器建立具有多節點的叢集,並知道了利用 Volume 來留存資料的方法。其實四天的時間能寫的東西很有限,因為剩下的幾天還想再研究一項工具,所以趕著在四天結束,變成只是挑一些我感覺比較重要的東西來看,有可能遺漏不少也很重要的內容,再請大家自己找資料研究囉。


上一篇
[Day 25] Kubernetes (3)
下一篇
[Day 27] Jenkins (1)
系列文
30 天準備 LPI DevOps Tools Engineer 證照30

尚未有邦友留言

立即登入留言