-
通过Docker运行TensorFlow 该方式的优点是不用操心软件依赖问题。方法 首先,安装Docker,一旦Docker已经启动运行,可以通过命令启动一个容器:$ docker run -it b.gcr.io/tensorflow/tensorflow 该命令将启动一个已经安装好的TensorFlow及相关依赖的容器。其它镜像 默认的 Docker 镜像只包含启动和运行 TensorFlow 所需依赖库的一个最小集. 我们额外提供了 下面的容器, 该容器同样可以通过上述 docker run 命令安装: b.gcr.io/tensorflow/tensorflow-full: 镜像中的 TensorFlow 是从源代码完整安装的, 包含了编译和运行 TensorFlow 所需的全部工具。 在该镜像上, 可以直接使用源代码进行实验, 而不需要再安装上述的任何依赖.
-
活动时间:2021年9月16日~2021年10月20日 参与方式:完成下列实验,并在本帖中回复实验完成截图,即可视为完成任务,我们在活动结束后统一核对完成情况。 使用ModelArts实现花卉图像分类基于ModelArts JupyterLab在线调优钢筋检测基于IoT平台构建智慧路灯应用基于CCE进行云原生应用部署与运维管理基于容器实现一分钟自动化部署基于Spark实现车主驾驶行为分析MapReduce服务初体验使用华为云DevCloud实现20分钟一行代码上云基于DevCloud进行黑白棋实时对战游戏开发使用Python爬虫抓取图片和文字实验活动奖励:完成1个沙箱体验即可获得10积分(技能提升直通车中沙箱和微认证积分总和最高可获得100积分)活动规则:请严格按照实验要求体验,并回复沙箱实验截图Tips:1、请务必使用个人账号参与活动(IAM、企业账号等账号参与无效)。2、所有获得华为电子产品奖项的获奖用户,请于获奖后3日内完成实名认证,否则视为放弃奖励。3、收货信息填写说明:1)为保证您顺利领取活动奖品,请您提前填写奖品收货信息,如您没有填写,视为放弃奖励。收货信息请【点击此处填写】2)填写时间截至2021年10月25日23:59。3)在HC2021开发者社区系列活动中完成一次填写即可。我们最终将会按照您填写的信息发放奖励。4、活动规则请戳https://bbs.huaweicloud.com/forum/thread-154048-1-1.html
-
Volcano是一个基于Kubernetes的云原生批量计算平台,也是CNCF的首个批量计算项目。Volcano 主要用于AI、大数据、基因、渲染等诸多高性能计算场景,对主流通用计算框架均有很好的支持。它提供高性能计算任务调度,异构设备管理,任务运行时管理等能力。本篇文章将从Volcano架构、Volcano核心概念及功能、Volcano Code Tour、平台组件安装部署等方面来带大家认识Volcano。 Volcano架构 1、Volcano全景Volcano是基于Kubernetes的高性能批量计算平台,目前支持几乎所有的主流计算框架,包括MindSpore、TensorFlow、Kubeflow、MPI、PyTorch、飞浆、Spark、HOROVOD 等。Volcano支持的部分计算框架 计算框架遇到的问题:1)1:1的operator部署运维复杂2)不同框架对作业管理、并行计算等要求不同3)计算密集高,资源需求波动大,需要高级调度能力 Volcano面向主流计算框架提供:1)统一容器基础设施,提高资源利用率2)通用作业管理、队列Fair-share, Gang, bin-pack等高级调度算法3)简化运维管理 2、Volcano整体架构Volcano利用声明式的CRD定义我们的API,主要有3个核心的API,Volcano Job、PodGroup、Queue。Volcano Job 是对高性能任务的通用定义,PodGroup提供了Job中Task的管理能力,Queue 为任务的分类提供了基础。Volcano的架构 Volcano 核心组件主要包含三个:Admission、ControllerManager、Scheduler 。Admission对Volcano CRD API提供校验能力;ControllerManager负责对Volcano CRD进行资源管理;Scheduler对任务提供丰富的调度能力。 3、Volcano工作流程从零开始运行Volcano作业:1)用户创建一个 Volcano 作业2)Volcano Admission 拦截作业的创建请求,并进行合法性校验3)Kubernetes 持久化存储 Volcano Job 到 ETCD4)ControllerManager 通过 List-Watch 机制观察到Job 资源的创建,创建任务(Pod)5)Scheduler 负责任务的调度,绑定 Node6)Kubelet Watch 到 Pod的创建,接管 Pod 的运行7)ControllerManager 监控所有任务的运行状态,保证所有的任务在期望的状态下运行 Volcano核心概念及功能1、Volcano核心概念1)Queue:Queue的概念源于 Yarn,它是Cluster 级别的资源对象,可为其声明资源配额,也可由多namespace 共享,并且提供 soft isolation2)PodGroup:PodGroup是任务的分组,它与 queue 绑定,占用队列的资源。它与 Volcano Job 是一对一的关系;也可为其声明 Scheduling 条件3)Volcano Job:它是批量计算作业的定义,支持定义作业所属队列、生命周期策略、所包含的任务模板以及持久卷等信息 2、作业管理插件 svc:提供不同类型任务之间互访能力env:任务索引,例如 Tensorflow Worker indexssh:ssh 秘钥对创建及挂载,主要供 MPI 作业使用 3、Scheduler架构Scheduler支持动态配置和加载。 4、核心调度算法1)Gang Scheduling2)Fair Share3)Preempt & Reclaim4)Reserve & Backfill5)Topology Aware Scheduling6)GPU Sharing Volcano Code Tourcmd目录是Volcano所有组件启动的入口;config 是Volcano的配置;defs 是安装时的配置;docs 是Volcano的设计文档;example 提供了简单的例子,hack 提供安装时的脚本;installer 提供安装的模板。 pkg 是最重要的目录,里面包含了 api、controller、scheduler 、webhook 等代码。test 提供了e2e测试用例, vendor是依赖库。 安装部署1、Volcano InstallVolcano安装部署有多种方式:若已存在K8S集群,建议通过 Helm方式安装部署,该方式支持自定义安装配置;开发者建议通过Development Yaml方式部署。 对于开发者,Volcano已内置一键式安装部署脚本,路径为 volcano. sh/volcano/hack/local-up-volcano. sh。运行该脚本时,默认会使用kind创建 Docker in Docker的模拟集群,并安装部署Volcano。 2、Volcano 组件正确安装部署后,将生成4个组件,分别为:Volcano-admission、Volcano-admission-init、Volcano-controllers、 Volcano-scheduler ,其中admission-init以作业的方式生成证书。
-
KubeEdge即Kube+Edge,顾名思义就是依托K8s的容器编排能力和调度能力,实现云边协同、计算下沉、海量设备的平滑接入。本篇文章将从KubeEdge架构设计理念、KubeEdge代码目录概览、KubeEdge集群部署三方面带大家认识KubeEdge。 KubeEdge架构设计理念 1、Kubernetes的架构这里是一个经典的K8s架构,K8s相信大家已经了解比较多了,它主要是分为控制面和数据面,而现在K8s的生态已经非常火爆了,关于应用管理和容器管理已经形成了一套标准,这里列举了它的一些优势:只有API server可以访问etcd组件通过 API Server 访问集群状态API采用声明式设计API对象彼此互补、可组合优先使用事件监听而不是轮询… 2、基于Kubernetes构建边缘计算的优势与痛点 核心优势主要有4方面: 容器化应用封装现在已经成为应用交付的一个趋势,我可以把我的应用打包到容器里,我只打包一次,可以跑在各种地方,这种如果应用到我们IOT领域,我们传统有很多IOT嵌入式设备,它其实很多硬件和软件强相关的,如果换一个硬件,可能软件就要更改,如果说我这个容器化封装以后,设备可支持容器runtime,我可以将容器跑在任何IOT设备上。通用应用抽象定义:K8s的API,包括development、pod现在其实在业内已经形成一套标准,大家都比较了解和认可,其实我们基于这些应用做这个平台,大家也更能容易接受。 松耦合架构:它的可扩展性比较好,比如我们基于K8s之上可以通过CRD来定义一些API,像我们通过设备管理CRD来定义一些IOT里device的一些API,到时候我们可以直接通过K8s的一些方式来管理这些设备;还有一些可扩展,比如它的CIA可以对接各种runtime,我们有些边缘节点它的资源非常有限,我们就可以对接一些轻量化的runtime。 其关键痛点有:1)资源有限网关设备,128MB内存K8s集群需要至少1G内存 2)网络不畅边缘位于私有网络,无公网IP云边跨越公网,带宽有限,延迟高K8s的List-watch需要数据中心网络 3)边缘如何离线自治网络不稳,随时可能离线边缘业务离线可工作边缘离线可故障恢复 4)设备接入和管理缺少边缘设备抽象缺少边缘设备接入协议支持 3、KubeEdge 架构与核心理念我们这个架构主要是分了云、边、端三部分,云上边就是我们的控制面,边就是我们的边缘节点,端就是跑了我们的一些端侧设备,云上左边是一个K8s的master,是没有做过改动的原生的K8s控制面,后边我们加了我们的一个组件叫CloudCore,它云上的组件主要是会拿一些K8s控制面上的东西,通过EdgeController和DeviceController做一些处理,然后通过下边的Cloud Hub,Cloud Hub主要是跟边端通信的,边端有个EdgeHub和Cloud Hub通信,然后把数据拿下来。边端是主要做了一个应用管理和设备管理的能力,应用管理左边会有一个Edged,右边有DeviceTwin、EventBus,分别是应用管理和设备管理,左边有个DataStore,就是我们说的本地自治的能力,比如说我们这应用或者设备的元素从云上分发下来,我们是先把它存到一个数据库里,然后再到它的Edged或者设备里边,这样就能保证云边网络断开或者边缘节点重启了以后我应用的Edged它可以从数据库里把应用源数据拿出来,这样就能保证在故障的情况下业务可以正常恢复。 核心理念:1)云边可靠协同双向多路复用消息通道,支持边缘节点位于私有网络Websocket + 消息封装,大幅减少通信压力,高时延下仍可正常工作云边消息校验,网络不稳定时不丢数据 2)边缘离线自治 节点元数据持久化,实现节点级离线自治节点故障恢复无需List-watch,降低网络压力,快速ready 3)边缘极致轻量重组Kubelet功能模块,极致轻量化(~70mb内存占用)支持CRI集成Containerd、CRI-O,优化runtime资源消耗 4)边缘设备管理云端通过Kubernetes API管理边缘Device 4、KubeEdge 社区生态KubeEdge致力于将Kubernetes的能力拓展到边缘业界首个边缘容器平台项目Apache 2.0协议2019年3月捐给CNCF基金会2020年9月晋级为孵化级托管项目K8s IoT Edge WG参考架构基于Kubernetes构建,100%兼容K8s API11个特性版本,最新版本为v1.6.0(截止2021年3月)3600+ Star,900+ Fork,550+贡献者目前成立3个社区SIG: SIG Device IoT、SIG MEC、SIG AI参与社区贡献的企业包括:中国联通,ARM,中国移动,谐云,中国电信,时速云,JD.com,浙大SEL实验室,EMQ,InfoBlox,Inovex,Midokura等 KubeEdge代码目录概览 ADOPTERS就是我们社区的一些采纳者,比如说你用了KubeEdge,并且想成为参与者,建议者,你可以提一个PR,把你们写到这个ADOPTER里面去,下面的这些就是代码目录,主要就是cloud(云端)、edge(边缘端)、mappers(接入设备的mapper端),还有OWNERS是我们项目的一些matiner,主要负责核代码,比如你对我们社区贡献比较多,我们可以把你加到OWNERS,帮我们核代码和检视代码。 KubeEdge集群部署1、KubeEdge 集群部署工具—— keadm这个是借鉴了K8s的Kubeadm,可以一键部署KubeEdge集群,在部署KubeEdge集群时,要先装一个K8s的master,这个master用任何符合K8s的标准都可以,这个 keadm是基于K8s之上部署KubeEdge系统。 子命令参数:init:部署云端组件join:部署边缘端组件gettoken:从云端获取边缘端启动凭据reset:重置KubeEdge集群的云端和边缘端 2、KubeEdge 部署 —— 云端在已经装好的master上装我们的云端,用 init即可:重要参数:--kube-config:连接K8s Master的凭据--advertise-address:签发到边缘证书里的IP地址 3、KubeEdge 部署 —— 边缘端边缘端主要用我们的join命令:重要参数:--token:边缘端启动时访问云端的凭据--cloudcore-ipport:边缘端访问的云端IP地址
-
随着云原生技术的普及,使用Kubernetes部署应用已经成为常态,越来越多的企业已经步入多集群时代。随着集群数量的增长,对集群的运维、管理带来了新的挑战:集群繁多的重复劳动:运维工程师需要应对繁琐的集群配置、不同云厂商集群间的管理差异以及碎片化的API访问入口等问题;业务过度分散的维护难题:应用在各集群的差异化配置繁琐;业务跨云访问以及集群间的应用同步难以管理。集群的边界限制:应用的可用性受限于集群;资源调度、弹性伸缩受限于集群。厂商绑定:业务部署的黏性问题,缺少自动化故障迁移;缺少中立的开源多云容器编排项目。 Karmada结合了华为云多云容器平台MCP以及Kubernetes Federation核心实践,并融入了众多新技术:包括Kubernetes原生API支持、多层级高可用部署、多集群自动故障迁移、多集群应用自动伸缩、多集群服务发现等,并且提供原生Kubernetes平滑演进路径,让基于Karmada的多云方案无缝融入云原生技术生态,为企业提供从单集群到多云架构的平滑演进方案。Karmada项目全景上图为Karmada在开源社区技术全景,区别于kubefed只做多集群应用分发的局限,Karmada将以模块化的方式提供应用多集群部署、高可用调度、故障迁移、多集群服务发现和流量治理、多云集群生命周期管理等能力集,并面向多种典型的用户场景预置策略集,让用户可以结合企业实际情况自由定制适合自身的多云平台。 2、Karmada关键1)Kubernetes 原生 API兼容既有应用配置及基础设施无需改造,由单集群架构平滑升级到多集群(多云)架构,无缝集成Kubernetes现有工具链生态2)开箱即用面向多场景的内置策略集,包括两地三中心、同城双活、异地容灾等支持应用的跨集群上的自动伸缩、故障迁移和负载均衡3)集中式管理提供地域无关的集中式集群管理支持公有云、私有云或边缘集群4)丰富的多集群调度策略多集群亲和性调度、应用跨集群拆分、资源重新平衡多维度多层次的高可用部署:区域/可用区/集群/供应商等5)开放中立由多家互联网、金融、制造业、电信、云服务厂商共同发起以 CNCF 的开放治理为目标 3、Karmada架构设计Karmada 控制平面由以下几个组件组成:API 服务器(Karmada API Server)控制管理器(Karmada Controller Manager )调度器 (Karmada Scheduler)Karmada通过独立的API 服务器(Karmada API Server)提供与其他组件进行通信的 REST 接口,包含Kubernetes原生API及Karmada扩展API,而Karmada 控制管理器根据用户创建的 API 对象执行操作, Karmada 调度器则实现应用在多集群中的调度。Karmada 控制器运行各种控制器,控制器监视Karmada 的对象,然后与底层集群的 API 服务器通信,对Kubernetes资源进行全生命周期管理。集群控制器:聚焦集群管理,将 Kubernetes 集群附加到 Karmada,通过创建集群对象管理集群的生命周期。策略控制器:实现PropagationPolicy对象的生命周期。根据 PropagationPolicy中的resourceSelector 匹配对应Kubernetes资源对象,并为创建ResourceBinding以进行应用多集群调度。绑定控制器:实现 ResourceBinding 对象的生命周期,根据调度器的角度结果,为每个调度到目标集群的对应资源创建Work 对象;执行控制器:负责work对象与成员集群中实际资源对象的状态同步。 4、未来展望Karmada计划在今年Q4完成整体技术栈的能力开发,发布1.0版本,并捐赠CNCF。 小红书技术部负责人张雷表示:“小红书一直努力为云原生产业发展贡献自己的力量,希望通过Karmada项目,把小红书构建多云业务的实践经验贡献给社区,让更多企业能享受云原生技术的红利。”CNCF总经理Priyanka Sharma也对Karmada项目的开源作出了回应:“华为一直是云原生社区与开发者生态的重要参与者,此次发布的Karmada对所有企业构建多云业务架构至关重要,希望未来CNCF与华为云继续密切合作,持续帮助广大云原生开发者。” 未来,我们也希望越来越多的开发者能加入Karmada社区,共建多云生态!关于 Karmada 的更多技术细节,请查看项目仓库 https://github.com/karmada-io/karmada扫码添加小助手,发送“karmada”加群社区专家入驻,技术问题随时答疑
-
Kubernetes 是当前非常流行的容器编排框架,在其发展早期重点以微服务类应用为主。 但随着Kuberentes的用户越来越多,更多的用户希望在Kubernetes上运行BigData和AI框架,如Spark、TensorFlow等以构建统一的容器平台。但在Kubernetes运行这些高性能应用时,Kubernetes的默认调度器无法满足高性能应用的需求,例如:公平调度、优先级、队列等高级调度功能。由于Kubernetes的默认调度器是基于Pod进行调度,虽然在1.17中引入了调度框架,但仍无法满足高性能应用对作业级调度的需求。 针对云原生场景下的高性能应用场景,华为云容器团队推出了Volcano项目。Volcano是基于Kubernetes构建的一个通用批量计算系统,它弥补了Kubernetes在“高性能应用”方面的不足,支持TensorFlow、Spark、MindSpore等多个领域框架,帮助用户通过Kubernetes构建统一的容器平台。Volcano作为容器调度系统,不仅包括了作业调度,还包含了作业生命周期管理、多集群调度、命令行、数据管理、作业视图及硬件加速等功能。而在调度方面,Volcano 又对场景进行了细分、归类,并提供了相关的方案及算法;同时也为这些功能提供了调度框架,方便用户对调度器进行扩展。对于分布式计算或是并行计算来说,根据场景和作业属性的不同,也可以对其进行细分;在 《并行计算导论》 中将并行计算大致分为三类: 简单的并行简单的并行指多个子任务(tasks)之间没有通信也不需要同步,可以完全的并行的执行。比较著名的例子应该就属MapReduce了,它的两个阶段都属于这种类型:mapper任务在执行时并不会彼此通信同步运行状态;另一个常见的例子是蒙特·卡罗方法 ,各个子任务在计算随机数时也无需彼此通信、同步。由于这种并行计算有比较广泛的应用,例如 数据处理、VatR 等,针对不同的场景也产生了不同的调度框架,例如 Hadoop、DataSynapse 和 Symphony。同时,由于子任务之间无需信息和同步,当其中某几个计算节点(workers)被驱逐后,虽然作业的执行时间可能会变长,但整个作业仍可以顺利完成;而当计算节点增加时,作业的执行时间一般都会缩短。因此,这种作业也常常被称作 Elastic Job。 复杂的并行复杂的并行作业指多个子任务 (tasks) 之间需要同步信息来执行复杂的并行算法,单个子任务无法完成部分计算。最近比较有名的例子应该算是 Tensorflow 的 "ps-work模式" 和 ring all-reduce 了,各个子任务之间需要大量的数据交换和信息同步,单独的子任务无法独立完成。正是由于作业的这种属性,对作业调度平台也提出了相应的调度要求,比如 gang-scheduling、作业拓扑等。由于子任务之间需要彼此通信,因此作业在启动后无法动态扩展子任务,在没有checkpoint的情况下,任一子任务失败或驱逐,整个作业都需要重启,这种作业也常常被称作 Batch Job,传统的HPC场景多属于这种类型的并行作业,针对这种场景的调度平台为 Slurm/PBS/SGE/HTCondor 等。 流水线并行流水线并行是指作业的多个子任务之间存在依赖关系,但不需要前置任务完全结束后再开始后续的任务;比如 Hadoop 里有相应的研究:在 Map 没有完全结束的时候就部分开始 Reduce 阶段,从而提高任务的并行度,提高整体的运行性能。符合这种场景的应用相对来说比较少,一般都做为性能优化;因此没有针对这种场景的作业管理平台。需要区分一下工作流与流水线并行,工作流一般指作业之间的依赖关系,而流水线并行一般指作业内部多个任务之间的依赖。由于工作流中的作业差异比较大,很难提前开始后续步骤。 值得一提的是"二次调度"。由于简单并行的作业一般会有大量的子任务,而且每个子任务所需要的资源相对一致,子任务之间也没有通信和同步;使得资源的复用率相对比较高,因此二次调度在这种场景下能发挥比较大的作用;Hadoop的YARN,Symphony的EGO都属于这种类型。但是在面对复杂并行的作业时,二次调度就显得有也吃力;复杂并行作业一般并没有太多的子任务,子任务之间还经常需要同时启动,子任务之间的通信拓扑也可能不同 (e.g. ps/worker, mpi),而且作业与作业之间对资源的需求差异较大,因此导致了资源的复用率较低。 虽然针对两种不同并行作业类型有不同的作业、资源管理平台,但是根本的目标都是为作业寻找最优的资源;因此,Volcano一直以支持以多种类型的作业为目标进行设计。目前,Volcano可以同时支持 Spark、TensorFlow和MPI等多种类型的作业。常见调度场景组调度 (Gang-scheduling)运行批处理作业(如Tensorflow/MPI)时,必须协调作业的所有任务才能一起启动;否则,将不会启动任何任务。如果有足够的资源并行运行作业的所有任务,则该作业将正确执行;但是,在大多数情况下,尤其是在prem环境中,情况并非如此。在最坏的情况下,由于死锁,所有作业都挂起。其中每个作业只成功启动了部分任务,并等待其余任务启动。 作业级的公平调度 (Job-based Fair-share)当运行多个弹性作业(如流媒体)时,需要公平地为每个作业分配资源,以满足多个作业竞争附加资源时的SLA/QoS要求。在最坏的情况下,单个作业可能会启动大量的pod资源利用率低, 从而阻止其他作业由于资源不足而运行。为了避免分配过小(例如,为每个作业启动一个Pod),弹性作业可以利用协同调度来定义应该启动的Pod的最小可用数量。超过指定的最小可用量的任何pod都将公平地与其他作业共享集群资源。 队列 (Queue)队列还广泛用于共享弹性工作负载和批处理工作负载的资源。队列的主要目的是:1)在不同的“租户”或资源池之间共享资源2)为不同的“租户”或资源池支持不同的调度策略或算法 这些功能可以通过层次队列进一步扩展,在层次队列中,项目被赋予额外的优先级,这将允许它们比队列中的其他项目“跳转”。在kube批处理中,队列被实现为集群范围的CRD。这允许将在不同命名空间中创建的作业放置在共享队列中。队列资源根据其队列配置(kube batch#590)按比例划分。当前不支持分层队列,但正在进行开发。 集群应该能够在不减慢任何操作的情况下处理队列中的大量作业。其他的HPC系统可以处理成百上千个作业的队列,并随着时间的推移缓慢地处理它们。如何与库伯内特斯达成这样的行为是一个悬而未决的问题。支持跨越多个集群的队列可能也很有用,在这种情况下,这是一个关于数据应该放在哪里以及etcd是否适合存储队列中的所有作业或pod的问题。 面向用户的, 跨队列的公平调度 (Namespace-based fair-share Cross Queue)在队列中,每个作业在调度循环期间有几乎相等的调度机会,这意味着拥有更多作业的用户有更大的机会安排他们的作业,这对其他用户不公平。例如,有一个队列包含少量资源,有10个pod属于UserA,1000个pod属于UserB。在这种情况下,UserA的pod被绑定到节点的概率较小。 为了平衡同一队列中用户之间的资源使用,需要更细粒度的策略。考虑到Kubernetes中的多用户模型,使用名称空间来区分不同的用户, 每个命名空间都将配置一个权重,作为控制其资源使用优先级的手段。 基于时间的公平调度 (Fairness over time)对于批处理工作负载,通常不要求在某个时间点公平地分配资源,而是要求在长期内公平地分配资源。例如,如果有用户提交大作业,则允许用户(或特定队列)在一定时间内使用整个集群的一半, 这是可以接受的,但在下一轮调度(可能是作业完成后数小时)中,应惩罚此用户(或队列)而不是其他用户(或队列)。在 HTCondor 中可以看到如何实现这种行为的好例子。 面向作业的优先级调度 (Job-based priority)Pod优先级/抢占在1.14版本中被中断,它有助于确保高优先级的pod在低优先级的pod之前绑定。不过,在job/podgroup级别的优先级上仍有一些工作要做,例如高优先级job/podgroup应该尝试以较低优先级抢占整个job/podgroup,而不是从不同job/podgroup抢占几个pod。 抢占 (Preemption & Reclaim)通过公平分享来支持借贷模型,一些作业/队列在空闲时会过度使用资源。但是,如果有任何进一步的资源请求,资源“所有者”将“收回”。资源可以在队列或作业之间共享:回收用于队列之间的资源平衡,抢占用于作业之间的资源平衡。 预留与回填 (Reservation & Backfill)当一个请求大量资源的“巨大”作业提交给kubernetes时,当有许多小作业在管道中时,该作业可能会饿死,并最终根据当前的调度策略/算法被杀死。为了避免饥饿, 应该有条件地为作业保留资源,例如超时。当资源被保留时,它们可能会处于空闲和未使用状态。为了提高资源利用率,调度程序将有条件地将“较小”作业回填到那些保留资源中。保留和回填都是根据插件的反馈触发的:volcano调度器提供了几个回调接口,供开发人员或用户决定哪些作业应该被填充或保留。 Volcano 调度框架Volcano调度器通过作业级的调度和多种插件机制来支持多种作业;Volcano的插件机制有效的支撑了针对不同场景算法的落地,从早期的gang-scheduling/co-scheduling,到后来各个级别的公平调度。下图展示了Volcano调度器的总体架构: Cache 缓存了集群中Node和Pod信息,并根据PodGroup的信息重新构建 Job (PodGroup) 和 Task (Pod) 的关系。由于在分布式系统中很难保证信息的同步,因此调度器经常以某一时间点的集群快照进行调度;并保证每个调度周期的决定是一致的。在每个调度周期中,Volcano 通过以下几个步骤派发作业: 1、在每个调度周期都会创建一个Session对象,用来存储当前调度周期的所需的数据,例如,Cache 的一个快照。当前的调度器中仅创建了一个Session,并由一个调度线程执行;后续将会根据需要创建多个Session,并为每个Session分配一个线程进行调度;并由Cache来解决调度冲突。 2、在每个调度周期中,会按顺序执行 OpenSession, 配置的多个动作(action)和CloseSession。在 OpenSession中用户可以注册自定义的插件,例如gang、 drf,这些插件为action提供了相应算法;多个action根据配置顺序执行,调用注册的插件进行调度;最后,CloseSession负责清理中间数据。action是第一级插件,定义了调度周期内需要的各个动作;默认提供 enqueue、allocate、 preempt和backfill四个action。以allocate为例,它定义了调度中资源分配过程:根据 plugin 的 JobOrderFn 对作业进行排序,根据NodeOrderFn对节点进行排序,检测节点上的资源是否满足,满足作业的分配要求(JobReady)后提交分配决定。由于action也是基于插件机制,因此用户可以重新定义自己的分配动作,例如 基于图的调度算法firmament。plugin是第二级插件,定义了action需要的各个算法;以drf插件为例,为了根据dominant resource进行作业排序,drf插件实现了 JobOrderFn函数。JobOrderFn函数根据 drf 计算每个作业的share值,share值较低代表当前作业分配的资源较少,因此会为其优先分配资源;drf插件还实现了EventHandler回调函数,当作业被分配或抢占资源后,调度器会通知drf插件来更新share值。 3、Cache 不仅提供了集群的快照,同时还提供了调度器与kube-apiserver的交互接口,调度器与kube-apiserver之间的通信也都通过Cache来完成,例如 Bind。 同时,为了支持上面这些场景,Volcano的调度器还增加了多个Pod状态以提高调度的性能:Pending: 当Pod被创建后就处于Pending状态,等待调度器对其进行调度;调度的主要目的也是为这些Pending的Pod寻找最优的资源Allocated: 当Pod被分配空闲资源,但是还没有向kube-apiserver发送调度决策时,Pod处于Allocated状态。Allocated状态仅存在于调度周期内部,用于记录Pod和资源分配情况。当作业满足启动条件时 (e.g. 满足minMember),会向kube-apiserver提交调度决策。如果本轮调度周期内无法提交调度决策,由状态会回滚为Pending状态 Pipelined: 该状态与Allocated状态相似,区别在于处于该状态的Pod分配到的资源为正在被释放的资源 (Releasing)。该状态主要用于等待被抢占的资源释放。该状态是调度周期中的状态,不会更新到kube-apiserver以减少通信,节省kube-apiserver的qps。Binding: 当作业满足启动条件时,调度器会向kube-apiserver提交调度决策,在kube-apiserver返回最终状态之前,Pod一直处于Binding状态。该状态也保存在调度器的Cache之中,因此跨调度周期有效。Bound: 当作业的调度决策在kube-apiserver确认后,该Pod即为Bound状态。Releasing: Pod等待被删除时即为Releasing状态。Running, Failed, Succeeded, Unknown: 与Pod的现有含义一致。 状态之间根据不同的操作进行转换,见下图。 Pod的这些状态为调度器提供了更多优化的可能。例如,当进行Pod驱逐时,驱逐在Binding和Bound状态的Pod要比较驱逐Running状态的Pod的代价要小 (思考:还有其它状态的Pod可以驱逐吗?);并且状态都是记录在Volcano调度内部,减少了与kube-apiserver的通信。但目前Volcano调度器仅使用了状态的部分功能,比如现在的preemption/reclaim仅会驱逐Running状态下的Pod;这主要是由于分布式系统中很难做到完全的状态同步,在驱逐Binding和Bound状态的Pod会有很多的状态竞争。 Volcano调度实现Volcano调度器在支持上面这些主要场景时,分别使用了action和plugin两级插件。 总体来讲,带有动作属性的功能,一般需要引入 action 插件;带有选择 (包括排序) 属性的功能,一般使用 plugin 插件。因此,这些常见场景中,fair-sharing、queue、co-scheduling都通过plugin机制来实现:都带有选择属性,比如“哪些作业应该被优先调度”;而preemption、reclaim、backfill、reserve 则通过 action 机制来实现:都带有动作属性,比如“作业A 抢占 作业B”。 这里需要注意的是,action 与 plugin 一定是一同工作的;fair-sharing 这些 plugin 是借助 allocate 发展作用,而 preemption 在创建新的 action 后,同样需要 plugin 来选择哪些作业应该被抢占。 通过job-based fairness (DRF) 和 preempt 两个功能的实现来介绍action 和 plugin 两种插件机制的使用,其它功能类似:Job-based Fairness (DRF): 目前的公平调度是基于DRF,并通过 plugin 插件来实现。在 OpenSession 中会先计算每个作业的 dominant resource和每个作业share的初始值;然后注册 JobOrderFn回调函数,JobOrderFn 中接收两个作业对象,并根据对像的 dominant resource 的 share值对作业进行排序;同时注册EventHandler, 当Pod被分配或抢占资源时,drf根据相应的作业及资源信息动态更新share值。其它插件的实现方案也基本相似,在OpenSession中注册相应的回调,例如 JobOrderFn, TaskOrderFn,调度器会根据回调函数的结果决定如何分配资源,并通过EventHandler来更新插件内的调度数。Preemption: preempt是在allocate之后的一个action,它会为“高”优先级的Pending作业选取一个或多个“低”优先级的作业进行驱逐。由于抢占的动作与分配的动作不一致,因此新创建了preempt action来处理相应的逻辑;同时,在选取高低优先级的作业时,preempt action还是依赖相应的plugin插件来实现。其它动作插件的实现方式也类似,即根据需要创建整体的流程;将带有选择属性的问题转换为算法插件。深入了解VolcanoVolcano官网:https://volcano.shGithub : https://github.com/volcano-sh/volcano
-
基于容器和无服务器平台的云原生应用在正在快速地被全球的组织所部署。虽然说云原生应用会带来易延展性、无与伦比的韧性、以及快捷的开发速度,云原生应用同样会带来挑战。云原生应用会有大量的可移动成分,并且基于那些短暂的架构组件。这就会给运营和维护产生难度;除此以外,自然还有安全隐患。云原生安全需要新的解决思路、策略和工具。这里,有五个可以帮助改善企业云原生安全的小建议。什么是云原生?云原生应用为云而创建,而且整个软件开发生命周期——开发、部署、测试和升级,都会在云环境完成。“云”的概念不局限于公有云,也可以意味着远程和本地资源都有的混合云或者超过一个云供应商的多云环境。云原生计算基金会(CNCF)认为三种工具应该用于云原生计算中:容器化、微服务结构和动态编排。容器化意味着软件和其关联依赖绑定,从而实现软件可移动、可扩展;动态编排包括了使用Kubernetes等工具管理云端容器;而微服务结构能够优化资源。容器能够被另一项云原生计算能力——无服务器功能所替代。云原生的安全挑战云原生应用给基础设施和应用安全带来了额外挑战。以下是一些关键挑战:多个需要保护的实体:DevOps团队和基础设施团队会使用微服务来运行云原生应用。在过去,多个进程或者软件功能会在一个虚拟机上运行。现在,每个进程或者能力都会被包装成分离的容器或者无服务器功能。每个实体都易于被攻破,因此需要全开发周期的防护。多样的结构:云原生系统会涉及很多公有云和私有云、云服务、以及应用结构。每个结构都有不同的隐患和安全需求。安全团队必须理解这一复杂的攻击面,并且为每个不同的结构找到解决方案。不断变化的环境:公有云和私有云环境在持续变化。快速的软件发布周期意味着微服务应用的每个组件都必须每日进行升级。另外,使用不可变性和基础设施即代码意味着应用会被持续分解并重构。安全团队会发现很难在不减缓发布周期的情况下,保护这些技术应用。如何保护云原生应用有多种保护云原生应用的方式,包括:安全左移、在函数和容器级别应用边界安全、贯彻最小角色和最低权限、保护应用依赖,以及安全共责。1. 安全左移许多企业依然在使用已有的工具,却无法处理云原生应用环境的速度、规模和动态网络。如果再加上无服务器功能,会让整个基础设施变得更抽象,让问题更严重。网络攻击者会寻找容器和无服务器代码中的隐患,以及云基础设施中的错误配置,以接入包含敏感信息的实体,再用它们提升权限,攻击其他实体。另一个问题是企业在用CI/CD工具持续开发、测试和发布应用。当使用容器部署云原生应用的时候,开发者会从本地或者公共库当中获取镜像,但一般不会检查这些镜像是否包含安全隐患。一种解决方案是给安全团队提供一些工具,阻止不受信任的镜像进入CI/CD管道,以及启用一些机制让不受信任的镜像在进入生产前就避免产生安全问题。通过在开发流程早期扫描镜像的漏洞、恶意软件成分等,开发者可以贯彻安全标准。2. 在函数和容器级别应用边界安全在无服务器应用中,系统会被分解成几个能从不同资源接受项目触发的可调用组件。这就给了攻击者更大的攻击选择,以及更多实施恶意行为的途径。一个很重要的方式是使用为云原生环境而制作的API和应用安全工具。除此以外,一个很普遍的操作是在功能级别使用边界安全——识别功能是否被一个和平时不同的来源所触发,然后监控事件触发中存在的异常情况。在容器化环境里,一个重要点是在不同级别都要实现安全——编排控制面板、物理主机、pod和容器。编排的一些最佳安全实践包括节点隔离、限制和监测容器之间的流量、以及对API服务器使用第三方认证机制。3. 最小角色与最低权限云原生资源之间会有大量频繁的交互。如果能够对每个无服务器功能或者容易都能配置一些独特的许可,就能有极大概率提升安全性。可以通过基于每个函数使用IAM,或者对容器进行颗粒度的许可,加强接入控制。花一点时间创建最小角色,或者为每个函数或容器创建一系列的许可。这就确保了即使云原生结构中有一个点失陷,其造成的危害也是最小的,并且会防止其他元件产生提权问题。4. 保护应用依赖无服务器函数和应用的代码经常从npm或者PyPI的库中获取有依赖关系的包。为了保护应用的依赖,就需要包括完整开源组件以及其漏洞数据库的自动化工具。同样,还需要能够在开发流程中触发安全行为的云原生编排工具。通过持续运作这些工具,就可以防范产线上运行的有隐患的代码包或者容器。5. 安全共责在开发者、DevOps和安全团队之间建立亲密的关系。开发者并不是安全专家,但他们可以被教导安全操作知识,从而确保他们可以安全地编写代码。安全团队应该知道应用是如何开发、测试和部署的,还有哪些工具在流程中被使用,从而安全团队能够在这些流程中有效地加入安全元素。云原生要求各种企业管理安全和开发的方式,因此尽快让不同团队减少隔阂至关重要。云原生的启用对企业来说是一个形成合作和共享文化的罕见契机。结论这篇文章提及了云原生面临的挑战,包括大量需要保护的实体,以及持续变化的环境和结构。同样,也给出了五个能够改善云原生环境的最佳实践:安全左移,在问题进入产线前进行规避。在函数和容器级别应用边界安全。对云原生应用中的实体实行最小角色和最低权限。保护好应用依赖。鼓励开发、运营和安全团队之间的安全共责。点评业务节奏加快使得无服务器应用等云原生应用会越来越多被企业所启用,云原生的安全也会更多被注意。不难发现,本文提到的五个安全建议中,软件安全相关的建议占了大部分:无论是安全左移、应用依赖的防护、还是实现DevSecOps整个安全协同,最终都离不开开发安全相关。这一点来看,DevSecOps和API安全的重要性都会随着云原生的使用进一步地提升。来源:https://netsecurity.51cto.com/art/202108/677745.htm
-
【功能模块】容器模块【操作步骤&问题现象】1、主机把按要求添加映射,保存重启,但LTE0映射不进去容器2、本来主机有LTE0映射之后主机的LTE0也没了,关机等一段时间重新启动,主机的LTE0就有了【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
对象定义你可以使用字符来定义和创建 JavaScript 对象:实例var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};定义 JavaScript 对象可以跨越多行,空格跟换行不是必须的:实例var person = { firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};对象属性可以说 "JavaScript 对象是变量的容器"。但是,我们通常认为 "JavaScript 对象是键值对的容器"。键值对通常写法为 name : value (键与值以冒号分割)。键值对在 JavaScript 对象通常称为 对象属性。Note JavaScript 对象是属性变量的容器。对象键值对的写法类似于:PHP 中的关联数组Python 中的字典C 语言中的哈希表Java 中的哈希映射Ruby 和 Perl 中的哈希表访问对象属性你可以通过两种方式访问对象属性:实例 1person.lastName;实例 2person["lastName"];对象方法对象的方法定义了一个函数,并作为对象的属性存储。对象方法通过添加 () 调用 (作为一个函数)。该实例访问了 person 对象的 fullName() 方法:实例name = person.fullName();如果你要访问 person 对象的 fullName 属性,它将作为一个定义函数的字符串返回:实例name = person.fullName; Note JavaScript 对象是属性和方法的容器。在随后的教程中你将学习到更多关于函数,属性和方法的知识。访问对象方法你可以使用以下语法创建对象方法:methodName : function() { // 代码 }你可以使用以下语法访问对象方法:实例objectName.methodName()通常 fullName() 是作为 person 对象的一个方法, fullName 是作为一个属性。如果使用 fullName 属性,不添加 (), 它会返回函数的定义:实例objectName.methodName
-
在NFS的基础之上,为了简便运维与管理,增加了持久卷的概念。PersistentVolume(PV):对存储资源创建和使用的抽象,使得存储作为集群中的资源管理PersistentVolumeClaim(PVC):让用户不需要关心具体的Volume实现细节。注:图片截取自李振良老师的视频课程上图是PV、PVC的使用逻辑,通俗的理解:一个企业雇一帮人工作(nfs),选个小组长(pv),再来个团队长(pvc),这样消费者就不会直接面对生产者,企业好管理,用户的服务也有保证。其实就是软件世界的官僚机构~PV使用方式:静态供给,需要K8s运维工程师提前创建一堆PV,供开发者使用。示例:[root@k8s-node2 /]# cat pv.yamlapiVersion: v1kind: PersistentVolumemetadata: name: my-pvspec: capacity: storage: 5Gi accessModes: - ReadWriteMany nfs: path: /ifs/kubernetes server: 192.168.0.21[root@k8s-node2 /]# cat deployment-pvc.yamlkind: DeploymentapiVersion: apps/v1metadata: name: deployment-pvcspec: selector: matchLabels: app: nginx-pvc replicas: 2 template: metadata: labels: app: nginx-pvc spec: containers: - name: nginx image: nginx volumeMounts: - name: wwwroot mountPath: /usr/share/nginx/html ports: - containerPort: 80 volumes: - name: wwwroot persistentVolumeClaim: claimName: my-pvc---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: my-pvcspec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi[root@k8s-node2 /]# 容器应用和卷需求模板写在了一个depolyment-pvc.yaml中,数据卷的定义写在了pv.yaml中。root@k8s-node2 /]# kubectl apply -f deployment-pvc.yamldeployment.apps/deployment-pvc createdpersistentvolumeclaim/my-pvc created[root@k8s-node2 /]# kubectl get pod -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESdeployment-pvc-84df7c6f46-b98wx 1/1 Running 0 55m 10.244.169.144 k8s-node2 <none> <none>deployment-pvc-84df7c6f46-d787b 1/1 Running 0 55m 10.244.169.143 k8s-node2 <none> <none>nginx-6799fc88d8-z76rk 1/1 Running 2 45h 10.244.169.142 k8s-node2 <none> <none>[root@k8s-node2 /]# curl 10.244.169.144hello //上一个实验中的index.html文件[root@k8s-node2 /]# cd ifs[root@k8s-node2 ifs]# cd kubernetes[root@k8s-node2 kubernetes]# echo hello world>index.html[root@k8s-node2 kubernetes]# curl 10.244.169.144 hello world[root@k8s-node2 kubernetes]#
-
最近复习了一遍Spring IOC容器的初始化过程,结合书籍《Spring源码深度解析》总结了一下,IOC容器的初始化过程,大概分为以下三点:1、定位资源: 定位相关的配置文件,扫描相关注解2、加载资源: 将配置信息加载到内存中3、注册: 根据载入的配置信息,初始化对象,并将其装载至容器中POM文件<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springTest</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> </dependencies> </project>测试代码public class ServiceB { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Object serviceA = context.getBean("serviceA"); System.out.println(serviceA); }xml配置<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <context:component-scan base-package="com.donkeys.spring"/> <bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean> </beans>ClassPathXmlApplicationContext构造方法 public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //根据传入的配置文件名称,调用父类的setConfigLocations方法,解析配置文件路径, setConfigLocations(configLocations); //refresh = true //refresh() 方法会重启整个容器 if (refresh) { refresh(); } } 构造方法中一共做了2件事,首先是设置配置文件的路径,然后对整个容器进行刷新。 这里我们重点关注refresh()方法;进入refresh()方法AbstractApplicationContext类的refresh()方法@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //为刷新前做准备 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //获取IOC容器,这里就是处理资源定位以配置文件加载/注册的方法 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } 这里主要关注**obtainFreshBeanFactory()**方法/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //刷新IOC容器 //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory方法,具体调用实现调用子类的refreshBeanFactory方法 refreshBeanFactory(); //获取一个新的容器 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } **obtainFreshBeanFactory()**方法总共干了2件事, 重置容器,refreshBeanFactory()方法中会设置相关标志,清除旧的容器,同时为Spring上下文生成一个新的容器,获取一个新的容器AbstractRefreshableApplicationContext的refreshBeanFactory()方法 下面我们进入**refreshBeanFactory()**方法/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. * 该方法会将之前的bean工厂全部关闭,并初始化一个全新的bean 工厂类 用于Spring 上下文的生命周期 * bean工厂就是IOC容器 */ @Override protected final void refreshBeanFactory() throws BeansException { //判断是否之前也有容器 //如果有就销毁掉 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建一个新的工厂 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //读取Bean对象的定义 //这里也是使用的委派设计模式 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } 这里创建了新的容器工厂,同时将新的工厂传入了loadBeanDefinitions()方法中,下面来看一下在loadBeanDefinitions方法中具体做了什么操作。AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法 在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中,调用了loadBeanDefinitions方法,但是这个方法它的一个抽象方法,具体实现应由子类去实现,我们在程序启动时,使用的ClassPathXmlApplicationContext类。根据文章开头的类图可以知道,这里会调用子类AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加载@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //使用默认的beanFactory去创建 XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //设置资源的加载环境 beanDefinitionReader.setEnvironment(this.getEnvironment()); //设置资源读取器 beanDefinitionReader.setResourceLoader(this); //设置实体解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader); //使用初始化完成的读取器,调用loadBeanDefinitions方法 loadBeanDefinitions(beanDefinitionReader); } 总的来说,这里只干了一件事,那就是初始化配置//设置资源读取器 beanDefinitionReader.setResourceLoader(this); //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader); 设置资源读取器,这里设置的资源读取器就是当前这个对象本身 通过类图我们可以发现我们这个类的顶级父类ApplicationContext,继承自DefaultResourceLoader这个类,该类实现了ResourceLoader接口,说明这个类的实例化对象本身是具有资源读取器的功能的 初始化bean对象定义读取器,这里设置xml文件的校验方式 下面我们继续看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //从子类对象中获取到资源定位 Resource[] configResources = getConfigResources(); if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } } 先看这一行//这行代码的具体实现是由子类完成的,通过类图可以知道AbstractXmlApplicationContext的子类为ClassPathXmlApplicationContext //这里主要将我们最开始在构造方法中设置好的配置文件进行返回 Resource[] configResources = getConfigResources(); 再看这一行//如果返回的配置文件不为空,就将返回的已经封装好的资源文件进行读取, if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); } 至此。资源文件定位过程已经加载完成。后续就是读取和注册。整个IOC容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。
-
【功能模块】容器编译【操作步骤&问题现象】1、根据AR-CORE-220E开发指南容器制作步骤编译出容器;2、安装到AR-CORE-220E设备上;3、使用container status查看容器状态,此时容器ip是ipv6地址;4、等待几分钟后,再次查看,容器ip仍旧是ipv6地址【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
2021年4月1日北京超图软件股份有限公司资深产品经理周世杰老师做客华为云云市场直播间,给大家带来了《场景+应用,带你硬核解析云原生GIS》的主题分享。其实在之前我是不太了解什么是GIS的,非常感谢这次的科普直播,让我不仅了解到了GIS技术,还了解到了相关的产品案例,大有裨益,本文就带大家一起梳理一下直播的内容。GIS是Geographic Information System的简称,即地理信息系统,那云原生GIS又是什么?云原生是一种构建和运行应用程序的方法,是一套技术体系和方法论。云原生GIS是面向环境设计的,基于微服务架构思想的,以容器位部署载体的,可自动化编排、运维管理的,更弹性、更稳定、更新更实时的GIS软件体系架构。也就是以云原生的方式构建和运行的 GIS 应用。云原生GIS有什么优势?1)更稳定:服务可自动恢复 ;故障可自动转移;保证服务永久在线。2)更便捷: 内置存储资源池:Redis、MySQL、MongoDB、HDFS 、 Elasticsearch、HBase、PostGIS、PostgreSQL内置计算资源池:Spark、Hadoop YARN内置流数据环境:Kafka集群3)更弹性:资源更集约计算更高效 云原生GIS的典型应用场景:1)GIS服务数量多云原生设计的初衷就是为了解决大数据时代所带来的数据量 不断增大引起的数据发布不成功、数据加载困难等问题。当用户有成百上千的服务实例时,如果发布在一个iServer或 iServer集群中,服务器的承受力问题和服务崩溃、重启所带来的种种,都是亟待解决的问题。这种情况使用云原生GIS,对服务按 照数据源类型、服务类型、访问频率等进行分类调度到不同的节点, 就可以化解上述问题。2)数据种类多,体积大数据种类多,发布出来的服务数量也会很多,造成的问题跟上述第一个场景一样。 数据的体积比较大的情况下,查询、分析、加载等过程都会 比较耗资源,如果采用传统方式发布,不仅访问该服务会比较慢, 而且也会拖慢其他服务。所以当数据种类多、体积大或服务类型复杂时,都适用于云原生GIS环境,对这类服务进行独立调度和横向伸缩。3)局部平滑升级、故障恢复困难云原生的优势就是更新快、升级快,可以快速更新单个微服务,而不会影响其他功能。 在项目生产过程中,有需要上线一些新功能时,如果采用传统方式往往需要做大量手工配置工作,且存在风险。而在云原生GIS中,用户只需要使用新镜像,就可以滚动更新该服务,从而便捷、平滑稳定的升级新功能。再配合灰度发布让服务上线更加严谨。4)关注微服务、创新随着应用程序的功能日益丰富和强大,微服务应运而生,成为新型软件架构。 云原生GIS是由多个功能小而专注、独立部署的微服务组合出的大型GIS应用程序。各个微服务的开发彼此独立,可以根据功能需求每个微服务使用最适合的技术栈或开发语言。当用户考虑升级整体技术栈时,可以考虑云原生创新应用。5)对高可用关注度高云原生GIS的部署是多节点集群方式搭建,一个节点故障,服务会自动转移到其他存活的节点;如果其中一个服务异常,系统会自动重建一个新的服务来替代异常的服务,因为是基于容器技术,替代过程可以达到秒级替代;基于微服务的横向扩展能力,保证了服务高效稳定。6)机器/VM比较多云原生GIS环境下,所有服务都是由Kubernetes统一管理调 度,用户需要做的就是把这些机器加入Kubernetes集群即可。 云原生GIS会全面监控物理机、容器等资源使用情况,并把服务部署调度到当前最优的机器来运行。 后续有采购新机器,只需要“一键”加入Kubernetes集群即可。 当有机器从Kubernetes中移除后,它上面的服务会自动迁移到其他机器,不需要人工干预。 云原生GIS环境搭建需要的包-华为云:直播链接:https://bbs.huaweicloud.com/live/marketplace_live/202104011900.html超图云GIS管理服务器平台:https://marketplace.huaweicloud.com/contents/1bdc29fb-2828-4aa3-b799-acb09538c1a8?marketplace_live_20210401
-
【功能模块】把第三方库打包进容器【操作步骤&问题现象】1,用build_sdk_base.sh制作了基础镜像2,自己编译出来了第三方的库(.so和.a),不是按照文档脚本制作,库是测试可用的,库没有问题3,修改了createdeb文件,把所有的库都打包成deb文件执行命令:./Todeb 库文件夹 库名目的:库文件夹里面的DEBIAN文件和第三方库(.so或.a)打包成deb文件,然后分别放到sdk和ova文件夹下(问题1:必须把deb文件放到这两个文件夹下吗,制作sdk,应该和ova没有关系吧)4,用build_sdk.sh制作出编译镜像,并安装(问题2:这一步后,第三方库应该也已经被打包进编译镜像了吧,但是怎么确定打包的.so成功被打包进镜像了,目前/usr/loca/lib和/usr/lib下都没有)5,在制作出的编译环境里面执行build_ova.sh制作出容器,这一步执行过程在附件日志(问题3:安装到核心板上,没有找到第三方库,目前/usr/loca/lib和/usr/lib下都没有,应该在哪里)问题4:上面的第3步,是否可用把所有的.so和.a文件都放到一个文件夹中,一次性打包成.deb,还是必须是分开制作成.deb,然后都放到sdk目录下?问题5:请帮忙看下附件日志,还有一些警告,请问哪些警告可以忽略,哪些不能忽略,必须解决?问题6:第1步制作的交叉编译环境,应该就是华为提供的交叉编译镜像吧?root@2b2df3265563:/data/502H/eciot-ova# cat Todeb #!/bin/bash if [ $# != 1 ];then echo "Usage: $0 appdir package architecture version email description " exit fi DIR=$1 PACKAGE=$1 if [[ ! -d $DIR ]]; then mkdir $DIR fi cp ./my_lib/${PACKAGE} $DIR/ ARCH=arm64 VERSION=1.0 INSTALLED_SIZE=`du -s $DIR | awk '{print $1}'` EMAIL=guoyanzhang@orena.com.cn DESC=orena cd $DIR if [[ ! -d "DEBIAN" ]]; then mkdir DEBIAN fi #md5sum `find . -type f` > DEBIAN/md5sums echo "Package: $PACKAGE" > DEBIAN/control echo "Version: $VERSION" >> DEBIAN/control echo "Architecture: $ARCH" >> DEBIAN/control echo "Depends: " >> DEBIAN/control echo "Installed-Size: $INSTALLED_SIZE " >> DEBIAN/control echo "Priority: optional" >> DEBIAN/control echo "Maintainer: $EMAIL" >> DEBIAN/control echo "Description: $DESC" >> DEBIAN/control chmod 755 -R DEBIAN #dpkg -b . ../${PACKAGE}_${VERSION}_${ARCH}.deb cd .. pwd debName=${PACKAGE}_${VERSION}_${ARCH}.deb dpkg -b $DIR ./${debName} echo ${debName} cp ./${debName} ./custom_deb/ova/${ARCH} mv ./${debName} ./custom_deb/sdk/【截图信息】五【日志信息】(可选,上传日志内容或者附件)无
推荐直播
-
华为云码道 × 仓颉编程:工程化AI编码探索2026/05/27 周三 19:00-21:00
刘俊杰-华为云仓颉语言专家/李炎-华为云码道技术专家/王智鹏-OpenCangjie开源社区发起人
本场直播围绕华为云仓颉语言与华为云码道的深度结合,展示华为云智能编程从零基础到高效落地的完整生态能力。以华为云码道为引擎,仓颉语言为载体,带给大家日常提效、趣味创新到极速量产的开发体验。
回顾中
热门标签