模型机流水微程序控制器设计_Kubernetes模型设计与控制器模式精要 | 留言送书...

520282a8ab7a5daa2b8bb99dbf9966ea.png

Kubernetes创建初期,其本身在业界地位并不占优,前有长期占有主流市场的Mesos和基于Mesos的DCOS围追堵截,后有Docker Swarm依托自己的容器事实标准异军突起,反倒是Kubernetes只有谷歌的品牌。Kubernetes为什么能最后胜出,成为容器云的实施标准呢?最根本原因就是其对管理范畴的所有对象进行了抽象,通过模型标准化将容器云平台各个维度的问题解决得非常完美。Kubernetes模型通常由以下部分组成:TypeMeta

f6a076c44b41ecd5886c31e91e736757.png

TypeMeta是Kubernetes对象的最基本定义,它通过引入GKV(Group,Kind,Version)定义了一个对象的类型。 GroupKubernetes定义了非常多对象,如何归类这些对象是一门学问,将对象依据其功能范围归入不同的分组,比如把支撑最基本功能的对象归入core组,把与应用部署有关的对象归入apps组,会使这些对象可维护性和可理解性更高。 Kind定义一个对象的基本类型,比如Node,Pod,Deployment等。 Version社区每个季度会推出一个Kubernetes版本,随着Kubernetes版本的演进,对象从创建之初到能够完全生产化就绪的版本是不断变化的。与软件版本类似,通常社区提出一个模型定义以后,随着该对象不断成熟,其版本可能会从v1alpha1,到v1alpha2,或者到v1beta1,最终变成生产就绪版本v1。Kubernetes通过Version属性来控制版本。当不同版本的对象定义发生变更时,有可能需要涉及到数据迁移,Kubernetes API Server允许通过Conversion方法转换不同版本的对象属性。这是一种自动数据迁移的机制,当集群版本升级以后,已经创建的老版本对象会被自动转换为新版本。这里所说的版本是对外版本(External Version),用户通过API能看到的版本。事实上资源定义都有对内版本(Internal Version),在Kubernetes API Server处先将对外版本转换成对内版本,然后再进行持久化。Metadata

f6a076c44b41ecd5886c31e91e736757.png

TypeMeta定义了“我是什么”,Metadata定义了“我是谁”。为方便管理,Kubernetes将不同用户或不同业务的对象用不同的Namespace隔离。Metadata中有两个最重要属性——Namespace和Name,分别定义了对象的Namespace归属及名字,这两个属性唯一定义了某个对象实例。前面说过,所有对象都会以API的形式发布供用户访问,Typemeta、Namespace和Name唯一确定了该对象所在的API访问路径,该路径也会被自动生成并保存在对象Metadata属性的selfLink中,如下所示:

selfLink: /api/v1/namespaces/default/pods/nginx-6ccb6b48dd-zvfrj

Label传统面向对象设计系统中,对象组合的方法通常是内嵌或引用,即将对象A内嵌到对象B中,或者将对象A的ID内嵌到对象B中。这种设计的弊端是这种关系是固化的,一个对象可能对多个其他对象发生关联,如果该对象发生变更,系统需要遍历所有其关联对象并做修改。Kubernetes采用了更巧妙的方式管理对象和对象的松耦合关系,其依赖的就是Label和Selector。Label,顾名思义就是给对象打标签,一个对象可以有任意对标签,其存在形式是键值对。不像名字和UID,标签不需要独一无二,多个对象可以有同一个标签,每个对象可以有多组标签。Label定义了这些对象的可识别属性,Kubernetes API支持以Label作为过滤条件查询对象。因此Label通常用最简形式定义:

metadata:
  labels:
    app: web
    tier: front

其他对象只需要定义Label Selector就可按条件查询出其需要关联的对象。Label的查询可以基于等式如app=web,或app!=db,或基于集合如app in (web, db)或app notin (web, db),可以只查询Label键,比如app。Label对多个条件查询只支持“与”操作,如app=web, tier=front。 AnnotationAnnotation与Label一样用键值对来定义,但其功能与Label不一样,所有在用法上也有不同原则,API也不支持针用Annotation做条件过滤。虽然Kubernetes把对象做了很好的抽象,在实际运用中特别是生产化落地过程中,总是需要保存一些在对象内置属性中无法保存的信息,Annotation就是为了满足这类需求,事实上Annotation是对象的属性扩展。社区在开发新功能,需要对象发生变更之前,往往会先把需要变更的属性放在Annotation中,当功能经历完实验阶段再将其移至正式属性中。Annotation作为属性扩展,更多是面向系统管理员和开发人员的,因此Annotation需要像其他属性一样做合理归类。与Java开发中的包名设计类似,通常需要将系统以不同功能规划为不同的Annotation Namespace,其键应以如下形式存在:/key:value, 比如一个最常用场景,为Pod标记如下Annotation以告知Prometheus为其抓取系统指标。

annotations:    
    prometheus.io/path: /mymetrics
    prometheus.io/port: "7355"
    prometheus.io/scrape: "true"

Finalizer如果只看社区实现,那么该属性毫无存在感,因为在社区代码中,很少有对Finalizer的操作。但在企业化落地过程中,它是一个十分重要,值得重点强调的属性。因为Kubernetes不是一个独立存在的系统,它最终会跟企业资源和系统整合,这意味着Kubernetes会操作这些集群外部资源或系统。试想一个场景,用户创建了一个Kubernetes对象,假设对应的控制器需要从外部系统获取资源,当用户删除该对象时,控制器接收到删除事件后,会尝试释放该资源。可是如果此时外部系统无法连通,并且同时控制器发生重启了会有何后果?该对象永远泄露了。Finalizer本质上是一个资源锁,Kubernetes在接收到某对象的删除请求,会检查Finalizer是否为空,如果为空则只对其做逻辑删除,即只会更新对象中metadata.deletionTimestamp字段。具有Finalizer的对象,不会立刻删除,需等到Finalizer列表中所有字段被删除后,也就是该对象相关的所有外部资源已被删除,这个对象才会被最终被删除。因此,如果控制器需要操作集群外部资源,则一定要在操作外部资源之前为对象添加Finalizer,确保资源不会因对象删除而泄露。同时控制器需要监听对象的更新时间,当对象的deletionTimestamp不为空时,则处理对象删除逻辑,回收外部资源,并清空自己之前添加的Finalizer。 ResourceVersion通常在多线程操作相同资源时,为保证实物的一致性,需要在对象进行访问时加锁,以确保在一个线程访问该对象时,其他线程无法修改该对象。排它锁的存在确保某一对象在同一时刻只有一个线程在修改,但其排它的特性会让其他线程等待锁,使得系统整体效率显著降低。ResourceVersion可以被看做是一种乐观锁,每个对象在任意时刻都有其ResourceVersion,当Kubernetes对象被客户端读取以后,ResourceVersion信息也被一并读取。客户端更改对象并回写APIServer时,ResourceVersion会被增加,同时APIServer需要确保回写的版本比服务器端当前版本高,在回写成功后服务器端的版本会更新为新的ResourceVersion。因此当两个线程同时访问某对象时,假设它们获取的对象ResourceVersion为1。紧接着第一个线程修改了对象,资源版本会变为2,回写至APIServer以后,该对象服务器端ResourceVersion会被更新为2。此时如果第二个线程对该对象在1的版本基础上做了更改,回写APIServer时,所带的新的版本信息也为2,APIServer校验会发现第二个线程新写入的对象ResourceVersion与服务器端ResourceVersion冲突,写入失败,需要第二个线程读取最新版本重新更新。此机制确保了分布式系统中,任意多线程无锁并发访问对象,极大提升系统整体效率。Spec和Status

f6a076c44b41ecd5886c31e91e736757.png

Spec和Status才是对象的核心,Spec是用户的期望状态,由创建对象的用户端定义。Status是对象的实际状态,由对应的控制器收集实际状态并更新。与TypeMeta和Metadata等通用属性不同,Spec和Status是每个对象独有的,后续的章节会通过介绍一些核心对象来深入理解。为方便对Kubernetes对象的理解,下图展示了按照业务目的归类的常用Kubernetes对象和其分组。Kubernetes对象设计完全遵循互补的原则。鼓励API对象尽量实现面向对象设计时的要求,即“高内聚,松耦合”,对业务相关的概念有一个合适的分解,提高分解出来的对象的可重用性。高层API对象设计一定是从业务出发的,低层API对象能够被高层API对象所使用,从而实现减少冗余、提高重用性的目的。

7281c334a642a94a0822533409f73bc3.png

常用Kubernetes对象和其分组核心对象概览

32e95b35865d5ab4b2e3b64167d25efe.png

Kubernetes的对象设计避免了简单封装和内部隐藏机制。简单地封装,A对象封装了B对象的定义,实际没有提供新的功能,反而增加了对所封装API的依赖性。内部隐藏的机制也非常不利于系统维护的设计方式。例如StatefulSet、ReplicaSet和DaemonSet,如图所示,本来就是三种Pod集合,那么Kubernetes就用不同API对象来定义它们,而不会说将它们封装在同一个资源对象,内部再通过特殊的隐藏算法再来区分这个资源对象是有状态的、无状态的还是节点服务。Pod是Kubernetes应用程序的基本执行单元,即它是Kubernetes对象模型中创建或部署的最小和最简单的单元。多数核心对象都为Pod对象服务的,但是它们都从Pod对象中所剥离出来的,有自己的API定义。Secret、ConfigMap和PVC是不同的资源对象定义,都可以作为存储卷在Pod中使用。而在Pod中使用时,只需要指定该对象的名称即可,无需将其具体信息在Pod资源对象中扩展。

baa346f838761c9e053ef30d1bb8cb84.png

核心对象间的关系图 NamespaceNamespace是Kubernetes进行归类的对象,当一个集群有多个用户或一个用户有多个应用需要管理时,有时需要将所有被管理的对象进行一定的隔离。Kubernetes引入了Namespace对象,类似文件目录,不同对象被划分到不同Namespace以后,可以通过权限控制来限制哪些用户以何种权限访问哪些Namespace的哪些对象,进而构建一个多租户、彼此隔离的通用集群。 Pod容器云平台需要解决的最核心问题是应用运行,Kubernetes将容器化应用运行的实体抽象为Pod,Pod类似豆荚,它是一个或者多个容器镜像的组合。当应用启动以后,每一个容器镜像对应一组进程,而同一个Pod的所有容器中的进程默认公用同一网络Namespace,并且共用同一网络标识。Pod具有基本的自恢复能力,当某个副本出现问题时,它会按照预定策略被重启。当然,应用运行通常需要配置文件,这些配置文件又有可以明文读写的配置,也包含需要加密和严格权限控制的密码证书等配置,Kubernetes为这些配置分别定义了Configmap和Secret。Configmap和Secret,和PersistVolumeClaim类似,都可以作为卷加载给运行的Pod,Pod中运行的进程可以像访问本地文件一样访问它们。Configmap和Secret没有本质区别,Secret只是将内容进行base64编码,我们知道base64编码是一种对称加密,可以轻松解密,事实上没有太多安全性可言。但Kubneretes支持Secret在持久化时的加密存储,这样保存在硬盘的Secret数据是无法解密的。其次,Kubernetes可以通过权限严格控制能够访问Secret的用户,以保证密码和证书信息的安全。Pod除了包含用户希望运行的容器镜像和配置文件,还允许用户定义其运行所需的资源,用户创建Pod以后,Kubernetes会为其选择一个最佳节点运行。计算节点被抽象成Node对象,节点数量和每个节点的资源汇总起来就是整个集群能提供的算力。每个计算节点负责汇报自己的心跳信息,并上报节点的资源总量和可用资源。 ServiceAccountPod中运行的进程有时需要与Kubernetes API通信,在启用了安全配置的集群后,Pod一定要以某种身份与Kubernetes通信,这个身份就是系统账户(ServiceAccount)。Kubernetes会默认为每个Namespace创建一个default ServiceAccount,并且为每个ServiceAccount生成一个JWT Token,这个Token保存在Secret中。用户可以在其Pod定义中指定ServiceAccount(默认为default),其对应的Token会被挂载在Pod中,Pod中的进程可以带着该Token与Kubernetes通信以标识其身份。 ReplicaSetPod只是单个应用实例的抽象,要构建高可用应用,通常需要构建多个同样的副本,提供同一个服务。Kubernetes为此抽象出副本集ReplicaSet,其允许用户定义Pod的副本数,每一个Pod都会被当作一个无状态的成员管理,Kubernetes保证总是有用户期望的数量的Pod正常运行。当某个副本宕机以后,控制器将会创建一个新的副本。当因业务负载发生变更而需要调整扩缩容时,可以方便地调整副本数量。 Deployment对于无状态在线应用,Kubernetes提供了更高级的版本变更控制。版本变更是一个日常频繁发生的关键操作,如何在不中断业务的前提下更新版本,一直是业界努力解决的问题。Deployment就是一个用来描述发布过程的对象,其实现机制是,当某个应用有新版本发布时,Deployment会同时操作两个版本的ReplicaSet。其内置多种滚动升级策略,会按照既定策略降低老版本的Pod数量,同时创建新版本的Pod,并且总是保证正在运行的Pod总数与用户期望副本数一致,并依次将该Deployment中的所有副本都更新至新版本。下图展示了基于Deployment进行版本发布的一个中间状态。

ee9f97e4ab429738a067d8adf6bbe857.png

Deployment的滚动升级因为Deployment会维护ReplicaSet,ReplicaSet会创建Pod,因此通过Deployment维护针对无状态的应用是第一选择,它可以满足诸多需求,缩短应用上线的时间,在不造成停机的情况下创建弹性部署,能够使用户更快或更频繁地发布应用和功能。
  • 创建并保证目标数量的Pod在运行状态。

  • 按既定策略滚动升级,同时支持升级暂停、恢复和回滚。选择滚动升级策略非常灵活,正确的策略对于交付弹性应用程序和基础架构都是至关重要的。

  • 便利的扩容和缩容。

Service和Ingress即使在传统平台中,为支持应用的高可用,都需要在应用实例之上构建负载均衡。Service和Ingress就是描述负载均衡配置的对象,它允许用户定义发布服务的协议和端口,并定义Selector选择后端服务的Pod。Selector本身是一个Label过滤器,它会选择所有Label与该Selector匹配的Pod作为目标。Kubernetes会为Service和其选择出来的Pod创建一个关联对象,Endpoint里面记录了所有Pod的IP,以及就绪状态,这些信息会被相应组件作为期望状态进行负载均衡配置。Ingress是在服务的基础上,定义API网关的对象。通过Ingress,用户可以定义七层转发规则、网关证书等高级路由功能。 PersistentVolume和PersistentVolumeClaimPersistentVolume(PV)是集群中的一块存储卷,可由管理员手动设置,或当用户创建PersistentVolumeClaim(PVC)时根据StorageClass动态设置。PV和PVC与Pod生命周期无关。也就是说当Pod中的容器重新启动、Pod重新调度或者删除时,PV和PVC不会受到影响,Pod存储于PV里的数据得以保留。对于不同的使用场景,用户通常需要不同属性(例如性能、访问模式等)的PV。所以集群一般需要提供各种类型的PV,由StorageClass来区分。一般集群环境都设置了默认的StorageClass。如果在PersistentVolumeClaim中未指定StorageClass,则使用群集的默认StorageClass。 CustomResourceDefinition自定义资源定义(CRD)是Kubernetes 1.7中引入的一项强大功能,它允许用户将自己的自定义对象添加到Kubernetes集群中,当创建新CRD的定义时,APIServer将为指定的每个版本创建一个新的RESTful资源路径。当集群中成功地创建了CRD,就可以像Kubernetes原生的资源一样使用它,利用Kubernetes的所有功能,例如其CLI、安全性、API服务、RBAC等。CRD的定义是集群范围内的,CRD的资源对象的作用域可以是命名空间(Namespaced)或者集群范围(Cluster-wide)的。与现有的内置对象一样,删除Namespace也会删除该Namespace中所有自定义的对象,但不会删除CRD的定义。Kubernetes还提供一系列Codegen工具(deepcopy-gen、client-gen、lister-gen、informer-gen等),能够自动生成该CRD资源的Golang版本的Clientset、Lister及Informer,这为该资源编写控制器提供了很大便利。CRD就像数据库的开放式表结构,允许用户自定义Schema。有了这种开放式设计,使得用户可以基于CRD定义一切需要的模型,满足不同业务的需求。社区鼓励基于CRD的业务抽象,众多主流的扩展应用都是基于CRD构建的,比如Istio,比如Knative。甚至基于CRD推出了Operator Mode和Operator SDK,可以以极低的开发成本定义新对象,并构建新对象的控制器。控制器模式

f6a076c44b41ecd5886c31e91e736757.png

声明式系统的工作原理是什么?当用户定义了对象的期望状态,Kubernetes通过何种机制确保实际状态与期望状态最终保持一致?定义了如此多的对象,那么这些对象是如何联动起来,完成一个个业务流的呢?秘密就是控制器模式,Kubernetes定义了一系列的控制器,事实上几乎所有的Kubernetes对象都被一个或数个控制器监听,当对象发生变化时,控制器会捕获对象变化并完成配置操作。Kubernetes的功能组件会在后面章节中展开,但本节深入理解控制器模式有助于理解Kubernetes的运作机制。APIServer是Kubernetes的大脑,保存了所有对象和其状态。开源项目client-go对控制器的编写提供了完备的自动化支持,任何Kubernetes对象都可以由client-go创建供控制器使用的Informer()和Lister()接口。如图所示,控制器的工作流程就是围绕着Informer()和Lister()的。
  • Informer()是用来接收资源对象的变化的Event,针对Add、Update和Delete的事件,可注册相应的EventHandler。在EventHandler内,根据传入的object调用controller.KeyFunc计算出字符串key,并把它加入控制器的队列中。

  • Lister()是给控制器提供主动查询资源对象的接口,根据labels.Selector去指定筛选条件。

控制器模式是一个标准的生产者消费者模式,一方面控制器在启动后,Informer会监听其所关注的对象变化。一旦对象发生了创建,更新和删除等事件,这些事件会由核心组件APIServer推送给控制器。控制器会将对象保存在本地缓存,并将对象的主键推送至消息队列,此为生产者。另一方面,控制器会启动多个工作子线程(Worker),从队列中依次获取对象主键,并从缓存中读取完整状态,按照期望状态完成配置更改并将最终状态回写至APIServer,此为消费者。Kubernetes就是基于此模式保证了整个系统的最终一致性。

45a251745d65c89c3e5c7b1bd6e01d17.png

控制器的工作流程Kubernetes运行一组控制器,以使资源的当前状态与所需状态保持匹配。基于事件的体系结构,控制器利用事件去触发相应的自定义代码,这部分都是由SharedInformer完成。例如创建Deployment的控制器,其核心代码如下:

kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, resyncPeriod)
deploymentInformer := kubeInformerFactory.Apps().V1().Deployments()
deploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
  AddFunc: controller.handleObject,
  UpdateFunc: func(old, new interface{}) {
     newDepl := new.(*appsv1.Deployment)
     oldDepl := old.(*appsv1.Deployment)if newDepl.ResourceVersion == oldDepl.ResourceVersion {return
     }
     controller.handleObject(new)
  },
  DeleteFunc: controller.handleObject,
})
kubeInformerFactory.Start(stopCh)

具体地,如图所示,SharedInformer有Reflector、Informer、Indexer和Store四个组件。

8982ef5ecd8bcf0937f4dd2ec2999c3d.png

Informer的内部机制Reflector是用来监听特定的Kubernetes API资源对象,可以是Kubernetes内建的或者是自定义的资源。具体的实现是通过ListAndWatch的方法。Reflector首先会将资源版本号设置为0,使用List操作获得指定资源对象,可能会导致本地的缓存相对于etcd里面的内容存在延迟。Reflector再通过Watch操作监听到APIServer处资源对象的版本号变化,并将最新的数据放入到Delta FIFO队列中,使得本地的缓存数据与etcd的数据保持一致。如果resyncPeriod不为零,那么Reflector会以resyncPeriod为周期定期执行Delta FIFO的Resync函数,这样就可以使Informer定期处理所有的对象。Informer是从Delta FIFO队列中弹出对象,一方面将对象存入本地存储以供检索,另一方面触发事件以调用资源事件回调函数。控制器后续的典型模式是获取资源对象的key,并将该key排入工作队列以进行进一步处理。Indexer提供对象的索引功能。Indexer可以根据多个索引函数维护索引。Indexer使用线程安全的数据存储来存储对象及其键。在Store中定义了一个名为MetaNamespaceKeyFunc的默认函数,该函数生成对象的键的格式是/的组合。控制器的协同工作原理

f6a076c44b41ecd5886c31e91e736757.png

单个Kubernetes资源对象的变更,触发多个控制器对该资源对象的变更进行响应,继而还能引发其相关的其他对象发生变更,从而触发其他对象控制器的配置逻辑,这一模式使得整个系统成为声明式。下图简要描述了用户创建一个Deployment对象时各个控制器是如何协同工作的。

49056342655639d707fb08b95e25ea3e.png

协同工作流程示例除APIServer和etcd外,所有Kubernetes组件,不论其名称是Scheduler,Controller Manager、或是Kubelet,其本质都是一致的,都可以被称为控制器,因为这些组件中都有一个控制循环。他们监听APIServer中的对象变更,并在自己关注的对象发生变更后完成既定的逻辑控制,并将控制逻辑执行完成后的结果更新回APIServer,并持久化到etcd中。APIServer作为集群的API网关,接收所有来自用户的请求。用户发创建Deployment之后,该请求被发送至APIServer,经过认证鉴权和准入三个环节,该Deployment对象被保存至etcd。Controller Manager中的Deployment Controller监听APIServer中所有Deployment的变更事件,此时其捕获了Deployment的创建事件,并开始执行控制逻辑。Deployment Controller读取Deployment对象的Selector定义,并通过该属性过滤当前Namespace中所有ReplicaSet对象,并判断是否有任何ReplicaSet对象的OwnerReference属性为此Deployment。因为此Deployment刚刚创建,因此没有满足此查询条件的ReplicaSet,于是Deployment Controller会读取Deployment中定义的podTemplate,并将其做哈希计算,并依照如下约定创建新的ReplicaSet:
  • 并创建新的ReplicaSet,将其命名为[deployment-name]-[pod-template-hash]。

  • 更新ReplicaSet,为ReplicaSet添加label,记pod-template-hash值为[计算出的哈希值]。

  • 将Deployment设置为ReplicaSet的OwnerReference。

Deployment Controller将新的ReplicaSet创建请求发送至APIServer,APIServer同样的经过认证授权和准入步骤,将该对象保存至etcd。ReplicaSet Controller监听APIServer中所有ReplicaSet对象的变更,新对象的创建令其唤醒并开始执行控制逻辑。ReplicaSet Controller读取ReplicaSet对象的Selector定义,并通过该属性过滤当前Namespace中所有Pod对象,并判断是否有任何Pod对象的OwnerReference为该ReplicaSet。因为此ReplicaSet刚刚创建,因此没有满足此查询条件的Pod,于是ReplicaSet会按照如下约定创建Pod:
  • 读取Replicas定义,Replicas的数量代表需要创建Pod的数量。

  • 以ReplicaSet名作为Pod的GenerateName,该属性会作为Pod名的前缀,Kubernetes在此基础上加一个随机字符串作为Pod名。

  • 该ReplicaSet作为Pod的OwnerReference。

ReplicaSet Controller将新建Pod的请求发送至APIServer,APIServer将Pod悉数保存。此时调度器被唤醒,其监听APIServer中所有nodeName为空的Pod,即未经过调度的Pod。经过一系列的调度算法,不满足Pod需求的节点被过滤,符合的节点按照空闲资源,端口占用情况,实际资源利用率等信息被排序,评分最高的节点名被更新至nodeName属性,该同样经APIServer保存至etcd。最后,运行在Pod被调度节点的Kubelet监听到有归属于自己节点的新Pod,则开始加载Pod清单,下载Pod所需的配置信息,调用容器运行时接口启动容器,调用容器网络接口加载网络,调用容器存储接口挂载存储,并完成Pod的启动。Kubernetes就是依靠这样的联动机制,通过分散的业务控制逻辑满足用户需求。从用户的角度看,只是发送了一个Deployment创建请求,但事实上,为满足该需求,可能会牵扯到数个甚至更多Kubernetes组件。此架构模式的优势是每个组件各司其职,巧妙而灵活,代码易维护,但带来的运维复杂度相对较高,此业务流中有任何组件出现故障,对用户感受来讲,都是Kubernetes不可用。留言福利

f6a076c44b41ecd5886c31e91e736757.png

本文节选自《Kubernetes生产化实践之路》,经出版社授权发布,本书来自eBay五年来Kubernetes生产实践的经验总结。为了感谢粉丝的支持特意送出6本此书作为福利。欢迎大家在留言区 谈谈学习Kubernetes的心得,截止2020年12月14日12时,精选留言点赞前6名,各送出此书一本!没中奖的同学可以点击下方链接购买。Kubernetes实战培训

f6a076c44b41ecd5886c31e91e736757.png

Kubernetes实战培训将于2020年12月25日在深圳开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习。本次培训包括:云原生介绍、微服务;Docker基础、Docker工作原理、镜像、网络、存储、数据卷、安全;Kubernetes架构、核心组件、常用对象、网络、存储、认证、服务发现、调度和服务质量保证、日志、监控、告警、Helm、实践案例等,点击下方图片或者阅读原文链接查看详情。

5ec95b9b55bdca2afc59a495d5206038.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程设计任务书 "一、设计题目 " "基于微程序控制器的简单计算系统设计与实现 " "二、设计内容 " "设计模型系统的总体结构、指令系统和时序信号。充分利用FPGA以及相关外围" "器件,设计实现模型的整系统。要求所设计的整系统能支持自动和单步运" "行方式,能正确地执行存放在主存中的程序的功能,对主要的数据流和控制流通" "过LED适时显示信息。尽可能利用EDA软件对模型系统中各部件进行仿真分析和" "功能验证。在学有余力的前提下,可为模型增加更多的扩展功能。 " "三、设计要求 " "支持算术运算、逻辑运算、存储器读写、寄存器间数据传送等几类指令; " "支持立即数寻址、直接寻址、隐含寻址、寄存器寻址等几种基本数据寻址方式和" "顺序寻址、跳跃寻址方式; " "支持10条以上的指令; " "能运行由自己设计的指令系统构成的一段程序,程序执行功能正确。 " "四、设计流程 " "根据课程设计指导书的要求,制定出设计方案; " "画出模型计算系统的原理框图和器件连接图,分析器件连接图中各器件不同引" "脚的功能,哪些可以固定连接,哪些需要通过微程序来控制,以及这些控制信号" "的有效形式; " "画出各指令的指令周期流程图和所需要的控制信号; " "设计出实现指令功能的微程序控制器或硬布线控制器; " "布线、调试、验收; " "课程设计报告和总结。 " "五、成绩评定 " " " "成绩评定根据考勤、课程设计的过程、课程设计的效果、课程设计报告质量等进" "行综合评定;其中设计过程和结果占70%,课程设计报告占30%;课程设计的成绩" "评定等级为不及格、及格、中、良好、优秀五级;对基本功能进行扩展或设计具" "有非常鲜明的特征和一定程度的创新,可根据实际情况加分。 " "六、设计报告要求 " " " "课程设计报告主要内容包括:设计题目、设计目的、设备器材、设计原理及内容" "、设计步骤、遇到的问题及解决方法、设计总结、参考文献等。要求在适当位置" "配合相应的实验原理图、数据通路图、微程序流程图、实验接线图、微指令代码" "表等图表进行说明。总结部分主要写设计工作简介以及设计体会。应做到文理通" "顺,内容正确完整,书写工整,装订整齐。课程设计报告采用《计算组成原理》" "专用设计报告模板,A4纸双面打印,除框图外需附下载的设计图以及微指令控制" "点图。 " "七、时间安排 " " 课程设计的总体时间为2周,具体安排如下: " "第1天:到实验室布置任务和集中讲解。 " "第2天:学生自己设计,选择并熟悉自己所要的芯片。 " "第3天:领导线和芯片,进行芯片功能测试,在此基础上修改完善原设计方案。 " "第4天到第10天:实验室调试、验收。 " "八、主要参考文献 " "秦磊华,王小兰. 计算组成原理实验指导及课程设计指导书(基于EDA平台). " "武汉:华中科技大学出版社,2010年. " "秦磊华,吴非,莫正坤.计算组成原理. 北京:清华大学出版社,2011年. " "DAVID A.PATTERSON(美).计算组成与设计硬件/软件接口(原书第3版).北京: " "械工业出版社. 2007年. " "袁春风编著. 计算组成与系统结构. 北京:清华大学出版社,2011年. " " " " " 目 录 1 课程设计概述 3 1.1 课设目的 3 1.2 设计任务 3 1.3 设计要求 3 2 实验原理与环境 4 2.1 实验原理 4 2.2 实验环境 4 3 总体方案设计 5 3.1 需求分析 5 3.2 硬件设计 5 3.3 软件设计 8 4 详细设计与实现 10 4.1 选用芯片 10 4.2 硬件实现 11 4.3 软件实现 14 5 实验过程与调试 19 5.1 仿真XXX 19 5.2 可自行安排章节 19 5.3 主要故障与调试 19 5.4 功能测试 19 5.5 实验流程图 20 6 设计总结与心得 21 6.1 课设总结 21 6.2 课设心得 21 参考文献 22 课程设计概述 请仔细阅读所有的批注,阅读理解后删除批注 模板各个标题下面的内容仅是举例,作者应依照自己思想重写该部分内容 1、格式不要轻易改动,严格按照模板来,不要自创格式。 2、所以批注阅读后一定要删除 3、模板中的图示意图,不能照搬,要根据自己的设计来画 4、参考文献只是格式和引用方式参考,列出自己实际参考文献 5、图中的文字一定要清晰。 6、所有的表和图都必须有名字和编号。 1 课设目的 计算组成原理是计算专业的核心专业基础课。课程设计属于设计型实验,不仅锻 炼学生简单计算系统的设计能力,而且通过进行设计及实现,进一步提高分析和解决 问题的能力。(自己写) ……(其它内容) 2 设计任务 计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值