Kubernetes-三大開放接口-初見


容器運行時接口CRI

歷史

OCI出現是為了它的核心目標圍繞容器的格式和運行時制定一個開放的工業化標准,並推動這個標准,保持容器的靈活性和開放性,容器能運行在任何的硬件和系統上。容器不應該綁定到特定的客戶機或編排堆棧,不應該與任何特定的供應商緊密關聯,並且可以跨多種操作系統
官網上對 OCI 的介紹如下:

Established in June 2015 by Docker and other leaders in the container industry, the OCI currently contains two specifications: the Runtime Specification (runtime-spec) and the Image Specification (image-spec). The Runtime Specification outlines how to run a “filesystem bundle” that is unpacked on disk. At a high-level an OCI implementation would download an OCI Image then unpack that image into an OCI Runtime filesystem bundle. At this point the OCI Runtime Bundle would be run by an OCI Runtime.

OCI由docker以及其他容器行業領導者創建於2015年,目前主要有兩個標准:容器運行時標准(runtime-spec)和容器鏡像標准(image-spec)。

這兩個標准通過OCI runtime filesytem bundle的標准格式連接在一起,OCI鏡像可以通過工具轉換成bundle,然后 OCI 容器引擎能夠識別這個bundle來運行容器

文檔主要做了兩個事情:

  • 創建鏡像的規則
  • 運行鏡像的規則

了解了OCI,以及docker在兼容OCI標准架構的調整后, 迎來我們的重點 CRI,CRI是kubernetes推出的一個標准,推出標准可見其在容器編排領域的地位。

簡介

容器運行時接口(Container Runtime Interface),簡稱 CRI。CRI 中定義了 容器鏡像 的服務的接口,因為容器運行時與鏡像的生命周期是彼此隔離的,因此需要定義兩個服務。該接口使用 Protocol Buffer,基於 gRPC,在 Kubernetes v1.10 + 版本中是在 pkg/kubelet/apis/cri/runtime/v1alpha2api.proto 中定義的。

容器運行時(Container Runtime):顧名思義就是容器從拉取鏡像到啟動運行再到中止的整個生命周期。 其中最知名的就是Docker了。

每個容器運行時都有特點,因此不少用戶希望Kubernetes能夠支持更多的容器運行時。為了更具擴展性,kubernetes引入了容器運行時插件API,即 Container Runtime Interface,簡稱CRI。

  • Protocol Buffers API(一種更高效的類似json的數據格式)包含兩個gRPC服務:
    • ImageService和RuntimeService。
      • ImageService提供了從倉庫拉取鏡像、查看和移除鏡像的功能。
      • RuntimeService負責Pod和容器的生命周期管理,以及與容器的交互 (exec/attach/port-forward)。

架構

Container Runtime 實現了 CRI gRPC Server,包括 RuntimeServiceImageService。該 gRPC Server 需要監聽本地的 Unix socket,而 kubelet 則作為 gRPC Client 運行。

啟用 CRI

除非集成了 rktnetes,否則 CRI 都是被默認啟用了,從 Kubernetes 1.7 版本開始,舊的預集成的 docker CRI 已經被移除。

要想啟用 CRI 只需要在 kubelet 的啟動參數重傳入此參數:--container-runtime-endpoint 遠程運行時服務的端點。當前 Linux 上支持 unix socket,windows 上支持 tcp。

Kubelet拉起一個容器的過程:

  1. Kubelet通過CRI接口(gRPC)調用docker-shim,請求創建一個容器這一步中,kubelet可以視作一個簡單的CRI Client,而docker-shim就是接收請求的Server,注意的是docker-shim是內嵌在Kubelet中的

  2. docker-shim收到請求后,轉化成Docker Daemon能聽懂的請求,發到Docker Daemon上請求創建一個容器

  3. Docker Daemon請求containerd創建一個容器

  4. containerd收到請求后創建一個containerd-shim進程,通過containerd-shim操作容器,容器進程需要一個父進程來做諸如收集狀態, 維持stdin等fd打開等工作

  5. containerd-shim在調用runC來啟動容器

  6. runC 啟動完容器后本身會直接退出,containerd-shim則會成為容器進程的父進程,負責收集容器進程的狀態,上報給containerd。

通過上面kubelet創建容器的流程, 我們可以看到kubelet通過CRI的標准來與外部容器運行時進行交互。

kubernetes 早期版本1.5之前內置了docker和rkt,也就是支持兩種運行時, 這個時候如果用戶想自定義運行時就比較痛苦了,需要修改kubelet源碼。

同時不同的容器運行時各有所長,隨着k8s在容器編排領域里面老大的地位,許多用戶希望kubernetes支持更多的容器運行時,滿足不同用戶,不同環境的使用。

於是從kubernetes1.5開始增加了CRI接口, 有了CRI接口無需修改kubelet源碼就可以支持更多的容器運行時,

與此同時內置的docker和rtk逐漸從kubernetes源碼中移除,到kubernetes1.11版本Kubelet內置的rkt代碼刪除,CNI的實現遷移到dockers-shim之內,除了docker之外,其他的容器運行時都通過CRI接入。

外部的容器運行時一般稱為CRI shim,它除了實現CRI接口外,也要負責為容器配置網絡,即CNI,有了CNI可以支持社區內的眾多網絡插件。

CRI 接口

CRI主要定義兩個接口, ImageService和RuntimeService,如下圖:

ImageService:負責鏡像的生命管理周期

  • 查詢鏡像列表
  • 拉取鏡像到本地
  • 查詢鏡像狀態
  • 刪除本地鏡像
  • 查詢鏡像占用空間

RuntimeService:負責管理Pod和容器的生命周期

  • PodSandbox 的管理接口
    PodSandbox是對kubernete Pod的抽象,用來給容器提供一個隔離的環境(比如掛載到相同的cgroup下面)並提供網絡等共享的命名空間。PodSandbox通常對應到一個Pause容器或者一台虛擬機。
  • Container 的管理接口
    在指定的 PodSandbox 中創建、啟動、停止和刪除容器。
  • Streaming API接口
    包括Exec、Attach和PortForward 等三個和容器進行數據交互的接口,這三個接口返回的是運行時Streaming Server的URL,而不是直接跟容器交互。
  • 狀態接口
    包括查詢API版本和查詢運行時狀態。

容器生態可以下面的三層抽象:

Orchestration API -> Container API -> Kernel API

  • Orchestration API: kubernetes API標准就是這層的標准,無可非議
  • Container API: 標准就是CRI
  • Kernel API: 標准就是OCI

當前支持的 CRI 后端

我們最初在使用 Kubernetes 時通常會默認使用 Docker 作為容器運行時,其實從 Kubernetes 1.5 開始已經支持 CRI,通過 CRI 接口可以指定使用其它容器運行時作為 Pod 的后端,目前支持 CRI 的后端有:

  • cri-o:cri-o 是 Kubernetes 的 CRI 標准的實現,並且允許 Kubernetes 間接使用 OCI 兼容的容器運行時,可以把 cri-o 看成 Kubernetes 使用 OCI 兼容的容器運行時的中間層。
  • cri-containerd:基於 Containerd 的 Kubernetes CRI 實現
  • rkt:由 CoreOS 主推的用來跟 docker 抗衡的容器運行時
  • frakti:基於 hypervisor 的 CRI
  • docker:Kuberentes 最初就開始支持的容器運行時,目前還沒完全從 kubelet 中解耦,Docker 公司同時推廣了 OCI 標准

CRI 是由 SIG-Node 來維護的。

容器網絡接口CNI

簡介

容器網絡接口(Container Network Interface),簡稱 CNI,是 CNCF 旗下的一個項目,由一組用於配置 Linux 容器的網絡接口的規范和庫組成,同時還包含了一些插件。CNI 僅關心容器創建時的網絡分配,和當容器被刪除時釋放網絡資源。有關詳情請查看 GitHub

Kubernetes 源碼的 vendor/github.com/containernetworking/cni/libcni 目錄中已經包含了 CNI 的代碼,也就是說 Kubernetes 中已經內置了 CNI。

為什么出現?

不管是 Docker 還是 Kubernetes,在網絡方面目前都沒有一個完美的、終極的、普適性的解決方案,不同的用戶和企業因為各種原因會使用不同的網絡方案。

目前存在網絡方案 flannel、calico、openvswitch、weave、ipvlan等,而且以后一定會有其他的網絡方案,這些方案接口和使用方法都不相同,而不同的容器平台都需要網絡功能,它們之間的適配如果沒有統一的標准,會有很大的工作量和重復勞動。

CNI 就是這樣一個標准,它旨在為容器平台提供網絡的標准化。不同的容器平台(比如目前的 kubernetes、mesos 和 rkt)能夠通過相同的接口調用不同的網絡組件。

CNI(Conteinre Network Interface) 是 Google 和 CoreOS 主導制定的容器網絡標准,它本身並不是實現或者代碼,可以理解成一個協議。這個標准是在 rkt 網絡提議 的基礎上發展起來的,綜合考慮了靈活性、擴展性、ip 分配、多網卡等因素。

這個協議連接了兩個組件:容器管理系統和網絡插件。它們之間通過 JSON 格式的文件進行通信,實現容器的網絡功能。具體的事情都是插件來實現的,包括:創建容器網絡空間(network namespace)、把網絡接口(interface)放到對應的網絡空間、給網絡接口分配 IP 等等。

關於網絡,Docker 也提出了 CNM 標准,它要解決的問題和 CNI 是重合的,也就是說目前兩者是競爭關系。

目前 CNM 只能使用在 Docker 中,而 CNI 可以使用在任何容器運行時。CNM 主要用來實現 docker 自身的網絡問題,也就是 docker network 子命令提供的功能。

接口定義

CNI 的接口中包括以下幾個方法:

type CNI interface {
    AddNetworkList (net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
    DelNetworkList (net *NetworkConfigList, rt *RuntimeConf) error
    AddNetwork (net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
    DelNetwork (net *NetworkConfig, rt *RuntimeConf) error
}

該接口只有四個方法,添加網絡、刪除網絡、添加網絡列表、刪除網絡列表。

官方網絡插件

所有的標准和協議都要有具體的實現,才能夠被大家使用。CNI 也不例外,目前官方在 github 上維護了同名的 CNI 代碼庫,里面已經有很多可以直接拿來使用的 CNI 插件。

官方提供的插件目前分成三類:main、meta 和 ipam。main 是主要的實現了某種特定網絡功能的插件;meta 本身並不會提供具體的網絡功能,它會調用其他插件,或者單純是為了測試;ipam 是分配 IP 地址的插件。

ipam 並不提供某種網絡功能,只是為了靈活性把它單獨抽象出來,這樣不同的網絡插件可以根據需求選擇 ipam,或者實現自己的 ipam。

這些插件的功能說明如下:

  • main
    • loopback:這個插件很簡單,負責生成 lo 網卡,並配置上 127.0.0.1/8 地址
    • bridge:和 docker 默認的網絡模型很像,把所有的容器連接到虛擬交換機上
    • macvlan:使用 macvlan 技術,從某個物理網卡虛擬出多個虛擬網卡,它們有獨立的 ip 和 mac 地址
    • ipvlan:和 macvlan 類似,區別是虛擬網卡有着相同的 mac 地址
    • ptp:通過 veth pair 在容器和主機之間建立通道
  • meta
    • flannel:結合 bridge 插件使用,根據 flannel 分配的網段信息,調用 bridge 插件,保證多主機情況下容器
  • ipam
    • host-local:基於本地文件的 ip 分配和管理,把分配的 IP 地址保存在文件中
    • dhcp:從已經運行的 DHCP 服務器中獲取 ip 地址

接口參數

網絡插件是獨立的可執行文件,被上層的容器管理平台調用。網絡插件只有兩件事情要做:把容器加入到網絡以及把容器從網絡中刪除。調用插件的數據通過兩種方式傳遞:環境變量和標准輸入。一般插件需要三種類型的數據:容器相關的信息,比如 ns 的文件、容器 id 等;網絡配置的信息,包括網段、網關、DNS 以及插件額外的信息等;還有就是 CNI 本身的信息,比如 CNI 插件的位置、添加網絡還是刪除網絡。

我們來看一下為容器添加網絡是怎么工作的,刪除網絡和它過程一樣。

把容器加入到網絡

調用插件的時候,這些參數會通過環境變量進行傳遞:

  • CNI_COMMAND:要執行的操作,可以是 ADD(把容器加入到某個網絡)、DEL(把容器從某個網絡中刪除)
  • CNI_CONTAINERID:容器的 ID,比如 ipam 會把容器 ID 和分配的 IP 地址保存下來。可選的參數,但是推薦傳遞過去。需要保證在管理平台上是唯一的,如果容器被刪除后可以循環使用
  • CNI_NETNS:容器的 network namespace 文件,訪問這個文件可以在容器的網絡 namespace 中操作
  • CNI_IFNAME:要配置的 interface 名字,比如 eth0
  • CNI_ARGS:額外的參數,是由分號;分割的鍵值對,比如 “FOO=BAR;hello=world”
  • CNI_PATH:CNI 二進制查找的路徑列表,多個路徑用分隔符 : 分隔

網絡信息主要通過標准輸入,作為 JSON 字符串傳遞給插件,必須的參數包括:

  • cniVersion:CNI 標准的版本號。因為 CNI 在演化過程中,不同的版本有不同的要求
  • name:網絡的名字,在集群中應該保持唯一
  • type:網絡插件的類型,也就是 CNI 可執行文件的名稱
  • args:額外的信息,類型為字典
  • ipMasq:是否在主機上為該網絡配置 IP masquerade
  • ipam:IP 分配相關的信息,類型為字典
  • dns:DNS 相關的信息,類型為字典

插件接到這些數據,從輸入和環境變量解析到需要的信息,根據這些信息執行程序邏輯,然后把結果返回給調用者,返回的結果中一般包括這些參數:

  • IPs assigned to the interface:網絡接口被分配的 ip,可以是 IPv4、IPv6 或者都有
  • DNS 信息:包含 nameservers、domain、search domains 和其他選項的字典

CNI 協議的內容還在不斷更新,請到官方文檔獲取當前的信息。

CNI 的特性

CNI 作為一個協議/標准,它有很強的擴展性和靈活性。如果用戶對某個插件有額外的需求,可以通過輸入中的 args 和環境變量 CNI_ARGS 傳輸,然后在插件中實現自定義的功能,這大大增加了它的擴展性;CNI 插件把 main 和 ipam 分開,用戶可以自由組合它們,而且一個 CNI 插件也可以直接調用另外一個 CNI 插件,使用起來非常靈活。

如果要實現一個繼承性的 CNI 插件也不復雜,可以編寫自己的 CNI 插件,根據傳入的配置調用 main 中已經有的插件,就能讓用戶自由選擇容器的網絡。

在 kubernetes 中的使用

CNI 目前已經在 kubernetes 中開始使用,也是目前官方推薦的網絡方案,具體的配置方法可以參考kubernetes 官方文檔

kubernetes 使用了 CNI 網絡插件之后,工作過程是這樣的:

  • kubernetes 先創建 pause 容器生成對應的 network namespace
  • 調用網絡 driver(因為配置的是 CNI,所以會調用 CNI 相關代碼)
  • CNI driver 根據配置調用具體的 cni 插件
  • cni 插件給 pause 容器配置正確的網絡
  • pod 中其他的容器都是用 pause 的網絡

容器存儲接口CSI

背景

Kubernetes原生支持一些存儲類型的 PV,如 iSCSI、NFS、CephFS 等等,這些 in-tree 類型的存儲代碼放在 Kubernetes 代碼倉庫中。這里帶來的問題是 Kubernetes 代碼與三方存儲廠商的代碼強耦合

  • 更改 in-tree 類型的存儲代碼,用戶必須更新 Kubernetes組件,成本較高
  • in-tree 存儲代碼中的 bug 會引發 Kubernetes組件不穩定
  • Kubernetes社區需要負責維護及測試 in-tree 類型的存儲功能
  • in-tree 存儲插件享有與 Kubernetes核心組件同等的特權,存在安全隱患
  • 三方存儲開發者必須遵循 KKubernetes8s 社區的規則開發 in-tree 類型存儲代碼

CSI 容器存儲接口標准的出現解決了上述問題,將三方存儲代碼與 Kubernetes代碼解耦,使得三方存儲廠商研發人員只需實現 CSI 接口(無需關注容器平台是 Kubernetes還是 Swarm 等)。

簡介

容器存儲接口(Container Storage Interface),簡稱 CSI,CSI 試圖建立一個行業標准接口的規范,借助 CSI 容器編排系統(CO)可以將任意存儲系統暴露給自己的容器工作負載。

csi 卷類型是一種 out-tree(即跟其它存儲插件在同一個代碼路徑下,隨 Kubernetes 的代碼同時編譯的) 的 CSI 卷插件,用於 Pod 與在同一節點上運行的外部 CSI 卷驅動程序交互。部署 CSI 兼容卷驅動后,用戶可以使用 csi 作為卷類型來掛載驅動提供的存儲。

CSI 持久化卷支持是在 Kubernetes v1.9 中引入的,作為一個 alpha 特性,必須由集群管理員明確啟用。換句話說,集群管理員需要在 apiserver、controller-manager 和 kubelet 組件的 “--feature-gates =” 標志中加上 “CSIPersistentVolume = true”。

CSI 持久化卷具有以下字段可供用戶指定:

  • driver:一個字符串值,指定要使用的卷驅動程序的名稱。必須少於 63 個字符,並以一個字符開頭。驅動程序名稱可以包含 “。”、“ - ”、“_” 或數字。
  • volumeHandle:一個字符串值,唯一標識從 CSI 卷插件的 CreateVolume 調用返回的卷名。隨后在卷驅動程序的所有后續調用中使用卷句柄來引用該卷。
  • readOnly:一個可選的布爾值,指示卷是否被發布為只讀。默認是 false。

參考

https://www.jianshu.com/p/c7748893ab00

https://jimmysong.io/kubernetes-handbook/concepts/cni.html

https://www.kubernetes.org.cn/9127.html


免責聲明!

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



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