一、k8s基本概念
k8s大部分概念比如Node,Pod、RC,service等都可以看做一種資源對象,幾乎所有的資源對象都可以通過k8s提供的kubectl工具執行增,刪,改,查等操作並將其保存在etcd中持久化存儲。
二、master
master指的是集群控制節點,來負責整個集群的管理和控制,基本上k8s的所有控制命令都是發給它。我們后面執行的命令基本都是在master節點上運行的。通常它會占據一個獨立的x86服務器(或一個虛擬機)。
master節點上運行一些關鍵進程:
- k8s API server(kube-apiserver):提供了HTTP Rest接口的關鍵服務進程,是所有資源的增刪改查的唯一入口,也是集群集群控制的入口進程。kubectl的命令會調用到api server,來實現資源的增刪查改。
- kube-controller-manager:k8s所有資源對象的自動化控制中心。
- kube-scheduler:pod調度進程。
其實往往還啟動了一個etcd server進程。因為k8s里所有資源對象的數據全部是保存在etcd中的。
三、node
k8s集群中其他機器被稱為node節點,Node可以是一台物理機,也可以是一台虛擬機。當某個node宕機,其上的工作負載會被master自動轉移到其他節點上。
Node運行着一些關鍵進程:
-
kubelet:負責pod對應的容器創建、啟停等任務。
-
kube-porxy:實現service通信的重要組件
-
docker engine:docker引擎,負責本機的容器創建和管理。
node節點可以在運行期間動態增加到k8s集群中,在默認情況下kubelet會將master注冊自己,並定時向master匯報自身情報。
可以執行下面命令查看集群中有多少個node:
kubectl get nodes
然后通過下面命令查看某個node的詳細信息:
kubectl describe node <node_name>
四、pod
pod的存在主要是讓幾個緊密連接的幾個容器之間共享資源,例如ip地址,共享存儲等信息。如果直接調度容器的話,那么幾個容器可能運行在不同的主機上,這樣就增加了系統的復雜性。
每個pod由一個根容器的pause容器,其他是業務容器。
k8s為每個pod分配了唯一的IP地址,一個pod里的多個容器共享pod IP。
pod其實有兩種類型:普通的pod和靜態pod,后者比較特殊,它並不存放在etcd存儲中,而是存放在某個具體的Node上的一個具體文件中,並且只在此Node上啟動運行。而普通的pod一旦被創建,就會被放入etcd中存儲。隨后被master調度到某個具體的Node上並進行綁定,隨后該pod被對應的Node上的kubelet進程實例化成一組相關的docker容器並啟動起來。
每個pod都可以對其使用的服務器上的計算資源設置限額,當前可以設置限額的源有CPU和memory兩種。其中CPU的資源單位為CPU的數量。
一般而言,一個CPU的配額已經算是相當大的一個資源配額,所以在k8s中,通常以千分之一的CPU配額為最小單位,以m來表示,通常一個容器的CPU配額為100-300m,即占用0.1-0.3個CPU。這個配額是個絕對值,不是占比。
在k8s中,一個計算資源進行配額限定需要設定兩個參數:
requests,資源的最小申請量,系統必須滿足要求
limits,資源最大允許使用的量。
五、label
一個label是一個key=value的鍵值組合,然后可以通過label selector(標簽選擇器)查詢和篩選擁有某些label的資源對象。
label selector的重要使用場景:
kube-controller進程通過資源對象RC上定義的label selector來篩選要監控的pod的數量,從而實現全自動控制流程。
kube-proxy進程通過service的label selector來選擇對應的pod,自動建立起每個service到對應pod的請求轉發路由表。從而實現service的智能負載均衡機制。
六、RC
RC主要是為pod進行一些設定,包括副本數,label selector等。總結如下
- 在大多數情況下,我們通過定義一個RC實現pod的創建過程及副本數量的自動控制。
- RC里包括完整的Pod定義模板。
- RC通過label selector機制實現對pod副本的自動控制
- 通過改變RC的pod副本數量,可以實現pod的擴容或縮容
- 通過改變RC中Pod模板的鏡像版本,可以實現Pod的滾動升級功能。
七、HPA(horizontal Pod Autoscaler)
通過手動執行kubectl scale命令,可以通過RC實現pod擴容。但並不滿足谷歌對k8s的定位模板-自動化。
HPA,pod橫向自動擴容,實現原理是通過追蹤分析RC控制的所有目標Pod的負載變化情況,來確定是否需要針對性地挑戰目標pod的副本數。
有兩種方式作為pod負載的度量指標。
-
CPU utilization percentage
-
應用程序自定義的度量指標,比如服務在每秒內的相應的請求數。
CPU utilization percentage是一個算術平均值,目標pod所有副本自身的CPU利用率的平均值。一個Pod自身的CPU利用率是該Pod當前CPU使用量除以它的Pod request的值。比如當我們定義一個Pod的pod request為0.4,而當前pod的cpu使用量為0.2,則使用率為50%。如此可以得出一個平均值,如果某一個時刻CPU utilization percentage超過80%,則表示當前副本數不夠,需要進行擴容。
CPU utilization percentage計算過程使用到的Pod的CPU使用量通常是1分鍾的平均值。
八、service
service就是一個微服務,如下圖所示:
RC的作用實際上是保證service的服務能力和服務質量始終處於預期的標准。
而通過建模系統中的所有服務為微服務-k8s service,最終我們的系統由多個提供不同業務能力而又彼此獨立的微服務單元所組成。服務之間通過TCP/IP通信,從而形成強大又靈活的彈性網絡,擁有強大的分布式能力,彈性拓展negligence,容錯能力等。如下圖所示:
每個pod會被分配一個獨立的IP地址,也就是每個pod都提供一個獨立的endpoint(IP+port)以被訪問,那多個pod如何被客戶端訪問呢,k8s通過運行在每個Node上的kube-proxy進程,負責將對service的請求轉發到后端某個pod實例上,也就實現了類似負載均衡器的功能,至於具體轉發到哪個pod,則由負載均衡器的算法所決定。
並且service不是共用一個負載均衡器的IP地址,而是每一個service分配了一個全局唯一的虛擬IP,cluster IP,這樣每個服務就變成了具有唯一IP的通信節點,服務調用也就變成了最為基礎的TCP通信問題。
我們知道,pod的endpoint的地址會隨着pod的銷毀和創建而改變。而service一旦創建,其cluster IP不會改變,服務發現問題得以解決,只要用service的name和其cluster IP做一個DNS即可。
k8s的服務發現機制
每個service都有一個唯一的cluster IP以及唯一的名字,而名字是由開發者自己定義的,部署的時候也沒必要改變,所以完全可以固定在配置中,接下來的問題就是如何通過service的名字找到對應的cluster IP。
最早的時候k8s采用了Linux環境變量的方式解決這個問題,即env,並在每個pod的容器啟動時,自動注入這些環境變量。
考慮到環境變量的方式獲取service的IP與端口的方式仍然不太方便,不夠直觀,后來k8s通過add-on增值包的方式引入了DNS系統。
外部系統訪問service的問題
k8s中有三種IP
a,Node IP:node節點的IP地址
b,Pod IP:pod的IP地址
c,cluster IP:service IP
首先,Node IP是k8s集群中每個節點的物理網卡的IP地址,這是一個真實存在的物理網絡,所有屬於這個網絡的服務器之間都能直接通信,不管屬不屬於k8s集群。這也表明了k8s集群之外的節點訪問k8s集群之內的某個節點后者TCP/IP服務的時候,必須要通過Node IP通信。
其次,pod IP是每個Pod的IP地址,它是docker根據docker網橋的IP地址段進行分配的,通常是一個虛擬的二層網絡,因此不同pod之間的通信就是通過Pod IP所在的虛擬二層網絡進行通信的。而真實的TCP/IP流量則是通過Node IP所在的物理網卡流出的。
cluster IP是一個虛擬的IP,無法被ping,因為沒有實體對象來響應。也就是說無法在集群外部直接使用這個地址,那么矛盾來了:
實際業務中肯定多少有一部分服務是要提供給k8s外部應用來使用的,典型的就是web端的服務模塊,比如tomcat-service,那么用戶怎么訪問它呢?
采用NodePort是最常見的做法,也就是新的端口。
九、Volume 存儲卷
Volume是pod中能夠被多個容器訪問的共同目錄。也就是被定義在pod上,然后被一個pod中的多個容器掛載到具體的文件目錄下,其次,volume與pod生命周期相同,但與容器生命周期不相關,當容器終止或重啟,volume中的數據也不會丟失。
十、namespace
大多數情況下用於實現多租戶的資源隔離,namespace通過將集群內部的資源對象分配到不同的namespace中,形成邏輯上分組的不同項目、小組,便於不同的分組在共享使用整個集群的資源的同時還能被分別管理。
namespace的定義很簡單,如下所示的yaml定義了名為development的namespace
apiVersion: v1
kind: Namespace
metadata:
name: development
一旦創建了Namespace,我們在創建資源對象時就可以指定這個資源對象屬於哪個namespace,比如下面,定義了名為busybox的Pod,放入development這個namespace里:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
當我們給每個租戶創建一個Namespace來實現多租戶的資源隔離時,還能結合k8s的資源配額管理,限定不同租戶能占用的資源,例如CPU,內存。