1. 程式人生 > >Kubernetes(k8s)容器執行時(CRI)簡介_Kubernetes中文社群

Kubernetes(k8s)容器執行時(CRI)簡介_Kubernetes中文社群

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框架進行通訊。

20161222091952

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作為示例。

$ minikube get-k8s-versions

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映象。

$ minikube start --kubernetes-version=v1.5.0-beta.1 --extra-config=kubelet.EnableCRI=true --network-plugin=kubenet --extra-config=kubelet.PodCIDR=10.180.1.0/24 --iso-url=http://storage.googleapis.com/minikube/iso/buildroot/minikube-v0.0.6.iso

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

$ minikube logs | grep EnableCRI
 I1209 01:48:51.150789 3226 localkube.go:116] Setting EnableCRI to true on kubelet.

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

$ kubectl run foo --image=gcr.io/google_containers/pause-amd64:3.0
 deployment "foo" created
 $ kubectl describe pod foo
 ...
 ... From Type Reason Message
 ... ----------------- ----- --------------- -----------------------------
 ...{default-scheduler } Normal Scheduled Successfully assigned foo-141968229-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的後續更新
20161219151628

譯者微信公眾號