CRI 与 ShimV2:一种 Kubernetes 集成容器运行时的新思路

news2024/11/19 8:29:44

作者|张磊

CRI 与 ShimV2:一种 Kubernetes 集成容器运行时的新思路-阿里云开发者社区 

Kubernetes 项目目前的重点发展方向,是为开发者和使用者暴露更多的接口和可扩展机制,将更多的用户需求下放到社区来完成。其中,发展最为成熟也最为重要的一个接口就是 CRI。2018 年,由 containerd 社区主导的 shimv2 API 的出现,在 CRI 的基础上,为用户集成自己的容器运行时带来了更加成熟和方便的实践方法。

本次演讲分享了关于 Kubernetes 接口化设计、CRI、容器运行时、shimv2、RuntimeClass 等关键技术特性的设计与实现,并以 KataContainers 为例,为听众演示上述技术特性的使用方法。本文整理自张磊在 KubeCon + CloudNativeCon 2018 现场的演讲速记。

今天,我给大家带来的分享是关于 Kubernetes CRI 和 containerd shimv2 的设计,这也是目前社区里比较重要的一个大方向。大家好,我是张磊,现在在阿里巴巴集团工作。既然今天咱们会聊 Kubernetes 这个项目,那么首先我们来简单看一下 Kubernetes 这个项目的工作原理。

Kubernetes 的工作原理

其实大家都知道 Kubernetes 这个项目它最上面是一层 Control Panel ,它也被很多人称之为 Master 节点。当你把 workload 就是你的应用提交给 Kubernetes 之后,首先为你做事情的是 API server,它会把你的 Application 存到 etcd 里,以 API 对象的方式存到 etcd 中去。

而 Kubernetes 中负责编排的是 Controller manager,一堆 controller 通过控制循环在 run。通过这个控制循环来做编排工作,帮你去创建出这些应用所需要的 Pod,注意不是容器,是 Pod。

而一旦一个 Pod 出现之后,Scheduler 会 watch 新 Pod 的变化。如果他发现有一个新的 Pod 出现,Scheduler 会帮你去把所有调度算法都 run 一遍,把 run 到的结果:就是一个 Node 的名字,写在我这个 Pod 对象 NodeName 字段上面,就是一个所谓的 bind 的操作。然后把 bind 的结果写回到 etcd 里去,这就是所谓的 Scheduler 工作过程。所以 Control Panel 它忙活这么一圈下来,最后得到的结果是什么呢?你的一个 Pod 跟一个 Node 绑定(bind)在了一起,就是所谓 Schedule 了。

而 Kubelet 呢?它是运行在所有节点上。Kubelet 会 watch 所有 Pod 对象的变化,当它发现一个 Pod 与一个  Node 绑定在一起的时,并且它又发现这个被绑定的 Node 是它自己,那么 Kubelet 就会帮你去接管接下来的所有事情。

如果你看一下 Kubelet ,看看它在做什么呢?很简单,其实当 Kubelet 拿到这个信息之后,他是去 call 你运行在每个机器上的 Containerd 进程,去 run 这个 Pod 里的每一个容器。

这时候,Containerd 帮你去 call runC 所以最后其实是 runC 帮你去 set up 起来这些 namespace、Cgroup 这些东西,是它去帮你  chroot ,“搭”出来所谓的一个应用和需要的容器。这就是整个 Kubernetes 工作的一个简单原理。

Linux Container

所以这个时候你可能会提出一个问题就是什么是容器?其实容器非常简单,我们平常所说这个容器就是 Linux 容器,你可以把 Linux 容器分为两部分:第一个是 Container Runtime,第二个是 Container Image。

所谓的 Runtime 部分就是你所运行进程的动态视图和资源边界,所以它是由  Namespace 和 Cgroup 为你构建出来的。而对于 Image(镜像),你可以把它理解为是你想要运行的程序的静态视图,所以它其实是你的程序+数据+所有的依赖+所有的目录文件组成一个压缩包而已。

而这些压缩包被以 union mount 的方式 mount 在一起的时候,我们称之为 rootfs 。rootfs 就是你的整个 process 的静态视图,他们看到这个世界就这样子,所以这是 Linux Container。

KataContainer

可今天我们还要聊另外一种 Container,它与前面 Linux Container 截然不同。他的 Container Runtime 是用 hypervisor 实现的,是用 hardware virtualization 实现的,像个虚拟机一样。所以每一个像这样的 KataContainer 的 Pod,都是一个轻量级虚拟机,它是有完整的 Linux 内核。所以我们经常说 KataContainer 与 VM 一样能提供强隔离性,但由于它的优化和性能设计,它拥有与容器项媲美的敏捷性。这个一点稍后会强调,而对于镜像部分, KataContainer 与 Docker 这些项目没有任何不同,它使用的是标准 Linux Continer 容器,支持标准的 OCR Image 所以这一部分是完全一样的。

容器安全

可是你可能会问为什么我们会有 KataContainer 这种项目? 其实很简单,因为我们关心安全这个事,比如很多金融的场景、加密的场景,甚至现在区块链很多场景下,都需要一个安全的 Container Runtime,所以这是我们强调 KataContainer 的一个原因。

如果你现在正在使用 Docker, 我问一个问题就是你怎样才能安全地使用 Docker?你可能会有很多套路去做。比如说你会 drop 掉一些 Linux capibility,你可以去指定 Runtime 可以做什么,不能做什么。第二个你可以去 read-only mount points 。第三,你可以使用 SELinux 或者 AppArmor 这些工具把容器给保护起来。还有一种方式是可以直接拒绝一些 syscalls,可以用到 SECCOMP。

但是我需要强调的是所有这些操作都会在你的 Container 和 Host 之间引入新的 layer,因为它要去做过滤,它要去拦截你的 syscalls,所以这个部分你搭的层越多,你容器性能越差,它一定是有额外的负面性能损耗的。

更重要的是,做这些事情之前你要想清楚到底应该干什么,到底应该 drop 掉哪些 syscalls,这个是需要具体问题具体分析的,那么这时候我应该怎么去跟我的用户去讲如何做这件事情?

所以,这些事情说起来很简单,但实际执行起来很少有人知道到底该怎么去做。所以在 99.99% 的情况下,大多数人都是把容器 run 到虚拟机里去的,尤其在公有云场景下。

而对于 KataContainer 这种项目来说,它由于使用了与虚拟机一样的 hardware virualization,它是有独立内核的,所以这个时候它提供的 isolation 是完全可信任的,就与你信任 VM 是一样的。

更重要的是,由于现在每一个 Pod 里是有一个 Independent Kernel,跟个小虚拟机一样,所以这时候就允许你容器运行的 Kernel 版本跟 Host machine 适应是完全不一样。这是完全 OK 的,就与你在在虚拟机中做这件事一样,所以这就是为什么我会强调 KataContainers 的一个原因,因为它提供了安全和多租户的能力。

Kubernetes + 安全容器

所以也就很自然会与有一个需求,就是我们怎么去把 KataContainer run 在 Kubernetes 里? 

那么这个时候我们还是先来看 Kubelet 在做什么事情,所以 Kubelet 要想办法像 call Containerd 一样去 call KataContainer,然后由 KataContainer 负责帮忙把 hypervisor 这些东西 set up 起来,帮我把这个小VM 运行起来。所以这个时候就要需要想怎么让 Kubernetes 能合理的操作 KataContainers。

Container Runtime Interface(CRI)

对于这个诉求,就关系到了我们之前一直在社区推进的 Container Runtime Interface ,我们叫它 CRI。CRI 的作用其实只有一个:就是它描述了,对于 Kubernetes 来说,一个 Container 应该有哪些操作,每个操作有哪些参数,这就是 CRI 的一个设计原理。但需要注意的是,CRI 是一个以容器为核心的 API,它里面没有 Pod 的这个概念。这个要记住。

为什么这么说呢?我们为什么要这么设计呢?很简单,我们不希望像 Docker 这样的项目,必须得懂什么是 Pod,暴露出 Pod 的 API,这是不合理的诉求。Pod 永远都是一个 Kubernetes 的编排概念,这跟容器没有关系,所以这就是为什么我们要把这个 API 做成 Containerd -centric。

另外一个原因出于 maintain 的考虑,因为如果现在, CRI 里有 Pod 这个概念,那么接下来任何一个 Pod feature 的变更都有可能会引起 CRI 的变动,对于一个接口来说,这样的维护代价是比较大的。所以如果你细看一下 CRI,你会发现它其实定了一些非常普遍的操作容器接口。

在这里,我可以把 CRI 大致它分为 Container 和 Sandbox。Sandbox 用来描述的是我通过什么样的机制来去实现 Pod ,所以它其实就是 Pod这个概念真正跟容器项目相关的字段。对于 Docker 或 Linux 容器来说,它其实 match 到最后 run 起来的是一个叫 infra container 的容器,就是一个极小的容器,这个容器用来 hold 整个 Pod 的 Node 和 Namespace。

不过, Kubernetes 如果用 Linux Container Runtim, 比如 Docker 的话,它不会给你提供 Pod level 的 isolation,除了一层 Pod level cgroups 。这是一个不同点。因为,如果你用 KataContainers 的话,KataContaniners 会在这一步为你创建一个轻量级的虚拟机。

接下来到下一阶段,到 Containers 这个 API 的时候,对于 Docker 来说它就给你起在宿主机上启动用户容器,但对 Kata 来说不是这样的,它会在前面的 Pod 对应的轻量级虚拟机里面,也就在前面创建的 Sandbox 里面 set up 这些用户容器所需要 Namespace ,而不会再跟你在一起新的容器。所以有了这样一个机制之后,当上面 Contol Panel 完成它的工作之后,它说我把 Pod 调度好了,这时候 Kubelet 这边启动或创建这个 Pod 的时候一路走下去,最后一步才会去 call 我们这个所谓 CRI。在此之前,在 Kubelet 或者 Kubernetes 这是没有所谓 Containers runtime 这个概念的。

所以走到这一步之后,如果你用 Docker 的话,那么 Kubernetes 里负责响应这个 CRI 请求 是 Dockershim。但如果你用的不是 Docker 的话一律都要去走一个叫 remote 的模式,就是你需要写一个 CRI Shim,去 serve 这个 CRI 请求,这就是我们今天所讨论下一个主题。

CRI Shim 如何工作?

CRI Shim 可以做什么?它可以把 CRI 请求 翻译成 Runtime  API。我举个例子,比如说现在有个 Pod 里有一个 A 容器和有个 B 容器,这时候我们把这件事提交给 Kubernetes 之后,在 Kubelet 那一端发起的 CRI code 大概是这样的序列:首先它会 run Sandbox foo,如果是 Docker 它会起一个 infra 容器,就是一个很小的容器叫 foo,如果是 Kata 它会给你起一个虚拟机叫 foo,这是不一样的。

所以接下来你 creat start container A 和 B 的时候,在 Docker 里面是起两个容器,但在 Kata 里面是在我这个小虚拟机里面,在这 Sandbox 里面起两个小 NameSpace,这是不一样的。所以你把这一切东西总结一下,你会发现 OK,我现在要把 Kata run 在 Kubernetes 里头,所以我要做工作,在这一步要需要去做这个 CRI shim,我就想办法给 Kata 作一个 CRI shim。

而我们能够想到一个方式,我能不能重用现在的这些 CRI shim。重用现在哪些?比如说 CRI containerd 这个项目它就是一个 containerd 的 CRI shim,它可以去响应 CRI 的请求过来,所以接下来我能不能把这些情况翻译成对 Kata 这些操作,所以这个是可以的,这也是我们将用一种方式,就是把 KataContainers 接到我的 Containerd 后面。这时候它的工作原理大概这样这个样子,Containerd 它有一个独特设计,就是他会为每一个 Contaner 起个叫做 Contained shim。你 run 一下之后你会看他那个宿主机里面,会 run 一片这个 Containerd shim 一个一个对上去。

而这时候由于 Kata 是一个有 Sandbox 概念的这样一个 container runtime,所以 Kata 需要去 match 这些 Shim 与 Kata 之间的关系,所以 Kata 做一个 Katashim。把这些东西对起来,就把你的 Contained 的处理的方式翻译成对 kata 的 request,这是我们之前的一个方式。

但是你能看到这其实有些问题的,最明显的一个问题在于 对 Kata 或 gVisor 来说,他们都是有实体的 Sandbox 概念的,而有了 Sandbox 概念后,它就不应该去再去给他的每一个 Container 启动有一个 shim match 起来,因为这给我们带来很大的额外性能损耗。我们不希望每一个容器都去 match 一个 shim,我们希望一个 Sandbox match 一个 shim。

另外,就是你会发现 CRI 是服务于 Kubernetes 的,而且它呈现向上汇报的状态,它是帮助 Kubernetes 的,但是它不帮助 Container runtime。所以说当你去做这个集成时候,你会发现尤其对于 VM gVisor\KataContainer 来说,它与 CRI 的很多假设或者是 API 的写法上是不对应的。所以你的集成工作会比较费劲,这是一个不 match 的状态。

最后一个就是我们维护起来非常困难,因为由于有了 CRI 之后,比如 RedHat 拥有自己的 CRI 实现叫 cri-o,他们和 containerd 在本质上没有任何区别,跑到最后都是靠 runC 起容器,为什么要这种东西?

我们不知道,但是我作为 Kata maintainer,我需要给他们两个分别写两部分的 integration 把 Kata 集成进去。这就很麻烦,者就意味着我有 100 种这种 CRI 我就要写 100 个集成,而且他们的功能全部都是重复的。

Containerd ShimV2

所以在今天我给大家 propose 的这个东西叫做 Containerd ShimV2。前面我们说过 CRI,CRI 决定的是 Runtime 和 Kubernetes 之间的关系,那么我们现在能不能再有一层更细致的 API 来决定我的 CRI Shim 跟下面的 Runtime 之间真正的接口是什么样的?

这就是 ShimV2 出现的原因,它是一层 CRI shim 到 Containerd runtime 之间的标准接口,所以前面我直接从 CRI 到 Containerd 到 runC,现在不是。我们是从 CRI 到 Containerd 到 ShimV2,然后 ShimV2 再到 RunC 再到 KataContainer。这么做有什么好处?

我们来看一下,最大的区别在于:在这种方式下,你可以为每一个 Pod 指定一个 Shim。因为在最开始的时候,Containerd 是直接启动了一个 Containerd Shim 来去做响应,但我们新的 API 是这样写的,是 Containerd Shim start 或者 stop。所以这个 start 和 stop 操作怎么去实现是你要做的事情。

而现在,我作为一位 KataContainers项目的 maintainer 我就可以这么实现。我在 created Sandbox 的时候 call 这个 start 的时候,我启动一个 Containerd Shim。但是当我下一步是 call API 的时候,就前面那个 CRI 里面, Container API 时候,我就不再起了,我是 reuse,我重用为你创建好的这个 Sandbox,这就位你的实现提供了很大的自由度。

所以这时候你会发现整个实现的方式变了,这时候 Containerd 用过来之后,它不再去 care 每个容器起 Containerd Shim,而是由你自己去实现。我的实现方式是我只在 Sandbox 时候,去创建 containerd-shim-v2,而接下来整个后面的 container level 操作,我会全部走到这个 containerd-shim-v2 里面,我去重用这个 Sandbox,所以这个跟前面的时间就出现很大的不同。

所以你现在去总结一下这个图的话,你发现我们实现方式是变成这个样子:

首先,你还是用原来的 CRI Containerd,只不过现在装的是 runC,你现在再装一个 katacontainer 放在那机器上面。接下来我们 Kata 那边会给你写一个实现叫 kata-Containerd-Shimv2。所以前面要写一大坨 CRI 的东西,现在不用了。现在,我们只 focus 在怎么去把 Containerd 对接在 kata container 上面,就是所谓的实现 Shimv2 API,这是我们要做的工作。而具体到我们这要做的事情上,其实它就是这样一系列与 run 一个容器相关的 API。

比如说我可以去 create、start,这些操作全部映射在我 Shimv2 上面去实现,而不是说我现在考虑怎么去映射,去实现 CRI,这个自由度由于之前太大,造成了我们现在的一个局面,就有一堆 CRI Shim 可以用。这其实是一个不好的事情。有很多政治原因,有很多非技术原因,这都不是我们作为技术人员应该关心的事情,你现在只需要想我怎么去跟 Shimv2 对接就好了。

接下来,我为你演示一下通过 CRI + containerd shimv2调用 KataContainers 的一个 Demo(具体内容略)

总结

Kubernetes 现在的核心设计思想,就是通过接口化和插件化,将原本复杂的、对主干代码有侵入性的特性,逐一从核心库中剥离和解耦。而在这个过程中,CRI 就是 Kubernetes 项目中最早完成了插件化的一个调用接口。而这次分享,主要为你介绍了在CRI基础上的另一种集成容器运行时的思路,即:CRI + containerd shimv2 的方式。通过这种方式,你就不需要再为自己的容器运行时专门编写一个 CRI 实现(CRI shim),而是可以直接重用 containerd对 CRI 的支持能力,然后通过 containerd shimv2的方式来对接具体的容器运行时(比如 runc)。目前,这种集成方式已经成为了社区对接下层容器运行时的主流思路,像很多类似于 KataContainers,gVisor,Firecracker 等基于独立内核或者虚拟化的容器项目,也都开始通过 shimv2 ,进而借助 containerd项目无缝接入到 Kubernetes 当中。

而众所周知,在阿里内部,Sigma/Kubernetes 系统使用的容器运行时主要是 PouchContainer。事实上,PouchContainer 本身选择使用 containerd 作为其主要的容器运行时管理引擎,并自我实现了增强版的 CRI 接口,使其满足阿里巴巴强隔离、生产级别的容器需求。所以在 shimv2 API 在 containerd 社区发布之后,PouchContainer 项目就已经率先开始探索和尝试通过 containerd shimv2 来对接下层的容器运行时,进而更高效的完成对其他种类的容器运行时尤其是虚拟化容器的集成工作。我们知道,自从开源以来,PouchContainer 团队一直都在积极地推动 containerd 上游社区的发展和演进工作,而在这次 CRI + containerd shimv2 的变革里, PouchContainer 再一次走到了各个容器项目的最前面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/191738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

再探前端低代码的“野路子”

之前码过很多低代码的文章,发现大家口中的低代码挺不一样的,这次心血来潮想探探低代码的野路子。 只需要拖拽操作或者几行基础代码,就能完成以往需要程序员才能搭建的各类应用系统,对效率要求较高的企业而言,是不是很有…

命令执行简介、命令执行函数

数据来源 命令执行简介 01 命令执行漏洞产生原因 02 命令执行漏洞的危害 03 远程代码执行 1)远程代码执行- eval函数 2)远程代码执行 - assert函数 3)远程代码执行 - preg_replace函数 使用方法和一句话木马一样 示例: 在ph…

9 多分类问题

文章目录问题引入网络设计改进网络方法softmax层lossMINIST引入代码实现课程内容来源: 链接课程文本借鉴: 链接以及Birandaの突然发现的也挺好:链接 问题引入 前篇中,对糖尿病数据集的问题是一个二分类问题,但实际问…

vue-node解决 rollbackFailedOptional: verb npm-session fd23ceb3f5797b77进度条卡住的问题

一、文章引导 #mermaid-svg-qv5tmCFBaoUwQojc {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-qv5tmCFBaoUwQojc .error-icon{fill:#552222;}#mermaid-svg-qv5tmCFBaoUwQojc .error-text{fill:#552222;stroke:#55222…

RabbitMQ常见场景问题

RabbitMQ常见场景问题 文章目录RabbitMQ常见场景问题6种工作模式1.直连模式2.发布订阅模式3.Routing路由模式4.Topic通配符模式5.Header模式6.RPC消息不丢失消息发送到交换机失败1.配置文件开启发布确认2.配置回调函数3.测试4.如何处理失败消息RabbitMQ服务器故障持久化消息发送…

存量房贷利率,一种简单估算其自然年利率调整的方法。

1.摘要2022年过去了,总所周知LPR被多次下调,目前有存量房贷的朋友,如果(普遍)设置的是根据自然年LPR动态调整利率,到2023年2月应该注意到了比较明显的房贷金额变动。这里主要给出一种根据这个变动&#xff…

Plecs电力电子仿真专业教程-第一季 第一节 Plecs简介

Plecs电力电子仿真专业教程-第一季 第一章 Plecs是什么? 第一节 Plecs简介 Plecs是瑞士Plexim GmbH公司开发的系统级电力电子仿真软件PLECS。PLECS是一个用于电路和控制结合的多功能仿真软件,尤其适用于电力电子和传动系统。不管您是工业领域中的开发…

[架构之路-96]:《软件架构设计:程序员向架构师转型必备》-6-需求与用户用例User Case/Senario建模

第6章 需求与用户用例User Case建模备注:严格意义上讲,用户用例属于需求分析领域,不属于架构设计。用户用例是架构设计最重要的输入参考之一。User Case和User Senario是非常重要的描述需求的重要手段6.1 常用的4种用例技术6.1.1 用例图6.1.2…

学习Java开发按此路线规划,从10K到40K全都有了,我就是这样过来的

如果有一天我醒来时,发现自己的几年Java开发经验被抹掉,重新回到了一个小白的状态。我想要重新自学Java,然后找到一份自己满意的Java工作,我想大概只需要6个月的时间就够了,如果顺利的话,4个月也差不多。如…

用光盘怎样重装电脑系统

用光盘怎样重装电脑系统?重装系统,听起来好像很难的样子。其实没那么难,用光盘装还是比较容易的。下面一起看看如何用光盘重装系统吧。 工具/原料: 系统版本:win7 品牌型号:联想yoga13 方法/步骤&#xf…

Vue使用axios发送get请求并携带参数

前言 其实关于Vue使用axios发送get请求并携带参数,我之前写过一篇,但是昨天又发现了另外一种方式,所以就单独写一篇进行总结。 之前写的那篇使用get请求并携带参数都是使用的字符串拼接的方式 感兴趣可以参考: Vue使用axios进行g…

基于Android的校园资产管理系统

需求信息: 管理员用户: 1:用户注册登录:通过手机号码、用户名称以及密码完成用户的注册和登录 2:添加资产:添加资产的编号、名称、归属部门之后生成资产二维码,以及查看添加过的资产信息 3&…

amCharts Javascript Web 5.3.0 Crack

添加新的 JSON 插件,允许您将序列化 (JSON) 配置解析为图表。 2023 年 1 月 31 日 - 16:00新版本 特征 添加了新JSON插件,允许将序列化 (JSON) 配置序列化和解析为图表。 crisp(默认:)false设置已添加到Sprite。如果设…

已经拿到IB成绩的学生,应该怎么为申请大学做准备呢?

2023年将会是过渡的一年,前几年的高分可能一去不复返了,大家心里也是要做好准备。对于今年已经拿到IB成绩的孩子们,应该怎么为申请大学做准备呢?老师也给了大家一些建议。1.如何递交IB成绩给申请的大学?今年1月出成绩的…

Shell + Datax 动态传递时间参数模式

Datax 数据同步模式Shell 脚本实现Datax 数据同步四种模式Datax 数据全量同步模式此脚本省略...Datax 数据实时增量(T1)模式功能:实现前一天日期 00:00:00 至前一天日期 23:59:59 数据同步#!/bin/bash # 切换至增量脚本文件存储目…

[NOI Online #3 入门组] 最急救助

题目描述: 救助中心每天都要收到很多求救信号。收到求救信号后,救助中心会分析求救信号,找出最紧急的求救者给予救助。 求救信号是一个由小写英文字母组成的字符串,字符串中连续三个字符依次组成sos的情况越多(即包含子串sos的数…

【蓝桥杯单片机】工厂灯光控制系统案例解析(小蜜蜂老师基础综合实训)

工厂灯光控制系统案例解析题目流程图关键点复盘参考代码(IO模式)题目 流程图 关键点复盘 设备检测——移位 L1~L8在板子上是从左至右,但是在对P0口赋值时是16进制从高位(L8)—>低位(L0) 根据原理图,LED赋值0亮1灭 为了方便赋值…

OpenShift 4 - 在单节点的 OpenShift 上用 NFS Operator 实现以 RWX 访问存储

《OpenShift / RHEL / DevSecOps 汇总目录》 文本已在 OpenShift Local 4.12 环境中进行验证。 文章目录OpenShift 支持的存储访问模式用 NFS Provisioner Operator 实现 RWX 访问存储安装 NFS Operator解决安装 Operator 过程无法访问谷歌 gcr.io 上的容器镜像配置 NFSProvisi…

《零基础学机器学习》读书笔记三之基本机器学习术语

《零基础学机器学习》读书笔记三之基本机器学习术语 一、机器学习快速上手路径(续) 1.3 基本机器学习术语 1.3.1 特征 特征是机器学习中的输入,原始的特征描述了数据的属性。特征的维度指的是特征的数目。 把向量、矩阵和其他张量的维度统…

React脚手架应用(二)

1、react脚手架 脚手架简介 用来帮助程序员快速创建一个基于xxx库的模板项目 1、包含了所有需要的配置(语法检查、jsx编译、devServer…); 2、下载好了所有相关的依赖; 3、可以直接运行一个简单效果; create-react-a…