k8s篇-k8s集羣架構及組件詳解【史上最詳細】

O kubernetes簡介

  • k8s是什麼

k8s是一個可移植的、可擴展的開源平臺,用於管理容器化的工作負載和服務,可以促進聲明式配置和自動化。

  • k8s能做什麼

1)服務發現和負載均衡
Kubernetes 可以使用 DNS 名稱或自己的 IP 地址公開容器,如果到容器的流量很大,Kubernetes 可以負載均衡並分配網絡流量,從而使部署穩定。
2)存儲編排
Kubernetes 允許您自動掛載您選擇的存儲系統,例如本地存儲、公共雲提供商等。
3)自動部署和回滾
您可以使用 Kubernetes 描述已部署容器的所需狀態,它可以以受控的速率將實際狀態更改爲所需狀態。例如,您可以自動化 Kubernetes 來爲您的部署創建新容器,刪除現有容器並將它們的所有資源用於新容器。
4)自動二進制打包
Kubernetes 允許您指定每個容器所需 CPU 和內存(RAM)。當容器指定了資源請求時,Kubernetes 可以做出更好的決策來管理容器的資源。
5)自我修復
Kubernetes 重新啓動失敗的容器、替換容器、殺死不響應用戶定義的運行狀況檢查的容器,並且在準備好服務之前不將其通告給客戶端。
6)密鑰與配置管理
Kubernetes 允許您存儲和管理敏感信息,例如密碼、OAuth 令牌和 ssh 密鑰。您可以在不重建容器鏡像的情況下部署和更新密鑰和應用程序配置,也無需在堆棧配置中暴露密鑰。

  • k8s不是傳統的PaaS系統,由於 Kubernetes 在容器級別而不是在硬件級別運行,因此它提供了 PaaS 產品共有的一些普遍適用的功能,例如部署、擴展、負載均衡、日誌記錄和監視。但是,Kubernetes 不是單一的,默認解決方案是可選和可插拔的。Kubernetes 提供了構建開發人員平臺的基礎,但是在重要的地方保留了用戶的選擇和靈活性。

1)Kubernetes 不限制支持的應用程序類型。Kubernetes 旨在支持極其多種多樣的工作負載,包括無狀態、有狀態和數據處理工作負載。如果應用程序可以在容器中運行,那麼它應該可以在 Kubernetes 上很好地運行。
2)Kubernetes 不部署源代碼,也不構建您的應用程序。持續集成(CI)、交付和部署(CI/CD)工作流取決於組織的文化和偏好以及技術要求。
3)Kubernetes 不提供應用程序級別的服務作爲內置服務,例如中間件(例如,消息中間件)、數據處理框架(例如,Spark)、數據庫(例如,mysql)、緩存、集羣存儲系統(例如,Ceph)。這樣的組件可以在 Kubernetes 上運行,並且/或者可以由運行在 Kubernetes 上的應用程序通過可移植機制(例如,開放服務代理)來訪問。
4)Kubernetes 不指定日誌記錄、監視或警報解決方案。它提供了一些集成作爲概念證明,並提供了收集和導出指標的機制。
5)Kubernetes 不提供或不要求配置語言/系統(例如 jsonnet),它提供了聲明性 API,該聲明性 API 可以由任意形式的聲明性規範所構成。
6)Kubernetes 不提供也不採用任何全面的機器配置、維護、管理或自我修復系統。
7)此外,Kubernetes 不僅僅是一個編排系統,實際上它消除了編排的需要。編排的技術定義是執行已定義的工作流程:首先執行 A,然後執行 B,再執行 C。相比之下,Kubernetes 包含一組獨立的、可組合的控制過程,這些過程連續地將當前狀態驅動到所提供的所需狀態。從 A 到 C 的方式無關緊要,也不需要集中控制,這使得系統更易於使用且功能更強大、健壯、彈性和可擴展性。

  • Kubernetes 主要由以下幾個核心組件組成:

etcd 保存了整個集羣的狀態;
kube-apiserver 提供了資源操作的唯一入口,並提供認證、授權、訪問控制、API 註冊和發現等機制;
kube-controller-manager 負責維護集羣的狀態,比如故障檢測、自動擴展、滾動更新等;
kube-scheduler 負責資源的調度,按照預定的調度策略將 Pod 調度到相應的機器上;
kubelet 負責維持容器的生命週期,同時也負責 Volume(CVI)和網絡(CNI)的管理;
Container runtime 負責鏡像管理以及 Pod 和容器的真正運行(CRI),默認的容器運行時爲 Docker;
kube-proxy 負責爲 Service 提供 cluster 內部的服務發現和負載均衡;

1 集羣架構圖

一個簡單架構圖,不含dashboard,只有一個master和兩個node
在這裏插入圖片描述
官網配圖:
在這裏插入圖片描述

  • 除了核心組件,還有一些推薦的 Add-ons:

kube-dns 負責爲整個集羣提供 DNS 服務
Ingress Controller 爲服務提供外網入口
Heapster 提供資源監控
Dashboard 提供 GUI
Federation 提供跨可用區的集羣
Fluentd-elasticsearch 提供集羣日誌採集、存儲與查詢

2 組件簡介

2.1 master組件

2.1.1 kube-apiserver

  • 功能

1)提供了集羣管理的REST API接口(包括認證授權、數據校驗以及集羣狀態變更);
2)提供其他模塊之間的數據交互和通信的樞紐(其他模塊通過API Server查詢或修改數據,只有API Server才直接操作etcd);
3)是資源配額控制的入口;

  • 工作原理
    在這裏插入圖片描述
  • 如何訪問apiserver

k8s通過kube-apiserver這個進程提供服務,該進程運行在單個k8s-master節點上。默認有兩個端口。

  • 1)本地端口

1)該端口用於接收HTTP請求;
2)該端口默認值爲8080,可以通過API Server的啓動參數“–insecure-port”的值來修改默認值;
3)默認的IP地址爲“localhost”,可以通過啓動參數“–insecure-bind-address”的值來修改該IP地址;
4)非認證或授權的HTTP請求通過該端口訪問API Server。

  • 2)安全端口

1)該端口默認值爲6443,可通過啓動參數“–secure-port”的值來修改默認值;
2)默認IP地址爲非本地(Non-Localhost)網絡端口,通過啓動參數“–bind-address”設置該值;
3)該端口用於接收HTTPS請求;
4)用於基於Tocken文件或客戶端證書及HTTP Base的認證;
5)用於基於策略的授權;
6)默認不啓動HTTPS安全訪問控制。

  • 訪問apiserver的方式
  • 1)curl

curl localhost:8080/api
curl localhost:8080/api/v1/pods
curl localhost:8080/api/v1/services
curl localhost:8080/api/v1/replicationcontrollers

  • 2)kubectl proxy

Kubectl Proxy代理程序既能作爲API Server的反向代理,也能作爲普通客戶端訪問API Server的代理。通過master節點的8080端口來啓動該代理程序。

  • 3)kubectl客戶端

命令行工具kubectl客戶端,通過命令行參數轉換爲對API Server的REST API調用,並將調用結果輸出。

  • 4)編程方式調用

  • 通過apiserver訪問node,pod,service

k8s API Server最主要的REST接口是資源對象的增刪改查。
另外還有一類特殊的REST接口—k8s Proxy API接口,這類接口的作用是代理REST請求,即kubernetes API Server把收到的REST請求轉發到某個Node上的kubelet守護進程的REST端口上,由該kubelet進程負責響應。

  • 集羣功能模塊之間的通信

kubernetes API Server作爲集羣的核心,負責集羣各功能模塊之間的通信,集羣內各個功能模塊通過API Server將信息存入etcd,當需要獲取和操作這些數據時,通過API Server提供的REST接口(GET\LIST\WATCH方法)來實現,從而實現各模塊之間的信息交互。

  • apiserver參數介紹
  • 1)kubelet與apiserver交互

每個Node節點上的kubelet定期就會調用API Server的REST接口報告自身狀態,API Server接收這些信息後,將節點狀態信息更新到etcd中。kubelet也通過API Server的Watch接口監聽Pod信息,從而對Node機器上的POD進行管理。

監聽信息 kubelet動作
新的pod副本被調度綁定到本節點 執行pod對性的容器的創建和啓動邏輯
pod對象被刪除 刪除本節點上相應的pod容器
修改pod信息 修改本節點的pod容器
  • 2)kube-controller-manager與apiserver交互

kube-controller-manager中的Node Controller模塊通過API Server提供的Watch接口,實時監控Node的信息,並做相應處理。

  • 3)kube-scheduler與apiserver交互

Scheduler通過API Server的Watch接口監聽到新建Pod副本的信息後,它會檢索所有符合該Pod要求的Node列表,開始執行Pod調度邏輯。調度成功後將Pod綁定到目標節點上。

  • apiserver參數介紹

API Server 主要是和 etcd 打交道,並且對外提供 HTTP 服務,以及進行安全控制,因此它的命令行提供的參數也主要和這幾個方面有關。下面是一些比較重要的參數以及說明(不同版本參數可能會有不同):

參數 含義 默認值
–advertise-address 通過該 ip 地址向集羣其他節點公佈 api server 的信息,必須能夠被其他節點訪問 nil
–allow-privileged 是否允許 privileged 容器運行 false
–admission-control 准入控制 AlwaysAdmit
–authorization-mode 授權模式 ,安全接口上的授權 AlwaysAllow
–bind-address HTTPS 安全接口的監聽地址 0.0.0.0
–secure-port HTTPS 安全接口的監聽端口 6443
–cert-dir TLS 證書的存放目錄 /var/run/kubernetes
–etcd-prefix 信息存放在 etcd 中地址的前綴 “/registry”
–etcd-servers 逗號分割的 etcd server 地址 []
–insecure-bind-address HTTP 訪問的地址 127.0.0.1
–insecure-port HTTP 訪問的端口 8080
–log-dir 日誌存放的目錄
–service-cluster-ip-range service 要使用的網段,使用 CIDR 格式,參考 kubernetes 中 service 的定義

2.1.2 kube-scheduler

kube-scheduler 負責分配調度 Pod 到集羣內的節點上,它監聽 kube-apiserver,查詢還未分配 Node 的 Pod,然後根據調度策略爲這些 Pod 分配節點(更新 Pod 的 NodeName 字段)。
https://feisky.gitbooks.io/kubernetes/components/scheduler.html

  • 調度器需要充分考慮諸多的因素:
    在這裏插入圖片描述
  • 指定node節點調度

有三種方式可以指定 Pod 只運行在指定的 Node 節點上
1)nodeSelector:只調度到匹配指定 label 的 Node 上
2)nodeAffinity:功能更豐富的 Node 選擇器,比如支持集合操作
3)podAffinity:調度到滿足條件的 Pod 所在的 Node 上

  • taints和tolerations

Taints 和 tolerations 用於保證 Pod 不被調度到不合適的 Node 上,其中 Taint 應用於 Node 上,而 toleration 則應用於 Pod 上。
目前支持的 taint 類型:
1)NoSchedule:新的 Pod 不調度到該 Node 上,不影響正在運行的 Pod
2)PreferNoSchedule:soft 版的 NoSchedule,儘量不調度到該 Node 上
3)NoExecute:新的 Pod 不調度到該 Node 上,並且刪除(evict)已在運行的 Pod。Pod 可以增加一個時間(tolerationSeconds)

  • 優先級調度

從 v1.8 開始,kube-scheduler 支持定義 Pod 的優先級,從而保證高優先級的 Pod 優先調度。並從 v1.11 開始默認開啓。

  • 多調度器

如果默認的調度器不滿足要求,還可以部署自定義的調度器。並且,在整個集羣中還可以同時運行多個調度器實例,通過 podSpec.schedulerName 來選擇使用哪一個調度器(默認使用內置的調度器)。

  • 調度器擴展
    kube-scheduler 還支持使用 --policy-config-file 指定一個調度策略文件來自定義調度策略
  • 其他影響調度的因素
    在這裏插入圖片描述
  • scheduler工作原理

kube-scheduler 調度分爲兩個階段,predicate 和 priority
1)predicate:過濾不符合條件的節點
2)priority:優先級排序,選擇優先級最高的節點

2.1.3 kube-controller-manager

  • Controller Manager 由 kube-controller-manager 和 cloud-controller-manager 組成,是 Kubernetes 的大腦,它通過 apiserver 監控整個集羣的狀態,並確保集羣處於預期的工作狀態。
    在這裏插入圖片描述
  • kube-controller-manager 由一系列的控制器組成,這些控制器可以劃分爲三組:

1)必須啓動的控制器:18個
EndpointController
ReplicationController
PodGCController
ResourceQuotaController
NamespaceController
ServiceAccountController
GarbageCollectorController
DaemonSetController
JobController
DeploymentController
ReplicaSetController
HPAController
DisruptionController
StatefulSetController
CronJobController
CSRSigningController
CSRApprovingController
TTLController

2)默認啓動可選的控制器,可以通過選項設置是否開啓
TokenController
NodeController
ServiceController
RouteController
PVBinderController
AttachDetachController

3)默認禁止的可選控制器,可通過選項設置是否開啓
BootstrapSignerController
TokenCleanerController

  • cloud-controller-manager 在 Kubernetes 啓用 Cloud Provider 的時候才需要,用來配合雲服務提供商的控制,也包括一系列的控制器,如

Node Controller
Route Controller
Service Controller

  • Metrics

Controller manager metrics 提供了控制器內部邏輯的性能度量,如 Go 語言運行時度量、etcd 請求延時、雲服務商 API 請求延時、雲存儲請求延時等。
Controller manager metrics 默認監聽在 kube-controller-manager 的 10252 端口,提供 Prometheus 格式的性能度量數據,可以通過 http://localhost:10252/metrics 來訪問。

  • 高可用和高性能

在啓動時設置 --leader-elect=true 後,controller manager 會使用多節點選主的方式選擇主節點。只有主節點纔會調用 StartControllers() 啓動所有控制器,而其他從節點則僅執行選主算法。

從 Kubernetes 1.7 開始,所有需要監控資源變化情況的調用均推薦使用 Informer。Informer 提供了基於事件通知的只讀緩存機制,可以註冊資源變化的回調函數,並可以極大減少 API 的調用。

  • node驅逐

Node 控制器在節點異常後,會按照默認的速率(–node-eviction-rate=0.1,即每10秒一個節點的速率)進行 Node 的驅逐。Node 控制器按照 Zone 將節點劃分爲不同的組,再跟進 Zone 的狀態進行速率調整:
1)Normal:所有節點都 Ready,默認速率驅逐。
2)PartialDisruption:即超過33% 的節點 NotReady 的狀態。當異常節點比例大於 --unhealthy-zone-threshold=0.55 時開始減慢速率:
小集羣(即節點數量小於 --large-cluster-size-threshold=50):停止驅逐
大集羣,減慢速率爲 --secondary-node-eviction-rate=0.01
3)FullDisruption:所有節點都 NotReady,返回使用默認速率驅逐。但當所有 Zone 都處在 FullDisruption 時,停止驅逐。

2.1.4 etcd

Etcd 是 CoreOS 基於 Raft 開發的分佈式 key-value 存儲,可用於服務發現、共享配置以及一致性保障(如數據庫選主、分佈式鎖等)。etcd作爲一個受到ZooKeeper與doozer啓發而催生的項目,除了擁有與之類似的功能外,更專注於以下四點。
1)簡單:基於HTTP+JSON的API讓你用curl就可以輕鬆使用。
2)安全:可選SSL客戶認證機制。
3)快速:每個實例每秒支持一千次寫操作。
4)可信:使用Raft算法充分實現了分佈式。

  • etcd主要功能

1)基本的 key-value 存儲
2)監聽機制
3)key 的過期及續約機制,用於監控和服務發現
4)原子性操作(CAS 和 CAD),用於分佈式鎖和 leader 選舉
tips:
CAS的原理:CAS操作需要輸入兩個數值,一箇舊值(期望操作之前的值)和一個新值,在操作期間先比較舊值有沒有變化,如果沒有發生變化,才交換新值,發生了變化則不交換

  • etcd的RAFT(replication and fault-tolerance)一致性算法

RAFT算法把一致性問題分解成了幾個子問題:選舉方法,日誌複製,安全性和失效處理

  • 1)選舉算法
  1. 初始啓動時,節點處於 follower 狀態並被設定一個 election timeout,如果在這一時間週期內沒有收到來自 leader 的 heartbeat,節點將發起選舉:將自己切換爲 candidate 之後,向集羣中其它 follower 節點發送請求,詢問其是否選舉自己成爲 leader。
  2. 當收到來自集羣中過半數節點的接受投票後,節點即成爲 leader,開始接收保存 client 的數據並向其它的 follower 節點同步日誌。如果沒有達成一致,則 candidate 隨機選擇一個等待間隔(150ms ~ 300ms)再次發起投票,得到集羣中半數以上 follower 接受的 candidate 將成爲 leader
  3. leader 節點依靠定時向 follower 發送 heartbeat 來保持其地位。
  4. 任何時候如果其它 follower 在 election timeout 期間都沒有收到來自 leader 的 heartbeat,同樣會將自己的狀態切換爲 candidate 併發起選舉。每成功選舉一次,新 leader 的任期(Term)都會比之前 leader 的任期大 1。
  • 2)日誌複製

當前 Leader 收到客戶端的日誌(事務請求)後先把該日誌追加到本地的 Log 中,然後通過 heartbeat 把該 Entry 同步給其他 Follower,Follower 接收到日誌後記錄日誌然後向 Leader 發送 ACK,當 Leader 收到大多數(n/2+1)Follower 的 ACK 信息後將該日誌設置爲已提交併追加到本地磁盤中,通知客戶端並在下個 heartbeat 中 Leader 將通知所有的 Follower 將該日誌存儲在自己的本地磁盤中。

  • 3)安全性

安全性是用於保證每個節點都執行相同序列的安全機制,如當某個 Follower 在當前 Leader commit Log 時變得不可用了,稍後可能該 Follower 又會被選舉爲 Leader,這時新 Leader 可能會用新的 Log 覆蓋先前已 committed 的 Log,這就是導致節點執行不同序列;Safety 就是用於保證選舉出來的 Leader 一定包含先前 committed Log 的機制;

  1. 選舉安全性(Election Safety):每個任期(Term)只能選舉出一個 Leader
  2. Leader 完整性(Leader Completeness):指 Leader 日誌的完整性,當 Log 在任期 Term1 被 Commit 後,那麼以後任期 Term2、Term3… 等的 Leader 必須包含該 Log;Raft 在選舉階段就使用 Term 的判斷用於保證完整性:當請求投票的該 Candidate 的 Term 較大或 Term 相同 Index 更大則投票,否則拒絕該請求。
  • 4)失效處理
  1. Leader 失效:其他沒有收到 heartbeat 的節點會發起新的選舉,而當 Leader 恢復後由於步進數小會自動成爲 follower(日誌也會被新 leader 的日誌覆蓋)
  2. follower 節點不可用:follower 節點不可用的情況相對容易解決。因爲集羣中的日誌內容始終是從 leader 節點同步的,只要這一節點再次加入集羣時重新從 leader 節點處複製日誌即可。
  3. 多個 candidate:衝突後 candidate 將隨機選擇一個等待間隔(150ms ~ 300ms)再次發起投票,得到集羣中半數以上 follower 接受的 candidate 將成爲 leader
  • etcd v2與v3

Etcd v2 和 v3 本質上是共享同一套 raft 協議代碼的兩個獨立的應用,接口不一樣,存儲不一樣,數據互相隔離。也就是說如果從 Etcd v2 升級到 Etcd v3,原來 v2 的數據還是隻能用 v2 的接口訪問,v3 的接口創建的數據也只能訪問通過 v3 的接口訪問。所以我們按照 v2 和 v3 分別分析。
推薦在 Kubernetes 集羣中使用 Etcd v3,v2 版本已在 Kubernetes v1.11 中棄用。

  • Etcd v2 存儲,Watch 以及過期機制
    在這裏插入圖片描述

Etcd v2 是個純內存的實現,並未實時將數據寫入到磁盤,持久化機制很簡單,就是將 store 整合序列化成 json 寫入文件。數據在內存中是一個簡單的樹結構。比如以下數據存儲到 Etcd 中的結構就如圖所示。
這裏有幾個影響使用的細節問題:

  1. EventHistroy 是有長度限制的,最長 1000。也就是說,如果你的客戶端停了許久,然後重新 watch 的時候,可能和該 waitIndex 相關的 event 已經被淘汰了,這種情況下會丟失變更。
  2. 如果通知 watcher 的時候,出現了阻塞(每個 watcher 的 channel 有 100 個緩衝空間),Etcd 會直接把 watcher 刪除,也就是會導致 wait 請求的連接中斷,客戶端需要重新連接。
  3. Etcd store 的每個 node 中都保存了過期時間,通過定時機制進行清理。

從而可以看出,Etcd v2 的一些限制:

  1. 過期時間只能設置到每個 key 上,如果多個 key 要保證生命週期一致則比較困難。
  2. watcher 只能 watch 某一個 key 以及其子節點(通過參數 recursive),不能進行多個 watch。
  3. 很難通過 watch 機制來實現完整的數據同步(有丟失變更的風險),所以當前的大多數使用方式是通過 watch 得知變更,然後通過 get 重新獲取數據,並不完全依賴於 watch 的變更 event。
  • Etcd v3 存儲,Watch以及過期機制
    在這裏插入圖片描述

Etcd v3 將 watch 和 store 拆開實現:

Etcd v3 store 分爲兩部分,一部分是內存中的索引,kvindex,是基於 google 開源的一個 golang 的 btree 實現的,另外一部分是後端存儲。按照它的設計,backend 可以對接多種存儲,當前使用的 boltdb。boltdb 是一個單機的支持事務的 kv 存儲,Etcd 的事務是基於 boltdb 的事務實現的。Etcd 在 boltdb 中存儲的 key 是 revision,value 是 Etcd 自己的 key-value 組合,也就是說 Etcd 會在 boltdb 中把每個版本都保存下,從而實現了多版本機制。

revision 主要由兩部分組成,第一部分 main rev,每次事務進行加一,第二部分 sub rev,同一個事務中的每次操作加一。如上示例,第一次操作的 main rev 是 3,第二次是 4。當然這種機制大家想到的第一個問題就是空間問題,所以 Etcd 提供了命令和設置選項來控制 compact,同時支持 put 操作的參數來精確控制某個 key 的歷史版本數。

瞭解了 Etcd 的磁盤存儲,可以看出如果要從 boltdb 中查詢數據,必須通過 revision,但客戶端都是通過 key 來查詢 value,所以 Etcd 的內存 kvindex 保存的就是 key 和 revision 之前的映射關係,用來加速查詢。

然後我們再分析下 watch 機制的實現。Etcd v3 的 watch 機制支持 watch 某個固定的 key,也支持 watch 一個範圍(可以用於模擬目錄的結構的 watch),所以 watchGroup 包含兩種 watcher,一種是 key watchers,數據結構是每個 key 對應一組 watcher,另外一種是 range watchers, 數據結構是一個 IntervalTree(不熟悉的參看文文末鏈接),方便通過區間查找到對應的 watcher。

同時,每個 WatchableStore 包含兩種 watcherGroup,一種是 synced,一種是 unsynced,前者表示該 group 的 watcher 數據都已經同步完畢,在等待新的變更,後者表示該 group 的 watcher 數據同步落後於當前最新變更,還在追趕。

當 Etcd 收到客戶端的 watch 請求,如果請求攜帶了 revision 參數,則比較請求的 revision 和 store 當前的 revision,如果大於當前 revision,則放入 synced 組中,否則放入 unsynced 組。同時 Etcd 會啓動一個後臺的 goroutine 持續同步 unsynced 的 watcher,然後將其遷移到 synced 組。也就是這種機制下,Etcd v3 支持從任意版本開始 watch,沒有 v2 的 1000 條歷史 event 表限制的問題(當然這是指沒有 compact 的情況下)。

另外我們前面提到的,Etcd v2 在通知客戶端時,如果網絡不好或者客戶端讀取比較慢,發生了阻塞,則會直接關閉當前連接,客戶端需要重新發起請求。Etcd v3 爲了解決這個問題,專門維護了一個推送時阻塞的 watcher 隊列,在另外的 goroutine 裏進行重試。

Etcd v3 對過期機制也做了改進,過期時間設置在 lease 上,然後 key 和 lease 關聯。這樣可以實現多個 key 關聯同一個 lease id,方便設置統一的過期時間,以及實現批量續約。

  • 相比 Etcd v2, Etcd v3 的一些主要變化
  1. 接口通過 grpc 提供 rpc 接口,放棄了 v2 的 http 接口。優勢是長連接效率提升明顯,缺點是使用不如以前方便,尤其對不方便維護長連接的場景。
  2. 廢棄了原來的目錄結構,變成了純粹的 kv,用戶可以通過前綴匹配模式模擬目錄。
  3. 內存中不再保存 value,同樣的內存可以支持存儲更多的 key。
  4. watch 機制更穩定,基本上可以通過 watch 機制實現數據的完全同步。
  5. 提供了批量操作以及事務機制,用戶可以通過批量事務請求來實現 Etcd v2 的 CAS 機制(批量事務支持 if 條件判斷)。
  • Etcd,Zookeeper,Consul 比較

Etcd 和 Zookeeper 提供的能力非常相似,都是通用的一致性元信息存儲,都提供 watch 機制用於變更通知和分發,也都被分佈式系統用來作爲共享信息存儲,在軟件生態中所處的位置也幾乎是一樣的,可以互相替代的。二者除了實現細節,語言,一致性協議上的區別,最大的區別在周邊生態圈。Zookeeper 是 apache 下的,用 java 寫的,提供 rpc 接口,最早從 hadoop 項目中孵化出來,在分佈式系統中得到廣泛使用(hadoop, solr, kafka, mesos 等)。Etcd 是 coreos 公司旗下的開源產品,比較新,以其簡單好用的 rest 接口以及活躍的社區俘獲了一批用戶,在新的一些集羣中得到使用(比如 kubernetes)。雖然 v3 爲了性能也改成二進制 rpc 接口了,但其易用性上比 Zookeeper 還是好一些。

而 Consul 的目標則更爲具體一些,Etcd 和 Zookeeper 提供的是分佈式一致性存儲能力,具體的業務場景需要用戶自己實現,比如服務發現,比如配置變更。而 Consul 則以服務發現和配置變更爲主要目標,同時附帶了 kv 存儲。

  • Etcd 的周邊工具
  • Confd

在分佈式系統中,理想情況下是應用程序直接和 Etcd 這樣的服務發現 / 配置中心交互,通過監聽 Etcd 進行服務發現以及配置變更。但我們還有許多歷史遺留的程序,服務發現以及配置大多都是通過變更配置文件進行的。Etcd 自己的定位是通用的 kv 存儲,所以並沒有像 Consul 那樣提供實現配置變更的機制和工具,而 Confd 就是用來實現這個目標的工具。

Confd 通過 watch 機制監聽 Etcd 的變更,然後將數據同步到自己的一個本地存儲。用戶可以通過配置定義自己關注哪些 key 的變更,同時提供一個配置文件模板。Confd 一旦發現數據變更就使用最新數據渲染模板生成配置文件,如果新舊配置文件有變化,則進行替換,同時觸發用戶提供的 reload 腳本,讓應用程序重新加載配置。

Confd 相當於實現了部分 Consul 的 agent 以及 consul-template 的功能,作者是 kubernetes 的 Kelsey Hightower,但大神貌似很忙,沒太多時間關注這個項目了,很久沒有發佈版本,我們着急用,所以 fork 了一份自己更新維護,主要增加了一些新的模板函數以及對 metad 後端的支持。confd

  • Metad

服務註冊的實現模式一般分爲兩種,一種是調度系統代爲註冊,一種是應用程序自己註冊。調度系統代爲註冊的情況下,應用程序啓動後需要有一種機制讓應用程序知道『我是誰』,然後發現自己所在的集羣以及自己的配置。Metad 提供這樣一種機制,客戶端請求 Metad 的一個固定的接口 /self,由 Metad 告知應用程序其所屬的元信息,簡化了客戶端的服務發現和配置變更邏輯。

Metad 通過保存一個 ip 到元信息路徑的映射關係來做到這一點,當前後端支持 Etcd v3,提供簡單好用的 http rest 接口。 它會把 Etcd 的數據通過 watch 機制同步到本地內存中,相當於 Etcd 的一個代理。所以也可以把它當做 Etcd 的代理來使用,適用於不方便使用 Etcd v3 的 rpc 接口或者想降低 Etcd 壓力的場景。

  • Etcd 使用注意事項
  • 1)Etcd cluster 初始化的問題

如果集羣第一次初始化啓動的時候,有一臺節點未啓動,通過 v3 的接口訪問的時候,會報告 Error: Etcdserver: not capable 錯誤。這是爲兼容性考慮,集羣啓動時默認的 API 版本是 2.3,只有當集羣中的所有節點都加入了,確認所有節點都支持 v3 接口時,才提升集羣版本到 v3。這個只有第一次初始化集羣的時候會遇到,如果集羣已經初始化完畢,再掛掉節點,或者集羣關閉重啓(關閉重啓的時候會從持久化數據中加載集羣 API 版本),都不會有影響。

  • 2)Etcd 讀請求的機制

v2 quorum=true 的時候,讀取是通過 raft 進行的,通過 cli 請求,該參數默認爲 true。
v3 --consistency=“l” 的時候(默認)通過 raft 讀取,否則讀取本地數據。sdk 代碼裏則是通過是否打開:WithSerializable option 來控制。
一致性讀取的情況下,每次讀取也需要走一次 raft 協議,能保證一致性,但性能有損失,如果出現網絡分區,集羣的少數節點是不能提供一致性讀取的。但如果不設置該參數,則是直接從本地的 store 裏讀取,這樣就損失了一致性。使用的時候需要注意根據應用場景設置這個參數,在一致性和可用性之間進行取捨。

  • 3)Etcd 的 compact 機制

Etcd 默認不會自動 compact,需要設置啓動參數,或者通過命令進行 compact,如果變更頻繁建議設置,否則會導致空間和內存的浪費以及錯誤。Etcd v3 的默認的 backend quota 2GB,如果不 compact,boltdb 文件大小超過這個限制後,就會報錯:”Error: etcdserver: mvcc: database space exceeded”,導致數據無法寫入。

2.2 node組件

2.2.1 kubelet

每個節點上都運行一個 kubelet 服務進程,默認監聽 10250 端口,接收並執行 master 發來的指令,管理 Pod 及 Pod 中的容器。每個 kubelet 進程會在 API Server 上註冊節點自身信息,定期向 master 節點彙報節點的資源使用情況,並通過 cAdvisor 監控節點和容器的資源。

  • 節點管理

節點管理主要是節點自注冊和節點狀態更新:

  1. Kubelet 可以通過設置啓動參數 --register-node 來確定是否向 API Server 註冊自己;
  2. 如果 Kubelet 沒有選擇自注冊模式,則需要用戶自己配置 Node 資源信息,同時需要告知 Kubelet 集羣上的 API Server 的位置;
  3. Kubelet 在啓動時通過 API Server 註冊節點信息,並定時向 API Server 發送節點新消息,API Server 在接收到新消息後,將信息寫入 etcd
  • pod管理
  • 1)獲取pod清單

Kubelet 以 PodSpec 的方式工作。PodSpec 是描述一個 Pod 的 YAML 或 JSON 對象。 kubelet 採用一組通過各種機制提供的 PodSpecs(主要通過 apiserver),並確保這些 PodSpecs 中描述的 Pod 正常健康運行。

向 Kubelet 提供節點上需要運行的 Pod 清單的方法:

  1. 文件:啓動參數 --config 指定的配置目錄下的文件 (默認 / etc/kubernetes/manifests/)。該文件每 20 秒重新檢查一次(可配置)。
  2. HTTP endpoint (URL):啓動參數 --manifest-url 設置。每 20 秒檢查一次這個端點(可配置)。
  3. API Server:通過 API Server 監聽 etcd 目錄,同步 Pod 清單。
  4. HTTP server:kubelet 偵聽 HTTP 請求,並響應簡單的 API 以提交新的 Pod 清單。
  • 2)通過apiserver獲取pod清單及創建pod的過程

Kubelet 通過 API Server Client(Kubelet 啓動時創建)使用 Watch 加 List 的方式監聽 “/registry/nodes/$ 當前節點名” 和 “/registry/pods” 目錄,將獲取的信息同步到本地緩存中。

Kubelet 監聽 etcd,所有針對 Pod 的操作都將會被 Kubelet 監聽到。如果發現有新的綁定到本節點的 Pod,則按照 Pod 清單的要求創建該 Pod。

如果發現本地的 Pod 被修改,則 Kubelet 會做出相應的修改,比如刪除 Pod 中某個容器時,則通過 Docker Client 刪除該容器。 如果發現刪除本節點的 Pod,則刪除相應的 Pod,並通過 Docker Client 刪除 Pod 中的容器。

Kubelet 讀取監聽到的信息,如果是創建和修改 Pod 任務,則執行如下處理:

  1. 爲該 Pod 創建一個數據目錄;
  2. 從 API Server 讀取該 Pod 清單;
  3. 爲該 Pod 掛載外部卷;
  4. 下載 Pod 用到的 Secret;
  5. 檢查已經在節點上運行的 Pod,如果該 Pod 沒有容器或 Pause 容器沒有啓動,則先停止 Pod 裏所有容器的進程。如果在 Pod 中有需要刪除的容器,則刪除這些容器;
  6. 用 “kubernetes/pause” 鏡像爲每個 Pod 創建一個容器。Pause 容器用於接管 Pod 中所有其他容器的網絡。每創建一個新的 Pod,Kubelet 都會先創建一個 Pause 容器,然後創建其他容器。
  7. 爲 Pod 中的每個容器做如下處理:
    爲容器計算一個 hash 值,然後用容器的名字去 Docker 查詢對應容器的 hash 值。若查找到容器,且兩者 hash 值不同,則停止 Docker 中容器的進程,並停止與之關聯的 Pause 容器的進程;若兩者相同,則不做任何處理;
    如果容器被終止了,且容器沒有指定的 restartPolicy,則不做任何處理;
    調用 Docker Client 下載容器鏡像,調用 Docker Client 運行容器。
  • 3)static pod

所有以非 API Server 方式創建的 Pod 都叫 Static Pod。Kubelet 將 Static Pod 的狀態彙報給 API Server,API Server 爲該 Static Pod 創建一個 Mirror Pod 和其相匹配。Mirror Pod 的狀態將真實反映 Static Pod 的狀態。當 Static Pod 被刪除時,與之相對應的 Mirror Pod 也會被刪除。

  • 容器健康檢查
  • Pod 通過兩類探針檢查容器的健康狀態:
  1. LivenessProbe 探針:用於判斷容器是否健康,告訴 Kubelet 一個容器什麼時候處於不健康的狀態。如果 LivenessProbe 探針探測到容器不健康,則 Kubelet 將刪除該容器,並根據容器的重啓策略做相應的處理。如果一個容器不包含 LivenessProbe 探針,那麼 Kubelet 認爲該容器的 LivenessProbe 探針返回的值永遠是 “Success”;
  2. ReadinessProbe:用於判斷容器是否啓動完成且準備接收請求。如果 ReadinessProbe 探針探測到失敗,則 Pod 的狀態將被修改。Endpoint Controller 將從 Service 的 Endpoint 中刪除包含該容器所在 Pod 的 IP 地址的 Endpoint 條目。

Kubelet 定期調用容器中的 LivenessProbe 探針來診斷容器的健康狀況。LivenessProbe 包含如下三種實現方式:

  1. ExecAction:在容器內部執行一個命令,如果該命令的退出狀態碼爲 0,則表明容器健康;
  2. TCPSocketAction:通過容器的 IP 地址和端口號執行 TCP 檢查,如果端口能被訪問,則表明容器健康;
  3. HTTPGetAction:通過容器的 IP 地址和端口號及路徑調用 HTTP GET 方法,如果響應的狀態碼大於等於 200 且小於 400,則認爲容器狀態健康。

LivenessProbe 探針包含在 Pod 定義的 spec.containers.{某個容器} 中。

  • cAdvisor資源監控

Kubernetes 集羣中,應用程序的執行情況可以在不同的級別上監測到,這些級別包括:容器、Pod、Service 和整個集羣。Heapster 項目爲 Kubernetes 提供了一個基本的監控平臺,它是集羣級別的監控和事件數據集成器 (Aggregator)。Heapster 以 Pod 的方式運行在集羣中,Heapster 通過 Kubelet 發現所有運行在集羣中的節點,並查看來自這些節點的資源使用情況。Kubelet 通過 cAdvisor 獲取其所在節點及容器的數據。Heapster 通過帶着關聯標籤的 Pod 分組這些信息,這些數據將被推到一個可配置的後端,用於存儲和可視化展示。支持的後端包括 InfluxDB(使用 Grafana 實現可視化) 和 Google Cloud Monitoring。

cAdvisor 是一個開源的分析容器資源使用率和性能特性的代理工具,已集成到 Kubernetes 代碼中。
cAdvisor 自動查找所有在其所在節點上的容器,自動採集 CPU、內存、文件系統和網絡使用的統計信息。
cAdvisor 通過它所在節點機的 Root 容器,採集並分析該節點機的全面使用情況。
cAdvisor 通過其所在節點機的 4194 端口暴露一個簡單的 UI。

  • kubelet 驅逐

Kubelet 會監控資源的使用情況,並使用驅逐機制防止計算和存儲資源耗盡。在驅逐時,Kubelet 將 Pod 的所有容器停止,並將 PodPhase 設置爲 Failed。
Kubelet 定期(housekeeping-interval)檢查系統的資源是否達到了預先配置的驅逐閾值,包括:
在這裏插入圖片描述

這些驅逐閾值可以使用百分比,也可以使用絕對值,如

--eviction-hard=memory.available<500Mi,nodefs.available<1Gi,imagefs.available<100Gi
--eviction-minimum-reclaim="memory.available=0Mi,nodefs.available=500Mi,imagefs.available=2Gi"`
--system-reserved=memory=1.5Gi

這些驅逐信號可以分爲軟驅逐和硬驅逐:

  1. 軟驅逐(Soft Eviction):配合驅逐寬限期(eviction-soft-grace-period和eviction-max-pod-grace-period)一起使用。系統資源達到軟驅逐閾值並在超過寬限期之後纔會執行驅逐動作。
  2. 硬驅逐(Hard Eviction ):系統資源達到硬驅逐閾值時立即執行驅逐動作。

驅逐動作包括回收節點資源和驅逐用戶 Pod 兩種:
在這裏插入圖片描述

  • Container Runtime

容器運行時(Container Runtime)是 Kubernetes 最重要的組件之一,負責真正管理鏡像和容器的生命週期。Kubelet 通過 Container Runtime Interface (CRI) 與容器運行時交互,以管理鏡像和容器。

  • kubelet工作原理

Kubelet 由許多內部組件構成

  1. Kubelet API,包括 10250 端口的認證 API、4194 端口的 cAdvisor API、10255 端口的只讀 API 以及 10248 端口的健康檢查 API
  2. syncLoop:從 API 或者 manifest 目錄接收 Pod 更新,發送到 podWorkers 處理,大量使用 channel 處理來處理異步請求
  3. 輔助的 manager,如 cAdvisor、PLEG、Volume Manager 等,處理 syncLoop 以外的其他工作
  4. CRI:容器執行引擎接口,負責與 container runtime shim 通信
  5. 容器執行引擎,如 dockershim、rkt 等(注:rkt 暫未完成 CRI 的遷移)
  6. 網絡插件,目前支持 CNI 和 kubenet

在這裏插入圖片描述

  • pod啓動流程
    在這裏插入圖片描述
  • 查詢node彙總指標

通過 Kubelet 的 10255 端口可以查詢 Node 的彙總指標。有兩種訪問方式

  1. 在集羣內部可以直接訪問 kubelet 的 10255 端口,比如 http://:10255/stats/summary
  2. 在集羣外部可以藉助 kubectl proxy 來訪問,比如
    kubectl proxy&
    curl http://localhost:8001/api/v1/proxy/nodes/:10255/stats/summary

2.2.2 kube-proxy

每臺機器上都運行一個 kube-proxy 服務,它監聽 API server 中 service 和 endpoint 的變化情況,並通過 iptables 等來爲服務配置負載均衡(僅支持 TCP 和 UDP)。
kube-proxy 可以直接運行在物理機上,也可以以 static pod 或者 daemonset 的方式運行。

kube-proxy 當前支持以下幾種實現:

  1. userspace:最早的負載均衡方案,它在用戶空間監聽一個端口,所有服務通過 iptables 轉發到這個端口,然後在其內部負載均衡到實際的 Pod。該方式最主要的問題是效率低,有明顯的性能瓶頸。
  2. iptables:目前推薦的方案,完全以 iptables 規則的方式來實現 service 負載均衡。該方式最主要的問題是在服務多的時候產生太多的 iptables 規則,非增量式更新會引入一定的時延,大規模情況下有明顯的性能問題
  3. ipvs:爲解決 iptables 模式的性能問題,v1.11 新增了 ipvs 模式(v1.8 開始支持測試版,並在 v1.11 GA),採用增量式更新,並可以保證 service 更新期間連接保持不斷開
  4. winuserspace:同 userspace,但僅工作在 windows 節點上

注意:使用 ipvs 模式時,需要預先在每臺 Node 上加載內核模塊 nf_conntrack_ipv4,ip_vs,ip_vs_rr,ip_vs_wrr, ip_vs_sh 等。

  • kube-proxy工作原理

kube-proxy 監聽 API server 中 service 和 endpoint 的變化情況,並通過 userspace、iptables、ipvs 或 winuserspace 等 proxier 來爲服務配置負載均衡(僅支持 TCP 和 UDP)。
在這裏插入圖片描述

2.3 插件簡介

2.3.1 DNS

DNS 是 Kubernetes 的核心功能之一,通過 kube-dns 或 CoreDNS 作爲集羣的必備擴展來提供命名服務。

  • CoreDNS

從 v1.11 開始可以使用 CoreDNS 來提供命名服務,並從 v1.13 開始成爲默認 DNS 服務。CoreDNS 的特點是效率更高,資源佔用率更小,推薦使用 CoreDNS 替代 kube-dns 爲集羣提供 DNS 服務。
從 kube-dns 升級爲 CoreDNS 的步驟爲:

$ git clone https://github.com/coredns/deployment
$ cd deployment/kubernetes
$ ./deploy.sh | kubectl apply -f -
$ kubectl delete --namespace=kube-system deployment kube-dns
  • kube-dns工作原理

如下圖所示,kube-dns 由三個容器構成:
在這裏插入圖片描述

  1. kube-dns:DNS 服務的核心組件,主要由 KubeDNS 和 SkyDNS 組成
    1)KubeDNS 負責監聽 Service 和 Endpoint 的變化情況,並將相關的信息更新到 SkyDNS 中
    2)SkyDNS 負責 DNS 解析,監聽在 10053 端口 (tcp/udp),同時也監聽在 10055 端口提供 metrics
    3)kube-dns 還監聽了 8081 端口,以供健康檢查使用
  2. dnsmasq-nanny:負責啓動 dnsmasq,並在配置發生變化時重啓 dnsmasq
    dnsmasq 的 upstream 爲 SkyDNS,即集羣內部的 DNS 解析由 SkyDNS 負責
  3. sidecar:負責健康檢查和提供 DNS metrics(監聽在 10054 端口)
  • 啓動kube-dns示例
kubectl apply -f https://github.com/feiskyer/kubernetes-handbook/raw/master/manifests/kubedns/kube-dns.yaml
  • 支持配置私有的DNS服務器和上游DNS服務器

從 Kubernetes 1.6 開始,可以通過爲 kube-dns 提供 ConfigMap 來實現對存根域以及上游名稱服務器的自定義指定。例如,下面的配置插入了一個單獨的私有根 DNS 服務器和兩個上游 DNS 服務器。

apiVersion: v1
kind: ConfigMap
metadata:
  name: kube-dns
  namespace: kube-system
data:
  stubDomains: |
    {“acme.local”: [“1.2.3.4”]}
  upstreamNameservers: |
    [“8.8.8.8”, “8.8.4.4”]

2.3.2 dashboard

https://kubernetes.io/zh/docs/tasks/access-application-cluster/web-ui-dashboard/

Dashboard 是 Kubernetes 集羣的通用基於 Web 的 UI。它使用戶可以管理集羣中運行的應用程序以及集羣本身並進行故障排除。

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml

2.3.3 容器資源監控

容器資源監控將關於容器的一些常見的時間序列度量值保存到一個集中的數據庫中,並提供用於瀏覽這些數據的界面。

Prometheus,一個 CNCF 項目,可以原生監控 Kubernetes、節點和 Prometheus 本身。

2.3.4 集羣層面日誌

集羣層面日誌 機制負責將容器的日誌數據保存到一個集中的日誌存儲中,該存儲能夠提供搜索和瀏覽接口.
https://kubernetes.io/zh/docs/concepts/cluster-administration/logging/

雖然Kubernetes沒有爲集羣級日誌記錄提供原生的解決方案,但您可以考慮幾種常見的方法。以下是一些選項:

  1. 使用在每個節點上運行的節點級日誌記錄代理。
  2. 在應用程序的 pod 中,包含專門記錄日誌的 sidecar 容器。
  3. 將日誌直接從應用程序中推送到日誌記錄後端。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章