深入剖析Kubernetes學習筆記:Kubernetes集群搭建與實踐(10)


一、難道 Kubernetes 項目就沒有簡單的部署方法了嗎?

這個問題,在 Kubernetes 社區里一直沒有得到足夠重視。直到 2017 年,在志願者的推動下,社區才終於發起了一個獨立的部署工具,名叫:kubeadm

這個項目的目的,就是要讓用戶能夠通過這樣兩條指令完成一個 Kubernetes 集群的部署:

# 創建一個 Master 節點
$ kubeadm init
 
# 將一個 Node 節點加入到當前集群中
$ kubeadm join <Master 節點的 IP 和端口 >

是不是非常方便呢?

不過,你可能也會有所顧慮:

Kubernetes 的功能那么多,這樣一鍵部署出來的集群,能用於生產環境嗎?

到目前為止(2018 年 9 月),這個問題的答案是:不能。

因為 kubeadm 目前最欠缺的是,一鍵部署一個高可用的 Kubernetes 集群,即:Etcd、Master 組件都應該是多節點集群,而不是現在這樣的單點。這,當然也正是 kubeadm 接下來發展的主要方向。

另一方面,Lucas 也正在積極地把 kubeadm phases 開放給用戶,即:用戶可以更加自由地定制 kubeadm 的每一個部署步驟。這些舉措,都可以讓這個項目更加完善,我對它的發展走向也充滿了信心。

當然,如果你有部署規模化生產環境的需求,我推薦使用kops或者 SaltStack 這樣更復雜的部署工具。但,在本專欄接下來的講解中,我都會以 kubeadm 為依據進行講述。

  • 一方面,作為 Kubernetes 項目的原生部署工具,kubeadm 對 Kubernetes 項目特性的使用和集成,確實要比其他項目“技高一籌”,非常值得我們學習和借鑒;
  • 另一方面,kubeadm 的部署方法,不會涉及到太多的運維工作,也不需要我們額外學習復雜的部署工具。而它部署的 Kubernetes 集群,跟一個完全使用二進制文件搭建起來的集群幾乎沒有任何區別。

因此,使用 kubeadm 去部署一個 Kubernetes 集群,對於你理解 Kubernetes 組件的工作方式和架構,最好不過了。

為了回答這個問題,在今天這篇文章,我就先和你介紹一下 kubeadm 的工作原理吧

二、kubeadm 的工作原理

1、為什么不用容器部署 Kubernetes 呢?

這樣,我只要給每個 Kubernetes 組件做一個容器鏡像,然后在每台宿主機上用 docker run 指令啟動這些組件容器,部署不就完成了嗎?

事實上,在 Kubernetes 早期的部署腳本里,確實有一個腳本就是用 Docker 部署 Kubernetes 項目的,這個腳本相比於 SaltStack 等的部署方式,也的確簡單了不少。

2、但是,這樣做會帶來一個很麻煩的問題,即:如何容器化 kubelet。

我在上一篇文章中,已經提到 kubelet 是 Kubernetes 項目用來操作 Docker 等容器運行時的核心組件。可是,除了跟容器運行時打交道外,kubelet 在配置容器網絡、管理容器數據卷時,都需要直接操作宿主機。

而如果現在 kubelet 本身就運行在一個容器里,那么直接操作宿主機就會變得很麻煩。對於網絡配置來說還好,kubelet 容器可以通過不開啟 Network Namespace(即 Docker 的 host network 模式)的方式,直接共享宿主機的網絡棧。可是,要讓 kubelet 隔着容器的 Mount Namespace 和文件系統,操作宿主機的文件系統,就有點兒困難了。

比如,如果用戶想要使用 NFS 做容器的持久化數據卷,那么 kubelet 就需要在容器進行綁定掛載前,在宿主機的指定目錄上,先掛載 NFS 的遠程目錄。

可是,這時候問題來了。由於現在 kubelet 是運行在容器里的,這就意味着它要做的這個“mount -F nfs”命令,被隔離在了一個單獨的 Mount Namespace 中。即,kubelet 做的掛載操作,不能被“傳播”到宿主機上。

對於這個問題,有人說,可以使用 setns() 系統調用,在宿主機的 Mount Namespace 中執行這些掛載操作;也有人說,應該讓 Docker 支持一個–mnt=host 的參數。

但是,到目前為止,在容器里運行 kubelet,依然沒有很好的解決辦法,我也不推薦你用容器去部署 Kubernetes 項目

3、正因為如此,kubeadm 選擇了一種妥協方案:

把 kubelet 直接運行在宿主機上,然后使用容器部署其他的 Kubernetes 組件。

所以,你使用 kubeadm 的第一步,是在機器上手動安裝 kubeadm、kubelet 和 kubectl 這三個二進制文件。當然,kubeadm 的作者已經為各個發行版的 Linux 准備好了安裝包,所以你只需要執行:

$ apt-get install kubeadm

就可以了。

接下來,你就可以使用“kubeadm init”部署 Master 節點了。

三、kubeadm init 的工作流程

當你執行 kubeadm init 指令后,kubeadm 首先要做的,是一系列的檢查工作,以確定這台機器可以用來部署 Kubernetes。這一步檢查,我們稱為“Preflight Checks”,它可以為你省掉很多后續的麻煩。

1、Preflight Checks

其實,Preflight Checks 包括了很多方面,比如:

  • Linux 內核的版本必須是否是 3.10 以上?
  • Linux Cgroups 模塊是否可用?
  • 機器的 hostname 是否標准?在 Kubernetes 項目里,機器的名字以及一切存儲在 Etcd 中的 API 對象,都必須使用標准的 DNS 命名(RFC 1123)。
  • 用戶安裝的 kubeadm 和 kubelet 的版本是否匹配?
  • 機器上是不是已經安裝了 Kubernetes 的二進制文件?
  • Kubernetes 的工作端口 10250/10251/10252 端口是不是已經被占用?
  • ip、mount 等 Linux 指令是否存在?
  • Docker 是否已經安裝?

2、生成 Kubernetes 對外提供服務所需的各種證書和對應的目錄。

Kubernetes 對外提供服務時,除非專門開啟“不安全模式”,否則都要通過 HTTPS 才能訪問 kube-apiserver。這就需要為 Kubernetes 集群配置好證書文件。

kubeadm 為 Kubernetes 項目生成的證書文件都放在 Master 節點的 /etc/kubernetes/pki 目錄下。在這個目錄下,最主要的證書文件是 ca.crt 和對應的私鑰 ca.key。

此外,用戶使用 kubectl 獲取容器日志等 streaming 操作時,需要通過 kube-apiserver 向 kubelet 發起請求,這個連接也必須是安全的。kubeadm 為這一步生成的是 apiserver-kubelet-client.crt 文件,對應的私鑰是 apiserver-kubelet-client.key。

除此之外,Kubernetes 集群中還有 Aggregate APIServer 等特性,也需要用到專門的證書,這里我就不再一一列舉了。需要指出的是,你可以選擇不讓 kubeadm 為你生成這些證書,而是拷貝現有的證書到如下證書的目錄里:

[root@master1 kubernetes]# pwd
/etc/kubernetes
[root@master1 kubernetes]# tree
.
├── kubelet.kubeconfig
├── kube-proxy.kubeconfig
└── ssl
    ├── admin.csr
    ├── admin-csr.json
    ├── admin-key.pem
    ├── admin.pem
    ├── aggregator-proxy.csr
    ├── aggregator-proxy-csr.json
    ├── aggregator-proxy-key.pem
    ├── aggregator-proxy.pem
    ├── ca-config.json
    ├── ca.csr
    ├── ca-csr.json
    ├── ca-key.pem
    ├── ca.pem
    ├── kubelet.csr
    ├── kubelet-csr.json
    ├── kubelet-key.pem
    ├── kubelet.pem
    ├── kube-proxy.csr
    ├── kube-proxy-csr.json
    ├── kube-proxy-key.pem
    ├── kube-proxy.pem
    ├── kubernetes.csr
    ├── kubernetes-csr.json
    ├── kubernetes-key.pem
    └── kubernetes.pem

1 directory, 27 files

這時,kubeadm 就會跳過證書生成的步驟,把它完全交給用戶處理。

3、kubeadm 接下來會為其他組件生成訪問 kube-apiserver 所需的配置文件

這些文件的路徑是:/etc/kubernetes/xxx.conf:

[root@master1 kubernetes]# ls
kubelet.kubeconfig  kube-proxy.kubeconfig  ssl

這些文件里面記錄的是,當前這個 Master 節點的服務器地址、監聽端口、證書目錄等信息。這樣,對應的客戶端(比如 scheduler,kubelet 等),可以直接加載相應的文件,使用里面的信息與 kube-apiserver 建立安全連接

4、接下來,kubeadm 會為 Master 組件生成 Pod 配置文件

接下來,kubeadm 會為 Master 組件生成 Pod 配置文件。我已經在上一篇文章中和你介紹過 Kubernetes 有三個 Master 組件 kube-apiserver、kube-controller-manager、kube-scheduler,而它們都會被使用 Pod 的方式部署起來。

你可能會有些疑問:這時,Kubernetes 集群尚不存在,難道 kubeadm 會直接執行 docker run 來啟動這些容器嗎?

當然不是。

在 Kubernetes 中,有一種特殊的容器啟動方法叫做“Static Pod”。它允許你把要部署的 Pod 的 YAML 文件放在一個指定的目錄里。這樣,當這台機器上的 kubelet 啟動時,它會自動檢查這個目錄,加載所有的 Pod YAML 文件,然后在這台機器上啟動它們。

從這一點也可以看出,kubelet 在 Kubernetes 項目中的地位非常高,在設計上它就是一個完全獨立的組件,而其他 Master 組件,則更像是輔助性的系統容器。

[root@master1 manifests]# pwd
/opt/kubeOperator-kube-bin/1.16.4/manifests
[root@master1 manifests]# ls
dashboard  ingress  kubeapps-plus  prometheus  storage  weave-scope
[root@master1 manifests]# cd dashboard/
[root@master1 dashboard]# ll
total 12
-rw-r--r--. 1 root root 5810 Feb 18 09:07 kubernetes-dashboard.yaml
-rw-r--r--. 1 root root 3383 Feb 18 09:07 metrics-server.yaml

而一旦這些 YAML 文件出現在被 kubelet 監視的 /etc/kubernetes/manifests 目錄下,kubelet 就會自動創建這些 YAML 文件中定義的 Pod,即 Master 組件的容器。

Master 容器啟動后,kubeadm 會通過檢查 localhost:6443/healthz 這個 Master 組件的健康檢查 URL,等待 Master 組件完全運行起來

5、然后,kubeadm 就會為集群生成一個 bootstrap token。

在后面,只要持有這個 token,任何一個安裝了 kubelet 和 kubadm 的節點,都可以通過 kubeadm join 加入到這個集群當中。

這個 token 的值和使用方法會,會在 kubeadm init 結束后被打印出來。

在 token 生成之后,kubeadm 會將 ca.crt 等 Master 節點的重要信息,通過 ConfigMap 的方式保存在 Etcd 當中,供后續部署 Node 節點使用。這個 ConfigMap 的名字是 cluster-info。

kubeadm init 的最后一步,就是安裝默認插件。Kubernetes 默認 kube-proxy 和 DNS 這兩個插件是必須安裝的。它們分別用來提供整個集群的服務發現和 DNS 功能。其實,這兩個插件也只是兩個容器鏡像而已,所以 kubeadm 只要用 Kubernetes 客戶端創建兩個 Pod 就可以了。

四、kubeadm join 的工作流程

這個流程其實非常簡單,kubeadm init 生成 bootstrap token 之后,你就可以在任意一台安裝了 kubelet 和 kubeadm 的機器上執行 kubeadm join 了。

1、可是,為什么執行 kubeadm join 需要這樣一個 token 呢?

因為,任何一台機器想要成為 Kubernetes 集群中的一個節點,就必須在集群的 kube-apiserver 上注冊。可是,要想跟 apiserver 打交道,這台機器就必須要獲取到相應的證書文件(CA 文件)。可是,為了能夠一鍵安裝,我們就不能讓用戶去 Master 節點上手動拷貝這些文件。

所以,kubeadm 至少需要發起一次“不安全模式”的訪問到 kube-apiserver,從而拿到保存在 ConfigMap 中的 cluster-info(它保存了 APIServer 的授權信息)。而 bootstrap token,扮演的就是這個過程中的安全驗證的角色。

只要有了 cluster-info 里的 kube-apiserver 的地址、端口、證書,kubelet 就可以以“安全模式”連接到 apiserver 上,這樣一個新的節點就部署完成了。

接下來,你只要在其他節點上重復這個指令就可以了。

五、配置 kubeadm 的部署參數

我在前面講解了 kubeadm 部署 Kubernetes 集群最關鍵的兩個步驟,kubeadm init 和 kubeadm join。相信你一定會有這樣的疑問:kubeadm 確實簡單易用,可是我又該如何定制我的集群組件參數呢?

比如,我要指定 kube-apiserver 的啟動參數,該怎么辦?

在這里,我強烈推薦你在使用 kubeadm init 部署 Master 節點時,使用下面這條指令:

$ kubeadm init --config kubeadm.yaml

這時,你就可以給 kubeadm 提供一個 YAML 文件(比如,kubeadm.yaml),它的內容如下所示(我僅列舉了主要部分):

apiVersion: kubeadm.k8s.io/v1alpha2
kind: MasterConfiguration
kubernetesVersion: v1.11.0
api:
  advertiseAddress: 192.168.0.102
  bindPort: 6443
  ...
etcd:
  local:
    dataDir: /var/lib/etcd
    image: ""
imageRepository: k8s.gcr.io
kubeProxy:
  config:
    bindAddress: 0.0.0.0
    ...
kubeletConfiguration:
  baseConfig:
    address: 0.0.0.0
    ...
networking:
  dnsDomain: cluster.local
  podSubnet: ""
  serviceSubnet: 10.96.0.0/12
nodeRegistration:
  criSocket: /var/run/dockershim.sock
  ...

通過制定這樣一個部署參數配置文件,你就可以很方便地在這個文件里填寫各種自定義的部署參數了。比如,我現在要指定 kube-apiserver 的參數,那么我只要在這個文件里加上這樣一段信息:

...
apiServerExtraArgs:
  advertise-address: 192.168.0.103
  anonymous-auth: false
  enable-admission-plugins: AlwaysPullImages,DefaultStorageClass
  audit-log-path: /home/johndoe/audit.log

然后,kubeadm 就會使用上面這些信息替換 /etc/kubernetes/manifests/kube-apiserver.yaml 里的 command 字段里的參數了。

而這個 YAML 文件提供的可配置項遠不止這些。比如,你還可以修改 kubelet 和 kube-proxy 的配置,修改 Kubernetes 使用的基礎鏡像的 URL(默認的k8s.gcr.io/xxx鏡像 URL 在國內訪問是有困難的),指定自己的證書文件,指定特殊的容器運行時等等。這些配置項,就留給你在后續實踐中探索了。

六、經典留言

1、Antergone

有一個ansible playbook可以推薦給大家。
https://github.com/gjmzj/kubeasz
初學者可以跟着一步步看原理,后期還可以自己定制化。主要是容易產生興趣。

2、MiracleWong

我也補充一個可用於部署生產級別的Kubernetes的開源項目:
https://github.com/kubernetes-incubator/kubespray 
我們公司正在使用。

3、yandd

推薦個k8s實驗平台
https://console.magicsandbox.com
可能需要fan qiang才能訪問

4、blackpiglet

1. Linux 下生成證書,主流的選擇應該是 OpenSSL,還可以使用 GnuGPG,或者 keybase。
2. Kubernetes 組件之間的交互方式:HTTP/HTTPS、gRPC、DNS、系統調用等。

5、alex

對牆經驗豐富的人來了,可以用下面這個鏡像

https://github.com/anjia0532/gcr.io_mirror


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM