虛擬機容器Kata架構

為促進社區發展,運維派尋求戰略合作、贊助、投資,請聯系微信:helloywp

容器(Container)是一種輕量級虛擬化技術,“輕量”主要是因為容器與傳統虛擬機比較,是內核共享的,所以啟動快、資源占用小。隨著虛擬化技術發展,Docker與Kubernets逐步成為應用打包與部署的標準,以及公有云租賃模式推廣,出現了“虛擬機容器”這種形態。

虛擬機容器首先是一臺虛擬機,由于Docker鏡像打包與分發方面的優勢(分層構建、Build Once Run Anywhere),虛擬機容器也兼容Docker鏡像;并提供了OCI規范的 runtime ,且適配了Kubernetes調度管理API,能夠被Kubernetes調度,因此被稱為虛擬機容器。

虛擬機容器主要使用場景是在公有云模式下,云服務廠商提供無服務器計算服務(函數服務,無服務器容器),這種模式下租戶只租一個容器或進程,不同租戶的容器/進程可能運行到同一臺機器上,傳統的內核共享模式在這種場景下安全風險太高,虛擬機容器能夠做到內核獨占,適合這種場景。

Kata 項目介紹

Kata?源自 Intel 的開源項目?Hyper.sh?,當前在 Openstack 軟件基金會(OSF)下治理。

Kata南向虛擬化實現技術可插拔,當前支持QEMU/NEMU,在最新的1.5版本里支持Firecrack;北向支持與Docker的Containerd對接,并可以被Kubernetes編排,接入Docker與Kubernetes生態。

Kata 架構概覽 (基于1.5版本)

Kata container runtime and shimv2

Kata容器項目主要由容器運行時( kata container runtime )與一個兼容CRI接口的 shim 部件組成。

Kata container runtime?符合?OCI?運行時規范?因此能夠被Docker引擎管理,作為Docker引擎的一個runtime插件。 Kata container runtime 還基于Containerd的CRI插件CRI-O實現了Kubernetes的CRI規范,因此,使用者可以在Docker默認的runtime?runc?與 kata container runtime (runv) 之間平滑切換,上層組件不感知差異。

Kata容器的另外一個組成部分是?containerd-shim-kata-v2?,簡稱為 shimv2 , shimv2 提供了了 Containerd Runtime V2 (Shim API) 的 Kata 實現,從而使得 Kubernetes 場景下能夠實現每個 Pod 一個 shim 進程 – shimv2 ;而在此之前,一個Pod需要一個2個shim( containrd-shim, kata-shim ),如果 Pod Sandbox沒有暴露 VSOCK 則還需要一個 kata-proxy 。

agent 與 kata-proxy

Kata容器運行在虛擬機沙箱內,每個虛擬機內運行一個Agent,Agent負責運行container。Agent同時提供gRPC接口,通過QEMU的VIRTIO serial或VSOCK接口向HOST主機暴露接口,主機上的 kata-runtime 使用gRPC協議與Agent通信,向虛擬機內的容器發送指令,I/O流也通過此通道管理。 如果是使用 VIRTIO serial 的方式暴露接口到Host主機,那么還需要在主機上部署一個 kata-proxy 負責轉發指令到Agent。

容器進程管理

在Host主機上,每個容器進程的清理是由更上層的進程管理器完成的,在Docker containerd的實現里,進程管理器是 containerd-shim ;在CRI-O的實現里是common。

在Kata容器場景下,容器進程運行在虛擬機內,Host主機上的進程管理器不能直接管理到容器進程,Kata容器項目通過 kata-shim 來解決此問題。kata-shim 運行在Host主機上,介于容器進程管理器與kata-proxy之間,kata-shim 將來自Host主機的信號量、stdin 轉發到虛擬機內的容器進程上,并將虛擬機容器內的stdout與stderr轉發到Host主機上的容器進程管理器。

kata-runtime 為每個容器進程創建一個 kata-shim 守護進程,為每個通過OCI命令連接到容器進程內部執行用戶命令的操作創建一個 kata-shim 守護進程(如docker exec)。

在 Kata1.5 版本,shimv2 收編 kata-runtime, kata-shim, kata-proxy 到 shimv2 進程中。

虛擬化

Kata 架構上能夠支持多種虛擬化實現,在 Kata1.0 版本,支持?QEMU/KVM?虛擬化。在?Kata1.5?版本,支持?AWS Firecracker?極輕量的虛擬機。

QEMU/KVM

根據Host主機架構,Kata容器支持多種主機類型,比如 x86 上的?pc?與?q35?,ARM 上的?virt?, IBM Power System 上的?pseries?。默認的Kata容器主機類型是?pc?,默認主機類型可以通過配置修改。

Kata容器使用下面的 QEMU 特性來管理資源配額、縮短啟動時間、減少內存占用:

  • 機器加速器
  • 熱插拔設備

機器加速器

機器加速器是與特定服務器架構相關的,機器加速器能夠提升性能并開啟某些特性。下面這些機器加速器在Kata容器中使用。

  • NVDIMM: x86平臺的機器加速器,僅支持?pc?與?q35?機器類型。nvdimm 用來以持久化內存(persistent memory)方式提供虛擬機的根文件系統。

雖然Kata容器能夠支持大多數QEMU發行版本,但是考慮到Kata容器的啟動時間、內存占用、IO性能因素,Kata容器使用一個針對這些因素專門優化過的QEMU版本?qemu-lite?,并增加了一些自定義的機器加速器,這些自定義加速器在 QEMU Upstream 版本中不可用。

  • nofw: x86平臺的機器加速器,僅支持?pc?與?q35?機器類型。?nofw?用來啟動 ELF 格式的系統內核,但是可以跳過 BIOS 與固件自檢 (BIOS/firmware) ,這個加速器可以顯著提升虛擬機啟動速度。
  • static-prt: x86平臺的機器加速器,僅支持?pc?與?q35?機器類型。?static-prt?用來減少虛擬機ACPI(Advanced Configuration and Power Management Interface)的解釋負擔。

熱插拔設備

Kata容器虛擬機初始以最小的資源啟動,為了提升啟動速度,在啟動過程中,設備可以熱插到虛擬機上。如,容器定義了cpu資源,可以通過熱插的方式加到虛擬機上。Kata容器虛擬機支持如下熱插設備:

  • Virtio block
  • Virtio SCSI
  • VFIO
  • CPU

Kernel 與 Image

虛擬機內核

虛擬機內核在虛擬機啟動時候被加載,Kata容器提供的虛擬機內核針對啟動時間與內存占用做了優化。

虛擬機鏡像

Kata 容器支持?initrd?與?root filesystem?兩種虛擬機鏡像。

root filesystem

Kata 容器提供的默認打包好的 root filesystem 鏡像,這種鏡像也被稱為 “mini O/S”,是基于?Clear Linux?優化的,提供最小的運行環境與高度優化的啟動路徑。 鏡像中只有Kata Agent與systemd兩個進程,用戶的工作負載被打包到docker鏡像,在虛擬機內通過libcontainer庫,以runc方式運行起來。

舉例,當用戶執行?docker run -it ubuntu date?命令時,流程如下:

  • 虛擬化層加載虛擬機內核,虛擬機內核加載虛擬機鏡像。
  • systemd?啟動虛擬機運行環境(mini-OS Context),并啟動 kata-agent 進程(在同一個Context)
  • kata-agent 創建一個獨立的context,運行用戶指定的命令(例子中是 date )
  • kata-agent 準備 ubuntu 的運行環境并運行 date 命令

initrd

待補充。

Agent

kata-agent 是一個運行在虛擬機中的進程管理虛擬機中的容器進程。

kata-agent 的最小運行單元是沙箱,一個 kata-agent 沙箱是一個由一些列namespace(NS, UTS, IPC, PID)隔離出來的。 kata-runtime 能夠在一個虛擬機內運行多個容器進程以支持POD內多個container模式。

kata-agent 使用gRPC協議與Kata其他組件通信,在gRPC同一個URL上還運行了一個?yamux?服務。

kata-agent 使用?libcontainer?管理容器生命周期,復用了?runc?的大部分代碼。

Runtime

kata-runtime 是一個符合OCI規范的容器運行時,負責處理OCI運行時規范中的所有命令,并啟動 kata-shim 進程。

關鍵的OCI命令實現

create

kata-runtime 處理 OCI?create?命令步驟:

  1. 創建虛擬機與shim進程的network namespace。
  2. 執行 pre-start hook ,回調中負責創建?veth?網絡設備,用于連接主機網絡與新創建的network namespace。
  3. 掃描新創建的network namespace,在其中的veth設備上創建一個macvtab設備。
  4. 在新的network namespace中創建虛擬機,并將tab設備傳遞給虛擬機。
  5. 等待虛擬機啟動完成。
  6. 啟動kata-proxy,kata-proxy負責代理所有發送給虛擬機的請求,每個虛擬機一個kata-proxy進程。
  7. 調用kata-agent接口配置虛擬機內的沙箱。
  8. 調用kata-agent接口創建容器,使用kata-runtime提供的默認的OCI配置文件?config.json?。
  9. 啟動kata-shim進程,kata-shim連接到kata-agent的gRPC socket端口。kata-shim會創建幾個Go routine阻塞式調用?ReadStdout(),?ReadStderr(),?WaitProcess()?。ReadStdout(),?ReadStderr()?以死循環方式執行直到虛擬機內的容器進程中止。?WaitProcess()?返回虛擬機內容器進程的 exit code。 kata-shim運行在虛擬機的network namespace中,通過kata-shim進程可以找到創建了哪些namespace。啟動 kata-shim 進程還會創建一個新的PID namespace,對應到同一個container的所有kata-shim進程都在同一個PID namespace,這樣當容器進程終止時候很容器將所有kata-shim進程終止掉。

此時容器進程在虛擬機內部運行起來了,在Host主機上對應到kata-shim進程。

start

傳統容器的 start 會在容器namespace中啟動容器進程。Kata容器中,?start?會在虛擬機內啟動容器的工作負載,步驟如下;

  1. 調用kata-agent接口在虛擬機內啟動容器負載命令。如,容器內負載命令為?top?,kata-shim 進程的?ReadStdout()?會讀取到 top 的輸出,?WaitProcess()?會一直等待到 top 命令結束。
  2. 執行 post-start 回調,當前 post-start 實現為空。

exec

OCI 的 exec 命令允許在已有的容器中執行命令。在Kata容器中,?exec?執行步驟如下:

  1. 調用kata-agent接口在已有容器中執行命令。
  2. 一個新的kata-shim進程會創建出來,被放置到已有容器對應的kata-shim所在的PID namespace中。

此時通過 exec 命令啟動的新的容器負載已經在虛擬機內運行,共享已有容器的namespace (uts, pid, mnt, ipc) 。

kill

OCI kill 命令通過發送 UNIX 信號,如?SIGTERM,?SIGKILL?,來終止容器進程。在Kata容器中,?kill?命令會終止虛擬機內的容器進程與虛擬機。

  1. 調用kata-agent接口請求kill容器進程。
  2. 等待 kata-shim 進程退出。
  3. 調用kata-agent接口請求強制kill容器進程(發送 KILL 信號量給容器進程),如果 kata-shim 進程在超時時間內未退出。
  4. 等待 kata-shim 進程退出,如果等待超時則報錯。
  5. 調用kata-agent接口刪除虛擬機內的容器配置。
  6. 調用kata-agent接口刪除虛擬機內的沙箱配置。
  7. 停止虛擬機。
  8. 刪除network namespace中的網絡配置,刪除network namespace。
  9. 執行 post-stop 回調。

delete

delete?指令刪除容器所有相關的資源,正在運行中的容器不能不刪除,除非通過?--force?指令強制刪除。

如果虛擬機內的沙箱未停止,但是沙箱內的容器進程已經推出,kata-runtime會先執行一次kill流程,之后如果沙箱已經停止,kata-runtime執行如下動作:

  1. 刪除容器相關資源:目錄?/var/{lib,run}/virtcontainers/sandboxes/<sandboxID>/<containerID>?下的所有文件。
  2. 刪除沙箱:目錄?/var/{lib,run}/virtcontainers/sandboxes/<sandboxID>?下的所有文件。

此時,所有容器相關內容都已經在Host主機上被刪除,沒有任何相關進程在運行。

state

state 返回容器的運行狀態。在Kata容器中,state?需要檢測容器進程是否在運行,通過檢查對應容器進程的 kata-shim 進程的狀態。

  1. 通過存儲在磁盤上的信息獲得容器狀態(需要澄清)。
  2. 檢查kata-shim進程。
  3. 如果kata-shim進程不存在,但是磁盤上的容器狀態文件還是ready或running,那么意味著在得到容器返回狀態之前,容器進程已經被正常停止了。

Proxy

Host主機與虛擬機通信可以通過?virtio-serial?或?virtio-socket?,?virtio-socket?需要內核版本 4.8 以上。默認使用?virtio-serial?。

虛擬機內可能運行多個容器進程,在使用?virtio-serial?場景下,需要Host主機上運行 multiplexed 與 demultiplexed 進程;使用?virtio-socket?則不需要。

kata-proxy?進程提供代理訪問虛擬機內的?kata-agent?,對應到多個 kata-shim 進程與 kata-runtime 客戶端。?kata-proxy主要功能是負責代理IO流與信號量到?kata-agent?。?kata-proxy?通過Unix domain socket方式連接?kata-agent?。

Shim

容器進程回收器(reaper),如Docker的?containerd-shim?或 CRI-O 的?common?,其設計假設是基于能夠監控并回收實際的容器進程。在Kata容器中,由于容器進程運行在虛擬機中,Host主機上的容器進程回收器不能直接監控到虛擬機內的容器進程,至多能看到QEMU進程,這個是遠遠不夠的。Kata容器中的kata-shim進程是主機上對應到虛擬機內容器進程的映射,因此kata-shim進程需要處理容器進程的I/O流并負責轉發信號到容器進程。

  1. kata-shim 通過Unix domain socket連接到kata-proxy,socket URL是由kata-runtime在啟動kata-shim進程時候傳遞給kata-shim的,一并傳遞的參數還有containerID與execID,containerID與execID用來標識虛擬機內的容器進程。
  2. 轉發來自容器進程回收器的標準輸入流,通過kata-proxy的gRPC的?WriteStdin?API。
  3. 從容器進程讀取標準輸出與錯誤輸出。
  4. 轉發來自容器進程回收器的信號,通過kata-proxy的?SignalProcessRequest?API。
  5. 監控終端的變化并轉發,通過kata-proxy的?TtyWinResize?API。

Networking

容器進程一般被放置在獨立的network namespace中,在容器生命周期的某個點,容器引擎會創建network namespace并將容器進程加入到network namespace中。容器進程網絡與主機Host網絡隔離。 在實現技術上,通常使用veth技術,veth的兩端分別放置到容器的network namespace與主機Host的network namespace中。這種方式是以namespace為中心的,而一些虛擬化技場景下不支持veth,特別是QEMU,這種情況下使用TAP技術替代。

為了消除虛擬化場景下網絡隔離與容器場景下網絡隔離的不兼容,kata-runtime使用veth+tab(MACVLAN)方式實現網絡隔離與互通。

  • 主機Host網絡與容器網絡通過network namespace隔離,使用veth互通。
  • 容器網絡network namespace內的veth網絡設備上創建MACVLAN(實際上為MACVTAP)設備。
  • 在創建虛擬機(QEMU)時候,將TAP網絡設備作為虛擬機網卡。
  • 虛擬機內的容器進程使用虛擬機網卡作為主網卡與外部通信,虛擬機內部的多個POD共享虛擬機網絡,不再隔離。

Kata容器支持?CNM?與?CNI?兩種容器網絡標準。

CNM

CNM lifecycle

  1. RequestPool
  2. CreateNetwork
  3. RequestAddress
  4. CreateEndPoint
  5. CreateContainer
  6. Create config.json
  7. Create PID and network namespace
  8. ProcessExternalKey
  9. JoinEndPoint
  10. LaunchContainer
  11. Launch
  12. Run container

CNM 網絡配置過程

  1. 讀取?config.json
  2. 創建network namespace: netns
  3. 回調?prestart?鉤子(在netns內)
  4. 掃描netns命名空間下的,由prestart回調創建的網絡接口
  5. 創建bridge/tap并通過veth與主機連接

網絡熱插拔

Kata容器開發了一套命令與api支持添加/刪除/查看 guest網絡。下面流程圖展示了Kata容器熱插拔的流程。

存儲

Kata容器的虛擬機與Host通過9pfs共享文件,對于虛擬機內的容器,不建議使用主流的overlay2存儲驅動,overlay2存儲驅動是基于文件系統的。

Kubernetes 集成

Kubernetes目前是容器編排的事實標準,Kubernetes為了解耦Kubelet與各種容器runtime,抽象了CRI(Container Runtime Interface)接口規范,Kubelet相當于是一個CRI客戶端,不同的CRI實現提供gRPC接口與Kubelet對接,接入到Kubernetes生態。當前基于OCI標準容器提供的CRI接口實現有CRI-OContainerd CRI Plugin

Kata容器的runtime是CRI-O與Containerd CRI Plugin的官方runtime之一,因此Kata容器可以很容易的集成到Kubernetes生態中。

但是由于Kubernetes最小調度單元是Pod而非容器進程,一個Pod可以有多個容器進程,而Kata容器又是將容器進程跑在虛擬機內的,因此Kata容器需要Kubernetes在創建容器進程傳遞更多信息,用來告知Kata runtime是要創建一個新的虛擬機,還是在已有虛擬機內啟動容器進程。

Containerd CRI Plugin 集成kata-runtime

在Kata1.5版本,對應containerd1.2.0,通過shimv2實現了Kata runtime與Kubernetes集成,具體指導參考鏈接。CRI-O的實現也正在開發中,跟蹤此Issue

CRI-O 集成Kata-runtime

OCI annotations

為了讓kata-runtime(或者任何虛擬機容器的runtime)區分是要創建一個虛擬機還是僅在虛擬機內創建容器進程,CRI-O在OCI配置文件(config.json)中增加了一個annotation來告知這個區分給kata-runtime。

在執行runtime之前,CRI-O會增加一個io.kubernetes.cri-o.ContainerType的annotation,這個注解由Kubelet生成,取值范圍是sandbox,?container,kata-runtime將sandbox對應到創建虛擬機(新Pod),container對應到在已有Pod中創建容器進程。

containerType, err := ociSpec.ContainerType()
if err != nil {
    return err
}

handleFactory(ctx, runtimeConfig)

disableOutput := noNeedForOutput(detach, ociSpec.Process.Terminal)

var process vc.Process
switch containerType {
case vc.PodSandbox:
    process, err = createSandbox(ctx, ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput, systemdCgroup)
    if err != nil {
        return err
    }
case vc.PodContainer:
    process, err = createContainer(ctx, ociSpec, containerID, bundlePath, console, disableOutput)
    if err != nil {
        return err
    }
}

虛擬機容器與namespace隔離容器混合管理

一個有趣的演進是在一個Kubernetes集群中混合管理虛擬機容器與namespace隔離的容器。 現在Kubernetes集群運維人員可以對工作負載打trusted,?untrusted標簽,trusted標簽表示工作負載是安全的,untrusted表示工作負載存在潛在風險,在支持kata容器的Kubernetes集群中,會自動根據標簽,將trusted工作負載以runc方式運行,untrusted工作負載以runv(kata-runtime)方式運行。

CRI-O默認行為是認為所有的工作負載在都是trusted,除非設置了注解io.kubernetes.cri-o.TrustedSandbox=false,CRI-O默認的trust配置在configuration.toml中。

綜合來看,CRI-O是選擇runc還是runv,由Pod的Privileged參數,CRI-O trust配置trusted/untrustedio.kubernetes.cri-o.TrustedSandbox注解三個值確定。 如果Pod是Privileged,那么只能是runc。如果Pod不是Privileged,那么runtime的選擇方式如下:

io.kubernetes.cri-o.TrustedSandbox 未設置 io.kubernetes.cri-o.TrustedSandbox=true io.kubernetes.cri-o.TrustedSandbox=false
默認的CRI-O turst設置: trusted runc runc runv(kata-runtime)
默認的CRI-O turst設置: untrusted runv(kata-runtime) runv(kata-runtime) runv(kata-runtime)

原文鏈接

虛擬機容器Kata架構

關注公眾號獲得更多云最佳實踐

網友評論comments

發表評論

電子郵件地址不會被公開。 必填項已用*標注

暫無評論

Copyright ? 2012-2019 YUNWEIPAI.COM - 運維派 - 粵ICP備14090526號-3
掃二維碼
掃二維碼
返回頂部
街机电玩捕鱼抢红包 试机号后杀一码 排列三一百期开奖助手 领先团队彩票是真的吗 刘伯温一码中特 安徽快3豹子遗漏统计表 广东时时系统 北京赛pk10号 PT电子游戏软件 香港赛马会图 天津时时走势图