-
为什么写?1.很多人听过中间件,但是没见过中间件,或者根本不知道中间件是什么,傻X百科上面的定义实在是模糊,所以就有了写这片博客的冲动。定义:中间件,顾名思义存在于两个系统之间的,起到连接的设备。(1)为什么是设备? 硬件和软件在一定程度上可以互用,中间件既可以是硬件,也可以是软件,所以我说是设备,而不定义为,硬件或者软件的一种。(2)起到连接作用怎么理解?中间件可以在两个软件之间起到连接(iis服务)。可以在客户机/服务系统之间起到功能(例如web代理服务器)。2.中间件的作用:(1)一个定义:在操作系统中所有的软件,硬件,固件都可以看作文件。文件有时会具有不同的格式,表现在应用上显示为拥有不同的api接口。①中间件的第一个功能:平衡api接口,使不同的应用通过中间件能够互联。(2)统一化接口后,中间件就表现为能够在不同的接口无限制的传输数据。①中间件第二个功能:负载均衡。软件可能直接相连,也可能通过网络相连,在数据量大的时候就会产生拥塞,但是通过中间件,好像拥塞消失了。(3)搭建iis服务的时候我们可以看到,创建网站的时候,直接点击就能创建一个网站。Iis服务已经为我们做好了一切的统筹工作,而我们只需要操作就好了。①中间件的第三个功能,提供容器。为一种或者多种应用程序提供服务功能。3.中间件的特性:(1)易用性。①一般中间件为软件易于控制,易于复制,在计算机上点击,或者在命令行加载就能够使用(2)位置透明性①中间件起到的是协调的作用,故在使用的时候我们仿佛看不到中间件的存在。(3)消息传输完整性①起到容器,作用和负载均衡作用的时候,要确保的就是消息传输的完整性,如果一个消息通过你的中间件,本质改变了。那么就没有意义了。1)小提示:数据和信息。数据是承载信息的,信息是数据的抽象,世间万物都可以变成数据,破坏数据的结构就会毁坏信息。4.中间件,容器,服务器:(1)客户端--------网络---------服务器---------中间件-------数据库(2)客户端在访问的时候,如果访问静态网页就直接和服务器操作,{例如get(获取数据),post,head,opting,put,delete,trace,connect。服务器返回信息,1**(收到,继续执行),2**(成功,操作成功处理),3**(重定向,页面不在这里)4**(客户端错误),5**(服务器错误),}客户端直接和服务器作用,而不经过中间件和数据库作用。(3)客户端访问动态网页,例如php之类的网页,客户端和服务器作用完,服务器和数据库作用,中间就用到中间件。(4)中间件,包含容器(例子windowns上面的iis服务)(5)有的时候,中间件和服务器是架构在一起的(透明性)。
-
lower_bound,upper_bound和equal_range函数初识 lower_bound.(k) 返回一个迭代器,指向第一个值不小于k的元素upper_bound(k) 返回一个迭代器,指向第一个值大于k的元素equal_range(k) 返回一个迭代器pair,表示值等于k的元素的范围。若k不存在,pair两个成员均等于end()–尾迭代器 上面三个函数多用于容器中使用,但是对于普通的数组也是可以使用的,下面会讲到. 如果所查找值在容器中,lower_bound返回的迭代器将指向第一个具有给定值的元素,而upper_bound返回的迭代器指向最后一个匹配给定值的元素之后的位置。 如果元素不在容器中,则lower_bound和upper_bound会返回相等的迭代器----指向一个不影响排序的值插入位置 因此,用相同的值调用lower_bound和upper_bound会得到一个迭代器的范围,表示具有该关键字的元素范围。 当然,这两个操作返回的迭代器可能是容器的尾后迭代器。如果我们查找的元素具有容器中最大值,则此关键字的upper_bound返回尾后迭代器。如果关键字不存在,且大于容器中任何关键字,则lower_bound返回的也是尾后迭代器.
-
STL中的容器:deque容器的常用接口及用法:deque:它被称作双端数组,可以在头部和尾部插入或删除数据;deque 容器和 vector 容器的区别:1.vector 容器对头部插入、删除数据的效率较低,因为 vector 容器是单端数组,若要从头部插入或删除,得把后面的数据都往后挪或往前挪,因此数据量越大,则其时间效率越低;2.deque 容器相对 vector 容器而言,它对于头部插入数据或头部删除数据的效率就高多了,这与它的内部实现相关;3.vector 容器访问单个数据的效率要高于 deque 容器,原因也是与 deque 容器的内部实现有关;图片转自于黑马程序员,是在学习的过程中截图下来的deque 容器内部工作原理:1.deque 内部有个中控器,它维护着每段缓冲区中的内容,而缓冲区里面放着真实的数据;2.中控器维护的其实是缓冲区的地址,使得使用 deque 容器时像一段连续的内存空间;3.缓冲区的头和尾是没有插满数据的,因此可以继续添加。添加满后,则会开辟一块新的缓冲区,中控器则记录下新的缓冲区的地址;4.由于中控器维护的是地址,因此当我们访问单个元素时,内部的实现是从地址再转到缓冲区,这里的时间效率就低于 vector 容器了;
-
云实验增加了许多新实验,HDC大会圆满闭幕,终于有时间做实验啦~因为最近做了很多docker和k8s的功课,想着先复习下容器的实验。可能因为上次做完了还剩20多分钟的时间,所以这次进来发现因为密钥配额的原因,实验不能进行下去,只能把实验的时间耗完然后,明天再来!
-
Kubernetes作为容器编排工具,简化容器管理,提升工作效率而颇受青睐,我们可以借助云容器引擎CCE平台快速搭建 Kubernetes环境,轻松玩转 Kubernetes。什么是容器?容器为Ap提供独立的、受控的运行环境,是_种轻量级的操作系统虚拟Concept for create environment for software, without disturbing the rest of the core operating system running job filesystem简单的容器: SandBox(沙盒、沙箱)Kubernetes-大海航行的舵手K8s集群主要包括两个部分: Master节点(管理节点)和Node节点(计算节点)Master节点主要还是负责管理和控制。Node节点是工作负载节点,里面是具体的容器。Master节点Master节点提供的集群控制,对集群做出全局性决策,例如调度等。通常在 master节点上不运行用户容器。Master节点包括AP| Server、 Scheduler、 Controller manager、etcdAPI Server:整个系统的对外接囗Scheduler:集群内部的资源进行调度Controller Manager:负责管理控制器etd: Kubernetes的后端存储Node节点节点组件运行在每一个Node节点上,维护运行的pod并提供 kubernetes运行时环境。Node节点包括Pod、 Docker、 kubelet、kube-proy、 Fluent、kube-dns(可选)Pod: Kubernetes最基本的操作单元;Docker:创建容器;Kubelet:负责监视指派到它所在Node上的Pod,包括创建、修改、监控、删除等Kube-proxy:负责为Pod对象提供代理 Fluent:主要负责日志收集、存储与查询。Kubernetes最小管理单元-PoDPod是 Kubernetes管理的最小基础单元。一个Pod中封装了:一个或多个紧耦合的应用容器,存储资源,独立的IP,容器运行的选项相同Pod中的任何容器都将其享相同的名称空间和本地网络。容器可以很容易地与其他容器在相同的容器中进行通信。有状态应用和无状态应用无状态应用有状态的服务,从部署开始,这些容器就开始与上游镜像不同了,时间越长它们的差异越大,每个运行的应用程序都至少有一个小状态,(差异),但对于“无状态”应用程序来说,状态(差异)很小,而目可以进行快速替换有状态应用无状态服务,易于部署且易于扩展。如果流量上升,则只需添加更多的负载平衡上游容器镜像和基础架构中正在运行的容器其实几乎没有区别;可以随时被替代,而且容器实例切换过程中几乎不需要耗费“切换成本
-
转载自 云计算D1net 原创 Anna Anisienia组织在将业务迁移到云平台时,遇到的最常见的问题之一是成本。采用云计算,组织可以将IT成本从资本支出(硬件设备和软件许可的长期投资)转换为运营支出,因此选择正确的云服务并进行正确估算至关重要。以下将探讨在调整云计算资源大小时常见的错误和陷阱,并讨论如何避免,从而真正受益于云计算的弹性。01 遵循提升和转移方法提升和转移方法意味着组织可以将工作负载的副本移动到云平台中,而只需进行少量的更改。即使组织只将部署业务快速迁移到云平台中,这种模式也很有用,但它可能导致资源使用不足。AWS公司承认,通过创建服务来简化迁移(CloudEndure迁移和AWS服务器迁移服务)是一个困难的问题。不过,为了获得更好的资源利用率,组织最好考虑重新构建云计算解决方案。组织采用提升和转移方法,从长远来看可能会支付更多的成本,也可能会错过云计算提供商提供的许多好处。例如,当选择完全管理的AWS Aurora而不是传统的Postgres实例时,组织可以获得高达三倍的吞吐量、存储自动扩展和低延迟读取副本。这可能是Aurora成为目前最受欢迎和发展最快的AWS云服务之一的原因。02 不标记资源如果组织没有足够的数据来做出明智的决定,则很难改进。如果无法跟踪云计算资源的性能以及它们产生的成本,那么就很难优化其利用率。最好的做法是根据项目或组织单位标记资源,以将成本正确分配给相应的服务。03 未能随着时间的推移监控资源使用情况管理云计算结构并不是一次性的过程。这是监视和评估组织使用的内容、使用方式以及原因的持续实践。也许组织最初对特定应用程序的增长的假设并不完全正确,而进行更改可能会显著地降低成本。例如一个过度配置的Kubernetes集群,它的节点比需要的多很多。在这种情况下,也许转向无服务器版本(Fargate上的EKS)更有意义。保持“僵尸”资源不受监控的情况并没有人们想象的那么普遍。在规模较大的组织中,可能会发生某些项目由于不完整的移交过程而被放弃并且相应的资源保持活动状态的情况。04 总是自己做所有的事情软件工程师有时可能会自己构建定制的解决方案和服务。一种可能更好的方法是首先对现有资源进行适当的研究。例如:也许不需要在EC2上使用自托管数据库,而是使用完全托管的RDS,这可以帮助更轻松地扩展和操作实例。也许不需要这个自我管理的RabbitMQ实例,而是可以使用经过实践检验的无服务器消息队列SQS。通常情况下,如果有一个无服务器或完全托管的解决方案,那么至少在为自己的解决方案投入过多的时间和精力以进行维护之前,先考虑采用这些方案是有意义的。05 只使用自己熟悉的工具在阅读Reddit或博客的一些文章时,经常看到许多工程师不愿意使用无服务器或容器编排平台,因为他们只知道EC2和人工管理的服务器。他们认为有些新技术可能只是昙花一现,因此没有必要改变自己的方式。这意味着转移到容器编排平台、无服务器和其他云服务是没有价值的。这似乎是一种谨慎的方法。最好挑战一下这种假设,用清楚的事实、成本和性能基准来判断新技术的可用性,而不是对新技术持怀疑态度。06 没有使用无服务器和容器编排平台如果要为所管理的每个服务和工具创建一个EC2实例,则可能会陷入维护的噩梦。但是,如果将每个服务部署到Kubernetes(EKS)或Fargate(ECS)集群的容器中,那么由于容器的动态端口映射和更紧凑的资源利用(例如共享层),可以将更多的资源分配到单个服务器实例中。容器编排平台将帮助你确保实例之间的负载平衡,并使工作负载保持健康。这在某种程度上消除了猜测容量的情况。你可以指定在任何时候应该运行多少个容器实例,并且控制平台将确保它发生,就像你定义的那样。如果可以轻松地在许多容器或无服务器资源之间实现负载平衡,那么不必再猜测哪种EC2或RDS实例大小适合自己的用例。07 不考虑总拥有成本如果只考虑硬件或服务成本,你可能最终会认为许多资源在内部部署设施中运行可能更具成本效益。但是,如果加上额外的维护、升级和员工管理这些服务器的成本,那么情况就完全不同了。08 没有长远的思考如果只根据当前情况扩展资源,则可能无法考虑到未来需求的变化。如果组织的业务和数据增长更好怎么办?如果结果正好相反呢?你的应用程序仍然易于更改,并适应未知的未来情况吗?最后,你是否能够找到并保留足够的员工以长期满足这些需求?09 过度配置“以防万一”如果你要保证万无一失,可能会过度配置所有东西,以确保为应对使用高峰期做好准备。如果你可以根据过去的使用模式来证明过度配置的合理性,则这是一个很好的策略。但是,如果是出于直觉,这样做可能是一个错误的策略。从某种意义上说,云服务可以提供弹性,你可以在集群中添加节点,在更多容器之间负载均衡工作负载,或者在需要时增加CPU数量或内存。如果配置和监视正确,则无需过多配置。这并不是说正确调整大小很容易,但是有了良好的流程和自动化,这是可行的,并且可以显著节省成本,尤其是在大规模运行大量资源时。10 选择错误的数据存储有时,瓶颈不是计算资源不足,而是数据存储选择不当。最好考虑一下:你是否需要丰富的查询语言(SQL),还是应用程序只需简单的键值存储即可(例如DynamoDB)。首先是否需要数据库,也许一个简单的S3数据转储就足够了。它自然取决于用例,但是数据库通常是构成任何可扩展架构的主要瓶颈。如何解决云计算资源大小问题?提高云计算资源利用率的一种可能的解决方案是采用自动化技术。例如,你可以使用Dashbird跟踪资源不足和资源过剩的情况,并获得有关它们的通知。使用结构良好的lens仪表板时,可以发现,具有EC2实例类型的ECS集群在过去一小时内的CPU利用率超过90%。然后,可以深入到特定的时间间隔,并进一步检查出现这一使用峰值的原因。同时,另一种容器服务可能会被超额配置,可能会浪费成本。有了这些信息,你可以根据实际使用模式优化资源配置。结论以上研究了调整云计算资源大小时的常见问题,并讨论了如何避免这些问题,并真正从云计算的弹性中受益。通过使用容器编排平台、无服务器和完全托管的解决方案,以及随着时间的推移持续监视使用模式,可以优化云计算架构的性能和成本。(来源:企业网D1Net)
-
12345678910111213141516171819202122232425262728293031323334353637383940<!-- 父组件代码片段 vue 文件版 --> <template> <div ref="father" style"width: 100%, height: 100%" > <ChildComponents v-for="(item, index) in playList" :key="index" :ref="index" :visible="true" :z-index="index" :back-value="backValue" :info="item" :close="close" :width="600" :height="400" /> </div></template><script>export default { components: { VideoPlayerModal }, props: { playList: { type: Array, required: true } }, methods: { backValue (left, top, zIndex) { this.$refs[zIndex][0].$el.style.top = `${top}px` this.$refs[zIndex][0].$el.style.left = `${left}px` } }}</script>设置子组件的围栏范围这个功能只需要在 onmousemove 事件中进行判断 子容器的 top 和 left 是否超出浏览器的可视范围12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061/** 1. this.width 数据为父组件传递进来的 width 值,或者子组件本身设置的默认值* 2. this.height 数据为父组件传递进来的 height 值,或者子组件本身设置的默认值*/ move (e) { // 判断 flag 是否允许移动 if (!this.moveFlag) return // 判断是否超出左边视图 if (this.$refs.fatherBox.offsetLeft < this.width / 2) { // 禁止弹框移动 this.moveFlag = false // 设置弹框左边位置 this.left = this.width / 2 + 10 // 调用回调函数把偏移量暴露给父组件 this.backValue(this.left, this.top, this.zIndex) return } // 判断是否超出右边视图 if (this.$refs.fatherBox.offsetLeft > document.body.clientWidth - this.width / 2) { // 禁止弹框移动 this.moveFlag = false // 设置弹框右边位置 this.left = document.body.clientWidth - this.width / 2 - 10 // 调用回调函数把偏移量暴露给父组件 this.backValue(this.left, this.top, this.zIndex) return } // 判断是否超出顶部视图 if (this.$refs.fatherBox.offsetTop < this.height / 2 + 70) { // 禁止弹框移动 this.moveFlag = false // 设置弹框顶部位置 this.top = this.height / 2 + 70 + 10 // 调用回调函数把偏移量暴露给父组件 this.backValue(this.left, this.top, this.zIndex) return } // 判断是否超出底部视图 if (this.$refs.fatherBox.offsetTop > document.body.clientHeight - this.height / 2 - 50) { // 禁止弹框移动 this.moveFlag = false // 设置弹框底部位置 this.top = document.body.clientHeight - this.height / 2 - 50 - 10 // 调用回调函数把偏移量暴露给父组件 this.backValue(this.left, this.top, this.zIndex) return } // 设置弹框左边位置 this.left = e.clientX - this.startLeft // 设置弹框右边位置 this.top = e.clientY - this.startTop // 调用回调函数把偏移量暴露给父组件 this.backValue(this.left, this.top, this.zIndex)}子组件还要设置一个当鼠标超出子容器时的 onmouseout 事件,用来防止不可预期的 bug 问题123mouseOut (e) { this.moveFlag = false}
-
基于 vue此功能核心思想就是通过 JavaScript 代码控制 node 在页面上的左边距与顶边距,不同的的样式定位方式有不同的解决方案本方案采用position: absolute定位方式的解决方案css 样式的核心代码1234// 父容器核心样式 width: 100%; height: 100%;12345// 子容器核心样式 position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);父容器通过width && height字段占满整个浏览器的可视范围,子容器通过position: absolute属性开启在父容器内的绝对定位,在通过top && left && transform: translate(-50%, -50%)属性控制子容器在父容器内的绝对位置JavaScript 逻辑控制的核心代码首先分解下,要实现 node 的移动需要哪些步骤和对应的 event 事件子容器创建时,在父容器内的绝对位置鼠标按键按下时,onmousedown 事件鼠标移动时,onmousemove 事件鼠标按键弹起时,onmouseup 事件只要使用 onMousedown、onMousemove和onMouseup 这三个事件,就可以实现最简单的移动12345678/** 在子容器创建的时候获取子容器相对于父容器的 top 和 left 位置*/ mounted () { this.left = this.$refs.fatherBox.offsetLeft this.top = this.$refs.fatherBox.offsetTop}12345678910111213/** 鼠标按下时* 1. 开启允许子容器移动的 flag* 2. 记录鼠标点击时的位置信息*/ mouseDown (e) { // 设置允许弹窗移动的 flag this.moveFlag = true // 保存鼠标开始位置 this.startLeft = e.clientX - this.left this.startTop = e.clientY - this.top}1234567891011121314151617/** 鼠标移动时* 1. 判断 flag 是否允许子容器移动* 2. 设置弹框左边位置* 3. 设置弹框右边位置*/ move (e) { // 判断 flag 是否允许移动 if (!this.moveFlag) return // 设置弹框左边位置 this.left = e.clientX - this.startLeft // 设置弹框右边位置 this.top = e.clientY - this.startTop }12345678/** 鼠标按键弹起时* 1. 关闭允许子容器移动的 flag*/ mouseUp (e) { this.flag = false}通过这几个方法就可以获取鼠标按下移动时,鼠标的top 和 left 的偏移量,通过把这偏移量暴露出去给父组件,父组件实时设置子组件的 top 和 left 值,来使得子容器跟随鼠标的移动父组件部分代码父组件通过设置子组件的 ref、zIndex 值,而且父组件的 backValue 方法会从子组件接收 zIndex 值,通过 zIndex 来识别具体的子组件实例12345678910111213141516171819202122232425262728293031323334353637383940/** 父组件代码片段 jsx 版*/ export default { props: { playList: { type: Array, required: true } }, render () { return ( <div style={{width: '100%', height: '100%'}} ref={'father'}> { this.playList && this.playList.map((item, index) => { return ( <ChildComponents key={index} ref={index} zIndex={index} visible={true} backValue={this.backValue} info={item} width={600} height={400} /> ) }) } </div> ) }, methods: { backValue (left, top, zIndex) { this.$refs[zIndex].$el.style.top = `${top}px` this.$refs[zIndex].$el.style.left = `${left}px` } }}
-
STL中的容器:deque容器的常用接口及用法:deque:它被称作双端数组,可以在头部和尾部插入或删除数据;deque 容器和 vector 容器的区别:1.vector 容器对头部插入、删除数据的效率较低,因为 vector 容器是单端数组,若要从头部插入或删除,得把后面的数据都往后挪或往前挪,因此数据量越大,则其时间效率越低;2.deque 容器相对 vector 容器而言,它对于头部插入数据或头部删除数据的效率就高多了,这与它的内部实现相关;3.vector 容器访问单个数据的效率要高于 deque 容器,原因也是与 deque 容器的内部实现有关;图片转自于黑马程序员,是在学习的过程中截图下来的deque 容器内部工作原理:1.deque 内部有个中控器,它维护着每段缓冲区中的内容,而缓冲区里面放着真实的数据;2.中控器维护的其实是缓冲区的地址,使得使用 deque 容器时像一段连续的内存空间;3.缓冲区的头和尾是没有插满数据的,因此可以继续添加。添加满后,则会开辟一块新的缓冲区,中控器则记录下新的缓冲区的地址;4.由于中控器维护的是地址,因此当我们访问单个元素时,内部的实现是从地址再转到缓冲区,这里的时间效率就低于 vector 容器了;图片转自于黑马程序员,是在学习的过程中截图下来的各种函数接口具体如何使用,下面的代码块中会有详细的使用方法deque 容器构造函数:1.deque<T> d; 默认(无参)构造;2.deque(const deque & d); 拷贝构造函数3.deque(begin,end); 把区间 [begin,end) 之间的数据拷贝到新创建的 deque 容器;4.deque(n,elem); 把n个 elem 拷贝给新创建的 deque 容器;#include <iostream>#include <deque> //使用STL中的容器,得包含它的头文件 #include <algorithm> //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d) //限制传进来的deque容器只读的状态{for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1() //deque容器构造函数 {int i = 0;deque<int> d1; //默认(无参)构造函数 for(i=0;i<10;i++){d1.push_back(i+1);}printDeque(d1);deque<int> d2(d1); //拷贝构造函数printDeque(d2);deque<int> d3(d1.begin(),d1.end()); //把区间[d1.begin(),d1.end())之间的数据拷贝到新创建的deque容器printDeque(d3);deque<int> d4(10,1); //把10个1拷贝到新创建的deque容器 printDeque(d4);}int main(){test_1();system("pause");return 0;} 123456789101112131415161718192021222324252627282930313233343536373839404142434445deque 容器赋值:1.deque& operator=(const deque & d); 通过重载赋值运算符的方式给新创建的 deque 容器赋值;2.assign(begin,end); 通过成员函数 assign() 把 [begin,end) 之间的数据给新创建的 deque 容器赋值;3.assign(n,elem); 通过成员函数 assign() 把n个 elem 给新创建的 deque 容器赋值;#include <iostream>#include <deque> //使用STL中的容器,得包含它的头文件 #include <algorithm> //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d) 限制传进来的deque容器只读的状态{for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1() //deque容器赋值{int i = 0;deque<int> d1; //默认(无参)构造函数 for(i=0;i<10;i++){d1.push_back(i+1);}printDeque(d1);deque<int> d2;d2 = d1; //通过重载赋值运算符的方式给新创建的deque容器赋值printDeque(d2);deque<int> d3;d3.assign(d1.begin(),d1.end()); //通过成员函数assign()把[begin,end)之间的数据给新创建的deque容器赋值printDeque(d3);deque<int> d4;d4.assign(10,1); //通过成员函数assign()把10个1给新创建的deque容器赋值printDeque(d4);}int main(){test_1();system("pause");return 0;} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647deque 容器的大小:1.empty(); 判断 deque 容器是否为空,如果 deque 容器为空,则返回 true,否则返回 false;2.size() 用于查看容器的大小(元素个数);3.resize(int num); 重新指定容器的大小为 num,若容器扩大了,则以默认值(0)来填充,若容器变小了,则删除多出来的部分;4.resize(int num,int elem);重新指定容器的大小为 num,若容器扩大了,则以 elem 来填充多出来的部分,若容器变小了,则删除多出来的部分;注意:由于 deque 内部工作原理的特殊性,deque 容器没有容量的概念;#include <iostream>#include <deque> //使用STL中的容器,得包含它的头文件 #include <algorithm> //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d){for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1() //deque容器的大小 {int i = 0;deque<int> d1;for(i=0;i<10;i++){d1.push_back(i+1);}printDeque(d1);if(d1.empty()) //判断deque容器是否为空,如果deque容器为空,则返回true,否则返回false{cout << "当前容器为空" << endl;}else{cout << "当前容器不为空" << endl;cout << "d1的大小为:" << d1.size() << endl; //用于查看容器的大小(元素个数)}d1.resize(20); //重新指定容器的大小为20,容器扩大,以默认值(0)来填充printDeque(d1);d1.resize(25,10); //重新指定容器的大小为25,容器扩大,以10来填充printDeque(d1);d1.resize(5); //重新指定容器的大小为5,容器缩小,删除多出来的部分printDeque(d1);}int main(){test_1();system("pause");return 0;} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354deque 容器插入和删除:两端插入和删除:1.push_back(elem); 在 deque 容器的尾部插入数据;2.push_front(elem); 在 deque 容器的头部插入数据;3.pop_back(); 在 deque 容器的尾部删除数据;4.pop_front(); 在 deque 容器的头部删除数据;指定位置的插入和删除:1.insert(pos,elem); 通过迭代器在 deque 容器的指定位置插入一个 elem 元素;2.insert(pos,n,elem); 通过迭代器在 deque 容器的指定位置插入n个 elem 元素;3.insert(pos,begin,end); 通过迭代器在 deque 容器的指定位置插入区间 [begin,end) 的数据;4.erase(pos); 通过迭代器删除掉 deque 容器指定位置的数据;5.erase(begin,end); 通过迭代器删除掉 deque 容器区间 [begin,end) 之间的数据;6.clear(); 清空当前的 deque 容器;#include <iostream>#include <deque> //使用STL中的容器,得包含它的头文件 #include <algorithm> //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d){for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1() //deque容器两端插入和删除 {deque<int> d1;//尾插法 d1.push_back(10);d1.push_back(20);//头插法d1.push_front(100);d1.push_front(200);printDeque(d1);//尾删法d1.pop_back();//头删法d1.pop_front();printDeque(d1); }void test_2() //deque容器指定位置的插入和删除 {deque<int> d1;d1.push_back(10);d1.push_back(20);d1.push_front(100);d1.push_front(200);d1.insert(d1.begin(),5000); //通过迭代器在deque容器的头部插入一个5000printDeque(d1);d1.insert(d1.end(),2,5000); //通过迭代器在deque容器的尾部部插入两个5000printDeque(d1);deque<int> d2;d2.push_back(1);d2.push_back(2);d2.push_back(3);d1.insert(d1.begin(),d2.begin(),d2.end()); //通过迭代器在d1的头部插入d2区间[d2.begin(),d2.end())的数据printDeque(d1);d1.erase(d1.begin()); //通过迭代器删除掉deque容器头部的数据printDeque(d1);deque<int>::iterator it = d2.begin(); it++;d2.erase(it,d2.end()); //通过迭代器删除掉deque容器区间[it,end)之间的数据printDeque(d2);d1.clear(); //清空当前的deque容器printDeque(d1);} int main(){test_1();test_2();system("pause");return 0;}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980deque 容器数据存取:1.operator[]; 通过重载 [] 的方式,获取 deque 容器的每一个元素;2.at(int idx); 通过成员函数 at() 获取 deque 容器的每一个元素;3.front(); 返回容器中的第一个元素;4.back(); 返回容器中的最后一个元素;#include <iostream>#include <deque> //使用STL中的容器,得包含它的头文件 #include <algorithm> //使用STL提供的算法,得包含它的头文件using namespace std;void test_1() //deque容器数据存取 {int i = 0;deque<int> d1;//300 200 100 10 20 30 d1.push_back(10);d1.push_back(20);d1.push_back(30);d1.push_front(100);d1.push_front(200);d1.push_front(300);for(i=0;i<d1.size();i++){cout << d1[i] << " "; //通过重载[]的方式,获取deque容器的每一个元素}cout << endl;for(i=0;i<d1.size();i++){cout << d1.at(i) << " "; // 通过成员函数at()获取deque容器的每一个元素}cout << endl;cout << "第一个元素为:" << d1.front() << endl; //返回容器中的第一个元素cout << "最后一个元素为:" << d1.back() << endl; //返回容器中的最后一个元素}int main(){test_1();system("pause");return 0;}1234567891011121314151617181920212223242526272829303132333435363738394041deque 容器排序:1.sort(iterator begin,iterator end); 对区间 [begin,end) 之间的元素进行排序;(默认为从小到大,即升序)注意:对于支持随机访问的迭代器,都可以直接利用 sort() 排序进行排序(sort()算法其实是 STL 中很常用的排序算法,STL 中的常用算法后续也会更新出来(●’◡’●));这里提一嘴,STL中的常用算法只允许拥有 支持随机访问的迭代器 的容器使用,迭代器不支持随机访问 的容器是不可以使用这些常用算法的,但为了解决这样的问题,这些容器内置了一些成员函数,这些成员函数的功能与常用算法一致,这些成员函数就只能由对应的容器来使用了;#include <iostream>#include <deque> //使用STL中的容器,得包含它的头文件 #include <algorithm> //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d){for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1() //deque容器排序 {int i = 0;deque<int> d1;//300 200 100 10 20 30 d1.push_back(10);d1.push_back(20);d1.push_back(30);d1.push_front(100);d1.push_front(200);d1.push_front(300);cout << "排序前:" << endl;printDeque(d1);sort(d1.begin(),d1.end()); //对于支持随机访问的迭代器,都可以直接利用sort排序进行排序 cout << "排序后:" << endl;printDeque(d1);}int main(){test_1();system("pause");return 0;}以上就是STL中deque容器的一些常用接口和用法啦O(∩_∩)O。笔记中有错误的地方,欢迎指出,欢迎大家讨论!
-
1.功能测试:主要关注水杯基本功能: 1.1 水杯是否可以正常装水 1.2 水杯是否可以正常喝水 1.3 水杯是否有盖子,盖子是否可以正常盖住 1.4 水杯是否有保温功能,保温功能是否正常保温 1.5 水杯是否会漏水,盖住盖子拧紧后是否会漏水 2.界面测试:主要关注水杯外观、颜色、设计等方面: 2.1 外观是否完整 2.2 外观是否舒适 2.3 颜色搭配及使用是否让人感到舒适 2.4 杯子外观大小是否适中 2.5 杯子是否有图案,图案是否易磨损 3.易用性测试:主要关注水杯使用是否方便: 3.1 水杯喝水时否方便 3.2 水杯拿起放下是否方便,这里会衍生到水杯形状的测试 3.3 水杯装水是否方便 3.4 水杯携带是否方方便 3.5 水杯是否有防滑功能 3.6 水杯装有低温或者高温水时,是否会让手感到不适 4.性能测试: 4.1 水杯装满水时,是否会漏出来 4.2 水杯最大使用次数 4.3 水杯的保温性是否达到要求 4.4 水杯的耐寒性是否达到要求 4.5 水杯的耐热性是否达到要求 4.6 水杯掉落时,是否可以正常使用 4.7 水杯长时间放置时,是否会发生泄露 5.兼容性测试:主要关注水杯是否可以装其他液体,如果汁、汽油、酒精等 6.可移植性测试:主要关注水杯放置环境等 6.1 将水杯放在常温环境中,使用是否正常 6.2 将水杯放在零下的环境中,使用是否正常 6.3 将水杯放在高于正常温度的环境中,使用是否正常 7.安全性测试:主要关注水杯外观和各种异常条件下是否释放有毒物质等 7.1 当水杯装满热水时,水杯是否会烫手 7.2 当水杯装上水后,是否会产生有毒物质 7.3 把水杯放在零下环境时,是否会产生有毒物质 7.4 把水杯放在高温环境时,是否会产生有毒物质
-
云原生是云计算技术不断进化的产物,它与传统GIS(地理信息系统)行业结合,解决了海量数据的分析与计算瓶颈,提供了更稳定、高效、高可用的系统架构。云原生GIS适用于与位置、数据、可视化有关的数据共享、处理、分析、管理类项目。其弹性伸缩、故障自动恢复、跨平台部署等特性,为众多平台搭建、项目改造升级提供了解决方案。如何基于华为云云容器引擎部署云原生GIS?它又有哪些典型应用场景呢?2021年4月1日 19:00,华为云云市场·新生态直播间将邀请北京超图软件股份有限公司云产品研发中心产品经理周世杰,为大家带来《场景+应用,带你“硬核”解析云原生GIS》主题分享。周老师从事GIS软件开发与云计算技术研发工作十数年,具有丰富的云原生理论知识与实战经验,此次直播他将从云原生GIS的概念与背景入手,通过部署实践、应用场景分析、真实案例分享等方面对云原生GIS技术进行全面讲解,剖析基于华为云部署云原生GIS的优势所在。为了推广落地云原生GIS,超图软件(SuperMap)将繁琐的部署流程封装,通过SuperMap iManager实现了一键部署,大大降低人工与时间成本。华为云云容器引擎(Cloud Container Engine)是提供高可靠高性能的企业级容器应用管理服务,支持 Kubernetes 社区原生应用和工具,简化云上自动化容器运行环境搭建。用户可基于华为云 CCE 平台,快速部署云原生GIS,从而享受高弹性、高可用的GIS服务。云原生GIS具有二三维一体化的服务发布、管理与聚合功能,提供多层次的扩展开发能力,内置丰富的 Web 端应用,具备零代码可视化界面定制,并可实现智能化监控、一体化运维。自推出以来,已助力多个项目成功上线,并长期保持稳定、高效运行,其中包括省市级“一张图”平台、大数据服务平台、时空信息平台、CIM平台建设等项目。想了解更多云原生 GIS技术干货,请点击云市场直播间选择新生态在线直播第30期——《场景+应用,带你“硬核”解析云原生GIS》,直播期间(2021年4月1日19:00~20:00),超图软件嘉宾周老师将结合应用场景与实际案例,带你深度解析云原生GIS技术和应用方案,参会还可以赢取超图917大学GIS学院开发者训练营入场券、蓝牙音箱、双肩包等超值大礼!点击查看本期直播商品:超图云GIS管理服务器平台超图云GIS应用服务器平台超图云GIS门户服务器平台超图边缘GIS服务器平台【华为云云市场,助您上云无忧】
-
云原生是云计算技术不断进化的产物,它与传统GIS(地理信息系统)行业结合,解决了海量数据的分析与计算瓶颈,提供了更稳定、高效、高可用的系统架构。云原生GIS适用于与位置、数据、可视化有关的数据共享、处理、分析、管理类项目。其弹性伸缩、故障自动恢复、跨平台部署等特性,为众多平台搭建、项目改造升级提供了解决方案。如何基于华为云云容器引擎部署云原生GIS?它又有哪些典型应用场景呢?2021年4月1日 19:00,华为云云市场·新生态直播间将邀请北京超图软件股份有限公司云产品研发中心产品经理周世杰,为大家带来《场景+应用,带你“硬核”解析云原生GIS》主题分享。周老师从事GIS软件开发与云计算技术研发工作十数年,具有丰富的云原生理论知识与实战经验,此次直播他将从云原生GIS的概念与背景入手,通过部署实践、应用场景分析、真实案例分享等方面对云原生GIS技术进行全面讲解,剖析基于华为云部署云原生GIS的优势所在。为了推广落地云原生GIS,超图软件(SuperMap)将繁琐的部署流程封装,通过SuperMap iManager实现了一键部署,大大降低人工与时间成本。华为云云容器引擎(Cloud Container Engine)是提供高可靠高性能的企业级容器应用管理服务,支持 Kubernetes 社区原生应用和工具,简化云上自动化容器运行环境搭建。用户可基于华为云 CCE 平台,快速部署云原生GIS,从而享受高弹性、高可用的GIS服务。 云原生GIS具有二三维一体化的服务发布、管理与聚合功能,提供多层次的扩展开发能力,内置丰富的 Web 端应用,具备零代码可视化界面定制,并可实现智能化监控、一体化运维。自推出以来,已助力多个项目成功上线,并长期保持稳定、高效运行,其中包括省市级“一张图”平台、大数据服务平台、时空信息平台、CIM平台建设等项目。想了解更多云原生 GIS技术干货,请点击云市场直播间选择新生态在线直播第30期——《场景+应用,带你“硬核”解析云原生GIS》,直播期间(2021年4月1日19:00~20:00),超图软件嘉宾周老师将结合应用场景与实际案例,带你深度解析云原生GIS技术和应用方案,参会还可以赢取超图917大学GIS学院开发者训练营入场券、蓝牙音箱、双肩包等超值大礼!点击查看本期直播商品:超图云GIS管理服务器平台超图云GIS应用服务器平台超图云GIS门户服务器平台超图边缘GIS服务器平台【华为云云市场,助您上云无忧】
-
2021年3月22日,业界应用最为广泛的企业级Kubernetes管理平台Rancher发布了2020年Kubernetes行业调研报告,研究结果表明,2020年,更多的受访者在混合云环境中使用Kubernetes 运行容器,与此同时,更多的企业将功能和服务部署至边缘,最终进一步推动企业IT现代化的进程。2019年和2020年,Rancher分别对近1,000名专业人员展开了调查。调查结果表明,Kubernetes在不同行业连续两年保持了90%以上的采用率,而生产环境中的容器采用率从2019年的85%增长至2020年的87%。“从调研结果可以清晰地看到,用户持续推动容器在混合云和多云环境落地,92%的受访者将容器作为DevOps、IT运维、IT架构、应用程序开发和基础架构转型的关键部分。”Kubernetes和云:真实环境下的真实工具近1,000名在技术、工程、电信、银行、网络安全和咨询等行业工作的专业人员的反馈,他们主要专注于实现DevOps、IT运维、IT架构、应用程序开发和基础架构转型,92%的受访者选择使用容器来实现这些目标。另一方面,这些受访者均更倾向于采用Rancher所推出的Kubernetes发行版。36%的受访者将RKE作为其Kubernetes发行版,而32%的受访者使用K3s。受访者指出,在容器快速发展的大前提下,多层级控制和功能是RKE被广泛采用的关键原因。推动传统IT架构现代化受访者表示,容器化是推动传统IT现代化的关键路径。49%的受访者通过容器实现传统IT应用程序的现代化,而67%的受访者利用容器设计基于微服务的应用程序。通过使用Kubernetes,企业可以将使用了数十年的传统IT系统转化为基于微服务的容器应用程序,并通过Kubernetes进行编排。迁移到Kubernetes还可以使开发团队并行工作,从而减少了重复的可能性,简化开发并加速部署。在混合云环境中,开发团队还可以通过云托管集群完全替代某些本地工作负载,这些集群可以在像Rancher一样的Kubernetes管理平台上运行和管理,进一步实现IT架构现代化的战略。推动边缘环境生产落地容器在生产环境中的采用率逐步增长,也覆盖到了应用端。基于此,许多受访者使用容器来为客户提供各种服务,包括应用程序、边缘计算、混合/多云应用程序、内部应用程序、传统应用程序现代化和将传统应用程序迁移上云等。K3s等体积较小的Kubernetes已经使企业可以更容易地将功能和服务部署至边缘,它赋予了组织从试点项目转向生产环境的能力,并使其能够根据需要进行扩展。2020年,62%的受访者在边缘使用K3s,去年这一比例仅为50%。随着混合云网络的成熟,客户可以部署面向客户的云原生微服务应用程序,所需的维护量更少,迭代次数更多,并且可以随时间轻松添加新功能。我们完全有理由相信,在面向客户的环境中几乎完全采用由Kubernetes管理的容器的这一趋势将持续发展。结论随着公司将他们的网络、应用程序和流程发展成为一个更现代的框架,他们发现无论环境如何,Kubernetes 都可以随之改变。世界各地的组织正在利用 Kubernetes 解决方案来创建一个云原生微服务集群。他们还对单体系统进行现代化,建立强大的云架构,同时无缝管理现有集群。这是向加速和保护应用程序的时代迈进的一步,同时赋予了DevOps和工程团队利用未来“更聪明地工作”而不是“更努力地工作”的能力。“容器及Kubernetes是混合云时代软件定义基础架构的最佳选择,它们不仅能帮助企业实现传统IT基础架构的容器化改造,还能帮助企业释放混合云的全部价值。”通过结合容器及Kubernetes的可靠性和灵活性推动企业在任意场景进行无限创新,从而推动创新无处不在。”文章转载自:https://tech.china.com/article/20210325/032021_738288.html
-
书读百遍,软件部署也一样,想要出坑就跟游戏打怪一样一样的,多试几遍!一开始搞不明白master和node是怎么关联到一起的: kubeadm init运行的时候会自动生成token,然后按照提示,在node上输入token运行 kubeadm join。再有就是不明白哪些在master上安装,哪些在node上安装,这种问题真的是小白变小黑的门槛![root@ecs-385f ~]# kubeadm join 192.168.x.x:6443 --token ************ --discovery-token-ca-cert-hash sha256:********************************b6f34c W0322 22:27:13.158886 9533 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set. [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.5. Latest validated version: 19.03 ^C//卡在这里不动了。搜资料,通常join卡住原因有几个:kubelet起不来 (系统兼容性导致)kubelet能起来但是docker无法正常创建容器 (系统兼容性导致)不通apiserver (服务器网络导致)sealos的ipvs代理异常,规则没创建或创建不生效 (系统网络不支持ipvs nat)[root@ecs-385f ~]# ping 192.168.x.x 64 bytes from 192.168.x.x: icmp_seq=1 ttl=64 time=0.529 ms ^C --- 192.168.x.x ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.274/0.344/0.529/0.108 ms [root@ecs-385f ~]# systemctl status kubelet â— kubelet.service - kubelet: The Kubernetes Node Agent Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled) Drop-In: /usr/lib/systemd/system/kubelet.service.d └─10-kubeadm.conf Active: activating (auto-restart) (Result: exit-code) since Mon 2021-03-22 22:30:20 CST; 5s ago Docs: https://kubernetes.io/docs/ Process: 9785 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=255) Main PID: 9785 (code=exited, status=255) Mar 22 22:30:20 ecs-385f systemd[1]: kubelet.service: main process exited, code=exited, status=255/n/a Mar 22 22:30:20 ecs-385f systemd[1]: Unit kubelet.service entered failed state. Mar 22 22:30:20 ecs-385f systemd[1]: kubelet.service failed. [root@ecs-385f ~]# curl -k https://192.168.x.x:6443 curl: (7) Failed connect to 192.168.x.x:6443; Connection timed out这里报错了,但是由于不知道这个测试是用来测什么的,去一个网络没问题的地方试试,对,就是master上:这里是一个网页文本显示,虽然是403,这个不用管它。推测是网络问题。返回控制台查询master的安全组,购买时以为默认会选全放通的,结果是web-server的.放通权限修改后,重新在node上运行测试,这次就成功了。[root@ecs-385f ~]# curl -k https://192.168.x.x:6443 { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"", "reason": "Forbidden", "details": { }, "code": 403 } [root@ecs-385f ~]# kubeadm join 192.168.x.x:6443 --token ********* --discovery-token-ca-cert-hash sha256:*****************89ceb58d22b6f34c W0322 22:37:32.704624 21395 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set. [preflight] Running pre-flight checks [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.5. Latest validated version: 19.03 [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' [kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.17" ConfigMap in the kube-system namespace [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the control-plane to see this node join the cluster.在master上查看:
-
1 Docker入门基础1.1 容器级虚拟机化技术主机级虚拟化, Type1:在硬件上安装虚拟机,hyper-v,没有宿主机 Type2:有宿主机及宿主机OS,虚拟机OS与宿主机OS不一样,如Vmware,workstations。容器级虚拟化:实现用户空间和宿主环境空间的隔离,保障了系统安全。在UTS内可以以名称空间互相隔离的,在同一个空间上创建多个名称空间,每个名称空间互相隔离,拥有各自的根文件系统,root用户,PID 和端口范围。 Namespace原生支持UTS、mount、IPC、PID、User、Net。Linux容器化需要内核支持namespace技术。所以容器是Linux内核封装的技术。1.2 Docker和容器关系1.2.1 Linux容器1、隔离与共享一台服务器运行着多个逻辑隔离的服务器进程,谁的运行环境都不希望影响到谁,也就是一个物理机需要虚拟出多个环境或容器,Linux提供一种创建和进入容器的方式,操作系统让应用程序就像在独立的机器上运行一样,但又能共享很多底层的资源。2、实现基础Linux容器功能是基于cgroups和Namespace实现的。(1)cgroups(control groups 控制组)cgroups是将进程分组管理的内核功能,通过cgroups可以隔离进程,同时还可以隔离进程的资源占用(cpu,内存等)情况,在操作系统底层限制物理资源,起到container的作用,进程可用的cpu资源由cpuset指定。(2)NamespaceNamespace让每个进程拥有独立的PID、IPC和网络空间。Namespace是通过clone系统调用来实现的。clone系统调用的第三个参数flags就是通过设置Namespace来划分资源的。Linux一共构建了6种不同的Namespace,用于不同场景下的隔离1.Mount - 隔离文件系统挂载点;2.UTS - 隔离主机名和域名3.IPC - 隔离进程间通信资源4.PID - 隔离PID空间5.Network - 隔离网络接口6.User - 隔离用户/用户组空间1.2.2 LXC基本概念根据Docker布道师Jerome Petazzoni的说法,Docker约等于LXC+AUFS(之前只支持ubuntu时)(作者2015-10-22更新:Docker0.9.0版本开始引入libcontainer,可以视作LXC的替代品)。其中LXC负责资源管理,AUFS负责镜像管理;而LXC包括cgroup、namespace、chroot等组件,并通过cgroup进行资源管理。所以只从资源管理这条线来看的话,Docker、LXC、Cgroup三者的关系是:Cgroup在最底层落实资源管理,LXC在cgroup上封装了一层,Docker又在LXC封装了一层,关系图如图1(b)所示。因此,要想玩转Docker,有必要了解负责资源管理的CGroup和LXC。Jail->vserver(chroot)->LXC, 大规模创建容器很难,管理不方便。1)LXC是什么LinuxContainer容器可以提供轻量级的内核级虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。LXC建立在CGroup基础上,我们可以粗略的认为LXC = Cgroup+ namespace + Chroot + veth +用户态控制脚本。LXC利用内核的新特性(CGroup)来提供用户空间的对象,用来保证资源的隔离和对于应用或者系统的资源控制。根据LXC官网(http://linuxcontainers.org/)的描述,LXC具有以下特性:与虚拟化相比,它的优势在于:a)不需要指令级模拟;b)不需要即时(Just-in-time)编译;c)容器可以在CPU核心的本地运行指令,而不需要任何专门的解释机制;d)避免了准虚拟化和系统调用替换中的复杂性。总结来说,就是LXC更加轻量级,具有更小的性能开销、更快的相应时间。linux contains 的技术是linux 内核的代码,并非Docker 开发出来的,Docker或者其他的虚拟化容器都是基于LXC 的技术,在基础的lxc 上包了一层代码,让LXC 更简单、更友好,更加好推广;下面就看下LXC 的三个技术 •chroot: 创建一个虚拟的根目录文件系统 【实质还是调用底层的文件系统】,不过是建立一个虚拟的,可以跟其他容器的虚拟文件系统相互隔离;但共享底层的文件系统•namespace : 命名空间可以提供一个进程相互隔离的独立网络空间,不同的容器间进程pid可以相同,进程并不冲突影响;但可以共享底层的计算和存储(cpu + mem)•cgroups: 实现了对容器的资源分配和限制,比如给容器1分配10core 30G 内存;那这个容器最多用这么大的资源;如果内存超过30G ,会启动swap,效率降低,也可能会被调度系统给kill掉LXC通过namespace进行资源的隔离,Gust1下的进程与Guset2下的进程是独立的,可以看作运行在两台物理机上一样。Contaniner管理工具就是对Guest进行管理的(创建、销毁)。下图是LXC与KVM技术的比较,KVM的优点是一个物理机上可以跑多个操作系统(Guest-OS),然后在每个操作系统运行应用,通过这种方式实现应用的隔离。而使用LXC技术直接可以在Host-OS的基础上实现隔离的。这就是LXC的优势--运行快。但是,如果有两个应用一个是在windows运行的,一个是在linux上运行的,这时只能使用KVM技术来实现了。1.2.2.1 Cgroups基本概念1)Cgroup是什么Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:CPU, Memory, IO等)的机制。最初由Google的工程师提出,后来被整合进Linux内核。Cgroups也是LXC为实现虚拟化所使用的资源管理手段,可以说没有Cgroups就没有LXC,也就没有Docker。Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的Cpuset等子系统,也为未来开发新的子系统提供接口。现在的Cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。简单说是把系统级资源分成多个组,每组资源分配到特定用户空间来使用。Cgroups提供以下功能:a)限制进程组可以使用的资源数量(Resource limiting )。比如:Memory子系统可以为进程组设定一个Memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。b)进程组的优先级控制(Prioritization)。比如:可以使用CPU子系统为某个进程组分配特定CPUshare。c)进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。d)记录进程组使用的资源数量(Accounting)。比如:可以使用Cpuacct子系统记录某个进程组使用的CPU时间e)进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。2)Cgroup基本概念与术语任务(task):在Cgroups中,任务就是系统的一个进程。控制族群(control group):控制族群就是一组按照某种标准划分的进程,控制族群通常按照应用划分,即与某应用相关的一组进程,被划分为一个进程组,即控制族群(control group)。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也可以从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用Cgroups以控制族群为单位分配的资源,同时受到Cgroups以控制族群为单位设定的限制。层级(hierarchy):控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。控制族群树的示意图如图2所示。子系统(subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。3)Cgroup子系统介绍a)blkio -- 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB等等)。b)cpu -- 这个子系统使用调度程序提供对CPU 的 Cgroup 任务访问。c)cpuacct -- 这个子系统自动生成Cgroup中任务所使用的 CPU 报告。d)cpuset-- 这个子系统为 Cgroup中的任务分配独立CPU(在多核系统)和内存节点。e)devices -- 这个子系统可允许或者拒绝Cgroup中的任务访问设备。f)freezer -- 这个子系统挂起或者恢复Cgroup中的任务。g)memory -- 这个子系统设定Cgroup中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。h)net_cls -- 这个子系统使用等级识别符(classid)标记网络数据包,可允许Linux 流量控制程序(tc)识别从具体cgroup 中生成的数据包。i)ns -- 名称空间子系统。Cgroup具有不同的挂载方法——“多挂载点”和“单挂载点”。子系统“多挂载点”挂载就是指不同子系统的文件挂载在不同的目录下,每个子系统各有一个挂载点,目录结构如图3(a)所示。cgroup对应服务cgconfig默认使用的就是“多挂载点”的方法。“单挂载点”则是指所有子系统的文件都挂载在同一个目录下,所有子系统都统一挂载在一个挂载点,目录结构如图3(b)所示。 1.2.2.2 NamespaceLinux Namespaces机制提供一种资源隔离方案。PID,IPC,Network等系统资源不再是全局性的(在Linux2.6内核以前是全局的),而是属于特定的Namespace。每个Namespace里面的资源对其他Namespace都是透明的。namespace是container中使用到的重要技术之一,是对系统资源的操作上的隔离。使Guest-OS1的操作对Guest-OS2无法产生影响。当然namespace的实现还在完善中,下面是3.8以上的内核实现的namespace。1. Mount namespace是对挂载的文件系统布局进行隔离。图中显示在Namespace1中的进程看到的文件系统的挂载方式是一致的,但是在Mount Namespace2中看到的是一另一种情况。2.IPC:处于同一namespace下的进程才可以进行进程间通信。3. NET NAMESPACE实现网络协议栈上的隔离,在自己的namespace中对网络的设置只能在本namespace中生效4.PID:我们通过fork来创建进程时可以为每个进程指定命名空间。linux下的进程关系是一棵树,所以有了父命名空间和子名字空间之分。在namespace2创建的P2进程有两个pid。第一个是在父命名空间的下的它的PID号,一个是在自己空间下的PID号。之所以有父pid号是因为P2最终还是在父命名空间下运行的,而为进程指定命名空间是为了让P2和P3实现隔离。5. User namespace中使用到了map转换,由于container并不是真正的虚拟化,所以在Guest-OS中创建的root用户会被映射到Host-OS中的普通用户中去。下图中的例子中,root用户在自己的namespace下创建了一个文件,那这个文件的所有者ID应该是0,当时在磁盘上存的时候文件UID会被转换为kuid,并且所有者ID为1000。想说名一点是在Guest-OS下你是个root用户,但是在Host-OS你只不过被转为一个普通用户而已。因为我们知道在Host-OS下已经有一个root用户了。6.PID namespace:linux下的proc目录是对整个系统状态的描述,用户可以通过查看proc目录来了解当前的系统状态。在proc目录下有很多数字,这些数字对应的是系统创建的进程ID,以前我们说进程是看不见摸不着的,但是通过proc目录我们的确可以看到一些关于进程的信息。每个进程下有个ns目录,在目下记录了该进程使用的到namespace。1.2.2.3 Chroot1. linux chroot 机制的由来•root 用户启动一个daemon在linux 系统上启动一个daemon 必须用root 用户来启动,比如一个web 服务器(nginx/apapce 80端口)是在操作系统的接口(1-1024),只有root 有这个权限来启动这类接口;用root 户启动daemon 的程序也是一个自然的事情。 •安全问题日益变大随着安全的攻击越来越严重,如果任何一个提供TCP 服务的程序出现漏洞,那攻击者就获取到了root 权限,无疑是灾难性的。为了降低这个问题带来的风险,需要主动放弃root 权限,该用一个普通的用户(比如 admin/nobody) 进行运行。这样一旦攻击者获取到了这个程序的权限,也是此时运行用户的权限,对系统造成的危害相对要小。•chroot 机制的引入为了进一步提高系统的安全性,linux 系统引入了chroot 机制;chroot 是一个系统调用,程序可以通过调用chroot的函数库来 更改一个进程所能看到的跟目录 。比如httpd 软件安装在/usr/local/httpd 这个目录下,那这个进程(httpd) 只可以读、写到这个指定的目录: [ usr/local/httpd] ;这样即使攻击者获取进程的权限,也只能读写这个目录下的文件,这样就变的安全了许多,起码不会影响这个台机器其他的进程,和其他的机器的安全问题 。2. chroot 简介chroot 全程是change to root : 其中root 是根目录的意思,也就是改变(linux 根目录是/,也可以理解为设置)一个程序运行时参考的根目录的位置 # 根目录的参考linux 系统(原始的方案) | 引入chroot 机制/ /lxc/usr /lxc/usr/bin /lxc/bin/sys /lxc/sys如上图我们看的一旦使用了chroot ,用户的春心就不是linux 系统的根目录,而是我们指定的/lxc (这个目录可以任意指定),所以chroot 确实可以修改根目录. 3. chroot 机制的意义•增强系统的安全行•指定程序访问的根目录,防止用攻击者可以通过程序的漏洞获取其他目录的读写权限; 4. chroot 机制在虚拟化中的作用chroot 机制因为安全问题才被引入的,但是在LXC 中却启动了举足轻重的作用,因为chroot 机制可以指定虚拟根目录,让不同的容器在不同的根目录下工作; •不同的容器进程参考的根目录不同,相互直接不影响•不同的容器因为共享linux 底层的文件系统,所以容器集成os的功能,实现轻量级!1.2.2.4 LXC总结通过对ns和Cgroups的介绍,一方面可以实现对系统资源的隔离,但是使用就非常不方便涉及代码实践、底层内核调用以及进程克隆等。通过LXC可以实现对这些工具的封装调用,简化了用户使用。但是LXC使用也不是很方便,比如需要创建出一个虚拟环境,通常会执行一个模板脚本,从指定官网下载对应的软件工具,在本地完成安装后,生产名字空间的运行环境。所以LXC的使用比虚拟机也不是很大方便,隔离性也不比虚拟机好。大规模使用和分发也不友好,后面出现docker。Docker是LXC的加强版,相当于是LXC的前端应用工具。容器是内核技术,docker通过简化容器的使用,得以普及。解决方案为镜像。利用容器管理引擎,通过镜像,来创建出名字空间所需的环境。 1.2.3 LXC与docker的比较:容器技术不是模仿硬件,而是在Linux内核里使用cgroup和namespaces来打造轻便的、将近裸机速度的虚拟技术操作系统环境。因为不是虚拟化存储,所以容器技术不会管底层存储或者文件系统,而是你放哪里,它操作哪里。 这从根本上改变了我们如何虚拟化工作负载和应用程序,因为容器速度比硬件虚拟化技术更快,更加便捷,弹性扩容的更加高效,只是它的工作负载要求操作系统,而不是Linux或特定的Linux内核版本。Docker并不是LXC的替代品,Docker的底层就是使用了LXC来实现的。LXC将Linux进程沙盒化,使得进程之间相互隔离,并且能够控制各进程的资源分配。 在LXC的基础之上,Docker提供了一系列更强的功能。不过现在Docker底层已不使用LXC,先后推出libcontainer和runC. 利用LXC做容器管理引擎,将用户空间需要用到的组件打包封装好,成为镜像文件。极大简化了容器的使用难度,使得容器使用得以推广。容器技术一方面给技术带来极大便利,同时也给维护带来很大挑战。 1.3 Docker基础概念1.3.1 docker是什么Docker是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在本地编译通过的容器可以批量的在生产环境上部署。Docker类似于集装箱,各式各样的货物,经过集装箱的标准化进行托管,而集装箱与集装箱之前没有影响。Docker是一个开放平台,使开发人员和管理员可以在称为容器的松散隔离的环境中构建镜像、交互和运行分布式应用程序,以便在开发、QA和生产环境之间进行高效的应用程序生命周期管理。分层构架和联合挂载虚拟机原理需要在Host OS(内核)之上虚拟出一套虚拟硬件,在虚拟硬件之上重新安装操作系统,即为虚拟机操作系统(内核),在该系统之上安装APP,对外提供服务。可见虚拟化对硬件资源的消耗还是比较大,通常在20~30%。而容器概念在于在Host OS之上,提供一套虚拟隔离环境(内核态),在用户空间隔离出进程环境。1.3.2 Docker三个组件 1.3.2.5 镜像一个特殊的文件系统。操作系统分为内核和用户空间,对于Linux来说,内核启动后会挂载root文件系统为其提供用户控件的支持。而Docker镜像,就相当于是一个root文件系统。除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含一些为运行时准备的配置参数。镜像不包含任何动态数据,其内容在构建之后也不会被改变。镜像实际是由多层文件系统联合组成。镜像构建时,会一层一层构建,前一层是后一层的基础。每一层构建完就不会再改变,后一层上的任何改变只发生在当前层。比如:删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅把当前层标记为该文件已删除。分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。1.3.2.6 容器镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、暂停、停止、删除等。容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行与属于自己独立的命名空间,容器也是分层存储。容器存储层的生命周期跟容器一样,容器消亡时,容器存储层也会消亡,任何保存于容器存储层的信息都会丢失。容器不应该向其存储层内写入任何数据,容器存储层也要保持无状态化。所有的文件写入操作,都应该使用数据卷、或者绑定宿主目录,在这些位置的读写会跳过存储层,直接对宿主发生读写,其性能和稳定性更高。容器消亡后数据卷的数据不会丢失。容器在整个应用程序生命周期工作流中提供以下优点:隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。1.3.2.7 仓库Docker Registry是一个集中存储、分发镜像的服务。一个Registry可以包含多个仓库(Repository),每个仓库只包含一种软件,但可以包含多个标签(tag,也就是版本),每个标签对应一个镜像。这三个组件的关系如下图,比如有两个仓库,分别是Redis和MySQL。 1.3.3 Docker组成客户端:docker pull build run服务器仓库 镜像image:是个模板,通过模板创建容器服务。通过镜像创建多个容器 容器container:docker通过容器独立运行一个或一组应用,通过镜像来创建的。启动 停止 删除等基本命令 容器是一个简易的Linux系统 仓库repository:存放镜像的地方1.3.4 Docker架构Client-Server架构:•docker守护进程运行在宿主机上systemctl start docker•daemon进程通过socket从客户端(docker命令)接受命令来运行管理各个容器•容器是一个运行时环境,可以看做是运行中的精简版Linux系统 1.4 docker安装:docker文档:https://docs.docker.com/安装指导:https://docs.docker.com/engine/install/centos/推荐使用阿里云的docker镜像安装:https://blog.csdn.net/lvdingding/article/details/112862396dockerHUB地址: ce社区版,官方推荐ee企业版 hello-world流程底层原理docker是server-client结构的系统,docker的守护进程运行在主机上,通过socketo从客户端访问。dockerserver接受客户端的指令,就会执行docker命令。 1.5 Docker和虚拟机的不同:1.传统虚拟机,虚拟出一套硬件,运行一个完整的操作系统,在这个系统之上安装和运行软件。占用资源大,启动慢。2.容器内的应用直接运行在宿主机的内核上,没有自己的内核,也不需要虚拟硬件,因此容器轻便。3.每个容器互相隔离,每个容器内部都有自己的文件系统,互不影响。DevOps开发运维应用更快的交付和部署传统的交付部署:一堆帮助文档,安装程序docker:打包镜像发布测试一键运行 更便捷的升级和扩缩容 更简单的系统运维,测试环境基本一致 更高效的计算资源利用,docker是内核级的虚拟化,一台服务器上可以运行多个docker实例。 内核的作用在于资源的管理和分配,如内存虚拟化,CPU调用,IO的调度,生产服务,如Nginx等都运行在用户应用空间。1.5.1 Virtual MachinesVM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;1.5.2 Containers容器完全在容器主机的应用程序层中运行。在UserLAnd[1]中,OSs有内核空间,操作系统的核心在其中运行。然后是用户空间,所有与用户活动相关的东西都在这里运行。没有安装操作系统。容器共享主机的内核空间,类似于主机中的一个用户,LXC所实现的隔离性主要是来自kernel的namespace, 其中pid, net, ipc, mnt, uts 等namespace将container的进程, 网络, 消息, 文件系统和hostname 隔离开。但是,cgroup的隔离性要比kvm粒度大,并且很难metric指标。比如,磁盘的IO就很难隔离。导致一个容器的io增大,这个主机都有可能hang。容器实际上就是一个进程。当它在容器内时,感觉就像一台完全独立的机器。我们可以启动新的进程,当监控这些进程时,您将只看到容器内的进程。但是,如果您监控主机进程,仍然能够看到容器中运行的所有内容。 容器共享主机。那么他是如何在同一台机器上运行Ubuntu和CentOS呢? 因为容器实际上共享主机的内核。特定的Linux发行版都构建在同一个内核之上(尽管版本不同)。所有的包管理器、UI之类的东西,以及其他各种软件,都可以在用户空间中运行,这些软件使发行版独一无二,并创造出不同的Linux风格。具有不同发行版的不同容器可以在同一台机器上运行而不会产生冲突。 当涉及到平台设计时,这个内核共享事实还有其他重要的含义。例如,Windows容器将不能在Linux主机上运行。 不可修改 容器本质上是不可变的。 容器操作系统、库、实用程序和应用程序在构建时都是冻结的,在此之后它就不能更改了。所以,不需要以传统的方式更新容器。重新构建和重新部署。虽然有一些缺点,但是在可重复性、简化部署和可靠性方面有很大的提高。 镜像(image) 镜像其实是一个标准的文件包,它表示容器运行时文件系统的状态。这可以发布到注册表,也可以用作父镜像。 大多数镜像将构建在父镜像之上(父镜像通常也构建在另一个镜像像之上)。基本映像没有父镜像的。1.5.3 小结 简而言之,容器化为单个OS上的工作负载隔离提供了标准化的方法,而虚拟化为在一台服务器上安装多个OSs提供了标准化的方法。它们在业界都很突出,在云计算中经常一起使用。 因为容器没有安装完整的操作系统,所以它的重量更轻,因为下载和运行更快,存储更小。下图以尽可能简单的方式说明了上述差异。请注意使用VMs和容器运行相同的两个工作负载的堆栈组合。 如果大家理解了上面描述的容器和vm之间的关键区别,那么第二个图将提供对正在发生的事情的更深入的信息。大家可以清楚地看到这两种技术如何提供工作负载隔离。 记住,堆栈表示逻辑层次结构。我们知道这些容器都在主机操作系统上作为唯一的进程运行,而VM客户操作系统是成熟的操作系统,可以管理它们自己的进程。 现在,让我们看看vm和容器通常是如何一起使用的。假设我们想在云中运行Python Flask应用程序和Java Spring应用程序。下图描述了AWS上可行的状态。 Amazon EC2是Amazon的托管计算服务。这意味着用户不需要担心服务器或管理程序。我们只需选择实例类型(针对不同工作负载的不同特性)并部署VM。(国内的同学们还是使用阿里云吧) 有许多不同的Amazon机器镜像可供选择[3],或者可以创建自己的镜像。因为vm比容器更重。 我们可以在许多不同的服务中共同使用VM镜像。可以将基本操作系统、包更新、一些脚本、监视代理的安装和配置,以及其他操作和安全工具打包到vm中。 通常使用配置管理工具(如Ansible或Chef)来管理工作负载或服务。Chef已经是devops的标准管理工具。 1.6 docker为什么比VM快1.docker的抽象层比虚拟机更少2.docker利用的是宿主机的内核,VM需要guest OSdocker申请容器的时候不需要像虚拟机那样加载一个操作系统内核,避免引导耗时。虚拟机是加载Guest OS,分钟级的docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级。 可见虚拟机对硬件资源的消耗很大,通常在20~30%。 1.7 Docker常用命令docker version版本信息/info 显示docker的系统信息,包括镜像和容器的数量/help 帮助命令官方命令地址:https://docs.docker.com/reference/镜像命令docker imagesdocker searchdocker pulldocker rmidocker rm 容器命令docker inspect 容器IDdocker top 容器ID进入当前运行的容器docker exec -it 容器ID /bin/bash 进入容器后开启一个新终端docker attach 容器ID 进入容器正在执行的终端,不会执行新进程docker stop/start 容器ID从容器内拷贝内容到宿主机上docker cp 容器ID:容器内部地址 宿主机地址 思考:在宿主机中提供容器文件的映射地址,实现在宿主机上修改达到容器内部内容的自动修改。---数据卷 docker run -it --rm tomcat ##之前都启动是后台启动,容器还可以查到,这个方法为用完就删除容器和镜像1.8 ElasticSearch实验https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-cli-run Docker stats: 1.9 PortainerPortainer是docker图形管理工具,提供一个后台面板供开发者使用。访问测试:登录创建用户后选择local本地仓库点击connect之后进入portainer主页 1.10 Docker持续开发工作流 2 Docker镜像详解2.1 镜像详解2.1.1 镜像是什么将应用和环境打包成一个镜像。镜像是轻量级、可执行的独立软件包,打包了运行某个软件(比如tomcat镜像)所需的所有内容,包括:•代码(tomcat代码)•运行时环境(OS、JDK)•依赖库•环境变量•配置文件等镜像底层基础是Union File System(联合文件系统):UnionFS:一种分层、轻量级且高性能的文件系统,支持对文件系统的修改作为一次提交来一层层的叠加,也支持将不同目录挂载到同一虚拟文件系统下。镜像由一层层的文件系统组成,通过分层进行继承。基于基础镜像,可以制作出各种具体的应用镜像。镜像运行时,一次联合加载多个文件系统,根据继承关系进行叠加,最终外部只看到一个文件系统,但拥有了完整的文件和目录结构。 2.1.2 Docker镜像加载原理分层构架,联合挂载。 下载镜像时一层一层下载就是这个概念。所以这也是为什么虚拟机是分钟级,容器是秒级的。 镜像实际有一层层的文件系统组成,即UnionFS。文件系统层级中主要关注bootfs和rootfs“”•bootfs包括BootLoader和kernel(操作系统内核),BootLoader主要是引导加载kernel。同Linux,docker镜像最底层是bootfs。Linux系统启动时,会加载bootfs,然后BootLoader加载kernel(Linux内核)至内存,完成之后内存的使用权由bootfs转移给内核,接着卸载掉bootfs。•rootfs包含了我们熟悉的Linux文件目录结构:/dev/ /proc/ /bin/ /etc/ 等。对于不同的Linux发行版(Ubuntu、centos等),bootfs基本一致(内核相同,都是Linux-kernel),而rootfs会有差别。why一个centos的docker镜像只有200M,而VMware的centos系统镜像几个G?对于一个精简的Linux系统,rootfs可以很小,只需要包括最基本的命令、工具和程序库就OK了。docker容器共用了宿主机的系统内核,只需要提供精简的rootfs就OK,所以docker的os镜像体积可以这么小,因此可以把docker容器看做一个精简的Linux系统。why一个tomcat的docker镜像反而比一个centos的docker镜像大得多每个应用级别的docker镜像,都是源于基础镜像(联合文件系统),类比Java中的Object类,一层层继承而来。 2.2 提交镜像Docker commit 提交容器成为一个新的副本实践如下: 3 Docker数据卷3.1 什么是容器数据卷Docker的核心是把应用和环境打包成镜像,那么数据呢?如果数据保存在容器中,那就无法保证数据的持久化。所以希望容器之间有数据共享功能。通过容器数据卷技术可以将容器中产生的数据,同步到本地。简单说,就是目录挂载,将容器内的目录挂载到宿主机。实现容器数据的持久化和同步操作,也实现了容器间的数据共享。Docker -v 宿主机目录:容器目录 查看容器卷是否挂载成功,执行命令docker inspect 容器ID3.2 具名和匿名挂载如何确认是具名还是匿名,以及指定路径挂载?3.3 Dockerfile创建 这个卷一定是与外部目录有一个同步目录。该挂载方式为匿名挂载。对应容器外的路径3.4 容器数据卷创建容器数据卷创建目标容器 实现了两个容器数据的同步。多个也是可以实现数据的同步。实现数据在两个mysql中的数据共享 4 Docker dockerfile4.1 基础知识Dockerfile就是构建docker镜像的构建文件,命令参数脚本。 4.2 Dockerfile构建过程Dockerfile是面向开发的,以后发布项目,做镜像,就要编写dockerfile文件,这个文件很简单。Docker镜像逐步成为企业交付的标准,必须掌握。Dockerfile是构建文件,定义了一切步骤,源代码;dockerimages通过dockerfile构建生成的镜像,最终发布和运行产品。Docker容器是镜像运行起来提供服务的。4.3 Dockerfile指令
上滑加载中
推荐直播
-
华为云码道Agent集成与鸿蒙实战2026/08/11 周二 19:00-21:00
王一男-华为云码道产品规划专家;李炎-华为云码道产品专家;彭江敏-华为云鸿蒙端云一体化开发专家
本次直播带你解读华为云码道7月份产品新特性、新功能。更有专家演示码道Agent Space × 钉钉机器集成实战,从0到1打通消息通道;码道鸿蒙端云一体化实战,快速搭建员工签到系统。
回顾中 -
华为云开发者AI素养直播课·第三期2026/08/21 周五 16:00-18:00
林华鼎-华为云AI开发者运营负责人;念擎-华为云AI开发者运营案例开发专家
本期直播内容:AI六层能力首次详细解读 + 新一代华为云开发者空间亮相 + 校园案例直播带练
回顾中
热门标签