Kata

Kata源自希腊文Καταπίστευμα(ka-ta-PI-stev-ma),原意是值得信任的人,kata container正是解容器安全的问题而诞生的。传统的容器是基于namespace和cgroup进行隔离,在带来轻量简洁的同时,也带来了安全的隐患。事实上容器虽然提供一个与系统中的其它进程资源相隔离的执行环境,但是与宿主机系统是共享内核的,一旦容器里的应用逃逸到内核,后果不堪设想,尤其是在多租户的场景下。Kata就是在这样的背景下应运而生,kata很好的权衡了传统虚拟机的隔离性、安全性与容器的简洁、轻量。这一点和firecracker很相似,都是轻量的虚拟机。但是他们的本质的区别在于:kata虽然是基于虚机,但是其表现的却跟容器是一样的,可以像使用容器一样使用kata;而firecracker虽然具备容器的轻量、极简性,但是其依然是虚机,一种比QEMU更轻量的VMM,暂时不能兼容容器生态。
Kata的基本原理是,为每一个容器单独开一个虚机(如果是k8s下作为runtime,则是一个pod对应一个虚机而不是容器),具有独立的内核,这样交付的容器就具备了虚机级别的隔离和安全性。kata的原理图如下所示:

kata1.png

Kata container作为OCI标准的成员之一,其kata-runtime也是兼容OCI标准,和runc处在同一个层级,对安全和隔离性要求高的场景,可以从Docker或者k8s默认的runtime(比如runc)切到kata-runtime。

CRI

CRI基本原理

早期的k8s使用docker作为默认的runtime,后来又加入rkt,每加入一种新运行时,k8s都要修改接口来关联新的容器运行时。随着越来越多的容器运行时想加入k8s运行时,而且不同的容器的实现和功能差异很大,比如docker已经再不是一个单纯的容器运行时,这时候亟需一套标准来定义k8s支持的运行时。这套标准就是CRI(Container RunTime Interface)。k8s(甚至k8s用户)不再关心底层的容器运行时,kubelet只感知到CRI server,而CRI server只需遵循CRI标准实现对应的runtime的标准化的接口。

CRI接口具体的定义细节在k8s的kubelet/apis/cri/runtime/v1alpha2/api.proto中:

service RuntimeService {// Version returns the runtime name, runtime version, and runtime API version.rpc Version(VersionRequest) returns (VersionResponse) {}// RunPodSandbox creates and starts a pod-level sandbox. Runtimes must ensure// the sandbox is in the ready state on success.rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}// CreateContainer creates a new container in specified PodSandboxrpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}// Exec prepares a streaming endpoint to execute a command in the container.rpc Exec(ExecRequest) returns (ExecResponse) {}// ContainerStats returns stats of the container. If the container does not// exist, the call returns an error.rpc ContainerStats(ContainerStatsRequest) returns (ContainerStatsResponse) {}// ListContainerStats returns stats of all running containers// Status returns the status of the runtime.rpc Status(StatusRequest) returns (StatusResponse) {}...
}// ImageService defines the public APIs for managing images.
service ImageService {// ListImages lists existing images.rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}// ImageStatus returns the status of the image. If the image is not// present, returns a response with ImageStatusResponse.Image set to// nil.rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}// PullImage pulls an image with authentication config.rpc PullImage(PullImageRequest) returns (PullImageResponse) {}// RemoveImage removes the image.// This call is idempotent, and must not return an error if the image has// already been removed.rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}// ImageFSInfo returns information of the filesystem that is used to store images.rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}
}

可以看到CRI server包括 RuntimeService 和 ImageService 两个服务,均为gRPC server。ImageService负责镜像的管理,比如查询、拉取、删除镜像等;RuntimeService负责四大块:PodSandbox(Pause容器或者一台虚拟机,取决于具体的runtime实现),Container,Streaming API(exec),状态查询接口等。

下面分别以k8s create pod和stream API exec来分析CRI工作的流程:

kata-2.png

CRI分类

CRI的第一个实现就是k8s自己提供的针对Docker运行时的dockerShim,也是目前k8s使用docker的标准方式,已经集成在k8s的源码中。如今CRI的众多实现中除了dockershim外,比较具有代表性的还有有CRI-containerd, CRI-O以及frakti。rkt作为k8s最早支持的运行时之一,现在也开始转向标准的CRI实现rktlet,这是k8s未来使用rkt的标准方式。该项目的目标就是像现在的dockerShim一样,而社区貌似并不活跃。目前主流的几种CRI实现的生产容器的流程图如下所示:

kata-3.png

其中dockershim、CRI-containerd、CRI-O属于基于OCI的CRI,dockershim目前不支持kata runtime,其他的两种CRI-containerd、CRI-O均支持runc和kata runtime。frakti是一种特殊的CRI实现,它不依赖于任何runtime,而是可以直接使用kata提供的硬件虚拟化API库来实现CRI的标准接口,即直接开VM然后runv启动pod和容器。frakti虽然相比于其他的CRI实现复杂、接口偏重,但是实现的灵活性更强。

Dockershim

k8s开始支持CRI之后,k8s便不再以之前的方式依赖docker,而是采用标准的CRI的方式来使用docker运行时。Dockershim便是k8s对CRI的一个标准实现,已经集成在了k8s的源码中,这里重点看下k8s中dockershim的实现。


3c7fc903db49157fccaefd1401bf6812.png

RuntimeService client实现:

首先,dockershim的gRPC client的实现是在kubelet/remote/remote_runtime.go中:

// RemoteRuntimeService is a gRPC implementation of internalapi.RuntimeService.
type RemoteRuntimeService struct {timeout       time.DurationruntimeClient runtimeapi.RuntimeServiceClient// Cache last per-container error message to reduce log spamlastError map[string]string// Time last per-container error message was printederrorPrinted map[string]time.TimeerrorMapLock sync.Mutex
}

runtimeapi.RuntimeServiceClient是一个连接到了gRPC server的client端,主要接收来自kubelet的请求。RuntimeServiceClient的所有调用都是gRPC的方式。以RunPodSandbox为例:

func (c *runtimeServiceClient) RunPodSandbox(ctx context.Context, in *RunPodSandboxRequest, opts ...grpc.CallOption) (*RunPodSandboxResponse, error) {out := new(RunPodSandboxResponse)err := grpc.Invoke(ctx, "/runtime.v1alpha2.RuntimeService/RunPodSandbox", in, out, c.cc, opts...)if err != nil {return nil, err}return out, nil
}

RuntimeService server实现:

服务端实现的核心部分在kubelet/dockershim/docker_service.go中

// CRIService includes all methods necessary for a CRI server.
type CRIService interface {runtimeapi.RuntimeServiceServerruntimeapi.ImageServiceServerStart() error
}// DockerService is an interface that embeds the new RuntimeService and
// ImageService interfaces.
type DockerService interface {CRIService// For serving streaming calls.http.Handler// For supporting legacy features.DockerLegacyService
}

可以看到DockerService接口包装了RuntimeServiceServer和ImageServiceServer两个必须的接口。DockerService的实现类是dockerService(go duck type):

type dockerService struct {client           libdocker.Interfaceos               kubecontainer.OSInterfacepodSandboxImage  stringstreamingRuntime *streamingRuntimestreamingServer  streaming.Servernetwork *network.PluginManager// Map of podSandboxID :: network-is-readynetworkReady     map[string]boolnetworkReadyLock sync.MutexcontainerManager cm.ContainerManager// cgroup driver used by Docker runtime.cgroupDriver      stringcheckpointManager checkpointmanager.CheckpointManager// caches the version of the runtime.versionCache *cache.ObjectCache// startLocalStreamingServer indicates whether dockershim should start a// streaming server on localhost.startLocalStreamingServer bool
}

其中最核心的就是client,它包含了dockerService必须要实现的所有方法,其本质上是一个docker client,即DockerService接口的所有方法的实现最终都是通过直接调用docker client实现的。其中dockerEndpoint值为kubelet初始化时通过--docker-endpoint传入。

// ConnectToDockerOrDie creates docker client connecting to docker daemon.
func ConnectToDockerOrDie(dockerEndpoint string, requestTimeout, imagePullProgressDeadline time.Duration,withTraceDisabled bool, enableSleep bool) Interface {if dockerEndpoint == FakeDockerEndpoint {fakeClient := NewFakeDockerClient()if withTraceDisabled {fakeClient = fakeClient.WithTraceDisabled()}if enableSleep {fakeClient.EnableSleep = true}return fakeClient}//最核心的一行代码,创建docker clientclient, err := getDockerClient(dockerEndpoint)if err != nil {klog.Fatalf("Couldn't connect to docker: %v", err)}klog.Infof("Start docker client with request timeout=%v", requestTimeout)return newKubeDockerClient(client, requestTimeout, imagePullProgressDeadline)
}

dockerService只是完成了标准接口的实现,还不能对外提供服务,需要注册到gRPC中,即DockerServer:

// DockerServer is the grpc server of dockershim.
type DockerServer struct {// endpoint is the endpoint to serve on.endpoint string// service is the docker service which implements runtime and image services.service dockershim.CRIService// server is the grpc server.server *grpc.Server
}
runtimeapi.RegisterRuntimeServiceServer(s.server, s.service)
runtimeapi.RegisterImageServiceServer(s.server, s.service)

虽然RuntimeService和ImageService均为gRPC服务,但是实现上可以共用一个gRPC也可以分别启用一个gRPC,dockerShim采用的是第一种方式。

再回到最上层,在kubelet初始化的时候,会判断container runtime的类型,如果是docker,就会进入dockershim的初始化,即完成以上的流程:dockershim.NewDockerService()—>dockerremote.NewDockerServer(),并启动gRPC server:

  case kubetypes.DockerContainerRuntime:// Create and start the CRI shim running as a grpc server.streamingConfig := getStreamingConfig(kubeCfg, kubeDeps, crOptions)ds, err := dockershim.NewDockerService(kubeDeps.DockerClientConfig, crOptions.PodSandboxImage, streamingConfig,&pluginSettings, runtimeCgroups, kubeCfg.CgroupDriver, crOptions.DockershimRootDirectory, !crOptions.RedirectContainerStreaming)if err != nil {return nil, err}if crOptions.RedirectContainerStreaming {klet.criHandler = ds}// The unix socket for kubelet <-> dockershim communication.klog.V(5).Infof("RemoteRuntimeEndpoint: %q, RemoteImageEndpoint: %q",remoteRuntimeEndpoint,remoteImageEndpoint)klog.V(2).Infof("Starting the GRPC server for the docker CRI shim.")server := dockerremote.NewDockerServer(remoteRuntimeEndpoint, ds)if err := server.Start(); err != nil {return nil, err}// Create dockerLegacyService when the logging driver is not supported.supported, err := ds.IsCRISupportedLogDriver()if err != nil {return nil, err}if !supported {klet.dockerLegacyService = dslegacyLogProvider = ds}case kubetypes.RemoteContainerRuntime:// No-op.breakdefault:return nil, fmt.Errorf("unsupported CRI runtime: %q", containerRuntime)

这是dockerShim的流程,rktlet和dockerShim的实现非常类似。由于是集成在k8s中,所以kubelet需要负责CRI server的这些初始化工作,如果是其他的CRI实现,就需要在节点上启动runtimeService和imageService,kubelet只用关心runtimeService和imageService的endpoint,即启动kubelet时通过设置参数--container-runtime-endpoint、image-service-endpoint(imageService的endpoint缺省值默认使用runtimeService的endpoint的值)来告诉kubelet CRI gRPC server的endpoint。Anyway,即便把dockerShim独立出来,这个流程依然是一样的,在kubelet看来依然是一个接口一样的gRPC server。

CRI-O

本节重点分析CRI-O的实现。如果说dockerShim只是针对docker runtime的标准实现,那CRI-O就是真正的兼容OCI标准的实现:CRI-O全名即为CRI-OCI。CRI-O默认会使用runc,但是能够识别k8s pod的注解annotations:io.kubernetes.cri-o.TrustedSandbox,用户可以使用这个注解来通过CRI-O选择合适的runtime。比如,对安全级别要求较高,可以将注解的值设置为false,CRI-O就会使用kata-runtime,即每个pod对应一个虚机(这和docker使用kata-runtime稍有区别,docker中默认每个kata container对应一个虚机隔离,而在k8s pod中的一组容器往往是业务上相互协作的应用,他们之间的安全隔离性不需要很高,所以pod内的一组容器和runc的类似,只是在pod层需要达到虚机级别的隔离)。还需要说明的是,CRI-O支持一个节点上同时运行两种不同类型runtime的pod。

kata-4.png

Kata + Firecracker

kata-5.png

在没有firecracker之前,CRI-O就可以通过k8s注解或者配置文件的方式将默认的runtime替换为kata runtime,进而为容器(pod)开独立的虚机。firecracker本质上是虚拟化技术,和qemu在一个层面,只是它更加轻量、精简。所以很自然的会想到,kata能否直接开出firecracker虚机,在虚机里运行容器?答案是肯定的。

在最新发布的kata1.5中,已经开始支持firecracker。通过k8s pod的runtimeClass字段来设定pod内容器的运行时。runtimeClass也是k8s的新增的字段,属于beta版。目前支持的最小粒度为pod,即一个node上可以运行多种runtime的pod,但是每个pod内部的容器的运行时必须相同。因为在一个pod内,容器和虚机的数据共享和网络通信是非常的麻烦,超出了k8s目前的能力。但是抽象到pod层以上,在k8s看来,虚机和pod就没有差异了。

kata-6.png

但是,限于firecracker本身功能过于简单,因为其设计之初就是追求最少的设备、最简洁的功能,firecracker目前很多k8s的功能还不支持,比如volume、secret、configmap等[1]。如果应用比较复杂,对运行环境的要求比较高,就只能使用qemu vm。

更多关于kata对firecracker hypervisor的支持实现方面的细节可以参考:

1、Firecracker hypervisor接口的实现:firecracker: VMM API support

2、添加firecracker作为qemu之后的新的一种hypervisor选项:virtcontainers: Add firecracker as a supported hypervisor

3、目前kata+fc的限制:Firecracker limitations

3、官方Demo演示

总结

从firecracker去年开始问世至今,将firecracker融入如今的容器生态一直是AWS和开源社区在致力推进。从最开始初见雏形的containerd+firecracker到如今已经接近成熟的kata+firecracker,未来firecracker在容器生态中将处于什么样的地位,是通过containerd和kata成为qemu一样的runtime选项,还是作为serverless容器底层沙箱的的标准(类似如今AWS的Lambda和 Fargat),现在还不能有定论。但是serverless领域对极简极快的追求和firecracker这种极简的VMM设计是完全契合的。qemu是没有做到最精简,依然有很多不必要的模块。用firecracker替换qemu(或者借鉴firecracker的思路构建自己的面向severless的轻量级容器底层沙箱)是未来可以尝试的方向。

引用资料

[1] https://github.com/kata-containers/documentation/issues/351

[2] https://github.com/kata-containers/runtime/releases/tag/1.5.0

[3] https://github.com/kata-containers/runtime/commit/c1d3f1a98b0f8be6a2353cf288cf94b6f27cc57c

[4] https://github.com/kata-containers/runtime/commit/e65bafa79371704090b81e89e145807b35dfd648

[5] https://github.com/kubernetes/kubernetes

[6] https://github.com/kubernetes-sigs/cri-o

[7] https://github.com/containerd/cri

[8] https://github.com/kubernetes/frakti

[9] https://github.com/kubernetes-incubator/rktlet

Kubernetes + CRI + Kata + Firecracker相关推荐

  1. Kata Containers及相关vmm介绍

    Kata Containers介绍 Kata Containers 是轻量级虚拟机的一种新颖实现,可无缝集成到容器生态系统中. Kata Containers 与容器一样轻巧快速,并与容器管理层集成, ...

  2. 最新容器项目 Kata 曝光

    2019独角兽企业重金招聘Python工程师标准>>> Kata Containers设计为硬件无关,与Open Container Initiative(OCI)标准.Kubern ...

  3. 解读2017之容器篇:后Kubernetes时代

    转载自:http://www.sohu.com/a/215846233_464005 作者|张磊 编辑|江柳 如果说 2017 年的容器技术圈子只能用一个关键词来概括的话,那一定非"Kube ...

  4. 国庆充电:从容器到容器云,什么才是Kubernetes的本质?

    我是谁 我叫张磊,是 Kubernetes 社区的一位资深成员和项目维护者.在 Kubernetes 和 Kata Containers 社区从事上游开发工作,先后发起了容器镜像亲密性调度.基于等价类 ...

  5. PouchContainer 发布 0.3.0 版本,支持 Kubernetes 拥抱 CNCF 生态

    划重点 PouchContainer 是一款轻量级.开源的富容器技术,拥有快速高效.隔离性强.可移植性高.资源占用少等特性,可以帮助企业快速实现存量业务容器化,同时提高超大规模下数据中心的物理资源利用 ...

  6. 三大公有云托管 Kubernetes 服务 (EKS、GKE、AKS) 评估

    EKS vs GKE vs AKS - Evaluating Kubernetes in the Cloud | StackRoxhttps://www.stackrox.com/post/2021/ ...

  7. Kubernetes 概述和搭建 (多节点)

    一.Kubernetes 整体概述和架构 Kubernetes 是什么 Kubernetes 是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务.通过 Kubernetes 能够进行应用的自动化 ...

  8. kubernetes 入门实践

    ㅤㅤㅤ ㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ(一个人的真正伟大之处就在于他能够认识到自己的渺小 -- 保罗) ㅤㅤㅤ ㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤ 学习完本教程能给你带来什么 k8s的基本概念和核心知 ...

  9. Kubernetes全套笔记

    Kubernetes学习资料 K8S概述及相关特征 概述 K8s是谷歌在2014年发布的容器化集群管理系统 使用k8s进行容器化应用部署 使用k8s利于应用扩展 k8s目标实施让部署容器化应用更加简洁 ...

最新文章

  1. quartz异常:Couldn't rollback jdbc connection
  2. Cortex-M3基本知识点(手册)
  3. 【干货】吴恩达deeplearning.ai专项课程历史文章汇总
  4. 面向对象2(构造方法、抽象类、接口)
  5. mysql delete返回值_Mybatis执行sql(insert、update、delete)返回值问题
  6. 迪信通机器人_迪信通要做机器人 玩票还是另有深意?
  7. LeetCode 893. 特殊等价字符串组
  8. git master主分支_Git分支管理策略及简单操作
  9. IIS 之 未能加载文件或程序集“IBM.Data.DB2”或它的某一个依赖项。试图加载格式不正确的程序。...
  10. oracle create tablespace、user and grant
  11. 免费CMS系统的广告如何去掉
  12. 【Matlab学习笔记】【函数学习】nargin 参数
  13. Atitit 效率提升分析与解决方案 1. 三大模式 优化资源配置 通过降低难度 提升培训 1 1.1. 优化资源配置 1 1.2. 通过降低难度 1 1.3. 提升培训 1 2. 有效与立即可
  14. 怎么将php文件改成web的servlet文件_遇到喜欢的网站怎么才能高效收藏整理
  15. 深入浅出设计模式---3、代理模式和工厂模式
  16. 结巴分词python教程_Python笔记:用结巴分词制作词云图
  17. EasyPoi 模板导出Excel (带图片) 以及一些踩坑记录
  18. 施工企业信息注册需要对计算机网络,关于建筑施工企业中计算机网络技术的应用论文...
  19. 知其然就够了——大数据时代的思考之一
  20. 使用VMWARE安装Mac OSX 雪豹操作系统并配置iphone开发环境

热门文章

  1. oracle 隐藏视图定义,Oracle中视图(views)的含义
  2. 面板什么都看不到 unity_杨幂今年第一封太敷衍!最新大片不露脸不秀身材,什么都看不到...
  3. ascii码_umask,补码,ASCII码:稍微深入考虑一点
  4. Ubuntu环境下TensorFlow 的环境搭建(二)安装TensorFlow(CPU版)
  5. 清华大学、中科院等研究机构研究人员提出BETA:面向SSVEP-BCI应用程序的大型基准测试数据库...
  6. 关卡设计快速入门_7. 自己来!
  7. 2021 CCF颁奖典礼隆重举行
  8. 特斯拉自动驾驶新能力:识别红绿灯停车标识;尝鲜车主:实用好用
  9. django中聚合aggregate和annotate GROUP BY的使用方法
  10. CentOS安装和配置Mysql