关于弹性伸缩:聚焦弹性问题杭州铭师堂的-Serverless-之路

得益于互联网的倒退,常识的流传有了新的载体,应用在线学习平台的学生规模逐年增长,越来越多学生在线上获取和应用学习资源,其中教育科技企业是比拟独特的存在,他们担当的不仅仅是教育者的角色,更是让新技术的创新者和实践者。作为一家在线教育高科技企业,杭州铭师堂成立十余年来统一致力于用“互联网+教育”的科技伎俩让更多的学生能享有优质的教育,促成他们的全面成长,在一直汇聚优质的全国各地教育资源的同时,杭州铭师堂深度聚焦教学效率的晋升,深耕先进技术,促成其在学校教育智能化畛域、个性化学习畛域广泛应用。 目前网上教学需要的常态化,老师在线审阅作业需求量急剧增大,为了加重老师的审批工作量,晋升教学效率,杭州铭师堂教育基于 Serverless 创造性的开发了学习笔记评优零碎, 晋升弹性效率,并大幅度降低老本。 残缺内容请点击下方链接查看: https://developer.aliyun.com/article/1189924 版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。

April 23, 2023 · 1 min · jiezi

关于弹性伸缩:从资源弹性到数据弹性乾象如何将云上量化研究效率提升-40

机器学习、云计算、云原生等技术的提高给金融行业翻新注入了新的能源,以乾象投资 Metabit Trading 为代表的以人工智能为外围的科技型量化投资公司的工作就十分有代表性。他们通过深度交融和改良机器学习算法,并将其利用于信噪比极低的金融数据中,为投资人发明长期可继续的回报。 与传统的量化剖析不同,机器学习不仅关注股价、交易量和历史回报率等结构化数据,还注入来自研报、财报、新闻和社交媒体等非结构化数据来深刻理解证券价格走势和波动性。然而,将机器学习利用于量化钻研是具备挑战性的,因为原始数据可能蕴含噪声。此外,他们还须要应答许多挑战,如突发工作、高并发数据拜访和计算资源限度等。 为此,乾象投资在研发投入、翻新反对和根底平台建设方面继续发力。他们的钻研基础设施团队构建了一个高效、平安、规模化的工具链研发流程,通过正当利用云计算和开源技术冲破了单机研发的限度。本文将分享乾象量化钻研根底平台的具体实际,介绍基于 Fluid+JuiceFSRuntime 的公共云弹性量化投研工作撑持。 01 量化钻研的工作详解作为 AI-powered hedge fund,通过 AI 模型训练进行策略钻研是咱们最次要的钻研形式。首先,在模型训练之前须要对原始数据做特征提取。金融数据的信噪比特地低,如果间接应用原始的数据进行训练,失去的模型乐音会十分大。原始数据除了行情数据,即大家常常会看到的市场上的股价、交易量之类的数据,也包含一些非量价的数据,比方研报、财报、新闻、社交媒体等之类的非结构化数据,钻研人员会通过一系列的变换提取出特色,再进行 AI 模型训练。能够参考上面咱们钻研场景中和机器学习关联最严密的策略钻研模式的简化示意图。 模型训练会产出模型以及信号。信号是对将来价格趋势的判断,信号的强度意味着策略导向性的强度。量化研究员会依据这些信息去优化投资组合,从而造成交易的实时仓位。这个过程中会思考横向维度(股票)的信息来进行危险管制,例如某一行业的股票不要适度持仓。当仓位策略造成之后,量化研究员会去模仿下单,而后失去实时仓位对应的盈亏信息,从而理解到这个策略的收益体现,这就是一个量化钻研的残缺流程。 02 量化钻研根底平台的需要第一,突发工作多,弹性要求高。在策略钻研的过程中,量化研究员会产生策略想法,并会通过试验去验证本人的想法。随同着钻研人员新想法的呈现,计算平台就会产生大量的突发工作,因而咱们对计算的弹性伸缩能力要求很高。 上图是咱们某个集群一段时间的运行实例数据。以上图为例,能够看到在多个时间段里,整个集群实例数顶峰时刻能够达到上千个,然而同时整个计算集群的规模也会有缩容到 0 时候。量化机构的计算工作和研究员的研发进度是有很大关联的,波峰波谷的差距会十分大,这也是离线钻研工作的特点。 第二,热数据高并发拜访,除了计算须要弹性,数据缓存也须要弹性。对于热数据,比方行情数据,通常会有上百个工作同时拜访数据,它的吞吐要求十分高,峰值时数百 Gbps 甚至 Tbps 级别的聚合带宽能力满足需要。然而当计算集群中没有任何节点的时候,此时的吞吐需要为 0,如果是刚性吞吐这就须要弹性吞吐扩缩容的能力。 第三,容量和吞吐的独立线性扩大能力,对金融模型训练十分重要。传统分布式存储带宽与吞吐仅和数据应用容量成正比,而量化钻研过程中会创立大量的容器并发拜访存储系统的数据,会触发存储系统拜访限流。这就造成计算资源极致弹性与存储系统无限带宽之间的矛盾。而量化钻研的数据量其实不是特地大,很多市场的量价数据总量也不会超过 TB 级,然而数据拜访须要的峰值吞吐却十分高。 第四,数据亲和性调度,同一数据源屡次运行拜访本地缓存能够被复用。充分发挥热点数据集的缓存节点劣势,在对用户无感知的前提下,智能的将任务调度到数据缓存节点上。让罕用的模型训练程序越来越快。 第五,IP爱护:数据共享与数据隔离。出于 IP 爱护的需要,不仅在计算工作上须要做隔离,在数据上也是须要具备权限管制的隔离能力;同时对行情数据这类绝对公开的数据,还须要反对研究员的获取形式是便捷的。 第六,缓存两头后果。计算工作模块化的场景会对两头后果的存储跟传输也有需要。举个简略的例子,在特色计算过程中会生成比拟大量的特色数据,这些数据会立即用于接下来大规模高并发的训练节点上。不言而喻在这种场景下咱们须要一个高吞吐和高稳固的两头缓存做数据传递。第七,多文件系统的反对。计算工作中各类型的工作会对应的各种个性的数据类型和应用形式,因此咱们不同团队会采纳不同的文件系统包含 OSS,CPFS,NAS,JuiceFS,以获取在各自状况下的性能最优化。Fluid 的不同 runtime 可能灵便的反对文件系统与工作的组合,使得工作计算可能在 K8s 上更高效正当的利用对应资源防止不必要的节约。 03 Fluid+JuiceFSRuntime:为云上量化钻研根底平台提供高效撑持出于 POSIX 兼容,老本,高吞吐的思考,咱们抉择了 JuiceFS 云服务作为分布式底层存储。抉择了 JuiceFS,发现现有 Kubernetes 的 CSI 体系并不能很好地反对咱们对数据拜访性能、弹性吞吐能力以及数据共享隔离的需要,具体来看: 1.传统的 Persistent Volume Claim 是面向通用存储的形象,不足对同一个存储简单数据拜访模式协同良好的反对:在不同的利用场景下,利用对同一存储中不同文件的应用形式不同,比方咱们少数并发计算工作要求只读;然而也有 Pipeline 数据直达,数据特色生成之后,须要直达到模型训练中,此时就要求读写;这导致了很难在同一个 PVC 中对立设置元数据更新和缓存策略。实际上,这些策略应该齐全取决于利用应用数据的模式。 2.数据隔离与共享:不同数据科学家团队拜访不同的数据集须要人造隔离,并且要求比拟容易治理;同时反对公共数据集访问共享,特地是缓存数据共享,因为行情数据这类绝对公开的数据,不同的研究员团队会重复应用,心愿取得“一次预热、全公司收益”的成果。 3.数据缓存感知的 Kubernetes 调度:雷同模型、雷同输出、不同的超参的作业以及微调模型、雷同输出的作业都会一直反复拜访同一数据,产生能够复用的数据缓存。然而原生的 Kubernetes 调度器无奈感知缓存,导致利用调度的后果不佳、缓存无奈重用,性能得不到晋升。 ...

March 6, 2023 · 3 min · jiezi

干货-京东云弹性伸缩功能实践

弹性伸缩AS(Auto Scaling)是一项 Web服务,可以根据您的业务需求和策略,自动调整云主机计算资源,可帮助确保您拥有适量的云主机实例来处理您的应用程序负载。使用 AS 进行容量调整,您只需事先设置好扩容条件及缩容条件,AS 会在达到条件时自动增加/减少使用的服务器数量以维护性能: 在业务需求增长时无缝地增加主机实例。在业务需求下降时自动减少主机实例以节约成本 产品简介弹性伸缩是一项按需自动调整云主机规模的服务,能够及时有效地应对客户业务出现剧烈波动的情形。根据客户不同业务特点,采用不同的伸缩模式,在降低客户业务成本的同时提高业务能力,充分体现了云计算平台弹性灵活的特点。 名词术语伸缩组:伸缩组是遵循相同规则、面向同一场景的云主机实例的集合。伸缩组定义了组内云主机实例数的最大值、最小值及其相关联的负载均衡实例等属性。 启动配置:启动配置是自动创建云服务器的模版,其中包括镜像、云服务器实例规格、系统盘及数据盘类型和容量、密钥等。创建伸缩组时必须指定启动配置,启动配置一经创建后其属性将不能编辑。 伸缩策略:即执行伸缩动作的条件。触发条件可以是云监控的报警或时间,动作可以是移出或加入云主机。 伸缩策略有以下两种: 定时伸缩策略 到达某个固定时间点,自动增加或减少云主机实例,支持周期性重复。告警伸缩 基于云监控指标(如CPU、内存、网络流量等),自动增加或减少云主机实例。伸缩活动:伸缩策略成功触发后,就会产生一条伸缩活动。伸缩活动主要用来描述伸缩组内云主机实例的变化情况。 冷却时间:冷却时间是指在同一个伸缩组内,一个伸缩活动执行完成后的一段锁定时间。冷却时间可指定范围为 0-86400(秒)。 使用场景Web应用服务:web服务业务逻辑层扩缩容。比如电商网站、视频网站、在线教育等,客户端的请求通过负载均衡到达应用服务器,当业务访问量大幅快速波动时,弹性伸缩服务可以根据请求量及负载弹性扩缩应用服务器的数量。 高性能计算:服务的计算节点扩缩容。分布式大数据的计算节点、数据处理等后端计算集群,根据计算量大小实时调整集群服务器数量,或者根据集群预定好的脚本执行时间来设定周期性定时任务,在脚本执行之前自动创建一批主机,保证高效的运算业务。 数据收集检索:时效性业务扩缩容。用于发送请求、数据收集、检索等业务服务器集群的部署,可通过弹性伸缩服务快速完成扩缩任务供业务使用。 操作实践1、操作流程: 测试AZ:华北-北京-可用区C 模拟场景:2台Apache服务器组成Web服务集群,前端通过LB做流量的分发;配置弹性伸缩策略,实现某台服务器CPU使用率>70%时自动扩容1台服务器,平均CPU使用率<40%时自动减少1台服务器。 2、测试步骤: 第一步:登录控制台使用已注册的京东云账号登录控制台 第二步:创建启动配置在控制台左侧功能导航栏选择“弹性计算”->“弹性伸缩”->“启动配置”->“创建” 设置配置名称,在“镜像”中选择“私有镜像”(“私有镜像”是通过创建好的云主机并部署完应用及完成配置制作的云主机镜像);配置的规格可以根据实际情况调整(CPU,内存,存储,带宽);创建完成后点击保存。 第三步:创建配置伸缩组初始实例数设定后系统会自动根据设定的数量从启动配置自动创建相应数量的云主机可支持跨可用区创建,达到容灾的效果;负载均衡设置绑定已创建的LB实例并选择对应的虚拟服务器做及配置好监听端口。 第四步:绑定云主机 按需添加云主机,数量必须在伸缩组设定的最小和最大实例数之间 第五步:添加告警策略创建“弹性伸”策略和“弹性缩”策略 支持定时任务和重复任务,增加和减少的设置需要配对使用。 第六步:添加告警联系人 “账号管理”->“联系人管理”中的联系人和联系组信息会自动被识别,添加相关组和联系人后,伸缩组发生启动,终止,无法启动,无法终止时会有邮件和短信及时进行通知 3、验证试验结果: 用原生centos自带工具,方法是通过压缩随机数据并将结果发送到 /dev/null在第一台Apache服务器运行如下命令: cat /dev/urandom | gzip -9 > /dev/nullCPU使用率到达99%。 成功弹出一台云主机;使用Ctrl+C结束加压命令后,CPU使用率降低;观察伸缩活动的日志,发现已自动移出新增的云主机,成功移除。 点击→"京东云"了解更多详情

June 25, 2019 · 1 min · jiezi

AutoScaling-成本优化模式升级混合实例策略

伸缩组成本优化模式以成本为目标,始终创建最低价的实例,同时,通过多可用区,多实例规格分布,以此来提高服务稳定性。但是,对于成本优势最大化的竞价实例,伸缩组难以防范竞价实例大范围回收可能导致的服务雪崩,本次升级允许用户制定更详细的成本控制策略,在成本和稳定性之间进行调整和权衡。 成本优化模式简介当您的伸缩配置选择了多实例规格,并想以最低的价格来使用同等规模的 ECS 实例配置时,您可以选择使用 成本优化策略 的伸缩组,来降低您的 ECS 实例使用成本;当您的伸缩配置选择的实例为抢占式实例时,您可能会遇到由于价格、库存等原因导致抢占式实例创建失败场景,从而导致扩容不及时,影响到业务,您也可以选择使用 成本优化策略 的伸缩组,在抢占式实例创建失败的时候自动为您尝试创建同规格的按量实例,来保证业务的稳定性。 从上述的描述,我们可以清晰的看到,成本优化模式的核心策略: 创建实例时,以单核cpu价格价格最低来选择创建实例的 InstanceType(实例规格),ZoneId(可用区)等配置信息。竞价实例创建失败时,调整为创建按量实例,以保证业务连续性。我们将上述的策略称为最低价策略(LowestPrice)。 关于成本优化模式更详细的信息,请查看 AutoScaling 推出成本优化模式。 成本优化模式升级成本优化模式的升级策略主要针对竞价实例回收机制可能带来的业务雪崩情况。主要集中在以下两点: 混合实例配比。允许用户为成本优化伸缩组制定按量实例与竞价实例的混合策略。竞价实例主动替换。在竞价实例释放前创建新实例,主动替换掉当前的竞价实例。在下面的文章中,我们将原成本优化伸缩组称为普通成本优化伸缩组,将指定实例混合策略的成本优化伸缩组称为成本优化混合实例伸缩组。 参数详解OnDemandBaseCapacity伸缩组所需要的按量实例的最小个数,当伸缩组中按量实例个数小于该值时,将优先创建按量实例。 OnDemandPercentageAboveBaseCapacity满足 OnDemandBaseCapacity 条件后,创建实例中按量实例所占的比例。 SpotInstancePoolsSpotInstancePools 指定了最低价的多个实例规格,当创建竞价实例时,将在 SpotInstancePools 中进行均衡分布。 SpotInstanceRemedy是否开启竞价实例的补偿机制。开启后在竞价实例被回收前5分钟左右,将主动替换掉当前竞价实例。 兼容性介绍成本优化混合实例伸缩组与普通成本优化伸缩组在接口和功能方面是完全兼容的。当您不指定混合实例策略的相关参数时,您将创建出普通成本优化伸缩组。同时,对于成本优化混合实例伸缩组,通过合理的制定混合实例策略,能够具有与普通成本优化伸缩组完全相同的行为。下面举例说明: 假设普通成本优化伸缩组创建的全为按量实例。此时,你创建的成本优化混合实例伸缩组只需要指定OnDemandBaseCapacity=0, OnDemandPercentageAboveBaseCapacity=100,spotInstancePools=1,那么将拥有完全相同的行为。 假设普通成本优化伸缩组优先创建竞价实例。此时,你创建的成本优化混合实例伸缩组只需要指定OnDemandBaseCapacity=0, OnDemandPercentageAboveBaseCapacity=0,spotInstancePools=1,那么将拥有完全相同的行为。 扩缩容策略成本优化混合实例伸缩组拥有一套相对独立的扩缩容策略,您在大多数情况下不需要关注实例的选择过程,如果您需要对伸缩组行为具有更详细的了解,本节中对扩缩容过程进行了详细的描述。 扩容策略当指定了伸缩组的实例混合策略之后,伸缩组并非仅对新创建出来的实例按照混合比例进行创建,而是保证伸缩组整体的实例配比趋近目标配比。 按量实例扩容策略按量实例部分,采用了 LowestPrice 的创建方式,多实例规格与多可用区按照优先级方式依此进行选择,该部分与普通成本优化伸缩组保持一致。 竞价实例扩容策略竞价实例部分,采用了 LowestPrice 的创建方式,当配置多实例规格时,将根据 SpotInstancePools 配置,在最低价的多个实例规格之间平均分配,针对每一种实例规格,当无法成功创建时,按照价格顺序依次选取下一规格继续进行创建,当竞价实例全部不可创建,将退回到创建对应的按量实例。多可用区则按照优先级的方式依次进行选择。 下面,我们通过示例来描述成本优化混合实例伸缩组的扩容行为: 假设伸缩组组内按量实例个数为3,竞价实例为1个ecs.n1.tiny规格实例,OnDemandBaseCapacity = 5,OnDemandPercentageAboveBaseCapacity = 40,SpotInstancePools = 2,伸缩组实例规格配置为:ecs.n1.tiny, ecs.n1.small,ecs.n1.medium(价格依此上升)。 扩容数量按量实例分配情况竞价实例分配情况031(tiny)141(tiny)251(tiny)361(tiny)471(tiny)571(tiny)1(small)672(tiny)1(small)782(tiny)1(small)882(tiny)2(small)缩容策略成本优化混合实例伸缩组的释放策略不遵循伸缩组上指定的释放策略,为了保持实例伸缩组内实例的混合配比,将采用以下描述的实例释放策略。首先,将根据伸缩组实例混合策略,确定将要释放的按量实例与竞价实例的个数,我们将在保证足够数量的实例被释放的前提下,按照伸缩组整体趋近期望配比的方式确定释放按量实例和竞价实例的个数。当按量实例个数不足时,将释放更多的竞价实例;当竞价实例个数不足时,将改为释放按量实例。 按量实例缩容策略释放按量实例时,将按照以下条件选择可释放的实例: 优先释放价格高的实例;价格相同时,按照伸缩组指定的释放策略选取合适数量的实例进行释放。竞价实例缩容策略释放竞价实例时,将按照以下条件选择可释放的实例: 将首先释放不属于spotInstancePools中规格类型的实例,这部分实例的释放策略与上述按量实例的缩容策略相同;如果还需要释放规格类型属于spotInstancePools的实例,将进一步选择释放所需要的实例,选择方式如下: 选择释放的实例将使得剩余实例的实例规格在spotInstancePools中趋于均衡分布;相同规格的多个实例可供选择时,将按照伸缩组指定的释放策略选择释放的实例。下面,同样我们通过简单的示例来描述成本优化混合实例伸缩组在缩容时的实例选择过程: 假设伸缩组组内按量实例个数为8,竞价实例为2个ecs.n1.tiny规格实例,2个ecs.n1.small规格实例,OnDemandBaseCapacity = 5,OnDemandPercentageAboveBaseCapacity = 40,SpotInstancePools = 2,伸缩组实例规格配置为:ecs.n1.tiny, ecs.n1.small,ecs.n1.medium(价格依此上升)。 缩容数量按量实例分配情况竞价实例分配情况082(tiny)2(small)182(tiny)1(small)272(tiny)1(small)371(tiny)1(small)471(tiny)561(tiny)660750840竞价实例补偿竞价实例在系统回收之前五分钟左右将会发送系统回收消息,当您开启竞价实例主动替换功能之后,在系统发送竞价实例的回收消息之后,弹性伸缩将会为该竞价实例创建补偿任务,并在稍后通过创建新的竞价实例来替换即将释放的实例。我们将这一主动替换即将被回收的竞价实例的行为称为竞价实例补偿。 竞价实例补偿是保障业务连续性的辅助保障机制,该补偿机制具有以下特点,你需要对这些特点有充分的认识,以便您配置合理的成本优化伸缩组。 竞价实例补偿的时间窗口。在收到竞价实例系统回收消息后,将为对应的实例生成补偿任务,大约五分钟时间后实例将被回收,当实例被回收后,伸缩组内的对应实例将被健康检查机制清除伸缩组(大约6分钟)。竞价实例补偿任务的有效期为:补偿任务生成到健康检查将实例移除伸缩组之间。一旦错过补偿时间窗口,对应的补偿任务将会失效和清理,意味着对应实例错过补偿期。有限的补偿能力。一次竞价实例的补偿过程分为新实例启动和旧实例释放两个过程,补偿任务执行过程中,伸缩组将处于锁定状态。由于暂时伸缩组不支持并行伸缩活动处理,因此,在有限的补偿时间窗口内,能够进行的补偿任务次数和实例数是有限的。由于竞价实例回收通常是呈现批次状,因此,为了最大程度利用有限的补偿能力,我们将对补偿任务进行一定程度的聚合之后,按批次进行下发,最大程度的补偿更多的实例。最佳实践这里我们主要展示如何使用java SDK创建伸缩规则,并采用maven进行依赖管理。创建目标追踪伸缩规则,需要使用aliyun-java-sdk-ess 2.3.1及以上版本。 程序所需的maven依赖如下: <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>3.0.8</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-ess</artifactId> <version>2.3.1</version> </dependency>创建混合实例的成本优化伸缩组: ...

June 25, 2019 · 1 min · jiezi

Flex常见布局实例

如果对flex不是很熟悉的同学,可以看一下我的另一篇文章Flex 布局1、网格布局1.1、基本网格布局最简单的网格布局,就是平均分布。HTML代码如下。<div class=“Grid”> <div class=“Grid-cell”>1/2</div> <div class=“Grid-cell”>1/2</div> </div> <div class=“Grid”> <div class=“Grid-cell”>1/3</div> <div class=“Grid-cell”>1/3</div> <div class=“Grid-cell”>1/3</div> </div>CSS代码如下。.Grid { display: flex;}.Grid-cell { flex: 1; background: #eee; margin: 10px;}这里最关键的就是flex:1使得各个子元素可以等比伸缩1.2、百分比布局某个网格的宽度为固定的百分比,其余网格平均分配剩余的空间。HTML代码如下。<div class=“Grid”> <div class=“Grid-cell col2”>50%</div> <div class=“Grid-cell”>auto</div> <div class=“Grid-cell “>auto</div></div><div class=“Grid”> <div class=“Grid-cell”>auto</div> <div class=“Grid-cell col2”>50%</div> <div class=“Grid-cell clo3”>1/3</div></div>CSS代码如下。.col2 { flex: 0 0 50%;}.col3 { flex: 0 0 33.3%;}这里最关键的是通过flex的第三个属性,也就是flex-basis来定义元素占据的空间。2、圣杯布局圣杯布局(Holy Grail Layout:)指的是一种最常见的网站布局。页面从上到下,分成三个部分:头部(header),躯干(body),尾部(footer)。其中躯干又水平分成三栏,从左到右为:导航、主栏、副栏。HTML代码如下。<div class=“container”> <header class=“bg”>header</header> <div class=“body”> <main class=“content bg”>content</main> <nav class=“nav bg”>nav</nav> <aside class=“ads bg”>aside</aside> </div> <footer class=“bg”>footer</footer> </div>CSS代码如下。.container { display: flex; flex-direction: column; min-height: 100vh;}.body { display: flex; flex: 1;}header,footer { flex: 0 0 100px;}.content { flex: 1;}.ads,.nav { flex: 0 0 100px;}.nav { order: -1;}.bg { background: #eee; margin: 10px;}@media (max-width: 768px) { .body { flex-direction: column; flex: 1; } .nav, .ads, .content { flex: auto; }}这里面需要注意的点有container的flex-direction: column这样保证了header,body,footer是在垂直轴上排列header,footer的高度可以通过flex: 0 0 100px来控制nav可以通过order:-1来调整位置通过@media (max-width: 768px)来调整小屏幕的页面结构3、侧边固定宽度侧边固定宽度,右边自适应html代码如下。<div class=“container1”> <div class=“aside1 bg”>aside</div> <div class=“body1”> <div class=“header1 bg”>header</div> <div class=“content1 bg”>content</div> <div class=“footer1 bg”>footer</div> </div></div>CSS代码如下。.bg { background: #eee; margin: 10px;}.container1 { min-height: 100vh; display: flex;}.aside1 { /* flex: 0 0 200px; */ flex: 0 0 20%;}.body1 { display: flex; flex-direction: column; flex: 1;}.content1 { flex: 1;}.header1, .footer1 { flex: 0 0 10%;}这个和上面的基本差不多就不做解释了。4、流式布局每行的项目数固定,会自动分行。html代码如下<div class=“container2”> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div> <div class=“item”></div></div>css代码如下.container2 { width: 200px; height: 150px; display: flex; flex-flow: row wrap; align-content: flex-start;}.item { box-sizing: border-box; background: #eee; flex: 0 0 20%; height: 40px; margin: 5px;}这里主要使用到了flex-flow: row wrap使得子元素水平排列,并且换行总结这是我简单总结的一些布局。如有错误,欢迎指正。更多系列文章 ...

March 26, 2019 · 2 min · jiezi

Flex 布局

在没有接触Flex之前一直使用float、display、position 。说实话用起来非常恶心。当使用Flex时,我们可以简洁优雅实现复杂的页面布局1、Flex 布局?在 flex 容器中默认存在两条轴,水平主轴(main axis) 和垂直的交叉轴(cross axis),这是默认的设置,当然你可以通过修改使垂直方向变为主轴,水平方向变为交叉轴。首先,实现 flex 布局需要先指定一个容器,任何一个容器都可以被指定为 flex 布局,这样容器内部的元素就可以使用 flex 来进行布局。.container { display: flex | inline-flex; //可以有两种取值}分别生成一个块状或行内的 flex 容器盒子。简单说来,如果你使用块元素如 div,你就可以使用 flex,而如果你使用行内元素,你可以使用 inline-flex。注意:当时设置 flex 布局之后,子元素的 float、clear、vertical-align 的属性将会失效。1.1、容器的属性容器有以下6个属性flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-content1.1.1 、flex-direction属性flex-direction属性决定主轴的方向(即项目的排列方向)。.box { flex-direction: row | row-reverse | column | column-reverse;}默认值:row,主轴为水平方向,起点在左端。row-reverse:主轴为水平方向,起点在右端。column:主轴为垂直方向,起点在上沿;column-reverse:主轴为垂直方向,起点在下沿;1.1.2、flex-wrap: 决定容器内项目是否可换行默认情况下,项目都排在主轴线上不换行,但使用 flex-wrap 可实现项目的换行。.container { flex-wrap: nowrap | wrap | wrap-reverse;}nowrap (默认值)不换行,即当主轴尺寸固定时,当空间不足时,项目尺寸会随之调整而并不会挤到下一行。wrap:项目主轴总尺寸超出容器时换行,第一行在上方wrap-reverse:换行,第一行在下方1.1.3、flex-flow: flex-direction 和 flex-wrap 的简写形式.container { flex-flow: <flex-direction> || <flex-wrap>;}默认值为: row nowrap;1.1.4、justify-content:定义了项目在主轴的对齐方式。.container { justify-content: flex-start | flex-end | center | space-between | space-around;}flex-start(默认值):左对齐flex-end:右对齐center:居中space-between:两端对齐,项目之间的间隔相等,即剩余空间等分成间隙。space-around:每个项目两侧的间隔相等,所以项目之间的间隔比项目与边缘的间隔大一倍。1.1.5、align-items: 定义了项目在交叉轴上的对齐方式.container { align-items: flex-start | flex-end | center | baseline | stretch;}建立在主轴为水平方向时测试,即 flex-direction: row默认值为 stretch 即如果项目未设置高度或者设为 auto,将占满整个容器的高度。假设容器高度设置为 100px,而项目都没有设置高度的情况下,则项目的高度也为 100px。flex-start:交叉轴的起点对齐,假设容器高度设置为 100px,而项目分别为 30px,60px, 100px, 则如上图显示。flex-end:交叉轴的终点对齐center:交叉轴的中点对齐baseline: 项目的第一行文字的基线对齐1.1.6、align-content: 定义了多根轴线的对齐方式,如果项目只有一根轴线,那么该属性将不起作用.container { align-content: flex-start | flex-end | center | space-between | space-around | stretch;}当我们 flex-wrap 设置为 nowrap 的时候,容器仅存在一根轴线,因为项目不会换行,就不会产生多条轴线。当我们 flex-wrap 设置为 wrap 的时候,容器可能会出现多条轴线,这时候就需要去设置多条轴线之间的对齐方式了。建立在主轴为水平方向时测试,即 flex-direction: row,flex-wrap: wrap;默认值为 stretch平分容器的垂直方向上的空间。如果没有设置高度将会撑开整个容器。flex-start:轴线全部在交叉轴上的起点对齐flex-end:轴线全部在交叉轴上的终点对齐center:轴线全部在交叉轴上的中间对齐space-between:轴线两端对齐,之间的间隔相等,即剩余空间等分成间隙。space-around:每个轴线两侧的间隔相等,所以轴线之间的间隔比轴线与边缘的间隔大一倍。到这里关于容器上的所有属性都讲完了,接下来就来讲讲关于在 flex item 上的属性。1.2、Flex项目的属性主要有以下6个属性设置在项目上。orderflex-growflex-shrinkflex-basisflexalign-self1.2.1、order属性order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。.item { order: <integer>;}1.2.2、 flex-grow: 定义项目的放大比例.item { flex-grow: <number>;}如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。1.2.3、flex-shrink属性:定义了项目的缩小比例.item { flex-shrink: <number>;}默认值: 1,即如果空间不足,该项目将缩小,负值对该属性无效。这里可以看出,虽然每个项目都设置了宽度为 50px,但是由于自身容器宽度只有 200px,这时候每个项目会被同比例进行缩小,因为默认值为 1。注意:如果所有项目的 flex-shrink 属性都为 1,当空间不足时,都将等比例缩小。 如果一个项目的 flex-shrink 属性为 0,其他项目都为 1,则空间不足时,前者不缩小。1.2.4、 flex-basis属性:定义了在分配多余空间之前,项目占据的主轴空间,浏览器根据这个属性,计算主轴是否有多余空间flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。.item { flex-basis: <length> | auto; /* default auto */}默认值:auto,即项目本来的大小, 这时候 item 的宽高取决于 width 或 height 的值。当主轴为水平方向的时候,当设置了 flex-basis,项目的宽度设置值会失效,flex-basis 需要跟 flex-grow 和 flex-shrink 配合使用才能发挥效果。当 flex-basis 值为 0 % 时,是把该项目视为零尺寸的,故即使声明该尺寸为 140px,也并没有什么用。当 flex-basis 值为 auto 时,则跟根据尺寸的设定值(假如为 100px),则这 100px 不会纳入剩余空间。1.2.5、flex属性flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。.item { flex: none | [ <‘flex-grow’> <‘flex-shrink’>? || <‘flex-basis’> ]}该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。关于 flex 取值,还有许多特殊的情况,可以按以下来进行划分:当 flex 取值为一个非负数字,则该数字为 flex-grow 值,flex-shrink 取 1,flex-basis 取 0%,如下是等同的:.item {flex: 1;}.item { flex-grow: 1; flex-shrink: 1; flex-basis: 0%;}当 flex 取值为一个长度或百分比,则视为 flex-basis 值,flex-grow 取 1,flex-shrink 取 1,有如下等同情况(注意 0% 是一个百分比而不是一个非负数字).item-1 {flex: 0%;}.item-1 { flex-grow: 1; flex-shrink: 1; flex-basis: 0%;}.item-2 {flex: 24px;}.item-2 { flex-grow: 1; flex-shrink: 1; flex-basis: 24px;}当 flex 取值为两个非负数字,则分别视为 flex-grow 和 flex-shrink 的值,flex-basis 取 0%,如下是等同的:.item {flex: 2 3;}.item { flex-grow: 2; flex-shrink: 3; flex-basis: 0%;}1.2.6、align-self属性:允许单个项目有与其他项目不一样的对齐方式单个项目覆盖 父元素的align-items 定义的属性默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch。.item { align-self: auto | flex-start | flex-end | center | baseline | stretch;}这个跟 align-items 属性时一样的,只不过 align-self 是对单个项目生效的,而 align-items 则是对容器下的所有项目生效的。容器 align-items 设置为 flex-start,而第三个子元素的align-self: flex-end;更多系列文章请看 ...

March 26, 2019 · 2 min · jiezi

开发函数计算的正确姿势——运行 Selenium Java

前言首先介绍下在本文出现的几个比较重要的概念:函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考。Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考。备注: 本文介绍的技巧需要 Fun 版本大于等于 2.10.2。依赖工具本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。DockerFunFcliFun 和 Fcli 工具依赖于 docker 来模拟本地环境。对于 MacOS 用户可以使用 homebrew 进行安装:brew cask install dockerbrew tap vangie/formulabrew install funbrew install fcliWindows 和 Linux 用户安装请参考:https://github.com/aliyun/fun/blob/master/docs/usage/installation.mdhttps://github.com/aliyun/fcli/releases安装好后,记得先执行 fun config 初始化一下配置。注意, 如果你已经安装过了 fun,确保 fun 的版本在 2.10.2 以上。$ fun –version2.10.1快速开始初始化使用 fun init 命令可以快捷地将本模板项目初始化到本地。fun init vangie/selenium-java-example安装依赖$ fun install…本地测试测试代码 ChromeDemo 的内容为:public class ChromeDemo implements StreamRequestHandler { public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { System.setProperty(“webdriver.chrome.driver”, “/code/chromedriver”); ChromeOptions options = new ChromeOptions(); options.setBinary("/code/headless-chromium"); options.addArguments("–disable-extensions"); // disabling extensions options.addArguments("–disable-gpu"); // applicable to windows os only options.addArguments("–disable-dev-shm-usage"); // overcome limited resource problems options.addArguments("–no-sandbox"); // Bypass OS security model options.addArguments("–headless"); WebDriver driver = new ChromeDriver(options); driver.get(“https://ide.fc.aliyun.com”); outputStream.write((“Page title is: " + driver.getTitle() + “\n”).getBytes()); driver.quit(); }}本地运行$ mvn package && fun local invoke selenium…FC Invoke Start RequestId: 68c83b4c-b053-479c-9b0e-9503582ccb56handle user request is com.aliyun.fc.selenium.ChromeDemo::handleRequestcache is null!Starting ChromeDriver 2.35.528139 (47ead77cb35ad2a9a83248b292151462a66cd881) on port 20652Only local connections are allowed.Mar 05, 2019 11:34:27 AM org.openqa.selenium.remote.ProtocolHandshake createSessionINFO: Detected dialect: OSSPage title is: 云端集成开发环境FC Invoke End RequestId: 68c83b4c-b053-479c-9b0e-9503582ccb56RequestId: 68c83b4c-b053-479c-9b0e-9503582ccb56 Billed Duration: 5265 ms Memory Size: 1998 MB Max Memory Used: 240 MB部署$ mvn package && fun deploy执行$ fcli function invoke -s chrome -f selenium Page title is: 云端集成开发环境关于文件尺寸由于 chromedriver 和 headless-chromium 压缩后体积已经非常接近 50MB,留给用户 Jar 的空间非常少,所以另外制作了一个高压缩比版本,使用压缩比更高的 brotli 算法进行压缩,压缩后的大小为 32.7MB。然后在运行时使用 initializer 进行解压,解压耗时大约为 3.7 S。https://github.com/vangie/packed-selenium-java-example参考阅读https://github.com/smithclay/lambdiumhttps://medium.com/clog/running-selenium-and-headless-chrome-on-aws-lambda-fb350458e4df本文作者:倚贤阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 7, 2019 · 1 min · jiezi

分布式系统「伸缩性」大招之——「弹性架构」详解

如果第二次看到我的文章,欢迎下方扫码订阅我的个人公众号(跨界架构师)哟本文长度为3633字,建议阅读10分钟。坚持原创,每一篇都是用心之作~如果我们的开发工作真的就如搭积木一般就好了,轮廓分明,个个分开,坏了哪块积木换掉哪块就好了。但是,实际我们的工作中所面临的可能只有一块积木,而且还是一大块,要换得一起换,要修得一起修。Z哥在之前《分布式系统关注点(13)——「高内聚低耦合」详解》中提到的分层架构它可以让我们有意识的去做一些切分,但是换和修的难度还是根据切分的粒度大小来决定的。有更好的方式吗?这是显然的。事件驱动架构我们来换一个思维看待这个问题。不管是平时的系统升级也好、修复bug也好、扩容也好,其实就是一场“手术”。通过这场“手术”来解决当前面临的一些问题。那么分层架构好比只是将一个人的手、脚、嘴、鼻等分的清清楚楚,但是整体还是紧密的耦合在一起。怎么耦合的呢?我们人是靠“血液”的流动连接起来的。这就好比在分布式系统中通过rpc框架连接起不同的节点一样。但是软件与人不同,有2种不同的连接方式,除了「同步」的方式之外还有「异步」的方式。因为有些时候你不需要知道其他系统的执行结果,只要确保自己将其需要的数据传递给它了即可。恰巧有一种架构是这种模式的典型——事件驱动架构(简称EDA,Event Driven Architecture)。平时常见的MQ、本地消息表等运用于数据传递的中转环节,就是事件驱动架构的思想体现。事件驱动架构又细分为两种典型的实现方式,与Z哥之前在《分布式系统关注点(3)——「共识」的兄弟「事务」》中提到的Saga模式的2种实现方式类似,一种是中心化的、一种是去中心化的。下面Z哥来举个例子,让你看起来更容易理解一些。(例子仅为了阐述是怎么工作的,真正的实施中还需要考虑如何保证数据一致性等问题,这部分可以参考之前发表的系列文章,文末带传送门)传统的电商场景中,用户从购物车中点击“提交”按钮后,至少需要做这几件事:生成一笔订单、生成一笔支付记录、给订单匹配发货的快递公司。在这个场景下,中心化和去中心化有什么不同呢?中心化这种模式拥有一个“上帝”。但是“上帝”不会处理也不知道任何业务逻辑,它只编排事件。除了中心化之外,它还有什么特点呢?Z哥给它的定义是“3+2结构”。这种模式中存在3种类型的主体:事件生产者、“上帝”(调停者)、事件处理者。然后中间夹着两层队列,以此结构就能解耦。就像这样:事件生产者 –> 队列 –> “上帝”(调停者) –> 队列 –> 事件处理者。那么回上面的这个例子中,事件生产者CartService发出了一个“订单创建”事件,通过队列传递给调停者。然后调停者根据事先制定好的编排规则对事件进行相应的转换,也通过队列做二次分发,传递给事件处理者。可能你会问,这些好理解。但是,我之前也经常看到什么编排编排的,到底编排该怎么做呢?其实编排主要做两件事:「事件转换」和「事件发送」(对应「服务编排」类框架的「调用」)。「事件转换」实质就是给将要发送的事件对象的参数进行赋值。赋值的数据来源于哪呢?除了事件产生的源头带入的参数,还有持续累积的「上下文」,就如下图中这样的一个共享存储空间。可能你又会问,我怎么将多个事件处理者之间组合成一个上下文呢?通过一个全局唯一的标识即可,每次向“上帝”丢事件的时候把这个全局唯一标识带过去。题外话:一般来说,还会在一个全局唯一标识之下带一个内部唯一的「子流水号」,为了配合做接下去要讲到的「事件发送」。一是为了后续排查问题的时候清晰的知道这次调用产生的异常是从哪个上游系统来的。二是为了便于观测整个调用的逻辑是否符合编排时的预期。怎么做呢?通过一个x.x.x.x格式的序号。比如,串行就是1,2,3。分支和并行就是2.1,2.2。分支+串行的结合就是1,2,2.1,2.2,3。「事件发送」实质就是负责事件流转的逻辑控制,然后发往「事件处理者」去处理。它决定了是按顺序还是分支进行?是串行还是并行?到这就不再展开了,要不然就跑题了,我们下次再细聊这部分内容。再强调一下,「事件转换」和「事件发送」是你在实现“上帝”(调停者)功能的时候需要满足的最基本的两个功能哦。中心化最大的优势是让流程更加的“可见”了,同时也更容易去做一些监控类的东西,系统规模越大,这个优势产生的效果越明显。但是一个最基本的“上帝”(调停者)实现起来还需要考虑数据一致性问题,所以,会大大增加它的实现复杂度。因此,如果你面对的场景,业务没有特别庞大,并且是比较稳定的,或许用去中心化的方式也是不错的选择。去中心化这个模式由于没有了“上帝”,因此每个事件处理者需要知道自己的下一个事件处理器是什么?需要哪些参数?以及队列是哪个之类的东西。但是整体结构会变得简单很多,从“3+2结构”变成了“2+1结构”。结构简化背后的复杂度都跑到事件处理者开发人员编写的业务代码中去了。因为他需要自己去负责「事件转换」和「事件发送」这两个事情。嗯,改造成事件驱动架构之后,通过「队列」的解耦和异步的事件流转,系统的运转的确会更顺畅。但是有时候你可能想进行更细粒度的控制,因为一般情况下,一个service中会处理很多业务环节,不太会只存在一个对外接口、一条业务逻辑。在这样的情况下,很多时候你可能需要修改的地方仅仅是其中的一个接口。能不能只修改其中的一部分代码并且进行「热更新」呢?微内核架构(插件架构)就适合来解决这个问题。微内核架构顾名思义,微内核架构的关键是内核。所以需要先找到并明确内核是什么?然后将其它部分都视作“可拆卸”的部件。好比我们一个人,大脑就是内核,其它的什么都可以换,换完之后你还是你,但是大脑换了就不是你了。微内核架构整体上由两部分组成:核心系统和插件模块。核心系统内又包含了微内核、插件模块,以及内置的一些同样以插件形式提供的默认功能。其中,微内核主要负责插件的生命周期管理和控制插件模块。插件模块则负责插件的加载、替换和卸载。外部的插件如果要能够接入进来并顺利运行,前提先要有一个满足标准接口规范的实现。一个插件的标准接口至少会有这样的2个方法需要具体的插件来实现:public interface IPlugin{ /// <summary> /// 初始化配置 /// </summary> void InitializeConfig(Dictionary<string,string> configs); /// <summary> /// 运行 /// </summary> void Run(); …}最后,插件之间彼此独立,但核心系统知道哪里可以找到它们以及如何运行它们。 最佳实践知道了这两种具有“弹性”的架构模式,你该如何判断什么情况下需要搬出来用呢?Z哥带你来分析一下每一种架构的优缺点,就能发现它适用的场景。事件驱动架构它的优点是:通过「队列」进行解耦,使得面对快速变化的需求可以即时上线,而不影响上游系统。由于「事件」是一个独立存在的“标准化”沟通载体,可以利用这个特点衔接各种跨平台、多语言的程序。如果再进行额外的持久化,还能便于后续的问题排查。同时也可以对「事件」进行反复的「重放」,对处理者的吞吐量进行更真实的压力测试。更“动态”、容错性好。可以很容易,低成本地集成、再集成、再配置新的和已经存在的事件处理者,也可以很容易的移除事件处理者。轻松的做扩容和缩容。在“上帝”模式下,对业务能有一个“可见”的掌控,更容易发现流程不合理或者被忽略的问题。同时能标准化一些技术细节,如「数据一致性」的实现方式等。它的缺点是:面对不稳定的网络问题、各种异常,想要处理好这些以确保一致性,需要比同步调用花费很大的精力和成本。无法像同步调用一般,操作成功后即代表可以看到最新的数据,需要容忍延迟或者对延迟做一些用户体验上的额外处理。那么,它所适用的场景就是:对实时性要求不高的场景。系统中存在大量的跨平台、多语言的异构环境。以尽可能提高程序复用度为目的的场景。业务灵活多变的场景。需要经常扩容缩容的场景。微内核架构它的优点是:为递进设计和增量开发提供了方便。可以先实现一个稳固的核心系统,然后逐渐地增加功能和特性。和事件驱动架构一样,也可避免单一组件失效,而造成整个系统崩溃,容错性好。内核只需要重新启动这个组件,不致于影响其他功能。它的缺点是:由于主要的微内核很小,所以无法对整体进行优化。每个插件都各自管各自的,甚至可能是由不同团队负责维护。一般来说,为了避免在单个应用程序中的复杂度爆炸,很少会启用插件嵌套插件的模式,所以插件中的代码复用度会差一些。那么,它所适用的场景就是:可以嵌入或者作为其它架构模式的一部分。例如事件驱动架构中,“上帝”的「事件转换」就可以使用微内核架构实现。业务逻辑虽然不同,但是运行逻辑相同的场景。比如,定期任务和作业调度类应用。具有清晰的增量开发预期的场景。总结好了,我们总结一下。这次呢,Z哥向你介绍了「事件驱动架构」的两种实现模式和实现思路,以及「微内核架构」的实现思路。并且奉上了对这两种架构模式的优缺点与适用场景分析的最佳实践。希望对你有所启发。相关文章:分布式系统关注点(1)——初识数据一致性分布式系统关注点(2)——通过“共识”达成数据一致性分布式系统关注点(3)——「共识」的兄弟「事务」作者:Zachary出处:https://www.cnblogs.com/Zacha…如果你喜欢这篇文章,可以点一下文末的「赞」。这样可以给我一点反馈。: )谢谢你的举手之劳。▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描下方的二维码。定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。

February 18, 2019 · 1 min · jiezi

开发函数计算的正确姿势——网页截图服务

前言首先介绍下在本文出现的几个比较重要的概念:函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考。Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考。fun install: fun install 是 fun 工具的一个子命令,用于安装 pip 和 apt 依赖,提供了命令行接口和 fun.yml 描述文件两种形式。备注: 本文介绍的技巧需要 Fun 版本大于等于 2.9.3。依赖工具本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。DockerFunFcliFun 和 Fcli 工具依赖于 docker 来模拟本地环境。对于 MacOS 用户可以使用 homebrew 进行安装:brew cask install dockerbrew tap vangie/formulabrew install funbrew install fcliWindows 和 Linux 用户安装请参考:https://github.com/aliyun/fun/blob/master/docs/usage/installation.mdhttps://github.com/aliyun/fcli/releases安装好后,记得先执行 fun config 初始化一下配置。初始化使用 fun init 命令可以快捷的将本模板项目初始化到本地。$ fun init vangie/puppeteer-example? Please input oss bucket to upload chrome shell? chrome-headless? Please select a region? cn-hangzhou? Please input oss accessKeyId for upload? xxxxxxxxxxxKbBS? Please input oss accessKeySecret for upload? xxxxxxxxxxxx5ZgM上面会提示输入一个 OSS 的 BUCKET,注意 OSS Bucket 是全球唯一的,上面的 chrome-headless 已经被占用了,请换一个新的名称或者一个已经创建好的(已经创建好的,请确保 region 一致)。然后选择一个 OSS 的 Region,请保持和部署函数计算 Region 一致输入一个具备 OSS 写权限的秘钥。安装依赖$ fun installskip pulling image aliyunfc/runtime-nodejs8:build-1.2.0…Task => [UNNAMED] => apt-get update (if need) => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libnss3 => bash -c ‘for f in $(ls /code/.fun/tmp/archives/*.deb); do dpkg -x $f /code/.fun/root; done;’ => bash -c ‘rm -rf /code/.fun/tmp/archives’Task => [UNNAMED] => bash -c ‘curl -L https://github.com/muxiangqiu/puppeteer-fc-starter-kit/raw/master/chrome/headless_shell.tar.gz –output headless_shell.tar.gz’…fun install 会执行 fun.yml 文件里的任务,这些任务会:安装 puppeteer 依赖的 .so 文件;将 puppeteer 依赖的 chrome headless 二进制文件上传到 OSS;安装 npm 依赖。部署$ fun deployusing region: cn-shanghaiusing accountId: ***********4733using accessKeyId: ***********KbBSusing timeout: 60Waiting for service puppeteer to be deployed… Waiting for function html2png to be deployed… Waiting for packaging function html2png code… package function html2png code done function html2png deploy successservice puppeteer deploy success执行$ fcli function invoke -s puppeteer -f html2pngThe screenshot has been uploaded to http://chrome-headless.oss-cn-shanghai.aliyuncs.com/screenshot.png打开上面的返回链接,看到截取出来的是全屏滚动的长图,考虑的篇幅下面只截取了部分:如果想换一个网址,可以使用如下命令格式fcli function invoke -s puppeteer -f html2png –event-str ‘http://www.alibaba.com’调试如果需要在本地调试代码,可以使用如下命令fun local invoke html2png <<<‘http://www.alibaba.com’参考阅读三分钟学会如何在函数计算中使用 puppeteer本文作者:倚贤阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 29, 2019 · 2 min · jiezi

一位技术校招生在支付宝的成长笔记

哪有那么多的“逆袭”,唯有努力与坚持,机会就会在前方。鲁直,1989年生,本科毕业于浙江工业大学,之后被校招进阿里巴巴。虽然,今年刚刚30岁,但他已是蚂蚁金服SOFA中间件开源负责人。看到这个开头,是不是觉得我们要向大家讲述一个普通程序员励志“逆袭”的故事?不,并不是这样。前4年,他的人生剧本和别人并没有什么不同但机会总是留给有准备的人“当时就是不想考研究生,而刚好阿里给的offer又能让我在杭州‘活’下去。”鲁直推了推眼镜,淡淡地说。2009年,鲁直报名参加了阿里和浙江工业大学校企合作的实习项目,经过1年的实习期,他在毕业季里成功拿下了B2B团队的offer。最初的时候,鲁直对于业务架构根本谈不上了解,只是每天重复着很普通,甚至是略微枯燥的代码工作。在当时,鲁直的工作就是做产品的研发以及业务系统的开发。每天想着的是建模和现在的业务模型是否匹配,IE6下能不能兼容……和大多数踏出校门、初入职场的大学生没什么不同。鲁直的生活和职业几乎谈不上计划,更谈不上梦想。但是,努力和幸运,让鲁直的人生轨迹逐渐发生变化。“当时的主管对我们说,因为我们刚进公司不久,在技术方面还需要更多的提升。”于是,鲁直就一头扎进开源代码的研究与分析中。在那时,业界的开源意识并不像现在这般普及,但鲁直他们组织的“半民间”开源兴趣小组却坚持了近2年的时间,一帮技术新人相互陪伴着学习开源,看代码,互相指出不足。不断的学习让鲁直对于中间件的兴趣日渐浓厚,他很想在这一领域进行尝试。终于,机会来了!有个同事提议推出一个研发效率提升工具,并被当时的技术主管知道了,他给了鲁直和这个同事一个月的时间把这个工具做出来,而且先不用管业务的事儿。于是,两个人用了一个月的时间,最终拿到结果:一款研发效率提升工具。从看书自学,到组成小团队一起研究代码,再到这次的实操,鲁直在B2B团队3年时间,想清楚了自己究竟要什么。“当时,就认定了自己想要去做中间件”,鲁直说,“而且阿里也有完备的人员流动机制。”于是,鲁直作出了一个重要的决定——从B2B团队转岗到蚂蚁金服中间件SOFA团队。那个属于鲁直的机会终于来了。在SOFA中间件团队5年的挑战与成长学习使人进步如愿以偿,鲁直进入了蚂蚁金服中间件SOFA团队,但这并不意味着是一片坦途。“更忙了,也更充实了;更有趣了,但挑战也更大了。”鲁直略带兴奋地告诉笔者,转岗后,他感受最明显的是角色发生了变化。“之前在业务团队的时候,我只需要具备业务视角即可。但是中间件不一样,需要充分考虑用户的感受。中间件的用户都是研发人员,我需要考量他们的使用场景和习惯等,甚至是在单词拼写以及命名规范等细节。”鲁直说,他必须要较真,因为程序员很多时间都花在变量命名上。随着对中间件的深入,鲁直发现,自己进入了“Hard”模式,之前那些认知看上去都不太管用,甚至有一些可笑。正是基于这种警醒,鲁直知道,不断学习才是自己唯一可选的路。于是,鲁直一头扎进书海,到处找中间件相关的书籍,从最底层的基础理论学起;然后将这些理论知识应用于实际的工作中。为此,鲁直主动要求做很多的支持工作。一段时间之后,鲁直很快就了解了所负责的中间件产品的细节,并快速地积累了解决问题的经验。“这段经历还是蛮有意思的。如果当时只是一味地读死书,而没有将其用于工作中,我想我可能没有那么大的提升。”鲁直感慨到。如果说学习让鲁直感到了愉悦,那么在中间件团队工作期间,收获的“痛苦”又是什么?——“你不知道,项目进度带来的压力真心大。”鲁直说。2016年双11,鲁直所在的SOFA团队负责弹性架构的改造,但其中一个非常老的协议却成了弹性架构下的“bug”。“都知道双11那种紧张的气氛,跟打仗没什么区别。”鲁直说,“架构改造的工作当时因为这个‘bug’而停滞了,整个团队不仅周末连续加班,身体疲惫不已;心理的压力更大。”然而,除了迎难而上,别无它途。鲁直和小伙伴们一起不断对协议进行深入的分析,不断地定制针对性的修复方案,终于让业务顺利升级中间件,平稳地支持了双11。“当时真的是身心俱疲,可以说是非常痛苦了。但最终,我们还是完成了任务。”鲁直说着,镜片后闪过一丝坚定的眼神。在鲁直看来,在越困难的时期,越需要逼自己一把,所谓破釜沉舟,大概如此吧。“SOFA这个名字的来历还挺有意思的,是我们的CTO鲁肃取的名字,里面包含两层意思,一是按照当时的技术趋势,要做面向服务的架构,即Service Oriented Architecture,但加入了金融业务,所以是ServiceOriented Fabric Architecture;二是希望能够像‘沙发’一样,让工程师可以非常爽地工作。”2018年4月,可以让工程师们非常爽的SOFA正式开源了!“现在,SOFA在蚂蚁金服有将近2000个应用,是在蚂蚁业务场景下被不断验证和锤炼的一套框架。”鲁直表示,“把SOFA开源出去,让更多的人使用,对于SOFA未来的发展极具意义。”鲁直告诉笔者,开源的意义就是给技术的发展装上轮子。9年阿里轨迹,一个普通又特别的“码农”越自律越自由程序员的头发,一直是一个不太好玩的老梗。尽管鲁直的头发仍然浓密,但还是能看到在危险边缘疯狂试探的发际线。尽管团队的小伙伴称其为“鲁大师”,但鲁直一直强调自己不过是个平凡的“码农”。“如果哪天,我在阿里的成长完全停滞了,那也就是我离开的时候了。”鲁直悠悠地说到。从大学毕业就在这家公司,9年,是一份执着的坚持。他认为,自己之所以能在阿里巴巴有所成,是因为自己很幸运,在工作中找到了自己的热爱,于是,所有的辛苦都不再是前行的负重,而是助推力。对于中间件的喜欢,以及阿里巴巴和蚂蚁金服提供的阔大舞台,让自己不断面对挑战,不断去学习,不断地成长。鲁直喜欢跑步,即便是天气再冷也会跑个5公里;而且他也喜欢马拉松,陆续坚持了8年之久。在作息时间方面,鲁直也有着“严格”的标准,他要求自己尽量在12点之前睡觉。“熬夜对身体真的不好,而且我跑步也是为了锻炼身体,但这些都其实是我对自己的一些要求。”鲁直说。不管作息规律也好,跑步也好,都可以视作是鲁直对自己的严格自律。鲁直在用自己的行动诠释“越自律越自由”。那些对开源有兴趣的小伙伴们,鲁直给出了自己的建议。“参与开源,一个错别字也是开始。根据对项目了解的深入程度,可以从找错别字、命名规范等找错开始,由浅入深,再去提出Issue、提交Bug。相信所有的开源项目维护者都会非常地欢迎大家一起参与、多提一些意见。”最后,鲁直引用他最喜欢的程序员Jamie Zanwinski的一句话与大家共勉:痛苦造就性格。在舒适的状态下,很多的人表现是差不多的,但是在逆境中,一些人内心非常深处的想法和力量才能被充分发挥出来。SOFA是什么?SOFA(Scalable OpenFinancial Architecture),蚂蚁金服自主研发的金融级分布式中间件,包含了构建金融级云原生架构所需的各个组件,包括微服务研发框架,RPC 框架,服务注册中心,分布式定时任务,限流/熔断框架,分布式链路追踪,分布式高可用消息队列,分布式事务框架等组件。简单来说,SOFA就是包含一整套组件的金融级分布式中间件。诞生于支付宝第2代技术系统的服务化,最开始只有一套框架,后来逐渐形成了一整套完整组件。SOFA和传统金融架构的区别1、传统的金融IT架构一般采取集中式,通过购入大型机小型机解决数据问题,拓展性弱且机器成本高昂。2、SOFA则采取分布式的架构,在高并发交易处理能力、强一致性、秒级容灾和弹性伸缩上都有突出的表现。譬如面对双11流量洪峰时,完全可以准备PC级的服务器去支撑,弹性伸缩。本文作者:越自律越自由阅读原文本文来自云栖社区合作伙伴“阿里技术”,如需转载请联系原作者。

January 25, 2019 · 1 min · jiezi

阿里云护航罗振宇2018“时间的朋友”跨年演讲,与千万观众一起跨年

摘要: “时间的朋友”跨年演讲背后的技术支撑。2018年12月31日20:30分,以“时间的朋友”为主题的罗振宇2018跨年演讲在深圳正式召开,同时通过深圳卫视、优酷等平台进行全球直播。作为年度总结式演讲开创者,2018年跨年演讲与以往跨年演讲一样,依旧保持着超高的人气,据现场统计演讲现场共吸引了7884名观众出席。罗辑思维自有平台“得到APP”,承载了演讲期间来自现场、网络及电视端用户持续不断的大流量、高并发访问,保障用户获得了稳定、流畅的使用体验。在这场知识盛宴的背后,罗辑思维技术团队与阿里云携手作战四余月,期间共同探讨、落实了多个提升和优化平台处理能力的技术方案,并通过多轮现网平台压测积累了多个应急处理措施。历届“时间的朋友”跨年演讲都吸引了大量现场和线上观众观看及互动,是典型的大流量、高并发、极端峰值的业务应用场景。随着演讲过程中罗振宇不断抛出新观点、新认知和新活动,海量用户也随之产生庞大的访问请求,这需要得到APP和生活作风电商平台能够稳定、流畅的将请求正确处理完成,使用户获得最好的使用体验。在项目筹备之初,双方技术团队就如何定制化的满足整场演讲高并发、大流量需求进行了深入探讨,阿里云技术团队将多年来支持阿里巴巴多年“双十一大促”活动所积累下来的产品技术及活动经验分享给罗辑思维技术团队。以优化系统处理能力提升为例,罗辑思维使用了阿里云全链路压测产品,在跨年演讲活动备战期间,双方团队协同使用了缓升、激增、脉冲等多种压测手段,模拟跨年演讲期间可能出现的各种对系统处理能力的需求。经过几十轮压测,多次模拟数百万用户集中访问平台等场景,实现了对平台各功能模块进行迭代式的摸底、扩容和验证的闭环过程,保障跨年演讲活动期间平台具备足够的计算、网络和存储等能力。阿里云技术服务团队全程参与到跨年演讲活动中,与罗辑思维技术团队一起进行现场护航工作,负责监控平台基础资源的运行指标、给出优化建议并在突发情况下给予全力支持。为了保证跨年演讲工作的技术投入具备最佳性价比,罗辑思维技术团队大量使用阿里云弹性计算、弹性带宽等产品技术。在跨年演讲活动准备期间,根据压测的结果罗辑思维技术团队扩缩容计算资源(ECS、RDS、Redis等)和带宽资源(SLB、EIP、NATGW等)。根据跨年活动的用户量预估和压测结果等数据,确定跨年演讲活动期间最具性价比的计算及带宽等云计算资源用量;同时,得到APP及生活作风平台经过17年和18年不断的技术架构进化,做到了弹性处理架构,使平台在活动期间可以做到秒级扩容云计算资源,进而秒级提升系统处理能力,最终保证了整场跨年演讲的顺利举行。同时在演讲中,罗振宇也提到了阿里的设计人工智能“鹿班”——让天下没有难撸的banner。刚刚过去的2018年双十一期间,阿里巴巴设计了5亿张广告图,那背后可能是人工设计师吗?当然不是,它是一个人工智能,叫做“鹿班”的系统。平均每秒可以设计8000张海报。使用了这个系统之后,首页Banner的点击率,提升了100%多,效果那是相当惊人。这只是阿里巴巴这样的公司,它庞大的智能后台系统的冰山一角。阿里云作为全球前三名、亚洲最大的云计算服务商,国内市场份额超第2-5名总和,在云计算、大数据及人工智能等领域拥有领先的技术优势,在全球19个地域开放了53个可用区,建设1500多个CDN节点,带宽储备120多T,为全球数十亿用户提供可靠的计算支持。2018年俄罗斯足球世界杯期间,为优酷、CNTV、CCTV5提供了技术及服务,承担了全网70%的世界杯流量。此外,阿里云还为全球客户部署超过200个飞天数据中心,通过底层统一的飞天操作系统,为客户提供全球独有的混合云体验。本文作者:阿里云头条阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 4, 2019 · 1 min · jiezi

开源 serverless 产品原理剖析 - Kubeless

背景Serverless 架构的出现让开发者不用过多地考虑传统的服务器采购、硬件运维、网络拓扑、资源扩容等问题,可以将更多的精力放在业务的拓展和创新上。随着 serverless 概念的深入人心,各大云计算厂商纷纷推出了各自的 serverless 产品,其中比较有代表性的有 AWS lambda、Azure Function、Google Cloud Functions、阿里云函数计算等。另外,CNCF 也于 2016 年创立了 Serverless Working Group,它致力于 cloud native 和 serverless 技术的结合。下图是 CNCF serverless 全景图,它将这些产品分成了工具型、安全型、框架型和平台型等类别。同时,容器以及容器编排工具的出现,大大降低了 serverless 产品的开发成本,促进了一大批优秀开源 serverless 产品的诞生,它们大多构建于 kubernetes 之上,如下图所示。Kubeless 简介本文将要介绍的 kubeless 便是这些开源 serverless 产品的典型代表。根据官方的定义,kubeless 是 kubernetes native 的无服务计算框架,它可以让用户在 kubernetes 之上使用 FaaS 构建高级应用程序。从 CNCF 视角,kubeless 属于平台型产品。Kubless 有三个核心概念:Functions - 代表需要被执行的用户代码,同时包含运行时依赖、构建指令等信息;Triggers - 代表和函数关联的事件源。如果把事件源比作生产者,函数比作执行者,那么触发器就是联系两者的桥梁;Runtime - 代表函数运行时所依赖的环境。原理剖析本章节将以 kubeless 为例介绍 serverless 产品需要具备的基本能力,以及 kubeless 是如何利用 K8s 现有功能来实现它们的。这些基本能力包括:敏捷构建 - 能够基于用户提交的源码迅速构建可执行的函数,简化部署流程;灵活触发 - 能够方便地基于各类事件触发函数的执行,并能方便快捷地集成新的事件源;自动伸缩 - 能够根据业务需求,自动完成扩容缩容,无须人工干预。本文所做的调研基于kubeless v1.0.0和k8s 1.13。敏捷构建CNCF 对函数生命周期的定义如下图所示。用户只需提供源码和函数说明,构建部署等工作通常由 serverless 平台完成。 因此,基于用户提交的源码迅速构建可执行函数是 serverless 产品必须具备的基础能力。在 kubeless 里,创建函数非常简单:kubeless function deploy hello –runtime python2.7 \ –from-file test.py \ –handler test.hello该命令各参数含义如下:hello:将要部署的函数名称;–runtime python2.7: 指定使用 python 2.7 作为运行环境。Kubeless 可供选择的运行环境请参考链接 runtimes。–from-file test.py:指定函数源码文件(支持 zip 格式)。–handler test.hello:指定使用 test.py 中的 hello 方法处理请求。函数资源与 K8s OperatorKubeless 函数是一个自定义 K8s 对象,本质上是 k8s operator。k8s operator 原理如下图所示:下面以 kubeless 函数为例,描述 K8s operator 的一般工作流程:使用 k8s 的 CustomResourceDefinition(CRD) 定义资源,这里创建了一个名为functions.kubeless.io的 CRD 来代表 kubeless 函数;创建一个 controller 监听自定义资源的 ADD、UPDATE、DELETE 事件并绑定 hander。这里创建了一个名为function-controller的 CRD controller,该 controller 会监听针对 function 的 ADD、UPDATE、DELETE 事件,并绑定 handler(参阅 AddEventHandler);用户执行创建、更新、删除自定义资源的命令;Controller 根据监听到的事件调用相应的 handler。除了函数外,下文将要介绍的 trigger 也是一个 k8s operator。函数构成Kubeless 的 function-controller监听到针对 function 的 ADD 事件后,会触发相应 handler 创建函数。一个函数由若干 K8s 对象组成,包括 ConfigMap、Service、Deployment、Pod 等,其结构如下图所示:ConfigMap函数中的 ConfigMap 用于描述函数源码和依赖。apiVersion: v1data: handler: test.hello # 函数依赖的第三方 python 库 requirements.txt: | kubernetes==2.0.0 # 函数源码 test.py: | def hello(event, context): print event return event[‘data’]kind: ConfigMapmetadata: labels: created-by: kubeless function: hello # 该 ConfigMap 名称 name: hello namespace: default…Service函数中的 Service 用于描述该函数的访问方式。该 Service 会与执行 function 逻辑的 Pods 相关联,类型是 ClusterIP。apiVersion: v1kind: Servicemetadata: labels: created-by: kubeless function: hello # 该 Service 名称 name: hello namespace: default …spec: clusterIP: 10.109.2.217 ports: - name: http-function-port port: 8080 protocol: TCP targetPort: 8080 selector: created-by: kubeless function: hello # Service 类型 type: ClusterIP…Deployment函数中的 Deployment 用于编排执行函数逻辑的 Pods,通过它可以描述函数期望的个数。apiVersion: extensions/v1beta1kind: Deploymentmetadata: labels: created-by: kubeless function: hello name: hello namespace: default …spec: # 指定函数期望的个数 replicas: 1…Pod函数中的 Pod 包含真正执行函数逻辑的容器。VolumesPod 中的 volumes 段指定了该函数的 ConfigMap。这会将 ConfigMap 中的源码和依赖添加到 volumeMounts.mountPath 指定的目录里面。从容器视角来看,文件路径为/src/test.py和 /src/requirements。… volumeMounts: - mountPath: /kubeless name: hello - mountPath: /src name: hello-depsvolumes:- emptyDir: {} name: hello- configMap: defaultMode: 420 name: hello…Init ContainerPod 中的 Init Container 主要作用如下:将源码和依赖文件拷贝到指定目录;安装第三方依赖。Func ContainerPod 中的 Func Container 会加载 Init Container 准备好的源码和依赖并执行函数。不同 runtime 加载代码的方式大同小异,可参考 kubeless.py,Handler.java。小结Kubeless 通过综合运用 K8s 中的多种组件以及利用各语言的动态加载能力实现了从用户源码到可执行的函数的构建逻辑;考虑了函数运行的安全性,通过 Security Context 机制限制容器中的进程以非 root 身份运行。灵活触发一款成熟的 serverless 产品需要具备灵活触发能力,以满足事件源的多样性需求,同时需要能够方便快捷地接入新事件源。CNCF 将函数的触发方式分成了如下图所示的几种类别,关于它们的详细介绍可参考链接 Function Invocation Types。对于 kubeless 的函数,最简单的触发方式是使用 kubeless CLI,另外还支持通过各种触发器。下表展示了 kubeless 函数目前支持的触发方式以及它们所属的类别。触发方式类别kubeless CLISynchronous Req/RepHttp TriggerSynchronous Req/RepCronjob TriggerJob (Master/Worker)Kafka TriggerAsync Message QueueNats TriggerAsync Message QueueKinesis TriggerMessage Stream下图展示了 kubeless 函数部分触发方式的原理:HTTP trigger如果希望通过发送 HTTP 请求触发函数执行,需要为函数创建 HTTP 触发器。 Kubeless 利用 K8s ingress 机制实现了 http trigger。Kubeless 创建了一个名为httptriggers.kubeless.io的 CRD 来代表 http trigger 对象。同时,kubeless 包含一个名为http-trigger-controller的 CRD controller,它会持续监听针对 http trigger 和 function 的 ADD、UPDATE、DELETE 事件,并执行对应的操作。以下命令将为函数 hello 创建一个名为http-hello的 http trigger,并指定选用 nginx 作为 gateway。kubeless trigger http create http-hello –function-name hello –gateway nginx –path echo –hostname example.com该命令会创建如下 ingress 对象,可以参考 CreateIngress 深入了解 ingress 的创建逻辑。apiVersion: extensions/v1beta1kind: Ingressmetadata: # 该 Ingress 的名字,即创建 http trigger 时指定的 name name: http-hello …spec: rules: - host: example.com http: paths: - backend: # 指向 kubeless 为函数 hello 创建的 ClusterIP 类型的 Service serviceName: hello servicePort: 8080 path: /echoIngress 只是用于描述路由规则,要让规则生效、实现请求转发,集群中需要有一个正在运行的 ingress controller。可供选择的 ingress controller 有 Contour、F5 BIG-IP Controller for Kubernetes、Kong Ingress Controllerfor Kubernetes、NGINX Ingress Controller for Kubernetes、Traefik 等。这种路由规则描述和路由功能实现相分离的思想很好地提现了 K8s 始终坚持的需求和供给分离的设计理念。上文中的命令在创建 trigger 时指定了 nginx 作为 gateway,因此需要部署一个 nginx-ingress-controller。该 controller 的基本工作原理如下:以 pod 的形式运行在独立的命名空间中;以 hostPort 的形式暴露出来供外界访问;内部运行着一个 nginx 实例;监听和 ingress、service 等资源相关的事件。如果发现这些事件最终会影响到路由规则,ingress controller 会采用向 Lua hander 发送新的 endpoints 列表或者直接修改 nginx.conf 并 reload nginx 等手段达到更新路由规则的目的。想要更深入地了解 nginx-ingress-controller 的工作原理可参考文章 how-it-works。完成上述工作后,我们便可以通过发送 HTTP 请求触发函数 hello 的执行:HTTP 请求首先会由 nginx-ingress-controller 中的 nginx 处理;Nginx 根据 nginx.conf 中的路由规则将请求转发给函数对应的 service;最后,请求会转发至挂载在 service 后的某个函数进行处理。样例如下:curl –data ‘{“Another”: “Echo”}’ \ –header “Host: example.com” \ –header “Content-Type:application/json” \ example.com/echo# 函数返回{“Another”: “Echo”}Cronjob trigger如果希望定期触发函数执行,需要为函数创建 cronjob 触发器。K8s 支持通过 CronJob 定期运行任务,kubeless 利用这个特性实现了 cronjob trigger。Kubeless 创建了一个名为cronjobtriggers.kubeless.io的 CRD 来代表 cronjob trigger 对象。同时,kubeless 包含一个名为cronjob-trigger-controller的 CRD controller,它会持续监听针对 cronjob trigger 和 function 的 ADD、UPDATE、DELETE 事件,并执行对应的操作。以下命令将为函数 hello 创建一个名为scheduled-invoke-hello的 cronjob trigger,该触发器每分钟会触发函数 hello 执行一次。kubeless trigger cronjob create scheduled-invoke-hello –function=hello –schedule="*/1 * * * *“该命令会创建如下 CronJob 对象,可以参考 EnsureCronJob 深入了解 CronJob 的创建逻辑。apiVersion: batch/v1beta1kind: CronJobmetadata: # 该 CronJob 的名字,即创建 cronjob trigger 时指定的 name name: scheduled-invoke-hello …spec: # 该 CronJob 的执行计划,即创建 cronjob trigger 时指定的 schedule schedule: */1 * * * * … jobTemplate: spec: activeDeadlineSeconds: 180 template: spec: containers: - args: - curl - -Lv # HTTP headers,包含 event-id、event-time、event-type、event-namespace 等信息 - ’ -H “event-id: xxx” -H “event-time: yyy” -H “event-type: application/json” -H “event-namespace: cronjobtrigger.kubeless.io”’ # kubeless 会为 function 创建一个 ClusterIP 类型的 Service # 可以根据 service 的 name、namespace 拼出 endpoint - http://hello.default.svc.cluster.local:8080 image: kubeless/unzip name: trigger restartPolicy: Never …自定义 trigger如果发现 kubeless 默认提供的触发器无法满足业务需求,可以自定义新的触发器。新触发器的构建流程如下:为新的事件源创建一个 CRD 来描述事件源触发器;在自定义资源对象的 spec 里描述该事件源的属性,例如 KafkaTriggerSpec、HTTPTriggerSpec;为该 CRD 创建一个 CRD controller。该 controller 需要持续监听针对事件源触发器和 function 的 CRUD 操作并作出正确的处理。例如,controller 监听到 function 的删除事件,需要把和该 function 关联的触发器一并删掉;当事件发生时,触发关联函数的执行。我们可以看到,自定义 trigger 的流程遵循了 K8s Operator 设计模式。小结Kubeless 提供了一些基本常用的触发器,如果有其他事件源也可以通过自定义触发器接入;不同事件源的接入方式不同,但最终都是通过访问函数 ClusterIP 类型的 service 触发函数执行。自动伸缩K8s 通过 Horizontal Pod Autoscaler 实现 pod 的自动水平伸缩。Kubeless 的 function 通过 K8s deployment 部署运行,因此天然可以利用 HPA 实现自动伸缩。度量数据获取自动伸缩的第一步是要让 HPA 能够获取度量数据。目前,kubeless 中的函数支持基于 cpu 和 qps 这两种指标进行自动伸缩。下图展示了 HPA 获取这两种度量数据的途径。内置度量指标 cpuCPU 使用率属于内置度量指标,对于这类指标 HPA 可以通过 metrics API 从 Metrics Server 中获取数据。Metrics Server 是 Heapster 的继承者,它可以通过kubernetes.summary_api从 Kubelet、cAdvisor 中获取度量数据。自定义度量指标 qpsQPS 属于自定义度量指标,想要获取这类指标的度量数据需要完成下列步骤。部署用于存储度量数据的系统,这里选择已经被纳入 CNCF 的 Prometheus。Prometheus 是一套开源监控&告警&时序数据解决方案,并且被 DigitalOcean、Red Hat、SUSE 和 Weaveworks 这些 cloud native 领导者广泛使用;采集度量数据,并写入部署好的 Prometheus 中。Kubeless 提供的函数框架会在函数每次被调用时,将下列度量数据 function_duration_seconds、function_calls_total、function_failures_total 写入 Prometheus(可参考 python 样例)。部署实现了 custom metrics API 的 custom API server。这里,因为度量数据被存入了 Prometheus,因此选择部署 k8s-prometheus-adapter,它可以从 Prometheus 中获取度量数据。完成上述步骤后,HPA 就可以通过 custom metrics API 从 Prometheus Adapter 中获取 qps 度量数据。详细配置步骤可参考文章 kubeless-autoscaling。K8s 度量指标简介有时基于 cpu 和 qps 这两种度量指标对函数进行自动伸缩还远远不够。如果希望基于其它度量指标,需要了解 K8s 定义的度量指标类型及其获取方式。目前,K8s 1.13 版本支持的度量指标类型如下:准备好相应的度量数据和获取数据的组件,HPA 就能基于它们对函数进行自动伸缩。更多关于 K8s 度量指标的介绍可参考文章 hpa-external-metrics。度量数据使用知道了 HPA 获取度量数据的途径后,下面描述 HPA 如何基于这些数据对函数进行自动伸缩。基于 cpu 使用率假设已经存在一个名为 hello 的函数,以下命令将为该函数创建一个基于 cpu 使用率的 HPA,它将运行该函数的 pod 数量控制在 1 到 3 之间,并通过增加或减少 pod 个数使得所有 pod 的平均 cpu 使用率维持在 70%。kubeless autoscale create hello –metric=cpu –min=1 –max=3 –value=70Kubeless 使用的是 autoscaling/v2alpha1 版本的 HPA API,该命令将要创建的 HPA 如下:kind: HorizontalPodAutoscalerapiVersion: autoscaling/v2alpha1metadata: name: hello namespace: default labels: created-by: kubeless function: hellospec: scaleTargetRef: kind: Deployment name: hello minReplicas: 1 maxReplicas: 3 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 70该 HPA 计算目标 pod 数量的公式如下:TargetNumOfPods = ceil(sum(CurrentPodsCPUUtilization) / Target)基于 qps以下命令将为函数 hello 创建一个基于 qps 的 HPA,它将运行该函数的 pod 数量控制在 1 到 5 之间,并通过增加或减少 pod 个数确保所有挂在服务 hello 后的 pod 每秒能处理的请求次数之和达到 2000。kubeless autoscale create hello –metric=qps –min=1 –max=5 –value=2k该命令将要创建的 HPA 如下:kind: HorizontalPodAutoscalerapiVersion: autoscaling/v2alpha1metadata: name: hello namespace: default labels: created-by: kubeless function: hellospec: scaleTargetRef: kind: Deployment name: hello minReplicas: 1 maxReplicas: 5 metrics: - type: Object object: metricName: function_calls target: apiVersion: autoscaling/v2beta1 kind: Service name: hello targetValue: 2k基于多项指标如果计划基于多项度量指标对函数进行自动伸缩,需要直接为运行 function 的 deployment 创建 HPA。使用如下 yaml 文件可以为函数 hello 创建一个名为hello-cpu-and-memory的 HPA,它将运行该函数的 pod 数量控制在 1 到 10 之间,并尝试让所有 pod 的平均 cpu 使用率维持在 50%,平均 memory 使用量维持在 200MB。对于多项度量指标,K8s 会计算出每项指标需要的 pod 数量,取其中的最大值作为最终的目标 pod 数量。kind: HorizontalPodAutoscalerapiVersion: autoscaling/v2alpha1metadata: name: hello-cpu-and-memory namespace: default labels: created-by: kubeless function: hellospec: scaleTargetRef: kind: Deployment name: hello minReplicas: 1 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 50 - type: Resource resource: name: memory targetAverageValue: 200Mi自动伸缩策略一个理想的自动伸缩策略应当处理好下列场景:当负载激增时,函数能迅速扩展以应对突发流量;当负载下降时,函数能立即收缩以节省资源消耗;具备抗噪声干扰能力,能够精确计算出目标容量;能够避免自动伸缩过于频繁造成系统抖动。Kubeless 依赖的 HPA 充分考虑了上述情形,不断改进和完善其使用的自动伸缩策略。下面以 K8s 1.13 版本为例描述该策略。如果想要更加深入地了解策略原理请参考链接 horizontal。HPA 每隔一段时间会根据获取的度量数据同步一次和该 HPA 关联的 RC / Deployment 中的 pod 个数,时间间隔通过 kube-controller-manager 的参数–horizontal-pod-autoscaler-sync-period指定,默认为 15s。在每一次同步过程中,HPA 需要经历如下图所示的计算流程。计算目标副本数分别计算 HPA 列表中每项指标需要的 pod 数量,记为 replicaCountProposal。选择其中的最大值作为 metricDesiredReplicas。在计算每项指标的 replicaCountProposal 过程中会考虑下列因素:允许目标度量值和实际度量值存在一定程度的误差,如果在误差范围内直接使用 currentReplicas 作为 replicaCountProposal。这样做是为了在可接受范围内避免伸缩过于频繁造成系统抖动,该误差值可以通过 kube-controller-manager 的参数–horizontal-pod-autoscaler-tolerance指定,默认值是 0.1。当一个 pod 刚刚启动时,该 pod 反映的度量值往往不是很准确,HPA 会将这种 pod 视为 unready。在计算度量值时,HPA 会跳过处于 unready 状态的 pod。这样做是为了消除噪声干扰,可以通过 kube-controller-manager 的参数–horizontal-pod-autoscaler-cpu-initialization-period(默认为 5 分钟)和–horizontal-pod-autoscaler-initial-readiness-delay(默认为 30 秒)调整 pod 被认为处于 unready 状态的时间。平滑目标副本数将最近一段时间计算出的 metricDesiredReplicas 记录下来,取其中的最大值作为 stabilizedRecommendation。这样做是为了让缩容过程变得平滑,消除度量数据异常波动造成的影响。该时间段可以通过参数–horizontal-pod-autoscaler-downscale-stabilization-window指定,默认为 5 分钟。规范目标副本数限制 desiredReplicas 最大为 currentReplicas * scaleUpLimitFactor,这样做是为了防止因 采集到了“虚假的”度量数据造成扩容过快。目前 scaleUpLimitFactor 无法通过参数设定,其值固定为 2。限制 desiredReplicas 大于等于 hpaMinReplicas,小于等于 hpaMaxReplicas。执行扩容缩容操作如果通过上述步骤计算出的 desiredReplicas 不等于 currentReplicas,则“执行”扩容缩容操作。这里所说的执行只是将 desiredReplicas 赋值给 RC / Deployment 中的 replicas,pod 的创建销毁会由 kube-scheduler 和 worker node 上的 kubelet 异步完成的。小结Kubeless 提供的自动伸缩功能是对 K8s HPA 的简单封装,避免了将创建 HPA 的复杂细节直接暴露给用户。Kubeless 目前提供的度量指标过少,功能过于简单。如果用户希望基于新的度量指标、综合多项度量指标或者调整自动伸缩的效果,需要深入了解 HPA 的细节。目前 HPA 的扩容缩容策略是基于既成事实被动地调整目标副本数,还无法根据历史规律预测性地进行扩容缩容。总结Kubeless 基于 K8s 提供了较为完整的 serverless 解决方案,但和一些商业 serverless 产品还存在一定差距:Kubeless 并未在镜像拉取、代码下载、容器启动等方面做过多优化,导致函数冷启动时间过长;Kubeless 并未过多考虑多租户的问题,如果希望多个用户的函数运行在同一个集群里,还需要进行二次开发。本文作者:吴波bruce_wu阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 3, 2018 · 6 min · jiezi

Kubernetes 弹性伸缩全场景解析 (一)- 概念延伸与组件布局

传统弹性伸缩的困境弹性伸缩是Kubernetes中被大家关注的一大亮点,在讨论相关的组件和实现方案之前。首先想先给大家扩充下弹性伸缩的边界与定义,传统意义上来讲,弹性伸缩主要解决的问题是容量规划与实际负载的矛盾。如上图所示,蓝色的水位线表示集群的容量随着负载的提高不断的增长,红色的曲线表示集群的实际的负载真实的变化。而弹性伸缩要解决的就是当实际负载出现激增,而容量规划没有来得及反应的场景。常规的弹性伸缩是基于阈值的,通过设置一个资源缓冲水位来保障资源的充盈,通常15%-30%左右的资源预留是比较常见的选择。换言之就是通过一个具备缓冲能力的资源池用资源的冗余换取集群的可用。这种方式表面上看是没有什么问题的,确实在很多的解决方案或者开源组件中也是按照这种方式进行实现的,但是当我们深入的思考这种实现方案的时候会发现,这种方式存在如下三个经典问题。1. 百分比碎片难题在一个Kubernetes集群中,通常不只包含一种规格的机器,针对不同的场景、不同的需求,机器的配置、容量可能会有非常大的差异,那么集群伸缩时的百分比就具备非常大的迷惑性。假设我们的集群中存在4C8G的机器与16C32G的机器两种不同规格,对于10%的资源预留,这两种规格是所代表的意义是完全不同的。特别是在缩容的场景下,通常为了保证缩容后的集群不处在震荡状态,我们会一个节点一个节点或者二分法来缩容节点,那么如何根据百分比来判断当前节点是处在缩容状态就尤为重要,此时如果大规格机器有较低的利用率被判断缩容,那么很有可能会造成节点缩容后,容器重新调度后的争抢饥饿。如果添加判断条件,优先缩容小配置的节点,则有可能造成缩容后资源的大量冗余,最终集群中可能会只剩下所有的巨石节点。2. 容量的规划炸弹还记得在没有使用容器前,是如何做容量规划的吗?一般会按照应用来进行机器的分配,例如,应用A需要2台4C8G的机器,应用B需要4台8C16G的机器,应用A的机器与应用B的机器是独立的,相互不干扰。到了容器的场景中,大部分的开发者无需关心底层的资源了,那么这个时候容量规划哪里去了呢?在Kubernetes中是通过Request和Limit的方式进行设置,Request表示资源的申请值,Limit表示资源的限制值。既然Request和Limit才是容量规划的对等概念,那么这就代表着资源的实际计算规则要根据Request和Limit才更加准确。而对于每个节点预留资源阈值而言,很有可能会造成小节点的预留无法满足调度,大节点的预留又调度不完的场景。3. 资源利用率困境集群的资源利用率是否可以真的代表当前的集群状态呢?当一个Pod的资源利用率很低的时候,不代表就可以侵占他所申请的资源。在大部分的生产集群中,资源利用率都不会保持在一个非常高的水位,但从调度来讲,资源的调度水位应该保持在一个比较高的水位。这样才能既保证集群的稳定可用,又不过于浪费资源。如果没有设置Request与Limit,而集群的整体资源利用率很高这意味着什么?这表示所有的Pod都在被以真实负载为单元进行调度,相互之间存在非常严重的争抢,而且简单的加入节点也丝毫无法解决问题,因为对于一个已调度的Pod而言,除了手动调度与驱逐没有任何方式可以将这个Pod从高负载的节点中移走。那如果我们设置了Request与Limit而节点的资源利用率又非常高的时候说明了什么呢?很可惜这在大部分的场景下都是不可能的,因为不同的应用不同的负载在不同的时刻资源的利用率也会有所差异,大概率的情况是集群还没有触发设置的阈值就已经无法调度Pod了。弹性伸缩概念的延伸既然基于资源利用率的弹性伸缩有上述已知的三个问题,有什么办法可以来解决呢?随着应用类型的多样性发展,不同类型的应用的资源要求也存在越来越大的差异。弹性伸缩的概念和意义也在变化,传统理解上弹性伸缩是为了解决容量规划和在线负载的矛盾,而现在是资源成本与可用性之间的博弈。如果将常见的应用进行规约分类,可以分为如下四种不同类型:在线任务类型比较常见的是网站、API服务、微服务等常见的互联网业务型应用,这种应用的特点是对常规资源消耗较高,比如CPU、内存、网络IO、磁盘IO等,对于业务中断容忍性差。离线任务类型例如大数据离线计算、边缘计算等,这种应用的特点是对可靠性的要求较低,也没有明确的时效性要求,更多的关注点是成本如何降低。定时任务类型定时运行一些批量计算任务是这种应用的比较常见形态,成本节约与调度能力是重点关注的部分。特殊任务类型例如闲时计算的场景、IOT类业务、网格计算、超算等,这类场景对于资源利用率都有比较高的要求。单纯的基于资源利用率的弹性伸缩大部分是用来解决第一种类型的应用而产生的,对于其他三种类型的应用并不是很合适,那么Kubernetes是如何解决这个问题的呢?kubernetes的弹性伸缩布局Kubernetes将弹性伸缩的本质进行了抽象,如果抛开实现的方式,对于不同应用的弹性伸缩而言,该如何统一模型呢?Kubernetes的设计思路是将弹性伸缩划分为保障应用负载处在容量规划之内与保障资源池大小满足整体容量规划两个层面。简单理解,当需要弹性伸缩时,优先变化的应该是负载的容量规划,当集群的资源池无法满足负载的容量规划时,再调整资源池的水位保证可用性。而两者相结合的方式是无法调度的Pod来实现的,这样开发者就可以在集群资源水位较低的时候使用HPA、VPA等处理容量规划的组件实现实时极致的弹性,资源不足的时候通过Cluster-Autoscaler进行集群资源水位的调整,重新调度,实现伸缩的补偿。两者相互解耦又相互结合,实现极致的弹性。在Kubernetes的生态中,在多个维度、多个层次提供了不同的组件来满足不同的伸缩场景。如果我们从伸缩对象与伸缩方向两个方面来解读Kubernetes的弹性伸缩的话,可以得到如下的弹性伸缩矩阵。cluster-autoscaler:kubernetes社区中负责节点水平伸缩的组件,目前处在GA阶段(General Availability,即正式发布的版本)。HPA:kubernetes社区中负责Pod水平伸缩的组件,是所有伸缩组件中历史最悠久的,目前支持autoscaling/v1、autoscaling/v2beta1与autoscaling/v2beta2,其中autoscaling/v1只支持CPU一种伸缩指标,在autoscaling/v2beta1中增加支持custom metrics,在autoscaling/v2beta2中增加支持external metrics。cluster-proportional-autoscaler:根据集群的节点数目,水平调整Pod数目的组件,目前处在GA阶段。vetical-pod-autoscaler:根据Pod的资源利用率、历史数据、异常事件,来动态调整负载的Request值的组件,主要关注在有状态服务、单体应用的资源伸缩场景,目前处在beta阶段。addon-resizer:根据集群中节点的数目,纵向调整负载的Request的组件,目前处在beta阶段。在这五个组件中,cluster-autoscaler、HPA、cluster-proportional-autoscaler是目前比较稳定的组件,建议有相关需求的开发者进行选用。对于百分之八十以上的场景,我们建议客户通过HPA结合cluster-autoscaler的方式进行集群的弹性伸缩管理,HPA负责负载的容量规划管理而cluster-autoscaler负责资源池的扩容与缩容。最后在本文中,和大家主要讨论的是在云原生时代下弹性伸缩概念的延伸,以及Kubernetes社区是如何通过解耦的方式通过多个转职的组件实现了两个维度的弹性伸缩,在本系列后面的文章中会为一一解析每个弹性伸缩组件的相关原理与用法。本文作者:莫源阅读原文本文为云栖社区原创内容,未经允许不得转载。

November 23, 2018 · 1 min · jiezi