天天看點

Kubernetes容器運作時(CRI)簡介

Kubernetes節點的底層由一個叫做“容器運作時”的軟體進行支撐,它負責比如啟停容器這樣的事情。最廣為人知的容器運作時當屬Docker,但它不是唯一的。事實上,容器運作時這個領域發展迅速。為了使Kubernetes的擴充變得更容易,我們一直在打磨支援容器運作時的K8s插件API:容器運作時接口(Container Runtime Interface, CRI)。

CRI是什麼?

每種容器運作時各有所長,許多使用者都希望Kubernetes支援更多的運作時。在Kubernetes 1.5釋出版裡,我們引入了CRI–一個能讓kubelet無需編譯就可以支援多種容器運作時的插件接口。CRI包含了一組protocol buffers,gRPC API,相關的庫,以及在活躍開發下的額外規範和工具。CRI目前是Alpha版本。

支援可替換的容器運作時在Kubernetes中概念中并非首次。在1.3釋出版裡,我們介紹了rktnetes項目,它可以讓rkt容器引擎作為Docker容器運作時的一個備選。然而,不管是Docker還是Rkt都需要通過内部、不太穩定的接口直接內建到kubelet的源碼中。這樣的內建過程要求十分熟悉kubelet内部原理,并且還會在Kubernetes社群引發巨大的維護反響。這些因素都在為容器運作時的初期造成了巨大的困難。我們通過提供一個清晰定義的抽象層消除了這些障礙,開發者可以專注于建構他們的容器運作時。這是很小的一步,但對于真正提供可插拔的容器運作時和建構一個更健康的生态系統卻意義非凡。

CRI總覽

Kubelet與容器運作時通信(或者是CRI插件填充了容器運作時)時,Kubelet就像是用戶端,而CRI插件就像對應的伺服器。它們之間可以通過Unix 套接字或者gRPC架構進行通信。

Kubernetes容器運作時(CRI)簡介

protocol buffers API包含了兩個gRPC服務:ImageService和RuntimeService。ImageService提供了從鏡像倉庫拉取、檢視、和移除鏡像的RPC。RuntimeSerivce包含了Pods和容器生命周期管理的RPC,以及跟容器互動的調用(exec/attach/port-forward)。一個單塊的容器運作時能夠管理鏡像和容器(例如:Docker和Rkt),并且通過同一個套接字同時提供這兩種服務。這個套接字可以在Kubelet裡通過辨別–container-runtime-endpoint和–image-service-endpoint進行設定。

生命周期管理

對于Pod和容器的生命周期管理,CRI提供了下面的機制:

service RuntimeService {
// Sandbox operations.
rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}
rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}
rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}
rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}
rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}
// Container operations.
rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}
rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}
rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}
rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}
rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}
rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
…
}
           

在資源受限的隔離環境裡的一組應用容器組成一個Pod。在CRI,這個環境被稱為PodSandbox。我們故意留下一些空間,讓容器運作時根據它們内部不同的原理來産生不同的PodSandbox。對于基于hypervisor的運作時,PodSandbox可能代表的是虛拟機。對于其他的,比如Docker,它可能是Linux命名空間。這個PodSandbox一定遵循着Pod的資源定義。在v1alpha1版API裡,kubelet将建立pod級的cgroup限制下的一組程序,并傳遞給容器運作時,由此實作。

在Pod啟動前,kubelet調用RuntimeService.RunPodSandbox來建立環境,包括為Pod設定網絡(例如:配置設定IP)等。當PodSandbox啟動後,就可以分别建立/啟動/停止/移除獨立的容器。為了删除Pod,kubelet會在停止和移除所有容器前先停止和移除PodSandbox。

Kubelet負責通過RPC來進行容器生命周期的管理,測試容器生命周期鈎子和健康/可讀性檢查,同時為Pod提供重新開機政策。

容器為中心的接口

Kubernetes擁有對Pod資源的聲明式API。我們認為一個可能的設計是為了使CRI能夠在它的抽象裡重用這個聲明式的Pod對象,給容器運作時實作和測試達到期望狀态的邏輯的自由。這會極大地簡化API,并讓CRI可以相容更廣泛的運作時。在早期的設計階段我們讨論過這個方法,但由于幾個原因否決了它。首先,Kubelet有許多Pod級的特性和特定的技術(比如crash-loop backoff邏輯),這會成為所有運作時重新實作時的巨大負擔。其次,越來越重要的是,Pod的定義更新快速。隻要kubelet直接管理容器,那麼許多新特性(比如init container)不需要底層容器運作時做任何改變。CRI包含了一個必要的容器級接口,這樣運作時就可以共享這些特性,擁有更快的開發速度。當然這并不意味着我們偏離了”level triggered”哲學。kubelet負責保證明際狀态到期望狀态的變化。

互動請求

service RuntimeService {
…
// ExecSync runs a command in a container synchronously.
rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {} // Exec prepares a streaming endpoint to execute a command in the container.
rpc Exec(ExecRequest) returns (ExecResponse) {} // Attach prepares a streaming endpoint to attach to a running container.
rpc Attach(AttachRequest) returns (AttachResponse) {} // PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}
…
}
           

Kubernetes提供了一些使用者可以與Pod及其内部容器進行互動的特性(例如kubectl exec/attach/port-forward)。Kubelet現在通過調用容器原生的方法或使用節點上可用的工具(例如nsenter和socat)來支援這些特性。在節點上使用這些工具不是一個可移植的好辦法,因為這些工具的大部分假定Pod是通過Linux命名空間進行隔離的。在CRI,我們顯式定義了這些調用,允許特定的運作時實作。

另外一個潛在的問題是,kubelet如今的實作是kubelet處理所有的流式連接配接請求。是以這會給節點的網絡流量帶來瓶頸。在設計CRI的時候,我們采納了這個回報,支援運作時防範中間人。容器運作時可以啟動一個對應請求的單獨的流伺服器(甚至可能為Pod審計資源使用),并且将位址傳回給Kubelet。Kubelet然後将這個資訊再傳回給Kubernetes API Server,它會打開直接與運作時提供的伺服器相連的流連接配接,并将它跟用戶端連通。

這篇博文并沒有包含CRI的全部。更多細節請參考設計文檔和建議。

目前狀态

盡管CRI還處于早期階段,已經有不少使用CRI來內建容器運作時的項目在開發中。下面是一些列子:

  • cri-o: OCI運作時
  • rktlt: rkt容器運作時
  • frakti: 基于hypervisor的容器運作時
  • docker CRI shim

如果你對上面列出的這些運作時感興趣,你可以關注這些獨立的github倉庫,擷取最新的進展和說明。

對內建新的容器運作時感興趣的開發者,請參考開發指南了解這些API的限制和問題。我們會從早期的開發者那裡積極采納回報來提升API。開發者可能會遇到API的一些意外改動(畢竟是Alpha版)。

內建CRI-Docker

Kubelet至今還沒有預設使用CRI,但我們仍在積極地推動。第一步就是使用CRI将Docker重新內建到kubelet裡。在1.5釋出版裡,我們擴充了Kubelet來支援CRI,并且為Docker添加了内置的CRI插件。kubelet啟動一個gRPC伺服器,代表Docker。如果嘗試新的kubelet-CRI-Docker內建,你可能僅僅會使用–feature-gates=StreamingProxyRedirects=true來打開Kubernetes API Server,以啟動新的流重定向特性,并且通過設定kubelet的辨別–experimental-cri=true來啟動。

除了一些欠缺的特性,新的內建可以一直通過主要的端到端測試。我們計劃盡快擴充測試的覆寫率,并且鼓勵社群反應關于這個轉化的任何問題。

Minikube與CRI

如果你想要嘗試新的內建,但是沒有時間在雲上啟動一個新的測試叢集。minikube是一個很棒的工具,你可以迅速在本地搭建叢集。在你開始前,請閱讀說明并下載下傳安裝Minikube:

1.檢查可用的Kubernetes版本,選擇可用的最新1.5.x版本。我們使用v1.5.0-beta.1作為示例。

2.通過内建的Docker CRI內建啟動一個Minikube叢集。 –extra-config=kubelet.EnableCRI=true開啟了kubelet的CRI實作。–network-plugin=kubenet和–extra-

config=kubelet.PodCIDR=10.180.1.0/24設定Kubenet網絡插件,保證配置設定給節點的PodCIDR。–iso-url設定本地節點啟動的minikube iso鏡像。

3.檢查minikube日志,檢視啟動CRI

$ minikube logs | grep EnableCRI
 I1209 ::  localkube.go:] Setting EnableCRI to true on kubelet.
           

4.建立pod,檢查它的狀态。你應該可以看見一個”SandboxReceived”事件,證明Kubelet在使用CRI

$ kubectl run foo --image=gcr.io/google_containers/pause-amd64:
 deployment "foo" created
 $ kubectl describe pod foo
 ...
 ... From Type Reason Message
 ... ----------------- ----- --------------- -----------------------------
 ...{default-scheduler } Normal Scheduled Successfully assigned foo--v1op9 to minikube
 ...{kubelet minikube} Normal SandboxReceived Pod sandbox received, it will be created.
           

注意:kubectl attach/exec/port-forward還不能在minikube的CRI中運作。但這會在新版本的minikube中得到改善。

社群

CRI的開發很活躍,由Kubernetes SIG-Node興趣小組維護。我們熱切地期盼你的回複。加入社群吧:

  • 通過Github回報問題和特性請求
  • 加入Slack上的#sig-node頻道
  • 參與SIG-Node郵件清單
  • 關注我們Twitter @Kubernetesio的後續更新
本文由立堯翻譯,由kuberneters中文社群授權轉載。
責編:魏偉,報道和投稿請加聯系郵箱[email protected],另外,歡迎加入CSDN 容器技術交流群,和大牛侃技術,搜尋微信号“k15751091376”,備注姓名、公司和職位。

繼續閱讀