容器的核心在于“容器化”應用
比如,應用既可能是
- Java Web和MySQL
- Cassandra這樣的分布式系統
而要使用容器把後者運作起來,單通過Docker把一個Cassandra鏡像run是沒用的。
Cassandra應用容器化的關鍵,在于處理好這些Cassandra容器之間的編排關系。比如
- 哪些Cassandra容器是主,哪些從?
- 主從容器如何區分?
- 它們之間又如何進行自動發現和通信?
- Cassandra容器的持久化資料又如何保持
- …
這也是
Kubernetes項目的主要原因:展現出來的容器化“表達能力”,具有獨有的先進性和完備性。
使得它不僅能運作Java Web&MySQL這樣的正常組合,還能夠處理Cassandra容器叢集等複雜編排問題。
是以,對編排能力的剖析、解讀和最佳實踐,才是容器技術研究重點!
作為一個典型的分布式項目,Kubernetes的部署一直以來都是擋在初學者前面的一隻“攔路虎”。尤其是在Kubernetes項目釋出初期,它的部署完全要依靠一堆由社群維護的腳本。
其實,Kubernetes作為一個Go項目,已經免去很多類似Python項目要安裝語言級别依賴的麻煩。但是,除了将各個元件編譯成二進制檔案外,使用者還要負責為這些二進制檔案編寫對應的配置檔案、配置自啟動腳本,以及為kube-apiserver配置授權檔案等等諸多運維工作。
目前,各大雲廠商最常用的部署的方法,是使用SaltStack、Ansible等運維工具自動化地執行這些步驟。
但即使這樣,這個部署過程依然非常繁瑣。因為,SaltStack這類專業運維工具本身的學習成本,就可能比Kubernetes項目還要高。
難道Kubernetes項目就沒有簡單的部署方法了嗎!!!
這個問題,在Kubernetes社群裡一直沒有得到足夠重視。直到2017年,在志願者的推動下,社群才終于發起了一個獨立的部署工具,名叫:kubeadm

該項目就是要讓使用者能夠通過這樣兩條指令完成一個Kubernetes叢集的部署:
# 建立一個Master節點
$ kubeadm init
# 将一個Node節點加入到目前叢集中
$ kubeadm join <Master節點的IP和端口>
太友善啦!
可能你會有所顧忌:Kubernetes的功能那麼多,這樣一鍵部署出來的叢集,能用于生産環境?!
是以本文就是讓你信服,是的!可以!
kubeadm的工作原理
在部署時,Kubernetes的每一個元件都是一個需要被執行的、單獨的二進制檔案。
是以SaltStack這樣的運維工具或者由社群維護的腳本的功能,就是要把這些二進制檔案傳輸到指定的機器當中,然後編寫控制腳本來啟停這些元件。
不過,在了解了容器技術之後,你可能已經萌生出了這樣一個想法,為什麼不用容器部署Kubernetes呢?
這樣,隻要給每個Kubernetes元件做一個容器鏡像,然後在每台主控端上用docker run指令啟動這些元件容器,部署不就完成了嗎?
事實上,在Kubernetes早期的部署腳本裡,确實有一個腳本就是用Docker部署Kubernetes項目的,這個腳本相比于SaltStack等的部署方式,也的确簡單了不少。
但是,這樣做會帶來一個很麻煩的問題,即:如何容器化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項目。
正因為如此,kubeadm選擇了一種妥協方案:
把kubelet直接運作在主控端上,然後使用容器部署其他的Kubernetes元件。
是以,你使用kubeadm的第一步,是在機器上手動安裝kubeadm、kubelet和kubectl這三個二進制檔案。當然,kubeadm的作者已經為各個發行版的Linux準備好了安裝包,是以你隻需要執行:
$ apt-get install kubeadm
接下來,就可以使用“kubeadm init”部署Master節點
kubeadm init的工作流程
當你執行kubeadm init指令後,kubeadm首先要做的,是一系列的檢查工作,以确定這台機器可以用來部署Kubernetes
這一步檢查,稱為“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是否已經安裝?
- ……
在通過了Preflight Checks之後,kubeadm要為你做的,是生成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為你生成這些證書,而是拷貝現有的證書到如下證書的目錄裡:
/etc/kubernetes/pki/ca.{crt,key}
這時,kubeadm就會跳過證書生成的步驟,把它完全交給使用者處理。
證書生成後,kubeadm接下來會為其他元件生成通路kube-apiserver所需的配置檔案
這些檔案的路徑是:
/etc/kubernetes/xxx.conf
ls /etc/kubernetes/
admin.conf controller-manager.conf kubelet.conf scheduler.conf
這些檔案記錄目前Master節點的
- 伺服器位址
- 監聽端口
- 證書目錄等資訊
這樣,對應的用戶端(比如scheduler,kubelet等),可以直接加載相應的檔案,使用裡面的資訊與kube-apiserver建立安全連接配接。
接下來
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元件,則更像是輔助性的系統容器。
在kubeadm中,Master元件的YAML檔案會被生成在/etc/kubernetes/manifests路徑下
比如,kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ""
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC
- --runtime-config=api/all=true
- --advertise-address=10.168.0.2
...
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: k8s.gcr.io/kube-apiserver-amd64:v1.11.1
imagePullPolicy: IfNotPresent
livenessProbe:
...
name: kube-apiserver
resources:
requests:
cpu: 250m
volumeMounts:
- mountPath: /usr/share/ca-certificates
name: usr-share-ca-certificates
readOnly: true
...
hostNetwork: true
priorityClassName: system-cluster-critical
volumes:
- hostPath:
path: /etc/ca-certificates
type: DirectoryOrCreate
name: etc-ca-certificates
...
解析:
-
Pod裡隻定義了一個容器,它使用的鏡像是:k8s.gcr.io/kube-apiserver-amd64:v1.11.1
該鏡像是Kubernetes官方維護的一個元件鏡像。
- 這個容器的啟動指令(commands)是
即:容器裡kube-apiserver二進制檔案 + 指定的配置參數kube-apiserver --authorization-mode=Node,RBAC …
- 如果要修改一個已有叢集的
的配置,需要修改該YAMLkube-apiserver
-
kube-apiserver
在這一步完成後,kubeadm還會再生成一個
Etcd
的Pod YAML,用來通過同樣的Static Pod啟動Etcd
是以,最後Master元件的Pod YAML檔案如下所示:
$ ls /etc/kubernetes/manifests/
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
一旦這些YAML出現在被kubelet監視的
/etc/kubernetes/manifests
目錄
kubelet就會自動建立這些YAML中定義的Pod(Master元件的容器)
Master容器啟動後,kubeadm會通過檢查
localhost:6443/healthz
(Master元件的健康檢查URL),等待Master元件完全運作
然後
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
可為什麼執行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字段裡的參數了。
總結
kubeadm的設計非常簡潔。并且,它在實作每一步部署功能時,都在最大程度地重用Kubernetes已有的功能,這也就使得我們在使用kubeadm部署Kubernetes項目時,非常有“原生”的感覺,一點都不會感到突兀。
而kubeadm的源代碼,直接就在kubernetes/cmd/kubeadm目錄下,是Kubernetes項目的一部分。
其中,app/phases檔案夾下的代碼,對應的就是這篇文章中詳細介紹的每一個具體步驟。
kubeadm幾乎完全是一位高中生的作品。他叫Lucas Käldström,芬蘭人,今年隻有19歲。
kubeadm,是他17歲時用業餘時間完成的一個社群項目。
最後,kubeadm能夠用于生産環境嗎?
至今(2019年12月),:不能。
因為kubeadm目前最欠缺的是,一鍵部署一個高可用的Kubernetes叢集
即:Etcd、Master元件都應該是多節點叢集,而不是現在這樣的單點。
這也正是kubeadm接下來發展的主要方向。
另一方面,Lucas也正在積極地把kubeadm phases開放給使用者
即:使用者可以更加自由地定制kubeadm的每一個部署步驟。
這些舉措,都可以讓這個項目更加完善,對它的發展走向也充滿了信心。
如果有部署規模化生産環境的需求,推薦使用kops或者SaltStack這樣更複雜的部署工具
- 作為Kubernetes項目的原生部署工具,kubeadm對Kubernetes項目特性的使用和內建,确實要比其他項目“技高一籌”,值得參考
- kubeadm的部署方法,不會涉及到太多運維,也不需要我們額外學習複雜的部署工具。而它部署的Kubernetes叢集,跟一個完全使用二進制檔案搭建起來的叢集幾乎沒有任何差別。
是以,使用kubeadm去部署一個Kubernetes叢集,對于你了解Kubernetes元件的工作方式和架構,最好不過了。
參考
- 深入剖析Kubernetes