1. 調度
kube- scheduler
- what
- 負責分配調度pod到集群節點
- 監聽kube-apiserver,查詢未分配node的pod
- 根據調度策略分配這些pod(更新pod的nodename)
- 需要考慮的因素:
- 公平調度,資源有效利用,QoS,affinity, anti-affinity, 數據本地化(data localoity),數據本地化,內部負載干擾,
調度器
- 兩個階段: 1. predicate, priority
- predicate: 過濾不符合條件的節點
- priority: 優先級排序,選擇優先級最高的節點
predicate 策略
- PodFitsHostPorts:檢查是否有HostPorts沖突。
- PodFitsPorts: 同PodFitsHostPorts
- PodFitsResources:檢查Node的資源是否充足,包括允許的Pod數量、CPU內存、GPU個數以及其他的- - OpaquelntResources。
- HostName:檢查pod.Spec.NodeName是否與候選節點一致
- MatchNodeSelector:檢查候選節點的pod.Spec.NodeSelector是否匹配
- NoVolumeZoneConflict:檢查volumezone是否沖突
- MatchlnterPodAffinity:檢查是否匹配Pod的親和性要求。
- NoDiskConflict:檢查是否存在Volume沖突,僅限于GCEPD、AWSEBS、CephRBD以及iscSl。
- PodToleratesNodeTaints:檢查Pod是否容忍NodeTaints,
- CheckNodeMemoryPressure:檢查Pod是否可以調度到MemoryPressure的節點上。
- CheckNodeDiskPressure:檢查Pod是否可以調度到DiskPressure的節點上,
- NoVolumeNodeConflict:檢查節點是否滿足Pod所引用的Volume的條件。
- 還有很多其他策略,你也可以編寫自已的策略
predicate的工作原理
priority 策略
-
SelectorSpreadPriority
- 優先將Pod分散到不同的節點上,減少同一Service或Replication Controller下的Pod在同一節點上的數量。
-
InterPodAffinityPriority
- 優先將Pod調度到與現有Pod具有相同拓撲的節點上,例如在同一機架或區域內。
-
LeastRequestedPriority
- 優先選擇資源請求最少的節點。
-
BalancedResourceAllocation
- 平衡各節點的資源使用,避免某些節點資源過載而其他節點資源空閑。
-
NodePreferAvoidPodsPriority
- 根據
alpha.kubernetes.io/preferAvoidPods
字段的判斷,給予高權重,以避免Pod被調度到某些節點上。
- 根據
-
NodeAffinityPriority
- 優先調度到與Pod的NodeAffinity匹配的節點上。
-
TaintTolerationPriority
- 優先調度到能夠容忍Pod的Taints的節點上。
-
ServiceSpreadingPriority
- 盡管已被SelectorSpreadPriority替代,但此策略旨在將同一Service的Pod分散到不同節點上。
-
EqualPriority
- 默認未使用,將所有節點的優先級設置為相同值。
-
ImageLocalityPriority
- 默認未使用,優先將使用大鏡像的Pod調度到已經緩存了該鏡像的節點上。
-
MostRequestedPriority
- 默認未使用,優先調度到資源使用最多的節點上,適用于自動擴縮的集群。
資源需求
-
CPU
- Requests: 確定Pod啟動所需的最小CPU量。
- Limits: 設置Pod可以使用的最大CPU量,超過此限制可能會導致Pod被終止。
-
內存 (Memory)
- Requests: 確定Pod啟動所需的最小內存量。
- Limits: 設置Pod可以使用的最大內存量,超過此限制可能會導致Pod被終止。
-
磁盤資源需求 (Disk Resource Requirements)
- 容器臨時存儲 (Ephemeral Storage): 包含日志和可寫層數據。
- 通過定義
PodSpec
中的limits.ephemeral-storage
和requests.ephemeral-storage
來申請。
- 通過定義
- 計算節點對臨時存儲的限制:不是基于cGroup的,而是由
kubelet
定時獲取容器的日志和容器可寫層的磁盤使用情況。如果超過限制,則會對Pod進行驅逐
- 容器臨時存儲 (Ephemeral Storage): 包含日志和可寫層數據。
-
InitContainer的資源需求 (InitContainer Resource Requirements)
- 當
kube-scheduler
調度帶有多個init容器的Pod時:- 只計算
cpu.request
最多的init容器,而不是計算所有init容器總和。 - 因為多個init容器按順序執行,并且執行完成立即退出,所以申請最多的資源init容器中的所需資源,即可滿足所有init容器需求。
kube-scheduler
在計算該節點被占用的資源時,init容器的資源依然會被納入計算。因為init容器在特定情況下可能會被再次執行,比如由于更換鏡像而引起sandbox重建時
- 只計算
- 當
pod調度到指定的node上
- 使用Node Selector
- 通過給Node打上標簽,并在Pod定義中使用nodeSelector來指定相應的標簽,Pod就會被調度到帶有這些標簽的Node
- kubectl label nodes node-01 disktype=ssd
- 在daemonset中指定nodeSelector為disktype=ssd
apiVersion: v1
kind: Pod
metadata:name: nginxlabels:env: test # 添加的標簽
spec:nodeSelector:disktype: ssd # 指定調度到帶有disktype=ssd標簽的Nodecontainers:- name: nginximage: nginximagePullPolicy: IfNotPresent
- 使用Node Affinity
- NodeAffinity提供了兩種類型的節點親和性規則:必須滿足的規則(requiredDuringSchedulingIgnoredDuringExecution)和優選的規則(preferredDuringSchedulingIgnoredDuringExecution)
apiVersion: v1
kind: Pod
metadata:name: with-node-affinity
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: kubernetes.io/e2e-az-nameoperator: Invalues:- e2e-az1- e2e-az2preferredDuringSchedulingIgnoredDuringExecution:- preference:matchExpressions:- key: another-node-label-keyoperator: Invalues:- another-node-label-valueweight: 1 # 正確放置的weight字段containers:- name: with-node-affinityimage: gcr.io/google_containers/pause:2.0
- 直接設置 nodeName
apiVersion: v1
kind: Pod
metadata:name: nginx
spec:nodeName: <指定的Node名稱> # 直接指定Node名稱containers:- name: nginximage: nginximagePullPolicy: IfNotPresent
- Pod Affinity
- 通過podAffinity和podAntiAffinity,基于其他Pod的標簽選擇Node
apiVersion: v1
kind: Pod
metadata:name: with-pod-affinity
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: securityoperator: Invalues:- S1topologyKey: kubernetes.io/hostnamecontainers:- name: with-pod-affinityimage: gcr.io/google_containers/pause:2.0
- 使用Taints 和 Tolerations
- aints應用于Node上,而Toleration則應用于Pod上。使用Taints和Tolerations可以確保Pod只被調度到滿足特定條件的Node上
- 支持的Taint類型
- NoSchedule:新的Pod不會調度到該Node上,但不影響已經在運行的Pod。
- PreferNoSchedule:相當于一個軟性的NoSchedule,盡量不讓新的Pod調度到該Node上。
- NoExecute:新的Pod不會調度到該Node上,并且會驅逐(evict)已經在運行的Pod。Pod可以設置一個tolerationSeconds字段,表示在被驅逐前等待的時間
- Pod如何調度到帶有Taints的Node
- 當Pod的Tolerations能夠匹配Node的所有Taints時,Pod可以被調度到該Node上
- 如果Pod已經運行在Node上,即使Node后來被添加了Taints,Pod也不會被驅逐,除非Taint是NoExecute類型,并且Pod沒有設置tolerationSeconds
多租戶Kubernetes集群的計算資源隔離
- 在多租戶Kubernetes集群中,一些自帶計算資源的客戶可能希望將他們的資源加入集群,并要求資源隔離
- 實現資源隔離的一種方案是將要隔離的計算節點打上Taints,在用戶創建Pod時,定義Tolerations來指定要調度到帶有這些Taints的Node上
生產環境中的一些經驗
- 用戶可能會忘記設置Toleration,導致Pod無法調度,處于Pending狀態。這是新員工常犯的錯誤,可以通過聊天機器人的Q&A來解決
- 其他用戶可能會查看Node詳情,查到Taints信息,并使用相同的Tolerations來占用資源
- 企業內部可以通過規范管理,通過統計數據來監控誰占用了哪些Node
- 為了防止用戶查看到Taints信息,可以限制他們獲取Node詳情或他人的Pod詳情
調度的優先級
- Pod優先級調度:從Kubernetes版本1.8開始,kube-scheduler支持定義Pod的優先級,確保高優先級的Pod在資源有限的情況下能夠優先調度
- 啟用Pod優先級:要使用Pod優先級調度功能,需要在API server和kube-scheduler的配置中開啟相應的feature
--feature-gates=PodPriority=true
--runtime-config=scheduling.k8s.io/v1alpha1=true
- PriorityClass
- 在設置Pod的優先級之前,需要先定義一個PriorityClass(非namespace資源)。PriorityClass定義了一組可用于Pod的優先級級別
apiVersion: v1
kind: PriorityClass
metadata:name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for XYZ service pods only."
- 為Pod設置PriorityClass
apiVersion: v1
kind: Pod
metadata:name: nginxlabels:env: test
spec:containers:- name: nginximage: nginximagePullPolicy: IfNotPresentpriorityClassName: high-priority
- 多調度器
- 如果默認的調度器不滿足要求,可以部署自定義的調度器,并在整個集群中同時運行多個調度器實例。可以通過podSpec.schedulerName來選擇使用哪一個調度器
來自生產的一些經驗
生產經驗分享
-
Pod Eviction 后IP變化
- Pod被驅逐(evict)后,其IP地址可能會發生變化。但如果endpoint中的address更新失敗,可能會導致連接問題。
-
Endpoint Worker 線程卡死
- 分析stack trace發現endpoint在更新LoadBalancer時調用gophercloud連接hang住,導致endpoint worker線程全部卡死。
-
用戶忘記設置Toleration
- 新員工常犯的錯誤是忘記給Pod設置Toleration,導致Pod無法調度,處于Pending狀態。這可以通過聊天機器人的Q&A來輔助解決。
-
用戶查看Node詳情獲取Taints信息
- 其他用戶可能會查看Node詳情,查到Taints信息,并使用相同的Tolerations來占用資源。
-
Dashboard 監控
- 通過dashboard可以監控哪些用戶的什么應用跑在哪些節點上,對于違規用戶,主要采取批評教育的方式。
-
調度器的穩定性
- 調度器是運營過程中穩定性最好的組件之一,基本不需要太多的維護工作。
-
調度性能
- 在擁有100個node的小集群中,并發創建8000個Pod的最大調度耗時大約是2分鐘左右。
-
Node刪除后的問題
- Node刪除后,如果scheduler cache中還有該Node的信息,可能會導致Pod調度失敗。
-
Node故障時的放大效應
- 當一個Node出現問題負載較小時,用戶的Pod可能會優先調度到該Node上,如果新創建的Pod都失敗,可能會因為打開文件句柄過多導致Node宕機。
-
應用炸彈問題
- 存在危險的用戶Pod(如forkbomb),在調度到某個Node上后,可能會導致該Node以及其他Node受到連鎖影響,最終可能導致整個集群的所有節點不可用。
Controler Manager
-
控制器的工作流程
-
informer的內部機制
-
控制器的協同工作原理
通過controler
- JobController:處理job。
- PodAutoScaler:處理pod的自動縮容/擴容。
- ReplicaSet:依據ReplicasetSpec創建Pod。
- ServiceController:為LoadBalancertype的service創建LB ViP。
- ServiceAccountController:確保serviceaccount在當前namespace存在。
- StatefulSetController:處理statefulset中的pod。
- VolumeController:依據PVspec創建volume。
- ResourcequotaController:在用戶使用資源之后,更新狀態。
- NamespaceController:保證namespace刪除時,該namespace下的所有資源都先被刪除。
- ReplicationController:創建RC后,負責創建POD。
- NodeController:維護node狀態,處理evict請求等。
- DaemonController:依據daemonset創建pod。
- DeploymentController:依據deploymentspec創建replicaset。
- EndpointController:依據servicespec創建endpoint,依據podip更新endpoint。
- GarbageCollector:處理級聯刪除,比如刪除deployment的同時刪除replicaset以及pod。
- CronJobController:處理cronjob。
cloud control manager
-
什么時候需要cloudcontrollermanager
- 認證授權:企業cloud往往需要認證信息,Kubernetes要與CloudAPi通信,需要獲取cloud系統里的ServiceAccount
- Cloudcontrollermanager本身作為一個用戶態的component,需要在Kubernetes中有正確的RBAC設置,獲得資源操作權限
- 高可用:需要通過leaderelection來確保cloudcontrollermanger高可用
-
配置
- cloudcontrollermanager是從老版本的APiServer分離出來的。
Kube-APiServer和kube-controller-manager中一定不能指定cloud-provider,否則會加載
內置的cloudcontrollermanager。
Kubelet要配置–cloud-provider=external
- cloudcontrollermanager是從老版本的APiServer分離出來的。
-
CloudControllerManager主要支持
- Nodecontroller:訪問cloudAPl,來更新node狀態;在cloud刪除該節點以后,從
kubernetes刪除node - Servicecontroller:負責配置為loadbalancer類型的服務配置LBViP;
- RouteController:在cloud環境配置路由;
- 可以自定義任何需要的CloudController。需要定制的Cloud controller
- Nodecontroller:訪問cloudAPl,來更新node狀態;在cloud刪除該節點以后,從
-
需要定制的cloud controller
-
Ingress Controller:負責管理Ingress資源,處理外部訪問到集群內部服務的流量路由。
-
Service Controller:管理Service資源,確保服務發現和負載均衡的正常工作。
-
自主研發的controller:
-
RBAC Controller:基于角色的訪問控制(Role-Based Access Control)的控制器,負責管理集群內角色和角色綁定。
-
Account Controller:負責用戶賬戶相關操作的控制器
-
-
生產經驗總結
- 保護好controller manager的kubeconfig:0
確保schdeduler和controller的高可用
- Kubernetes 提供基于 configmap 和 endpoint 的 leader election 類庫
- 采用 leader election 模式啟動 component 后,會創建對應 endpoint,并把當前的 leader 信息 annotate 到 endpoint 上
apiVersion: v1
kind: Endpoints
metadata:annotations:control-plane.alpha.kubernetes.io/leader: '{"holderldentity":"minikube","leaseDurationSeconds":15,"acquireTime":"2018-04-05T17:31:29Z","renewTime":"2018-04-07T07:18:39Z","leaderTransitions":0,"resourceVersion":"138930"}'selfLink: /api/v1/namespaces/kube-system/endpoints/kube-schedulercreationTimestamp: 2018-04-05T17:31:29Zname: kube-schedulernamespace: kube-system
subsets: null
uid: 2d12578d-38f7-11e8-8dfo-0800275259e5
election 機制
kubelet
kubelet 的架構
kubelet 管理pod的核心流程
kubelet
- 在每個節點上都運行著一個kubelet服務進程,默認監聽10250端口
- 接收并執行來自master的指令;
- 管理Pod及Pod中的容器;
- 每個kubelet進程會在API Server上注冊節點自身信息,定期向master節點匯報節點的資源使用情況,并通過cAdvisor監控節點和容器的資源
節點管理
-
節點管理主要包括節點自注冊和節點狀態更新:
- Kubelet可以通過設置啟動參數–register-node來確定是否向API Server注冊自己;
- 如果Kubelet沒有選擇自注冊模式,則需要用戶自己配置Node資源信息,同時需要告知Kubelet集群上的API Server位置;
- Kubelet在啟動時通過API Server注冊節點信息,并定時向API Server發送節點新消息,API Server在接收到新消息后,將信息寫入etcd。
pod 管理
-
獲取Pod清單:
-
文件:通過啟動參數–config指定的配置目錄下的文件(默認為/etc/Kubernetes/manifests/)。該- - 文件每20秒會重新檢查一次,這個時間間隔是可配置的。
-
HTTP endpoint(URL):通過啟動參數–manifest-url設置。每20秒會檢查一次這個端點,這個時間間隔也是可配置的。
-
API Server:通過API Server監聽etcd目錄,同步Pod清單。
-
HTTP server:kubelet偵聽HTTP請求,并響應簡單的API以提交新的Pod清單。
pod 啟動流程
kubelet 啟動pod的流程
4 CRI
容器運行時(Container Runtime)運行于 Kubernetes(k8s)集群的每個節點中,負責容器的整個生命周期。其中,Docker 是目前應用最廣泛的。隨著容器云的發展,越來越多的容器運行時涌現。為解決這些容器運行時與 Kubernetes 的集成問題,在 Kubernetes 1.5 版本中,社區推出了 CRI(容器運行時接口,Container Runtime Interface),以支持更多容器運行時。
CRI(容器運行時接口)
- 是 Kubernetes 定義的一組 gRPC 服務。kubelet 作為客戶端,基于 gRPC 框架,通過套接字(Socket)與容器運行時通信。它包含兩類服務:
鏡像服務(Image Service):提供鏡像下載、檢查和刪除的遠程過程調用。
運行時服務(Runtime Service):包含用于管理容器生命周期,以及與容器交互(如 exec、attach、port-forward)的遠程過程調用。
運行時的層級
Dockershim、containerd 和 CRI-O 均為遵循 CRI 的容器運行時,我們將其稱為 高層級運行時(High-level Runtime)。
OCI(開放容器計劃,Open Container Initiative)定義了創建容器的格式與運行時的開源行業標準,涵蓋鏡像規范(Image Specification)和運行時規范(Runtime Specification)。
鏡像規范明確了 OCI 鏡像的標準。高層級運行時會下載 OCI 鏡像,并將其解壓為 OCI 運行時文件系統包(filesystem bundle)。
運行時規范則描述了如何通過 OCI 運行時文件系統包運行容器程序,同時定義其配置、運行環境及生命周期。例如,為新容器設置命名空間(namespaces)、控制組(cgroups),以及掛載根文件系統等操作,均在該規范中定義。其參考實現之一是 runC,我們稱其為 低層級運行時(Low-level Runtime)。除 runC 外,還有許多遵循 OCI 標準的運行時,如 kata-runtime。
Docker 內部實現容器運行時功能的核心組件是 containerd,后來 containerd 可直接通過 CRI 與 kubelet 對接,獨立用于 Kubernetes。相較于 Docker,containerd 減少了 Dockerd 和 Docker-shim 等處理模塊,優化了存儲驅動支持,在容器創建、啟動、停止、刪除及鏡像拉取上具備性能優勢,且簡化架構便于維護。盡管 Docker 有不少 containerd 不具備的功能(如支持 zfs 存儲驅動、日志大小和文件限制,基于 overlayfs2 存儲驅動時通過 xfs_quota 限制容器可寫層大小等),但 containerd 已基本滿足容器管理需求,使用它作為運行時的場景也越來越多
開源運行時比較
Docker 的多層封裝和調用,導致其在可維護性上略遜一籌,增加了線上問題的定位難度;幾乎除了重啟 Docker,我們就毫無他法了。
containerd 和 CRI-O 的方案比起 Docker 簡潔很多。
docker 和 containerd的區別
containerd在各個方面表現良好,除了啟動容器
運行時有劣勢對比
功能性:containerd 和 CRI-O 都符合 CRI 和 OCI 的標準。
穩定性:containerd 略勝一籌。
性能:containerd 勝出。
containerd | CRI-O | 備注 | |
---|---|---|---|
性能 | 更優 | 優 | |
功能 | 優 | 優 | CRI與OCI兼容 |
穩定性 | 穩定 | 未知 |
5 CNI
Kubernetes 網絡模型設計的基礎原則是:
- 所有的 Pod 能夠不通過 NAT 就能相互訪問。
- 所有的節點能夠不通過 NAT 就能相互訪問。
- 容器內看見的 IP 地址和外部組件看到的容器 IP 是一樣的。
在 Kubernetes 的集群里,IP 地址以 Pod 為單位分配,每個 Pod 擁有獨立 IP 地址。一個 Pod 內部的所有容器共享宿主機上的一個網絡命名空間(網絡棧),包括 IP 地址、網絡設備、配置等。因此,Pod 內所有容器能通過 localhost:port
連接對方。Kubernetes 提供輕量通用的容器網絡接口 CNI(Container Network Interface),專門用于設置和刪除容器的網絡連通性,容器運行時通過 CNI 調用網絡插件完成容器網絡設置。
CNI插件分類和常見插件
- IPAM:IP 地址分配
- 主插件:網卡設置
- bridge:創建一個網橋,并把主機端口和容器端口插入網橋
- ipvlan:為容器添加 ipvlan 網口
- loopback:設置 loopback 網口
- Meta:附加功能
- portmap:設置主機端口和容器端口映射
- bandwidth:利用 Linux Traffic Control 限流
- firewall:通過 iptables 或 firewalld 為容器設置防火墻規則
鏈接:https://github.com/containernetworking/plugins
CNI 插件運行機制
容器運行時在啟動時會從 CNI 的配置目錄中讀取 JSON 格式的配置文件,文件后綴為“.conf”“.conflist”“.json”。若配置目錄含多個文件,通常以名字排序選用第一個作為默認網絡配置,加載其中指定的 CNI 插件名稱和配置參數。
CNI 的運行機制
關于容器網絡管理,容器運行時一般需配置 --cni-bin-dir
和 --cni-conf-dir
兩個參數。特殊情況:當 kubelet 內置的 Docker 作為容器運行時,由 kubelet 查找 CNI 插件并為容器設置網絡,此時這兩個參數配置在 kubelet 處:
cni-bin-dir
:網絡插件可執行文件目錄,默認/opt/cni/bin
。cni-conf-dir
:網絡插件配置文件目錄,默認/etc/cni/net.d
。
CNI插件的設計考量
- 容器運行時必須在調用任何插件之前為容器創建一個新的網絡命名空間。
- 容器運行時必須決定這個容器屬于哪些網絡,針對每個網絡,哪些插件必須要執行。
- 容器運行時必須加載配置文件,并確定設置網絡時哪些插件必須被執行。
- 網絡配置采用 JSON 格式,可以很容易地存儲在文件中。
- 容器運行時必須按順序執行配置文件里相應的插件。
- 在完成容器生命周期后,容器運行時必須按照與執行添加容器相反的順序執行插件,以便將容器與網絡斷開連接。
- 容器運行時被同一容器調用時不能并行操作,但被不同的容器調用時,允許并行操作。
- 容器運行時針對一個容器必須按順序執行 ADD 和 DEL 操作,ADD 后面總是跟著相應的 DEL。DEL 可能跟著額外的 DEL,插件應該允許處理多個 DEL。
- 容器必須由 ContainerID 來唯一標識,需要存儲狀態的插件需要使用網絡名稱、容器 ID 和網絡接口組成的主 key 用于索引。
- 容器運行時針對同一個網絡、同一個容器、同一個網絡接口,不能連續調用兩次 ADD 命令。
打通主機層網絡
除 CNI 插件外,Kubernetes 還需標準的 CNI 插件 lo
(最低版本 0.2.0)。網絡插件除支持設置和清理 Pod 網絡接口,還需支持 Iptables。若 Kube-proxy 工作在 Iptables 模式,網絡插件需確保容器流量通過 Iptables 轉發。例如:
- 若網絡插件將容器連接到 Linux 網橋,必須通過
sysctl
將net/bridge/bridge-nf-call-iptables
參數設為 1,使網橋上數據包遍歷 Iptables 規則。 - 若插件不使用 Linux 網橋(如類似 Open vSwitch 等機制的插件),則需確保容器流量路由設置正確。
CNI Plugin
- ContainerNetworking 組維護的插件:
- 網絡接口創建:bridge、ipvlan、loopback、macvlan、ptp、host-device 等。
- IP 地址分配:DHCP、host-local、static 等。
- 其他:Flannel、tunning、portmap、firewall 等。
- 第三方網絡策略插件:社區還有 Calico、Cilium、Weave 等,多樣的選項使用戶能根據需求和部署環境選擇 CNI 插件,并在需求變化時快速切換方案。
Flannel
Flannel 是由 CoreOS 開發的項目,屬于早期入門級 CNI 插件,具備簡單易用的特點。
- 存儲與部署:借助 Kubernetes 集群現有的 etcd 集群存儲狀態信息,無需專用數據存儲,僅需在每個節點運行
flanneld
守護進程。 - IP 分配:為每個節點分配子網,用于給節點上的 Pod 分配 IP 地址。
- 通信機制:
- 同一主機內的 Pod 通過網橋通信。
- 不同主機的 Pod 流量經
flanneld
封裝在 UDP 數據包中,路由至目標地址。
- 封裝技術:默認及推薦使用 VxLAN 封裝,優勢是性能好、人為干預少,不足是流量跟蹤難度高。
Calico
Calico 以性能、靈活性和網絡策略著稱,不僅實現主機與 Pod 間的網絡連接,還涉及網絡安全性與策略管理:
- 通信機制:
- 同網段:基于第 3 層,通過 BGP 路由協議在主機間路由數據包,無需額外封裝層。
- 跨網段:基于 IPinIP,利用虛擬網卡設備
tunl0
,用一個 IP 數據包封裝另一個 IP 數據包,外層 IP 數據包頭的源地址為隧道入口設備 IP,目標地址為隧道出口設備 IP。
- 網絡策略:使用 ACLs 協議和 kube-proxy 創建 iptables 過濾規則,實現容器網絡隔離,是其最受歡迎的功能之一。
- 集成能力:可與服務網格 Istio 集成,在服務網格層和網絡基礎結構層解釋并實施集群工作負載策略,支持配置規則控制 Pod 流量收發,提升安全性與網絡控制。
- 架構優勢:采用完全分布式橫向擴展結構,便于開發人員和管理員快速、平穩擴展部署規模,適合對性能和網絡策略要求高的環境。
- Calico 初始化
initContainers:- command:- /opt/cni/bin/installenv:- name: CNI_CONF_NAMEvalue: 10-calico.conflist- name: SLEEPvalue: "false"- name: CNI_NET_DIRvalue: /etc/cni/net.d- name: CNI_NETWORK_CONFIGvalueFrom:configMapKeyRef:key: configname: cni-config- name: KUBERNETES_SERVICE_HOSTvalue: 10.96.0.1- name: KUBERNETES_SERVICE_PORTvalue: "443"image: docker.io/calico/cni:v3.20.1imagePullPolicy: IfNotPresentname: install-cni
- calico 配置一覽
{"name": "k8s-pod-network","cniVersion": "0.3.1","plugins": [{"type": "calico","datastore_type": "kubernetes","mtu": 0,"nodename_file_optional": false,"log_level": "Info","log_file_path": "/var/log/calico/cni/cni.log","ipam": { "type": "calico-ipam", "assign_ipv4": "true", "assign_ipv6": "false" },"container_settings": {"allow_ip_forwarding": false},"policy": {"type": "k8s"},"kubernetes": {"k8s_api_root": "https://10.96.0.1:443","kubeconfig": "/etc/cni/net.d/calico-kubeconfig"}},{"type": "bandwidth","capabilities": {"bandwidth": true}},{"type": "portmap", "snat": true, "capabilities": {"portMappings": true}}]
}
- calico vxlan
IPPool
IPPool 用于定義集群的預定義 IP 段,示例內容如下:
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata: name: default-ipv4-ippool
spec: blockSize: 26 cidr: 192.168.0.0/16 ipipMode: Never natOutgoing: true nodeSelector: all() vxlanMode: CrossSubnet
IPAMBlock
IPAMBlock 用于定義每個主機預分配的 IP 段,內容提取如下:
apiVersion: crd.projectcalico.org/v1
kind: IPAMBlock
metadata: annotations: name: 192-168-119-64-26
spec: affinity: host:cadmin
allocations: - null - 0 - null - 1 - 2 - 3
attributes: - handle_id: vxlan-tunnel-addr-cadmin
secondary: - node: cadmin type: vxlanTunnelAddress handle_id: k8s-pod-network.6680d3883c6150e75ffbd031f6c689a97a5be0f260c6442b2bb46b567c2ca40 secondary: namespace: calico-apiserver node: cadmin pod: calico-apiserver-77dffffcdf-g2tcx timestamp: 2021-09-30 09:46:57.4551016 +0000 UTC - handle_id: k8s-pod-network.b10d7702bf334fc55a5e399a731ab3201ea9990a1e3bc79894abddd712646699 secondary: namespace: calico-system node: cadmin pod: calico-kube-controllers-bdd5f97c5-554z5 timestamp: 2021-09-30 09:46:57.502331346 +0000 UTC
- IPAMHandle
apiVersion: crd.projectcalico.org/v1
kind: IPAMHandle
metadata: name: k8s-pod-network.8d75b941d85c4998016b72c83f9c5a75512c82c052357daf0ec8e67365635d93
spec: block: 192.168.119.64/26: 1 deleted: false handleID: k8s-pod-network.8d75b941d85c4998016b72c83f9c5a75512c82c052357daf0ec8e67365635d93
創建Pod并查看IP配置情況
容器 namespace
# 查看IP配置
nsenter -t 720702 -n ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.1/8 scope host lo valid_lft forever preferred_lft forever
3: eth0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link/ether f2:86:d2:4f:1f:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.119.84/32 brd 192.168.119.84 scope global eth0 valid_lft forever preferred_lft forever # 查看路由
nsenter -t 720702 -n ip r
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link # 查看ARP表
nsenter -t 720702 -n arp
Address HWtype HWaddress Flags Mask iface
169.254.1.1 ether ee:ee:ee:ee:ee:ee C eth0
10.0.2.15 ether ee:ee:ee:ee:ee:ee C eth0
主機 Namespace
# 查看網絡接口
ip link
27: cali23a582ef038@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 9 inet6 fe80::ecee:eeff:feee:eeee/64 scope link valid_lft forever preferred_lft forever # 查看路由
ip route
192.168.119.84 dev cali23a582ef038 scope link
CNI plugin對比
CSI
容器運行時
除外掛存儲卷外,容器啟動后,運行時所需文件系統性能直接影響容器性能。
早期的 Docker 采用 Device Mapper 作為容器運行時存儲驅動,因 OverlayFS 尚未合并進 Kernel。
目前 Docker 和 containerd 均默認以 OverlayFS 作為運行時存儲驅動。
OverlayFS 性能表現優異,與 DeviceMapper 相比提升約 20%,且與操作主機文件性能幾乎一致
存儲卷插件管理
Kubernetes 支持以插件形式實現對不同存儲的支持和擴展,基于以下三種方式:
in-tree 插件
- Kubernetes 社區已不再接受新的 in-tree 存儲插件,新存儲需通過 out-of-tree 插件支持。
out-of-tree FlexVolume 插件
- 交互方式:Kubernetes 通過調用計算節點本地可執行文件與存儲插件交互。
- 權限與工具:需宿主機用
root
權限安裝插件驅動,且存儲驅動需宿主機安裝attach
、mount
等工具,具備root
訪問權限。
out-of-tree CSI 插件
- 圖中僅呈現標題,未展開具體說明內容。
out-of-tree CSI 插件
- 交互方式:CSI 通過 RPC 與存儲驅動進行交互。
- Kubernetes 模塊定義:設計 CSI 時,Kubernetes 對存儲驅動打包部署要求少,主要定義以下模塊:
kube-controller-manager
- 功能:感知 CSI 驅動存在。
- 交互特點:
- 主控模塊通過 Unix domain socket(非 CSI 驅動)或其他方式直接交互。
- 僅與 Kubernetes 相關 API 交互。
- 操作依賴:若 CSI 驅動涉及依賴 Kubernetes API 的操作(如卷創建、卷 attach、卷快照等),需在驅動內通過 Kubernetes API 觸發對應 CSI 操作。
kubelet
- 功能:與 CSI 驅動交互。
- 交互流程:
- 通過 Unix domain socket 向 CSI 驅動發起 CSI 調用(如
NodeStageVolume
、NodePublishVolume
等),再執行 mount 卷和 umount 卷操作。
- 通過 Unix domain socket 向 CSI 驅動發起 CSI 調用(如
- 注冊機制:
- 通過插件注冊機制發現 CSI 驅動及交互所用的 Unix Domain Socket。
- 所有部署在 Kubernetes 集群中的 CSI 驅動,均需通過 kubelet 的插件注冊機制完成注冊。
CSI驅動
CSI 的驅動一般包含 external-attacher、external-provisioner、external-resizer、 external-snapshotter、node-driver-register、CSI driver 等模塊,可以根據實際的存儲類型和需求進行不同方式的部署。
臨時存儲
常見的臨時存儲主要是emptyDir卷。
- 數據生命周期:emptyDir卷最初為空,Pod從節點刪除時,卷中數據會永久刪除;Pod的容器退出再重啟,卷內數據不會丟失。
- 存儲介質:默認存儲在節點支持的介質上,如本地磁盤或網絡存儲。也可通過設置emptyDir.medium為“Memory”使用tmpfs,數據存于內存,速度快,但節點重啟數據清除;存于磁盤則重啟后數據保留。使用tmpfs的內存計入容器內存總量,受Cgroup限制。
- 設計初衷與使用建議:主要用于充當應用緩存空間或存儲中間數據,方便快速恢復。但不建議所有有此類需求的用戶都使用,因其空間在系統根盤且被容器共享,磁盤使用率高時可能觸發Pod驅逐操作,影響業務穩定 ,需根據業務實際特點判斷是否使用。
半持久化存儲
常見的半持久化存儲主要是hostPath卷。
- 功能與適用場景:能將主機節點文件系統上的文件或目錄掛載到指定Pod中。普通用戶一般不需要,對于需獲取節點系統信息的Pod很必要。
- 用法舉例:
- 若Pod需獲取節點上所有Pod的log,可通過hostPath訪問所有Pod的stdout輸出存儲目錄,如/var/log/pods路徑。
- 若Pod需統計系統相關信息,可通過hostPath訪問系統的/proc目錄。
- 使用要點:使用時除設置必需的path屬性外,還可選擇性為hostPath卷指定類型,支持目錄、字符設備、塊設備等類型。
hostPath卷需要注意
- 若多個Pod使用同一個目錄,因調度到不同節點,目錄內容可能不同。
- Kubernetes調度時不會考慮hostPath使用的資源。
- Pod刪除后,若未特殊處理,hostPath上的數據會留在節點上,占用磁盤空間 。
持久化存儲
- 概念引入:支持持久化的存儲是分布式系統必備特性。Kubernetes引入StorageClass、Volume、PVC(Persistent Volume Claim)、PV(Persitent Volume)概念,使存儲獨立于Pod生命周期管理。
- 支持類型:Kubernetes目前支持多種主流的塊存儲和文件存儲,如awsElasticBlockStore、azureDisk、cinder、NFS、cephfs、iscsi等,總體可分為網絡存儲和本地存儲兩類。
以下是提取的內容:
StorageClass
- 概念與構成:StorageClass 用于指示存儲類型,不同存儲類型可通過它為用戶服務,主要包含存儲插件
provisioner
、卷的創建和mount
參數等字段。 - 示例代碼:
allowVolumeExpansion: true
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:annotations:storageclass.kubernetes.io/is-default-class: "false"name: rook - ceph - block
parameters:clusterID: rook - cephcsi.storage.k8s.io/controller - expand - secret - name: rook - csi - rbd - provisionercsi.storage.k8s.io/controller - expand - secret - namespace: rook - cephcsi.storage.k8s.io/fstype: ext4csi.storage.k8s.io/node - stage - secret - name: rook - csi - rbd - nodecsi.storage.k8s.io/node - stage - secret - namespace: rook - cephcsi.storage.k8s.io/provisioner - secret - name: rook - csi - rbd - provisionercsi.storage.k8s.io/provisioner - secret - namespace: rook - cephimageFeatures: layeringimageFormat: "2"pool: replicapool
provisioner: rook - ceph.rbd.csi.ceph.com
reclaimPolicy: Delete
volumeBindingMode: Immediate
PVC
由用戶創建,代表用戶對存儲需求的聲明,主要包含需要的存儲大小、存儲卷的訪問模式、StorageClass 等類型,其中存儲卷的訪問模式必須與存儲的類型一致。
簡稱 | 全稱 | 說明 |
---|---|---|
RWO | ReadWriteOnce | 該卷只能在一個節點上被 mount,屬性為可讀可寫 |
ROX | ReadOnlyMany | 該卷可以在不同的節點上被 mount,屬性為只讀 |
RWX | ReadWriteMany | 該卷可以在不同的節點上被 mount,屬性為可讀可寫 |
PV
由集群管理員提前創建,或者根據PVC的申請需求動態地創建,它代表系統后端的真實的存儲空間,可以稱之為卷空間。
存儲對象關系
用戶通過創建 PVC 來申請存儲。控制器通過 PVC 的 StorageClass 和請求的大小聲明來存儲后端創建卷,進而創建 PV,Pod 通過指定 PVC 來引用存儲。
生產實踐經驗分享
- StorageClass設置:不同介質類型磁盤,需設置不同StorageClass以便用戶區分,且應設置磁盤介質類型,讓用戶了解存儲屬性。
- 本地存儲PV部署:本地存儲的PV靜態部署模式下,每個物理磁盤盡量只創建一個PV,避免分區間I/O干擾。
- 磁盤檢測配合:本地存儲需配合磁盤檢測。集群規模化后磁盤損壞頻發,檢測到磁盤損壞等問題,需對節點磁盤和本地存儲PV 進行處理,如觸發告警、自動cordon節點、自動通知用戶。
- 磁盤管理要點:本地存儲節點的磁盤管理要靈活且自動化,節點磁盤信息可統一集中管理。在local-volume-provisioner中增加部署邏輯,容器運行時拉取磁盤信息,依此對磁盤格式化或加入VG,形成本地存儲支持的自動化閉環。
生產實踐經驗分享
- StorageClass設置:針對不同介質類型磁盤,設置不同StorageClass 以便用戶區分,同時在其中明確磁盤介質類型,展示存儲屬性。
- 本地存儲PV部署:本地存儲PV靜態部署時,每個物理磁盤盡量只創建一個PV,防止分區間I/O干擾。
- 磁盤檢測與處理:本地存儲要結合磁盤檢測。集群規模擴大后磁盤損壞可能增多,檢測到磁盤損壞等問題,需對節點磁盤及對應本地存儲PV 采取措施,如觸發告警、自動隔離節點(cordon)、通知用戶。
- 磁盤管理自動化:本地存儲節點的磁盤管理需靈活且自動化。將節點磁盤信息統一集中管理,在local-volume-provisioner中添加部署邏輯,容器運行時獲取本地存儲磁盤信息,如設備路徑等,依此對磁盤格式化或加入虛擬組(VG),實現本地存儲支持的自動化閉環。
獨占的 Local Volume
- 創建PV:通過local-volume-provisioner DaemonSet創建本地存儲的PV。
- 創建PVC:用戶創建PVC,此時其處于pending狀態,kube-controller-manager不會對該PVC做任何操作。
- 創建Pod:用戶創建Pod。
- Pod挑選節點:kube-scheduler開始調度Pod,依據PVC的resources.request.storage和volumeMode選擇滿足條件的PV,并為Pod選擇合適節點。
- 更新PV:kube-scheduler將PV的pv.Spec.claimRef設置為對應的PVC,并設置annotation pv.kubernetes.io/bound-by-controller的值為“yes”。
- PVC和PV綁定:pv_controller同步PVC和PV的狀態,并將二者綁定。
- 監聽PVC對象:kube-scheduler等待PVC的狀態變為Bound狀態。
- Pod調度到節點:若PVC狀態變為Bound則調度成功;若一直處于pending狀態,超時后會再次調度。
- Mount卷啟動容器:kubelet監聽到Pod調度到節點上,對本地存儲進行mount操作并啟動容器。
Dynamic Local Volume
- CSI驅動要求:CSI驅動需匯報節點上相關存儲的資源信息,用于調度,但因機器廠家不同,匯報方式各異。
- 示例情況:部分廠家的機器節點存在NVMe、SSD、HDD等多種存儲介質,希望分別匯報這些介質信息。
- 現狀與挑戰:該需求與其他存儲類型的CSI驅動接口需求不同,目前在如何匯報節點存儲信息以及如何將其應用于調度方面,尚無統一意見。
- 應對措施:集群管理員可根據節點存儲實際情況,對開源CSI驅動和調度進行代碼修改后再部署使用。
Local Dynamic 的掛載流程
- 創建PVC:用戶創建PVC,此時PVC處于pending狀態。
- 創建Pod:用戶創建Pod。
- Pod選擇節點:kube-scheduler依據PVC的pvc.spec.resources.request.storage等條件,調度選擇滿足要求的節點。
- 更新PVC:選定節點后,kube-scheduler給PVC添加包含節點信息的annotation(volume.kubernetes.io/selected-node: <節點名字>)。
- 創建卷:運行在節點上的external-provisioner容器監聽到PVC帶有該節點相關的annotation,向對應的CSI驅動申請分配卷。
- 創建PV:PVC申請到所需存儲空間后,external-provisioner創建PV,并將PV的pv.Spec.claimRef設置為對應的PVC。
- PVC和PV綁定:kube-controller-manager將PVC和PV綁定,并把狀態修改為Bound。
- 監聽PVC狀態:kube-scheduler等待PVC變為Bound狀態。
- Pod調度到節點:當PVC狀態為Bound時,Pod調度成功;若一直處于Pending狀態,超時后會重新調度。
- Mount卷:kubelet監聽到Pod調度到節點,對本地存儲進行mount操作。
- 啟動容器:啟動容器。
Local Dynamic 的挑戰
- 若將磁盤空間作為存儲池(如LVM)進行動態分配,分配出的邏輯卷空間在使用時,可能因底層物理卷相同,而受到其他邏輯卷的I/O干擾。
- 若PV后端的磁盤空間是獨立的物理磁盤,則I/O不會受到干擾 。
生產實踐經驗分享
- StorageClass設置:不同介質類型磁盤需設置不同StorageClass 以便用戶區分,且應設置磁盤介質類型,展示存儲屬性。
- 本地存儲PV部署:本地存儲的PV靜態部署時,每個物理磁盤盡量只創建一個PV,避免分區間I/O干擾。
- 磁盤檢測與處理:本地存儲要配合磁盤檢測。集群規模擴大后磁盤損壞可能增多,檢測到磁盤損壞等問題,需對節點磁盤及對應本地存儲PV 采取觸發告警、自動隔離節點(cordon)、通知用戶等措施。
- 磁盤管理自動化:本地存儲節點的磁盤管理需靈活且自動化。將節點磁盤信息統一集中管理,在local-volume-provisioner中添加部署邏輯,容器運行時獲取本地存儲磁盤信息,如設備路徑等,依此對磁盤格式化或加入虛擬組(VG),實現本地存儲支持的自動化閉環。
Rook
Rook是云原生環境下的開源分布式存儲編排系統,支持Ceph、NFS、EdgeFS、Cassandra、CockroachDB等存儲系統。具備自動管理、自動擴容、自動修復功能,支持自動部署、啟動、配置、分配、擴容/縮容、升級、遷移、災難恢復、監控及資源管理。
- 架構
Rook Operator
- 基本屬性:Rook Operator是Rook的核心組件,以deployment形式存在。
- 資源創建:利用Kubernetes的controller-runtime框架實現CRD(自定義資源定義),接收Kubernetes創建資源的請求,進而創建集群、pool、塊存儲服務、文件存儲服務等相關資源。
- 存儲監控:監控存儲守護進程,保障存儲集群健康;監聽Rook Discovers收集的存儲磁盤設備,并創建相應服務(如Ceph中的OSD )。
Rook Discover
- 部署與功能:Rook Discover以DaemonSet形式部署在所有存儲機上,用于檢測掛接到存儲節點的存儲設備,記錄符合要求的設備,以便Rook Operator基于這些設備創建相應服務。
- 操作命令:
- 發現設備:
$ lsblk --all --noheadings --list --output KNAME
$ lsblk /dev/vdd --bytes --nodeps --pairs --paths --output SIZE,ROTA,RO,TYPE,PKNAME,NAME,KNAME
$ udevadm info --query=property /dev/vdd
$ lsblk --noheadings --pairs /dev/vdd
- 發現Ceph清單:
$ ceph-volume inventory --format json
,若設備有ceph清單,device.CephVolumeData = CVData
- 將設備信息存入每個節點的ConfigMap :
put device info into configmap per node
- 發現設備:
CSIDriver發現
- 驅動發現方式:若CSI驅動創建CSIDriver對象,Kubernetes用戶可通過
get CSIDriver
命令發現它們。 - CSI對象特點:
- 具備自定義的Kubernetes邏輯。
- 可自定義支持Kubernetes對存儲卷的哪些操作。
Provisioner
- 組件性質:CSI external - provisioner是監控Kubernetes PVC對象的Sidecar容器。
- 工作流程:用戶創建PVC后,Kubernetes監測PVC對應的StorageClass,若其中的provisioner與某插件匹配,該容器通過CSI Endpoint(通常是unix socket)調用CreateVolume方法。若調用成功,Provisioner sidecar創建Kubernetes PV對象。