SegmentFault 金融级分布式架构SOFAStack最新的文章
2023-09-14T15:08:09+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
议题征集中|KCD 2023 杭州站
https://segmentfault.com/a/1190000044217867
2023-09-14T15:08:09+08:00
2023-09-14T15:08:09+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000044217869" alt="图片" title="图片"></p><h3>关于 KCD</h3><p>Kubernetes Community Days <em>(KCD)</em> 由云原生计算基金会 <em>(CNCF)</em> 发起,由全球各国当地的 CNCF 大使、CNCF 员工以及 CNCF 会员单位联合组织。目前 KCD 正在全球各个国家活跃地组织进行中,KCD 聚集了来自云原生领域开源社区的最终用户、贡献者和技术专家,这一系列的活动有助于提高 Kubernetes 社区的活跃度并完善其发展潜力,使更多用户能接触到云原生信息,也推动云原生技术在不同行业中更广泛的传播。</p><h3>KCD 杭州</h3><p>杭州是中国东南沿海中心城市之一、浙江省省会。美丽的西湖成就了“上有天堂,下有苏杭”的千古美誉。而今天,这张名片,似乎已经被“电商之都”所取代。杭州凭借着互联网电商经济成为了炙手可热的新一线城市,同时也吸引了很多大型互联网公司,并且具有浓厚的技术氛围。本次也是 Kubernetes Community Days 首次来到杭州,由 <strong>CNCF</strong>、<strong>蚂蚁开源</strong>、<strong>龙蜥社区</strong>、<strong>Dragonfly 社区</strong>、<strong>Harbor 社区</strong>联合发起。希望能够在这座充满活力的城市进一步推广云原生相关技术。</p><p><img width="723" height="301" src="/img/bVc9Hdt" alt="![图片" title="![图片"></p><p><strong>KCD 杭州站主页</strong>:<a href="https://link.segmentfault.com/?enc=wmrzd9aNdLiCVsJYQ4Y5KQ%3D%3D.OdNtdTCVi758bl5V3ux0TOJ09MZjuk4AWf8lAKAZE%2B7UVTQx0Ptt5s8KAJkzHz6f" rel="nofollow"><strong>https://community.cncf.io/e/myjve4/</strong></a></p><h3>KCD 2023 杭州站-议题征集中</h3><p>本次 KCD 杭州站 2023 演讲议题已开放投稿,届时我们会筛选出优秀议题在本次 KCD 杭州站活动中呈现。 </p><p><img width="256" height="256" src="/img/bVc9Hdx" alt="图片" title="图片"></p><p>扫描二维码或点击链接🔗即可投递议题</p><p>投递议题🔗:<a href="https://link.segmentfault.com/?enc=uvZ31Sj2e6upOWwp%2FxysMQ%3D%3D.SgHDTrqAhUfFKAyB3fpSnceCassY2MERpvmEzGCrHNGKVe9hIwVtodKQQGl3NtzR" rel="nofollow"><strong>https://www.wjx.top/vm/eyRKT8y.aspx#</strong></a></p><p><em>(注:此次活动不提供讲师差旅报销,请您合理安排出行)</em></p><h4>议题截止时间</h4><p>2023 年 <strong>9 月 20 日 23:59</strong><em>*</em>*</p><h4>活动信息</h4><p>时间:<strong>10 月 21 日 <em>(半天)</strong></em></p><p>规模:150~200 人 </p><p>形式:线下主题分享+圆桌 </p><h4>议题涉及方向介绍</h4><p>本次活动由 <strong>云原生供应链</strong> 以及 <strong>AI 基础设施</strong> 两个方向主题会场组成,议题内容可包括<strong>软件供应链</strong>、<strong>AI Infra</strong>、<strong>容器镜像</strong>、<strong>DevOps</strong>、<strong>可观测</strong>、<strong>安全</strong>等热点领域。CNCF 生态中任何与主题相关的开源项目与云原生技术实践的内容均可参与,不局限于 Kubernetes 本身。</p><h4>时长</h4><p>每个演讲时长 30~45 分钟,包括最后 5 分钟的 Q&A 提问环节。</p><h4>注意事项</h4><ul><li>此次活动不提供讲师差旅报销,请您合理安排出行</li><li>在议题征集期间,可自由提交演讲议题</li><li>若内容合适,但相应专题已无空档,可安排其他演讲机会</li><li>请确保您所提供的个人信息内容准确无误</li><li>请确保您在演讲内容中使用的案例已经获得第三方的认可和允许,我们将不负责由此引起的任何纠纷和问题</li><li>确认的演讲内容将发布在主办单位的有关媒介中,包括会议演讲 PPT、现场视频等。此非强制条款,如果不同意,请提前声明</li><li>对于已提交演讲议题的审批结果,我们将在议题征集截止后的一周内通过电子邮件或电话联系的方式通知您</li><li>最终分享议题以 KCD 杭州组委会最终发送的通知为准</li><li>如有活动相关问题,请添加微信:<strong>gaius_qi</strong></li></ul><h4>社区合作</h4><p>KCD 2023 杭州站<strong>社区合作进行中</strong>,合作方式多样,欢迎小伙伴们来咨询哦!</p><p>社区合作请联系:</p><p><strong>13167455321</strong> <em>(微信同)</em></p><p><img src="/img/remote/1460000044217870" alt="图片" title="图片"></p><p>主页持续更新中……</p><p>关于云原生计算基金会</p><p>CNCF <em>(Cloud Native Computing Foundation)</em> 成立于 2015 年 12 月,隶属于 Linux Foundation,是非营利性组织。</p><p>CNCF <em>(云原生计算基金会)</em> 致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。</p>
大象转身:支付宝资金技术 Serverless 提效总结
https://segmentfault.com/a/1190000044193549
2023-09-08T10:00:00+08:00
2023-09-08T10:00:00+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<h2>1. 前言</h2><p>本文是支付宝资金技术团队在《大象转身-支付宝资金技术交付提效之路》系列总结之一。如果你正在负责一个所支撑的<strong>业务面临从平台到场景化创新的发展阶段</strong>,希望通过提升交付效率来<strong>提升技术团队的竞争力和团队研发幸福感</strong>, 那么这篇文章也许会让你有收获:</p><p><strong>1、Serverless 的技术理念是什么?对业务研发有什么借鉴意义?</strong></p><p><strong>2、如何基于 Serverless 驱动研发模式的升级,设计一个能让场景化创新快起来的技术架构,提升全局研发效率?</strong></p><p>本篇作者:<strong>呆莫(代巍)、歆明(何鑫铭)</strong></p><p><strong>文章较长,建议收藏和保存</strong></p><hr><h3><strong>为什么写这篇文章</strong></h3><p>我们做了近两年的 Serverless 研发模式的探索和落地,目前该模式已经承接了大部分资金业务的场景,平台也已经进入推广期,有 20+ 团队经过 SOFAServerless 技术团队的介绍找到我们。未接入 SOFAServerless 的想从业务视角寻求架构选型依据和应用架构设计,已接入 SOFAServerless 的同学来寻找基座治理经验。因此决定写一篇文章,<strong>将我们的架构选型、设计和落地经验做一次完整的总结。</strong></p><h2>2. 我们遇到的问题</h2><p><em>我们的问题:随着场景创新增多,SOFA 应用变臃肿、团队研发人数变多导致研发和运维难度升高,阻碍了业务创新的交付效率,系统需要做重构。</em></p><h3><strong>简单说说背景</strong></h3><p>资金在这两年时间,肩负了很多业务创新、寻找业务增量的目标。增量时代的创新是残酷的,成功的明星产品背后,是无数次的失败(这三年间,技术支撑了 15+ 个创新产品的建设,包括小程序、行业解决方案)。而支付团队转型做创新也面临着巨大的挑战:动辄 1-2 个月起的研发交付周期,在当时使得技术团队被按在地上摩擦,各种问题接踵而来。</p><p><img src="/img/remote/1460000044193551" alt="图片" title="图片"></p><p>蚂蚁集团 CTO 苗人凤曾经说过,创新是九死一生,技术就是要帮助业务降低机会成本。</p><p>我们逐渐意识到业务创新的快速发展,一定离不开<strong>业务快速交付的新型技术研发模式</strong>。如果我们还是沿用平台化的架构思路,在一个庞大的资金平台上写一堆创新场景的代码,在完成业务需求的同时还要思考如何抽象、如何保障增量的代码不影响平台稳定性,那一定快不了。</p><p>2021 年,我们成立“<strong>资金场景创新提效项目</strong>”,试图通过平台架构升级、研发模式升级解决场景创新效率问题。</p><h3>问题定义:资金场景应用变成巨石应用带来一系列问题</h3><blockquote><p>试着代入一下案例:</p><p>如果有那么 5 个创新业务迭代在同一个应用发布窗口进行推进,会存在各种各样导致延期的原因:</p><ul><li>A 业务验证延后 2 天,那么 5 个迭代都要延后 2 天;</li><li>B 业务升级了中间件版本,那整个应用的业务都面临回归成本一样要延后整个发布期;</li><li>C 业务想要在预发搭个车微调一些代码,但是由于动作慢没能成功验证,导致污染了主干,进而耽误了整体;</li><li>……</li></ul></blockquote><p>在应用架构层面,资金部门早期孵化出了一个“大资金场景层应用”——Fundapplication。Fundapplication 在研发协作方面,从一开始的几个同学,到后来几个业务团队、几十个研发同学,都在一个应用系统上研发,通过多个 bundle 区分不同业务,公共的 bundle 沉淀通用能力和研发工具(<em>类似于之前的 mng 系统</em>),如下图:</p><p><img src="/img/remote/1460000044193552" alt="图片" title="图片"></p><p>这种架构设计在遇到大量的彼此没有复用的业务创新需求时,就逐步沦为了巨石应用,如下图:</p><p><img src="/img/remote/1460000044193553" alt="图片" title="图片"></p><ol><li><strong>研发运维效率低</strong>:作为入口系统,mgw 接口无法在本地开发自测,改动任何代码必须发布到联调环境。发布一次需要 10 分钟以上,还要忍受环境不稳定因素带来的额外时间花费。</li><li><strong>业务迭代抢占,协同成本高</strong>:系统作为创新应用系统,有 10+ 个创新产品,30+ 个正式员工和外包员工也在这个系统上做研发迭代,火车式发布迭代痛点明显——一人晚点,全体延期。</li><li><strong>研发时隔离效率低</strong>:30+ 研发同学(含外包)在一个系统内研发,经过不到一年的时间,我们的系统就变成 10+ 个 bundle、2w+ 个 Java 文件(<em>全站均值 4000</em>)的体量了,因为各种业务的各种依赖的增加,系统启动一次需要 15 分钟。随着创新场景增多会变得更加糟糕,而且很多代码没有办法随着业务的下线而删除。</li><li><strong>运行时不隔离</strong>:目前我们没有对不同的业务区分不同的机房,业务吃大锅饭,难以做到按产品的资源和稳定性保障。</li></ol><p>如果你的应用也存在上述问题,对业务创新的交付效率产生了一定的影响,那可以继续参考第 3、4 章我们的架构选型和关键设计。</p><h2>3. 应用架构的选型</h2><p>当前蚂蚁基于 SOFABoot 的应用架构,在规模化创新场景研发的交付效率、分工协作模式上存在局限性。从行业的发展趋势分析,云原生等新技术引领的“集装箱”架构模式,有利于促进规模化的生产效率和分工协作方式。借助蚂蚁 SOFAServerless 的孵化,我们决定作为种子用户,使用 Serverless 中 BaaS+FaaS 的理念,重塑创新场景研发的研发模式。</p><h3><strong>微服务应用架构的局限性</strong></h3><p>大应用创新的模式,是在一个应用系统中按照不同 bundle 来隔离创新应用,可复用的代码可能是放到一个公共的 bundle 中。优点当然是复用效率高,运维统一,缺点主要是协作效率、隔离成本和研发效率。大应用变成巨石应用如何解决?这个我们很容易就想到使用分治的思路,将一个大应用拆成多个应用,通过 RPC 来互相调用。</p><p>在蚂蚁当前体系下,微服务系统的 0-1 成本、后续运维成本、硬件机器成本仍然会是一个不小的开销,创新试错还是快不起来。且后端业务创新具有巨大的不确定性,如果因为业务没量了变成长尾系统下线的成本也很高;在性能方面,多个系统间通用能力的调用,会使全链路的调用耗时增加,在业务上不可接受。<strong>所以有没有一种技术,可以让业务既能够隔离,又能够降低隔离带来的负向影响:运维成本、能力复用效率、性能?</strong></p><h3><strong>行业技术架构的发展趋势</strong></h3><p>一部分做架构的同学喜欢用“架构隐喻”来介绍架构的理念。近几年容器化、云原生、Serverless 等新技术层出不穷,作为架构师需要在对新技术保持敏感的同时,能够有一双洞察架构本质的眼睛。</p><p>这些底层新技术的出现,本质上是通过抽象定义“集装箱”的方式,规范了构建、部署、运行、运维标准,提升了软件的生产效率。大家可以感受一下这些技术底层理念的相通之处:</p><ul><li><strong>Docker</strong> 通过定义容器“集装箱”提升传统虚拟机、基础设施软件构建、运维效率;</li><li><strong>K8s</strong> 进一步抽象万物皆可定义为 CRD(<em>自定义资源</em>)提升“集装箱”的部署、运维效率;</li><li><strong>中间件 Mesh 化</strong>将“集装箱”理念应用到中间件近端,将中间件近端与业务代码剥离解耦;</li><li><strong>BaaS、FaaS 等 Serverless 技术</strong>,将“集装箱”理念范围进一步扩大到可复用的“业务领域插件”。</li></ul><p>可以这么说,集装箱改变世界航运效率,计算机软件行业的 Docker、K8s 们通过“集装箱”的架构理念改变了基础软件生产效率。而随着 Serverless 的兴起,“集装箱”的架构理念将会自底向上地,重塑对软件交付效率重度依赖的互联网、金融等行业。</p><p><img src="/img/remote/1460000044193554" alt="图片" title="图片"></p><p>我们来看下,集装箱的理念对我们业务研发的启示:</p><p>过去,业务研发需要关注独立的业务应用,既需要关注业务代码、中间件代码等代码信息,又需要关注 Java 进程、CPU、内存等运维信息,随着这两年蚂蚁 MOSN 等 Service Mesh 基础设施的出现,将中间件代码做了拆分和代托管运维,解放了业务研发的部分精力,使其不需要再经常性地忍受中间件近端升级带来的迭代变更。</p><p>再进一步的,我们开一个脑洞,能否将 SOFA 运行容器的 JVM 托管出去?甚至更进一步的,将一些通用的业务流程能力、日志和框架等能力托管出去呢?这样创新业务研发的关注点,可以从一个独立的 Java 应用,进一步聚焦到 Java 业务功能性代码,而将非功能性的 Java 容器、日志、框架的构建、部署、运行、运维等工作交给非业务研发同学来负责。带着这样的脑洞,我们来了解下 Serverless 的行业概念并分析架构的可能性。</p><p><img src="/img/remote/1460000044193555" alt="图片" title="图片"></p><h3><strong>理解 Serverless 的设计理念</strong></h3><h4>Serverless 的行业概念解释</h4><p>先带大家看一下行业中对于 Serverless 的解释。</p><p><strong>第一个层面是站在运维和云计算厂商的视角:</strong> 将 Serverless 拆开看,Serverless = Server(<em>服务端</em>)+ less(<em>较少关心</em>),组合起来便是“较少关心服务端”。这里的“服务端”是指对服务运行资源的运维。因此,Serverless 还有另一种解释是服务端免运维,即 NoOps。<strong>概念方面 Serverless 是对运维体系的一个极端抽象。</strong> 在这种架构中,我们并不看重运行一个函数需要多少 CPU 或 RAM 或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。</p><p><strong>第二个层面是站在应用开发的视角:</strong> 在 Serverless 中,“计算资源”这个概念,除了是作为服务器出现之外也作为服务出现。与传统架构的不同之处在于,它可以完全由第三方管理、由事件触发、无状态地(<em>Stateless</em>)暂存(<em>可能只存在于一次调用的过程中</em>)在计算容器内。我们基于这些“服务计算资源”,就可以更容易地搭建出一个应用,比如你可以把账户管理、交易管理、营销管理等服务完全托管给这些第三方“服务计算资源”。</p><p>一个 Serverless 化的应用,就是指大量依赖第三方服务(<em>也叫做后端即服务,即“BaaS”</em> )或暂存容器中运行的自定义代码(<em>函数即服务,即“FaaS”</em> )的应用程序。这是一种架构思想,类似微服务一样,不过微服务这种思想架构,目前已经有成熟的框架来支撑,比如 Dubbo、Spring Cloud 等。但是 Serverless 架构目前还没有一个框架来支撑,也因此说 Serverless 技术实际上是一组技术的集合和一种站在服务视角上的应用设计理念。</p><p>按照 CNCF(<em>Cloud Native Computing Foundation 云原生技术基金会</em>)对 Serverless 的定义,Serverless 架构应该是采用 FaaS(<em>函数即服务</em>)和 BaaS(<em>后端即服务</em>)服务来解决问题的一种设计。</p><p><img src="/img/remote/1460000044193556" alt="图片" title="图片"></p><p>它的整体架构如上图所示,包含两种模式和形态:</p><ul><li><strong>BaaS(<em>Backend as a Service</em>):后端即服务。</strong> 具备高可用&弹性,且免运维的后端服务。可以是符合以上特点的对象存储服务、数据库服务、身份验证服务等等。狭义上讲,BaaS 是为 FaaS 提供服务支撑。</li><li><strong>FaaS(<em>Function as a Service</em>):函数即服务。</strong> 提供流程化的服务,将客户对流程的扩展采用一定范式的抽象发布成函数实例,外部可通过触发器使用该服务。FaaS 支持动态扩缩容。以 HTTP 请求为例,当请求量很大时,FaaS 函数会自动扩容多实例同时运行;在 HTTP 请求降低时自动缩容;无流量时还可以缩容到 0 实例。</li></ul><h4>Serverless 的设计理念抽象成应用架构</h4><p>理解了 Serverless 的行业概念解释,我们带着这个理念再回顾上面出现过的这张图。</p><p><img src="/img/remote/1460000044193555" alt="图片" title="图片"></p><p>借助集装箱的架构思想,Serverless 理念无非是站在云服务视角,将一个应用程序按照集装箱的方式、拆分成不同的“image”,并进行可标准化的 BUILD 构建、SHIP 部署、RUN 运行,以提升规模化生产效率。</p><p>如下图所示,可拆分成使用方、服务方,服务方以 BaaS 独立服务、FaaS 独立流程模版的形式将业务代码抽象成“image”(<em>镜像</em>),使用方将简单的业务代码或者基于 FaaS 扩展的函数也打包成“image”,大家将 image 作为基本单元。我们知道 Docker 是有一套容器运行环境来支持 image 镜像的构建、部署和运行的;类似的,Serverless 也需要有这样一套基本容器环境。这套容器环境,需要对镜像的构建、部署和运行做实现。</p><p><img src="/img/remote/1460000044193557" alt="图片" title="图片"></p><h4>Java 应用的 Serverless 运行环境实现</h4><p>2021 年,我们看到蚂蚁内部有 SOFAArk 这样的模块化框架,提供同 JVM 内多个模块分离研发、部署、运行的研发框架。我们经过 POC 技术验证认为,基于 SOFAArk 定义的 Ark 模块、基座概念和 SOFAArk 提供的模块构建、部署和运行机制(模块粒度代码拆分、模块热部署、同一 JVM 运行),是初步符合 Serverless 的设计理念的。</p><p>同时我们看到,围绕这套框架的配套运维系统、产品系统也开始初见雏形,经过架构决策后,非常笃定这个架构演进方向,决定作为种子用户加入初创团队,作为 SOFAServerless 的业务样板间进行孵化。</p><p><img src="/img/remote/1460000044193558" alt="图片" title="图片"></p><h2><strong>4. 关键架构设计</strong></h2><p>如何重构我们的巨石应用?我们借助 SOFAServerless 对模块、基座的理念,对巨石应用 Fundapplication 进行了拆分、托管,将业务的关注点从应用聚焦为“轻应用”,将公共模块、基座采用了类 BaaS 和 FaaS 的全托管模式。</p><h3><strong>新模式的探索</strong></h3><p>基于 SOFAServerless,我们探索了“轻应用架构范式”,通过 SOFAArk 框架将应用程序拆分为基座、业务模块、公共模块这三种不同的“image”。</p><p>这种做法的好处,一方面可复用的业务能力、日志等技术服务能力抽象为公共模块,由能力的供给方托管,可快速实现规模化复用;另一方面模块和基座的边界比较清楚,基座不承载业务逻辑,只承载 Java 运行时容器、二方 Jar 包管理、蚂蚁全套中间件集成等基础运行能力,可完全由非业务研发同学托管,真正实现业务模块研发同学的关注点聚焦。业务研发同学只需要关注业务模块,把所有业务高频变更的代码都放在业务模块,减少迭代竞争。</p><p><img src="/img/remote/1460000044193559" alt="图片" title="图片"></p><h3><strong>Serverless 无服务器化带来的轻运维方式</strong></h3><p>因 Serverless 无服务器化技术的的核心原理,能够带来一些显著的变化。比如热部署的技术、场景化服务动态发布的技术、无服务器技术(<em>共享基座运行时</em>),可以让模块快起来、并能够做到隔离、“免”运维。 </p><p>形成了一种新的研发模式,基座这种低频变更可以对标传统的经典 SOFA 体系;模块高频变更可以走到轻研发模式,同时有了容器弹性腾挪的能力加持,也一定程度上做到了流量隔离和弹性伸缩,见下图:</p><ul><li>研发过程独立,互不影响,4 个业务方,20+ 模块独立迭代敏捷变更;</li><li>快速划分并创建集群,隔离部署业务,业务集群间模块支持弹性伸缩,方便快速弹出解决运维的问题;</li><li>基座统一托管代运维,贴合人性,削减为轻运维成本极低。</li></ul><p><img src="/img/remote/1460000044193560" alt="图片" title="图片"></p><blockquote><p>试想一下这个案例,大家研发中可能经常在提交部署后才发现一个 if else 判断逻辑写反,需要 commit 修复代码+重新部署。</p><p><strong>SOFA 应用:10min 的成本。</strong> 研发同学的直觉肯定是先去干别的,过一会儿再回过头来再去做验证工作,我相信同学很难有这个时间节点把握,不太可能定一个 10min 的闹钟提醒自己回来,所以整体花费可能远大于 10min。</p><p><strong>SOFAServerless 模块:30s 的成本。</strong> 研发同学一般会认为可能可以等一等,马上就能投入验证和自测工作,所以整体是一个“延续性”的过程,可以一鼓作气把整个研发自测做完。</p></blockquote><h3><strong>Serverless 应用架构带来的复用提效</strong></h3><p>SOFAServerless 代表了一种应用设计范式,有基座抽象、模块内聚的特性,通过基座这种复用模式可以提供开箱即用的方便模块快速集成的技术能力,让我们可以在基座上有所发挥。</p><p>基座可以成为一个“BaaS 化的应用服务市场”,我们可以把一些例如工具、脚手架等能力内置到模块上;提供一些技术产品让模块可以选装快速集成;引入一些通用业务能力委托给基座加载,减少模块的集成和运维层面的负担。我们之所以把他比作应用服务市场,是因为我们仍然认为模块应该被松绑,基座提供的能力是可以按需选购,且轻量使用的。</p><p>从单应用研发,到随意从市场选购,复用可以很敏捷。通过 BaaS 化的市场,已内置、选用、托管等方式把复用成本打到很低。</p><p><img src="/img/remote/1460000044193561" alt="图片" title="图片"></p><p><strong>直击心灵:研发时我们是不是有一些事项是重复性工作、甚至非常想偷懒?</strong></p><p>比如研发过程中做一个模块级应用,势必需要一些通用的切面、工具集、方法执行模板、基础模型类……有些甚至还需要自己设计一把,还可能需要集成参数中心等较重的配置平台,如果已经内置好了研发体验会非常舒适。</p><p>得益于模块和基座在运行时共享一个 Java 进程,所以我们可以非常方便地为模块提供无感的集成体验。</p><h2><strong>5. 基座 Owner 的自我修养</strong></h2><p>如果你已经决定选型或者已经使用 SOFAServerless 了,可以继续查看本章。本章将<strong>资金近两年 Serverless 的落地经验浓缩成</strong> <strong>基座 Owner 的自我修养</strong>。</p><p>在新的应用架构下,将原先的系统 Owner 拆分成基座 Owner 和业务模块 Owner 两类角色。业务的 Owner、基座的 Owner 存在上下游服务和供需关系,基座 Owner 要服务好模块客户、解决模块客户问题,让业务模块开发者能够享受到基座建设的红利,进而更快速支撑业务增长。</p><p>以前的系统 Owner,只需要关心单应用的稳定性、容量评估、演练、应用的中间件升级等。但是当我们做了基座和模块的拆分之后,也带来了一些新的问题。因为新的研发模式,引入了热部署框架、场景服务托管框架、跨模块通讯等新的研发框架需要去理解,相比原先的系统 Owner,对基座 Owner 也有了一些新的角色要求。我们将资金这两年基座 Owner 走过的路和经验总结为 4 个代名词——<strong>效能官、技术布道师、应用架构师、治理同行者</strong>。</p><p><img src="/img/remote/1460000044193562" alt="图片" title="图片"></p><h3><strong>业务模块研发提效的效能官</strong></h3><p>面向场景交付提效,实际上对基座管理员有更高的要求,这个变化的来源于模块研发者需要一整套敏捷研发、轻运维、高质量保证的交付模式,这就需要让基座提供更多的能力、更便捷的运维能力、更好的统一防控。新的应用架构下,我们愿景是场景的研发者可以只关心场景服务,一切与场景模块研发提效相关的命题都可以由基座管理员的去承担托管。</p><p>基于交付提效的目标,我们需要下探一下,需要一个可刻画、可跟踪、可优化的全链路研发和协作机制。</p><p><img src="/img/remote/1460000044193563" alt="图片" title="图片"></p><h4>研发洞察体系</h4><p>说“提效”并不是盲目拍一个目标,而是一个有数据指标刻画以及有追踪低效的手段,所以首先就需要定义研发效能的度量。因此,我们解剖了交付过程,然后通过“湖水岩石效应”的方法论,来圈定我们改进的方向。因此在 2022 年,我们和研发效能一起探索了资金业务全链路研发洞察体系,发现了很多的效能短板,我们也基于指标的牵引,完成了很多针对性成片的建设。</p><p><img src="/img/remote/1460000044193564" alt="图片" title="图片"></p><p>我们把软件开发整个交付周期比作湖的水位,那所有问题都掩藏于水面下方;可能全量问题都看不到,或者只是感觉可能有问题但没那么严重。如果我们想要削减交付周期(<em>降低水位</em>),那水面下的“岩石”就逐渐暴露出来,如需求复杂度的问题、多人协作成本的问题、环境稳定性的问题、研发的理解成本的问题、研发复用的问题、质量回归的问题、跨团队组织沟通的问题……</p><h4>研发提效</h4><p>基于 SOFAServerless 这样一种新的应用架构,首先,对于一个小白用户来说,一定是有一些理解成本的,例如对中间件的理解、对框架的理解。其次,在新研发模式的研发过程中,也有一些“过程成本”,例如代码库从 0-1 的成本、代码调试的成本、非功能性考量的成本……</p><ul><li>由于引入了热部署框架,就需要研发同学理解运维单元、服务发现的生命周期;</li><li>由于框架层面深度使用了 Spring 上下文、跨 ClassLoader 等机制,需要对研发同学的基本功有一定要求;</li><li>由于中间件、框架演进(中间件演进、Serverless 框架演进、基座能力封装演进)带来的编程界面差异,需要研发同学掌握一些编程技巧和问题规避。</li></ul><p>我们也一直在尝试削减 Serverless 引入的新要素带来的负向效能影响:</p><p><strong>在系分阶段:</strong> 我们就非常注重提高同学们对所需技术能力和服务可复用性的认识,提供了成熟度对照表、复用对照分母等,告别“手口相传”这样的低效方式。</p><p><strong>在研发阶段:</strong> 研发的“过程成本”层面,我们一开始面临环境可用率很差、常调飞的问题,通过环境中心上云、迁移九州等一些代际升级解决。后来我们发现了模块初始化建设提效、服务抽象复用的诉求,也做了通用脚手架内置,框架集成等针对性的建设;研发的“理解成本”层面,起初模块研发者使用中间件的编程方式跟之前有一定差异,我们作为客户推动 SOFAServerless 研发框架的研发体验升级,“将复杂留给自己,将简单留给模块研发者”,做了多个版本的框架升级,最终达到常用中间件的原生编程界面的体验。</p><h4>质量风险提效</h4><p>目前处于在 SOFAServerless 质量工具配套适配过程态,我们也在抓紧基座防控体系的建设,确保风险可控,不出大问题。在我们面临一些大版本升级、大项目上线的过程中,很依赖一线质量同学的人肉回归。需要把这部分成本削减下来。所以需要各个阶段的质量工具配套的适配,第一阶段是预期拉平 SOFA 应用,即能够支持 SOFABoot 的质量工具也要能适配 SOFAServerless,主要包括线下研发阶段的的 FlyTest 测试框架集成、预发阶段的仿真回放比、灰度阶段的灰度引流。第二阶段是探索出和 Serverless 要素适配的质量能力,比如智能化回放、动态弹性伸缩、智能变更防御等。</p><h3><strong>团队 Serverless 研发模式的技术布道师</strong></h3><p>Serverless 研发模式对一线研发者的编程方式、服务路由方式、基础运维方式都有一定的变化,这些变化会让一线同学经历“不适应”阶段,基座 Owner 需要具备能力和心理素质,来做技术布道的第一责任人,帮助大家熟悉这些变化。</p><h4>编程界面差异</h4><p><img src="/img/remote/1460000044193565" alt="图片" title="图片"></p><p><strong>SOFA 应用:</strong> 都是基于 SOFA 中间件体系进行研发,包括半模块化、Spring 上下文、中间件 starter 等能力都是蚂蚁 SOFAStack 原生的编程界面。</p><p><strong>SOFAServerless 应用:</strong> 场景化服务的动态发布、中间件的使用、通讯路由等。我们经历过 Linglong SDK 或者基座封装的方式,这个阶段很多服务发布、服务通讯的编程界面都是非标的,对研发者来说还是有一些理解成本的。后续演进为 SOFA-Trigger 框架统一做封装和管理,各类中间件如果按照 SOFA-Trigger 的标准来适配,就能够正常在 SOFAArk 框架下运转。所以在升级了 SOFA-Trigger 的基座上,模块可以原生使用 SOFAStack 编程界面。</p><blockquote><p>举例说明一下,假如我们研发过程中需要使用事务型消息:</p><ul><li>在 SOFA 应用下:我们肯定是使用 MsgBroker 的原生编程界面去实现消息的发送和监听、以及事务的回查;</li><li>在 LinglongSDK 环境下:SOFAServerless 的 LinglongSDK 并不能提供事务消息的能力,我们可能需要通过基座做一层代理和消息中心交互。通过基座和模块的通讯去间接实现模块的事务消息监听、发送、回查;</li><li>在 SOFA-Trigger 环境下:SOFA-Trigger 框架已经能够完全代理消息中间件,做到协助消息中间件在 Serverless 运行时和注册中心进行服务发布、注销的交互。让用户可以使用消息中心 SOFA 的编程注解、配置。</li></ul></blockquote><h4>场景化路由和通讯隔离</h4><p><img src="/img/remote/1460000044193566" alt="图片" title="图片"></p><p>SOFAServerless 具有多 AIG、多场景隔离的特性。AIG 是一个网络层面的切分概念,AIG 之间的机器、域名、服务都是隔离的;场景是一个服务发布管理层面的概念,场景具有动态发布服务的能力,一个模块可以在多个场景内发布出不同服务实现业务隔离。</p><p>我们也做了多业务 AIG 的隔离、多业务场景的拆分,拆出一个通用 AIG 用作多场景混布,主要承载一些低流量、新孵化的业务。一些比较高保、量级大的场景则独占 AIG。例如 C 业务场景几乎是独享 AIG,C 场景和 B 场景在公共集群上混布。所以整体上我们是一种多 AIG 非对等部署的部署形态。当然在这个形态下我们也需要应对服务治理相关的问题。</p><ul><li>在各类型的中间件、流量入口、路由,都有了一些特殊的要点,比如 SOFA-Trigger 框架下,各个 RPC 服务、消息的场景 ID 隔离和路由。</li><li>涉及到跨场景、跨 AIG 的特殊通讯方式。比如一些公共服务,在同 AIG 内要做到 JVM 级别的服务通讯,也要考虑跨 AIG 通讯的情况。</li></ul><h4>更细运维粒度和生命周期</h4><p><img src="/img/remote/1460000044193567" alt="图片" title="图片"></p><p>代码变更的过程、以及运维的过程,最小的研发、运维单元由原来的应用级别缩减到场景级别。这样研发和运维关注点就可以细到只关注自身的服务,尽可能的减少对部署单元、服务器的关注。</p><p>而在单机视角来看,每台服务器只有在基座启动时会受到耗时长的影响,模块的拆装生命周期都是在热部署的环境下运转的。会极大缩小部署和运维过程带来的耗时</p><h3><strong>模块发展规划的业务应用架构师</strong></h3><p>虽然 Serverless 模块是一个面向“轻运维”的一个粒度,但是轻不代表可以无序扩张,无序扩张也会带来一些治理成本,防止架构陷入低成本模块新建陷阱。</p><p>我们在前期解决了各个场景的隔离问题,按照垂直领域拆分出了模块。但是在模块发展到一定规模的时候(<em>资金创新业务在 2022 年末已经有 20+ 模块</em>),大家面临的问题就不仅仅是单一模块的研发过程提效了,而是有大量可复用的服务没有能够复用起来形成烟囱式建设了。公共服务的沉淀又成为了新的挑战,虽然重复建设的基础上做一些归并,选择部分模块增强“东西向”的接口调用承接一些公共服务,这样很可能会有边界分歧;如果把公共服务下沉到基座,就很有可能形成高频热点,让我们的又重回大量迭代竞争的巨石应用时代。</p><p>因此我们对模块的创建也是有一定原则的,当然,任何原则订立下来都是具有二义性的。我们只是在业务动态发展和增长的情况下,尽可能地适配我们的业务发展,对于怎么更好地削减理解成本和运维成本,寻找一个拆分和抽象的折中点。</p><p><img src="/img/remote/1460000044193568" alt="图片" title="图片"></p><blockquote><p>举一个很直观的例子:一个创业公司初期会孵化出很多微服务,但是当微服务发展到一定规模的时候会面临额外的运维成本、研发理解成本、运行时通讯成本……此时就需要集中进行治理,尤其是对可复用的部分的沉淀和抽象。</p><p>这个时候一方面是需要一些原则来约束增量应用的扩张的,另外一方面也要依据这些原则去做存量的模块的架构治理,做好归并和场景化复用。</p></blockquote><h3><strong>蚂蚁研发全链路治理同行者</strong></h3><p>在 SOFAServerless 的新应用架构下,我们也需要关注周边生态和配套,可能部分能力的适配具有一些滞后性的。但是整体上,终态肯定是要追平甚至超越经典 SOFA 体系。所以我们尽可能梳理清楚我们不同的阶段、不同的维度到底需要哪些能力,这些能力是否已经适配;已经适配我们就直接享受这个红利,适配有滞后我们就寻求一些短期方案去过渡。</p><p><img src="/img/remote/1460000044193569" alt="图片" title="图片"></p><p>基座管理员要对配套成熟度梳理清楚,在 SOFAServerless 发展的前期,有一些中间件、二方包、质量配套、技术风险配套对模块研发者来说是有适配滞后性的,所以短期必须需经过一些特殊的机制去封装。且作为基座 Owner,需要有研发体系全链路视角,洞察研发模式变化后对一线模块研发同学研发生命周期过程中的变化,并建立与研发效能团队的通道来消化、推进他们的诉求。</p><p>举个例子,现在质量仿真体系、智能化 CI 自动化用例体系,对于 SOFAServerless 的兼容具备一定滞后性,基座 Owner 需要与相关团队建立链接,并关注进展。</p><p>目前 SOFAServerless 已经解决了大多数的成熟度问题,已经度过了催熟期,对基座 Owner 已经减少了 80% 的工作要求,研发模式已经趋近于稳定成熟。</p><h2><strong>6. 回顾和展望</strong></h2><h3><strong>我们目前取得的成果</strong></h3><p><strong>场景创新技术效率的极致提升——我们基于 SOFAServerless,构建了“资金场景应用中心”系统,颠覆了传统巨石 SOFA 应用做场景化创新的模式。</strong></p><p>我们构建的“资金场景应用中心”平台,采用了上述思路,将业务关注点从 SOFA 应用粒度聚焦到了轻应用粒度,对 Java 运行容器、通用业务能力做了全托管,平台陪伴了业务 idea 不断落地试错、业务创新节奏小步快跑。两年时间,我们业务轻应用模块已经达到 20+,部署次数在月均 2000+ 次。但是我们的每次部署耗时维持在 20 秒左右,相比之前每月开发迭代部署整体耗时减少了 95% 以上;业务之间基于场景的互相隔离机制,也基本上摆脱了传统 SOFA 应用火车式发布频繁抢占。整体研发和发布效率的体感,毫无疑问提升了大家研发幸福度和“今晚早下班”的概率👏。</p><p><img src="/img/remote/1460000044193570" alt="图片" title="图片"></p><h3><strong>回顾落地方法和原则</strong></h3><p>回顾全篇文章,我们从分析问题、定义问题、技术选型、详细设计、落地执行去把我们资金技术 Serverless 提效的思路描绘了出来。需要明确的是,虽然对应用架构的创新和探索是非常激进的一件事,但是创新并不是拿锤找钉,带着“造好的轮子”摸着黑去探索,而是遵循了一些方法论和原则,把优秀的架构设计、优秀的提效特性吃透后做到在业务域平稳落地。</p><p><strong>基座落地前的推演,我们会思考以下几个问题:</strong></p><ul><li>如何做到激进的架构升级同时也兼顾业务的稳定性?</li><li>Serverless 配套拼图如何落地?中间件的配套、质量风险配套、研发过程配套……等相关设施如何建设?</li><li>需要深度思考如何把增量价值放大?以及如何把基座的复利性发挥出来?</li></ul><p><strong>结合资金域的落地过程,我们是这样应对的:</strong></p><p>落地过程面临很多未知问题,初期我们可能只是宏观上做了可行性考量,但是随着对 Serverless 架构的认知提升,我们做架构治理的问题分母也变大了,这些“不确定性问题”带来的边际成本是比较大的。所以我们也遵循了一些的成熟的方法论来牵引整个架构升级的事项推进,初期我们运用了假设验证的方法来削减“不确定性”,通过问题反馈做调整和持续迭代;中期通过战役、跨团队协作去联动各个问题域弥补 Serverless 发展中的过程态短板;后期我们也针对资金基座的下探,去锚定低效问题的治理方向的同时,做精准发力并沉淀一些效能领域的建设。</p><p><img src="/img/remote/1460000044193571" alt="图片" title="图片"></p><ul><li><strong>分时:</strong> 运用 MVP 最小价值交付的方法,先做 poc 假设验证,再进一步在可试错的业务上迭代,最终形成成熟稳定的态势后规模化推广。</li><li><p><strong>分治:</strong> 分拆问题,各自突破。协同了 SOFAServerless 战役的 8+ 团队,把业务面临最紧迫的问题分析清楚并提供输入。</p><ul><li><strong>中间件适配滞后性的问题:</strong> 短期用自身的适配框架度过+长期推进 SOFA-Trigger 适配中间件;</li><li><strong>研发效能工具的问题:</strong> 专题战役给 Linke、Linkw、九州提供了输入并推进解决;</li><li><strong>技术风险配套的问题:</strong> 让仿真回放工具、灰度引流工具、采集框架工具等平台各自带回;</li><li><strong>自身的架构治理:</strong> 包括迁移九州机房、升级 SOFA-Trigger 等代际升级,和 SOFAServerless 团队做了紧密联动。</li></ul></li><li><strong>分层:</strong> 按照自上而下不同的层次发掘提效点,从模块研发的层面、基座管理的层面、风险防控的层面各自去做一些下探,形成一些经验和过程的沉淀。</li></ul><h3><strong>对“轻研发”模式的期待</strong></h3><p>作为轻应用这种研发模式的早期探索者,我们已经把心路历程分享出来,但是与此同时也有一些新的思路。</p><p>虽然蚂蚁的每个域的业务形态不一样,但是“资金场景应用中心”还是比较有代表性的,如果把所有技术产品都按照“类中间件”的方式提炼、抽象成 starter 插件放在插件市场里,顺便把一些问题规避、二方包基线、通用配置等运行时要素也提取出来,我们可以得到一个通用的基座模板。其他域可以基于这个模板,快速实例化构建出一个适合自己业务域的基座,让使用方可以大幅削减基座 0-1 成本, 可以快速丝滑地接入 SOFAServerless。</p><p><strong>我们在 2023 初开了一个脑洞,也和 SOFAServerless</strong> <strong>产品团队贡献了这个想法,如下图所示:</strong></p><p><img src="/img/remote/1460000044193572" alt="图片" title="图片"></p><p><strong>这样还是不够,实际上还是存在多个业务基座各自运维,还可以更进一步。</strong></p><p>比如目前的基座还是由业务团队的基座 Owner 来负责的,且模块研发者可能或多或少还需要接触基座的概念,实际上这部分与业务没太大关系,可以考虑由特定技术团队完全托管掉并持续做技术演进。如果未来有一个通用基座的设想,基座如果能全面 BaaS 化,基础能力的集成、弹性资源的管控、业务流量的管控都能够技术产品化,我们就能够让模块开发者真正专注于业务场景的研发不用关心底层运维事项,让 SRE 能够接管基座的“代运维”,统一运维的基线可以覆盖到 BaaS 化基座。</p><p><img src="/img/remote/1460000044193573" alt="图片" title="图片"></p><p>实际上 SOFAServerless 技术产品也在做一些相关通用基座的试点。如果真正具备通用基座的广泛运用,才可以真正称得上是“轻研发”模式。</p><h3><strong>对 SOFAServerless 架构的期待</strong></h3><p>SOFAServerless 是一种新兴的、高效的应用架构,具有敏捷开发、低复用成本、高灵活性、易隔离等优势,正在逐渐运用到蚂蚁的各个业务领域。但是 Serverless 架构也面临着治理上的挑战,需要建立相应的事件处理、安全性控制、性能测试、成本控制等机制来保证服务的稳定性、可用性和可靠性。</p><p>未来,随着 SOFAServerless 的不断发展,蚂蚁架构部也在做 Serverless 提效样板间推广,在蚂蚁各业务领域中的应用必将越来越广泛。当然,Serverless 架构仍有一些优化空间,需要不断建设以满足不断变化的业务需求,如更灵敏的弹性能力、轻运维托管能力、智能化质量工程能力等还需要持续进行技术创新和探索。我们相信,借助 Serverless 技术的进步和发展,将会为蚂蚁集团未来的业务创新带来更多的机会与可能!</p><h3>SOFAServerless 开源助力企业低成本实现 Serverless</h3><p>SOFAServerless 当前已平稳支撑了蚂蚁集团 42 万核数规模的生产业务,并成功支撑了近 3 年的大小促活动,随着这套技术在蚂蚁内部越来越成熟,凸显了它能够让普通应用以尽可能低的成本平滑演进到 Serverless 架构,并且还很好解决了微服务领域里 4 个痛点问题:</p><ul><li><strong>资源与维护成本之痛:</strong> 存量微服务如果拆分过多,能低成本改造成模块合并部署在一起,从而解决拆分过多带来的企业<strong>资源成本</strong>和<strong>维护成本痛点</strong>。</li><li><strong>研发协作之痛:</strong> 一个应用可以低成本拆分出多个模块,模块间可以并行独立迭代,从而解决业务多人<strong>协作阻塞问题</strong>。</li><li><strong>开发认知之痛:</strong> 通过拆分出基座<strong>屏蔽</strong>了业务以下的基础设施、中间件和业务通用逻辑等部分,从而极大降低了开发者的<strong>认知负荷</strong>,提升了<strong>开发效率</strong>。</li><li><strong>微服务演进之痛:</strong> 模块可以灵活部署,解决了微服务拆分与组织发展灵敏度不一致导致的<strong>协作低效和分工不合理</strong>问题。应用能低成本拆分成多个模块,可以部署在一起,也可以<strong>演进</strong>成独立微服务,同样微服务拆分过多也可以<strong>低成本</strong>改为模块<strong>合并</strong>部署在一起。</li></ul><p>因此,蚂蚁集团开始致力于 SOFAServerless 开源版建设(当前已有 8+ 企业在使用 SOFAServerless 开源版),旨在帮助整个行业去解决上述痛点问题,帮助企业早日实现降本增效!</p><p>欢迎大家关注 SOFAServerless 开源社区,加入我们的社区钉钉群:<strong>24970018417</strong>。更欢迎大家与我们来一起共建,一起创造价值解决行业痛点!</p><h2>了解更多...</h2><p><strong>SOFAServerless Star 一下✨:</strong></p><p><a href="https://link.segmentfault.com/?enc=xGBVMdWeKbjHGYT9T06xLQ%3D%3D.Vm%2BSzUYz69Le9oyOuHUjEBgJoj8nYTEiaKb%2B9f5mUOEb1dJOb%2B5qCL6P4rBJ8NL7" rel="nofollow">https://github.com/sofastack/sofa-serverless</a></p><h2><strong>推荐阅读</strong></h2><p><a href="https://link.segmentfault.com/?enc=xYRLyPXYuzekg0q3yWOSVA%3D%3D.2GESQDr6T3NLqQiYw3wXbQd3PkXs93Ak8fgC9VwEyzyu8IYgAVjsXRVQJs6f698%2BCdcTAzblHk1mc5Yl3nz9F42hC2y5Ih%2FT%2F%2FSYLEWuNFW57jZkcqlwl%2BHTNciYZjv2tqq8aJeFwx9epWjdUrYoMfEf7h1%2BtZRMhMRh2saCHH7CtA9PM%2FzycsmYnM%2Bl4tyDMjlgTjup61mFY8ZDmXVtySSbcdTopfVsmHfCHN%2FzBIIK7Jqnwvbmlei0FDq4yNsKj0zsL6c2S9BdawwTgwXA7Q%3D%3D" rel="nofollow">超越边界:FaaS 的应用实践和未来展望</a></p><p><a href="https://link.segmentfault.com/?enc=x7jRc4UNa0NXK1%2F7rEYtXQ%3D%3D.2Ahu1tA4rhWKffL4DxqxoCSlKcXvF1Wq3Ad7%2BwaXxCbFlk09GYAE%2BN0CNqruoSZ3V%2BvdCgDz3JqRrFXKMCa9JNfc1EIwKrU5g3E6HOMYVEUIxgwkQk3yI7CC26rS48NUQN9ZrrPy7bbOKJHqAwhzVecrqytlOBeIBGJEFSYr4bs3L12pt8F0g8i0tQMqfjP%2F2IhqiVCg8Kq%2Bph4dax4hlRhTdBRAYUVEycbN3vV8%2FsEe2imXgk4VN6MPRJIlQPJB6S2YB305Cj75wtQpq0K%2F1w%3D%3D" rel="nofollow">蚂蚁 SOFAServerless 微服务新架构的探索与实践</a></p><p><a href="https://link.segmentfault.com/?enc=EjMzarhY%2BgWUyypTzXMTbg%3D%3D.%2BLA7vWbl3OEo6xL7tVpapDv1%2Fyzzk8IbacaMGHJYWYas6TiANyGqB3X8UrEnWvzwTz%2Fjz2o8%2BdDQgY2p1iAGzCB%2FYQ2jen%2FQS%2B0Bnv%2FtTsFFBr3wyJwLOch8Wfkui%2B2iCiCctHNxKxdyCbjPPDukdz2lqwN2t6w%2FanXhiqACK1nPeAHQwWr7%2F%2BBdYvoIbVJ0Qhl4KUbPWTnha3z%2BGH5wKikOvoV%2B%2BmI%2FB1E7lXGnhzXEe%2BBBOGpbgzCyE%2FOtJGDGWS13fBGTVEWKXgWBzSkIRQ%3D%3D" rel="nofollow">SOFABoot 4.0 正式发布,多项新特性等你来体验!</a></p><p><a href="https://link.segmentfault.com/?enc=iJCXKJbKoNT3v%2FZPwspgow%3D%3D.JjDpEMI9Q44Oq11W8g5m5xED7bz5N4iJcLtu8grSawflf3TDdwxBlyIaVVoOU8I6DzV30nQ0%2FijQ4H4Bkici4UF6uDSFrQf7IStYFgk9p73dwJoCPVXDh8Clz6G7VdmBJ58JNXIRPSZlo6ea9d6vhw1KoMTaXbQqTSsCR2yuE8qMmYxs83Pona4SZgP7dyevi3qH474sPUZBdv2wMPE1GgUPO3%2F7xLUNxFz5OYU8rwTlAgSDGSWn%2BOAcw6X73XQwiHzrWUiZzLQyx4nkeU7Wxg%3D%3D" rel="nofollow">MoE 系列(五)|Envoy Go 扩展之内存安全</a></p>
Dragonfly 在 Kubernetes 多集群环境下分发文件和镜像
https://segmentfault.com/a/1190000044167161
2023-08-31T09:30:00+08:00
2023-08-31T09:30:00+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<h2>作者简介</h2><p><strong>戚文博(百蓦)</strong></p><p>Dragonfly Maintainer,蚂蚁集团软件工程师</p><p><em>主要负责「基于 P2P 的文件分发以及镜像加速系统」。</em></p><p><strong>本文 2036 字 阅读 8 分钟</strong></p><p>Dragonfly 提供高效、稳定、安全的基于 P2P 技术的文件分发和镜像加速系统,并且是云原生架构中镜像加速领域的标准解决方案以及最佳实践。现在为云原生计算机基金会(<em>CNCF</em>)托管作为孵化级(<em>Incubating</em>)项目。</p><p>文章主要阐述如何在多集群环境下部署 Dragonfly。一个 Dragonfly 集群管理一个单独网络环境的集群,如果有两个集群是相互隔离的网络环境,就需要有两个 Dragonfly 集群管理各自的集群。</p><p>推荐用户在多 Kubernetes 集群场景下,使用一个 Dragonfly 集群管理一个 Kubernetes 集群,二者 1:1 关系。并且使用一个中心化的 Manager 服务去管理多个 Dragonfly 集群。因为对于 Dragonfly,一个 Dragonfly 集群中的所有 Peers 只能在当前 Dragonfly 集群内 P2P 传输数据,所以一定要保证一个 Dragonfly 集群中的所有 Peers 网络是互通的。那么如果一个 Dragonfly 集群管理一个 Kubernetes 集群,那么代表集群内的 Peers 只在 Kubernetes 集群维度进行 P2P 传输数据。</p><p><img src="/img/remote/1460000044167163" alt="图片" title="图片"></p><h2><strong>准备 Kubernetes 集群</strong></h2><p>如果没有可用的 Kubernetes 集群进行测试,推荐使用 Kind[1]。</p><p>创建 Kind 多节点集群配置文件 kind-config.yaml,配置如下:</p><pre><code>kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
extraPortMappings:
- containerPort: 30950
hostPort: 8080
labels:
cluster: a
- role: worker
labels:
cluster: a
- role: worker
labels:
cluster: b
- role: worker
labels:
cluster: b</code></pre><p>使用配置文件创建 Kind 集群:</p><pre><code> kind create cluster --config kind-config.yaml </code></pre><p>切换 Kubectl 的 context 到 Kind 集群:</p><pre><code> kubectl config use-context kind-kind</code></pre><h2><strong>Kind 加载 Dragonfly 镜像</strong></h2><p>下载 Dragonfly latest 镜像:</p><pre><code> docker pull dragonflyoss/scheduler:latest
docker pull dragonflyoss/manager:latest
docker pull dragonflyoss/dfdaemon:latest</code></pre><p>Kind 集群加载 Dragonfly latest 镜像:</p><pre><code> kind load docker-image dragonflyoss/scheduler:latest
kind load docker-image dragonflyoss/manager:latest
kind load docker-image dragonflyoss/dfdaemon:latest</code></pre><h2><strong>创建 Dragonfly 集群 A</strong></h2><p>创建 Dragonfly 集群 A,应该使用 Helm 在当前集群内安装中心化的 Manager、Scheduler、Seed Peer、Peer。</p><h3>基于 Helm Charts 创建 Dragonfly 集群 A</h3><p>创建 Helm Charts 的 Dragonfly 集群 A 的配置文件 charts-config-cluster-a.yaml,配置如下:</p><pre><code> containerRuntime:
containerd:
enable: true
injectConfigPath: true
registries:
- 'https://ghcr.io'
scheduler:
image: dragonflyoss/scheduler
tag: latest
nodeSelector:
cluster: a
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
seedPeer:
image: dragonflyoss/dfdaemon
tag: latest
nodeSelector:
cluster: a
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
dfdaemon:
image: dragonflyoss/dfdaemon
tag: latest
nodeSelector:
cluster: a
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
manager:
image: dragonflyoss/manager
tag: latest
nodeSelector:
cluster: a
replicas: 1
metrics:
enable: true
config:
verbose: true
pprofPort: 18066
jaeger:
enable: true</code></pre><p>使用配置文件部署 Helm Charts 的 Dragonfly 集群 A:</p><pre><code> $ helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/
$ helm install --wait --create-namespace --namespace cluster-a dragonfly dragonfly/dragonfly -f charts-config-cluster-a.yaml
NAME: dragonfly
LAST DEPLOYED: Mon Aug 7 22:07:02 2023
NAMESPACE: cluster-a
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the scheduler address by running these commands:
export SCHEDULER_POD_NAME=$(kubectl get pods --namespace cluster-a -l "app=dragonfly,release=dragonfly,component=scheduler" -o jsonpath={.items[0].metadata.name})
export SCHEDULER_CONTAINER_PORT=$(kubectl get pod --namespace cluster-a $SCHEDULER_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
kubectl --namespace cluster-a port-forward $SCHEDULER_POD_NAME 8002:$SCHEDULER_CONTAINER_PORT
echo "Visit http://127.0.0.1:8002 to use your scheduler"
2. Get the dfdaemon port by running these commands:
export DFDAEMON_POD_NAME=$(kubectl get pods --namespace cluster-a -l "app=dragonfly,release=dragonfly,component=dfdaemon" -o jsonpath={.items[0].metadata.name})
export DFDAEMON_CONTAINER_PORT=$(kubectl get pod --namespace cluster-a $DFDAEMON_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
You can use $DFDAEMON_CONTAINER_PORT as a proxy port in Node.
3. Configure runtime to use dragonfly:
https://d7y.io/docs/getting-started/quick-start/kubernetes/
4. Get Jaeger query URL by running these commands:
export JAEGER_QUERY_PORT=$(kubectl --namespace cluster-a get services dragonfly-jaeger-query -o jsonpath="{.spec.ports[0].port}")
kubectl --namespace cluster-a port-forward service/dragonfly-jaeger-query 16686:$JAEGER_QUERY_PORT
echo "Visit http://127.0.0.1:16686/search?limit=20&lookback=1h&maxDuration&minDuration&service=dragonfly to query download events"</code></pre><p>检查 Dragonfly 集群 A 是否部署成功:</p><pre><code> $ kubectl get po -n cluster-a
NAME READY STATUS RESTARTS AGE
dragonfly-dfdaemon-7t6wc 1/1 Running 0 3m18s
dragonfly-dfdaemon-r45bk 1/1 Running 0 3m18s
dragonfly-jaeger-84dbfd5b56-fmhh6 1/1 Running 0 3m18s
dragonfly-manager-75f4c54d6d-tr88v 1/1 Running 0 3m18s
dragonfly-mysql-0 1/1 Running 0 3m18s
dragonfly-redis-master-0 1/1 Running 0 3m18s
dragonfly-redis-replicas-0 1/1 Running 1 (2m ago) 3m18s
dragonfly-redis-replicas-1 1/1 Running 0 96s
dragonfly-redis-replicas-2 1/1 Running 0 45s
dragonfly-scheduler-0 1/1 Running 0 3m18s
dragonfly-seed-peer-0 1/1 Running 1 (37s ago) 3m18s</code></pre><h3>创建 Manager REST 服务的 NodePort Service 资源</h3><p>创建 Manager REST 服务的配置文件 manager-rest-svc.yaml,配置如下:</p><pre><code> apiVersion: v1
kind: Service
metadata:
name: manager-rest
namespace: cluster-a
spec:
type: NodePort
ports:
- name: http
nodePort: 30950
port: 8080
selector:
app: dragonfly
component: manager
release: dragonfly</code></pre><p>使用配置文件创建 Manager REST 服务的 Service 资源:</p><pre><code> kubectl apply -f manager-rest-svc.yaml -n cluster-a</code></pre><h3>访问 Manager 控制台</h3><p>使用默认用户名 root,密码 dragonfly 访问 localhost:8080 的 Manager 控制台地址,并且进入控制台。</p><p><img src="/img/remote/1460000044167164" alt="图片" title="图片"></p><p><img src="/img/remote/1460000044167165" alt="图片" title="图片"></p><p>在 Dragonfly Manager 部署成功后,默认情况下 Dragonfly Manager 在第一次启动的时候,如果没有任何集群,那么会自动创建集群 A 的记录。用户可以点击 Manager 控制台看到集群 A 的详细信息。</p><p><img src="/img/remote/1460000044167166" alt="图片" title="图片"></p><h2><strong>创建 Dragonfly 集群 B</strong></h2><p>创建 Dragonfly 集群 B,需要在 Manager 控制台首先创建 Dragonfly 集群记录,然后再使用 Helm 安装 Scheduler、Seed Peer 和 Peer。</p><h3>Manager 控制台创建 Dragonfly 集群 B 的记录</h3><p>点击 <code>ADD CLUSTER</code> 按钮创建集群 B 的记录,注意 IDC 设置为 cluster-2 使其能够跟后面 Peer 配置文件中 IDC 值为 cluster-2 的 Peer 相匹配。</p><p><img src="/img/remote/1460000044167167" alt="图片" title="图片"></p><p>创建 Dragonfly 集群 B 记录成功。</p><p><img src="/img/remote/1460000044167168" alt="图片" title="图片"></p><h3>使用 Scopes 配置区分 不同 Dragonfly 集群</h3><p>Cluster 管辖的 Scopes 信息。Peer 会根据 Dfdaemon 启动的配置文件 host.idc、host.location 以及 host.advertiseIP 的内容上报给 Manager。然后 Manager 选择跟 Cluster Scopes 中 IDC、Location 以及 CIDRs 匹配的 Cluster。被选中的 Cluster 会提供自身的 Scheduler Cluster 和 Seed Peer Cluster 对当前 Peer 进行服务。这样可以通过 Scopes 来区分不同 Cluster 服务的 Peer 群,在多集群场景中非常重要。Peer 的配置文件可以参考文档 dfdaemon config[2]。</p><p>如果 Peer 的 Scopes 信息和 Dragonfly 集群匹配,那么会优先使用当前 Dragonfly 集群的 Scheduler 和 Seed Peer 提供服务。也就是说当前 Dragonfly 集群内的 Peer 只能在集群内部进行 P2P 传输数据。如果没有匹配的 Dragonfly 集群,那么使用默认的 Dragonfly 集群提供服务。</p><p><strong>Location:</strong> Cluster 需要为该 Location 的所有 Peer 提供服务。当对 Peer 配置中的 Location 与 Cluster 中的 Location 配时,Peer 将优先使用 Cluster 的 Scheduler 和 Seed Peer。用“|”分隔,例如“地区|国家|省|城市”。</p><p><strong>IDC:</strong> Cluster 需要服务 IDC 内的所有 Peer。当 Peer 配置中的 IDC 与 Cluster 中的 IDC 匹配时,Peer 将优先使用 Cluster 的 Scheduler 和 Seed Peer。IDC 在 Scopes 内的优先级高于 Location。</p><p><strong>CIDRs:</strong> Cluster 需要为 CIDR 中的所有 Peer 提供服务。当 Peer 启动时,将在 Peer 配置中使用 Advertise IP,如果 Peer 配置中的 Advertise IP 为空, 则 Peer 将自动获取 Expose IP 作为 Advertise IP。当 Peer 上报的 IP 与 Cluster 中的 CIDR 匹配时,Peer 将优先使用 Cluster 的 Scheduler 和 Seed Peer。CIDR 在 Scopes 内的优先级高于 IDC。</p><h3>基于 Helm Charts 创建 Dragonfly 集群 B</h3><p>创建 Helm Charts 文件的内容可以在 Manager 控制台对应的 Dragonfly 集群信息详情中查看。</p><p><img src="/img/remote/1460000044167169" alt="图片" title="图片"></p><ul><li><code>Scheduler.config.manager.schedulerClusterID</code> 是 Manager 控制台的 cluster-2 集群信息中的 <code>Scheduler cluster ID</code> 值。</li><li><code>Scheduler.config.manager.addr</code> 是 Manager 的 GRPC 服务地址。</li><li><code>seedPeer.config.scheduler.manager.seedPeer.clusterID</code> 是 Manager 控制台的 cluster-2 集群信息中的 <code>Seed peer cluster ID</code> 值。</li><li><code>seedPeer.config.scheduler.manager.netAddrs[0].addr</code> 是 Manager 的 GRPC 服务地址。</li><li><code>dfdaemon.config.host.idc</code> 是 Manager 控制台的 cluster-2 集群信息中的 IDC 值。</li><li><code>dfdaemon.config.scheduler.manager.netAddrs[0].addr</code> 是 Manager 的 GRPC 服务地址。</li><li><code>externalManager.host</code> 是 Manager 的 GRPC 服务的 Host。</li><li><code>externalRedis.addrs[0]</code> 是 Redis 的服务地址。</li></ul><p>创建 Helm Charts 的 Dragonfly 集群 B 的配置文件 charts-config-cluster-b.yaml,配置如下:</p><pre><code>containerRuntime:
containerd:
enable: true
injectConfigPath: true
registries:
- 'https://ghcr.io'
scheduler:
image: dragonflyoss/scheduler
tag: latest
nodeSelector:
cluster: b
replicas: 1
config:
manager:
addr: dragonfly-manager.cluster-a.svc.cluster.local:65003
schedulerClusterID: 2
seedPeer:
image: dragonflyoss/dfdaemon
tag: latest
nodeSelector:
cluster: b
replicas: 1
config:
scheduler:
manager:
netAddrs:
- type: tcp
addr: dragonfly-manager.cluster-a.svc.cluster.local:65003
seedPeer:
enable: true
clusterID: 2
dfdaemon:
image: dragonflyoss/dfdaemon
tag: latest
nodeSelector:
cluster: b
config:
host:
idc: cluster-2
scheduler:
manager:
netAddrs:
- type: tcp
addr: dragonfly-manager.cluster-a.svc.cluster.local:65003
manager:
enable: false
externalManager:
enable: true
host: dragonfly-manager.cluster-a.svc.cluster.local
restPort: 8080
grpcPort: 65003
redis:
enable: false
externalRedis:
addrs:
- dragonfly-redis-master.cluster-a.svc.cluster.local:6379
password: dragonfly
mysql:
enable: false
jaeger:
enable: true</code></pre><p>使用配置文件部署 Helm Charts 的 Dragonfly 集群 B:</p><pre><code>$ helm install --wait --create-namespace --namespace cluster-b dragonfly dragonfly/dragonfly -f charts-config-cluster-b.yaml
NAME: dragonfly
LAST DEPLOYED: Mon Aug 7 22:13:51 2023
NAMESPACE: cluster-b
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the scheduler address by running these commands:
export SCHEDULER_POD_NAME=$(kubectl get pods --namespace cluster-b -l "app=dragonfly,release=dragonfly,component=scheduler" -o jsonpath={.items[0].metadata.name})
export SCHEDULER_CONTAINER_PORT=$(kubectl get pod --namespace cluster-b $SCHEDULER_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
kubectl --namespace cluster-b port-forward $SCHEDULER_POD_NAME 8002:$SCHEDULER_CONTAINER_PORT
echo "Visit http://127.0.0.1:8002 to use your scheduler"
2. Get the dfdaemon port by running these commands:
export DFDAEMON_POD_NAME=$(kubectl get pods --namespace cluster-b -l "app=dragonfly,release=dragonfly,component=dfdaemon" -o jsonpath={.items[0].metadata.name})
export DFDAEMON_CONTAINER_PORT=$(kubectl get pod --namespace cluster-b $DFDAEMON_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
You can use $DFDAEMON_CONTAINER_PORT as a proxy port in Node.
3. Configure runtime to use dragonfly:
https://d7y.io/docs/getting-started/quick-start/kubernetes/
4. Get Jaeger query URL by running these commands:
export JAEGER_QUERY_PORT=$(kubectl --namespace cluster-b get services dragonfly-jaeger-query -o jsonpath="{.spec.ports[0].port}")
kubectl --namespace cluster-b port-forward service/dragonfly-jaeger-query 16686:$JAEGER_QUERY_PORT
echo "Visit http://127.0.0.1:16686/search?limit=20&lookback=1h&maxDuration&minDuration&service=dragonfly to query download events"</code></pre><p>检查 Dragonfly 集群 B 是否部署成功:</p><pre><code>$ kubectl get po -n dragonfly-system
NAME READY STATUS RESTARTS AGE
dragonfly-dfdaemon-q8bsg 1/1 Running 0 67s
dragonfly-dfdaemon-tsqls 1/1 Running 0 67s
dragonfly-jaeger-84dbfd5b56-rg5dv 1/1 Running 0 67s
dragonfly-scheduler-0 1/1 Running 0 67s
dragonfly-seed-peer-0 1/1 Running 0 67s</code></pre><p>创建 Dragonfly 集群 B 成功。</p><p><img src="/img/remote/1460000044167170" alt="图片" title="图片"></p><h2>使用 Dragonfly 在多集群环境下分发镜像</h2><h3>集群 A 中 Containerd 通过 Dragonfly 首次回源拉镜像</h3><p>在 kind-worker Node 下载 <code>ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code> 镜像:</p><pre><code> docker exec -i kind-worker /usr/local/bin/crictl pull ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code></pre><p>暴露 Jaeger 16686 端口:</p><pre><code> kubectl --namespace cluster-a port-forward service/dragonfly-jaeger-query 16686:16686</code></pre><p>进入 Jaeger 页面 \<<a href="https://link.segmentfault.com/?enc=GvzJESPQoFBNnZK9VjbVwg%3D%3D.74dg1h19TPKRu0NR1C6qtYymGTWKMbnhp1QSTYAwMxI%3D" rel="nofollow">http://127.0.0.1:16686/search</a> >,搜索 Tags 值为 <code>http.url="/v2/dragonflyoss/dragonfly2/scheduler/blobs/sha256:82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399?ns=ghcr.io"</code> Tracing:</p><p><img src="/img/remote/1460000044167171" alt="图片" title="图片"></p><p>Tracing 详细内容:</p><p><img src="/img/remote/1460000044167172" alt="图片" title="图片"></p><p>集群 A 内首次回源时,下载 <code>82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399</code> 层需要消耗时间为 <code>1.47s</code>。</p><h3>集群 A 中 Containerd 下载镜像命中 Dragonfly 远程 Peer 的缓存</h3><p>在 kind-worker2 Node 下载 <code>ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code> 镜像:</p><pre><code> docker exec -i kind-worker2 /usr/local/bin/crictl pull ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code></pre><p>暴露 Jaeger 16686 端口:</p><pre><code> kubectl --namespace cluster-a port-forward service/dragonfly-jaeger-query 16686:16686</code></pre><p>进入 Jaeger 页面 \<<a href="https://link.segmentfault.com/?enc=lB3DjDtQ%2BpSYG4kBLZD%2FLw%3D%3D.5WRfo3EPXlffelMv09Z%2Fwwis4vhsYyYshqeHhV%2F%2Bt3Y%3D" rel="nofollow">http://127.0.0.1:16686/search</a> >,搜索 Tags 值为 <code>http.url="/v2/dragonflyoss/dragonfly2/scheduler/blobs/sha256:82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399?ns=ghcr.io"</code> Tracing:</p><p><img src="/img/remote/1460000044167173" alt="图片" title="图片"></p><p>Tracing 详细内容:</p><p><img src="/img/remote/1460000044167174" alt="图片" title="图片"></p><p>集群 A 中命中远程 Peer 缓存时,下载 <code>82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399</code> 层需要消耗时间为 <code>37.48ms</code>。</p><h3>集群 B 中 Containerd 通过 Dragonfly 首次回源拉镜像</h3><p>在 kind-worker3 Node 下载 <code>ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code> 镜像:</p><pre><code> docker exec -i kind-worker3 /usr/local/bin/crictl pull ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code></pre><p>暴露 Jaeger 16686 端口:</p><pre><code> kubectl --namespace cluster-b port-forward service/dragonfly-jaeger-query 16686:16686</code></pre><p>进入 Jaeger 页面 \<<a href="https://link.segmentfault.com/?enc=iv0F9X%2FDCqLrVLfyAuJmDg%3D%3D.TBpO14KHIFM1ZnWHjBIERlITiflIk7BfDUdtoqig2PM%3D" rel="nofollow">http://127.0.0.1:16686/search</a> >,搜索 Tags 值为 <code>http.url="/v2/dragonflyoss/dragonfly2/scheduler/blobs/sha256:82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399?ns=ghcr.io"</code> Tracing:</p><p><img src="/img/remote/1460000044167175" alt="图片" title="图片"></p><p>Tracing 详细内容:</p><p><img src="/img/remote/1460000044167176" alt="图片" title="图片"></p><p>集群 B 中命中远程 Peer 缓存时,下载 <code>82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399</code> 层需要消耗时间为 <code>4.97s</code>。</p><h3>集群 B 中 Containerd 下载镜像命中 Dragonfly 远程 Peer 的缓存</h3><p>在 kind-worker4 Node 下载 <code>ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code> 镜像:</p><pre><code> docker exec -i kind-worker4 /usr/local/bin/crictl pull ghcr.io/dragonflyoss/dragonfly2/scheduler:v2.0.5</code></pre><p>暴露 Jaeger 16686 端口:</p><pre><code> kubectl --namespace cluster-b port-forward service/dragonfly-jaeger-query 16686:16686</code></pre><p>进入 Jaeger 页面 \<<a href="https://link.segmentfault.com/?enc=ddFQh9HckO%2B53flYHC%2B2bw%3D%3D.P9BnlTpjjfW8YlzfrCmEEoSKsLrnRK6yOm5gEuGP6HQ%3D" rel="nofollow">http://127.0.0.1:16686/search</a> > ,搜索 Tags 值为 <code>http.url="/v2/dragonflyoss/dragonfly2/scheduler/blobs/sha256:82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399?ns=ghcr.io"</code> Tracing:</p><p><img src="/img/remote/1460000044167177" alt="图片" title="图片"></p><p>Tracing 详细内容:</p><p><img src="/img/remote/1460000044167178" alt="图片" title="图片"></p><p>集群 B 中命中远程 Peer 缓存时,下载 <code>82cbeb56bf8065dfb9ff5a0c6ea212ab3a32f413a137675df59d496e68eaf399</code> 层需要消耗时间为 <code>14.53ms</code>。</p><h2><strong>Dragonfly Star 一下✨:</strong></h2><p><strong><em><a href="https://link.segmentfault.com/?enc=tasoAdGz527c03SZGSyJ9A%3D%3D.WotEmx1jeFlX9HU0ElmHG9OSY4xuFiKDaOaY0Iaah%2B5ZHnBwAWfdiHZZPip2TmON" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a></em></strong></p><h2>相关链接</h2><p>[1]Kind:<a href="https://link.segmentfault.com/?enc=%2F3x2pcrPefyuhuS9VJX3mQ%3D%3D.faOej48yPGonHEp1EsIV1VdDM9VU5T6BwdSDaPyozi8%3D" rel="nofollow">https://kind.sigs.k8s.io/</a></p><p>[2]dfdaemon config:<a href="https://link.segmentfault.com/?enc=s5xrYViBbvs4ZfRE2vewXg%3D%3D.n%2FM99uYq2SAUDQ130%2BsD6a4dV58gg%2F02J5aVh5m%2BgUivZ%2F2%2BhdJcyWlmN%2FIO9kcWb%2BBsRB6NiObSTeonhfbJKA%3D%3D" rel="nofollow">https://d7y.io/zh/docs/next/reference/configuration/dfdaemon/</a></p><p>[3]Dragonfly 官网:<a href="https://link.segmentfault.com/?enc=T%2BoSpVYyNEDJx5o7XnHy7g%3D%3D.22eTCYLnS1ViKxZ0miHPPw%3D%3D" rel="nofollow">https://d7y.io/</a></p><p>[4]Dragonfly Github 仓库:<a href="https://link.segmentfault.com/?enc=g0jLNDk1trXI%2F%2FY2ytkdww%3D%3D.MYpIN14OCs3oMzw6KFsME2b2LaGhvx0SWHsjSDzVJYFJxj8BPz22kr42uzbMuEk%2B" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a></p><p>[5]Dragonfly Slack Channel( <em>#dragonfly on CNCF Slack</em>):<a href="https://link.segmentfault.com/?enc=fVXz5z9vK8YpgKOB0Q8SXQ%3D%3D.cOjYij0pVfwvC80HrxUVfYoaJASg%2B6PoSZmstB0LBbUnnmyUMaLeS7%2FKkFVIPHagsl%2FRbu65kVRw2Kvq4t4bWl5BorlUbqk5xTe3cY3LuAo%3D" rel="nofollow">https://cloud-native.slack.com/?redir=%2Fmessages%2Fdragonfly%2F</a></p><p>[6]Dragonfly Discussion Group: <a href="mailto:dragonfly-discuss@googlegroups.com">dragonfly-discuss@googlegroups.com</a> </p><p>[7]Dragonfly Twitter( <em>@dragonfly\_oss</em>):<a href="https://link.segmentfault.com/?enc=5uChru3ukM2cYxQpUq%2BZaw%3D%3D.LBotxmmMrhJKQCjL9O0wBsLOMHvR7CX2JdVKx6%2Bfhel5SMYi4rphHrq4uiFEeNMv" rel="nofollow">https://twitter.com/dragonfly_oss</a></p><p>[8]Nydus Github 仓库: <a href="https://link.segmentfault.com/?enc=apdw3asQLN4D1bl6qZjXSA%3D%3D.6n4Gf8EAPbodo%2F3gCw3vhFhZNtljLDq0PEuhTvCSlPV6dzOsrpt%2BZHFGnRKOEhy6" rel="nofollow">https://github.com/dragonflyoss/image-service</a></p><p>[9]Nydus 官网: <a href="https://link.segmentfault.com/?enc=jrVHer4dHuELyEuEKnok6A%3D%3D.mKl21DoqANteHCeVPD9P53O3306i3%2FmqAYcoYQXCNxo%3D" rel="nofollow">https://nydus.dev/</a></p><h2>推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=l%2BUxuG%2BfZ%2Ble9dlvtbhRjA%3D%3D.zh1NM9MOmnLRIEbgqewlDaQo5DEgy%2FpAeimKjEkduLU1aB9bPx3vBSo17YLN3iRX6ZXeBebwUbbjI24FDeKScg%3D%3D" rel="nofollow">Dragonfly 发布 v2.1.0 版本!</a></p><p><a href="https://link.segmentfault.com/?enc=kWbJ7VC%2BKFK2FPgPfx2LWw%3D%3D.hKtSwKf9B5qHcnyZX9U4TCjfmaWA0Zfq%2BMUxJEloT8Q15xvhq4K%2FS0ZNaczVwlrKt9%2BI7EIGuJS42y1SW7HzeQ%3D%3D" rel="nofollow">Dragonfly 中 P2P 传输协议优化</a></p><p><a href="https://link.segmentfault.com/?enc=Zb67F1jNQwK15IGJuNjHnA%3D%3D.rC8qjzwUYNk%2FU1%2F5luHy8sfhqczXk%2BsHdB%2FGd2jwtFNNxupbY%2FBOf99GR%2F6US6ssIhA9zzkmlHqmz3dSNyu1sg%3D%3D" rel="nofollow">蚂蚁 SOFAServerless 微服务新架构的探索与实践</a></p><p><a href="https://link.segmentfault.com/?enc=Y%2FL4TSrh8cN0OuXPdjetgw%3D%3D.yenzW3yw%2FhgPlEBmH5nyzbrPz4v%2BHbmw42c%2BWy3qaGHuCI4iJUnI68xK3DPpGEBQAwgya5zImjSIxfPrRzcyow%3D%3D" rel="nofollow">超越边界:FaaS 的应用实践和未来展望</a></p>
蚂蚁 SOFAServerless 微服务新架构的探索与实践
https://segmentfault.com/a/1190000044142557
2023-08-23T16:26:00+08:00
2023-08-23T16:26:00+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<h2>作者简介</h2><p><strong>赵真灵(有济)</strong></p><p>蚂蚁集团技术专家,Serverless 和微服务领域专家</p><p>曾负责基于 K8s Deployment 的应用发布运维平台建设、K8s 集群的 Node/pod 多级弹性伸缩与产品建设。当前主要负责应用架构演进和 Serverless 相关工作。同时也是 SOFAArk 社区的开发和维护者以及 KNative 社区的贡献者。</p><p><strong>本文</strong> <strong>3612</strong> <strong>字,预计阅读</strong> <strong>12</strong> <strong>分钟</strong></p><h2>传统微服务架构面临的问题和挑战?</h2><p>应用架构从单体应用发展到微服务,结合软件工程从瀑布模式到当前的 DevOps 模式的发展,解决了可扩展、分布式、分工协作等问题,为企业提供较好的敏捷性与执行效率,给企业带来了明显的价值。但该模式发展至今,虽然解决了一些问题,也有微服务自身的问题慢慢暴露出来,在当前已经得到持续关注:</p><p><strong>1、业务开发者需要感知复杂的基础设施,启动慢(<em>分钟级</em>),研发效率低,运维负担重:</strong></p><p><img src="/img/remote/1460000044142559" alt="图片" title="图片"></p><p>对于基础设施的问题,在服务网格和应用运行时的工作已经取得了一定的成果,但是基础设施到业务开发之间还存在业务通用的部分,这里当前没有一个模式来给予支持。</p><p>当前已经有一些开源项目在尝试解决基础设施的问题,例如服务网格、应用运行时,如 Dapr/Layotto,也都在实际应用中得到了不错的效果。但当前服务网格和应用运行时更多的是将中间件以下下沉到 sidecar,而一个应用一般还包括通用的业务逻辑部分,要让更广泛的业务也能享受到无基础设施的体感,也需要让业务以下(<em>可以把业务层以下的看作基础设施</em>)都能屏蔽。另外当前对于中小企业来说,使用服务网格和应用运行时的成本还是比较高的。</p><p><img src="/img/remote/1460000044142560" alt="图片" title="图片"></p><p><strong>2、拆分微服务的资源与维护成本高:</strong></p><p>拆分后每个子应用都包含公共部分(<em>框架、中间件等</em>),除了同样存在上述第一个问题之外,还需要独占机器资源成本高,如果部分业务萎缩,会面临长尾应用问题,需要承担长期维护的成本。</p><p><img src="/img/remote/1460000044142561" alt="图片" title="图片"></p><p><strong>3、拆分微服务的敏捷度与业务、组织发展的敏捷度不一致,导致如何合理地拆分微服务始终是个老大难的问题:</strong></p><ul><li>拆得多增加了资源和管理成本;</li><li>拆得不够造成协作效率问题。有些是应该拆但没拆,有些是因为业务领域已经较为细分不便再拆,特别在一些中小企业里,可能都没有微服务的配套设施。</li></ul><h2><strong>蚂蚁的解决思路和方案</strong></h2><p>为了解决这些问题,我们对应用同时做了横向和纵向的拆分。纵向拆分:把应用拆分成<strong>基座</strong>和<strong>模块</strong>两层,这两层分别对应两层的组织分工。基座小组与传统应用一样,负责机器维护、通用逻辑沉淀、模块架构治理,并为模块提供运行资源和环境。模块在业务层以下所有的基础设施、应用框架、中间件可以不再关注,聚焦在业务逻辑研发本身;并且采用 jar 包的研发模式,具备秒级的验证能力,让模块开发得到极致的提效。</p><p><img src="/img/remote/1460000044142562" alt="图片" title="图片"></p><p>这可以理解为这套架构的核心模型,核心的能力有两个:<strong>平台化 + 模块化</strong>。模块化是 20 年前 OSGI 就已经提出的概念,从 OSGI 到 JPMS 一直未被抛弃,到最近 Spring Modulith、Service Weaver 等行业里又兴起一些开源框架,它一直在发展;平台化从 2017 年出现在技术雷达到 2023 年被 Gartner 列为十大战略趋势之一,到现在国内的平台工程,不断得到重视和发展。而我们实际上在行业还没有对这两个技术方向充分关注的情况下,就在尝试把他们结合起来,并在蚂蚁内部得到规模化验证和落地,给业务带来极致的降本增效效果。</p><p>该模式的另一个特点是<strong>可演进、可回滚</strong>。这里的模块随着业务发展壮大,可以独立部署成微服务;如果微服务拆分过多,可以低成本改造成模块,合并部署在一起,解决资源成本和长期维护成本。实际上可以理解为我们是在单体应用架构和传统微服务架构中间,增加了一个可以演进过渡的架构。</p><p><img src="/img/remote/1460000044142563" alt="图片" title="图片"></p><p>总结下来这套新微服务架构可以解决这四个问题:</p><p>1、横向拆分出基座屏蔽业务以下的基础设施、框架、中间件和业务通用逻辑等部分,从而极大降低了业务开发者的认知负荷、提高了开发效率。</p><p>2、一个应用可以低成本改造或拆分出多个模块,模块间可以并行独立迭代,从而解决了多人协作阻塞问题,每个模块不单独占用机器资源,没有拆分的机器成本问题。</p><p>3、存量微服务如果拆分过多,可以低成本改造成模块应用,合并部署在一起,解决拆分过多带来的资源成本和维护成本痛点。</p><p>4、模块可以灵活部署,解决微服务拆分与组织发展灵敏度不一致导致的协作低效与分工不合理问题。应用拆分出多个模块,可以部署在一起,也可以进一步演进成独立微服务,同样如果微服务拆分过多,也可以低成本改回模块合并部署到一起。</p><p>这里卖个关子——<em>为什么这些技术在蚂蚁能规模化落地?存量的业务 owner 在业务迭代进度和升级新架构之间做权衡时,我们做了哪些工作?</em> 欢迎来到 9 月 3 号 QCon 大会现场获得更详细的信息。</p><p><img src="/img/remote/1460000044142564" alt="图片" title="图片"></p><h2><strong>在采用新的微服务架构模式后的成果</strong></h2><p>举个当前蚂蚁实际业务采用新模式前后的对比数据:</p><p><img src="/img/remote/1460000044142565" alt="图片" title="图片"></p><p>可以看到这些数据是十倍级以上的提升,当前蚂蚁所有 BU 都已经接入,将近 40W core 的在线业务,并为两种业务模式:中台模式和轻应用模式的业务都提供秒级研发运维的能力。一个基座上面最多有上百个模块,一个开发同学在研发验证阶段,一下午可以验证上百次,需求的交付效率最快可以到小时级别。</p><h2><strong>在当下行情下,新技术落地的挑战与蚂蚁的思路</strong></h2><p>当前行情下,企业对新技术会更加谨慎,技术人也对新技术采取保守态度。新技术虽然很酷,但投入大落地场景有限。这其实是发展过程的转换,在高速发展的行情下,一方面是历史包袱少,另一方面是乐观态度占据主导,更加相信新技术能较快得到规模化落地,整个社会都对新技术充满热情。而在当下阶段,很多企业已经有一定的历史包袱,时间证明新技术规模化落地需要很长的周期,需要整个体系一起演进才可能达到最初的预想,可能也会带来越来越繁复的基础设施,所以当前行业对新技术更加偏保守也是非常合理的。</p><p>所以蚂蚁在建设这套微服务新架构时,有一个非常关键的设计思路,那就是要<strong>接地气</strong>或者是<strong>可演进</strong>,也即是要让存量业务能低成本接入。这也是最初蚂蚁在落地该模式时踩过的最大的坑:一个普通应用转换成基座需要花费上月时间(<em>包括流量迁移</em>),模块研发与现有基础设施不匹配导致模块研发成本也很高,这个问题在当时也影响了该模式的生死存亡。后来蚂蚁在这块上投入了很大精力,最终让普通应用在小时内可以成为基座或模块,研发模式也与普通应用基本一致。</p><p>经过这个过程,最终低成本、可演进也成为了该模式的一个核心优势。未来对外开源,我们会把接地气做得更加彻底,不对企业的基础设施程度有预设条件:</p><ul><li>无需容器化也可以接入;</li><li>无需使用 K8s 平台也可接入;</li><li>无需具备微服务配套设施可也接入;</li><li>无需服务网格化也可接入。</li></ul><p><img src="/img/remote/1460000044142566" alt="图片" title="图片"></p><h2><strong>微服务新架构落地实战中遇到的更具体的困难和挑战</strong></h2><p>我们做的这套模式在行业内没有先例,相当于是在无人区里摸索,因此面临多方面的挑战:</p><p>1、关于模块化技术的质疑:为什么现在模块化技术又开始被关注?为什么我们基于 SOFAArk 的模块化技术能推广?挑战主要集中在如何制定合理的隔离和共享通信策略,我们需要避免 OSGI 之类的复杂度问题,做到可以低成本使用。</p><p>2、模块化技术采用了多 ClassLoader,对于 ClassLoader 的隔离、卸载不干净等问题,我们一步一个脚印,深入并体系化分析底层问题,制定各种问题的解法,需要用实际效果证明多 ClassLoader 的问题对业务的影响能否控制在可控可接受范围内。</p><p>3、不同于传统应用发布运维调度是建立在机器维度上的,我们在机器维度之上做了三层运维调度。这里成熟的配套能力需要多团队协作共同推动建设:运维能力、机器分组、流量分组调拨、监控、日志、trace、风险防御等都有全新的建设,而这些在蚂蚁现有的技术体系里,与现有的基础设施不匹配,有很多的适配改造、多团队协作推动工作。</p><p>4、存量业务在快速迭代的压力下为何会选择接入这套新的模式?做到低成本是影响用户是否愿意接入的关键。我们在低成本上做了大量工作:基座的改造、存量的应用改造成模块、存量的应用拆分成多个模块等。</p><p>5、这套模式对业务应用的分层,需要业务方团队的配合调整,其中的用户心智培养和宣讲,需要有一个过程。</p><h2>总结蚂蚁落地该模式的经验和启示以及未来微服务领域的发展趋势和展望</h2><p>一个新的模式不是一蹴而就的,更不是一夜之间就提出的。新模式的出现一般是在前人探索的基础上,用新的思路方法,保持解决问题的初心坚持下去,最终慢慢成型的。</p><ul><li>当前在解决基础设施屏蔽上,从 Docker 到 Kubernetes 到 sidecar 到应用运行时等方向在发展,这里更多是从底层向上层的发展。而我们实际上可以从另一个方向,也就是自上而下地来考虑建设,我们直接从应用这层做了纵向的拆分,把业务以下的所有部分打包成基座这层,基座及以下的所有基础设施也就直接对业务开发者屏蔽了。所以相同问题,从不同角度出发可以有新的方法,得到新的效果。</li><li>3 年前的时候还没有那么多对微服务反思的声音,也还没有应用运行时(<em>Dapr</em>)的概念,对模块化技术也更多的是不看好;我们做的事情在行业里没有前人的指引。但我们依旧紧盯业务痛点,也并没有因为困难而采取妥协的策略,比如一个基座上只允许一个模块、一个模块只能使用 SPI 模式。我们实际上走了一条最难的路线,更多的是靠一群人的坚持、业务的理解和认可、组织的包容,才最终在蚂蚁得到规模化的落地。</li></ul><p>当前应用的架构,有两个方向的发展:纵向不断地把业务以下的逻辑和依赖下沉,横向不断地往更细粒度的方向发展。未来 Serverless 会有多种形态,但也是在这两个方向上的发展,例如 BaaS + FaaS 模式。但是存量应用如何使用上这套模式,一直是这个行业里的问题,这个问题既是挑战,也是行业里的机会。我们需要一套能让应用平滑、逐步演进到未来 Serverless 形态的应用架构和平台能力。</p><p>软件架构好比建造一座大厦,是一层一层的沉淀稳定、一层一层的建设。观察 Kubernetes 资源编排这层已经成熟,当前领域里更多是在做 mesh/微服务这层,当这一层未来也成熟稳定时,相信也会出现几个类似 Kubernetes 的产品,这是我们当前的机会,当然其中也充满了挑战。</p><p>今年我们会把我们这套能力对外开源,欢迎有志之士参与共建。关注 <strong>SOFAServerless</strong>,共同解决微服务领域里的问题,让 Serverless 在未来能成为一种普适的技术。</p><p>欢迎 9 月 3 号 来 QCon 大会现场一起探讨<strong>微服务架构新模式</strong>。</p><h2>了解更多...</h2><p><strong>SOFAServerless Star 一下✨:</strong></p><p><a href="https://link.segmentfault.com/?enc=j3hh3AOxB%2FDCsxVgN0i0zg%3D%3D.yMJun%2F7HL7Ft1rTpHGa%2BdkIvVqfZwlJ7Ok6Bo2GYHnG0kYASyfutmq5Oc47lPAYD" rel="nofollow">https://github.com/sofastack/sofa-serverless</a></p><h2>推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=GdtghtzkPnwm37Nwd9iE9w%3D%3D.YEJk5cK6hPvhgGnUv8B%2BAaxe8MLB0d%2B53hasPnY2Q27PtTJZJphc6z2E1U5AGcdIUz%2BArv5f5%2FWQCOERPol5qw%3D%3D" rel="nofollow">超越边界:FaaS 的应用实践和未来展望</a></p><p><a href="https://link.segmentfault.com/?enc=6rfeUYIrVXaTw6FwgEltbQ%3D%3D.hE2xF%2F7XSwtMuiNHHDLMCkqc%2FJbnwThwjn0vweePvwkIG2OR3mqs01emfw9oni6j0ghlhKGu8GW7RXRTg%2FkuOg%3D%3D" rel="nofollow">如何看待 Dapr、Layotto 这种多运行时架构?</a></p><p><a href="https://link.segmentfault.com/?enc=ZCvgwAUdVNc2nhdFWEJDRw%3D%3D.kctKGJCvhN8JpxEs42idloR2vT6PFKKed6%2FzFB1kMVOCozIJoCU7MX51mVyZGjFr8ykw3%2Fgb84dW6NUKVXBJCQ%3D%3D" rel="nofollow">SOFABoot 4.0 正式发布,多项新特性等你来体验!</a></p><p><a href="https://link.segmentfault.com/?enc=o0MshDzpo8XvkJ8ydPpj2Q%3D%3D.j415UR5G4pGZFso%2BzmXdJi1pCs4qAIOpGEfH3Pe%2F7Y%2BW98Uzfw2onCEK8BuH78Y3zipX%2BlP5zTB659srueAwfA%3D%3D" rel="nofollow">MoE 系列(七)| Envoy Go 扩展之沙箱安全</a></p>
超越边界:FaaS 的应用实践和未来展望
https://segmentfault.com/a/1190000044142615
2023-08-23T16:21:28+08:00
2023-08-23T16:21:28+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<h2>作者简介</h2><p><strong>邢奇(薯片)</strong></p><p>蚂蚁集团技术专家,云原生和 Service Mesh 领域专家</p><p>长期从事服务治理和服务发现等相关领域的研究和实践,在 RPC 框架(<em>Dubbo、Spring Cloud 和 SOFARPC 等</em>)方面有源码级的研究和贡献;在 Service Mesh、云原生、容器和 K8s 等方面有深入的研究和实践经验。</p><p>参与了多个开源项目的贡献,包括 MOSN、SOFA、Dubbo 和 Nacos 等。目前担任蚂蚁云开发技术负责人,负责支付宝云开发产品的研发和实践。</p><p><strong>本文 5689 字,预计阅读 16 分钟</strong></p><h2>概述</h2><p>什么是 FaaS ?</p><p>在 ChatGPT 里面输入 FaaS 关键字,得到的结果是:<em>FaaS 是一种云计算服务模型</em>。它允许开发者编写和部署函数,而不需要管理底层基础设施的运行,即 Function as a Service。</p><p><img src="/img/remote/1460000044142617" alt="图片" title="图片"></p><p>同时通过 ChatGPT 可以生成对应的函数代码——</p><p><img src="/img/remote/1460000044142618" alt="图片" title="图片"></p><h2>FaaS 的崛起</h2><p>FaaS 的理念和函数研发模式,为传统的应用模式解决了许多问题,有着超前的优势。</p><h3>传统应用模式的困境</h3><p>在研发态,不管是单体应用还是微服务应用,亦或是 Mesh 架构或者应用运行时,在研发态,开发者除了要关注业务逻辑本身之外,经常会被中间件所打扰,需要配合去做 SDK 升级改造,性能或者功能优化等。同时在使用云产品或者云服务的时候,需要被迫去感知多云的差异。</p><p>在运维态,开发者面临着更重的运维压力。当一个应用上线,开发者需要对这个业务的未来发展进行一个复杂且不确定的容量评估,再去为这个容量去申请对应的资源,最后经过一个复杂的上线流程进行发布。在发布结束之后,开发者还得时刻关注线上流量的变化,去进行不断的扩容和缩容的调整。</p><p>总而言之,整个中间件和基础设施对开发者的打扰是非常严重的:</p><ul><li>应用研发模式的代码耦合严重,复杂度高;</li><li>运维流程繁琐,效率低;</li><li>容量评估一般很难符合真实情况,线上的资源利用率一般都较低,存在着浪费。</li></ul><p>于是 FaaS 函数的研发模式应运而生。</p><p>可以很直观地看到,在传统应用和微服务应用的改造和优化的基础之上,FaaS 希望做得更进一步,更面向未来。以函数为编程对象,用户无需关注应用、机器等数据和基础设施信息。</p><p>通过这样的改变,大大提升研发效能,做到快速开发;并且提高运维效率,提供一站式免运维的 Serverless 平台;最后,函数会随着流量进行创建和销毁,最终降低成本和资源的消耗。</p><p><img src="/img/remote/1460000044142619" alt="图片" title="图片"></p><h3>FaaS 使用场景</h3><p>尽管 FaaS 具有许多优势,但并非所有场景都适合采用函数编程模式。下面介绍一些 FaaS 适用的普遍场景:</p><p><strong>1、BFF 的场景。</strong> 即一些胶水代码(<em>对接多个接口、进行数据的组装和适配等</em>)。胶水代码的逻辑相对简单,但同时需求变化快、生命周期短。对应的应用场景如运营/营销活动等。活动结束之后,就不再有流量进入,也没有必要再进行代码和机器的维护。</p><p><strong>2、事件驱动的场景。</strong> 例如音视频转码,用户上传文件触发任务,或者通过消息触发调度,或者业务上有明显的波峰和波峰的流量特征。</p><p><strong>3、中台型业务。</strong> 例如算法平台的算子。算子计算是非常独立的业务逻辑,但是参与的研发人数非常多,逻辑相对来说不可控,需要有更高的隔离能力。</p><h2>FaaS 落地面临的技术问题</h2><p>FaaS 技术产品的落地,可能会面临以下问题和挑战:</p><p><strong>性能问题:</strong></p><p>1、在传统的微服务架构下,开发者会为 RPC 调用性能进行了大量的优化;在 FaaS 的场景,也需要保证函数调用的性能。</p><p>2、弹性扩缩容的反应时效性。很多 FaaS 产品会采用弹性的模型去采集 CPU、QPS 并发等指标,再通过平台去计算指标,进而进行一些扩容和缩容的操作,时效性很低。</p><p>3、函数启动的速度。函数启动的速度在 FaaS 场景中至关重要,函数容器的创建和启动不仅仅是发布态的事情,而是一个数据面流量的依赖。</p><p><strong>安全问题:</strong></p><p>1、想要充分地利用计算资源以降低成本,其必要的前提就是有效地利用和隔离资源。</p><p>2、代码容器。用户的函数代码跑在容器里面,防止容器逃逸就是重中之重。</p><p>3、相较传统的编程模型而言,FaaS 的编程模型到底是如何屏蔽中间件以及云服务的干扰的呢?</p><h2>蚂蚁 FaaS 技术架构</h2><p>蚂蚁在 FaaS 实践之初设定了 3 个 FaaS 技术架构实践的基本原则。</p><p><strong>原则 1:流量模型。</strong> 蚂蚁的函数容器是随着流量进行创建和销毁的,而不是通过指标数据进行分析的弹性模型。</p><p><strong>原则 2:函数冷启动。</strong> 尽管有 Warm Pool 或者 cache 技术可作选择,但为了最大程度降低成本和利用资源,蚂蚁将目标定为 100ms 以内的极致的冷启动。</p><p><strong>原则 3:安全隔离。</strong> 用户的函数都跑在我们的容器里面,因此必须保证高水位的安全隔离特性。</p><p>其实,蚂蚁在实践 FaaS 技术架构时,有一个总的原则就是 <strong>one request per instance</strong>—极致的情况下,是创建一个函数容器去处理一个请求,类似编程模型创建一个线程去处理一个请求。在这里,创建函数容器就相当于创建一个线程,具有相似的快速、消耗低的优点,同时还有线程所不具备的安全隔离特性。</p><h3>架构说明</h3><h4>组件介绍和功能说明</h4><p><strong>函数网关</strong>:负责对函数请求进行转发和控制,并为每一个请求发起一次容器调度任务。</p><p><strong>容器调度引擎</strong>:负责对容器进行调度,维护容器的整个生命周期,并且可以对函数容器进行并发度和复用等状态控制,同时也负责管理整个集群的函数 Pod 资源池。(<em>函数 Pod 资源池是函数容器运行的一个环境,一个集群内会有 N 多个 Pod 资源池。</em> )</p><p><strong>函数运行时</strong>:函数运行时是 OCI 标准的实现,它负责快速地启动函数容器,并对容器的 runtime 进行有效的控制。</p><p><strong>函数容器</strong>:函数容器可以理解为是函数运行时+runtime+用户代码的一个运行态,用户的函数代码就跑在函数容器中。</p><p><img src="/img/remote/1460000044142620" alt="图片" title="图片"></p><h4>数据面流量和调度流程说明</h4><p>从上图可以看到,所有请求都会通过函数网关进入到函数集群,函数网关会发起一次调度任务:通过容器调度引擎 Scheduler 为这一请求快速分配一个 Pod 资源。然后网关就会把这个流量转发给这个 Pod 资源里面的节点网关,节点网关随即缓存对应的请求,并且等待函数容器启动。同时函数节点调度器会并发地创建函数容器并且为容器挂载函数代码。函数容器在启动完成之后,就会立刻作为一个客户端去节点网关上拉取请求,然后进行业务逻辑处理。</p><p>从上述流程中可以看到,蚂蚁 FaaS 场景中的 Serverless 有了第二层含义——no server(<em>没有 server</em>)。函数容器永远是作为一个 client 去处理请求。这样的方式从设计上就避免了对基础设施环境的依赖,同时减少了需要去打开一些网络端口、处理网络连接的损耗,也不需要像微服务应用那样去做一些 checkhealth 和 readliness 探针等之后才能进行注册,然后再进行服务发现和调用。</p><h3>性能优化实践</h3><h4>函数网关</h4><p>FaaS 函数网关采用 Go 语言进行编写,网络编程模型是通过一个 go routine 处理一个请求的同步编程,比较符合开发者的习惯。同时由于 Go 语言良好的垃圾回收机制和 GPM 调度模型,网关也有了不错的性能。但是随着业务的不断增长,整个网关在高并发下会出现毛刺现象,P99 长尾也比较严重。</p><p>基于以上情况,<strong>新版的 FaaS 函数采用 Go 语言的 Gateway 运行在 C++ 语言的 Envoy 运行时上</strong>。我们知道,越靠近 Linux 原生语言的方式性能越好。同时 Envoy 采用高性能的同步非阻塞网络编程模型,性能更优。所有的函数请求通过 Envoy 的网络接口进入,并且进行网络处理,然后通过 cgo 调用 Go 语言网关实现的 API 接口,这样 Gateway 网关就能在七层去做一些 filter 和路由的逻辑。最后将请求转发给对应的节点网关。节点网关会进行请求的缓存,同时可以收敛网关连接数。同时函数容器在 running 之后会通过 UDS 和节点网关进行交互。</p><p>通过上述优化,可以看到整个函数网关的吞吐上升,并且在同样吞吐的情况下,网关的 CPU 能够下降 50% 以上,而请求耗时下降 30% 左右。</p><p><img src="/img/remote/1460000044142621" alt="图片" title="图片"></p><h4>容器调度引擎</h4><p>HUSE 容器调度引擎,作为蚂蚁容器调度引擎的下一代架构,是专门面向高吞吐、低延迟、低成本、急速启动的 Serverleess 场景而设计的。目前应用在 FaaS 场景,以及一些 batch job、ODPS、大数据等场景。</p><p>为了能够实现高吞吐、低延迟等功能,HUSE 提供了如:多级自适应缓存、高速的协议通讯栈、智能包加载等性能优化,同时也支持高可用和自运维功能。</p><p>在性能上,可以看到 HUSE 容器调度引擎的性能数据,在全集群 1 万 QPS 吞吐压力下,整个 HUSE 的调度耗时 P50 基本上在 21ms 左右,P99 在 50ms 以内。相比于传统的容器调度引擎,有着数量级级别的提升。</p><p><img src="/img/remote/1460000044142622" alt="图片" title="图片"></p><p>全集群 10000 QPS 调度吞吐下,HUSE 可以实现平均 21ms 的容器调度延迟</p><h4>100ms 函数容器冷启动 </h4><p>最后也最重要的一部分,函数容器冷启动性能优化实践。虽然在服务端也做了一些优化,但是服务端整理的耗时本身也不过几毫秒,可优化空间很小。因此整个函数请求耗时的大头还是在函数容器的冷启动上。而关于函数容器冷启动,有四个方面可以进行性能优化。</p><p><img src="/img/remote/1460000044142624" alt="图片" title="图片"></p><p>第一,是<strong>从 Warm P</strong> <strong>ool 模式变成完全的冷启动</strong>。在并发的场景下,cache 技术本身也会占用 CPU 核数和计算资源,并不会有太多的性能提升。</p><p>第二,<strong>容器资源实时分配改为缓存资源</strong>。容器运行依赖一些资源,比如 IP,下载镜像,挂载 volume,设置 cgroup 和 namespace 等。这些资源分配都很慢,基本是分钟级别。而蚂蚁 FaaS 对容器所依赖的所有资源,只要不占用 CPU 和 Mem 的话,都会对其进行缓存,最终将资源分配的速度做到 0ms。</p><p>第三,传统的文件系统,创建、挂载以及访问都比较慢,蚂蚁 FaaS <strong>采用 ROFS 的文件格式</strong>,即 Read Only File System 的方式去进行优化。</p><p>第四,是<strong>容器的启动方式</strong>。标准的 OCI 容器的启动方式是 create+ start,启动速度很慢,而蚂蚁 FaaS 采用了 <strong>checkpoint+restore</strong> 技术进行性能优化。</p><h5>蚂蚁函数运行时介绍</h5><p>容器运行时分为 runC、runD 和 runSC 等不同类型。runC 的安全隔离太差,runD 的启动速度太慢并且资源消耗太高,都不适合 FaaS 的场景。NanoVisor 是蚂蚁 runSC 安全容器。它是 Go 语言编写的安全容器,重构于开源的 gVisor 项目,是为云原生设计优化的、弹性安全容器。它也是轻量级 HyperVisor,进行 syscall interception 和 host syscall 加速。支持多个平台的运行,同时在性能方面尤为出色,可以支持一些火焰图性能分析,并且对 Go Runtime 进行优化,同时引入了高性能的用户态协议栈。目前应用在 FaaS 和一些增强安全的场景。</p><p><img src="/img/remote/1460000044142625" alt="图片" title="图片"></p><h5>ROFS 文件系统优化</h5><p><img src="/img/remote/1460000044142627" alt="图片" title="图片"></p><p>上图中可以看到,一个容器会有两个进程, runSC Sandbox 进程和 Gofer 进程,容器镜像解压成 Rootfs 文件 bindmount 到 Gofer 进程中,用户函数代码也一样。Sandbox 需要访问函数和系统文件,需要经过 syscall 然后被 Gofer 进程拦截和控制。这种方式是为了保证安全,但是多了一个进程占用 CPU 核数,同时函数访问系统文件都需要进行 syscall ,拉低了速度。</p><p>ROFS 文件系统优化,就是将镜像和用户代码都编译成 ROFS 可以解析的格式,并且在沙箱外部打开。同时通过 mmap() 映射到沙箱进程中去。通过这样的方式,可以降低一半的 CPU 占用,同时所有文件的操作都变成了内存操作。不仅更加快速,而且更加安全。</p><h5>容器启动方式优化</h5><p>一个标准的 OCI 容器会提供两个相关的接口:create 和 start。create 是根据容器镜像和配置文件创建容器运行的环境,对应 NanoVisor 容器沙箱的创建和应用程序内核的初始化;start 则是启动容器,对应 NanoVisor 容器沙箱内一号进程(<em>Nodejs Runtime</em>)的创建和启动。这个过程非常慢。</p><p>蚂蚁 FaaS 采用的是 checkpoint + restore 方式进行容器的恢复。首先按照传统方式创建、启动一个容器,等待容器内的 Nodejs Runtime 初始化完成之后,使用 checkpoint 技术对 Nodejs Runtime 进程和应用程序内核 sentry 进行状态、数据的保存。可以理解为对整个容器进行了一个内存快照,然后导出 checkpoint.img 的种子文件。这样的话,下一次有请求过来,直接从 checkpoint.img 种子恢复函数容器,也就是 restore 的过程。所以 restore 就是直接利用之前保存好的进程、内核状态和数据进行恢复,不再需要重新初始化 Nodejs Runtime。在恢复完成之后,Nodejs Runtime 就可以立刻进行业务逻辑处理。</p><p>通过以上的优化,目前 FaaS 函数容器落地的启动速度达到了 90ms 以内,额外的内存开销要小于一兆,这一提升相当可观。其中使用到的 NanoVisor 作为蚂蚁的第三代安全容器,始于安全。因此之后可以期待在性能提高和成本缩减上能够做到十倍甚至百倍的一个提升。</p><p>目前业界的类似产品中,AWS Lambda 函数的冷启动性能是比较好的:在 Node.js 运行时环境,平均冷启动时间为 200ms。</p><p>需要注意的是,此数据仅供参考,实际情况会受多种因素影响。</p><h3>安全能力建设</h3><p>性能优化的同时,安全功能也必须得到保障。以蚂蚁 FaaS 安全功能的建设为参考。函数容器(<em>runSC Sandbox</em>)是一个完全隔离的 runSC 沙箱环境,配置有 ACL 规则和虚拟的 veth pair 网卡。这个网卡是完全虚拟的,没有任何意义,而且在 FaaS 的场景下基础设施从设计上本身就是透明的。网卡的另一端插在 bridge 网桥设备上,并且通过 eBPF 进行高效的网络过滤、控制和转发。因此整个函数容器沙箱是完全隔离的。</p><h4>纵向容器防逃逸</h4><p><img src="/img/remote/1460000044142628" alt="图片" title="图片"></p><p>函数程序运行在虚拟化的 guest 态,它的系统调用会被 sentry(<em>运行在 Guest Kernel 态和 Host Kernel 态</em>) 也就是 NanoVisor 处理和响应。NanoVisor 运行在 Guest Kernel 态和 Host Kernel 态, 处理所有函数实例的系统调用,进行限制和管控。同时 NanoVisor sentry 本身的系统调用也会由 seccomp 进行限制。</p><p>FaaS 的场景基本隔离掉了基础设施信息,因此限制的接口会更多,攻击面会更小更安全。同时 NanoVisor 提供进程级别的 NanoVM 虚拟化技术,是一个轻量级的 VMM 管理,作为 host 上的一个内核模块,可以有效保障内核安全。</p><h4>横向安全能力</h4><p><img src="/img/remote/1460000044142629" alt="图片" title="图片"></p><p>在横向控制上,NanoVisor 也做了很多能力建设,包括对所有的网络操作,包括 accept 一些端口、监听 DNS 请求等,都会进行网络审计。同时基于四层网络的五元组信息,都可以进行 ACL 控制。</p><h4>免鉴权调用 </h4><p><img src="/img/remote/1460000044142630" alt="图片" title="图片"></p><p>隔离之外,互通也值得关注。所有函数都通过函数网关进入,但是用户函数代码也需要访问其他的服务。一些场景比如函数代码里面可以访问其他的函数、访问云服务、访问 DB 和 OSS 等,或者访问公网,或者访问多云的 VPC 或者 IVS 的 VPC。</p><p>在这样的情况下,蚂蚁 FaaS 建设了对应的代理服务和网络设备,在四层为这些服务打开 ACL 控制,同时会在七层进行应用层的认证和授权。认证和授权的过程完全由 runtime 和代理服务实现,整个过程对开发者是完全透明和无感的。所以开发者也不需要设置 IP、账号/密码等信息,这样可以最大限制地屏蔽中间件、基础设施和多云的干扰。</p><h2>体验</h2><p>有着这样的整体架构,再加上性能的优化实践和安全能力建设,蚂蚁 FaaS 的产品使用体验是什么样的呢?</p><p><strong>研发态的体验:</strong> 从创建函数到编写函数到执行函数,基本上几秒钟就能够完成一个函数代码的上线。和传统应用模式对比鲜明,不需要去申请应用创建代码仓库,编写代码,编译打包等。曾经在 7 月的一次活动中,一个六年级的小朋友现场花 5 分钟,就完成了整个支付宝小程序+FaaS 云函数的开发。</p><p><strong>运维体验:</strong> 由于整个蚂蚁 FaaS 的设计都是符合 Serverless 理念,所以这里看不到任何基础设施信息,但是可观测性相关的链路日志指标告警是完全具备的。作为 Serverless 的一站式免运维的平台,它能够自动集成监控和告警功能。</p><h2>总结</h2><p>总之,蚂蚁 FaaS 的改变和优势在于:</p><ul><li>从成本上来说,更小内存开销、更快启动速度。用户只用为流量付费,甚至只用为其函数代码的运行时间付费。(而运行时间可压缩至一个毫秒。)</li><li>更高保的安全隔离,能够进行免鉴权的调用。更快的研发速度、更高效的运维。</li><li>可以快速开发,没有复杂的流程,也没有碎片化的代码版本的困扰。</li><li>完全 Serverless 一站式免运维平台,能够集成监控和告警。</li></ul><h2>展望:FaaS + AI 开启编程新纪元</h2><p>蚂蚁 FaaS 对未来的展望包括对<strong>极致性能</strong>与<strong>极致效能</strong>的追求,将通过 fork 等技术去实现更高的性能,同时通过 AIGC 等智能化方式去达到极致研发效能。</p><h3>极致性能</h3><p><img src="/img/remote/1460000044142631" alt="图片" title="图片"></p><p>在极致性能的实现中,蚂蚁目前正在调研的 fork 技术,函数冷启动时间已经达到了 3.5ms,稳定地跑进 10ms 以内。有着这样的启动速度,函数容器的创建跟创建一个线程一样又快、开销又低。同时 fork 技术还可以进行和 runtime、和 user code 的组合,让启动再加速。</p><h3>极致效能</h3><p>追求极致效能的方式,就是 FaaS+AI。在文章最开始,可以看到 ChatGPT 生成的一段函数代码,只要十几行代码就可以完成一个业务逻辑的处理。所以 AIGC+FaaS 并非纸上谈兵,而是很有前景的,并且将会在不远的未来落地实践。AI+函数开发模式会结合一些低代码平台,并且利用蚂蚁集团的 NLP GPT/ Code GPT/ OpsGPT 等智能化平台,去演进和诞生一些新的产品形态和编程体验。想象未来 PD 或运营通过自然语言,和 AI 平台沟通,由 AI 平台生成一些格式化的 PRD,再输入到类似低代码/无代码的平台,平台一方面集成 AI 的代码生成能力,一方面集成 FaaS 的 Serverless 功能,这样将大大提高研发效能。</p><p><img src="/img/remote/1460000044142632" alt="图片" title="图片"></p><p>在过去的几年里,云原生技术已经改变了整个运维体验。但是直到今天,研发模式持续了二十多年,还是一成不变,开发者们还是在电脑面前苦哈哈地敲着键盘。希望未来 FaaS+AI 能开启编程新纪元,颠覆整个研发体验。</p><h2>FaaS 延伸支付宝云开发</h2><p>FaaS 技术体系在蚂蚁已经十分成熟,今年基于蚂蚁 FaaS 技术体系沉淀,打造了一款支付宝小程序云开发产品,欢迎大家了解试用。</p><p><img src="/img/remote/1460000044142633" alt="图片" title="图片"></p><p>欢迎大家搜索钉钉群号:<em>25600034150</em>,加入支付宝云开发钉钉支持群了解试用:</p><p>产品官网:<a href="https://link.segmentfault.com/?enc=Z6onhrJx%2B%2FkgQqYMmFhC%2FA%3D%3D.16RLYBKYf8JkuuWhpy6lRHxWn23W%2Bp7CwImP9wrvtTZG6lJdApfdU8kdLovAqkMS" rel="nofollow">https://cloud.alipay.com/main/product/cloudbase</a></p><h2>推荐阅读</h2><p>1、<a href="https://link.segmentfault.com/?enc=sefjBtFROVVHwhnTroLlLg%3D%3D.H4YY1XeWc%2FHXRStOrfiSWJf4UstG8kercHqqD8nZAq4gM%2Bnkf0YzgjaACinyAi%2Fr6DjASVh4AsJ9Z07qWpPzMw%3D%3D" rel="nofollow">如何看待 Dapr、Layotto 这种多运行时架构?</a></p><p>2、<a href="https://link.segmentfault.com/?enc=qZffoCl9LlYQZLwLIVfCig%3D%3D.C5Wl0GfvuaGArw5NTlCiuZtXJCmQwUsffRLoeHsk6eSyQLaZ9khlo1WnYsXRpwqRn3Zx1ZaxhIXOB5WwEsYr1Q%3D%3D" rel="nofollow">Service Mesh 的下一站是 Sidecarless 吗?</a></p><p>3、<a href="https://link.segmentfault.com/?enc=TBjKdiH0HPxbwaMyBTfIMQ%3D%3D.QzPMXOMNGFksf5PHTxZkfPEZDf3QD%2FMvV9Cx4ZMxntelyo%2FqAnMN2m0sBEuBFr%2B3AFlFT8lQ2iNd4%2BaE%2BFAcdw%3D%3D" rel="nofollow">MoE 系列(七)| Envoy Go 扩展之沙箱安全</a></p><p>4、<a href="https://link.segmentfault.com/?enc=pZ7ujhpAHYjZseEVgWP%2FTA%3D%3D.dtriQ9fI%2BtLnS5EvS0Z1%2B7H6fd8P3r8PPH3zbiB7MtYPK7vDEeyj9YTzJ2Q3nAX4ib3%2F8lI2Kd9Waxa3uXdEQw%3D%3D" rel="nofollow">Seata-DTX|分布式事务金融场景案例介绍</a></p>
MoE 系列(七)| Envoy Go 扩展之沙箱安全
https://segmentfault.com/a/1190000044076186
2023-08-03T11:05:40+08:00
2023-08-03T11:05:40+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p>在本系列的第 5 篇<a href="https://link.segmentfault.com/?enc=k4KnVKt4x7%2FhQXM%2Bx7SaUQ%3D%3D.b62IpU%2Buq5HboMnI%2Fv29nHiWEEILKPa6GqHNWyAVxWj7QBQrIrxFueiTMsqyrOI2s2v6iSoVO0UMlPVDJQQqtnRf72fQLhCMzyECB4N6hOm%2BjPqpe0Pe6ryGfV5mEuge8PXFe9AIOGHSBp7Uo5GaffedxD4YUql9vmCGoBTe3SnqX6Lwxvx9IAUzRJYw5dBUwI3Eu0MGRdbqT9chltsjCdpogBGWJOWz9BCKsKbEf5dBc0SC067LSSksPggTV39CDMLGeDfZPCDRZoZpiTUGz%2BzlKoi18c8OOUk9stYLhUY%3D" rel="nofollow">《MoE 系列(五)|Envoy Go 扩展之内存安全》</a>中我们介绍了内存安全如何实现。第 6 篇<a href="https://link.segmentfault.com/?enc=QXzDi4IapGlDBeZ5F%2BIvEA%3D%3D.pxn5BAgqDhVU2A7Ibmnd1p0V4w1TJktFvhk9d%2BQQEKKosDh97%2FLJUsjQQXe9oVkh29uHV3mY93ny%2FbEs9NfdoovqMY%2BLMAflnwggDD9JDyCI8v5D8JPv7ppVFu4INzaMAcWkVqKrJygzXo74jV2gItz57NeyGGzVSQZ%2FCqjVPdhWErAc9xIsY3E4xBkUp5FuLNcgaFlfl3Cu7BU%2FlQTM95iOpLqUJrO4XW0SWfWjslGxh6WKr20D3tER2Q079k0xDz0f2vcvfnRU5SBD97oSUJaU0WXN%2BzN3237z13YHN1s%3D" rel="nofollow">《MoE 系列(六)| Envoy Go 扩展之并发安全》</a>又谈到了并发场景下的内存安全。 </p><p>今天,我们来到了安全性的最后一篇:<strong>沙箱安全</strong>,也是相对来说,最简单的一篇。</p><h2>沙箱安全</h2><p>所谓的沙箱安全,是为了保护 Envoy 这个宿主程序的安全,也就是说,扩展的 Go 代码运行在一个沙箱环境中,即使 Go 代码跑飞了,也不会把 Envoy 搞挂。</p><p>具体到一个场景,也就是当我们使用 Golang 来扩展 Envoy 的时候,不用担心自己的 Go 代码写得不好,而把整个 Envoy 进程搞挂了。</p><p>那么目前 Envoy Go 扩展的沙箱安全做到了什么程度呢?</p><p>简单来说,目前只做到了<strong>比较浅层次的沙箱安全</strong>。不过,也是实用性比较高的一层。</p><p>严格来说,Envoy Go 扩展加载的是可执行的机器指令,是直接交给 CPU 来运行的,并不像 Wasm 或者 Lua 一样由虚拟机来解释执行。所以,理论上来说,也没办法做到绝对的沙箱安全。</p><h2>实现机制</h2><p>目前实现的沙箱安全机制,依赖的是 Go Runtime 的 <code>recover</code> 机制。</p><p>具体来说,Go 扩展底层框架会自动地,或者(<em>代码里显示启动的协程</em>)依赖人工显示地,通过 <code>defer</code> 注入我们的恢复机制。所以,当 Go 代码发生了崩溃的时候,则会执行我们注入的恢复策略,此时的处理策略是,使用 <code>500</code> 错误码结束当前请求,而不会影响其他请求的执行。</p><p>但是这里有一个不太完美的点,有一些异常是 <code>recover</code> 也不能恢复的,比如这几个:</p><pre><code>Concurrent map writes
Out of memory
Stack memory exhaustion
Attempting to launch a nil function as a goroutine
All goroutines are asleep - deadlock</code></pre><p>好在这几个异常,都是不太容易出现的。唯一一个值得担心的是 <code>Concurrent map writes</code>,不熟悉 Go 的话,还是比较容易踩这个坑的。</p><p>所以,在写 Go 扩展的时候,我们建议还是小心一些,写得不好的话,还是有可能会把 Envoy 搞挂的。</p><p>当然,这个也不是一个很高的要求,毕竟这是 Gopher 写 Go 代码的很常见的基本要求。</p><p>好在大多常见的异常,都是可以 <code>recover</code> 恢复的,这也就是为什么现在的机制,还是比较有实用性。</p><h2>未来</h2><p>那么,对于 <code>recover</code> 恢复不了的,也是有解决的思路: </p><p>比如 <code>recover</code> 恢复不了 <code>Concurrent map writes</code>,是因为 Runtime 认为 <code>map</code> 已经被写坏了,不可逆了。</p><p>那如果我们放弃整个 <code>runtime</code>,重新加载 so 来重建 <code>runtime</code> 呢?那影响面也会小很多,至少 Envoy 还是安全的,不过实现起来还是比较地麻烦。</p><p>眼下比较浅的安全机制,也足够解决大多数的问题了。</p><h2>MOSN Star 一下✨:</h2><p><em><a href="https://link.segmentfault.com/?enc=4nxn%2BZVtUXitvaN2MY8foQ%3D%3D.Qvp6Qss8T7asufYVgOrwxT9sH2RM0rY3ILt1wGbCMBA%3D" rel="nofollow">https://github.com/mosn/mosn</a></em></p><h2>本周推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=PXmlsmhYM5FIDt5QfIzQdA%3D%3D.CFgmJh9jKhiNa8soeBC4XSSxIHK12qTIUY%2Bw1IOkpri3qBJC0He3px%2BhysnNR5s93IUDbEfMlHhIbrnGK4ds56FtB0i%2FbZsIU%2FQ6hZzYWLezsfD11PtNPchYVgU5cqEk6eIdmniQi%2FxKHF%2F2JH2EfjBKJJAlXDxjlwfkfc7tnP4voUoGt80S%2FdVhh7CfJSZAjy4y%2FULef1lwpZS%2BJHJQ6Rt5bUCYOOxclTIDGfcZhdffMeATGwf3zi5uDvL3y5n32LvgS%2BiMnESJfosSsWyAnQ%3D%3D" rel="nofollow">MoE 系列(一)|如何使用 Golang 扩展 Envoy</a></p><p><a href="https://link.segmentfault.com/?enc=JQnLDtgGAPZOWwISIQivyA%3D%3D.0HDSQJiakxXK9fztWjT2lsG2NKQH5KyVZqNKQEFUmNjuks%2F5wt9EVgmhLYa1PnO6N6yOCFTMk0N%2F9tO5Dog6gFggrKdvpXv3G6QzxUR9LsBDG95MZe67vCs90VtZ40T4fWqOhKhQrw6E5Q1BwrYBJKy0VQbtu2fPy7Z0RYxKhaASHhMpdnn%2FD29F0Pu4AYzidlldK1W5GFRT4UWS915Ujfb0creJfCki40wpS33YXXaimOj7z6otSGfB8Wg7beeXNmw3Ta0Ybk%2FQdO7%2F4e405w%3D%3D" rel="nofollow">MoE 系列(二)|Golang 扩展从 Envoy 接收配置</a></p><p><a href="https://link.segmentfault.com/?enc=F%2FD03w68K%2FICnpbdUjBfEQ%3D%3D.U%2FujmNL3xCckxtqzmknkcvKDo%2FPpAAfj8dXcNFCI%2FiHW3TzQaYqnmIBiA1mOMd7V8hhhN8hZb%2BmM1AI52vFU%2F3Kk74qcNisXcy9CUpUX8FHOy%2B05L6DSrunrN%2BXtvTR%2BXlWU%2Bs4RyKL0DaxeaLrcE5gwGiUzcntJpjtmaoAiVvAel61dbtQhtZ4LJL8Cfe4z7tcvsr1%2FyWQv1PJ4jrZjSabaSpaCeNNoW%2B95W%2FX9lDPuAQAa1dlflc49R1qyJgaakywVZNyhQ8%2B58HkCWrVH%2FA%3D%3D" rel="nofollow">MoE 系列(三)|使用 Istio 动态更新 Go 扩展配置</a></p><p><a href="https://link.segmentfault.com/?enc=%2Fd5NFimlfSPLzhF8JKxqXw%3D%3D.nEO32yHbu%2Bkqv3Cpg3kK0vdEIc5IeKRniZIK90qL5uic1YubbCf2Mm7ZMwaunM5GTj3cori11KkM%2ByYC4gKIP9irI77XTRqs1XLmIdajE2%2B9Tz%2BVfyYdyeMldsqAE7QWUJ91JMvPCAbBimsPlskO%2BehHxj8Q4faJTtnZxPXglzXYMpjpinyEbhi9EIBDEtNLhvl4xey6RCC4wKFM9Cvmw1pwAX7U%2BWkcfMShjGrMejz%2BMjb2t02PyJa1kH1XuRsUI8hPGkid4z0lSQ%2F4ZSZy2A%3D%3D" rel="nofollow">MoE 系列(四)|Go 扩展的异步模式</a></p>
MoE 系列(六)|Envoy Go 扩展之并发安全
https://segmentfault.com/a/1190000044076172
2023-08-03T11:04:28+08:00
2023-08-03T11:04:28+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p>前一篇介绍了 Envoy Go 扩展的内存安全,相对来说,还是比较好理解的,主要是 Envoy C++ 和 Go GC 都有自己一套的内存对象的生命周期管理。 这篇聊的并发安全,则是专注在并发场景下的内存安全,相对来说会复杂一些。</p><h2>并发的原因</h2><p><strong>首先,为什么会有并发呢🤔️</strong></p><p>本质上因为 Go 有自己的抢占式的协程调度,这是 Go 比较重的部分,也是与 Lua 这类嵌入式语言区别很大的点。</p><p>细节的话,这里就不展开了,感兴趣的可以看这篇👉<a href="https://link.segmentfault.com/?enc=GVgfnsBaZh0F2VMr03hiug%3D%3D.SlMPsxT1TMXqBToLNKz%2BhVo1d6G7%2FN0NaGfsRViOdaIMrQnnH%2BXvGot6UfRenny5RqrXsGddS3Ag7nYfCGHOAQYCG8SDZ6HsO8sP6%2BNl273pjv01uCJaUHezwA8%2FPnwk6HpCfEktkGC5zcfkau81aUBYbRZ1qTWAo8tqKk2e1a%2BTBzgXIvx41D5cZq%2F3Yt5jtkA1IFCVw4NmA7BG71pqoQ54eHH%2Bysj8FYRLvAcwQEMgVd3zGF5YvQGjNH9x2s1BwQxaQkcGqlccYPh20D4oGCCbTqyLCkcxNmYHT3ZPChE%3D" rel="nofollow"><em>cgo 实现机制 - 从 C 调用 Go</em></a></p><p>这里简单交代一下,因为 C 调用 Go,入口的 Go 函数的运行环境是,Goroutine 运行在 Envoy worker 线程上,但是这个时候,如果发生了网络调用这种可能导致 Goroutine 挂起的,则会导致 Envoy worker 线程被挂起。</p><p>所以,<strong>解决思路</strong>🪄就是像 Go 扩展的异步模式[1]中的示例一样,新起一个 Goroutine,它会运行在普通的 Go 线程上。</p><p>那么此时,对于同一个请求,则会同时有 Envoy worker 线程和 Go 线程,两个线程并发在处理这个请求,这个就是并发的来源。</p><p>但是,我们并不希望用户操心这些细节,而是在底层提供并发安全的 API,把复杂度留在 Envoy Go 扩展的底层实现里。</p><h2>并发安全的实现</h2><p>接下来,我们就针对 <strong>Goroutine 运行在普通的 Go 线程上</strong>这个并发场景,来聊一聊如何实现并发安全。</p><p>对于 Goroutine 运行在 Envoy 线程上,因为并不存在并发冲突,这里不做介绍。</p><h3>写 header 操作</h3><p>我们先聊一个简单的,比如在 Go 里面通过 <code>header.Set</code> 写一个请求头。</p><p>核心思路是,是通过 <code>dispatcher.post</code>,将写操作当做一个事件派发给 Envoy worker 线程来执行,这样就避免了并发冲突。</p><h3>读 header 操作</h3><p>读 header 则要复杂不少,因为写不需要返回值,可以异步执行,读就不行了,必须得到返回值。</p><p>为此,我们根据 Envoy 流式的处理套路,设计了一个类似于所有权的机制。</p><p>Envoy 的流式处理,可以看这篇👉 搞懂 http filter 状态码[2]。</p><p>简单来说,我们可以这么理解,当进入 <code>decodeHeaders</code> 的时候,header 所有权就交给 Envoy Go 的 C++ 侧了,然后,当通过 cgo 进入 Go 之后,我们会通过一个简单的状态机,标记所有权在 Go 了。</p><p>通过这套设计/约定,就可以安全地读取 header 了,本质上,还是属于规避并发冲突。</p><p>为什么不通过锁来解决呢?因为 Envoy 并没有对于 header 的锁机制,C++ 侧完全不会有并发冲突。</p><h3>读写 data 操作</h3><p>有了这套所有权机制,data 操作就要简单很多了。</p><p>因为 header 只有一份,并发冲突域很大,需要考虑 Go 代码与 C++ 侧的其他 filter 的竞争。</p><p>data 则是流式处理,我们在 C++ 侧设计了两个 buffer 对象,一个用于接受 filter manager 的流式数据,一个用于缓存交给 Go 侧的数据。</p><p>这样的话,交给 Go 来处理的数据,Go 代码拥有完整的所有权,不需要考虑 Go 代码与 C++ 侧其他 filter 的竞争,可以安全地读写,也没有并发冲突。</p><h3>请求生命周期</h3><p>另外一个很大的并发冲突,则关乎请求的生命周期,比如 Envoy 随时都有可能提前销毁请求,此时 Goroutine 还在 Go thread 上继续执行,并且随时可能读写请求数据。</p><p><strong>处理的思路是:</strong></p><p>并没有有效的办法,能够立即 kill Goroutine,所以,我们允许 Goroutine 可能在请求被销毁之后继续执行。</p><p>但是,Goroutine 如果读写请求数据,Goroutine 会被终止,panic + recover(<em>recover 细节我们下一篇会介绍</em>)。</p><p>那么,我们要做的就是,所有的 API 都检查当前操作的请求是否合法,这里有两个关键:</p><p>每请求有一个内存对象,这个对象只会由 Go 来销毁,并不会在请求结束时,被 Envoy 销毁,但是这个内存对象中保存了一个 weakPtr,可以获取 C++ filter 的状态。</p><p>通过这个机制,Go 可以安全地获取 C++ 侧的 filter,判断请求是否还在。</p><p>同时,我们还会在 <code>onDestroy</code>,也就是 C++ filter 被销毁的 Hook 点;以及 Go thread 读写请求数据,这两个位置都加锁处理,以解决这两个之间的并发冲突。</p><h2>最后</h2><p>对于并发冲突,其实最简单的就是,通过加锁来竞争所有权,但是 Envoy 在这块的底层设计并没有锁,因为它根本不需要锁。</p><p>所以,基于 Envoy 的处理模型,我们设计了一套类似所有权的机制,来避免并发冲突。</p><p>所有权的概念也受到了 Rust 的启发,只是两者工作的层次不一样,Rust 是更底层的语言层面,可以作用于语言层面,我们这里则是更上层的概念,特定于 Envoy 的处理模型,也只能作用于这一个小场景。</p><p>但是某种程度上,解决的问题,以及其中部分思想是一样的。</p><h2>MOSN Star 一下🌟:</h2><p><em><a href="https://link.segmentfault.com/?enc=xXN7lLU3JSImkTd8VGpF8g%3D%3D.LGuGV2xYhzKYD0nSFd39xIdegOy3RU737hBr53v%2BIt8%3D" rel="nofollow">https://github.com/mosn/mosn</a></em></p><h2>参考链接</h2><p>[1]Go 扩展的异步模式:</p><p><em><a href="https://link.segmentfault.com/?enc=PezKvmtAR0tIpuCMOUn2og%3D%3D.m1n2rXtdZrapENcpAuyDbd%2Bo6a3S2K2fSpGy1r3dhLJXrSHPVrI4eneUfXWMc1jwfKRx%2BRDO1zZ6ycKOcyxTUA%3D%3D" rel="nofollow">https://uncledou.site/2023/moe-extend-envoy-using-golang-4/</a></em></p><p>[2]搞懂 http filter 状态码:</p><p><a href="https://link.segmentfault.com/?enc=KVOUcrYB%2FW%2FrvzxTzPx4MQ%3D%3D.JIptFIVrFDCMEwup3giNTSDgP%2BPvr1j6oBGdJYi43KsMkefs%2BMa9f9oaez3KMbgk" rel="nofollow">https://uncledou.site/2022/envoy-filter-status/</a></p><h2>推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=IM1MHmPD9XfVGKHupKtVGg%3D%3D.SgTmI5%2Fyz76Y3RDKTDVY0FbzmF1ITsXH9gQcWIIUYFFQHvv3cWurAle0B06Om16z1qwk7zgNarv2e%2F%2Frhl2IsKZJKuL9YHWJtle9HKZqiOuKEXNW8MtcWBDs3U8Bqkt%2BJPue87t9fZHk26MBsIC0Ohou%2B1xgIZ002hC9a%2FSh8Enr6zdobp7zzwsfbl4hJ7EeJup8BbLujWmA2k0RhMXbhosezdD7B7vwAfQF4OUGnDd%2FFUPukWo38NDAT4N69On4oNnFssXzI%2BpcLtlgGfucBg%3D%3D" rel="nofollow">MoE 系列(二)|Golang 扩展从 Envoy 接收配置</a></p><p><a href="https://link.segmentfault.com/?enc=kSa2%2B%2BYvB3hlQ0yrClbrWQ%3D%3D.zq7K29v48lYCzLgTxb9y2NvEc9zBtHwqzem01zJTEyL6%2FkGs2oqxhPkG%2BTCiwv3rsRpFKnmGDsPctfZuSygEPzibDnV%2Bs7qaBg3SIhjIV6zfDIOhGhgkgkfpR3SY9eBJIIzBrCOlVPAx88M30pvMzaT0L4%2BmEsq9%2FbWYIEYQdAPkrHFKDQDvINU0Kf%2FFxaXB0sSVExgc01RZREPCbiaUE1rr7ZOvkmbSnyzEE7x98wAIOiYbArRe3Z9kKTIf1ZgFcVjiK6cXlXDMU6qIax1IwA%3D%3D" rel="nofollow">MoE 系列(三)|使用 Istio 动态更新 Go 扩展配置</a></p><p><a href="https://link.segmentfault.com/?enc=0CXvfE0Aasj9AwaEHbrlKg%3D%3D.6ld0MK9u1Kyw3tbZltAJIvhUZVMRdN%2FfCIyNQqszy7kwIbscpacac%2FDilYPIN%2FtoEx%2BzjrdujZrKvGry933lGo%2FjpBcfj2X%2BZGuElCMbM227YbAm7wi0TuhS81ZtSI%2Br%2FatPouQQRgJh5NAldEZYsUtiWO9dAlPf1gZPNHOAa7tOkG43PFOWwi2jOiPEaVtoEtGQfPwK4IrbPL2aolYyVU8U1MXN4LQ5da4wYLLhDeBqAZsw%2FZEGNxkQdK6lohwchKoNzEMpsRMTC7uUGiNtSg%3D%3D" rel="nofollow">MoE 系列(四)|Go 扩展的异步模式</a></p><p><a href="https://link.segmentfault.com/?enc=m%2BVG4xhOGHNJeEBydVVMpA%3D%3D.goT0hjyl7ORTGuzxjFHJH0Ajk6VUPCl5hh7pqNmIjiYZGywn%2F6%2FUcnMBaf0M2waonzOVPVaBTjQs9rcQE0FDK1pxaLwZgjKbA3t%2FZussc%2FfymhpvSQBMd649oFoas458c33POsl4YoyLWs7oKbMF28PYN%2FFSFH9Mab%2BbE%2Bh5QNOLyHLUkyKfjzx3s8ELr2%2FKTWosj7C6W8fPy1cw4sAqziw5K7MOFVf9atJgU0oMELi1vt8qmcAC1kJLbDdSQZHN1yeTS3z7uH9IK%2BJBPdlH3g%3D%3D" rel="nofollow">MoE 系列(五)|Envoy Go 扩展之内存安全</a></p>
SOFABoot 4.0 正式发布,多项新特性等你来体验!
https://segmentfault.com/a/1190000044009971
2023-07-14T14:35:23+08:00
2023-07-14T14:35:23+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<h2>Part.1 「亿点点」新特性</h2><h3>基于 Java 17</h3><p>SOFABoot 4.0 依赖 Java 17 作为最小支持的 JDK 版本。如果你的应用目前使用 Java 8 或 11,你需要先将自己的 JDK 版本升级到 17 才能基于 SOFABoot 4.0 进行开发。</p><h3>二方库升级</h3><p>SOFABoot 4.0 基于 Spring Boot 3.0 与 Spring Framework 6 构建。在 Spring Boot 3.0 与 Spring Framework 6 引入的二方库升级列表可参考文档👉 <a href="https://link.segmentfault.com/?enc=7TPtYT5oPdA%2FKpMXyxLyxw%3D%3D.oKX78JDs%2B87Cjy18F6LRYlD6N%2F1sIZD9khZUS0J4ZEUr8E8pi2troOiZPWwoLEPmx5z%2FjEmg2cO6QtsvrdPOLONhwOp0tSfdNG0C02XhVzRpnxYa3gOfiz5UJ3b5LH84" rel="nofollow">Spring Boot 3.0 Release Notes</a></p><p><strong>在 SOFABoot 4.0 引入的二方库升级列表如下</strong>:</p><ul><li>Spring Boot 3.0.5</li><li>Spring Cloud 4.0.0</li><li>Spring Cloud Stream 3.2.6</li><li>SOFA Common tools 2.0.0</li><li>SOFATracer 4.0.0</li><li>SOFARPC 5.10.0</li><li>FastJson 1.2.83</li><li>Guava 31.1-jre</li><li>Grpc 1.51.1</li><li>Grpc common protos 2.11.0</li><li>Druid 1.2.16</li><li>ASM 9.4</li><li>Javassist 3.29.2-GA</li><li>Curator 4.3.0</li><li>Dubbo 3.1.8</li><li>Nacos 2.0.3</li><li>Swagger 1.6.7</li><li>Swagger2 2.2.8</li></ul><h3>基于 Jakarta EE</h3><p>Spring Boot 3.0 中依赖 Jakarta EE 规范的部分已经升级到了 Jakarta EE 10 版本。例如,使用 Servlet 6.0 和 JPA 3.1 规范。因此,部分包的命名空间也进行了替换,例如你应该使用:</p><p>✅<code>jakarta.servlet.Filter</code> </p><p>而不是 <code>javax.servlet.Filter</code>。</p><p>同时,如果你使用了自己的依赖提供 Jakarta EE 规范的 API,需注意进行对应的依赖升级,例如你应该使用:</p><p>✅<code>jakarta.servlet:jakarta.servlet-api</code> </p><p>而不是 <code>javax.servlet:javax.servlet-api</code>。</p><p>可参考文档:<a href="https://link.segmentfault.com/?enc=ok92DZEVy9mOSGOSSLSnFg%3D%3D.0U5BESIjI%2BLMx7TmdUOWHkfRfsUS4zk1j93AtaiXluflqXqsG4UI9OQ0HUHAHec1nlAWvFV%2FZ5bfYMeFM94uwVXZfE00R8T3GC241SMh6BRozkjfbm6jgkOQJp%2BoPtM9" rel="nofollow">Migrate to Jakarta EE 9</a> 来修改 Jakarta 相关的包名以及依赖。</p><h3>支持 SOFAArk 2.0</h3><p><a href="https://link.segmentfault.com/?enc=Ow0VA72iNrMCuqCBl33wXg%3D%3D.tShlh86%2F84RwqnQMfN6HrsZ%2Bt8dhjkEJfuOgZIq38Em3o8PMYRzBqq8m%2FiX5Rd5BONd%2B7IusEnbOVrRn2UqisyUxxwkwV%2B9KU%2FPdqgk5SVs%3D" rel="nofollow">SOFAArk 2.0 模式</a>是 SOFAArk 框架的整体优化版本,相较于 SOFAArk 1.0 模式,它的整体优化思路和原则是 Ark Master Biz 保持和原生 SOFABoot 保持一致,弱化复杂的 Ark Plugin 类管控机制,将 Ark Plugin 与 Master Biz 合并。使得 Ark Master Biz 和原生 SOFABoot 应用的启动方式、类加载方式保持一致,大大降低了 Master Biz 应用的编程难度。</p><p>SOFABoot 4.0 版本不再支持 SOFAArk 1.0 模式,如果你想要在 SOFABoot 应用中使用 SOFAArk 2.0 模式,可以按照以下步骤进行操作:</p><p><strong>1、在项目中引入 SOFAArk 组件依赖</strong></p><pre><code class="xml"><dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>ark-sofa-boot-starter</artifactId>
</dependency>
</code></pre><p><strong>2、添加 Spring Boot 的 Package 插件</strong></p><pre><code class="xml"><build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<outputDirectory>target</outputDirectory>
<classifier>ark-biz</classifier>
</configuration>
<executions>
<execution>
<id>package</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build></code></pre><p><strong>3、启动应用</strong></p><ul><li>方式一:</li></ul><p>IDEA 启动,注意需要添加启动参数:<code>-Dsofa.ark.embed.enable=true -Dcom.alipay.sofa.ark.master.biz=${bizName}</code></p><ul><li>方式二:</li></ul><p>命令行启动,先运行 <code>mvn clean package</code> 命令进行打包,生成 ${bizName}-${bizVersion}-ark-biz.jar 的可执行文件,然后在终端运行以下启动参数:</p><p><code>java -jar -Dsofa.ark.embed.enable=true -Dcom.alipay.sofa.ark.master.biz=${bizName} ${bizName}-${bizVersion}-ark-biz.jar</code></p><h3>支持场景化的按配置加载能力</h3><p>通常情况下,应用在不同的场景下可能需要开启或者关闭不同的功能,Spring Boot 提供了丰富的 Configuration 动态配置能力[4] 能力以支持应用在不同的场景下加载不同的 Bean。 <br>SOFABoot 在此基础上,对 <code>org.springframework.context.ApplicationContextInitializer</code> 等扩展点进行了增强,支持通过统一风格的配置定制各类 Bean 以及扩展点的开启与关闭,并提供了定制模版配置的开启方式以降低应用配置项的复杂度。</p><ul><li>通过:</li></ul><p><code>com.alipay.sofa.boot.autoconfigure.condition.ConditionalOnSwitch</code> 注解为 Bean 添加按配置开启能力:</p><pre><code class="java">@Configuration(proxyBeanMethods = false)
@ConditionalOnSwitch(id="sample")
public class SampleConfiguration {
@Bean
public SampleBean sampleBean() {
return new SampleBean();
}
}</code></pre><p>添加:</p><p><code>sofa.boot.switch.bean.sample.enabled=false</code> 配置后,<code>SampleConfiguration</code> 配置类将不再加载。</p><ul><li>通过继承:</li></ul><p><code>com.alipay.sofa.boot.Initializer.SwitchableApplicationContextInitializer</code> 类为:</p><p><code>ApplicationContextInitializer</code> 添加按配置开启能力:</p><pre><code class="java">public class SampleSwitchSpringContextInitializer extends
SwitchableApplicationContextInitializer {
@Override
protected void doInitialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getEnvironment().addActiveProfile("sampleswitchtest");
}
@Override
protected String switchKey() {
return "sample";
}
}</code></pre><p>添加:</p><p><code>sofa.boot.switch.initializer.sample.enabled=false</code> 配置后,</p><p><code>SampleSwitchSpringContextInitializer</code> 类将不再执行 <code>doInitialize</code> 方法。</p><ul><li>通过继承:</li></ul><p><code>com.alipay.sofa.boot.listener.SwitchableApplicationListener</code> 类为:</p><p><code>ApplicationListener</code> 添加添加按配置开启能力:</p><pre><code class="java">public class SampleSwitchApplicationListener
extends
SwitchableApplicationListener<ContextRefreshedEvent> {
@Override
protected void doOnApplicationEvent(ContextRefreshedEvent event) {
SampleBean sampleBean = event.getApplicationContext().getBean(SampleBean.class);
sampleBean.setTrigger(true);
}
@Override
protected String switchKey() {
return "sample";
}
}</code></pre><p>添加:</p><p><code>sofa.boot.switch.listener.sample.enabled=false</code> 配置后,</p><p><code>SampleSwitchApplicationListener</code> 类将不再执行 <code>doOnApplicationEvent</code> 方法。</p><p>在使用上述扩展点为你的 Bean 和扩展点添加按配置开启能力后,你可以在 <code>/sofa-boot/scenens</code> 目录下添加指定场景名 <code>scene-key</code> 前缀的配置文件 <em>(支持 application 及 yaml 格式)</em> ,在配置文件中添加该场景下的配置文件模版,例如:</p><pre><code>sofa.boot.switch.bean.a.enabled=false
sofa.boot.switch.bean.b.enabled=true
sofa.boot.switch.initializer.a.enabled=false
sofa.boot.switch.initializer.b.enabled=false
sofa.boot.switch.initializer.c.enabled=false</code></pre><p>当你的应用打包后,你只需要添加配置 <code>sofa.boot.scenens=scene-key</code> 便可以生效 <code>/sofa-boot/scenens/scene-key.properites</code> 配置文件中的开关配置。该配置项同时支持配置多个场景名,可以同时生效多个场景配置文件。</p><h3>启动耗时统计能力增强</h3><p>SOFABoot 在 3.6.0 版本提供了 <code>/actuator/startup</code> 能力用于查询应用启动过程中的耗时细节。后来,Spring Boot 在 2.4 版本也提供了官方的 <code>/actuator/startup</code> 能力用于展示应用启动耗时详情。在 SOFA Boot 4.0 版本中,我们整合了 SOFA Boot 与 Spring Boot 提供的启动耗时统计能力:</p><ul><li>保留了 SOFABoot 特有的阶段耗时统计能力,例如 SOFA 模块化刷新耗时统计、健康检查耗时统计等;</li><li>使用 Spring Framework 提供的 <code>org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup</code> 统计 Applicaiton Context 的启动耗时详情。</li></ul><p>你可以通过下述方式使用 SOFABoot 增强的 <code>/actuator/startup</code> 能力:</p><p><strong>1、在项目中引入 actuator 组件依赖:</strong></p><pre><code class="xml"><dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>actuator-sofa-boot-starter</artifactId>
</dependency></code></pre><p><strong>2、使用:</strong></p><p><code>com.alipay.sofa.boot.startup.StartupSpringApplication</code> <strong>作为启动类启动应用:</strong></p><pre><code class="java">public static void main(String[] args) {
StartupSpringApplication startupSpringApplication = new StartupSpringApplication(Sofaboot4DemoApplication.class);
startupSpringApplication.run(args);
}</code></pre><p><strong>3、启动应用后,访问:</strong></p><p>localhost:8080/actuator/startup <strong>查看启动耗时详情信息。</strong></p><h3>更丰富的 SPI</h3><p>SOFABoot 4.0 版本中新增了大量 SPI ,你可以通过这些 SPI 定制 SOFABoot 框架的运行逻辑。</p><ul><li><strong>添加自定义的启动耗时阶段信息</strong></li></ul><p><code>com.alipay.sofa.boot.startup.StartupReporterAware</code> 接口的使用方式与 <code>org.springframework.context.ApplicationContextAware</code> 接口的使用方法使用类似,当你的 Bean 实现了该接口时,你可以感知到应用中的:<code>com.alipay.sofa.boot.startup.StartupReporter</code> 实例。</p><p><code>com.alipay.sofa.boot.startup.StartupReporter</code> 类用于管理 SOFABoot 提供的 <code>/actuator/startup</code> 耗时信息,你可以通过 <code>com.alipay.sofa.boot.startup.StartupReporter#addCommonStartupStat</code> 方法添加你定制的耗时阶段信息。</p><ul><li><strong>添加自定义的 Bean 启动耗时信息</strong></li></ul><p><code>com.alipay.sofa.boot.startup.BeanStatCustomizer</code> 接口用于定制 SOFABoot 提供的 <code>/actuator/startup</code> 耗时信息中的 Bean 启动耗时特征。如果你想注册自定义的 <code>com.alipay.sofa.boot.startup.BeanStatCustomizer</code> 接口实现类,需要在 <code>META-INF/spring.factories</code> 文件注册 Spring Factories 形式的 SPI。你可以参考框架内置的 <code>com.alipay.sofa.runtime.startup.ComponentBeanStatCustomizer</code> 类用于提取 ServiceFactoryBean 类型的 Bean 的 Interface 字段用于展示。</p><ul><li><strong>定制 BeanPostProcessor 与 BeanFactoryPostProcessor 在 SOFA 上下文中的共享模式</strong></li></ul><p>在开启模块化隔离特性时,你在 Spring Boot 上下文中注册的 <code>BeanPostProcessor</code> 以及 <code>BeanFactoryPostProcessor</code> 将 BeanDefinition 将被共享至所有的 SOFA 模块中,每个 SOFA 模块的上下文中都会创建一个 PostProcessor 类的实例。你可以通过以下方式,修改指定的 <code>BeanPostProcessor</code> 或者<code>BeanFactoryPostProcessor</code> 的共享方式:</p><ul><li><ul><li>在 PostProcessor 类上添加:</li></ul></li></ul><p><code>com.alipay.sofa.boot.context.processor.UnshareSofaPostProcessor</code> 注解,PostProcessor 将不会被共享至 SOFA 模块中。</p><ul><li><ul><li>在 PostProcessor 类上添加:</li></ul></li></ul><p><code>com.alipay.sofa.boot.context.processor.SingletonSofaPostProcessor</code> 注解,SOFA Boot 框架会获取其在 Spring Boot 上下文中的 Singleton 实例注册至 SOFA 模块中,而不是注册 BeanDefinition,这将确保 PostProcessor 类在整个应用中保持单例。</p><ul><li><ul><li>自定义:</li></ul></li></ul><p><code>com.alipay.sofa.boot.context.processor.SofaPostProcessorShareFilter</code> 接口的实现类并将其注册至 Spring Boot 上下文中,通过 bean name 或 bean class 指定 PostProcessor 的共享方式。</p><ul><li><strong>添加感知 SOFA 模块刷新的扩展点</strong></li></ul><p>在开启模块化隔离特性时,你可以自定义 <code>com.alipay.sofa.boot.context.ContextRefreshInterceptor</code> 接口的实现类并将其注册至 Spring Boot 上下文中,当每个 SOFA 模块的 Spring 上下文开始刷新前以及刷新完成后,都会触发该接口的 <code>beforeRefresh</code> 以及 <code>afterRefresh</code> 方法。</p><p>你可以参考框架内置的:<code>com.alipay.sofa.runtime.context.ComponentContextRefreshInterceptor</code> 类,它用于在 SOFA 模块中的 Spring 上下文刷新成功后,将其注为 <code>SpringContextComponent</code>,在 SOFA 模块中的 Spring 上下文刷新失败后取消注册的 <code>ComponentInfo</code>。</p><ul><li><strong>支持注解参数的占位符替换</strong></li></ul><p>在一些情况下,我们希望自定义注解中的属性使用非固定值,通过 Spring 配置进行定制。SOFABoot 提供了:</p><p><code>com.alipay.sofa.boot.annotation.WrapperAnnotation</code> 工具类,用于快速实现上述功能。例如,你自定义了一个注解类 <code>DemoAnnotation</code>,并在某个类上使用了该注解:</p><pre><code class="java">@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface DemoAnnotation {
String key() default "";
} </code></pre><pre><code class="java">@DemoAnnotation(key = "${spring.annotation.config}")
public class TestClass {
}</code></pre><p>通过以下方式获取的 key 字段数值将转换为你在 Spring Envrionment 中定义的 <code>spring.annotation.config</code> 配置值:</p><pre><code class="java">public String getAnnotationKey(Environment environment, DemoAnnotation demoAnnotation) {
AnnotationWrapper<DemoAnnotation> serviceAnnotationWrapper = AnnotationWrapper.create(DemoAnnotation.class)
.withEnvironment(environment)
.withBinder(DefaultPlaceHolderBinder.INSTANCE);
return serviceAnnotationWrapper.wrap(demoAnnotation).key();
}</code></pre><h2>Part.2 注意有重命名哦!</h2><p><strong>请先留意以下信息!</strong></p><ul><li>通过 actuator-sofa-boot-starter 引入的 SOFABoot 增强的 actuator 能力,通过配置项定制开启的 actuator。</li><li>依赖 health-sofa-boot-starter 与 startup-sofa-boot-starter 已被废弃,请使用 actuator-sofa-boot-starter 替换。</li><li>依赖 log-sofa-boot-starter 已被废弃,如果你引入了其他的 sofa boot starter,可以直接删除该依赖。如果你没有引入任何其他 sofa boot starter,使用 sofa-boot-starter 代替它。</li><li>依赖 rpc-sofa-boot-plugin、runtime-sofa-boot-plugin、tracer-sofa-boot-plugin 已被废弃,可使用 ark-sofa-boot-starter 代替它们。</li></ul><p>我们对下表中包名下的类进行了重命名,如果你使用了对应的类,则需要修改为新的包名:</p><table><thead><tr><th>原包名</th><th>新包名</th></tr></thead><tbody><tr><td>com.alipay.sofa.startup</td><td>com.alipay.sofa.boot.actuator.startup</td></tr><tr><td>com.alipay.sofa.healthcheck</td><td>com.alipay.sofa.boot.actuator.health</td></tr><tr><td>com.alipay.sofa.isle</td><td>com.alipay.sofa.boot.isle</td></tr><tr><td>com.alipay.sofa.tracer.boot</td><td>com.alipay.sofa.boot.tracer</td></tr><tr><td>com.alipay.sofa.service.api.component</td><td>com.alipay.sofa.runtime.ext</td></tr></tbody></table><p>SOFABoot 提供的以下 API 类进行了重命名,如果你使用了对应的类,则需要修改为新的类名:</p><table><thead><tr><th>原类名</th><th>新类名</th></tr></thead><tbody><tr><td>com.alipay.sofa.healthcheck.core.HealthChecker</td><td>com.alipay.sofa.boot.actuator.health.HealthChecker</td></tr><tr><td>com.alipay.sofa.healthcheck.startup.ReadinessCheckCallback</td><td>com.alipay.sofa.boot.actuator.health.ReadinessCheckCallback</td></tr><tr><td>com.alipay.sofa.runtime.spring.singleton.SingletonSofaPostProcessor</td><td>com.alipay.sofa.boot.context.processor.SingletonSofaPostProcessor</td></tr><tr><td>com.alipay.sofa.runtime.spring.share.UnshareSofaPostProcessor</td><td>com.alipay.sofa.boot.context.processor.UnshareSofaPostProcessor</td></tr><tr><td>com.alipay.sofa.runtime.factory.BeanLoadCostBeanFactory</td><td>com.alipay.sofa.boot.context.SofaDefaultListableBeanFactory</td></tr></tbody></table><h2>Part.3 废弃特性</h2><p><strong>再见啦,SOFAArk 1.0</strong></p><p>SOFABoot 4.0 不再支持 SOFAArk 1.0 模式,用于支持 Ark 测试的相关工具类已被移除,SOFAArk 2.0 模式下你不再需要这些类:</p><ul><li>com.alipay.sofa.test.annotation.DelegateToRunner</li><li>com.alipay.sofa.test.runner.SofaBootRunner</li><li>com.alipay.sofa.test.runner.SofaJUnit4Runner</li><li></li></ul><h2>Part.4 请收下这份升级指南🙆🏻♂️</h2><h3>Before You Start</h3><h4>升级 SOFABoot 至 最新的 3.x 版本</h4><p>在开始升级之前,请确保升级到最新可用的 3.x 版本。这将确保你正在针对该行的最新依赖项进行构建。</p><h4>检查依赖列表</h4><p>迁移到 SOFABoot 4.0 将升级许多依赖项 <em>(包括 Spring Boot 3.0 升级的依赖项)</em> ,请确认依赖项升级对你的应用的影响。请参考:</p><ul><li><a href="https://link.segmentfault.com/?enc=IVL9FkNMMQG93dA3bBhp%2BQ%3D%3D.unCAC6e%2BI58wzCCwHTsAy%2BXF64ElMW7QsU1weKkItRVX0nB0jey1DmxxIEb4gk%2BVYSMMHmb13L4ghf%2BK%2BSV%2BhdvZJr17yNthEjOSjXKiUFlAHzIfOI6H%2FbNUe1xLW6A1kB%2FGcoSWt83XeN7bKDAGAZVZE196RCELUdP%2BPnFYyBQ%3D" rel="nofollow">Spring Boot 2.7.x 依赖索引</a></li><li><a href="https://link.segmentfault.com/?enc=7J70KQH0ziVjvf9OKBZbhA%3D%3D.LuOJCzvzP%2FrVgXFq%2BH%2BipBP31jy4S65KaiuXuye5eRmmCiMtBstVqC0TwIT1b%2FnGPFz5HwTSJ3qk2UiCwWOWYgFIxghQK3N5mgozxj7qUsVcmuD4%2FvjQ61g65IUd1uMUNeWkIYKooUH2qsujv0wc9gIHtRZXwtlTXwnzZjQSdvA%3D" rel="nofollow">Spring Boot 3.0.x 依赖索引</a></li></ul><h4>检查已废弃的特性</h4><p>SOFABoot 3 以及 Spring Boot 2 中弃用的类、方法和属性,请确保在升级之前没有调用已弃用的功能。详情请参考上方「<strong>Part.3 废弃特性|再见啦,SOFAArk 1.0</strong>」章节。</p><h4>检查系统配置</h4><p>SOFABoot 4.0 依赖 Java 17 或者更高的版本,不再支持 Java 8。同时依赖 Spring Boot 3.0。</p><h3>升级至 SOFA Boot 4.0</h3><h4>修改重命名的配置项</h4><p>请参考下方「<strong>Part.5 附录|配置变更</strong>」章节以及文档 <a href="https://link.segmentfault.com/?enc=%2BRfZVabyPPuJzMehAAMlbA%3D%3D.GhEj6cYAGd055drLJ%2FbMJNnZ8BR1uYbrbd1rvnKnTNJVuS3TJMPxwYqeVz5xT0Pxg%2BiySLtankdoHKdJjqhVssXPvaBWVKcRI5dvhkTfj44sx%2FvvIuE%2BGvWImiw8TmUL" rel="nofollow">Spring Boot 2 -> 3 配置变更</a></p><h4>修改重命名的类与依赖</h4><p>参考上方「<strong>Part.2 注意有重命名哦!</strong> 」章节</p><h4>升级 Spring Boot 3.0</h4><p>参考文档: <a href="https://link.segmentfault.com/?enc=FF1KdvZL3oJeGrzq8tWArA%3D%3D.imXZVCx5HVMs8i4T3nUHqKR8igo9Zq%2FVzHioxEJl3SydMlla7vxV4Dx4zSKa8K1frrYlpEZXNTyOAB168XK29jwEbkzeDWJfDt9CnuY548VIXMtxH5Hyy%2Brq3XfSr85p" rel="nofollow">Spring Boot 3.0 升级文档</a></p><h2>Part.5 附录|配置变更</h2><p><em>注:配置属性对比基于 3.16.3 与 4.0 版本。</em></p><ul><li><strong>在 4.0 版本中废弃的配置</strong></li></ul><table><thead><tr><th>key</th></tr></thead><tbody><tr><td>com.alipay.sofa.boot.serviceNameWithBeanId</td></tr><tr><td>com.alipay.sofa.boot.unregisterComponentWhenModuleInstallFailure</td></tr><tr><td>com.alipay.sofa.boot.startup.skipSofaBean</td></tr></tbody></table><ul><li><strong>在 4.0 版本中新增的配置</strong></li></ul><table><thead><tr><th>key</th><th>default value</th><th>description</th></tr></thead><tbody><tr><td>sofa.boot.isle.ignoreModules</td><td>无</td><td>指定忽略的 SOFA 模块列表</td></tr><tr><td>sofa.boot.isle.ignoreCalculateRequireModules</td><td>无</td><td>指定忽略 require module 属性的 SOFA 模块列表</td></tr><tr><td>sofa.boot.scenes</td><td>无</td><td>开启的场景配置列表</td></tr><tr><td>sofa.boot.startup.bufferSize</td><td>4096</td><td>框架内置的 BufferingApplicationStartup 的缓存大小</td></tr><tr><td>sofa.boot.threadPoolMonitor.disable</td><td>false</td><td>关闭 SOFA 线程池监控能力</td></tr><tr><td>sofa.boot.rpc.enableAutoPublish</td><td>false</td><td>支持应用启动时自动发布 rpc服务,不依赖 Actuator 模块</td></tr></tbody></table><ul><li><strong>在 4.0 版本中重命名的配置</strong></li></ul><p><strong>runtime properties</strong></p><table><thead><tr><th>origin key</th><th>replaced key</th></tr></thead><tbody><tr><td>com.alipay.sofa.boot.jvmFilterEnable</td><td>sofa.boot.runtime.jvmFilterEnable</td></tr><tr><td>com.alipay.sofa.boot.disableJvmFirst</td><td>sofa.boot.runtime.disableJvmFirst</td></tr><tr><td>com.alipay.sofa.boot.skipJvmReferenceHealthCheck</td><td>sofa.boot.runtime.skipJvmReferenceHealthCheck</td></tr><tr><td>com.alipay.sofa.boot.extensionFailureInsulating</td><td>sofa.boot.runtime.extensionFailureInsulating</td></tr><tr><td>com.alipay.sofa.boot.skipExtensionHealthCheck</td><td>sofa.boot.runtime.skipExtensionHealthCheck</td></tr><tr><td>com.alipay.sofa.boot.serviceInterfaceTypeCheck</td><td>sofa.boot.runtime.serviceInterfaceTypeCheck</td></tr><tr><td>com.alipay.sofa.boot.skipAllComponentShutdown</td><td>sofa.boot.runtime.skipAllComponentShutdown</td></tr><tr><td>com.alipay.sofa.boot.skipCommonComponentShutdown</td><td>sofa.boot.runtime.skipCommonComponentShutdown</td></tr><tr><td>com.alipay.sofa.boot.asyncInitBeanCoreSize</td><td>sofa.boot.runtime.asyncInitExecutorCoreSize</td></tr><tr><td>com.alipay.sofa.boot.asyncInitBeanMaxSize</td><td>sofa.boot.runtime.asyncInitExecutorMaxSize</td></tr><tr><td>com.alipay.sofa.boot.dynamicJvmServiceCacheEnable</td><td>sofa.boot.ark.jvmServiceCache</td></tr><tr><td>com.alipay.sofa.boot.skipJvmSerialize</td><td>sofa.boot.ark.jvmInvokeSerialize</td></tr><tr><td>com.alipay.sofa.boot.beanLoadCost</td><td>sofa.boot.startup.costThreshold</td></tr></tbody></table><p><strong>isle properties</strong></p><table><thead><tr><th>origin key</th><th>replaced key</th></tr></thead><tbody><tr><td>com.alipay.sofa.boot.activeProfiles</td><td>sofa.boot.isle.activeProfiles</td></tr><tr><td>com.alipay.sofa.boot.allowBeanDefinitionOverriding</td><td>sofa.boot.isle.allowBeanDefinitionOverriding</td></tr><tr><td>com.alipay.sofa.boot.moduleStartUpParallel</td><td>sofa.boot.isle.moduleStartUpParallel</td></tr><tr><td>com.alipay.sofa.boot.publishEventToParent</td><td>sofa.boot.isle.publishEventToParent</td></tr><tr><td>com.alipay.sofa.boot.enableIsle</td><td>sofa.boot.isle.enabled</td></tr><tr><td>com.alipay.sofa.boot.allowModuleOverriding</td><td>sofa.boot.isle.allowModuleOverriding</td></tr><tr><td>com.alipay.sofa.boot.ignoreModuleInstallFailure</td><td>sofa.boot.isle.ignoreModuleInstallFailure</td></tr><tr><td>com.alipay.sofa.boot.parallelRefreshCoreCountFactor</td><td>sofa.boot.isle.parallelRefreshPoolSizeFactor</td></tr><tr><td>com.alipay.sofa.boot.parallelRefreshTimeout</td><td>sofa.boot.isle.parallelRefreshTimeout</td></tr><tr><td>com.alipay.sofa.boot.parallelRefreshCheckPeriod</td><td>sofa.boot.isle.parallelRefreshCheckPeriod</td></tr><tr><td>com.alipay.sofa.boot.share.parent.context.post.processor.enabled</td><td>sofa.boot.isle.shareParentPostProcessor</td></tr></tbody></table><p><strong>actuator properties</strong></p><table><thead><tr><th>origin key</th><th>replaced key</th></tr></thead><tbody><tr><td>com.alipay.sofa.boot.manualReadinessCallback</td><td>sofa.boot.actuator.health.manualReadinessCallback</td></tr><tr><td>com.alipay.sofa.healthcheck.skip.all</td><td>sofa.boot.actuator.health.skipAll</td></tr><tr><td>com.alipay.sofa.healthcheck.skip.component</td><td>sofa.boot.actuator.health.skipHealthChecker</td></tr><tr><td>com.alipay.sofa.healthcheck.skip.indicator</td><td>sofa.boot.actuator.health.skipHealthIndicator</td></tr><tr><td>com.alipay.sofa.healthcheck.component.check.retry.count</td><td>sofa.boot.actuator.health.healthCheckerConfig.components.retryCount</td></tr><tr><td>com.alipay.sofa.healthcheck.component.check.retry.interval</td><td>sofa.boot.actuator.health.healthCheckerConfig.components.retryTimeInterval</td></tr><tr><td>com.alipay.sofa.healthcheck.component.check.strict.enabled</td><td>sofa.boot.actuator.health.healthCheckerConfig.components.strictCheck</td></tr><tr><td>com.alipay.sofa.healthcheck.component.timeout</td><td>sofa.boot.actuator.health.healthCheckerConfig.components.timeout</td></tr><tr><td>com.alipay.sofa.healthcheck.module.check.retry.count</td><td>sofa.boot.actuator.health.healthCheckerConfig.modules.retryCount</td></tr><tr><td>com.alipay.sofa.healthcheck.module.check.retry.interval</td><td>sofa.boot.actuator.health.healthCheckerConfig.modules.retryTimeInterval</td></tr><tr><td>com.alipay.sofa.healthcheck.module.check.strict.enabled</td><td>sofa.boot.actuator.health.healthCheckerConfig.modules.strictCheck</td></tr><tr><td>com.alipay.sofa.healthcheck.module.timeout</td><td>sofa.boot.actuator.health.healthCheckerConfig.modules.timeout</td></tr><tr><td>com.alipay.sofa.healthcheck.default.timeout</td><td>sofa.boot.actuator.health.globalHealthCheckerTimeout</td></tr><tr><td>com.alipay.sofa.healthcheck.indicator.timeout</td><td>sofa.boot.actuator.health.globalHealthIndicatorTimeout</td></tr><tr><td>com.alipay.sofa.boot.healthCheckInsulator</td><td>sofa.boot.actuator.health.insulator</td></tr><tr><td>com.alipay.sofa.boot.healthCheckParallelEnable</td><td>sofa.boot.actuator.health.parallelCheck</td></tr><tr><td>com.alipay.sofa.boot.healthCheckParallelTimeout</td><td>sofa.boot.actuator.health.parallelCheckTimeout</td></tr><tr><td>com.alipay.sofa.boot.excludedIndicators</td><td>sofa.boot.actuator.health.excludedIndicators</td></tr></tbody></table><p><strong>tracer properties</strong></p><table><thead><tr><th>origin key</th><th>replaced key</th></tr></thead><tbody><tr><td>com.alipay.sofa.tracer.datasource.enable</td><td>sofa.boot.tracer.datasource.enabled</td></tr><tr><td>com.alipay.sofa.tracer.feign.enabled</td><td>sofa.boot.tracer.feign.enabled</td></tr><tr><td>com.alipay.sofa.tracer.springmvc.enable</td><td>sofa.boot.tracer.springmvc.enabled</td></tr><tr><td>com.alipay.sofa.tracer.springmvc.filterOrder</td><td>sofa.boot.tracer.springmvc.filterOrder</td></tr><tr><td>com.alipay.tracer.kafka.enabled</td><td>sofa.boot.tracer.kafka.enabled</td></tr><tr><td>com.alipay.tracer.mongodb.enabled</td><td>sofa.boot.tracer.mongodb.enabled</td></tr><tr><td>com.alipay.sofa.tracer.rabbitmq.enable</td><td>sofa.boot.tracer.rabbitmq.enabled</td></tr><tr><td>com.alipay.sofa.tracer.redis.enabled</td><td>sofa.boot.tracer.redis.enabled</td></tr><tr><td>com.alipay.sofa.tracer.resttemplate</td><td>sofa.boot.tracer.resttemplate.enabled</td></tr><tr><td>com.alipay.sofa.tracer.rocketmq</td><td>sofa.boot.tracer.rocketmq.enabled</td></tr><tr><td>com.alipay.sofa.tracer.message</td><td>sofa.boot.tracer.springmessage.enabled</td></tr><tr><td>com.alipay.sofa.tracer.flexible</td><td>sofa.boot.tracer.flexible.enabled</td></tr><tr><td>com.alipay.sofa.tracer.zipkin.enabled</td><td>sofa.boot.tracer.zipkin.enabled</td></tr><tr><td>com.alipay.sofa.tracer.zipkin.baseUrl</td><td>sofa.boot.tracer.zipkin.baseUrl</td></tr><tr><td>com.alipay.sofa.tracer.zipkin.gzipped</td><td>sofa.boot.tracer.zipkin.gzipped</td></tr><tr><td>com.alipay.sofa.tracer.disableDigestLog</td><td>sofa.boot.tracer.disableDigestLog</td></tr><tr><td>com.alipay.sofa.tracer.disableConfiguration</td><td>sofa.boot.tracer.disableConfiguration</td></tr><tr><td>com.alipay.sofa.tracer.tracerGlobalRollingPolicy</td><td>sofa.boot.tracer.tracerGlobalRollingPolicy</td></tr><tr><td>com.alipay.sofa.tracer.tracerGlobalLogReserveDay</td><td>sofa.boot.tracer.tracerGlobalLogReserveDay</td></tr><tr><td>com.alipay.sofa.tracer.statLogInterval</td><td>sofa.boot.tracer.statLogInterval</td></tr><tr><td>com.alipay.sofa.tracer.baggageMaxLength</td><td>sofa.boot.tracer.baggageMaxLength</td></tr><tr><td>com.alipay.sofa.tracer.samplerName</td><td>sofa.boot.tracer.samplerName</td></tr><tr><td>com.alipay.sofa.tracer.samplerPercentage</td><td>sofa.boot.tracer.samplerPercentage</td></tr><tr><td>com.alipay.sofa.tracer.samplerCustomRuleClassName</td><td>sofa.boot.tracer.samplerCustomRuleClassName</td></tr><tr><td>com.alipay.sofa.tracer.reporterName</td><td>sofa.boot.tracer.reporterName</td></tr><tr><td>com.alipay.sofa.tracer.jsonOutput</td><td>sofa.boot.tracer.jsonOutput</td></tr></tbody></table><p><strong>rpc properties</strong></p><table><thead><tr><th>origin key</th><th>replaced key</th></tr></thead><tbody><tr><td>com.alipay.sofa.rpc.aftRegulationEffective</td><td>sofa.boot.rpc.aftRegulationEffective</td></tr><tr><td>com.alipay.sofa.rpc.aftDegradeEffective</td><td>sofa.boot.rpc.aftDegradeEffective</td></tr><tr><td>com.alipay.sofa.rpc.aftTimeWindow</td><td>sofa.boot.rpc.aftTimeWindow</td></tr><tr><td>com.alipay.sofa.rpc.aftLeastWindowCount</td><td>sofa.boot.rpc.aftLeastWindowCount</td></tr><tr><td>com.alipay.sofa.rpc.aftLeastWindowExceptionRateMultiple</td><td>sofa.boot.rpc.aftLeastWindowExceptionRateMultiple</td></tr><tr><td>com.alipay.sofa.rpc.aftWeightDegradeRate</td><td>sofa.boot.rpc.aftWeightDegradeRate</td></tr><tr><td>com.alipay.sofa.rpc.aftWeightRecoverRate</td><td>sofa.boot.rpc.aftWeightRecoverRate</td></tr><tr><td>com.alipay.sofa.rpc.aftDegradeLeastWeight</td><td>sofa.boot.rpc.aftDegradeLeastWeight</td></tr><tr><td>com.alipay.sofa.rpc.aftDegradeMaxIpCount</td><td>sofa.boot.rpc.aftDegradeMaxIpCount</td></tr><tr><td>com.alipay.sofa.rpc.boltPort</td><td>sofa.boot.rpc.boltPort</td></tr><tr><td>com.alipay.sofa.rpc.boltThreadPoolCoreSize</td><td>sofa.boot.rpc.boltThreadPoolCoreSize</td></tr><tr><td>com.alipay.sofa.rpc.boltThreadPoolMaxSize</td><td>sofa.boot.rpc.boltThreadPoolMaxSize</td></tr><tr><td>com.alipay.sofa.rpc.boltThreadPoolQueueSize</td><td>sofa.boot.rpc.boltThreadPoolQueueSize</td></tr><tr><td>com.alipay.sofa.rpc.boltAcceptsSize</td><td>sofa.boot.rpc.boltAcceptsSize</td></tr><tr><td>com.alipay.sofa.rpc.boltProcessInIoThread</td><td>sofa.boot.rpc.boltProcessInIoThread</td></tr><tr><td>com.alipay.sofa.rpc.enableSwagger</td><td>sofa.boot.rpc.enableSwagger</td></tr><tr><td>com.alipay.sofa.rpc.mockUrl</td><td>sofa.boot.rpc.mockUrl</td></tr><tr><td>com.alipay.sofa.rpc.h2cPort</td><td>sofa.boot.rpc.h2cPort</td></tr><tr><td>com.alipay.sofa.rpc.h2cThreadPoolCoreSize</td><td>sofa.boot.rpc.h2cThreadPoolCoreSize</td></tr><tr><td>com.alipay.sofa.rpc.h2cThreadPoolMaxSize</td><td>sofa.boot.rpc.h2cThreadPoolMaxSize</td></tr><tr><td>com.alipay.sofa.rpc.h2cThreadPoolQueueSize</td><td>sofa.boot.rpc.h2cThreadPoolQueueSize</td></tr><tr><td>com.alipay.sofa.rpc.h2cAcceptsSize</td><td>sofa.boot.rpc.h2cAcceptsSize</td></tr><tr><td>com.alipay.sofa.rpc.restHostname</td><td>sofa.boot.rpc.restHostname</td></tr><tr><td>com.alipay.sofa.rpc.restPort</td><td>sofa.boot.rpc.restPort</td></tr><tr><td>com.alipay.sofa.rpc.restIoThreadSize</td><td>sofa.boot.rpc.restIoThreadSize</td></tr><tr><td>com.alipay.sofa.rpc.restContextPath</td><td>sofa.boot.rpc.restContextPath</td></tr><tr><td>com.alipay.sofa.rpc.restAllowedOrigins</td><td>sofa.boot.rpc.restAllowedOrigins</td></tr><tr><td>com.alipay.sofa.rpc.restThreadPoolCoreSize</td><td>sofa.boot.rpc.restThreadPoolCoreSize</td></tr><tr><td>com.alipay.sofa.rpc.restThreadPoolMaxSize</td><td>sofa.boot.rpc.restThreadPoolMaxSize</td></tr><tr><td>com.alipay.sofa.rpc.restMaxRequestSize</td><td>sofa.boot.rpc.restMaxRequestSize</td></tr><tr><td>com.alipay.sofa.rpc.restTelnet</td><td>sofa.boot.rpc.restTelnet</td></tr><tr><td>com.alipay.sofa.rpc.restDaemon</td><td>sofa.boot.rpc.restDaemon</td></tr><tr><td>com.alipay.sofa.rpc.restSwagger</td><td>sofa.boot.rpc.restSwagger</td></tr><tr><td>com.alipay.sofa.rpc.dubboPort</td><td>sofa.boot.rpc.dubboPort</td></tr><tr><td>com.alipay.sofa.rpc.dubboIoThreadSize</td><td>sofa.boot.rpc.dubboIoThreadSize</td></tr><tr><td>com.alipay.sofa.rpc.dubboThreadPoolCoreSize</td><td>sofa.boot.rpc.dubboThreadPoolCoreSize</td></tr><tr><td>com.alipay.sofa.rpc.dubboThreadPoolMaxSize</td><td>sofa.boot.rpc.dubboThreadPoolMaxSize</td></tr><tr><td>com.alipay.sofa.rpc.dubboThreadPoolQueueSize</td><td>sofa.boot.rpc.dubboThreadPoolQueueSize</td></tr><tr><td>com.alipay.sofa.rpc.dubboAcceptsSize</td><td>sofa.boot.rpc.dubboAcceptsSize</td></tr><tr><td>com.alipay.sofa.rpc.httpPort</td><td>sofa.boot.rpc.httpPort</td></tr><tr><td>com.alipay.sofa.rpc.httpThreadPoolCoreSize</td><td>sofa.boot.rpc.httpThreadPoolCoreSize</td></tr><tr><td>com.alipay.sofa.rpc.httpThreadPoolMaxSize</td><td>sofa.boot.rpc.httpThreadPoolMaxSize</td></tr><tr><td>com.alipay.sofa.rpc.httpThreadPoolQueueSize</td><td>sofa.boot.rpc.httpThreadPoolQueueSize</td></tr><tr><td>com.alipay.sofa.rpc.httpAcceptsSize</td><td>sofa.boot.rpc.httpAcceptsSize</td></tr><tr><td>com.alipay.sofa.rpc.triplePort</td><td>sofa.boot.rpc.triplePort</td></tr><tr><td>com.alipay.sofa.rpc.tripleThreadPoolCoreSize</td><td>sofa.boot.rpc.tripleThreadPoolCoreSize</td></tr><tr><td>com.alipay.sofa.rpc.tripleThreadPoolMaxSize</td><td>sofa.boot.rpc.tripleThreadPoolMaxSize</td></tr><tr><td>com.alipay.sofa.rpc.tripleThreadPoolQueueSize</td><td>sofa.boot.rpc.tripleThreadPoolQueueSize</td></tr><tr><td>com.alipay.sofa.rpc.tripleAcceptsSize</td><td>sofa.boot.rpc.tripleAcceptsSize</td></tr><tr><td>com.alipay.sofa.rpc.registryAddress</td><td>sofa.boot.rpc.registryAddress</td></tr><tr><td>com.alipay.sofa.rpc.virtualHost</td><td>sofa.boot.rpc.virtualHost</td></tr><tr><td>com.alipay.sofa.rpc.virtualPort</td><td>sofa.boot.rpc.virtualPort</td></tr><tr><td>com.alipay.sofa.rpc.enabledIpRange</td><td>sofa.boot.rpc.enabledIpRange</td></tr><tr><td>com.alipay.sofa.rpc.bindNetworkInterface</td><td>sofa.boot.rpc.bindNetworkInterface</td></tr><tr><td>com.alipay.sofa.rpc.boundHost</td><td>sofa.boot.rpc.boundHost</td></tr><tr><td>com.alipay.sofa.rpc.lookoutCollectDisable</td><td>sofa.boot.rpc.lookoutCollectDisable</td></tr><tr><td>com.alipay.sofa.rpc.registries</td><td>sofa.boot.rpc.registries</td></tr><tr><td>com.alipay.sofa.rpc.enableMesh</td><td>sofa.boot.rpc.enableMesh</td></tr><tr><td>com.alipay.sofa.rpc.consumerRepeatedReferenceLimit</td><td>sofa.boot.rpc.consumerRepeatedReferenceLimit</td></tr><tr><td>com.alipay.sofa.rpc.hystrixEnable</td><td>sofa.boot.rpc.hystrixEnable</td></tr><tr><td>com.alipay.sofa.rpc.defaultTracer</td><td>sofa.boot.rpc.defaultTracer</td></tr><tr><td>com.alipay.sofa.rpc.dynamicConfig</td><td>sofa.boot.rpc.dynamicConfig</td></tr><tr><td>sofa.rpc.registry.disablePub</td><td>sofa.boot.rpc.registry.disablePub</td></tr><tr><td>sofa.rpc.registry.defaultRegistry</td><td>sofa.boot.rpc.registry.defaultRegistry</td></tr></tbody></table><p><strong>了解更多...</strong></p><p><strong>SOFABoot Star 一下✨:</strong> <br><a href="https://link.segmentfault.com/?enc=HjdSuqf11IaAmDwsgo%2BGTA%3D%3D.IiKXI6t5oVEY94OAa38Fvwxc2KI17bWREM%2B4p8u6V3mzoA6A6Cdkt0C0pGGgDk9H" rel="nofollow">https://github.com/sofastack/sofa-boot</a></p>
SOFAStack 的一下五年
https://segmentfault.com/a/1190000043932998
2023-06-25T16:05:04+08:00
2023-06-25T16:05:04+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043933000" alt="图片" title="图片"></p><p>文|宋顺(GitHub ID:nobodyiam)<br>SOFAStack 社区开源负责人蚂蚁集团高级技术专家 </p><p><img src="/img/remote/1460000043933001" alt="图片" title="图片"></p><p>本文 3861 字 阅读 11 分钟 </p><h2>01</h2><h3>回顾开源这五年</h3><p>回想起 2018 年 4 月 19 日 SOFAStack 首次开源,当时的官宣文章中就提到了我们开源的初心:</p><p>期望通过逐步向社区开源 SOFA 中各个组件,从而一方面帮助更多机构和合作伙伴完成金融分布式转型,帮助大家更加快速构建稳定的金融级云原生的架构,另一方面也是期望 SOFA 在蚂蚁体系之外的更大场景下去应用,来进一步锻造改进这套体系,使其更加完善和稳固。</p><p>所以这几年我们也是围绕着这个初心把 SOFAStack 体系的各个产品逐渐开源,包括首批开源的 SOFABoot、SOFARPC、SOFAArk,以及后来的 SOFARegistry、SOFAJRaft、Seata 等。同时我们也孵化了 MOSN 社区,开源了云原生网络代理 MOSN 以及应用运行时 Layotto。这些产品也是代表着 SOFAStack 在金融级云原生领域的沉淀和积累,目前已经在上百家企业中生根发芽。</p><p><img src="/img/remote/1460000043933002" alt="图片" title="图片"><br>图 1 - 产品开源时间线</p><p>我们再来看几个数字,在这 5 年中,我们举办了 18 场 Meetup,开展了 32 场直播分享,目前在 GitHub 组织层面有 438 名贡献者以及 3W 的 Star。</p><p><img src="/img/remote/1460000043933003" alt="图片" title="图片"><br>图 2 - 开源 5 年的几个数字</p><p>除了在项目上收获了不少贡献者和 Star 外,我们也逐渐地对开源有了更为深刻的认识。</p><p>以开源指标为例,我们的初心是为了帮助更多机构和合作伙伴完成金融分布式转型,我们一开始最为关注的指标是用户数;但因开源的特殊性,我们无法直接获取实际的用户数,所以采用了 GitHub 的 Star 作为衡量指标。</p><p>我相信这也是很多开源项目常见的第一反应,后期大家其实也发现了一些问题。举例来说,有些开源项目为了完成指标,采取了点 Star 送礼物等行为。这些虽然没有在我们的项目上发生,我们仍觉得有违开源初心,就放弃了 Star 数作为核心的指标。</p><p><img src="/img/remote/1460000043933004" alt="图片" title="图片"><br>图 3 - Star 数作为衡量指标</p><p>另一个开源项目常用的衡量指标是贡献者数量,很多开源项目是没有商业公司支撑的。为了项目的长期发展,需要持续的吸引新的贡献者加入,从而为社区带来活力。</p><p>我们在放弃 Star 数指标后,把重心放在了贡献者数量上,会为新贡献者提供一些简单的任务,使他们能更快的融入社区,成为潜在的长期贡献者。</p><p>不过和 Star 指标类似,在过程中我们也发现其它社区中出现了一些不好的现象。比如故意留一些明显的 bug,或者提供一些修改错别字的任务来刷贡献者数量,我们认为这些也是有违开源初心的。</p><p><img src="/img/remote/1460000043933005" alt="图片" title="图片"><br>图 4 - Contributor 数作为衡量指标</p><p>那么,我们该如何对待开源呢?</p><p>我脑海中想起了 Envoy 作者 Matt Klein 在 Envoy 开源五周年时说的一句话:成功的开源软件就像创办一个企业。</p><p><img src="/img/remote/1460000043933006" alt="图片" title="图片"><br>图 5 - 成功的开源软件就像创办一个企业</p><p>其实开源项目和创业很类似,首先你需要有一个好的点子,然后去吸引人才一起加入来提升产品能力,再通过一些营销手段对项目做推广来获取客户,而后持续迭代改进。</p><p>对企业而言,员工和客户固然很重要,不过我认为最核心的还是产品,只有一个定位准确、解决实际问题的产品才能受到市场欢迎,从而获取资金维持公司的运营。</p><p>在开源项目上,用户对应着企业的客户,是提供场景的驱动力来源;贡献者对应着企业的员工,属于资源投入,是推动力来源;而产品才是真正能解决用户问题的,是整个开源飞轮中最为核心的部分。所以 Star 数和贡献者数量都只是过程指标,核心还是要提升产品力,不能本末倒置。</p><p><img src="/img/remote/1460000043933007" alt="图片" title="图片"><br>图 6 - 开源飞轮</p><h2>02</h2><h3>展望下一个五年</h3><p>聊完了过去五年的过程和收获,对 SOFAStack 而言,下一个五年的主要方向就比较明确:我们还是会着重放在产品力的提升上,希望能持续解决分布式场景中的核心问题。</p><p>那核心问题究竟是哪些呢?我想起了泛在计算的提出者 Mark Weiser 曾经说过的一句话:最卓越的技术是那些“消失不见的”技术。</p><p><img src="/img/remote/1460000043933008" alt="图片" title="图片"><br>图 7 - 消失不见的技术</p><p>对这个判断,我也是深以为然。在日常生活中,这类案例也是比比皆是:比如电,我们现在都是即插即用,以至于已经不感知电力背后的复杂基础设施,类似的还有水、煤气等。它们背后都有着复杂的基础设施在支撑,经过了数十年的技术发展之后,已经非常稳定可靠,交互也非常简单,所以这些技术就像是“消失不见”一样。</p><p><img src="/img/remote/1460000043933009" alt="图片" title="图片"><br>图 8 - 水电煤随开随用</p><p>然而在我们实际的研发场景中,业务研发对基础设施的感知还远没有达到无感的地步。</p><p>比如在研发态,我们除了要关注业务逻辑之外,还会经常被中间件 SDK 升级所打扰,在使用云产品时还得感知多云的差异。</p><p>在运维态,除了要关注发布时的业务表现,还要时刻去关注资源状况。在其容量不足的时候要申请资源做扩容,在业务低峰的时候要缩容服务来节约成本。</p><p>可以说,技术基础设施的存在感越强,研发运维的效率就越低,无法把精力集中在业务的创新上。</p><p><img src="/img/remote/1460000043933010" alt="图片" title="图片"><br>图 9 - 实际研发场景</p><p>那么,我们该如何才能让技术基础设施也消失不见呢?</p><p>我们知道对微服务而言,可以通过服务网格来解耦业务逻辑和 RPC 通用能力,从而实现独立演进、透明升级。</p><p><img src="/img/remote/1460000043933011" alt="图片" title="图片"><br>图 10 - Service Mesh 演进架构<br>项目地址:<a href="https://link.segmentfault.com/?enc=5jWW8WYqgJTb%2FINkaSsFUA%3D%3D.cZL3g77eGuHUNYdoVzCE9nILrCxupgpiJOyxJktgXPY%3D" rel="nofollow">https://github.com/mosn/mosn</a></p><p>在实际场景中,除了微服务之外,业务往往还会使用其它的中间件能力。例如动态配置、消息、缓存、数据库等,如何降低这些中间件和业务应用的耦合是一个新的问题。另外,在多语言场景,我们仍然要为每种语言开发一套轻量 SDK 来实现通信协议和编解码逻辑,这部分也有很高的成本。所以我们如何进一步去降低多语言的支持成本是另一个亟待解决的问题。</p><p>为此,我们也是借鉴了 Dapr 的应用运行时思路,基于 MOSN 设计开发了 Layotto,在下层对接了各种基础服务;在上层为应用提供了统一的、具备各种分布式能力的 API。</p><p>开发者不需要再关心底层各种组件的实现差异,只需要关注应用本身需要哪些能力。比如调用 RPC、发送消息,然后通过 gRPC 调用对应的 API 即可。这样就可以彻底和底层基础服务解绑,同时也是极大地降低了多语言的支持成本。</p><p><img src="/img/remote/1460000043933012" alt="图片" title="图片"><br>图 11 - Layotto 架构<br>项目地址:<a href="https://link.segmentfault.com/?enc=XtJ5dk5Xabe4qhJYqHLLSQ%3D%3D.6l71rQ2xsdL8CbyzQJfCpXkSlQF9VZ7m0sDJrba126A%3D" rel="nofollow">https://github.com/mosn/layotto</a></p><p>通过服务网格和应用运行时,我们解决了研发态对中间件 SDK 升级、多云差异感知等负担,我们再来看下如何通过 Serverless 技术来降低运维态的负担。</p><p>我们知道业务研发一般是从需求到设计、开发、测试,最后发布生产的一个循环过程,其中不少业务还会出现多个迭代并行开发的场景。然而发布生产要求是串行的,就会导致迭代堵车的现象,后一个迭代必须得等前一个迭代发完才能开始发布,整体效率比较低。</p><p>除此之外,随着业务重要性的提升,发布流程也会变重,发布周期短则几个小时,长则几天甚至几周也屡见不鲜。同时,业务逻辑的增加也会导致应用启动变慢,启动一个系统往往需要几十分钟,导致应用扩容等操作响应迟缓。</p><p><img src="/img/remote/1460000043933013" alt="图片" title="图片"><br>图 12 - 研发迭代形式</p><p>我们经过分析,发现不少应用代码本质上是可以分成两层的。一层是公共逻辑和核心模型,这部分是比较稳定的,很少有变化。另一层是基于公共部分之上的逻辑,我们把它抽象为模块,模块和业务逻辑紧密相关,变化也较为频繁。</p><p>因此我们首先考虑把代码拆分成基座和模块,在基座代码库中沉淀通用逻辑,为模块提供计算支撑,同时为每个模块也创建独立的代码仓库。</p><p>在运行时通过 SOFAArk 技术实现基座和模块在同一个进程中展开,同时开发了热部署的能力从而模块可以独立于基座运维。</p><p>这样,我们就区分了基座开发者和模块开发者。基座开发者和传统的应用开发没什么区别,而模块开发者由于不再需要关注容量、资源,同时可以独立于基座运维,实现了 Serverless,具备了快速迭代和快速伸缩能力。</p><p>这个方案也存在一些不足:比如因为依赖 SOFAArk,所以主要针对 Java 场景,另外由于多个模块是在同一进程中运行,因此隔离性较差,会互相影响。</p><p><img src="/img/remote/1460000043933014" alt="图片" title="图片"><br>图 13 - SOFAArk 热部署<br>项目地址:<a href="https://link.segmentfault.com/?enc=pKNTYjo0Wl6D%2BfSfD5tnvQ%3D%3D.UNR0SOqmNaWjajY8QO4EkPlkV7Go7HEiinwTt3jaIBtHc3LlaNNVgJGv5HmUnRFW" rel="nofollow">https://github.com/sofastack/sofa-ark</a></p><p>由于 Serverless 方案存在上述提到的技术栈、隔离性等问题,在覆盖面上还是有一些盲区。结合业界的实践,我们决定继续向 FaaS 迈进。</p><p>图 14 直观地展示了应用研发粒度的演变过程:最早从单体应用到微服务,是把粒度降低到服务级别,从而解开了业务团队之间的耦合。</p><p>我们现在是继续把粒度降低到函数级别,以此来实现快写快发、免运维,从而进一步提升研发和运维效率。</p><p><img src="/img/remote/1460000043933015" alt="图片" title="图片"><br>图 14 - 应用粒度演变<br>图片来源:<a href="https://link.segmentfault.com/?enc=b%2BHflUbsseuH7VfzI9%2BVxQ%3D%3D.TDb8hTL8bFrpIWdcABvMWZIGC8gPeSW1PrR4T%2BCwjYirKT7wq9OOZauajAlfXQDAw8Mt%2Bvhs3DjAF1UP8ef%2Baeo%2BeqEqGWpL%2FgE9ILduGfHldic6EKuRhb2fnkDKXE4U" rel="nofollow">https://www.cloudflare.com/zh-cn/learning/serverless/glossary/function-as-a-service-faas/</a></p><p>考虑到函数粒度是非常小的,FaaS 的应用范围是相对有限的。我们认为下面这些场景是比较适合 FaaS 研发模式的:</p><ul><li>碎片化需求场景:例如 BFF,大多是胶水代码,逻辑简单,不过需求变化快,通过函数实现组装式开发,从而助力业务创新</li><li>事件驱动场景:例如音视频转码,大多是 CPU 密集型,对处理时间不是特别敏感,而且有着比较明显的波峰和波谷</li><li>中台业务场景:例如算法平台,它的的算子逻辑比较独立,但是参与研发人数多,所以代码逻辑不可控,需要更好的隔离能力</li></ul><p><img src="/img/remote/1460000043933016" alt="图片" title="图片"><br>图 15 - FaaS 适用场景</p><p>目前我们也在公司内部探索 FaaS ,整体产品架构如图 16 所示,未来我们也会逐步地去开源相应的组件。</p><ul><li>触发源支持 RPC、HTTP、Message、Cron 等</li><li>冷启动采用了 Cache pool、 Fork 等技术实现加速,对简单的 Node.js 和 Java 函数可以实现几百毫秒冷启动</li><li>提供了 Layotto 作为 Sidecar 帮助函数轻松访问各类 BaaS 服务,同时具备完善的治理能力</li></ul><p><img src="/img/remote/1460000043933017" alt="图片" title="图片"><br>图 16 - SOFA Function 产品架构</p><h2>03</h2><h3>致谢</h3><p>最后,值此 SOFAStack 开源五周年,还是要对大家表示感谢。</p><p>首先,我要感谢所有 SOFAStack 项目的贡献者们,无论是提交代码、撰写文档、解答问题,还是组织活动、传播理念,正是因为有了你们的不懈努力和无私奉献,才让 SOFAStack 能够不断完善和进步,使更多用户受益。</p><p><img src="/img/remote/1460000043933018" alt="图片" title="图片"><br>图 17 - SOFAStack 的贡献者</p><p>其次,我要感谢所有选择使用 SOFAStack 产品的合作伙伴和用户们,无论是金融机构、互联网企业还是个人开发者,正是因为有了你们的信任和支持,才让 SOFAStack 能够在各种复杂的场景中得到验证和应用,持续提升产品能力。</p><p><img src="/img/remote/1460000043933019" alt="图片" title="图片"><br>图 18 - SOFAStack 的用户</p><p>最后,我还要感谢所有关注和关心 SOFAStack 社区的朋友们,正是因为有了你们的鼓励和期待,才让 SOFAStack 社区持续保持活力。</p><p>那让我们保持初心,一起把 SOFAStack 社区建设得更开放、更有趣!</p><h3>了解更多...</h3><p>Layotto Star 一下<br><a href="https://link.segmentfault.com/?enc=xR86J4RFC3OpHqttjznOJg%3D%3D.6g8FR42rTdhIjkmG%2FrRcnGdMrxqhG8NxHXQq3Pto%2FL0%3D" rel="nofollow">https://github.com/mosn/layotto</a></p>
Seata Saga 模式快速入门和最佳实践
https://segmentfault.com/a/1190000043901897
2023-06-14T20:08:12+08:00
2023-06-14T20:08:12+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043901899" alt="图片" title="图片"> </p><p>文|王特 <em>(花名:亦夏)</em></p><p>Email:<a href="mailto:yixia.wt@antgroup.com">yixia.wt@antgroup.com</a></p><p>蚂蚁集团数据中间件核心开发</p><p>本文<strong>4927</strong> 字 阅读 <strong>13</strong> 分钟</p><p>Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。Seata 为用户提供了 AT、TCC、SAGA、XA 等多种事务模式,帮助解决不同业务场景下的事务一致性问题。 </p><p>本文主要介绍 Seata Saga 模式的使用以及最佳实践,围绕三个部分展开,第一部分是 Seata Saga 的简介、第二部分旨在快速介绍 Seata Saga 模式的使用方法并帮助大家入门,最后一部分将会给大家分享一些 Seata Saga 实践中的经验,帮助用户更快、更好得使用 Seata Saga 模式。</p><h2>1 Seata Saga 简介</h2><h3>1.1 Saga 模式</h3><p>Saga 模式是分布式事务的解决方案之一,理念起源于 1987 年 Hector & Kenneth 发表的 Sagas 论文。它将整个分布式事务流程拆分成多个阶段,每个阶段对应我们的子事务,子事务是本地事务执行的,执行完成就会真实提交。 </p><p><img src="/img/remote/1460000043901900" alt="图片" title="图片"></p><p>它是一种基于失败的设计,如上图可以看到,每个活动或者子事务流程,一般都会有对应的补偿服务。如果分布式事务发生异常的话,在 Saga 模式中,就要进行所谓的“恢复”,恢复有两种方式,<strong>逆向补偿</strong>和<strong>正向重试</strong>。比如上面的分布式事务执行到 T3 失败,逆向补偿将会依次执行对应的 C3、C2、C1 操作,取消事务活动的“影响”。那正向补偿,它是一往无前的,T3 失败了,会进行不断的重试,然后继续按照流程执行 T4、T5 等。</p><p>根据 Saga 模式的设计,我们可以得到 Saga 事务模式的优缺点。</p><p><strong>优点:</strong></p><ul><li>子事务 <em>(或流程)</em> ,提交是本地事务级别的,没有所谓的全局锁,在长事务流程下,避免了长时间的资源锁定;另外这种流水线的处理模型天然符合阶段式信号处理模型,能发掘出更高的性能和吞吐。</li><li>正向服务和补偿服务都是交给业务开发实现的,所以 Saga 模式和底层数据库协议是无关的。XA/AT 模式可能依赖特定的数据库类型和版本,比如 MySQL 是 5.0 之后才支持的 XA,那么低版本的 MySQL 就不能适用到 XA 模式。</li></ul><p><strong>缺点:</strong></p><ul><li>也是因为正向服务和补偿服务都由业务开发者实现,所以业务上是有开发成本的,侵入性相对 XA/AT 打一个注解的方式会高很多。</li><li>因为一阶段子事务活动提交是本地事务级别的,所以 Saga 模式不保证隔离性。提交之后就可能“影响”其他分布式事务、或者被其他分布式事务所“影响”。例如:其他分布式事务读取到了当前未完成分布式事务中子事务的更新,导致脏读;其他分布式事务更新了当前未完成分布式事务子事务更新过的字段,导致当前事物更新丢失;还有不可重复读的场景等。</li></ul><p>所以 Saga 模式的使用也需要考虑这些问题带来的“影响”。一般 Saga 模式的使用场景有如下几个:</p><ul><li>长事务流程,业务上难以接受长时间的资源锁定,Saga 的特性使得它在长事务流程上处理非常容易;</li><li>业务性质上,业务可以接受或者解决缺乏隔离性导致的“影响”。例如部分业务只要求最终一致性,对于隔离性要求没有那么严格,其实是可以落地 Saga 模式的;</li><li>分布式事务参与者包含其他机构或者三方的服务,数据资源服务不是我们自身维护,无法提供 TCC 模式要求的几个接口。</li></ul><h3>1.2 Seata Saga</h3><p>接下来我们来看看 Seata Saga 的实现。Saga 主流的实现分为两种:编排式和协调式。<strong>Seata Saga 的实现方式是编排式,是基于状态机引擎实现的。</strong> 状态机执行的最小单位是节点:节点可以表示一个服务调用,对应 Saga 事务就是子事务活动或流程,也可以配置其补偿节点,通过链路的串联,编排出一个状态机调用流程。在 Seata 里,调用流程目前使用 JSON 描述,由状态机引擎驱动执行,当异常的时候,我们也可以选择补偿策略,由 Seata 协调者端触发事务补偿。</p><p>有没有感觉像是服务编排,区别于服务编排,Seata Saga 状态机是 <strong>Saga+服务编排</strong>,支持补偿服务,保证最终一致性。</p><p>我们来看看一个简单的状态机流程定义:</p><p><img src="/img/remote/1460000043901901" alt="图片" title="图片"></p><p>上方是一个 Name 为 <code>reduceIncentoryAndBalance</code> 的状态机描述,里面定了 ServiceTask 类型的服务调用节点以及对应的补偿节点 <code>CompensateReduceInventory</code>。</p><p>看看几个基本的属性:</p><ul><li>Type:节点类型,Seata Saga 支持多种类型的节点。例如 ServiceTask 是服务调用节点</li><li>ServiceName/ServiceMethod:标识 ServiceTask 服务及对应方法</li><li>Input/Output:定义输入输出参数,输入输出参数取值目前使用的是 SPEL 表达式</li><li>Retry:控制重试流程</li><li>Catch/Next:用于流程控制、衔接,串联整个状态机流程</li></ul><p>更多类型和语法可以参考 Seata 官方文档[1],可以看到状态机 JSON 声明还是有些难度的,为了简化状态机 JSON 的编写,我们也提供了可视化的编排界面[2],如下所示,编排了一个较为复杂的流程。</p><p><img src="/img/remote/1460000043901902" alt="1.png" title="1.png"></p><p>话不多说,我们进入下面的实践环节。</p><h2>2 Seata Saga 使用入门</h2><h3>2.1 从 Seata 官网新人文档开始</h3><blockquote><p>Seata 分 TC、TM 和 RM 三个角色,TC <em>(Server 端)</em> 为单独服务端部署,TM 和 RM <em>(Client 端)</em> 由业务系统集成。</p><p>Server 端存储模式 <em>(store.mode)</em> 现有 file、db、redis 三种 <em>(后续将引入 Raft、MongoDB)</em> ,file 模式无需改动,直接启动即可。</p></blockquote><p><strong>部署 Seata Server</strong></p><p>从新人文档,可以看出 Seata 还是传统的 CS 模型。首先我们需要部署 Seata Server 端。Server 端默认的存储模式时 file 模式,无需改动,直接执行 SpringBoot 启动类 main 方法即可启动 Seata Server。为了方便,本次演示就使用 file 模式启动,其他模式的启动方式可以参考新人文档的详细介绍。</p><p><strong>创建 Client 端测试应用</strong> </p><p>同时我们需要创建一个客户端的测试应用,这里命名 <code>seata-saga-test</code>,测试应用使用 SpringBoot 框架,配置好 spring 的 aplication.pname 和 port,并且引入 <code>seata-spring-boot-starter</code> 依赖,完成 Client 端应用的搭建。</p><p><img src="/img/remote/1460000043901903" alt="图片" title="图片"></p><h3>2.2 从 Seata Saga 单元测试看起</h3><p>一般了解一个框架的功能,建议是从入口的单元测试类开始看起。在 Seata 仓库中找到 Seata Saga 的 test 模块,从最外围的测试类 <code>io.seata.saga.engine.StateMachineTests</code> 看起 <em>(一般开源项目最外围的测试类即是入口类)</em> : </p><p><img src="/img/remote/1460000043901904" alt="图片" title="图片"></p><p>从上面的截图可以看出,入口测试方法主要分为三个部分:</p><p>【1】处的 spring 配置文件声明了 StateMachineEngine Bean 以及对应的属性,【2】处也引用了该类执行 start,判断该类为我们状态机的入口类,其实 StateMachineEngine 该类也就是 Seata Saga 状态机操作入口,控制状态机的开始、恢复等操作。StateMachineEngine 有一个重要的属性 resources,该属性声明了状态机 JSON 文件的存储路径,Seata Saga 状态机引擎启动的时候会加载对应路径下的状态机定义,以供后续使用,这里的路径根据我们需求更改。</p><p>【3】处调用了 StateMachineEngine 的 start 方法,传递状态机名称、启动参数,开启一个状态机流程调用。简单跟下实现,可以看到其中状态名称对应 resources 路径下状态机 JSON 定义中的 Name 属性。</p><p>测试 Seata Saga 状态机流程,我们得先有一个状态机 JSON 定义。使用 Seata Saga StateMachine Designer[3],定义一个简单 AService#doA 方法调用 BService#doB 方法的状态机流程,再加个入参,最终我们的类#方法和状态机 JSON 如下所示。</p><p><img src="/img/remote/1460000043901905" alt="图片" title="图片"></p><p>有了基础的调用模型和状态机 JSON 定义,按照测试用例,我们同样声明出状态机 Bean 及执行入口 <em>(注意:start 方法里面的状态机名称需要和状态机 JSON 定义里面的 Name 名称保持一致)</em> ,执行下 main 方法,我们可以发现 AService#doA 方法和 BService#doB 方法都被成功调用了。</p><p><img src="/img/remote/1460000043901906" alt="图片" title="图片"></p><p>至此,我们已经完成了 Seata Saga 状态机的入门使用。继续观察单测,我们发现 Seata Saga 单测还有两个模块,分别是 db 和 mock。</p><p><img src="/img/remote/1460000043901907" alt="图片" title="图片"></p><p>我们先来看看 db 模块的单测,可以看到 db 模块的单测类和上面基本类似,唯一的区别就在于 StateMachineEngine,指定了 db 存储,执行了 DDL SQL <em>(初始化 Seata Saga 相关表)</em> 。指定了 db 存储,那么我们的状态机执行过程将会持久化在 db 存储,方便事务执行过程查询和异常恢复,也是生产环境的实践方式。</p><p><img src="/img/remote/1460000043901908" alt="图片" title="图片"></p><p>mock 模块通过 Mock Transcation,脱离 Seata Sever,仅使用了 Seata Saga 的服务编排能力。有兴趣的同学可以再去实践下 db 和 mock 模块的使用,这里就不展开了。</p><h2>Seata Saga 最佳实践</h2><h3>3.1 基本使用</h3><ul><li>在应用层面,Seata Saga 状态机模式使用上不同于 AT、TCC 注解化方式,要使用状态机 API 执行;</li><li>在状态机模式里面,恢复策略分为向前重试和向后补偿,根据业务场景,要选择合适的补偿策略;</li><li>Seata Saga 支持异步状态执行、状态机异步执行,适时使用异步,可以提升整个系统的吞吐量。</li></ul><h3>3.2 Saga 服务</h3><ul><li>Saga 服务可能被多次调用,所以要保证幂等</li><li>补偿服务较原服务可能先执行、需要允许空补偿、同时需要拒绝后续的原服务请求,进行防悬挂控制</li></ul><h3>3.3 隔离性问题应对</h3><ul><li>业务驱动,如果业务上可以接受缺乏隔离性的影响,可以不用做任何操作</li><li>语义锁,对操作资源进行语义级别的锁定</li><li>使用悲观流程,例如 A->B 转账操作,先给 B 加钱,再给 B 减钱;换成悲观视图就是先给 A 减钱,再给 B 加钱,防止 B 加钱之后立刻消费导致的短款问题</li><li>其它方式</li></ul><h3>3.4 稳定性</h3><p>基于 db 存储的 Saga 模式,需要注意:重试或者补偿默认会插入一条状态执行记录,频繁重试或者补偿,会导致状态执行记录爆炸,如果有大对象存储,可能会导致内存 crash。Seata Saga 提供了 update 模式,使用 update 记录代替新增执行记录,用来避免此类问题。</p><h3>3.5 扩展</h3><ul><li>Seata Saga 状态机存储、语法解析等都是面向 SPI 设计的,业务上可以平滑替换对应的存储或者状态机语言实现。例如将状态机的 JSON 解析替换到 YAML 解析。</li><li>Seata Saga 支持 Mock Transaction 的方式,仅使用服务编排能力,也支持状态机定义 <em>(JSON)</em> 动态发布,也就是编排的动态发布,这一点在做 DSL 动态管控端的时候将会非常有用。</li></ul><p>讲了这么多,Seata Saga 目前状态机的实现,上手成本相对还是比较高。一方面我们致力提升 Seata Saga 状态机模式的易用性,同时也在设计 Saga 的注解化模式、流式编排模式,期望提供给用户更具产品化能力的 Seata Saga。有兴趣的同学,也非常欢迎加入共建。</p><p><strong>Seata Group 开源交流群:44816898</strong></p><p><strong>相关链接:</strong> </p><p>[1]Seata 官方文档:</p><p><em><a href="https://link.segmentfault.com/?enc=7USOXDWxUwlSO30zCZJOhw%3D%3D.K%2B0pNUieVBNhIIiRH6Tl8vQ1seGTkN%2B4OvVBODAw8XNq6GHxGqvTEF5MKPdNYJEZ" rel="nofollow">http://seata.io/zh-cn/docs/user/saga.html</a></em></p><p>[2]可视化的编排界面</p><p><em><a href="https://link.segmentfault.com/?enc=QtFLAEauUwRT69GMjlUbJQ%3D%3D.CAUOllPq%2BbmzwXYGCrONlFCAuUF8R1httwpIhuFyXdRiV%2FGZomuBftxSUtO53uCi" rel="nofollow">http://seata.io/saga_designer/index.html#/</a></em></p><p>[3]Seata Saga StateMachine Designer:</p><p><em><a href="https://link.segmentfault.com/?enc=WD%2BHolSRfMazUIId%2BNS0pw%3D%3D.la1OqzJyMxBHii2%2BbpwqV6vF3yaI%2BrLUM4uJrLBgkFWEq9ZYVj3%2FKlNcBxHiw%2BCS" rel="nofollow">http://seata.io/saga_designer/index.html#/</a></em></p><p><strong>Seata Star 一下✨:</strong> <br><strong><a href="https://link.segmentfault.com/?enc=DTfFRqzJMrXkeZJgM7te9g%3D%3D.5LraSbztvr7rsKJT3S6OJ8pqoAH42CLsoPU4uXDgl8w%3D" rel="nofollow">https://github.com/seata/seata</a></strong></p>
生产环境可用的 Seata-go 1.2.0 来啦!!!
https://segmentfault.com/a/1190000043901190
2023-06-14T18:09:13+08:00
2023-06-14T18:09:13+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043901192" alt="图片" title="图片"> </p><p>文|刘月财(GitHub ID:luky116)</p><p>360 服务端开发专家</p><p>Seata-go 项目负责人</p><p>本文 <strong>2752</strong> 字 阅读 <strong>7</strong> 分钟</p><p><strong>发布概览</strong></p><p>Seata-go 1.2.0 版本支持 XA 模式。XA 协议是由 X/Open 组织提出的分布式事务处理规范,其优点是对业务代码无侵入。当前 Seata-go 的 XA 模式支持 MySQL 数据库。至此,Seata-go 已经集齐 AT、TCC、Saga 和 XA 四种事务模式,完成了与 Seata Java 的功能对齐。</p><p><strong>XA 模式的主要功能:</strong></p><ul><li>支持了 XA 数据源代理</li><li>支持了 XA 事务模式</li></ul><p>XA 相关的 Samples 可以参考示例:</p><p><a href="https://link.segmentfault.com/?enc=Mc8DU2rT5G2WyhCaVJdZ4w%3D%3D.703rJGMK5xwSvAyVgJ1bnuG0O8NSw8KrPzwkIjybdM9f2sqNKj%2FMZ83JEd2ccfH2MUh%2Bu4BucygdSE7r4xpmFQ%3D%3D" rel="nofollow">https://github.com/seata/seata-go-samples/tree/main/xa</a></p><p>在本版本中还修复了近期大量用户在使用过程中提交的 issue。 </p><p><strong>版本的主要更新如下</strong> </p><p><strong>Feature:</strong></p><p>[#467] 实现 XA 模式支持 MySQL</p><p><em><a href="https://link.segmentfault.com/?enc=jQHpNPnmeoOgJjBU2gza5Q%3D%3D.rIWzXf66lRoPRgRgo6vgfND0scQnjTihtBZEHiEGOIr73RWDrDnzxK0dn1daXNFs" rel="nofollow">https://github.com/seata/seata-go/pull/467</a></em></p><p>[#534] 支持 Session 的负载均衡 </p><p><em><a href="https://link.segmentfault.com/?enc=hXRoX3JOtFDFY8rjt%2Fp7Sg%3D%3D.FSsDOjnYoBeBsx38K8piQ77vSjdqWRnvSJGG1E3kNdX1ibukkPqnh%2BU2Q31lp4DJ" rel="nofollow">https://github.com/seata/seata-go/pull/534</a></em></p><p><strong>Bugfix:</strong></p><p>[#540] 修复初始化 XA 模式的 bug </p><p><em><a href="https://link.segmentfault.com/?enc=n710O1OsN4IB7O0yaWqnyQ%3D%3D.sDTUlhJDCa4tHo5mXGNsqleymR6dLVC2X%2FfRVgQrBF%2Fmt9Cyc7ZYjHSXCr7Uj7hT" rel="nofollow">https://github.com/seata/seata-go/pull/540</a></em></p><p>[#545] 修复 XA 模式获取 db 版本号的 bug</p><p><em><a href="https://link.segmentfault.com/?enc=wOFnBJlyHGv334RrXt3DjA%3D%3D.xzyz80C2mPTeUssXG2VvYEbkOJQvZDlBsafeDpPWuBevkh1OXhtCHvcQ15fjYp%2Bf" rel="nofollow">https://github.com/seata/seata-go/pull/545</a></em></p><p>[#548] 修复启动 XA 时候会失败的 bug</p><p><em><a href="https://link.segmentfault.com/?enc=Z3fyqV2nn7Q1nfDpMpwuyw%3D%3D.Qrcoe3crW0yLX%2FbPSKqE1961%2BgiEha0FgxSLYtLiakgLQ2X8Yt3f8qYGQ4V7X9QN" rel="nofollow">https://github.com/seata/seata-go/pull/548</a></em></p><p>[#556] 修复 XA 数据源的 bug</p><p><em><a href="https://link.segmentfault.com/?enc=guhv3GzozdLM5zZ%2BqMbxuQ%3D%3D.JzD1z9xfKPpXNKN4IMEfFZX%2FvyyO%2BIcUCJl5HF172qIYtNIMHoGw0FQPGqKGkvLt" rel="nofollow">https://github.com/seata/seata-go/pull/556</a></em></p><p>[#562] 修复提交 XA 全局事务的 bug</p><p><em><a href="https://link.segmentfault.com/?enc=Z5jaUN2tC%2FmrG9bdTLlw8Q%3D%3D.jil7Qg%2FZUF%2FK9ULsTrGuYI8tVVTdaS7nQ7w9gS2E8%2F7I348y0wH7qWCjjqwFdQFV" rel="nofollow">https://github.com/seata/seata-go/pull/562</a></em></p><p>[#564] 修复提交 XA 分支事务的 bug </p><p><em><a href="https://link.segmentfault.com/?enc=BUrtlv8HuCL0vYITSsmhpg%3D%3D.2yqa%2FnZdwGze6pKKq5J1mYjaFZzwo8IPxmN7atvZH5dYy%2BQdVwO7fG%2BKdlJXkgFC" rel="nofollow">https://github.com/seata/seata-go/pull/564</a></em></p><p>[#566] 修复使用 XA 数据源执行本地事务的 bug</p><p><em><a href="https://link.segmentfault.com/?enc=C8T13fLSjH%2FUbejDG6GCtw%3D%3D.DHGqKML0Qp02LDE%2BaEqy1b3HCEujegknfIRbFS6nIHueBbpVxrdpmbJs52JRElNX" rel="nofollow">https://github.com/seata/seata-go/pull/566</a></em></p><p><strong>Optimize:</strong></p><p>[#523] 优化 CI 流程</p><p><em><a href="https://link.segmentfault.com/?enc=JkbB9ZrHxarA74QONp%2F2OQ%3D%3D.7d4WNWfsC2h7CIZppVpo3Sn%2FnrG8ZEwpuwPQ7M6WUkGIpG22jUssuwz0ivvlwUUf" rel="nofollow">https://github.com/seata/seata-go/pull/523</a></em></p><p>[#525] 将 Jackson 序列化重命名为 JSON</p><p><em><a href="https://link.segmentfault.com/?enc=kldaMCKNZmht1wqLJ1oHaQ%3D%3D.eAqdK64Tn1AU92zKPiXuZoliIs6Dp68ajjzwjba0CU%2BHiwwSdiprweeDNxOtG0mT" rel="nofollow">https://github.com/seata/seata-go/pull/525</a></em></p><p>[#532] 移除重复的代码</p><p><em><a href="https://link.segmentfault.com/?enc=3cnwnQiSxo6q6nIZ5I6vfg%3D%3D.KlU0r9r0I513fcdbSlqXIjrlfr4Tsq5EG%2F8PTIFbO2VtjL5SE8Bl2IWDMmMoG0SB" rel="nofollow">https://github.com/seata/seata-go/pull/532</a></em></p><p>[#536] 优化 go import 代码格式</p><p><em><a href="https://link.segmentfault.com/?enc=UG85qcN8AMWqBSMoqzDqaQ%3D%3D.c%2Fgco1YUMH8IL%2FH0OiuS0IQVcSgGqfK2e%2FGRc8MjE3K2h%2BVzF%2FUsyE8aLZN1Zg0A" rel="nofollow">https://github.com/seata/seata-go/pull/536</a></em></p><p>[#554] 优化 XA 模式的性能</p><p><em><a href="https://link.segmentfault.com/?enc=CqkRmWpsS33caUt1jkGG%2BQ%3D%3D.i%2BJs0iPFB%2FHeyJ3%2BXW7uldcTi4fz8ufWN9gqkICfvgHQWh30puFlz6GpejORUupw" rel="nofollow">https://github.com/seata/seata-go/pull/</a></em> <em>554</em></p><p>[#561] 优化 XA 模式的日志输出</p><p><em><a href="https://link.segmentfault.com/?enc=T4LkjCRQeukQmyHQylerCw%3D%3D.yaRyg7G2%2Fxs7J6T1IzAJc1Lod7bZRdpJMD5N%2FzJhocgu%2BCTrnv8qtTNf8cwpphkY" rel="nofollow">https://github.com/seata/seata-go/pull/561</a></em></p><p><strong>Test:</strong></p><p>[#535] 添加集成测试 </p><p><em><a href="https://link.segmentfault.com/?enc=0WZA22BRNxc1Tu9CSGzICw%3D%3D.CTTpX4AJqD9zBIepkg4FaptzGgTqH6W4LaiX4eFphPL4fV5Z9LV3tnV3FKCdA6ZR" rel="nofollow">https://github.com/seata/seata-go/pull/535</a></em></p><p><strong>Doc:</strong></p><p>[#550] 添加 1.2.0 版本的改动日志 </p><p><em><a href="https://link.segmentfault.com/?enc=dEE1S43UIpobSjt6bUOHGg%3D%3D.BhIkIfY6g1dKveFxWL1Pjx%2B7jo%2Fkpu05fkm4J6iZrScUbLReUpBOqOKZRKL04b5T" rel="nofollow">https://github.com/seata/seata-go/pull/550</a></em></p><p>英文版:<em><a href="https://link.segmentfault.com/?enc=SQxXSaPyaw9mFw6ak%2FtHmw%3D%3D.01piQdBxQumPlR2MNMMyUO%2BJ%2FopvGvkl%2Ftvr8mFsZDL74mxQ%2FGMn%2F1nvkfex7YKxVCrFaqabz1eClkdKDYzbEA%3D%3D" rel="nofollow">https://github.com/seata/seata-go/releases/tag/v1.2.0</a></em></p><p><strong>致谢</strong></p><p>非常感谢以下 Contributors 的代码贡献。若有无意遗漏,请报告。</p><p>@georgehao</p><p><em><a href="https://link.segmentfault.com/?enc=Qx874RXYmwocXlPJ3wqITw%3D%3D.bj1UfLPA5mKqGWC1E9g2z9JkG4t8I8k%2FcG9GxDr2Wn0%3D" rel="nofollow">https://github.com/georgehao</a></em></p><p>@luky116</p><p><em><a href="https://link.segmentfault.com/?enc=t061NBtfsA9RW%2BN3BNML%2Bw%3D%3D.9TBTVCGTjiW4aPDhygtzHs9iRMZJzeLZDMo8DgngCOg%3D" rel="nofollow">https://github.com/luky116</a></em></p><p>@jasondeng1997</p><p><em><a href="https://link.segmentfault.com/?enc=gJulUodeNVpUe%2B%2BvnV%2FLPg%3D%3D.VLSYWLEZ2duS4iteu54saiIZD7g0OmQIHtpfl9JbWn%2F7e1DWYx%2F5d8BjkMmWj1%2FK" rel="nofollow">https://github.com/jasondeng1997</a></em></p><p>@106umao</p><p><em><a href="https://link.segmentfault.com/?enc=R2COjQkSOfJtYrIMT8FSOQ%3D%3D.l22GLY2yTDzL4B2p7BFaKNdkTIIm82%2Bek5n9KArQAhg%3D" rel="nofollow">https://github.com/106umao</a></em></p><p>@wang1309</p><p><em><a href="https://link.segmentfault.com/?enc=dVpFr8YnSyLyY86P8ZI7Aw%3D%3D.6BlGhJ0P0D9f4OOi0SLsuH%2BPHfaUvGgf18E0eSnGaXU%3D" rel="nofollow">https://github.com/wang1309</a></em></p><p>@iSuperCoder</p><p><em><a href="https://link.segmentfault.com/?enc=%2FGCgfOD6IHrME%2BPuLDsGUw%3D%3D.8mvNk%2BxLGUjVc4DlfZ8leDs1U2Cqe%2BNQy90jDJm1goM%3D" rel="nofollow">https://github.com/iSuperCoder</a></em></p><p>@Charlie17Li</p><p><em><a href="https://link.segmentfault.com/?enc=TzAjOI62czVXDeQXKpwM6g%3D%3D.8s3u54zi7Wh2arpSsbsqlweYZG6rpnqGrIzYiciBhL0%3D" rel="nofollow">https://github.com/Charlie17Li</a></em></p><p>@Code-Fight</p><p><em><a href="https://link.segmentfault.com/?enc=u0UUKL5zUINxVtuDZ8uodg%3D%3D.IjNHJ8FsQJifdsLEB4IKl04T7Rkqzm43K3o%2BwxlZ4hg%3D" rel="nofollow">https://github.com/Code-Fight</a></em></p><p>@Kirhaku</p><p><em><a href="https://link.segmentfault.com/?enc=0gdWfT5jGkAIMkDQIYrnbA%3D%3D.y0HzRZBB03VYC88elA05bGszrsJMlxR80Pp%2FIJvQxsc%3D" rel="nofollow">https://github.com/Kirhaku</a></em></p><p>@Vaderkai</p><p><em><a href="https://link.segmentfault.com/?enc=2K02bt3EjUAmxDDgx7MXXQ%3D%3D.nq10tecg70ZRxrjQugjI4%2BrzXwcwIjUmleWjeS7045E%3D" rel="nofollow">https://github.com/VaderKai</a></em></p><p>同时,我们收到了社区反馈的很多有价值的 issue 和建议,非常感谢大家。</p><p><strong>社区讨论群</strong></p><p><strong>扫码加入钉钉群:</strong></p><p>Seata-go 社区群:<strong>33069364</strong></p><p><img src="/img/remote/1460000043901193" alt="图片" title="图片"></p><p>Seata-go 开发群:<strong>44816898</strong><br><img src="/img/remote/1460000043901194" alt="图片" title="图片"></p><p><strong>未来展望</strong></p><p>Seata 社区近期与不少国内 Go 语言微服务框架以及 ORM 框架背后的开发社区达成合作,比如 GORM 框架,已经集成到了 Sample 中,后续会将更多的 ORM 框架集成在 Seata-go-samples 项目中。</p><p>Seata-go-samples 集成到 Seata-go GitHub Actions 的集成测试环境,目前已经在进行中,用于测试每个 PR,保证系统的兼容性与稳定性。 <br>Seata-go 后续的 Saga 模式,计划采用 Temporal 框架来做服务编排,目前正在规划中,期待能给用户带来更实用便利的 Saga 使用体验。</p><p>欢迎对开源感兴趣的朋友加入 Seata 开源建设中来。 </p><p><strong>常用链接</strong></p><p><strong>Seata:</strong></p><p><em><a href="https://link.segmentfault.com/?enc=ER2%2BD71H6XkqTBXdy6%2BMDA%3D%3D.1b1EYAYAUKgMW4YJ1mqaw0uE67%2BCZEMVsdL5r58whvQ%3D" rel="nofollow">http://github.com/seata/seata</a></em> </p><p><em><a href="https://link.segmentfault.com/?enc=Oo%2BL7s0eQWKbim6eU6by3Q%3D%3D.MwtVDpyypgcFNreLWBnXg2lWUVrFM8Cx%2B5ccZlx7q4As7rf8Wh%2B4T%2BitDc05BduU" rel="nofollow">https://github.com/seata/seata-php</a></em></p><p><em><a href="https://link.segmentfault.com/?enc=GeToetjkAwR%2FoeAA7TBSIw%3D%3D.L4KTNh47P7Ocuw80%2BTFfcq6Ndf8mijsn%2FRHuQBJmvW5HmU6OSd5fydhzv%2Fy7STgB" rel="nofollow">https://github.com/seata/seata-js</a></em></p><p><em><a href="https://link.segmentfault.com/?enc=ofL4Hmgh6oYbJ7CLhPh34w%3D%3D.pB67MpvuTeHojddyhBdJcHASPRpylcR5Y6SV1OnslsI1iXTXF9Zp7OzWtkLIuUim" rel="nofollow">https://github.com/seata/seata-go</a></em></p><p><strong>Samples:</strong></p><p><em><a href="https://link.segmentfault.com/?enc=A5StURlf4GAu0eACNRdNSQ%3D%3D.vSFjSlz0B1WQ8fhpZCVoIw362MZCYo8XH%2FtK2tEo1OsfjLgX3ayJS5I3R%2B%2Bt8e1z" rel="nofollow">https://github.com/seata/seata-samples</a></em> </p><p><em><a href="https://link.segmentfault.com/?enc=ZHvI4aKmD%2FV7RgC9adfKPQ%3D%3D.xv7JXOLSq3337P5XyffhepbuZZLACc%2FA5Dx7xSwwLjX0YjmxXFA241%2B1ymQStjVD" rel="nofollow">https://github.com/seata/seata-go-samples</a></em></p><p><strong>官网:</strong></p><p><em><a href="https://link.segmentfault.com/?enc=xPSNAONcSAv6rvR8iKF0VA%3D%3D.%2FzDhqp4UOv%2FsFug5aW4utuRzTIfWVIIuKguFTHqJoMo%3D" rel="nofollow">https://seata.io/</a></em> </p><p><strong>投稿</strong></p><p>欢迎大家将 Seata/Seata-go/Seata-php/Seata-js 相关的实践文章投稿至:<em><a href="https://link.segmentfault.com/?enc=TO7ZF0yifCCP%2FlObuv1mHA%3D%3D.pfHENNNrHkQNvj%2BpD0iF48wcxnHpz8l6R8mCP8A9fFPYECb8Wl3PJv93fBdK2qjNWokpGZnuTpHX%2FR55BcvOwA%3D%3D" rel="nofollow">https://www.yuque.com/fred-x/ngfgiz/le1h4u5kn0xyhhoh</a></em></p><p><strong>Seata Star 一下✨:</strong> <br><strong><a href="https://link.segmentfault.com/?enc=aubA3fpE%2FiYqUHvvFLTMlA%3D%3D.%2BZYcStRrYgom18mOKRp3KaVEolvatfmeI1KYKxyro%2FdllrEe1hOB0cgiGsNkpZUg" rel="nofollow">https://github.com/seata/seata-go</a></strong></p>
MoE 系列(五)|Envoy Go 扩展之内存安全
https://segmentfault.com/a/1190000043849098
2023-05-31T11:29:20+08:00
2023-05-31T11:29:20+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><strong>前面几篇介绍了 Envoy Go 扩展的基本用法,接下来几篇将介绍实现机制和原理。</strong></p><p>Envoy 是 C++ 实现的,那 Envoy Go 扩展,本质上就相当于把 Go 语言嵌入 C++ 里了。</p><p>在 Go 圈里,将 Go 当做嵌入式语言来用的,貌似并不太多见,这里面细节还是比较多的。比如:</p><ol><li>Envoy 有一套自己的内存管理机制,而 Go 又是一门自带 GC 的语言。</li><li>Envoy 是基于 libevent 封装的事件驱动,而 Go 又是包含了抢占式的协程调度。</li></ol><p>为了降低用户开发时的心智负担,我们提供了三种安全保障。有了这三层保障,用户写 Go 来扩展 Envoy 的时候,就可以像平常写 Go 代码一样简单,而不必关心这些底层细节。</p><h2>三种安全</h2><h3>1. 内存安全</h3><p>用户通过 API 获取到的内存对象,可以当做普通的 Go 对象来使用。</p><p>比如,通过 Headers.Get 得到的字符串,在请求结束之后还可以使用,而不用担心请求已经在 Envoy 侧结束了,导致这个字符串被提前释放了。</p><h3>2. 并发安全</h3><p>当启用协程的时候,我们的 Go 代码将会运行在另外的 Go 线程上,而不是在当前的 Envoy worker 线程上,此时对于同一个请求,则存在 Envoy worker 线程和 Go 线程的并发。</p><p>但是,用户并不需要关心这个细节,我们提供的 API 都是并发安全的,用户可以不感知并发的存在。</p><h3>3. 沙箱安全</h3><p>这一条是针对宿主 Envoy 的保障,因为我们并不希望某一个 Go 扩展的异常,把整个 Envoy 进程搞崩溃。</p><p>目前我们提供的是,Go Runtime 可以 recover 的有限沙箱安全,这通常也足够了。</p><p>更深度的,Runtime 不能 recover 的,比如 Map 并发访问,则只能将 Go So 重载,重建整个 Go Runtime 了,这个后续也可以加上。</p><h2>内存安全实现机制</h2><p>要提供安全的内存机制,最简单的办法,也是 <em>(几乎)</em> 唯一的办法,就是复制。但是,什么时候复制、怎么复制,还是有一些讲究的。这里权衡的目标是降低复制的开销,提升性能。</p><p>这里讲的内存安全,还不涉及并发时的内存安全,只是 Envoy <em>(C++)</em> 和 Go 这两个语言运行时之间的差异。</p><p>PS:以前用 OpenResty 的时候,也是复制的玩法,只是有一点区别是,Lua String 的 Internal 归一化在大内存场景下,会有相对较大的开销;Go String 则没有这一层开销,只有 Memory Copy + GC 的开销。</p><h3>复制时机</h3><p>首先是复制时机,我们选择了按需复制,比如 Header,Body Data 并不是一开始就复制到 Go 里面,只有在对应的 API 调用时,才会真的去 Envoy 侧获取&复制。</p><p>如果没有被真实需要,则并不会产生复制,这个优化对于 Header 这种常用的,效果倒是不太明显,对于 Body 这种经常不需要获取内容的,效果则会比较的明显。</p><h3>复制方式</h3><p>另一个则是复制方式,比如 Header 获取上,我们采用的是在 Go 侧预先申请内存,在 C++ 侧完成赋值的方式,这样我们只需要一次内存赋值即可完成。</p><p>这里值得一提的是,因为我们在进入 Go 的时候,已经把 Header 的大小传给了 Go,所以我们可以在 Go 侧预先分配好需要的内存。</p><p>不过呢,这个玩法确实有点 tricky,并不是 Go 文档上注明推荐的用法,但是也确实是我们发现的最优的解法了。</p><p>如果按照 Go 常规的玩法,我们可能需要一次半或两次内存拷贝,才能保证安全,这里有个半次的差异,就是我们下回要说的并发造成的。</p><p>另外,在 API 实现上,我们并不是每次获取一个 Header,而是直接一次性把所有的 Header 全复制过来,在 Go 侧缓存了。这是因为大多数场景下,我们需要获取的 Header 数量会有多个,在权衡了 CGO 的调用开销和内存拷贝的开销之后,我们认为一次性全拷贝是更优的选择。</p><h2>最后</h2><p>相对来说,不考虑并发的内存安全,还是比较简单的,只有复制最安全,需要权衡考虑的则更多是优化的事情了。</p><p>比较复杂的还是并发时的安全处理,这个我们下回再聊。</p><h2>MOSN Star 一下✨:</h2><p><a href="https://link.segmentfault.com/?enc=8sLp1NaEd3wfi31BhJRgDQ%3D%3D.D3lHoge0A%2BtOzV4YemPk5%2FsnPN7b2e0Do%2Bgi0cfgtKc%3D" rel="nofollow">https://github.com/mosn/mosn</a></p><h2>推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=yW6mojUPAXmoEFJ3R7qGYQ%3D%3D.p1e8A84ebXwUGK10ZgOrmlzgXCTm3xl0pOpsbKbC4WoxYKFWch0DlUKy8hcyYSqyoIv9kVx4q%2BBuiRMpQYz7Dw%3D%3D" rel="nofollow">MoE 系列(一)|如何使用 Golang 扩展 Envoy</a></p><p><a href="https://link.segmentfault.com/?enc=Dq0%2FY3LMgc%2B7OSWmM0pWRQ%3D%3D.sb9DX%2BICK7CtqkoCeqoUtnuPhr8vSkfuFwWIiRhHf6QdfaIuhge4hXvoJvT6DbmalGzI398VGe%2F6al5tw2mOWA%3D%3D" rel="nofollow">MoE 系列(二)|Golang 扩展从 Envoy 接收配置</a></p><p><a href="https://link.segmentfault.com/?enc=q5xUGHhnjNDkKJmgNuqehA%3D%3D.r938VtZn8vkQsrzCbYO71IAXIuSw5U82DhwCdPZ%2FwhPwJOrfsRM%2B0aukLQiGEGDfVDt4yq4zKzsztITHvg%2BTjA%3D%3D" rel="nofollow">MoE 系列(三)|使用 Istio 动态更新 Go 扩展配置</a></p><p><a href="https://link.segmentfault.com/?enc=3T%2FoWuE9vJJT6GIxmMk%2FsQ%3D%3D.a421yndUMEfauLq9msTFmEJcIHpFvwQw4nKeuSvnwve%2FWqiSEsH6e6sH1d4vVzMtt72DRRb4CkN4%2BOgIKfUt1A%3D%3D" rel="nofollow">MoE 系列(四)|Go 扩展的异步模式</a></p>
MOSN 基于延迟负载均衡算法——走得更快,期待走得更稳
https://segmentfault.com/a/1190000043820344
2023-05-23T17:36:21+08:00
2023-05-23T17:36:21+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043820346" alt="图片" title="图片"></p><p>文|纪卓志(GitHub ID:jizhuozhi)</p><p>京东高级开发工程师</p><p>MOSN 项目 Committer</p><p>专注于云原生网关研发的相关工作,长期投入在负载均衡和流量控制领域</p><h2>前言</h2><p>这篇文章主要是介绍 MOSN 在 v1.5.0 中新引入的基于延迟的负载均衡算法<a href="https://link.segmentfault.com/?enc=86VZ3ULIjT9%2FwHHvep8EQg%3D%3D.bxh4LO2jYFrBXP5KluqCFdZRdOnELfAS0LWkHG3sc%2Bvl20ZHFDvu9XVtKp7uRc%2BQ" rel="nofollow">#2253</a>。首先会对分布式系统中延迟出现的原因进行剖析,之后介绍 MOSN 都通过哪些方法来降低延迟,最后构建与生产环境性能分布相近的测试用例来对算法进行验证。</p><p>在开始聊基于延迟的负载均衡算法之前,我们先介绍下什么是负载均衡。</p><h3>什么是负载均衡</h3><p>Wikipedia中 <a href="https://link.segmentfault.com/?enc=1QLGgsB66cXbXkScHBw%2F%2Fw%3D%3D.%2Bp0qgSJNMX%2FGpQSshex42iagLLKrQP95Suc%2FjxkckQOup3gaeJu6lx%2BH74Do0Cw9Ua%2Bcfwmux2o7zMlrIaNlNw%3D%3D" rel="nofollow">Load Balancing (Computing)</a>) 词条是这样介绍负载均衡的:</p><blockquote>负载均衡是将一组任务分配到一组资源(计算单元)上的过程,目的是使它们的整体处理更有效率。负载均衡可以优化响应时间,避免负载不均匀导致一些计算节点过载而其他计算节点处于空闲状态</blockquote><p>负载均衡在大型分布式系统中是关键的组成部分。负载均衡解决了分布式系统中最重要的两个问题:可伸缩性 <em>(Scalability)</em> 和韧性 <em>(Resilience)</em> 。</p><ul><li><strong>可伸缩性</strong>:应用程序部署在多个相同的副本中。当计算资源不足时可以通过部署额外的副本来增加计算资源,而当计算资源大量冗余时可以通过减少副本来节省成本。通过负载均衡可以将请求负载分布到不同的副本中。</li><li><strong>韧性</strong>:分布式系统的故障是部分的。应用程序通过冗余副本的方式,保证在部分组件故障时仍能正常地提供服务。负载均衡通过感知节点的故障,调整流量的分配,将流量更多的分配到那些能够正常提供服务的节点上。</li></ul><h2>走得更快</h2><p>负载均衡使得现代软件系统具备了可扩展性和韧性。但在分布式系统中还存在不容忽视的问题:<strong>延迟</strong>。</p><h3>延迟来自哪里</h3><p>现代软件系统通常是多层级结构大型分布式系统,即使是只服务单个终端用户的请求,它背后也有可能经过了上百次的数据访问,这种情况在微服务架构中更是尤为普遍。</p><p><img src="/img/remote/1460000043820347" alt="图片" title="图片"></p><p>微服务架构 <em>(引用自 Microservices Pattern)</em></p><p>单台性能稳定的服务器中延迟通常由以下几个方面造成:</p><ul><li>计算任务本身的复杂度</li><li>内容的传输过程中的延迟</li><li>请求排队等待的延迟</li><li>后台任务活动所导的资源竞争</li></ul><p>这些服务器之间的延迟将会叠加,任何显著的延迟增加都会影响终端用户的体验。此外,任何来自单个节点的延迟峰值也会直接影响到终端用户体验。同时越来越多地使用公有云部署应用程序也进一步加剧了响应时间的不可预测性。因为在这些环境中存在共享资源 <em>(CPU、内存和 IO)</em> 的争用,应用程序机几乎不可避免地遇到性能影响,而这种影响是随时发生的。</p><h3>如何减少延迟</h3><p>有研究表明,在大型互联网应用中,延迟往往具有长尾特点,P99 比中位数高出几个数量级。如果在应用架构的每层都能够减少这些尾部延迟,那么对终端用户整体的尾部延迟将会显著降低。</p><p><img src="/img/remote/1460000043820348" alt="图片" title="图片"></p><p>在服务网格中,所有接收和发送的流量都会经过边车代理,通过边车代理可以轻松地控制网格的流量,而无需对服务进行任何修改。如果边车代理在对应用层流量进行转发时,总是通过负载均衡时选择响应时间较短的服务器,那么将会显著降低对终端用户的尾部延迟。</p><p>基于此,我们准备开始为 MOSN 引入基于延迟的负载均衡算法,并进行适当调整来保证能够在大多数使用场景下显著减少延迟。</p><h3>性能问题是局部的</h3><p>前面提到了,每个节点的性能受到多种因素的影响,这些影响因素是动态的,难以准确预测每个节点的性能,因此我们无法精确地选择最好的节点,但是可以避免较差的节点。</p><p>在云环境中,服务器的性能常常是难以预测的,但是我们可以通过对大量的数据进行分析,发现服务器性能的分布大多数情况下是符合正态分布的。因此,尽管有一部分的服务器在性能方面表现比较差,它们的数量通常都是少数的 <em>(3Sigma)</em> ,而绝大部分服务器节点的表现是正常的。</p><p><img src="/img/remote/1460000043820349" alt="图片" title="图片"></p><p>除了服务器之间的差异,还存在由基础设施导致的动态延迟,这种延迟可能是由于网络拥塞、故障或不断增长的流量所导致。这种延迟通常具有持续性和局部性:持续性则表示延迟会长时间存在,不会在短时间内消失;而局部性指的是延迟往往只出现在某些特定服务器上,而不会在全局发生。</p><h3>PeakEWMA</h3><p>面对这些问题,我们使用 Peak EWMA <em>(Peak Exponentially Weighted Moving Average)</em> 计算响应时间指标,并根据这个指标来对节点进行负载均衡。</p><p>EWMA 是一种动态权重调整算法,各数值的加权影响力随时间而指数式衰退,越近期的数据加权影响力越重,但较旧的数据也给予一定的加权值。</p><p><img src="/img/remote/1460000043820350" alt="图片" title="图片"></p><p>它以相对较高的权重考虑了最近响应时间的影响,因此更具有针对性和时效性。加权的程度以常数 𝛼 决定,𝛼 数值介于 0 至 1,它用来控制数据加权影响力衰退的速率。</p><p><img src="/img/remote/1460000043820351" alt="图片" title="图片"></p><p>作为一种统计学指标,EWMA 的计算过程不需要大量的采样点以及时间窗口的设定,有效地避免了计算资源的浪费,更适合在 MOSN 这样的边车代理中使用。</p><p>由于响应时间是历史指标,当服务器出现性能问题导致长时间未返回时,负载均衡算法会错误地认为这台服务器仍是最优的,而不断地向其发送请求而导致长尾延迟增高。我们使用活跃连接数作为实时变化的指标对响应时间进行加权,表示等待所有活跃的连接都返回所需要的最大时间。</p><h3>P2C(Power of Two Choice)</h3><p>在大规模集群中,如果使用遍历所有服务器选择最好的服务器的方法,虽然可以找到最轻负载的服务器来处理请求,但这种方法通常需要大量的计算资源和时间,因此无法处理大规模的请求。因此,我们使用 P2C 来选择最优节点。相比之下,P2C 算法可以在常数时间内选择两个服务器进行比较,并选择其中负载更轻的服务器来处理请求。P2C 基于概率分配,即不直接基于权重分配,而是根据每个服务器优于其他服务器的概率值来决定请求的分配。</p><p>此外,在多个负载均衡器的情况下,不同负载均衡器可能会有不同的节点视图,这可能导致某些负载均衡器选择的最优节点总是最差的节点。这是因为负载均衡器选择最优节点时基于自己的视图信息,而节点视图随着时间的变化可能会发生变化,因此不同的负载均衡器选择的最优节点也可能不同。P2C 算法通过对随机选择的两个节点进行比较,可以使节点间的负载均衡更加均匀,即使节点视图发生变化,也能提供稳定的负载均衡效果。</p><blockquote>在 MOSN 的 v1.5.0 版本中,只有节点权重相同时会使用 P2C,当权重不同时会使用 EDF 进行加权选择。后续会提供可配置的选项。</blockquote><h3>模拟流量验证</h3><p>我们构建了与生产环境性能分布相近的测试用例来对算法进行验证。</p><p>首先我们使用正态分布生成了 10 台服务器的基准性能,其中数学期望为 50ms,标准差为 10ms。接下来,我们将这些基准性能作为数学期望,并以标准差为 5ms 的正态分布随机生成了请求延迟,以模拟真实世界的情况。此外,我们还在其中一台服务器注入了概率为 0.1 的故障,故障发生时会产生 1000ms 的延迟,以测试系统的容错性。</p><p>为了模拟请求倾斜时请求排队等待的延迟,我们限制了每台服务器的最大并发数为 8,当同时处理的最大请求数超过了最大并发数时,将会排队等待。这样能够更加真实地模拟出系统的运行情况。</p><p>最后,我们使用了 Round Robin、Least Request 和 Peak EWMA 三种算法,分别以 16 并发同时发送请求,得到的 P99 如下:</p><p><img src="/img/remote/1460000043820352" alt="图片" title="图片"></p><p>Round Robin 算法虽然平衡,但是始终会选择到注入了故障的服务器,导致 P99 始终在 1000ms 上下波动;Least Request 算法虽然避开了故障服务器,但是其 P99 值依然表现出较大的波动。</p><p>与此相比,Peak EWMA 算法在保持稳定的同时,P99 值始终低于 Round Robin 和 Least Request 算法。这恰当地体现了 MOSN 在性能优化方面的成功,MOSN 确实做到了走得更快。</p><h2>期待走得更稳</h2><p>虽然 MOSN 在服务网格中解决了让应用跑得更快的问题,但是分布式系统中的故障却时刻存在。我们期望通过 MOSN 的负载均衡算法,可以让我们的服务走得更稳。</p><h3>快速失败的挑战</h3><p>根据经验,故障时的响应时间往往远小于正常值,比如网络分区导致的连接超时,而没有实际处理请求。我们称这种故障时的响应时间远远小于正常值的情况为快速失败。</p><p>在服务器出现快速失败时,从负载均衡的角度看,就会错误地认为该服务器是最优的选择。尽管可以通过断路器来避免向该服务器发送长期请求,但断路器本身也是一种快速失败,错误的视图依然会传播。此外,断路器的阈值设置也存在挑战。此外,断路器需要足够的错误样本才能触发,而我们期望尽可能避免错误的发生。</p><p>因此,我们在后续版本中将会对负载均衡算法进行调整,让负载均衡算法能够感知错误的发生,并在触发断路器前就避免将请求转发到故障的服务器中。</p><p><strong>MOSN Star 一下✨:</strong> <a href="https://link.segmentfault.com/?enc=p%2BRUWSi5rQZsxWHKR0e5LQ%3D%3D.uhdtNHjJZyjitBZNk5b%2FGNh6nWkldkozsD8iad3NCqSPT2eJ217JDddKlnZ6tiIZ" rel="nofollow">https://github.com/mosn/mosn</a></p>
MoE 系列(四)|Go 扩展的异步模式
https://segmentfault.com/a/1190000043796411
2023-05-17T10:52:59+08:00
2023-05-17T10:52:59+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><em>在</em><a href="https://link.segmentfault.com/?enc=VHy9l8vMD8gjpM0QbGZTBg%3D%3D.sU2HrR93LS24oIp9Tksz7j6mqOd%2FgWYEYbGFBt2tDskR%2FKH4yqlZ0yuxiqF38doqa7y2Ry4MQJENKL3DYCfSVw%3D%3D" rel="nofollow"><em>《MoE 系列(三)|使用 Istio 动态更新 Go 扩展配置》</em></a><em>中我们体验了用 Istio 做控制面,给 Go 扩展推送配置,这次我们来体验一下,在 Go 扩展的异步模式下,对 Goroutine 等全部 Go 特性的支持。</em></p><p><strong>异步模式</strong></p><p>之前,我们实现了一个简单的 Basic Auth[1],但是,那个实现是同步的,也就是说,Go 扩展会阻塞,直到 Basic Auth 验证完成,才会返回给 Envoy。</p><p>因为 Basic Auth 是一个非常简单的场景,用户名密码已经解析在 Go 内存中了,整个过程只是纯 CPU 计算,所以,这种同步的实现方式是没问题的。</p><p>但是,如果我们要实现一个更复杂的需求,比如,我们要将用户名密码调用远程接口查询,涉及网络操作,这个时候,同步的实现方式就不太合适了。因为同步模式下,如果我们要等待远程接口返回,Go 扩展就会阻塞,Envoy 也就无法处理其他请求了。</p><p>所以,我们需要一种异步模式:</p><ul><li>我们在 Go 扩展中,启动一个 Goroutine,然后立即返回给 Envoy,当前正在处理的请求会被挂起,Envoy 则可以继续处理其他请求。</li><li>Goroutine 在后台异步执行,当 Goroutine 中的任务完成之后,再回调通知 Envoy,挂起的请求可以继续处理了。</li></ul><p>注意:虽然 Goroutine 是异步执行,但是 Goroutine 中的代码,与同步模式下的代码,几乎是一样的,并不需要特别的处理。</p><p><strong>为什么需要</strong></p><p>为什么需要支持 Goroutine 等全部 Go 的特性呢?</p><p>有两方面的原因:</p><ul><li>有了 Full-feature supported Go,我们可以实现非常强大、复杂的扩展。</li><li>可以非常方便的集成现有 Go 世界的代码,享受 Go 生态的红利。</li></ul><p>如果不支持全部的 Go 特性,那么在集成现有 Go 代码的时候,会有诸多限制,导致需要重写大量的代码,这样,就享受不到 Go 生态的红利了。</p><p><strong>实现</strong></p><p>接下来,我们还是通过一个示例来体验,这次我们实现 Basic Auth 的远程校验版本,关键代码如下:</p><pre><code>func (f *filter) DecodeHeaders(header api.RequestHeaderMap, endStream bool) api.StatusType {
go func() {
// verify 中的代码,可以不需要感知是否异步
// 同时,verify 中是可以使用全部的 Go 特性,比如,http.Post
if ok, msg := f.verify(header); !ok {
f.callbacks.SendLocalReply(401, msg, map[string]string{}, 0, "bad-request")
return
}
// 这里是唯一的 API 区别,异步回调,通知 Envoy,可以继续处理当前请求了
f.callbacks.Continue(api.Continue)
}()
// Running 表示 Go 还在处理中,Envoy 会挂起当前请求,继续处理其他请求
return api.Running
}</code></pre><p>再来看 <code>verify</code> 的代码,重点是,我们可以在这里使用全部的 Go 特性:</p><pre><code>// 这里使用了 http.Post
func checkRemote(config *config, username, password string) bool {
body := fmt.Sprintf(`{"username": "%s", "password": "%s"}`, username, password)
remoteAddr := "http://" + config.host + ":" + strconv.Itoa(int(config.port)) + "/check"
resp, err := http.Post(remoteAddr, "application/json", strings.NewReader(body))
if err != nil {
fmt.Printf("check error: %v\n", err)
return false
}
if resp.StatusCode != 200 {
return false
}
return true
}
// 这里操作 header 这个 interface,与同步模式完全一样
func (f *filter) verify(header api.RequestHeaderMap) (bool, string) {
auth, ok := header.Get("authorization")
if !ok {
return false, "no Authorization"
}
username, password, ok := parseBasicAuth(auth)
if !ok {
return false, "invalid Authorization format"
}
fmt.Printf("got username: %v, password: %v\n", username, password)
if ok := checkRemote(f.config, username, password); !ok {
return false, "invalid username or password"
}
return true, ""
}</code></pre><p>另外,我们还需要实现一个简单的 HTTP 服务,用来校验用户名密码,这里就不展开了,用户名密码还是 <code>foo:bar</code>。</p><p>完整的代码,请移步 Github[2]。</p><p><strong>测试</strong></p><p>老规矩,启动之后,我们使用 <code>curl</code> 来测试一下:</p><pre><code>$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
HTTP/1.1 401 Unauthorized
# valid foo:bar
$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200" -H 'Authorization: basic Zm9vOmJhcg=='
HTTP/1.1 200 OK</code></pre><p>依旧符合预期。</p><p><strong>总结</strong></p><p>在同步模式下,Go 代码中常规的异步非阻塞也会变成阻塞执行,这是因为 Go 和 Envoy 是两套事件循环体系。</p><p>而通过异步模式,Go 可以在后台异步执行,不会阻塞 Envoy 的事件循环,这样,就可以用上全部的 Go 特性了。</p><p>由于 Envoy Go 暴露的是底层的 API,所以实现 Go 扩展的时候,需要关心同步和异步的区别。</p><p>当然,这对于普通的扩展开发而言,并不是一个友好的设计,之所以这么设计,更多是为了极致性能的考量。</p><p>大多数场景下,其实并不需要到这么极致,所以,我们会在更上层提供一种默认异步的模式。这样,Go 扩展的开发者,就不需要关心同步和异步的区别了。</p><p>下一篇我们将介绍 Envoy Go 扩展之内存安全。欢迎感兴趣的持续关注~</p><p>敬请期待:<strong>MoE 系列(五)|Envoy Go 扩展之内存安全</strong></p><p>[1]Basic Auth:</p><p><a href="https://link.segmentfault.com/?enc=QDNH6PSHObDtFlg%2FClIuHg%3D%3D.q0swRfmZWm4bmp7o%2BR8SWNAp9k7BP1zVkSfWjF81pkLudg62O54qLS9XrnWztpCS%2FzQ4mo2ALhGAyxo6gxvhBg%3D%3D" rel="nofollow">https://uncledou.site/2023/moe-extend-envoy-using-golang-2/</a></p><p>[2]Github:</p><p><a href="https://link.segmentfault.com/?enc=pIS8%2FdvN3bCdmxMVMvYTlw%3D%3D.ODb%2B81mD7B8P5yf5%2BTn%2FyPtckLYlGKDWDE1wnMZCRkSZOBRg6C3zB6WiBtc0X2awlolpLR%2Bav89Kv1KxxWT5BjFg5iRWuxTwdUC%2Bn%2B4spYgvzjSd7fGPUgYtjyi6Qcaj" rel="nofollow">https://github.com/doujiang24/envoy-go-filter-example/tree/master/example-remote-basic-auth</a></p>
【开源之夏 2023】欢迎报名 Dragonfly、Kata Containers、Nydus 社区项目!
https://segmentfault.com/a/1190000043785557
2023-05-14T16:05:18+08:00
2023-05-14T16:05:18+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043785559" alt="图片" title="图片"></p><p>开源之夏是由“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展,培养和发掘更多优秀的开发者。</p><p>活动联合国内外各大开源社区,针对重要开源软件的开发与维护提供项目任务,并面向全球高校学生开放报名。</p><p><strong>Dragonfly 项目介绍</strong></p><p>Dragonfly 是一个基于 P2P 技术的文件分发和镜像加速系统,并且是云原生架构中镜像加速领域的标准解决方案以及最佳实践。自 2017 年开源以来,Dragonfly 被许多大规模互联网公司选用并投入生产使用,并在 2018 年 10 月正式进入 CNCF,成为中国第三个进入 CNCF 沙箱项目 <em>(Sandbox)</em> 。2020 年 4 月,CNCF 技术监督委员会 <em>(TOC)</em> 投票决定接受 Dragonfly 作为孵化项目 <em>(Incubating)</em> 。经过多年生产实践经验打磨的下一代产品,汲取了 Dragonfly 1.x 的优点并针对已知问题做了大量的优化,在解决大规模文件分发场景下有着无可比拟的优势。基于 P2P 技术的优势,在 AI Inference 分发模型场景可以解决大文件分发过程中的性能瓶颈。并且可以通过集成 Dragonfly P2P 技术减少源站压力,提高模型分发效率。在未来,Dragonfly 会结合 AI 领域生态进行拓展,服务 AI 领域并且成为其重要基础设施。</p><p><strong>Kata Containers</strong> <strong>项目介绍</strong></p><p>自 2013 年 Docker 问世以来,容器技术立刻让全球的开发者为之着迷,并逐渐成为现代应用程序、构建、发布和运维的主流方式。容器以标准格式对应用程序进行封装,应用程序可从一个计算环境快速、安全地切换到另一个计算环境,这对于想要快速构建、测试和部署软件的开发者而言至关重要。然而传统的以 runC 为代表的容器方案基于共享内核技术,通过 Linux 提供的 Cgroups 和 Namespace 等方案进行隔离和控制,如果某一容器中的恶意程序利用了系统缺陷从容器中逃逸,则会对宿主机系统构成严重威胁。尤其是在公有云环境,这一潜在威胁成为了容器技术普及和落地的一大障碍。如果将不同容器再嵌套放入到不同的虚拟机,通过增加一层相对安全、成熟的隔离技术,就能大大提高系统的安全性,减少系统被攻破的可能。基于这种思想的开源技术也随之出现,代表性的两个项目为 Intel 开源技术中心的 Clear Containers 和 Hyper.sh 的 runV。2017 年,这两个开源项目合并,共同创建了开源项目 Kata Containers,其目标是将虚拟机的安全优势与容器的高速及可管理性相结合,为用户提供标准化、安全、高性能的容器解决方案。Kata Containers 创建的不同 Pod <em>(容器)</em> 运行在不同的虚拟机 <em>(Kernel)</em> 之中,比传统容器提供了更好的隔离性和安全性,同时继承了容器快速启动和标准化等优点。</p><p><strong>Nydus</strong> <strong>项目介绍</strong></p><p>镜像是容器基础设施中的一个重要部分,目前 OCI 标准镜像的缺陷之一是容器需要等待整个镜像数据下载完成后才能启动,这导致了容器启动时消耗了过多的端到端时间。在大规模集群场景下,这对网络与存储负载的影响尤为明显。Nydus 镜像加速框架提供了容器镜像按需加载的能力,它在生产环境里支撑了每日百万级别的加速镜像容器创建,将容器端到端冷启动时间从分钟级降低到了秒级。Nydus 目前由蚂蚁集团,阿里云,字节跳动联合研发,与内核态 EROFS 做了深度集成,也是 Kata Containers 与 Linux 内核态原生支持的镜像加速方案。目前 Nydus 已经被容器生态主流项目支持,例如 Containerd,Docker,Podman,BuildKit, Nerdctl,Kata Containers。</p><p><strong>活动规则</strong></p><p>开源之夏官网:</p><p><a href="https://link.segmentfault.com/?enc=mn2LqqngZSjIVa0Rg3PkJQ%3D%3D.6KZvn3tyy7A9AMInboCOR4%2BgldKUyRXnN%2FuouDAwYy8%3D" rel="nofollow">https://summer-ospp.ac.cn/</a></p><p>各位同学可以自由选择项目,与社区导师沟通实现方案并撰写项目计划书。被选中的学生将在社区导师指导下,按计划完成开发工作,并将成果贡献给社区。社区评估学生的完成度,主办方根据评估结果发放资助金额给学生。</p><p><img src="/img/remote/1460000043785560" alt="图片" title="图片"></p><p><strong>Dragonfly 社区项目</strong></p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=jbHd5JSBglvfQY2IiiLTUA%3D%3D.kTM5En%2FXGKnUsyslEZxaBkdtAO%2FtXitdjvFuGMMaAWhOk1z67gFgqM6KGlQRtTYIebGv8%2BKPF07dH4fseGLt1s3FWEDK%2BNsiDbduzwHRtsL9g1wWEESghNilb2r7qVwS" rel="nofollow">https://summer-ospp.ac.cn/org/orgdetail/72e6f975-d2b8-4fa3-a377-441c1038db10?lang=zh</a></p><p><strong>PyTorch Serve 基于 Dragonfly P2P 技术分发模型</strong></p><p><strong>导师:</strong> yxxhero</p><p><strong>邮箱:</strong> <a href="mailto:aiopsclub@163.com">mailto:aiopsclub@163.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=z7B9odIWDJko7j7DC6FO3w%3D%3D.WTNpfcqIAMq537ef0via%2B%2BadO3UegTMykDKWkQA39NJ36Ixcch53SsDEE9Hnd18lSK%2FQVbaeE96I1OFnZtVlB%2FDuzmWC7EhE%2F6jWGa%2BrAKM%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/2372e0132?list=org&navpage=org</a></p><p><strong>TensorFlow Serving 基于 Dragonfly P2P 技术分发模型</strong></p><p><strong>导师:</strong> 崔大钧</p><p><strong>邮箱:</strong> <a href="mailto:bigerous@qq.com">mailto:bigerous@qq.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=o8MsWB9zQJRD%2Foe0m8sKkg%3D%3D.jtah7cRukbrizWWQTnN9yZf1su8tvfaZqAI0eWQKcGoinhqR%2F%2BgOzQJ6k42EF4p1kaV1zt5geX5DxYFfb%2Bh0Bmz93fNTAA1XKiIiIPN%2Bc7Y%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/2372e0022?list=org&navpage=org</a></p><p><strong>Triton Inference Server 基于 Dragonfly P2P 技术分发模型</strong></p><p><strong>导师:</strong> 戚文博</p><p><strong>邮箱:</strong> <a href="mailto:gaius.qi@gmail.com">mailto:gaius.qi@gmail.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=PNNX35i6RX%2B%2B8huwxaibqQ%3D%3D.8PLrB6O9foLgBKaPooahIEhkaMolCuy%2BVAvQPyKFLH8OgOGlML8q6xHPjT5Qmf2DgB1KtbkAmyHsa4OVDFbCK9A4aXvNcsVyp4s5qjVSB%2Bg%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/2372e0001?list=org&navpage=org</a></p><p><strong>Kata Containers 社区项目</strong></p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=hP81lP4uZylJmtuRccn3zw%3D%3D.dwbZjjc9an%2BjtEfNIaWTN0EutOVIyS2wCzf7Q2DbOE%2BezQ3RU9yP7aZpBCJn9zExnhBkQ0nBaqPYVLVdAR3oBnr4wE0%2BMBinSZoBVdFX0a7GV3YleP8wL1%2BuInovLzdB" rel="nofollow">https://summer-ospp.ac.cn/org/orgdetail/301597a0-ca46-418a-89d1-13ea3c050ee9?lang=zh</a></p><p><strong>基于 VSOCK FD Passthrough 对 Container IO Stream 进行重构</strong></p><p><strong>导师:</strong> 李福攀</p><p><strong>邮箱:</strong> <a href="mailto:fupan.lfp@antgroup.com">mailto:fupan.lfp@antgroup.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=PYegdkhmecEfphYgQ7f%2Fjg%3D%3D.dk0YDqYIQYfloVY2W%2Fm3GvuREhxntfM94Nho%2Bttz0D1WxM6mKA8d54IfDqEQkLXwGv9yespxLDUyKnAe5DgKk5Itld0N9vruqz98e4c2fOE%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/233010451?list=org&navpage=org</a></p><p><strong>Nydus RAFS v6 guest 内核支持优化</strong></p><p><strong>导师:</strong> 李亚南</p><p><strong>邮箱:</strong> <a href="mailto:alex.lyn@antgroup.com">mailto:alex.lyn@antgroup.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=JVD8TfQrY%2FmJgUTMFOpdfw%3D%3D.O8yIcLsUiWjHyaw0k%2FxL09vKHuV9a1WHH4URrtXSClZ%2FQNtAGIv71MQi5sXdmeGff3p0VNcmBk2BrXxyBEJeNnyYDDiE0QPw7THJe%2FyX8E4%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/233010419?list=org&navpage=org</a></p><p><strong>跨容器 shared-mount 支持</strong></p><p><strong>导师:</strong> 彭涛</p><p><strong>邮箱:</strong> <a href="mailto:bergwolf@hyper.sh">bergwolf@hyper.sh</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=OgqS3ngrYuIY4wqSOOCF6Q%3D%3D.n7a%2Bw3o%2Fkr0BnrzB618jUcAodNEwVs5txgFlAZCZhsQuH0S6D1fJa4Bupdjgz6acEzXrK5ROCNN9nnNjvGVY4SUsbQ0Cr8gF1O2pqlc7gFs%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/233010417?list=org&navpage=org</a></p><p><strong>Nydus 社区项目</strong></p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=TU2%2BJ%2FB15p287FE0%2FJeqrg%3D%3D.4%2FYGy2HmON%2FOGQXGceQw9mEozQirFr51LJdwWAZ0uU5xIxw7DNdOsgdJc6U25uoBhYH82FUcg6d0FJoo3zEBtmQxIUh6oeg%2FYuMkQ3R3d44Y6CJ9kkiaDUFke5NQaLeT" rel="nofollow">https://summer-ospp.ac.cn/org/orgdetail/1919a78b-344c-46d6-9276-b47d3a0a4a42?lang=zh</a></p><p><strong>Nydus 容器社区开源生态集成</strong></p><p><strong>导师:</strong> 井守</p><p><strong>邮箱:</strong> <a href="mailto:yansong.ys@antgroup.com">mailto:yansong.ys@antgroup.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=D29g1I2RM8J4oCVGjynJcA%3D%3D.DMpErHr%2BLunt2V9b9URzx%2BUHpo87D2DO4D4Cq01hzpO4m3hec1sVOEI2pzW%2BMzmRaIQUxTkG3%2B3iYaYLQxpl6EEXE6GtgMM3Q7kReyQ5mEI%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/231910250?list=org&navpage=org</a></p><p><strong>Nydus 开源存储构建与分发支持</strong></p><p><strong>导师:</strong> 泰友</p><p><strong>邮箱:</strong> <a href="mailto:cuichengxu.ccx@antgroup.com">mailto:cuichengxu.ccx@antgroup.com</a></p><p><strong>项目难度:</strong> 进阶/Advanced</p><p><strong>项目链接:</strong> <a href="https://link.segmentfault.com/?enc=NtzvARYv9zBkynVXb5ADVA%3D%3D.flT793bWzgx06yYX%2B1g1MZFt9AnzROGGJi0HSJVFb43e7%2BE5nd1QrWeSV6T2gwPJpd3JOpi2qstA6v0PfphynIqYLT%2FpiVjJX8kK2pN8Yz4%3D" rel="nofollow">https://summer-ospp.ac.cn/org/prodetail/231910252?list=org&navpage=org</a></p><p><strong>申请资格</strong></p><ul><li>本活动面向年满 18 周岁在校学生。</li><li>暑期即将毕业的学生,只要在申请时学生证处在有效期内,就可以提交申请。</li><li>中国籍学生参与活动需提供身份证、学生证、教育部学籍在线验证报告(学信网)或在读证明。</li><li>外籍学生参与活动需提供护照,同时提供录取通知书、学生卡、在读证明等文件用于证明学生身份。</li></ul><p><strong>活动流程</strong></p><p><img src="/img/remote/1460000043785561" alt="图片" title="图片"></p><p>欢迎扫描下方二维码加入钉钉群交流,或搜索群号:31047501 入群,期待各大高校学生报名参加。</p>
【开源之夏 2023】欢迎报名 MOSN 社区项目!
https://segmentfault.com/a/1190000043769975
2023-05-09T22:00:52+08:00
2023-05-09T22:00:52+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043769977" alt="图片" title="图片"></p><p>开源之夏是由“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展,培养和发掘更多优秀的开发者。</p><p>活动联合国内外各大开源社区,针对重要开源软件的开发与维护提供项目任务,并面向全球高校学生开放报名。</p><p>2023 年,<strong>MOSN 社区</strong>再次加入中国科学院软件研究所的高校开源活动—— <strong>“开源之夏 2023”</strong> ,为大家准备了三个任务,涉及 Go、HTTP、Security、Software-Defined Networking、Container 等多个领域。</p><p><strong>MOSN 项目介绍</strong></p><p>MOSN <em>(Modular Open Smart Network)</em> 是一款基于 Go 语言开发的云原生网络代理平台,由蚂蚁集团开源并在双 11 大促期间经过几十万容器的生产级验证。MOSN 为服务提供多协议、模块化、智能化、安全的代理能力,融合了大量云原生通用组件,同时也可以集成 Envoy 作为网络库,具备高性能、易扩展的特点。另外,MOSN 可以集成 Istio 构建 Service Mesh,也可以作为独立的四、七层负载均衡,API Gateway、云原生 Ingress 等使用。</p><p><strong>Layotto 项目介绍</strong></p><p>Layotto <em>(/leɪˈɒtəʊ/)</em> 是一款使用 Golang 开发的应用运行时, 旨在帮助开发人员快速构建云原生应用,帮助应用和基础设施解耦。它为应用提供了各种分布式能力,例如状态管理、配置管理、事件发布订阅等,以简化应用的开发。</p><p><strong>活动规则</strong></p><p>开源之夏官网:</p><p><a href="https://link.segmentfault.com/?enc=8Ooh0j6PUUKWjc6T35Cbvw%3D%3D.RH4DofWHcvUYVKIu09Pvn5X%2Bz%2BV8v7qLxVuFh1nEP4A%3D" rel="nofollow">(https://summer-ospp.ac.cn/</a></p><p>各位同学可以自由选择项目,与社区导师沟通实现方案并撰写项目计划书。被选中的学生将在社区导师指导下,按计划完成开发工作,并将成果贡献给社区。社区评估学生的完成度,主办方根据评估结果发放资助金额给学生。</p><p><img src="/img/remote/1460000043769978" alt="图片" title="图片"></p><p><strong>MOSN 社区项目</strong></p><p>项目链接:[*<a href="https://link.segmentfault.com/?enc=lYJEmVOJ%2FGIlJfsWQLptDA%3D%3D.5rtkWTyIs%2Fp6B08L52TQsKZqm2NaacaM7VKwhsF2oxGDtfK76ylVpgHoYDaavGQT0g0DH2UEWGlj0IWP9D6cSRlvx6flSrKyKwuUZ8%2FAp%2BfQJwWC%2FUQwr4i7US1xfgrHbywVa%2B9cF%2F9De5g6XTFOMSZdJy01FkfYsde08NH5SjDONy6le4TqyPI1FIthhehevHYi8F9S%2BG%2BfjLQ256QOJwRmsJGieYM5xEfEGEeTu%2FU%3D" rel="nofollow">https://summer-ospp.ac.cn/org/orgdetail/f0813e66-fa19-4302-a3...</a>)</p><p><strong>MOSN</strong> Go、HTTP、Security</p><p><strong>项目社区导师:罗泽轩</strong></p><p><a href="mailto:spacewanderlzx@gmail.com">mailto:spacewanderlzx@gmail.com</a></p><p><strong>基于 Coraza 和 MOSN on Envoy 开发 Envoy 的 WAF 插件</strong></p><p><strong>项目编号:23f080212</strong></p><p><strong>项目难度:进阶/Advanced</strong></p><p>Coraza 是一个用 Go 实现的 WAF 引擎,我们希望能够借助 MOSN on Envoy 的能力,让 Coraza 运行在 Envoy 当中,并与官方的基于 Wasm 的实现 <em>(<a href="https://link.segmentfault.com/?enc=Ht%2BYT1FXLthsN0x%2BRJ%2BD4g%3D%3D.j6gyc9Az2HlwFQKAu9l39L75q%2Fs9PlELvQx3XmGK%2BIqunVixm1kacY%2Fqh6hvhQVq" rel="nofollow">https://github.com/corazawaf/coraza-proxy-wasm</a>)</em> 进行比较。</p><ul><li>实现一个基本可用的 WAF 插件 <em>(需要有详尽的文档+测试)</em> ,并与 Wasm 版本做对比,输出一份比较报告。</li><li>了解 MOSN、Envoy 和 WAF,能够用 Go 写代码。</li></ul><p><strong>MOSN</strong> Go、Software-Defined Networking</p><p><strong>项目社区导师:纪卓志</strong></p><p><a href="mailto:jizhuozhi.george@gmail.com">mailto:jizhuozhi.george@gmail.com</a></p><p><strong>为 Envoy Go 扩展建设插件市场</strong></p><p><strong>项目编号:23f080259</strong></p><p><strong>项目难度:进阶/Advanced</strong></p><p>Envoy 是当前最流行的网络代理之一,Go 扩展是 MOSN 社区为 Envoy 增加的 Go 生态基础,也是 MOSN 社区 MoE 框架的基础。</p><p>受益于 Golang 生态系统,研发可以轻松在 Envoy 实现插件用于更多的长尾场景,其中很多场景都是通用的。</p><p>本项目是为 Envoy Go 扩展构建插件市场。在插件市场中,人们可以在插件市场中分享插件,选用已经存在的插件。通过插件市场,可以让 Envoy、MoE 生态变得更加开放、共享、丰富。</p><ul><li>提供一个 Envoy Go 插件的内容平台,在这里可以发布经过社区 Review 的优秀插件,需要拥有服务端与前端页面。</li><li>不自建账号体系,通过 GitHub OAuth2.0 完成用户认证与授权。</li><li>进阶——对接 GitHub OpenAPI,支持动态获取插件所在仓库信息,包括 README、分支版本以及 Star 数。</li><li>能够使用 Go 语言 <em>(框架不限)</em> 开发出带前端页面的小型站点。</li><li>对认证与授权及 OAuth2.0 有基本的了解。</li><li>熟悉 Git 和 GitHub 工作流程 <em>(分支、版本、合并请求等)</em> 。</li></ul><p><strong>Layotto</strong> Go、gRPC</p><p><strong>项目社区导师:wenxuwan</strong></p><p><a href="mailto:wangwenxue.wwx@antgroup.com">mailto:wangwenxue.wwx@antgroup.com</a></p><p><strong>Layotto Support Pluggable Components</strong></p><p><strong>项目编号:23f080194</strong></p><p><strong>项目难度:进阶/Advanced</strong></p><p>当前 Layotto 的 Components 都是实现在 Layotto 的工程里面的。用户若要想使用新的 Component,就必须使用 Golang 语言开发,同时必须在 Layotto 工程中实现,然后统一编译。对于多语言用户来说非常不友好,因此 Layotto 需要提供 Pluggable Components 的能力,允许用户可以通过任何语言实现自己的 Components,Layotto 通过 gRPC 协议和外部的 Components 进行通信。</p><ul><li>完成 Pluggable Components 框架设计。</li><li>提供 Pluggable Components 接入文档和示例。</li><li>熟悉 Golang 和 gRPC,熟悉 Dapr 和 Layotto 运行时架构。</li></ul><p><img src="/img/remote/1460000043769979" alt="图片" title="图片"></p><p><strong>申请资格</strong></p><ul><li>本活动面向年满 18 周岁在校学生。</li><li>暑期即将毕业的学生,只要在申请时学生证处在有效期内,就可以提交申请。</li><li>中国籍学生参与活动需提供身份证、学生证、教育部学籍在线验证报告(学信网)或在读证明。</li><li>外籍学生参与活动需提供护照,同时提供录取通知书、学生卡、在读证明等文件用于证明学生身份。</li></ul><p><strong>活动流程</strong></p><p><img src="/img/remote/1460000043769980" alt="图片" title="图片"></p>
【开源之夏 2023】欢迎报名 SOFAStack 社区项目!
https://segmentfault.com/a/1190000043769502
2023-05-09T18:44:09+08:00
2023-05-09T18:44:09+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043769504" alt="图片" title="图片"></p><p>开源之夏是由“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展,培养和发掘更多优秀的开发者。</p><p>活动联合国内外各大开源社区,针对重要开源软件的开发与维护提供项目任务,并面向全球高校学生开放报名。</p><p>2023 年,<strong>SOFAStack 社区</strong>再次加入中国科学院软件研究所的高校开源活动——“<strong>开源之夏 2023</strong>”,一共为大家准备了五个任务,涵盖 SOFARPC、SOFAArk、SOFAJRaft 和 Layotto 等核心项目,涉及 Golang、Java、Kubernetes、Cloud Native、Distributed System 等多个领域。</p><p><strong>SOFARPC 项目介绍</strong></p><p>SOFARPC 是由蚂蚁集团开源的一款 Java RPC 框架,具有高可扩展性、高性能和生产级特性。该框架旨在简化应用之间的 RPC 调用,并为应用提供便捷透明、稳定高效的点对点远程服务调用方案。为方便用户和开发者进行功能扩展,SOFARPC 提供了丰富的模型抽象和可扩展接口,包括过滤器、路由、负载均衡等。</p><p><strong>SOFAArk 项目介绍</strong></p><p>SOFAArk 是一款基于 Java 实现的轻量级类隔离容器,由蚂蚁集团开源贡献。该容器主要提供类隔离和应用(模块)合并部署能力。SOFAArk 提供多种方式来支持多应用(模块)合并部署,包括基于命令行的管控、基于 API 的管控等。</p><p><strong>SOFAJRaft 项目介绍</strong></p><p>SOFAJRaft 是一个基于 RAFT 一致性算法的生产级高性能 Java 实现,适用于高负载低延迟的场景,支持 MULTI-RAFT-GROUP。使用 SOFAJRaft 可专注于业务领域,由 SOFAJRaft 解决与 RAFT 相关的技术难题。并且 SOFAJRaft 易于使用,可以通过几个示例快速掌握它。</p><p><strong>Layotto 项目介绍</strong></p><p>Layotto(/leɪˈɒtəʊ/) 是一款使用 Golang 开发的应用运行时, 旨在帮助开发人员快速构建云原生应用,帮助应用和基础设施解耦。它为应用提供了各种分布式能力,例如状态管理、配置管理、事件发布订阅等,以简化应用的开发。</p><p><strong>活动规则</strong></p><p>开源之夏官网:</p><p><a href="https://link.segmentfault.com/?enc=h0e6hmMPQmS6bTH9GCRS4A%3D%3D.CSt7puSIrqiN3W5JZ%2FdBgz%2FwYNqfRuBkRtAyqGny330%3D" rel="nofollow">https://summer-ospp.ac.cn/</a></p><p>各位同学可以自由选择项目,与社区导师沟通实现方案并撰写项目计划书。被选中的学生将在社区导师指导下,按计划完成开发工作,并将成果贡献给社区。社区评估学生的完成度,主办方根据评估结果发放资助金额给学生。</p><p><strong>SOFAStack 社区项目</strong></p><p>项目链接:<a href="https://link.segmentfault.com/?enc=hfwyK0aWljROHdi4IWhcZg%3D%3D.VWb%2FPzsCiMxqUQ9BCkGF8tGglJVUb4fJKQg4MU5fEfmNFaBO7Vrqc1xjJLoB4Kts4IPapG%2F4sbmV6L73a0ngo9PCogJdJrHr9LhP2HKux8OWV89Tz1OuRlp61lim61Vv" rel="nofollow">https://summer-ospp.ac.cn/org/orgdetail/95a9e459-a200-4a26-bc0a-81074c2d89be?lang=zh</a></p><p><strong>SOFARPC</strong> Java、网络通信、RPC</p><p><strong>项目社区导师:EvenLiu</strong></p><p><strong><a href="mailto:evenljj@163.com">mailto:evenljj@163.com</a></strong></p><p><strong>SOFARPC 支持 Stream 流式处理方式</strong></p><p><strong>项目编号:2395a0260</strong></p><p><strong>项目难度:进阶/Advanced</strong></p><p>Stream 方式是一种异步的流式处理方式,可以在数据传输过程中逐个处理数据,避免一次性传输大量数据造成的性能问题。服务端 Stream 是指服务端在处理请求时,将数据分成多个部分逐个返回给客户端的过程;客户端 Stream 是指客户端在请求服务器时,将请求参数分成多个部分逐个发送给服务器的过程。Stream 方式可以让我们在处理大量数据时更高效地使用资源,提高系统的性能和响应速度。SOFARPC 中需要 Triple、Bolt 协议支持 Stream 方式流式处理。</p><ul><li>SOFARPC 中 Triple 协议支持 Stream 流式处理。</li><li>SOFARPC 中 Bolt 协议支持 Stream 流式处理。</li></ul><p><strong>SOFAArk</strong> Java、SOFAArk 源代码</p><p><strong>项目社区导师:卫恒</strong></p><p><strong><a href="mailto:glmapper_2018@163.com">mailto:glmapper_2018@163.com</a></strong></p><p><strong>开发一个客户端,支持 Biz 模块的热部署和热卸载,初步实现 Serverless 体验</strong></p><p><strong>项目编号:2395a0267</strong></p><p><strong>项目难度:基础/Basic</strong></p><p>SOFAArk 从最初的一个类隔离框架,逐步演进为支持合并部署与热部署的 “Serverless” 运行时框架,尤其在去年我们完成了 SOFAArk1.0 到 2.0 架构的演进。但是为了让开发者真正享受 Serverless 的研发体验,我们还需要建设一个客户端框架,对接 SOFAArk 实现 Biz 模块的热部署和热卸载,并暴露 HTTP API 接口可以让上游系统或者开发者直接使用。</p><ul><li>设计并开发一个新的 SDK(SOFALet),新的 SDK 也就是 SOFALet 暴露一组 HTTP 接口,底层调用 SOFAArk 原子能力实现模块的热部署和热卸载。SOFALet 未来还会有 Node.js 版,这一期先支持 Java 版也就是对接 SOFAArk。</li><li>理解 SOFAArk 源代码,尤其是关于 telnet 指令安装和卸载模块的部分。</li></ul><p><strong>SOFAArk</strong> Go、K8s</p><p><strong>项目社区导师:流铄</strong></p><p><strong><a href="mailto:xujinle300@126.com">mailto:xujinle300@126.com</a></strong></p><p><strong>开发一个 K8s Operator,编排客户端 API 实现 Biz 模块的热部署,初步达成 Serverless 研发体验</strong></p><p><strong>项目编号:2395a0392</strong></p><p><strong>项目难度:基础/Basic</strong></p><p>为了让开发者真正享受 Serverless 的研发体验,我们需要先建设一个简易的 K8s Operator 和 SOFA Module Deployment、SOFA Module ReplicaSet CRD,对接编排模块热装载和热卸载的客户端,实现模块秒级发布的初步能力,让开发者能初步体验到 Serverless 的发布运维能力。</p><ul><li>理解 SOFAArk 模块安装和卸载部分的源代码,并且熟悉 K8s CRD 和 Operator 体系的设计与开发。</li></ul><p><strong>SOFAJRaft</strong> Java、网络通信、RPC</p><p><strong>项目社区导师:刘源远</strong></p><p><strong><a href="mailto:gege87417376@qq.com">mailto:gege87417376@qq.com</a></strong></p><p><strong>结合 NWR 实现 Flexible RAFT,用于自定义 Quorum 的大小</strong></p><p><strong>项目编号:2395a0390</strong></p><p><strong>项目难度:进阶/Advanced</strong></p><p>JRaft 是一个基于 RAFT 一致性算法的生产级高性能 Java 实现,它运行过程分为两个阶段,即 Leader 选举和日志复制。在原始的 RAFT 算法中,Leader 选举和日志复制都需要获得多数派成员的支持。而 NWR 模型则可以在动态调整一致性强度的场景中使用,它需要满足 W+R>N,以保证强一致性。JRaft 将 RAFT 和 NWR 结合起来,使得用户可以根据不同的业务需求来动态调整 Quorum 的数量。例如,在一个写多读少的场景中,用户可以将多数派的数量从 3 调整为 2,以降低达成共识的条件,从而提高写请求的效率。同时,为了保证 RAFT 的正确性,写 Quorum 的调整需要付出代价,即读 Quorum 的数量也需要相应调整。JRaft 支持成员变更,因此用户可以配置 (0,1] 范围内的小数来计算 W 和 R 的具体值。通过使用 JRaft,用户可以根据自己的业务需求来灵活地调整一致性强度,使得分布式系统在不同场景下都可以获得最佳的性能和正确性。</p><ul><li>为 JRaft 实现自定义 Quorum,动态调节 Quorum 的参数,为自定义 Quorum 的场景增加 Jepsen Case。</li><li>掌握 RAFT 算法协商过程;基于 RAFT 设计 NWR 模型,编写具体设计文档。</li></ul><p><strong>Layotto</strong> Go、Kubernetes、K8s</p><p><strong>项目社区导师:刘训灼</strong></p><p><strong><a href="mailto:mixdeers@gmail.com">mailto:mixdeers@gmail.com</a></strong></p><p><strong>Layotto 支持自动/手动注入 Pod 部署</strong></p><p><strong>项目编号:2395a0359</strong></p><p><strong>项目难度:进阶/Advanced</strong></p><p>Kubernetes 是 CNCF 下容器资源编排的一个实施标准,Layotto 也拥抱 K8s 环境,在 Kubernetes 集群中,常常以 Sidecar 方式运行。社区需提供方式,以便开发者 / 运维在 Kubernetes 环境中快速部署 Layotto。</p><ul><li>提供命令行工具,支持手动注入 Layotto 至 Pod 中;提供 Webhook 插件,支持动态注入 Layotto 至 Pod 中;熟悉 Golang、熟悉 Cobra 编写 Golang 命令行工具。</li><li>了解 K8s 基本架构与原理,理解 Pod 生命周期;了解 K8s WebHook 机制;了解 Golang 模版化以及动态渲染模版相关知识。</li></ul><p><strong>申请资格</strong></p><ul><li>本活动面向年满 18 周岁在校学生。</li><li>暑期即将毕业的学生,只要在申请时学生证处在有效期内,就可以提交申请。</li><li>中国籍学生参与活动需提供身份证、学生证、教育部学籍在线验证报告(学信网)或在读证明。</li><li>外籍学生参与活动需提供护照,同时提供录取通知书、学生卡、在读证明等文件用于证明学生身份。</li></ul><p><strong>活动流程</strong></p><p><img src="/img/remote/1460000043769505" alt="图片" title="图片"></p>
MoE 系列(三)|使用 Istio 动态更新 Go 扩展配置
https://segmentfault.com/a/1190000043750268
2023-05-04T21:31:54+08:00
2023-05-04T21:31:54+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p>上一篇我们用 Go 扩展实现了 Basic Auth,体验了 Go 扩展从 Envoy 接受配置。</p><p>之所以这么设计,是想复用 Envoy 原有的 xDS 配置推送通道,今天我们就来体验一番,<strong>云原生的配置变更</strong>。</p><p><strong>前提准备</strong></p><p>这次我们需要一套 K8s 环境,如果你手头没有,推荐使用 Kind 安装一套。具体安装方式,这里就不展开了。</p><p><strong>安装 Istio</strong></p><p>我们直接安装最新版的 Istio:</p><pre><code># 下载最新版的 istioctl$ export ISTIO_VERSION=1.18.0-alpha.0$ curl -L https://istio.io/downloadIstio | sh -
# 将 istioctl 加入 PATH$ cd istio-$ISTIO_VERSION/$ export PATH=$PATH:$(pwd)/bin
# 安装,包括 istiod 和 ingressgateway$ istioctl install</code></pre><p>是的,由于 Go 扩展已经贡献给了上游官方,Istiod(Pilot)和 Ingress Gateway 都已经默认开启了 Go 扩展,并不需要重新编译。</p><p><strong>Istio 配置 Ingress</strong></p><p>我们先用 Istio 完成标准的 Ingress 场景配置,具体可以看 Istio 的官方文档[1]。</p><p>配置好了之后,简单测试一下:</p><pre><code>$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"HTTP/1.1 200 OKserver: istio-envoydate: Fri, 10 Mar 2023 15:49:37 GMT</code></pre><p>基本的 Ingress 已经跑起来了。</p><p><strong>挂载 Golang so</strong></p><p>之前我们介绍过,Go 扩展是单独编译为 so 文件的,所以,我们需要把 so 文件,挂载到 Ingress Gateway 中。</p><p>这里我们把上次 Basic Auth 编译出来的 libgolang.so,通过本地文件挂载进来。简单点搞,直接 edit deployment 加了这些配置:</p><pre><code># 申明一个 hostPath 的 volumevolumes:- name: golang-so-basic-auth hostPath: path: /data/golang-so/example-basic-auth/libgolang.so type: File
# 挂载进来volumeMounts:- mountPath: /etc/golang/basic-auth.so name: golang-so-basic-auth readOnly: true</code></pre><p><strong>开启 Basic Auth 认证</strong></p><p>Istio 提供了 EnvoyFilter CRD,所以,用 Istio 来配置 Go 扩展也比较方便,apply 这段配置,Basic Auth 就开启了。</p><pre><code>apiVersion: networking.istio.io/v1alpha3kind: EnvoyFiltermetadata: name: golang-filter namespace: istio-systemspec: configPatches: # The first patch adds the lua filter to the listener/http connection manager - applyTo: HTTP_FILTER match: context: GATEWAY listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" subFilter: name: "envoy.filters.http.router" patch: operation: INSERT_BEFORE value: # golang filter specification name: envoy.filters.http.golang typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config" library_id: example library_path: /etc/golang/basic-auth.so plugin_name: basic-auth plugin_config: "@type": "type.googleapis.com/xds.type.v3.TypedStruct" type_url: typexx value: username: foo password: bar</code></pre><p>虽然有点长,但是,也很明显,配置的用户名密码还是:<code>foo:bar</code>。</p><p><strong>测试</strong></p><p>我们测试一下:</p><pre><code>$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"HTTP/1.1 401 Unauthorized
# valid foo:bar$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200" -H 'Authorization: basic Zm9vOmJhcg=='HTTP/1.1 200 OK</code></pre><p>符合预期。</p><p>接下来,我们改一下 EnvoyFilter 中的密码,重新 apply,再测试一下:</p><pre><code># foo:bar not match the new password$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200" -H 'Authorization: basic Zm9vOmJhcg=='HTTP/1.1 401 Unauthorized</code></pre><p>此时的 Envoy 并不需要重启,新的配置就立即生效了,云原生的体验就是这么溜~</p><p><strong>总结</strong></p><p>因为 Go 扩展可以利用 Envoy 原有的 xDS 来接受配置,所以,从 Istio 推送配置也变得很顺利。</p><p>不过,Istio 提供的 EnvoyFilter CRD 在使用上,其实并不是那么方便和自然,后面我们找机会试试 Envoy Gateway,看看 K8s Gateway API 的体验如何。</p><p>至此,我们已经体验了整个 Envoy Go 的开发&使用流程,在云原生时代,人均 Golang 的背景下,相信可以很好的完成网关场景的各种定制需求。</p><p>下一篇,我们将介绍,如何在 Go 扩展中使用异步协程。这意味着,我们可以使用的是一个全功能的 Go 语言,而不是像 Go Wasm 那样,只能用阉割版的。</p><p>敬请期待:<strong>MoE 系列(四)|Go 扩展的异步模式</strong></p><p>[1]Istio 的官方文档:</p><p><a href="https://link.segmentfault.com/?enc=jJvEnsWzfY%2FnVvKsQTQMsQ%3D%3D.gRmIljd%2F71%2F4oJpA9Puo6l9UUDXxNtZdV%2BixzSpDz2YX8lbY8zHFQ8j0hjiXM%2BNi0skci21o0Msdlf4DdkDxgrFNL2n01DLYFqzoDcem6Us%3D" rel="nofollow"><em>https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/</em></a></p>
蚂蚁安全科技 Nydus 镜像加速实践
https://segmentfault.com/a/1190000043726709
2023-04-25T18:50:35+08:00
2023-04-25T18:50:35+08:00
SOFAStack
https://segmentfault.com/u/sofastack
1
<h2>蚂蚁安全科技 Nydus 镜像加速实践</h2><p>原创 曦栖 金融级分布式架构</p><p><img src="/img/remote/1460000043726711" alt="图片" title="图片"></p><p>文|蚂蚁集团 ZOLOZ 团队</p><p><strong>使用全球领先安全科技,为用户和机构提供安全、便捷的安全风控解决方案。</strong></p><p>本文 <strong>6386</strong> 字 阅读 <strong>12</strong> 分钟</p><p><strong>背景简介</strong></p><p>ZOLOZ[1]是蚂蚁集团旗下的全球安全风控平台,通过业内领先的生物识别、大数据分析和人工智能技术,为用户和机构提供安全又便捷的安全风控解决方案。ZOLOZ 已为中国、印尼、马来西亚、菲律宾等 14 个国家和地区的 70 余家合作伙伴提供数字化转型过程中的安全风控技术支持。目前,已经覆盖金融、保险、证券、信贷、电信、公众服务等领域,累计服务用户超 12 亿。</p><p>随着 Kubernetes 和云原生的大爆发,ZOLOZ 应用开始在公有云上进行大规模容器化部署。ZOLOZ 业务的镜像经过长期维护和更新,无论是镜像层数还是整体大小都达到了一个较大的量级 <em>(数百 MB 或者几个 GB)</em> 。特别是 ZOLOZ AI 算法推理应用的基础镜像大小要远大于一般应用镜像 <em>(Docker Hub 上 PyTorch/PyTorch:1.13.1-CUDA 11.6-cuDNN 8-Runtime 有 4.92GB,同比 CentOS:latest 只有约 234MB)</em> ,对于容器冷启动,即在本地无镜像的情况下,需要先从 Registry 下载镜像才能创建容器,在生产环境中,容器的冷启动往往耗时数分钟,并且随规模扩大会导致 Registry 因集群内网络拥堵而无法快速地下载镜像,如此庞大的镜像给应用的更新和扩容等操作都带来了不少挑战。在公有云上容器化持续推进的当下,ZOLOZ 应用主要遇到了三大挑战:</p><ol><li>算法镜像大,推送到云上镜像仓库耗时长,开发过程中,在使用测试环境进行测试时,往往希望快速迭代,快速验证,但是每次改完一个分支发布验证都要经过几十分钟,开发效率十分低下。</li><li>拉取算法镜像耗时长,在集群扩容大量机器拉取镜像文件会容易导致集群网卡被打满,影响业务正常运行。</li><li>集群机器拉起时间长,难以满足流量突增时,弹性自动扩缩容。</li></ol><p>虽然也尝试过各种折中的解决方案,但这些方案都有缺陷,现在结合蚂蚁、阿里云、字节跳动等多个技术团队打造了一套更通用的公有云上解决方案,该方案改造成本低,性能好,目前看来是比较理想的方案。</p><p><strong>术语及定义</strong></p><p><strong>OCI</strong>:Open Container Initiative,开放容器计划是一个 Linux 基金会项目,由 Docker 在 2015 年 6 月启动,旨在为操作系统级虚拟化 <em>(最重要的是 Linux 容器)</em> 设计开放标准。</p><p><strong>OCI Manifest</strong>:遵循 OCI Image Spec 的制品。</p><p><strong>BuildKit</strong>:是 Docker 公司出品的一款更高效、Docekrfile 无关、更契合云原生应用的新一代 Docker 构建工具。</p><p><strong>镜像</strong>:本文中的镜像指 OCI Manifest,也包括 Helm Chart 等其他 OCI Manifest。</p><p><strong>镜像仓库</strong>:遵循 OCI Distribution Spec 实现的制品仓库。</p><p><strong>ECS</strong>:是一种由 CPU、内存、云盘组成的资源集合,每一种资源都会逻辑对应到数据中心的计算硬件实体。</p><p><strong>ACR</strong>:阿里云镜像仓库服务。</p><p><strong>ACK</strong>:阿里云容器服务 Kubernetes 版提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。</p><p><strong>ACI</strong>:ACI 全称 Ant Continuous Integration <em>(AntCI)</em> ,是蚂蚁集团研发效能旗下一款以流水线 <em>(Pipeline)</em> 为核心的 CI/CD 效能产品。使用智能自动化构建、测试和部署,提供了以代码流为输入的轻量级持续交付解决方案,提高团队研发的工作效率。</p><p><strong>Private Zone</strong>:基于专有网络 VPC <em>(Virtual Private Cloud)</em> 环境的私有 DNS 服务。该服务允许在自定义的一个或多个 VPC 中将私有域名映射到 IP 地址。</p><p><strong>P2P</strong>:点对点技术,当 P2P 网络中某一个 Peer 从 Server 下载数据的时候,下载完数据后也能当作服务端供其他 Peer 下载。当大量节点同时下载的时候,能保证后续下载的数据,可以不用从 Server 端下载。从而减轻 Server 端的压力。</p><p><strong>Dragonfly</strong>:Dragonfly 是⼀款基于 P2P 技术的文件分发和镜像加速系统,并且是云原生架构中镜像加速领域的标准解决方案以及最佳实践。现在为云原生计算机基金会 <em>(CNCF)</em> 托管作为孵化级项目。</p><p><strong>Nydus</strong>:Nydus 镜像加速框架是 Dragonfly 的子项目,它提供了容器镜像按需加载的能力,在生产环境支撑了每日百万级别的加速镜像容器创建,在启动性能,镜像空间优化,端到端数据一致性,内核态支持等方面相比 OCIv1 有巨大优势。</p><p><strong>LifseaOS</strong>:面向容器场景,阿里云推出轻量、快速、安全、镜像原子管理的容器优化操作系统,相比传统操作系统软件包数量减少 60%,镜像大小减少 70%,OS 首次启动从传统 OS 的 1min 以上下降到了 2s 左右。支持镜像只读和 OSTree 技术,将 OS 镜像版本化管理,更新操作系统上的软件包、或者固化的配置时,以整个镜像为粒度进行更新。</p><p><strong>方案设计</strong></p><p><strong>解决镜像大的问题</strong></p><p><strong>1. 精简基础</strong> <strong>镜像</strong> <strong>大小</strong></p><p>基础 OS 从 CentOS 7 改为 AnolisOS 8,精简运维工具的安装,只默认安装一些必须的工具列表 <em>(基础运维工具、运行时通用依赖、日志清理、安全基线等组件)</em> ,并简化安全加固的配置,基础镜像从 1.63GB 减少到 300MB。 </p><p>AnolisOS 仓库:<a href="https://link.segmentfault.com/?enc=71P7A5MRMAWlLkaDSMgQxg%3D%3D.zrPT%2FVeq7r7AzxqpLm5bpCE4S3sFvBQ8HlHCNZsevVznloB1bPaNp56MLYy7Pdhe5RwTKdmGWflYMZ5ZAmmKFA%3D%3D" rel="nofollow"><em>https://hub.docker.com/r/openanolis/anolisos/tags</em></a></p><p><strong>2. Dockerfile 优化</strong></p><p>通过 Dockerfile 编写约束、镜像检测等手段减少不必要的构建资源和时间。 </p><p>Dockerfile 最佳实践原则: <a href="https://link.segmentfault.com/?enc=7T4XxT2Cdj2sAWuBRuLGVQ%3D%3D.DHkBcpoiXl3tGOk%2BHbQY0gCts3zSCbvitn5V3g7I6RmWrsYBjX%2BevXDHEnfnXDtmMEwPSnuO%2Bt4MQxEuXEDWg9mr3zx%2Fd%2FQgnwFRUtvZdGQ%3D" rel="nofollow"><em>https://docs.docker.com/develop/develop-images/dockerfile_best-practices/</em></a></p><p><strong>3. 并行构建和构建缓存</strong></p><p>蚂蚁构建中心采用 Nydus 社区优化版的 BuildKit[2],BuildKit 支持 layer 级别缓存,准确引用先前的产出物并进行缓存匹配,使用方法与普通镜像并无区别,对于 Multistage 类型 Dockerfile,BuildKit 可以实现不同 stage 之间的并行执行。 </p><p><strong>推送到云上镜像仓库耗时长</strong></p><p><strong>1. 使用 Nydus 镜像进行块级别数据去重</strong></p><p>传统 OCI 镜像,不同镜像之间可以共享的最小单位是镜像中的层,在 deduplication 上的效率是非常低的,层内部存在重复的数据,层与层之间可能存在大量重复的数据,即使有微小的差别,也会被作为不同的层,根据 OCI Image Spec 对删除文件和 Hard Link 的设计,一个镜像内部可能存在已经被上层删除的文件仍然存在于下层中,并包含在镜像中。另外 OCI Image 使用了 tar+gzip 格式来表达镜像中的层,而 tar 格式并不区分 tar archive entries ordering,这带来一个问题即如果用户在不同机器上 build 去同一个镜像,最终可能会因为使用了不同的文件系统而得到不同的镜像,但若干不同镜像的实质内容是完全相同的情况,导致上传下载数据量飙增。 </p><p>OCIv1 存在的问题与 OCIv2 提案:<a href="https://link.segmentfault.com/?enc=RYspnN0L35QA01w23J20UQ%3D%3D.iKqt7bY1p551FJpRM9a2Zk%2BPJQStCpoP8XwMhR57rK%2FESbFrkKwbdSa5IVc7OeGn" rel="nofollow"><em>https://hackmd.io/@cyphar/ociv2-brainstorm</em></a></p><p>Nydus 镜像文件以文件 Chunk 为粒度分割,扁平化元数据层 <em>(移除中间层</em> <em>)</em> ,每一个 Chunk 在镜像中只会保存一次,可指定 Base Image , 用作其他 Nydus Image 的 Chunk dictionary,基于 Chunk level deduplication 提供了在不同镜像之间低成本的 data 去重能力,大大降低了镜像的上传和下载数据量。</p><p><img src="/img/remote/1460000043726712" alt="图片" title="图片"></p><p>Nydus 镜像块共享</p><p>如上图 Nydus 镜像 1 和镜像 2 存在相同的数据块 B2、C、E1、F,镜像 2 新增 E2、G1、H1、H2,如果镜像仓库已经存在镜像 1,那么镜像 2 可以基于镜像 1 进行镜像构建,仅需要将 E2、G1、H1、H2 构建在一层,在上传的时候仅需要将这一层上传到镜像仓库,达到仅文件差异上传、拉取的效果,缩短研发周期。 </p><p><strong>2. 直接构建云上 Nydus 镜像</strong></p><p>目前在大多数加速镜像的落地场景中,加速镜像的生产都是基于镜像转换的。目前落地的 Nydus 转换方案主要为以下两种: </p><p>i. 镜像仓库转换</p><p>普通镜像构建完成并 push 到镜像仓库后,触发镜像仓库的转换动作,完成镜像转换。这种方案的缺点在于,构建和转换往往在不同机器上进行。镜像构建并 push 后,还需要 pull 到转换机并将产出 push 到镜像仓库,需要增加一次完整的镜像流转过程,延迟较高,而且还占用镜像仓库的网络资源。在加速镜像转换完成前,应用发布并无法享受加速效果,仍需要完整 pull 镜像。 </p><p><img src="/img/remote/1460000043726713" alt="图片" title="图片"></p><p>ii. 双版本构建</p><p>在普通镜像构建完成后,在构建机本地直接转换。为提高效率,可以在每层构建完成后即开始对该层进行转换,加速镜像生成延迟可以大幅降低。这个方案,无需等待普通镜像上传即可开始转换,而且在本地转换,相比较方案 1,可以省掉的转换机镜像传输的开销。如果基础镜像对应的加速镜像不存在,则将其转换出来;如果存在,pull 可以忽略不计,但是无可避免的是 push 总是需要双份。 </p><p><img src="/img/remote/1460000043726714" alt="图片" title="图片"></p><p>iii. 直接构建</p><p>上述两种基于转换的方案,与直接构建 Nydus 加速镜像相比,都有明显的生产延迟。一是基于 OCI 的镜像构建速度明显慢于 Nydus 镜像构建;二是转换是事后行为,都存在或多或少的滞后;三是都存在额外的数据传输。而直接构建,流程少,速度快,又节约资源:</p><p><img src="/img/remote/1460000043726715" alt="图片" title="图片"></p><p>可以看出,加速镜像构建,步骤明显减少,数据传输量明显减少,构建完成后即可直接享受加速镜像的能力,应用发布速度可以大幅提升。</p><p><strong>镜像启动慢</strong></p><p><strong>1. Nydus 镜像按需加载</strong></p><p>在容器启动时,容器内业务 IO 请求哪些文件的数据,再从远端 Registry 拉取这些数据,这样避免镜像大量数据拉取阻塞容器的启动,镜像的数据的实际使用率是很低的,比如 Cern 的这篇论文[3]中就提到,一般镜像只有 6% 的内容会被实际用到。按需加载的目的是让容器运行时有选择地从 Blob 中的镜像层 <em>(layer)</em> 下载和提取文件,但 OCI[4]/ Docker[5]镜像规范将所有的镜像层打包成一个 tar 或 tar.gz 存档,这样即使你要提取单个文件也要扫描整个 Blob。如果镜像使用 gzip 进行压缩,就更没有办法提取特定文件了。 </p><p><img src="/img/remote/1460000043726716" alt="图片" title="图片"></p><p>Nydus 镜像格式</p><p>RAFS 镜像格式[6]是 Nydus 提出的存档压缩格式。其中将容器镜像文件系统的数据 <em>(</em> <em>Blobs)</em> 和元数据 <em>(</em> <em>Bootstrap)</em> 分离,让原来的镜像层只存储文件的数据部分。并且把文件以 Chunk 为粒度分割,每层 Blob 存储对应的 Chunk 数据;因为采用了 Chunk 粒度,这细化了去重粒度,Chunk 级去重让层与层之间,镜像与镜像之间共享数据更容易,也更容易实现按需加载。原来的镜像层只存储文件的数据部分 <em>(也就是图中的 Blob 层)</em> 。Blob 层存储的是文件数据的切块 <em>(Chunk)</em> ,例如将一个 10MB 的文件,切割成 10 个 1MB 的块,于是就可以将 Chunk 的 Offset 记录在一个索引中,容器在请求文件的部分数据时,通过结合 OCI/Docker 镜像仓库规范支持的 HTTP Range Request,容器运行时可以有选择地从镜像仓库中获取文件,如此一来节省不必要的网络开销。关于 Nydus 镜像格式的更多细节,请参考 Nydus Image Service 项目[7]。 </p><p>元数据和 Chunk 的索引加在一起,就组成了上图中的 Meta 层,它是所有镜像层堆叠后容器能看到的整个 Filesystem 结构,包含目录树结构,文件元数据,Chunk 信息 <em>(块的大小和偏移量,以及每个文件的元数据(名称、文件类型、所有者等))</em> 。有了 Meta 之后,就可以在不扫描整个存档文件的情况下提取需要的文件。另外,Meta 层包含了 Hash 树以及 Chunk 数据块的 Hash,以此来保证我们可以在运行时对整颗文件树校验,以及针对某个 Chunk 数据块做校验,并且可以对整个 Meta 层签名,以保证运行时数据被篡改后依然能够被检查出来。</p><p><img src="/img/remote/1460000043726717" alt="图片" title="图片"></p><p>Nydus 按需加载</p><p>Nydus 默认使用用户态文件系统实现 FUSE[8]来做按需加载,用户态的 Nydus Daemon 进程将 Nydus 镜像挂载点作为容器 RootFS 目录,当容器产生 <code>read(fd,count)</code> 之类的文件系统 IO 时,内核态 FUSE 驱动将该请求加入处理队列,用户态 Nydus Daemon 通过 FUSE Device 读取并处理该请求,从远端 Registry 拉取 Count 对应数量的 Chunk 数据块后,最终通过内核态 FUSE 回复给容器。Nydus 还实现了一层本地 Cache,已经从远端拉取的 Chunk 会解压缩后缓存在本地,Cache 可以做到以层为单位在镜像之间共享,也可以做到 Chunk 级别的共享。</p><p><img src="/img/remote/1460000043726718" alt="图片" title="图片"></p><p>从 Pod 创建到容器启动</p><p>利用 Nydus 做镜像加速后,不同应用的启动时间都有了质的飞跃,能够在非常短的时间内拉起应用,满足云上快速伸缩的要求。</p><p><strong>2. 只读文件系统 EROFS</strong></p><p>当容器镜像中存在大量文件时,频繁的文件操作会产生大量的 FUSE 请求,造成内核态/用户态上下文的频繁切换,造成性能瓶颈;依托于内核态 EROFS <em>(</em> <em>始于 Linux 4.19)</em> 文件系统,Nydus 对其进行了一系列的改进与增强,拓展其在镜像场景下的能力,最终呈现为一个内核态的容器镜像格式,Nydus RAFS <em>(Registry Acceleration File System)</em> v6,相比于此前的格式,它具备块数据对齐,元数据更加精简,高可扩展性与高性能等优势。在镜像数据全部下载到本地的情况下,FUSE 用户态方案会导致访问文件的进程频繁陷出到用户态,并涉及内核态/用户态之间的内存拷贝,更进一步支持了 EROFS over FS-Cache 方案 <em>(Linux 5.19-rc1)</em> ,当用户态 Nydusd 从远端下载 Chunk 后会直接写入 FS-Cache 缓存,之后容器访问时,能够直接通过内核态 FS-Cache 读取数据,而无需陷出到用户态,在容器镜像的场景下实现几乎无损的性能和稳定性,其表现优于 FUSE 用户态方案,同时与原生文件系统 <em>(未使用按需加载)</em> 的性能相近。</p><p><img src="/img/remote/1460000043726719" alt="图片" title="图片"></p><p>不同文件系统方案耗时对比</p><p>目前 Nydus 在构建,运行,内核态 <em>(</em> <em>Linux 5.19-rc1)</em> 均已支持了该方案,详细用法可以参见 Nydus EROFS FS-Cache user guide[9],另外想了解更多 Nydus 内核态实现细节,可以参见 Nydus 镜像加速之内核演进之路[10]。</p><p><img src="/img/remote/1460000043726720" alt="图片" title="图片"></p><p>Nydus 内核实现细节</p><p><strong>3. Dragonfly P2P 加速镜像下载</strong></p><p>不论是镜像仓库服务本身还是背后的存储,最终肯定是有带宽和 QPS 限制的。如果单纯依赖服务端提供的带宽和 QPS,很容易就无法满足需求。因此需要引入 P2P,减轻服务端压力,进而满足大规模并发拉取镜像的需求。在大规模拉镜像的场景下,在使用 Dragonfly&Nydus 场景对比 OCIv1 场景能够节省 90% 以上的容器启动时间。 </p><p><img src="/img/remote/1460000043726721" alt="图片" title="图片"></p><p>Dragonfly P2P 镜像加速拉取</p><p>使用 Nydus 之后启动时间更短是因为镜像 Lazyload 的特性,只需要拉取很小的一部分元数据 Pod 就能启动。在大规模场景下,使用 Dragonfly 回源拉取镜像的数量很少。OCIv1 的场景所有的镜像拉取都要回源,因此使用 Dragonfly 回源峰值和回源流量相比 OCIv1 的场景少很多。并且使用 Dragonfly 后随着并发数提高,回源峰值和流量不会显著提高。</p><p><img src="/img/remote/1460000043726722" alt="图片" title="图片"></p><p>1G 大小的随机文件测试</p><p><strong>集群伸缩时间长</strong></p><p><strong>1. ACR 镜像仓库全球同步</strong></p><p>为了满足客户优质的体验以及数据合规需求,需要就近接入,因此 ZOLOZ 在全球多个站点进行云上部署。借助 ACR 镜像仓库进行跨境同步加速,全球多地域间同步,提高容器镜像分发效率。上传和下载镜像都在本区域机房内进行,特别是在一些网络不太好的国家内,也能够像本地机房一样进行部署,真正做到应用的一键部署到全球各地。 </p><p><strong>2. 采用 ContainerOS 极速启动</strong></p><p>借助云原生满足客户急速增长资源扩容,利用弹性降低成本,在云上需要极速伸缩虚拟机,并将其加入到集群内部。ContainerOS 通过简化 OS 启动流程,预置集群管控必备组件的容器镜像以减少节点启动过程中因镜像拉取而带来的耗时,极大地提高了 OS 启动速度,降低了 ACK 链路中的节点扩容时间。ContainerOS 从如下几个方面进行了优化:</p><ul><li>ContainerOS 通过简化 OS 启动流程,有效降低 OS 启动时间。ContainerOS 的定位是跑在云上虚拟机的操作系统,不会涉及到太多的硬件驱动,因此 ContainerOS 将必要的内核驱动模块修改为 built-in 模式。此外,ContainerOS 去除了 initramfs,且 udev 规则也被大大简化,此时 OS 启动速度得到了大幅提升。以 ecs.g7.large 规格的 ECS 实例为例,LifseaOS 的首次启动时间保持在 2s 左右,而 Alinux3 则需要 1min 以上。</li><li>ContainerOS 通过预置集群管控必备组件的容器镜像以减少节点启动过程中因镜像拉取而带来的耗时。ECS 节点启动完成后需要拉取部分组件的容器镜像,这些组件负责在 ACK 场景下执行一些基础性的工作。例如 Terway 组件负责网络,节点必须在 Terway 组件的容器就绪的情况下才能转换为就绪状态。因此,既然网络拉取的长尾效应会带来极大的耗时,那么可以通过预置的方式提前将此组件提前安装在 OS 内部,此时可直接从本地目录获取,避免网络拉取镜像耗时。</li><li>ContainerOS 也会通过结合 ACK 管控链路优化,提高节点弹性性能。</li></ul><p>最终,统计了从空的 ACK 节点池扩容的端到端的 P90 耗时,从下发扩容请求开始计时,到 90% 的节点处于就绪状态结束计时,并对比了 CentOS、Alinux2 Optimized-OS[11]方案,ContainerOS 性能优势明显,具体数据如下图所示。</p><p><img src="/img/remote/1460000043726723" alt="图片" title="图片"></p><p><p align=center>ecs.c6.xlarge 并发启动数据</p></p><p><strong>整体链路</strong></p><p><img src="/img/remote/1460000043726724" alt="图片" title="图片"></p><p><p align=center>ZOLOZ 公有云应用部署整体方案</p></p><ul><li>通过精简基础镜像以及遵循 Dockerfile 规约,对镜像大小进行精简。</li><li>利用蚂蚁托管的 BuildKit 对镜像进行 Multistage 并行构建,在重复构建时采用缓存加快镜像构建。直接构建 Nydus 加速镜像时通过镜像之间重复分析进行去重,仅上传镜像之间差异的块到远程镜像仓库。</li><li>通过 ACR 全球加速同步的能力,将镜像分发到全球不同的镜像仓库中,进行就近拉取。</li><li>通过 Dragonfly P2P 网络对 Nydus 镜像块进行按需加速拉取。</li><li>节点上使用 ContainerOS 操作系统,提高 OS 启动速度以及镜像启动速度。</li></ul><p><img src="/img/remote/1460000043726725" alt="图片" title="图片"></p><p>容器研发流程</p><p><img src="/img/remote/1460000043726726" alt="图片" title="图片"></p><p>以 3G 镜像为例</p><p>*调度时间:该时间是指从阿里云创建一台 ECS,到节点加入到 K8s 集群并 Ready 的时间,得益于 ContainerOS 的优化,该耗时降低非常明显。</p><p>通过对研发全流程各个环节进行极致的优化,可以发现优化后,研发效率和线上稳定性都得到了质的提升,目前整套方案已经在阿里云和 AWS 都完成了部署,线上稳定运行 3 个月,未来将在云厂商提供标准的部署环境,满足更多类型的业务场景。</p><p><strong>使用指南</strong></p><p><strong>镜像构建</strong></p><p>代码资产都在蚂蚁域内,利用蚂蚁镜像构建中心托管的 BuildKit 集群,通过自定义的 ACI 组件进行构建 Nydus 镜像。</p><pre><code>镜像构建: stage: 镜像构建 id: build-image component: nydus-image-build inputs: imageName: ${{parameters.imageName}} #构建的镜像 name imageTag: ${{vcs.commitSha}} # 构建的镜像 tag,这里的 ${{vcs.commitSha}} 是 ACI 内置参数 dockerfile: Dockerfile # dockerfile文件位置(默认相对代码根目录) chunkDictImage: ${{parameters.chunkDictImage}} timeoutInSec: 1200</code></pre><p>可以指定 Chunk Dict Image 按 Chunk 去重粒度,如果构建的镜像和 Chunk Dict Image。Image Name 可以直接指定阿里云 ACR 仓库,构建的 Nydus 镜像直接推送到云上,减少镜像中转耗时。</p><p><strong>Dragonfly 安装</strong></p><pre><code>$ helm repo add dragonfly https://dragonflyoss.github.io/helm-charts/$ helm install --wait --timeout 10m --dependency-update --create-namespace --namespace dragonfly-system dragonfly dragonfly/dragonfly --set dfdaemon.config.download.prefetch=true,seedPeer.config.download.prefetch=trueNAME: dragonflyLAST DEPLOYED: Fri Apr 7 10:35:12 2023NAMESPACE: dragonfly-systemSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:1. Get the scheduler address by running these commands: export SCHEDULER_POD_NAME=$(kubectl get pods --namespace dragonfly-system -l "app=dragonfly,release=dragonfly,component=scheduler" -o jsonpath={.items[0].metadata.name}) export SCHEDULER_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly-system $SCHEDULER_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") kubectl --namespace dragonfly-system port-forward $SCHEDULER_POD_NAME 8002:$SCHEDULER_CONTAINER_PORT echo "Visit http://127.0.0.1:8002 to use your scheduler"
2. Get the dfdaemon port by running these commands: export DFDAEMON_POD_NAME=$(kubectl get pods --namespace dragonfly-system -l "app=dragonfly,release=dragonfly,component=dfdaemon" -o jsonpath={.items[0].metadata.name}) export DFDAEMON_CONTAINER_PORT=$(kubectl get pod --namespace dragonfly-system $DFDAEMON_POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") You can use $DFDAEMON_CONTAINER_PORT as a proxy port in Node.
3. Configure runtime to use dragonfly: https://d7y.io/docs/getting-started/quick-start/kubernetes/</code></pre><p>更多详情参考:<a href="https://link.segmentfault.com/?enc=eH%2BHGpB%2B4ZsvuwHqSECryA%3D%3D.DIc4EjkIgy3pBQOUgiQobDalFjoSw1t2pMaTEoguSfyzF1ZyuV3aIGIBdIcMvjdO" rel="nofollow"><em>https://d7y.io/zh/docs/setup/integration/nydus</em></a></p><p><strong>Nydus 安装</strong></p><pre><code>$ curl -fsSL -o config-nydus.yaml https://raw.githubusercontent.com/dragonflyoss/Dragonfly2/main/test/testdata/charts/config-nydus.yaml$ helm install --wait --timeout 10m --dependency-update --create-namespace --namespace nydus-snapshotter nydus-snapshotter dragonfly/nydus-snapshotter -f config-nydus.yamlNAME: nydus-snapshotterLAST DEPLOYED: Fri Apr 7 10:40:50 2023NAMESPACE: nydus-snapshotterSTATUS: deployedREVISION: 1TEST SUITE: NoneNOTES:Thank you for installing nydus-snapshotter.
Your release is named nydus-snapshotter.
To learn more about the release, try:
$ helm status nydus-snapshotter $ helm get all nydus-snapshotter</code></pre><p>更多详情参考:<a href="https://link.segmentfault.com/?enc=xMmWAsCGZqSNuYEKg20srw%3D%3D.6Ti9eaUEfKqEKPixJ93RaxQDD2tP7kOG%2BBAuC6y5mu7CVaPGaDqJiSlZ0eWk5mlfXZvZt4XZV%2F9cKobsM%2B9JKZ76AGvWs1TPnvVemx74N7w%3D" rel="nofollow"><em>https://github.com/dragonflyoss/helm-charts/blob/main/INSTALL.md</em></a></p><p><strong>ContainerOS 使用</strong></p><p>ContainerOS 针对 ACK 集群节点池的弹性扩容场景,实现了极速扩容的特性。一方面,LifseaOS 通过简化 OS 本身的启动流程提高了 OS 启动速度。它裁剪掉了大量云上场景无需的硬件驱动,必要的内核驱动模块修改为 built-in 模式,去除了 initramfs,udev 规则也被大大简化,OS 首次启动时间从传统 OS 的 1min 以上下降到了 2s 左右。另一方面,ContainerOS 结合 ACK 场景进行了定制优化。它通过预置集群管控必备组件的容器镜像以减少节点启动过程中因镜像拉取而带来的耗时,并结合 ACK 管控链路优化(例如调节关键逻辑的检测频率、调整高负载下系统瓶颈中的限流值等),极大地提高了节点扩容速度。在阿里云控制台上为 ACK 集群建立托管节点池[12]时,在配置菜单中可以选择 ECS 实例的操作系统,下拉选择 ContainerOS 即可,OS 镜像名字中的 1.24.6 对应的是集群的 K8s 版本。另外,如果您需要高性能的节点池弹性扩容能力,为了实现最佳的节点扩容性能,更多信息请参见使用 ContainerOS 实现节点极速扩容[13]。</p><p><img src="/img/remote/1460000043726727" alt="图片" title="图片"></p><p><strong>注意事项</strong></p><p>ContainerOS 目前仅支持 Kubernetes 1.24.6 及以上版本,需要在创建 ACK 集群,或升级 ACK 集群的 K8s 版本[14]为 1.24.6 及以上版本方可使用。ContainerOS 预置了影响 Node Ready 和 Pod Ready 的必备的组件,如 Flannel、Terway 等网络插件。原本节点启动后需要拉取并启动这些组件的容器镜像,节点才会处于就绪状态,预置之后便无需从网络上拉取。但是,为了防止集群的组件版本与预置的组件版本不一致的情况,请参考注意事项[15]。 </p><p><strong>参考资料</strong></p><ul><li><a href="https://link.segmentfault.com/?enc=Ln49b1GjtPqD5SX3HiEF1A%3D%3D.Ie4a9jOt3wZDbHRhijZbz4fMsMexZsuuc3FbvZCsGbRiuCypX0yFokteGh5abrW5" rel="nofollow"><em>https://github.com/containerd/containerd</em></a></li><li><a href="https://link.segmentfault.com/?enc=cvdpdi5ITzug2n5uPvWrqQ%3D%3D.1n8epFa0Ta0K%2FC3c6V64DQjtJDxMe3H%2BtDnKPD6EuL3HgEzVKU7AMZXGo46OoE4b" rel="nofollow"><em>https://github.com/dragonflyoss/Dragonfly2</em></a></li><li><a href="https://link.segmentfault.com/?enc=t1PgYsrmZwqGsE%2BkWz%2BZLw%3D%3D.%2F5CQ0pjdQxEKSh4aj0SXbw%3D%3D" rel="nofollow"><em>https://d7y.io/</em></a></li><li><a href="https://link.segmentfault.com/?enc=zeoWuZooN2jHneuSEI7uEg%3D%3D.fUnX3FJZYPjLOEabgufPVEnCdDv0Wp5vNrshBXQ3BlzqwPQNnWmuUosSWyrMT2Fg" rel="nofollow"><em>https://github.com/dragonflyoss/image-service</em></a></li><li><a href="https://link.segmentfault.com/?enc=A9KqHAmYeobQ1PPX4gjayA%3D%3D.U2YooVZv6IR5a3vybql5wy9PJrwn89uaDbMtSGOpYL0%3D" rel="nofollow"><em>https://nydus.dev/</em></a></li><li><a href="https://link.segmentfault.com/?enc=t%2BedSMoTmf4Lus%2Bp26UztA%3D%3D.lQZdURyYUubW7dDQlm1WruVLGT%2FQ7BrdbclNDwulp2c38TB2mDbMbqNL3Vi0rzHX" rel="nofollow"><em>https://github.com/goharbor/harbor</em></a></li><li><a href="https://link.segmentfault.com/?enc=A7NuqVlnfQ3xVHShLXOFGA%3D%3D.HFyZJrG6NKA37nsTJGjBE0zl4qPyHAC1UMm2P5zL38I0DpZNezsjAD4m1oRiE09erdR%2BkyncUWtpQv%2B2xfVNZQ%3D%3D" rel="nofollow"><em>https://www.alibabacloud.com/help/zh/container-registry</em></a></li><li><a href="https://link.segmentfault.com/?enc=WyaJkYWG0c%2B2IceHqSljJg%3D%3D.zsf4F557%2Frdgla%2FSCUWdOXNz7bfibteHI58m1u2weqelTXnSNRV8%2Bod2RbFP80i68Uvl4%2BQNL0bo4InLjlR8Vw%3D%3D" rel="nofollow"><em>https://github.com/moby/moby/tree/master/image/spec</em></a></li><li><a href="https://link.segmentfault.com/?enc=vUODO1o7H4QE1nvcHUsiKg%3D%3D.Ul4VMkPWauq8wU3CA9XMtgAuJGI06Q8Zo39lDPpemwrYbiIwesga309vEtHgROX5PfUwZrDLD5t1ixktlogAig%3D%3D" rel="nofollow"><em>https://docs.docker.com/registry/spec/manifest-v2-1/</em></a></li><li><a href="https://link.segmentfault.com/?enc=9EZjbVHPrzpbtZavobN8ng%3D%3D.EWqUNuY7MJPoYQB%2Bo1HOZTi%2BD4PSzc9mcMer%2BdtRkKiBc2zFqGv%2BYsKpSnPVUONEDhS4WBYUxRSTw9lWrLbyqQ%3D%3D" rel="nofollow"><em>https://docs.docker.com/registry/spec/manifest-v2-2/</em></a></li><li><a href="https://link.segmentfault.com/?enc=dgBBl5Ke7iSwC9qYUmbvXQ%3D%3D.ET74hLnkEmUaJUxb8UuAD9oM1ppQdAXQDs9wEZODv%2BIbfBDLAZorjdZ%2BOmAi7bkwrawSQj6HO8ioVslBxm43ze4BSPyl0tP0RhN0l%2FGbrPrvf%2FW8tMqoeqwuRvkmQday" rel="nofollow"><em>https://github.com/opencontainers/image-spec/blob/main/layer.md#representing-changes</em></a></li><li><a href="https://link.segmentfault.com/?enc=I%2BJ1HM0i4qb2ohRAm1lSgw%3D%3D.SGnl%2FP57vxowNjF9fiiSvd8FH1IdzssYTjoO3wVcP6TJPE2dNcJAvGTKvcNEEFbtWARzRWlHe5RaZ6uD6624CnAcz8RlrJDKQf2quMi2F2s%3D" rel="nofollow"><em>https://github.com/opencontainers/image-spec/blob/main/manifest.md</em></a></li><li><a href="https://link.segmentfault.com/?enc=Rjfw2icn3N89CabVaqo8fQ%3D%3D.wS3m9H%2F8AtXtkP0BI%2Bm%2BSNIijKGbNAjFMhLevfTmRqaTcJb41WPPt%2FsW%2Bl7%2BivWuBUlpA2Ewbp3wL%2FkoAAS7d721twzrl0CQvKY%2BwFG0uq0%3D" rel="nofollow"><em>https://www.usenix.org/conference/fast16/technical-sessions/presentation/harter</em></a></li><li><a href="https://link.segmentfault.com/?enc=TQhHebcjE7Lx%2FUyF3OoCqg%3D%3D.IsInCHgCgA8ntnfCbpcEeJW3FmN9WiHR6N4hSqxLrHS5l4Ozk02uqgBJ4KSSH1lpyvnXaK7iQhV1OFtZhVrBMA%3D%3D" rel="nofollow"><em>https://www.kernel.org/doc/html/latest/filesystems/fuse.html</em></a></li><li><a href="https://link.segmentfault.com/?enc=8jSRZ6vxb9UiLfgl4yY5uw%3D%3D.Ww1%2B8cjlkQOEOhVIaFPMVcohm7bjcOMiR4rMWUesX4s%3D" rel="nofollow"><em>https://virtio-fs.gitlab.io/</em></a></li><li><a href="https://link.segmentfault.com/?enc=FY3%2FGNmk%2BRjYwf2hCNdRiQ%3D%3D.V3AE7%2BS7PU7gQ6eRRrwiZFXOpJ1XxjAhwrPF1EvdDVjotSBcD%2B1e6ja%2FUW8rBYf95EOJ0AsWRu%2BbtvqtNr2L4w%3D%3D" rel="nofollow"><em>https://www.kernel.org/doc/html/latest/filesystems/erofs.html</em></a></li><li><a href="https://link.segmentfault.com/?enc=UQqKAdnpvFfcACjOcuN3yA%3D%3D.DZLyocijc7F6Li%2B8lugIFY3oucNN30KZBbqQrmNk%2FpDUmFMzdXcAm7o6AnkN4rjyOUAaAUSGUoJavcPiunvfZOGJIyFV5Zd%2FswiWaeEh8XQ1BSaub6gmUj2m%2B4eqR6C1" rel="nofollow"><em>https://github.com/dragonflyoss/image-service/blob/fscache/docs/nydus-fscache.md</em></a></li><li><a href="https://link.segmentfault.com/?enc=PH214B2ZCB7Y1v0hCkuINA%3D%3D.FbZmEgu%2Bu2LKRdhax6jovMqjeQE%2BlgfdsSZRkkK3qk3M3YEroa4cauBiG5j04KMY93%2FrQUh96rcpmRWvAgwSZQ%3D%3D" rel="nofollow"><em>https://mp.weixin.qq.com/s/w7lIZxT9Wk6-zJr23oBDzA</em></a></li><li><a href="https://link.segmentfault.com/?enc=gWrpTgq3w5HQ%2FBOrlqi6Ew%3D%3D.16kPGRFjVLMPfF74Onk2fP7ursTxokb3A8oDnEBBWPFmOhwWml8E30b19EL39WQ5MXrozml2AD2tqtUQE%2FbRtSwE8jkaDbB9%2Fz4r0h23H%2FGMoQ6tMbd04NyThGnAG0K1%2FAqjn6C9rjrXgoDCdpD2oA%3D%3D" rel="nofollow"><em>https://static.sched.com/hosted_files/kccncosschn21/fd/EROFS_What_Are_We_Doing_Now_For_Containers.pdf</em></a></li><li><a href="https://link.segmentfault.com/?enc=IH05Wh%2BKh7FJ79JgtuFqEw%3D%3D.5DLEf1AIzBve%2B3U1u8gKgZoFXixhF2Pg8rhfN%2Fw9%2B6JIbrS%2Bh22tjQyUrnv3eULukLItzGsbK%2B6a1M70gPVdzA%3D%3D" rel="nofollow"><em>https://github.com/imeoer/buildkit/tree/nydus-compression-type</em></a></li></ul><p><strong>参考链接</strong></p><p>[1]ZOLOZ:<a href="https://link.segmentfault.com/?enc=Ax5eR0nCV2HJwvW4NvHYYw%3D%3D.jxxzC5qip1poZKT7ljC254kC8nKFRCN22ghkyw1P4qs%3D" rel="nofollow"><em>https://www.zoloz.com/</em></a></p><p>[2]BuildKit:<a href="https://link.segmentfault.com/?enc=YRUjAUoxY9hlXOeXUvsFXw%3D%3D.IWH9ooIBMAK0DSZe%2B06ix4ZDL8wvf%2Bb%2BXQrgVgwxoUgToL2KFqTYnrltr3NYxfc7Q%2BDByABIOgRiU3Ze3LkPNw%3D%3D" rel="nofollow"><em>https://github.com/moby/buildkit/blob/master/docs/nydus.md</em></a></p><p>[3]Cern 的这篇论文:<a href="https://link.segmentfault.com/?enc=PvzbbohFXUIJOo87aYg2Kg%3D%3D.3HBa9feUEt4EHUPbJgAOal6Vbn3qsFB6x8mcCqfM0HcS2NH1YOAFXdpzWd6AMptb1Ojckr5KISNP6Y3T7Q0T93ySN8f5w%2BLLwH%2FJs7Tfp5k%3D" rel="nofollow"><em>https://indico.cern.ch/event/567550/papers/2627182/files/6153-paper.pdf</em></a></p><p>[4]OCI:<a href="https://link.segmentfault.com/?enc=ikSV9qQyHPXwUeRr5auiyA%3D%3D.LYBXdN%2BtFHDCZlgOlf7jSMf6DBMoZFiseMqX8jLSp2E%2Fd6RO0EBuKAnCXMKkG3UI" rel="nofollow"><em>https://github.com/opencontainers/image-spec/</em></a></p><p>[5]Docker:<a href="https://link.segmentfault.com/?enc=XApwI%2BZQMiDMIx%2FcY6WT%2Fg%3D%3D.nd01La7cdmIngtpdfL6qvu3tAVSTHn0kjzGP%2BpOSkzW3ncjA3KQ7nz7i53MzIA1NSYZgUI6sSrtEYYJai1%2FDLQ%3D%3D" rel="nofollow"><em>https://github.com/moby/moby/blob/master/image/spec/v1.2.md</em></a></p><p>[6]RAFS 镜像格式:<a href="https://link.segmentfault.com/?enc=6twwo%2FHQK1ihoULmXG9l8g%3D%3D.NXWDcrjSwxNiQwuyD9HEO3MOKomTrefJDtn0HZe%2BV%2BlOqiCiHJ2G05fmcvU7oCNEdzQKI3DvP8FZ%2BRZQaf8ayNF2SuE9R41KMYDdRlHvi9M%3D" rel="nofollow"><em>https://d7y.io/zh/blog/2022/06/06/evolution-of-nydus/#rafs-v6-镜像格式</em></a></p><p>[7]Nydus Image Service 项目:<a href="https://link.segmentfault.com/?enc=t6ZCpw%2FozTYzbBMw5hExmw%3D%3D.eFeQOdfUnufJGVIgYMiMfkfS6zJSkPrSUjVmULmtmTzw5Ay3sL96ddgO6yDSnNBh" rel="nofollow"><em>https://github.com/dragonflyoss/image-service</em></a></p><p>[8]FUSE:<a href="https://link.segmentfault.com/?enc=cSFkbm5dzyWIYhSgmIINeQ%3D%3D.6fTnYqWM3VX0yuwaJl1STJXxHjBYY794A3NHVH%2FEWp9QidRf0sFz0Kh9WPX6YsYmaB%2BhKCBhdfLaQzlrrD2b8g%3D%3D" rel="nofollow"><em>https://www.kernel.org/doc/html/latest/filesystems/fuse.html</em></a></p><p>[9]Nydus EROFS fscache user guide:<a href="https://link.segmentfault.com/?enc=R6Pz2%2F%2BNGHhI4%2ByhgZPQEw%3D%3D.7Vc3nGzuBWsxDSQQ7gZCoT2agHfg1Vvva6d7A1jdDZ7OVMWKFNe0lsUnRo47eagdmd6ni9QrjWwHj%2FHUzWr1xbbJzls17E7cGCkKVbfoz%2Bw%3D" rel="nofollow"><em>https://github.com/dragonflyoss/image-service/blob/master/docs/nydus-fscache.md</em></a></p><p>[10]Nydus 镜像加速之内核演进之路:<a href="https://link.segmentfault.com/?enc=rK2c8mtq%2BTDdrS9CPDWX0g%3D%3D.lKUB%2B8v8nRsRgkWn%2BfiQmL3HVMPMRM%2FNpa2zm8DDTpVR%2Bde%2FeXZNbwSu%2BsfNn9fB394qT9uGVBdMDKZiKTDslg%3D%3D" rel="nofollow"><em>https://mp.weixin.qq.com/s/w7lIZxT9Wk6-zJr23oBDzA</em></a></p><p>[11]Alinux2 Optimized-OS:<a href="https://link.segmentfault.com/?enc=SFpPnKSHs6dFmBWsRgDidQ%3D%3D.nLgh9cld%2FxaTs3%2BSQrds7UBjmSJelPWKbQKgEqVnrcPlUxXbtoKJYhfZ9hq9fLtDPkRoi3mvGCRcRpNN1JJL3w%3D%3D" rel="nofollow"><em>https://help.aliyun.com/document_detail/206271.html</em></a></p><p>[12]托管节点池:<a href="https://link.segmentfault.com/?enc=ofNGxzVpmT3ZSPFDpnI%2BVg%3D%3D.9fe3UdH948heZrrHrU8dKpbJOiE7nGWLqV3j0lL13dzYMaGo4Nu4%2BkO3HIhM641HwvwlulOu3M8700FIirzYCg%3D%3D" rel="nofollow"><em>https://help.aliyun.com/document_detail/190616.html</em></a></p><p>[13]使用 ContainerOS 实现节点极速扩容:<a href="https://link.segmentfault.com/?enc=DTnE7XaTkyg6%2F%2F8w6W1w%2Bw%3D%3D.kk6zQ5wNzFgwZJwRbolyq7Wdk0jvoiTEYfQi1XL96oW5KRtTULs50vggh65qaq1PLfSaK7RYetzLNroUeMDhZObuMcdh8z6N%2F7DFSPmgLAdxmDLCM2WCZbtqQsDvGHIxm3kjl%2B1PH%2BXC6jcJLOc9hQ%3D%3D" rel="nofollow"><em>https://www.alibabacloud.com/help/zh/container-service-for-kubernetes/latest/add-containeros-nodes-to-autoscale</em></a></p><p>[14]升级 ACK 集群的 K8s 版本:<a href="https://link.segmentfault.com/?enc=QcK9NKY9AZGVoviJEcENEg%3D%3D.B1fe7jvbaMnwLNLCxJOQuZg%2F7vvK%2BYWvUiJKOnVF0BSZKhcwhTyKH84wQ%2Byy1GPUlyYoqNULxkfgTw%2FZppmYjw%3D%3D" rel="nofollow"><em>https://help.aliyun.com/document_detail/86497.html</em></a></p><p>[15]注意事项:<a href="https://link.segmentfault.com/?enc=SD7X3MajtDCnYVxW%2FfXUYA%3D%3D.BXbqnjO2iQd3pHFaE9r7%2B0CoEfd54S%2FQImtrmwjT1hOKqYlfeRDmE8m7HHK5LBIkOzJWS%2BVPaeUNjFHBMmLVuKFMxTW1LwxfbC0124zqWoc%3D" rel="nofollow"><em>https://help.aliyun.com/document_detail/607514.html#section-832-c5w-8gy</em></a></p><p><strong>了解更多...</strong></p><p><strong>Nydus Star 一下✨:</strong><a href="https://link.segmentfault.com/?enc=XDbWdYjPGGoyfofIbGJ09g%3D%3D.LuB3mxQ8%2BkyCFSH%2F%2B%2BDJ6ANTsMkaiDeBkJESKaArKZAYoOWzEnjHLggJ9IfKurFR" rel="nofollow"><em>https://github.com/dragonflyoss/image-service</em></a></p>
MoE 系列(二)|Golang 扩展从 Envoy 接收配置
https://segmentfault.com/a/1190000043691902
2023-04-18T18:30:27+08:00
2023-04-18T18:30:27+08:00
SOFAStack
https://segmentfault.com/u/sofastack
1
<p><img src="/img/remote/1460000043691904" alt="图片" title="图片"> </p><p>文|朱德江(GitHub ID:doujiang24)</p><p>MOSN 项目核心开发者蚂蚁集团技术专家 </p><p><img src="/img/remote/1460000043691905" alt="图片" title="图片"></p><p><em>专注于云原生网关研发的相关工作</em></p><p><strong><em><em>本文</em></strong></em> <strong>1445 字 阅读 5 分钟</strong></p><p><a href="https://link.segmentfault.com/?enc=sQf0xw9bApBYwaTjtBjkXQ%3D%3D.W6se347ujJRuZPnHwer5mNgB%2BzfpCXdQFEGkdES5rNCk5O8CKuxkIJP%2Bm9g4W2zjEAV1alQSiESfFt0LWicQO4jbJvCMnSP8BEH7kK%2FeaJw%2Fz6qkyQzWoaezOzh394c7P4goHXJEPwX4pug%2B83RTqjiM65hUczXb9ETmVNRa4OR6kWbf9%2Bt4cImP0hPBeofWx1kEGAU8rBwuw4XnP%2FHNrAFSFdxPeLlHFE67PRifCjPnT%2BuItEP%2FKkB4v2bdVPSSY2Ez6%2FaPCHMkRc7EqPhYAQ%3D%3D" rel="nofollow"><em>上一篇</em></a>我们用一个简单的示例,体验了用 Golang 扩展 Envoy 的极速上手。 </p><p><em>这次我们再通过一个示例,来体验 Golang 扩展的一个强大的特性:</em></p><p><strong><em>从 Envoy 接收配置</em></strong> 。</p><h2>Basic Auth</h2><p>我们还是从一个小示例来体验,这次我们实现标准的 Basic Auth 的认证,与上一次示例不同的是,这次认证的用户密码信息,需要从 Envoy 传给 Go,不能在 Go 代码中写死了。</p><p>完整的代码可以看 example-basic-auth[1],下面我们展开介绍一番。</p><h2>获取配置</h2><p>为了更加灵活,在设计上,Envoy 传给 Go 的配置是 Protobuf 的 Any 类型,也就是说,配置内容对于 Envoy 是透明的,我们在 Go 侧注册一个解析器,来完成这个 Any 配置的解析。</p><p>如下示例:</p><pre><code>func init() {
// 注册 parser
http.RegisterHttpFilterConfigParser(&parser{})
}
func (p *parser) Parse(any *anypb.Any) interface{} {
configStruct := &xds.TypedStruct{}
if err := any.UnmarshalTo(configStruct); err != nil {
panic(err)
}
v := configStruct.Value
conf := &config{}
if username, ok := v.AsMap()["username"].(string); ok {
conf.username = username
}
if password, ok := v.AsMap()["password"].(string); ok {
conf.password = password
}
return conf
}</code></pre><p>这里为了方便,Any 中的类型是 Envoy 定义的 TypedStruct 类型,这样我们可以直接使用现成的 Go pb 库。</p><p>值得一提的是,这个配置解析,只有在首次加载的时候需要执行,后续在 Go 使用的是解析后的配置,所以,我们解析到一个 Go map 可以拥有更好的运行时性能。</p><p>同时,由于 Envoy 的配置,也是有层级关系的,比如 http-filter, virtual host, router, virtual clusters 这四级,我们也支持这四个层级同时有配置,在 Go 侧来组织 merge。</p><p>当然,这个只有在 Go 侧有复杂的 filter 组织逻辑的时候用得上,后面我们在 MOSN 的上层封装的时候,可以看到这种用法,这里暂时不做展开介绍。</p><h2>认证</h2><p>具体的 Basic Auth 认证逻辑,我们可以参考 Go 标准 net/http 库中的 Basic Auth 实现。</p><pre><code>func (f *filter) verify(header api.RequestHeaderMap) (bool, string) {
auth, ok := header.Get("authorization")
if !ok {
return false, "no Authorization"
}
username, password, ok := parseBasicAuth(auth)
if !ok {
return false, "invalid Authorization format"
}
if f.config.username == username && f.config.password == password {
return true, ""
}
return false, "invalid username or password"
}</code></pre><p>这里面的 <code>parseBasicAuth</code> 就是从 net/http 库中的实现,是不是很方便呢。</p><h2>配置</h2><p>简单起见,这次我们使用本地文件的配置方式。如下是关键的配置:</p><pre><code>http_filters:
- name: envoy.filters.http.golang
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.golang.v3alpha.Config
library_id: example
library_path: /etc/envoy/libgolang.so
plugin_name: basic-auth
plugin_config:
"@type": type.googleapis.com/xds.type.v3.TypedStruct
value:
username: "foo"
password: "bar"</code></pre><p>这里我们配置了用户名密码:<code>foo:bar</code>。</p><p>预告一下,下一篇我们会体验通过 Istio 来推送配置,体会一番动态更新配置的全流程。</p><h2>测试</h2><p>编译,运行,跟上篇一样,我们还是使用 Envoy 官方提供的镜像即可。</p><p>跑起来之后,我们测试一下:</p><pre><code>$ curl -s -I 'http://localhost:10000/'
HTTP/1.1 401 Unauthorized
# invalid username:password
$ curl -s -I 'http://localhost:10000/' -H 'Authorization: basic invalid'
HTTP/1.1 401 Unauthorized
# valid foo:bar
$ curl -s -I 'http://localhost:10000/' -H 'Authorization: basic Zm9vOmJhcg=='
HTTP/1.1 200 OK</code></pre><p>是不是很简单呢,一个标准的 Basic Auth 扩展就完成了。</p><h2>总结</h2><p>Envoy 是面向云原生的架构设计,提供了配置动态变更的机制,Go 扩展可以从 Envoy 接受配置,也就意味着 Go 扩展也可以很好的利用这套机制。 </p><p>Go 扩展的开发者,不需要关心配置的动态更新,只需要解析配置即可,非常的方便~</p><p>下一篇我们会介绍,配合 Istio 来动态更新用户名密码,体验一番云原生的配置变更体验。</p><p>后续还有更多 Golang 扩展的特性介绍,原理解析,以及,更上层的 MOSN 集成体验,欢迎持续关注。</p><p>[1]example-basic-auth:</p><p><em><a href="https://link.segmentfault.com/?enc=liHOcuAJ4ETbeo99RBuWAA%3D%3D.mclwpzxseosdkBjhUvmTSya%2Brr5Enu%2FiGiYqwalLxfpWZqZ5%2BbRHK6e3YjrcjK4j44ylIzSJJWz392WlVvPqD7AU32SOqnK2oziHAaKPGXbmvpD039uRJFSIjA9F5Hz7" rel="nofollow">https://github.com/doujiang24/envoy-go-filter-example/tree/master/example-basic-auth</a></em></p><p><strong>了解更多…</strong></p><p><strong>MOSN Star 一下✨:</strong> <br><em><a href="https://link.segmentfault.com/?enc=IHb9k6%2BXSLuhDJlvRfVEHw%3D%3D.FrTCz0zEeRHq09qjTGqLpo1ItPNApebWfOP5p%2Fqv%2Fo8%3D" rel="nofollow">https://github.com/mosn/mosn</a></em></p>
Tongsuo/铜锁|「开放原子开源基金会」拜访篇
https://segmentfault.com/a/1190000043630079
2023-04-06T15:44:51+08:00
2023-04-06T15:44:51+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><strong><em>2023 年 3 月,铜锁密码学开源项目的研发和运营团队拜访了位于北京的开放原子开源基金会(以下简称基金会),并就 2023 年开源项目运营计划进行了深入沟通。</em></strong></p><p>双方同意在项目运营层面展开更加积极的交流,并在 2023 年中对基金会主导的重点工作进行全面共建。 </p><p>铜锁项目团队将积极参与正在推进的“<strong>开放原子训练营</strong>”、“<strong>开放原子开源大赛</strong>”等主题活动,努力让铜锁开源社区更好的发展,并为社区用户和开发者们带来更大的价值。</p><p><img src="/img/remote/1460000043630081" alt="" title=""></p><p>本次铜锁项目团队也设计了一个小小的<strong>调查问卷</strong>,希望能收到大家的反馈,让我们可以更好的服务更多的开发者和用户,成为让千家万户都能开箱即用的“铜锁”。</p><p>悄悄告诉正在看文章的读者们,每位认真填写问卷的小伙伴都有机会获得如下铜锁精美周边之一哦!</p><p><img src="/img/remote/1460000043630082" alt="" title=""></p><p>扫描下方二维码 <br>或点击文末<a href="https://link.segmentfault.com/?enc=jl1d0dmIwbezSGYWzt4O3g%3D%3D.AJJW4jPkym9ZUDAxDD6SBWgzCTgbrWnGJj6wildNaVQYoE6KpnPAIeHxu%2F2x3S0A" rel="nofollow">“<strong>铜锁调查问卷</strong>”</a>即可参与问卷填写🧸</p><p><a href="https://link.segmentfault.com/?enc=iZmTWUVHFeZPQqy72XHs%2Bg%3D%3D.lde6WkjIMGXAPcqlJ%2BfBWx4f%2BTf6OKpifzJ4zeTpM67G9SKw2WRoAl2JL3AAn2TD" rel="nofollow"><img src="/img/remote/1460000043630083" alt="" title=""></a></p>
Dragonfly 最新版本 v2.0.9 发布
https://segmentfault.com/a/1190000043580156
2023-03-24T14:19:16+08:00
2023-03-24T14:19:16+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043508668" alt="图片" title="图片"></p><p><img src="/img/remote/1460000043580158" alt="图片" title="图片"></p><p>Dragonfly 最新正式版本 v2.0.9 已经发布!感谢 Dragonfly 的贡献者们,同时也感谢默默支持 Dragonfly 项目的各个公有云团队。欢迎访问 d7y.io[1] 网站来了解详情,下面具体介绍 v2.0.9 版本带来了那些更新。</p><p><img src="/img/remote/1460000043580159" alt="图片" title="图片"></p><p><strong>功能</strong></p><p><strong>1、</strong>下载任务可以根据优先级(Priority)进行下载。优先级可以在下载任务时 Cli 中作为参数传入,也可以在 Manager Console 设置对应应用关联的优先级。具体不同优先级的功能可以参考文档 priority protoc definition[2] 。</p><p><strong>2、</strong>Scheduler 配置新增 PieceDownloadTimeout 字段,做用是当某个 Piece 下载超过 Timeout 时,设置对应的 Task 状态变更为失败。防止异常 Task 元信息残留在 Scheduler 中。</p><p><strong>3、</strong>gRPC 服务新增 Health Service 和 Reflection 服务。</p><p><strong>4、</strong>Manager 支持 Redis 哨兵模式<em>(sentinal model)</em>。</p><p><strong>5、</strong>重构 Dynconfig 模块移除 json.Unmarshal 操作,提高 Dynconfig 模块运行时效率。并且支持通过健康检查的方式过滤异常地址服务。</p><p><strong>6、</strong>修复当前无可用服务时,gRPC 未构建哈希环所造成的异常。</p><p><strong>7、</strong>早期版本当同一个 Task 的多个 Piece 并发下载时,Scheduler 会调度多个 Parent 给当前 Peer 进行下载,但多个 Piece 基本都会从单个 Parent 进行下载。当前版本更改多个 Piece 可以从不同的 Parent 并发下载,可以防止流量集中在部分热点 Parent,提高下载效率的同时也提高了带宽平均利用率。</p><p><strong>8、</strong>Peer 调用 Manager 获取匹配的 Scheduler Cluster 的时候。如果没有匹配到任何 Scheduler Cluster,那么 Manager 会返回所有的 Scheduler Cluster 提供给 Peer。Peer 会使用 Health Check 通过的 Scheduler Cluster 地址供后续下载调度使用。</p><p><strong>9、</strong>支持 ORAS[3] 网客户端的回源下载协议,扩展容器镜像生态支持。</p><p><strong>10、</strong>增加 UDP Ping 包的支持和虚拟网络拓扑的 gRPC Protoc 定义。未来会新增基于网络探测构建虚拟网络拓扑结构,提高调度算法的精确度。</p><p><strong>11、</strong>完成 V2 版本的 P2P 协议[4] 网的定义。Scheduler 和 Manager 对应实现了 V2 版本的 P2P 协议的功能。未来会基于 V2 版本的 P2P 协议和 Rust 语言重写 Dfdaemon,提高客户端性能的同时能够依赖更加标准且扩展性更强的 V2 版本的 P2P 协议。</p><p><strong>12、</strong>OSS 客户端回源协议新增基于 STS 临时访问凭证来访问 OSS 源站。</p><p><strong>13、</strong>Scheduler 新增 Host TTLandhostGCInterval 配置,主要作用于 Host 元数据的释放。可以保证在 Peer 主机异常退出的情况下,仍然可以释放掉异常的 Host 元数据,防止脏数据残留。</p><p><strong>14、</strong>Manager 的 Searcher 模块新增根据 CIDR[5] 条件去筛选当前 Peer 匹配的 Scheduler Cluster,提供更精确的匹配计算方式。</p><p><strong>15、</strong>重构 V1 版本 P2P 协议的 Metric,新增加了 V2 版本 P2P 协议的 Metric,并且根据新的 Metrics 更新 Helm Charts[6] 的 PrometheusRule 对应的告警规则。重新整理 Dragonfly Grafana Dashboards[7] 方便用户可以一键导入 Dashboards,观测 P2P 网络流量以及服务相关数据。</p><p>具体文档可以参考 Monitoring[8] ,Grafana Dashboard 维护在项目 Dragonflyoss/Monitoring[9] 中。版本更新包含的更多细节可以参考 ChangeLog[10] 。</p><p><strong>破坏性变更</strong></p><p>老版本 Manager 由于使用了默认的 JWT Key 生成 JWT Token 会引起安全性问题,具体可以参考 security issues[11] 。所以 Manager 服务配置文件新增了 JWT 配置[12] 。</p><p>当老版本 Manager 升级过程中需要在新版本 Manager 的配置文件中新增 JWT Key 配置,并且 JWT Key 是需要用户自己生成,具体 JWT Key 如何配置可以参考 Setting it in the Manager Configuration[13] 。</p><p><strong>公有云厂商支持</strong></p><p>Alibaba Cloud<em>(阿里云)</em>- 阿里云提供安装 Dragonfly 1.x 在容器镜像仓库 Container Registry[14] 。使用文档可以参考 Use P2P Acceleration in ASK[15] 。推荐使用更加高效且稳定的 Dragonfly 2.0[16] ,部署文档参考 Setup Dragonfly in Kubernetes[17]。</p><p>Google Cloud Platform<em>(GCP)</em>- GCP 提供一键点击部署 Dragonfly 在 Google Kubernetes Engine<em>(GKE)</em>[18] 的 Marketplace[19],具体文档可以参考 Click to Deploy Dragonfly[20]。</p><p>Volcano Engine<em>(火山引擎)</em>- 火山引擎在容器服务(<em>VKE</em><em>)</em> and 和镜像仓库<em>(CR)</em>中集成了 CNCF 孵化项目 Dragonfly。欢迎访问 VKE[21] & CR[22] 了解更多。</p><p>Baiidu AI Cloud<em>(百度智能云)</em>- 百度智能云提供在 Cloud Container Engine<em>(CCE)</em>[24] 一键部署 P2P 加速服务,其能力来自于 Dragonfly。</p><p><strong>|相关链接|</strong></p><p><strong>Dragonfly 社区官网网站</strong>:</p><p>[1] Dragonfly 官网:<em><a href="https://link.segmentfault.com/?enc=LELiF6kn9ZC0VtuKCTkjig%3D%3D.csUy5gzYW1cvLMIihuLoDQ%3D%3D" rel="nofollow">https://d7y.io/</a></em><br>[2] Priority Protoc Definition:<em><a href="https://link.segmentfault.com/?enc=%2Fvsse8uwG%2FVTrVHB1fIw5g%3D%3D.nW4nKBaur2On71U8Z8KY%2Ba8QzxlNAu0D%2B7vF2HYT0xtpeZAzG6WeJ4ZwIcja7wBbhDVS9s%2F5OpAeNyf2VC95fOKwtQTss4NYE%2FNKo0dISm27vo3v6w6SDZAlHct6ZpmU" rel="nofollow">https://github.com/dragonflyoss/api/blob/main/pkg/apis/common/v2/common.proto#L74</a></em><br>[3] ORAS:<em><a href="https://link.segmentfault.com/?enc=RD5PkHwGwMkMsZZAaTrBxg%3D%3D.Wpi3zuj1zxQjioiygFqL7WYkcVbELTk5jYDQDisSUPPzLnu5erNaC2FC3hAh36Nm" rel="nofollow">https://github.com/oras-project/oras</a></em><br>[4] V2 版本的 P2P 协议:<em><a href="https://link.segmentfault.com/?enc=xwyCvqMzpFYTMomxHlMC%2Bg%3D%3D.qg%2BkPz0b3vd98BaKh1quvd%2Ff1rBTA1KCboGhGNw%2B73l9o4qkD0RShvwSlA%2BAyzDaoGvTzmadMiE0VFk42T8JQA%3D%3D" rel="nofollow">https://github.com/dragonflyoss/api/tree/main/proto</a></em><br>[5] CIDR:<em><a href="https://link.segmentfault.com/?enc=sO1PNLTv0RfEWCdV3KYPqw%3D%3D.8aaGdokWjb7oAtjBPl%2FqeYLgRwSZvxh8FH4khMpaAvlC4KJzNXrsRpWJmcBEBkAHHyZsexNUQjXo80oC%2BB1nJg%3D%3D" rel="nofollow">https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing</a></em><br>[6] Helm Charts:<em><a href="https://link.segmentfault.com/?enc=FpmBeOXPZqwiJeg1ItqRqg%3D%3D.54f%2FQ5i8%2FDVmljD%2BFZ3i7iPx44mHCWxRNAqFhgUvGl06Y6VxQM5jdi47GEdUM3mH" rel="nofollow">https://github.com/dragonflyoss/helm-charts</a></em><br>[7] Dragonfly Grafana Dashboards:<em><a href="https://link.segmentfault.com/?enc=biFWLPpKaMWaP%2FRo8L5CMA%3D%3D.bCk6%2BcvZOeF16657lfY%2FsDdoN%2BF3VFpWWzgven6VhhNYCfAmkIiIrY0CnO17I4kyG32EhJxMQe59ltxssK5AaA%3D%3D" rel="nofollow">https://grafana.com/grafana/dashboards/?search=dragonfly</a></em><br>[8] Monitoring:<em><a href="https://link.segmentfault.com/?enc=Y%2BfXSMRtjO4tWx40QpeaUg%3D%3D.leZqNbo%2FD94eJNi3Djf2nzvtUCp3DURYG2YFwZDGamDl7%2FGA6Shz%2FJMYy5jR8234FfaNqhV%2Fg8%2BjChQua7sf9Q%3D%3D" rel="nofollow">https://d7y.io/docs/concepts/observability/monitoring</a></em><br>[9] Dragonfly Monitor:<em><a href="https://link.segmentfault.com/?enc=ZmIpkYfiO3sL%2Bp1Y2SOmWQ%3D%3D.hAttWPq6S2ccfAhYkMWhOrU5e80nDGMocI%2FiP8BkTO5rtPZvPXISZs2r30nPeXA4" rel="nofollow">https://github.com/dragonflyoss/monitoring</a></em><br>[10] ChangeLog:<em><a href="https://link.segmentfault.com/?enc=0TnFj%2BVIPSGk8G1IrGjHkQ%3D%3D.tGN3OGvw%2FRQ0gmY%2F3gsrTlpl6aUKGTj8T6p%2FZDs%2By08yIGmomhqyeMe0jaRom0%2FkZBW0YBSpQP0CYYVhANqozlKkqlUCoSH4uSZELGusDRs%3D" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2/blob/main/CHANGELOG.md</a></em><br>[11] Security issues:<em><a href="https://link.segmentfault.com/?enc=voUTKvJkTLllb344Sxj3qA%3D%3D.ZMJCU%2Be8SuzmKZoXO43%2F9uGZNgAgLyFOcGYm%2B2HL6QLdR957ySWVU4%2BlRt8nJVprl7bYN6vHQShEqUCYFVaenXoBZm79CJ7HOVNfYn8GaD6LFHZcnTCK27oW0PJ99eqg" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2/security/advisories/GHSA-hpc8-7wpm-889w</a></em><br>[12] JWT 配置:<em><a href="https://link.segmentfault.com/?enc=3Xn2jgpltes4%2BIPpYaA9bA%3D%3D.v9znPBasyMWLSV9AeRqyhhUDcTX5Fmd2goCV87QjtVdJoAL0pvxiTb6iUH8QvgQIRIUvg6rxuIcx0yFc%2F%2BAfbQ%3D%3D" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2/pull/2161</a></em><br>[13] Setting it in the Manager configuration:<em><a href="https://link.segmentfault.com/?enc=mXYD%2BRdGpZSlZ1wcgah6tw%3D%3D.A1d9L4ctZ5edCBM7cvuZ27QgpJ9JhjoLuhRRKzBYBBuOlzwjcN3iNiTc9Fb1BXPCnRGyM%2BTyjehsijEC0VBfHRHlEEdvxbfLARclNT%2BFWfLojAogqsr4ZtEUvWVsXayg8xviFgkc6n8JRtI4%2FyCRNA%3D%3D" rel="nofollow">https://github.com/dragonflyoss/d7y.io/blob/main/docs/reference/configuration/manager.md?plain=1#L56</a></em><br>[14] 阿里云镜像仓库:<em><a href="https://link.segmentfault.com/?enc=4otx7mqesx8FVN7t7h8HPA%3D%3D.K76ttjdvY3HBsQVS71uCNLpITL7Y46PhZ9GnW8xwbquPPLdX2dYbwfXskc2JrB9Vs7CAA1p09Ri1CEIRwNdcvA%3D%3D" rel="nofollow">https://www.alibabacloud.com/product/container-registry</a></em><br>[15] Use P2P Acceleration in ASK:<em><a href="https://link.segmentfault.com/?enc=susQGJ%2FcJfhoV%2FjRhJRZQQ%3D%3D.t0Ir3Ti3vJQuBaUYXqqYndUFZarcJLSkhIAiPNXyOhpsJIl0BHVIzcRa%2B9ICgiTQsxvzpSWPIphMwPRpvwTeD%2FLIuEQFHsrRlrYD%2BocBFhRLStRXvqhfGWG01OYAMVh8GGLikug4mKa78xtWlkccu%2Fs93D5TQn12PSTj9PX41AM%3D" rel="nofollow">https://www.alibabacloud.com/help/en/container-registry/lates...</a></em><br>[16] Dragonfly 仓库:<a href="https://link.segmentfault.com/?enc=5mNKYbJksrzO4Qh7MuT2fg%3D%3D.S%2FsHjsSthcdg%2FNFyw51qPQLvPO%2BT%2BBG5UwRLG%2BngvwSNq%2B3jzVK8wjL1xqV2QzZC" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a><br>[17] Setup Dragonfly in Kubernetes:<em><a href="https://link.segmentfault.com/?enc=VU69qU5RLtUYd478kzc%2B3A%3D%3D.YUZ4VgIDyleyLyGAcTpwJLaFRI2lYsaQsmBeH%2F9ecsoYJP2MKIAJPWaU1a%2Bm435e%2BHG%2BlXJ7HMheBUP7YjTY5A%3D%3D" rel="nofollow">https://d7y.io/docs/getting-started/quick-start/kubernetes/</a></em><br>[18] Google Kubernetes Engine(GKE):<em><a href="https://link.segmentfault.com/?enc=s0IwpgrDkbn9DpyLNpnZzA%3D%3D.0DCTVPrIKaYL4x3Xen45gvkH82ekV3%2BYpfedfyQ4VrsP35nPS7g3i%2ByDU4Xk062U" rel="nofollow">https://cloud.google.com/kubernetes-engine</a></em><br>[19] Google Cloud Platform(GCP)Marketplace:<em><a href="https://link.segmentfault.com/?enc=%2Bh8Ac49bHgi5MmzxJlIDOA%3D%3D.Zn8UUkasVI3ecjLbZ%2F%2FD5RXT7EaIYczNTFLhaZknjBZ5O4PmQFPwbpNpdc1XXMrvFHri3tedaTI10qKr9G4BUIA5EgDbHBaWWEeEDN1LtR0%3D" rel="nofollow">https://console.cloud.google.com/marketplace/product/google/d...</a></em><br>[20] Google Cloud Platform(GCP)Dragonfly Marketplace:<em><a href="https://link.segmentfault.com/?enc=fue0k7I4jvbxrlIMpz2GAg%3D%3D.2ZihyS7MZ8DfPN3fOCb3XxTiYMQdMPAh3kiZ8%2FLXOnTIqIinKDzeHKf5DIvU1o6yjzGFDeNeGgvj8PxnLG26r7j4nBIXeUY%2Ft7lFepMBv24%3D" rel="nofollow">https://console.cloud.google.com/marketplace/product/google/d...</a></em><br>[21] 火山引擎容器服务(VKE):<em><a href="https://link.segmentfault.com/?enc=cLpD1xBYciiz387erzA0RA%3D%3D.aaBQ0Q%2Fy%2FcPTXqc3PrZPCY7dhEA3uMbXbjcRNPb1xB5CnwWzumnFa%2FO87D%2F4PIAl" rel="nofollow">https://www.volcengine.com/product/vke</a></em><br>[22] 火山引擎镜像仓库(CR):<em><a href="https://link.segmentfault.com/?enc=sYzbDVwKFqXKusaI0eXTDA%3D%3D.t8LesNHqlHmoz1glVFtoYsdwniiBw838wkkrczXCt167IiTHlhiEbRBB1kg2Oazu" rel="nofollow">https://www.volcengine.com/product/cr</a></em><br>[23] 百度智能云容器服务(CCE):<em><a href="https://link.segmentfault.com/?enc=09arJCAFqoesHqgE1PA1XQ%3D%3D.jL6qQPuqFCJdanIhtIn%2BYp4foY3nE2AZnYZqlhdBtgP9mV8hOx2pAvxvdZ%2FsXDOY" rel="nofollow">https://intl.cloud.baidu.com/product/cce.html</a></em><br>[24]Dragonfly 官网:<em><a href="https://link.segmentfault.com/?enc=nzJpfwyH7NMTGZAisjDfyA%3D%3D.krJ73FKezilWY40W%2FqG6xw%3D%3D" rel="nofollow">https://d7y.io/</a></em><br>[25]Dragonfly Charts 仓库:<em><a href="https://link.segmentfault.com/?enc=4PgqoT60OpTFWmQmXzh0jw%3D%3D.PrBAM8VtXA%2Bybn%2BejmgUPmibWXY2tptcTr8seuJicimMEqxmhFt0L5SpXyKdwXIo" rel="nofollow">https://github.com/dragonflyoss/helm-charts</a>。</em><br>[26]Dragonfly Monitor 仓库:<em><a href="https://link.segmentfault.com/?enc=Se2lkWbe7E9MmvhVQLKZvA%3D%3D.QpsY82z2wR7iFehagPkAFfXjw7hAukA%2Fo4PTfVyI83A6hGOxbrORCwsZHg8Tog8%2B" rel="nofollow">https://github.com/dragonflyoss/monitoring</a>。</em><br>[27]阿里云容器仓库:<em><a href="https://link.segmentfault.com/?enc=%2FFPGqRlVLTfga1pPKfU4mw%3D%3D.fxlN4XifP6jrm1mfsfZxKfQN1LgXEOj5INmTgK1inTZ19WsciKGlfcngGkPlLkyxjL2KEHbWH0iQY%2BrgyC5Kdw%3D%3D" rel="nofollow">https://www.alibabacloud.com/product/container-registry</a>。</em><br>[28]阿里云容器服务<em>(ACK)</em>:<em><a href="https://link.segmentfault.com/?enc=Mhnuf5wZ84gXcW%2BrztOCpg%3D%3D.ghxEpQipFOPXmbhEuSEJGARHXH26HUBZhA6OwfFAl8vZ4xFOhJm3XUZ7zdUAHxEs" rel="nofollow">https://www.alibabacloud.com/product/kubernetes</a>。</em><br>[29]Google Kubernetes Engine<em>(GKE)</em>:<em><a href="https://link.segmentfault.com/?enc=9BD80FVZoPyObsZ5WHKVrg%3D%3D.47xSAauM9yIPU%2BL9%2BujarabWHZsf%2BzsxIh6a%2BA7afODOwNIY0djG%2BtPX0QDspTdk" rel="nofollow">https://cloud.google.com/kubernetes-engine</a>。</em><br>[30]Google Cloud Platform<em>(GCP)</em>Dragonfly Marketplace:<em><a href="https://link.segmentfault.com/?enc=mPmrVptCHGtU9HlvHfdFhg%3D%3D.1pZp2%2FjYIVDqFlEnPXuelGmx2Fwwv%2BFVEBlsy2yzh63RIyeOn7m8LhQrW%2FCqjk8xDQnqunAsBtcOVm%2BTmw%2FoRZJc8Ue3mGhXFdMSvKOmHoE%3D" rel="nofollow">https://console.cloud.google.com/marketplace/product/google/d...</a></em><br>[31]火山引擎容器服务<em>(VKE)</em>:<em><a href="https://link.segmentfault.com/?enc=Rer15NW9RvxvyXaxyG76KQ%3D%3D.ruCvI9%2FIxj51MQoNTBCo5RZEsi0XGNVI%2BLAroPSuZvNyjkfm8VuZY3LGbpWCRBuF" rel="nofollow">https://www.volcengine.com/product/vke</a></em><br>[32]火山引擎容器仓库<em>(CR)</em>:<em><a href="https://link.segmentfault.com/?enc=QTi9%2B4J3V3aNHDGAApd23w%3D%3D.1KTJXmsojytIQENrzLJO5GadQm1DgeLJN0hv7iq1T9Tuw4YPsLyzWAWhFNaAADNX" rel="nofollow">https://www.volcengine.com/product/cr</a></em><br>[33]百度智能云容器服务<em>(CCE)</em>:<em><a href="https://link.segmentfault.com/?enc=0NAJDDhwBHeSmTsViuZgDQ%3D%3D.xRiFbt8cMOaYzk0PdDG9vDUzhhklVOGz3GfMA6nrQdnuFUlyK5v5DrnU27bTjp6o" rel="nofollow">https://intl.cloud.baidu.com/product/cce.html</a></em></p><p><strong>Dragonfly Star 一下✨:</strong><br><strong><em><a href="https://link.segmentfault.com/?enc=YrdZJEB4Ylo8N0gULpH7Mw%3D%3D.jU%2BXTdOygTLSFaST2p0mADnN%2FK1vw%2FztV%2FIUCNbIaEf%2BCYU9jzbURXMKh9XXAkk%2F" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a></em></strong></p><p><strong>本周推荐阅读</strong> </p><p><img src="/img/remote/1460000043580160" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=FW7xhWmzLk35i1WPVnrYvg%3D%3D.yLLg8GTDFfEUcgfq3T%2B37GRCFsVZ8hGC%2FdFgNXXfvuIeUBl%2BobrQxgLSki2ZtMxllWkZqNDEdD2NHX4YFazOcYA1aqWvtrc4lm1XkuqDLDpdI%2F7gW%2BnebsItTb7jyEAWYAqfBBhrecYt6a3j757gLbxMJ1TUE5y3MLlCCymKPD35aN8%2FSFrTqczljkmQl4ZB%2FcFUY2K0NFbueirNAJcNhK%2FRZhIOYl%2F173p0JfF4Dity9QwH6Z4Jv2djrf9Go1Ha2Om8ZxojRD0GOxPU716KbAW2yKbQ7pJr1wza9XVajT6yiQz3eafzzQxaTr0NS1%2Fu" rel="nofollow">Dragonfly 基于 P2P 的文件和镜像分发系统</a></em></p><p><img src="/img/remote/1460000043580161" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=zkrP0Fs%2FwGcf7OuuSi%2Fisg%3D%3D.hG%2F0MxaMM2iZdsmjzwq7Z5b4z1nVz33Wj5Rgj6j8TASG12d4LhkYOe5SbIFEH7UTQSfsNuREsbKs06XIIaMosGzaBV%2F0nsnQhsFssmw45a1c9DrAJdVfMwz%2F6oh42a4JtmolzMwD3gOj%2F161b4IPSFQx01JzWGwNytDiBwoNbqe5qjNOa7NqUxgCy2QuGI89q0ftZOiy%2Fbdw8hucK8y%2BJcngKKozorQXeiBCuVL1U3PVNnrey7lMGEmCPL%2Bts6%2BG8S9djpxq9luB4wZTJ4kh2FQA%2BfVdKG6WSkLj8%2Fjf4plhS6NGoX1zvb%2FR%2FhEZj8PA" rel="nofollow">Dragonfly 中 P2P 传输协议优化</a></em></p><p><img src="/img/remote/1460000043580162" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=4FuBX0K523zZtNsvn0waBg%3D%3D.XgcigFvffp19m0rr6azVXYaa608yQ12KLSKRDBIh6N%2FKHk9TGVjU4%2FRAGvaysj64nhf6NpEwg0x%2BEfEOTxvhYDL7GyT8cGJdR7VMNMw3gnIOaeHfL0R9f6EUKxzgX23MUVhcuO9PrHaVy7Uhfx8HFvre7xbSOOlp%2B3RTJvxEymiCsNnr1X6o0ZEX1rBR91sThhTAntsOmklFFutw38t6W3XBnaeIjxfjClBao8oGFh1jbc96j9hV4MvtRMHL3J6CUL0C7TsljgXhfu8x4K6Vjx7Vp80lLvwMjaBLoTnBbxCsVcsYwRYhWiRr4wsu4EeZ" rel="nofollow">Occlum Meetup - 一起来聊机密计算 TEE</a></em></p><p><img src="/img/remote/1460000043578950" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=GsCyP5%2FvAPdAoZl0fxMclA%3D%3D.hOON6WfClVctiJ0szjdxqtBb%2F1NUrnn2EHpOgwg7N195UWYxVCfjC0KDHUeCXl8wrmBhJ3wsiiCIWDwlxtah6XMXgR9rDEByCsTwX8u7JLodbyDkqjWuvJnK9oz%2FGEf2aRrBoux0j8P0Zn16SG40vAvHaDFRRgFe2uC82HlHT5zheuB2wXvdVtG4QNt9O%2BKzKlXV3iVMayfqsA0QD0YKfz%2FQNyzhjXzp5VKGja%2FB7RAPGJW1nyuYaONAVE9TxNZKeu235Q0u0%2B2H%2FcC%2FrroA5DGjVyDUEmoiWuM5eQCXPrP4Bvpm2cHJ38KeR16%2FVDli" rel="nofollow">展台招募|“SOFA 五周年”开源集市等你来!</a></em></p>
火山引擎基于 Dragonfly 加速实践
https://segmentfault.com/a/1190000043578937
2023-03-24T10:38:10+08:00
2023-03-24T10:38:10+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043508668" alt="图片" title="图片"></p><p><strong>PART. 0</strong></p><p><strong>背景</strong></p><p>火山引擎镜像仓库 CR 使用 TOS 来存储容器镜像。目前在一定程度上能满足并发大规模的镜像拉取。然而最终拉取的并发量受限于 TOS 的带宽和 QPS。</p><p>这里简单介绍一下目前针对于大规模拉镜像遇到的两个场景的问题:</p><p><strong>1、</strong>客户端数量越来越多,镜像越来越大,TOS 带宽最终无法满足需求。<br><strong>2、</strong>如果客户端使用了 Nydus 对镜像格式做转换之后,对 TOS 的请求量会有数量级的增加,TOS API 的 QPS 限制导致无法满足需求。</p><p>不论是镜像仓库服务本身还是背后的存储,最终肯定是有带宽和 QPS 限制的。如果单纯依赖服务端提供的带宽和 QPS,很容易就无法满足需求。因此需要引入 P2P ,减轻服务端压力,进而满足大规模并发拉取镜像的需求。</p><p><strong>PART. 1</strong></p><p><strong>基于 P2P 技术镜像分发系统调研</strong></p><p>目前开源社区有几个 P2P 项目,这里对这些项目进行简单介绍。</p><p><strong>Dragonfly</strong></p><p><strong>架构图</strong></p><p><img src="/img/remote/1460000043578939" alt="图片" title="图片"></p><p><strong>术语</strong></p><p><strong>Manager</strong></p><p><strong>1、</strong>存储动态配置供 Seed Peer 集群、Scheduler 集群以及 Dfdaemon 消费。</p><p><strong>2、</strong>维护 Seed Peer 集群和 Scheduler 集群之间关联关系。</p><p><strong>3、</strong>提供统一异步任务管理,用作预热等功能。</p><p><strong>4、</strong>监听各模块是否健康运行。</p><p><strong>5、</strong>为 Dfdaemon 筛选最优 Scheduler 集群调度使用。</p><p><strong>6、</strong>提供可视化控制台,方便用户操作管理 P2P 集群。</p><p><strong>Scheduler</strong></p><p><strong>1、</strong>基于机器学习的多场景自适应智能 P2P 节点调度, 为当前下载节点选择最优父节点。</p><p><strong>2、</strong>构建 P2P 下载网络的有向无环图。</p><p><strong>3、</strong>根据不同特征值评估节点下载能力, 剔除异常节点。</p><p><strong>4、</strong>当下载失败情况,主动通知 Dfdaemon 进行回源下载。</p><p><strong>Dfdaemon</strong></p><p><strong>1、</strong>基于 gRPC 提供下载功能, 并提供多源适配能力。</p><p><strong>2、</strong>开启 Seed Peer 模式可以作为 P2P 集群中回源下载节点, 也就是整个集群中下载的根节点。</p><p><strong>3、</strong>为镜像仓库或者其他 HTTP 下载任务提供代理服务。</p><p><strong>4、</strong>下载任务基于 HTTP 或 HTTPS 或其他自定义协议。</p><p><strong>Kraken</strong></p><p><strong>架构图</strong></p><p><img src="/img/remote/1460000043578940" alt="图片" title="图片"></p><p><strong>术语</strong></p><p><strong>Agent</strong></p><p><strong>1、</strong>是 P2P 网络中的对等节点,需要在每个节点上部署。</p><p><strong>2、</strong>实现了 Docker Registry interface。</p><p><strong>3、</strong>通知 tracker 自己拥有的数据。</p><p><strong>4、</strong>下载其他 agent 的数据(tracker 会告诉该 agent 需要下载这块数据需要到哪个 agent 上下载)</p><p><strong>Origin</strong></p><p><strong>1、</strong>负责从存储中读取数据做种。</p><p><strong>2、</strong>支持不同的存储。</p><p><strong>3、</strong>通过 Hash 环的形式保证高可用。</p><p><strong>Tracker</strong></p><p><strong>1、</strong>P2P 网络中的协调者,追踪谁是 Peer,谁是 Seeder。</p><p><strong>2、</strong>追踪 Peer 拥有的数据。</p><p><strong>3、</strong>提供有序的 Peer 节点供 Peer 下载数据。</p><p><strong>4、</strong>通过 Hash 环的形式保证高可用。</p><p><strong>Proxy</strong></p><p><strong>1、</strong>实现了 Docker Registry Interface。</p><p><strong>2、</strong>将镜像层传给 Origin 组件。</p><p><strong>3、</strong>将 Tag 传给 BUILD INDEX 组件。</p><p><strong>Build-Index</strong></p><p><strong>1、</strong>Tag 和 digest 映射,agent 下载对应 Tag 数据时向 Build-Index 获取对应的 Digest 值。</p><p><strong>2、</strong>集群之间镜像复制。</p><p><strong>3、</strong>保存 Tag 数据在存储中。</p><p><strong>4、</strong>通过 Hash 环的形式保证高可用。</p><p><strong>Dragonfly vs Kraken</strong></p><p><img src="/img/remote/1460000043578940" alt="图片" title="图片"></p><p><strong>PART. 2</strong></p><p><strong>方案</strong></p><p>在火山引擎上,主要考虑 VKE 和 VCI 通过 CR 拉取镜像。</p><p><strong>1、</strong>VKE 的产品特点是基于 ECS 部署的 K8S,因此十分适合每个节点部署 Dfdaemon,充分利用每个节点的带宽,进而充分利用 P2P 的能力。</p><p><strong>2、</strong>VCI 的产品特点是底层有一些资源很充足虚拟节点。上层的服务是以 POD 为载体,因此无法像 VKE 那样每个节点部署 Dfdaemon,所以部署的形式部署几个 Dfdaemon 作为缓存,利用缓存的能力。</p><p><strong>3、</strong>VKE 或 VCI 客户端拉取经过 Nydus 格式转化过的镜像。在该场景下,需要使用 Dfdaemon 作为缓存,不宜使用过多的节点,避免对 Scheduler 造成过大的调度压力。</p><p>基于火山引擎对于以上产品的需求,以及结合 Dragonfly 的特点,需要设计一套兼容诸多因素的部署方案。部署 Dagonfly 的方案设计如下。</p><p><strong>PART. 3</strong></p><p><strong>整体架构图</strong></p><p><img src="/img/remote/1460000043578941" alt="图片" title="图片"></p><p><strong>1、</strong>火山引擎上的资源都是归属于主账号下。P2P 控制组件以主账号级别隔离,每个主账号下一套 P2P 控制组件。服务端实现 P2P Manager Controller,通过该 Controller 来管控控制面所有 P2P 控制组件。</p><p><strong>2、</strong>P2P 控制组件部署在镜像仓库数据面 VPC,通过 LB 与用户集群打通。</p><p><strong>3、</strong>在 VKE 集群上,Dfdaemon 以 DaemonSet 方式部署,每个节点上部署一个 Dfdaemon。</p><p><strong>4、</strong>在 VCI 上,Dfdaemon 以 Deployment 方式部署。</p><p><strong>5、</strong>ECS 上 Containerd 通过 127.0.0.1:65001 访问本节点上的 Dfdaemon。</p><p><strong>6、</strong>通过在用户集群部署一个 controller 组件,基于 PrivateZone 功能,在用户集群生成.p2p.volces.com 域名, controller 会根据一定的规则挑选特定节点<em>(包括 VKE、VCI)</em>的 Dfdaemon pod,以 A 记录的形式解析到上述域名。</p><ul><li>ECS 上 Nydusd 通过.p2p.volces.com 域名访问 Dfdaemon。</li><li>VCI 上镜像服务客户端和 Nydusd 通过.p2p.volces.com 域名访问 Dfdaemon。</li></ul><p><strong>PART. 4</strong></p><p><strong>压测数据</strong></p><p><strong>环境</strong></p><p>镜像仓库:带宽 10Gbit/s。</p><p>ECS: 4C8G,挂载本地盘,带宽 6Gbit/s。</p><p><strong>镜像</strong></p><p>Nginx (500M)</p><p>TensorFlow (3G)</p><p><strong>组件版本</strong></p><p>Dragonfly v2.0.8。</p><p><strong>Quota</strong></p><p>Dfdaemon: Limit 2C6G。</p><p>Scheduler: 2 Replicas,Request 1C2G,Limit 4C8G。</p><p>Manager: 2 Replicas,Request 1C2G,Limit 4C8G。</p><p><strong>POD 启动时间对比</strong></p><p>Nginx Pod 分别并发 50、100、200、500 的所有 Pod 从创建到启动消耗时间。</p><p><img src="/img/remote/1460000043578942" alt="图片" title="图片"></p><p>TensorFlow Pod 分别并发 50、100、200、500 的所有 Pod 从创建到启动消耗时间。</p><p><img src="/img/remote/1460000043578943" alt="图片" title="图片"></p><p>在大规模拉镜像的场景下,在使用 Dragonfly 和 Dragonfly & Nydus 场景对比 OCIv1 场景能够节省 90% 以上的容器启动时间。使用 Nydus 之后启动时间更短是因为镜像 lazyload 的特性,只需要拉取很小的一部分元数据 Pod 就能启动。</p><p><strong>存储源端带宽峰值对比</strong></p><p>Nginx Pod 分别并发 50、100、200、500 的存储端峰值流量。</p><p><img src="/img/remote/1460000043578944" alt="图片" title="图片"></p><p>TensorFlow Pod 分别并发 50、100、200、500 的存储端峰值流量。</p><p><img src="/img/remote/1460000043578945" alt="图片" title="图片"></p><p><strong>回源流量对比</strong></p><p>Nginx Pod 分别并发 50、100、200、500 的回源流量。</p><p><img src="/img/remote/1460000043578946" alt="图片" title="图片"></p><p>TensorFlow Pod 分别并发 50、100、200、500 的回源流量。</p><p><img src="/img/remote/1460000043578947" alt="图片" title="图片"></p><p>在大规模场景下,使用 Dragonfly 回源拉取镜像的数量很少。OCIv1 的场景所有的镜像拉取都要回源,因此使用 Dragonfly 回源峰值和回源流量相比 OCIv1 的场景少很多。并且使用 Dragonfly 后随着并发数提高,回源峰值和流量不会显著提高。</p><p><strong>PART. 5</strong></p><p><strong>总结</strong></p><p>基于项目整体成熟度,社区活跃度、用户数量、架构复杂度,是否针对 Nydus 优化。未来发展趋势等因素综合考虑,Dragonfly 是 P2P 项目中最优的选型。</p><p><strong>PART. 6</strong></p><p><strong>术语及定义</strong></p><p><strong>OCI</strong><br>Open Container Initiative,开放容器计划是一个 Linux 基金会项目,由Docker在2015年6月启动,旨在为操作系统级虚拟化(<em>最重要的是 Linux 容器)</em>设计开放标准。</p><p><strong>OCI Artifac</strong><strong>t</strong><br>遵循 OCI image spec 的制品。</p><p><strong>镜像</strong><br>本文中的镜像指 OCI Artifact,因此也包括 Helm Chart 等其他 OCI Artifact。</p><p><strong>镜像仓库</strong><br>遵循 OCI distribution spec 实现的制品仓库。</p><p><strong>ECS</strong><br>是一种由CPU、内存、云盘组成的资源集合,每一种资源都会逻辑对应到数据中心的计算硬件实体。</p><p><strong>CR</strong><br>火山引擎镜像仓库服务。</p><p><strong>VKE</strong><br>火山引擎通过深度融合新一代云原生技术,提供以容器为核心的高性能 Kubernetes 容器集群管理服务,助力用户快速构建容器化应用。</p><p><strong>VCI</strong><br>火山一种 Serverless 和容器化的计算服务。当前 VCI 可无缝集成容器服务 VKE,提供 Kubernetes 编排能力。</p><p>使用 VCI,可以专注于构建应用本身,而无需购买和管理底层云服务器等基础设施,并仅为容器实际运行消耗的资源付费。VCI 还支持秒级启动、高并发创建、沙箱容器安全隔离等能力。</p><p><strong>TOS</strong><br>火山引擎提供的海量、安全、低成本、易用、高可靠、高可用的分布式云存储服务。</p><p><strong>Private Zone</strong><br>基于专有网络VPC<em>(Virtual Private Cloud)</em>环境的私有DNS服务。该服务允许在自定义的一个或多个VPC中将私有域名映射到IP地址。</p><p><strong>P2P</strong></p><p>点对点技术,当 P2P 网络中某一个 peer 从 server 下载数据的时候,下载完数据后也能当作服务端供其他 peer 下载。当大量节点同时下载的时候,能保证后续下载的数据,可以不用从 server 端下载。从而减轻 server 端的压力。</p><p><strong>Dragonfly</strong><br>Dragonfly 是⼀款基于 P2P 技术的文件分发和镜像加速系统,并且是云原生架构中镜像加速领域的标准解决方案以及最佳实践。现在为云原生计算机基金会<em>(CNCF)</em>托管作为孵化级项目。</p><p><strong>Nydus</strong><br>Nydus 简介: Nydus 镜像加速框架是 Dragonfly 的子项目,它提供了容器镜像按需加载的能力,在生产环境支撑了每日百万级别的加速镜像容器创建,在启动性能,镜像空间优化,端到端数据一致性,内核态支持等方面相比 OCIv1 有巨大优势。</p><p><strong><em>*|社区相关网址|*</em></strong></p><p><strong>Dragonfly 社区官网网站</strong>:</p><p>Volcano Engine: <em><a href="https://link.segmentfault.com/?enc=s2lED22k8injOgZdNE0agw%3D%3D.jdOXtN%2BsdLtiJ17s4Hwp57Y51s7QVlh8qJgDia88wp8%3D" rel="nofollow">https://www.volcengine.com/</a></em></p><p>Volcano Engine VKE: <em><a href="https://link.segmentfault.com/?enc=%2FTJS27h5EkPd4Dx40PCUDQ%3D%3D.F8NuFLYwTkFrea8ie8p%2FrWo6mH4%2B%2F%2FgvMeCq6S4cAxBMVVvRRqzJWVtrUJFsJ77f" rel="nofollow">https://www.volcengine.com/product/vke</a></em></p><p>Volcano Engine CR: <em><a href="https://link.segmentfault.com/?enc=9wp6D9V2cupVIUYu0MctHw%3D%3D.DkA2AlS4ZNipgLVDhgJBx2p8dgu%2Fz%2BHCOBnXEwp0yGWj0rjLUzFz57Jx5CWtj%2FVq" rel="nofollow">https://www.volcengine.com/product/cr</a></em></p><p>Dragonfly 官网: <em><a href="https://link.segmentfault.com/?enc=5VPZM0WebA1F%2B%2Fy4Ws%2FEGQ%3D%3D.ljFp7csA063EsGcp2ty6Gg%3D%3D" rel="nofollow">https://d7y.io/</a></em></p><p>Dragonfly Github Repo: <em><a href="https://link.segmentfault.com/?enc=19KnZOMl%2Bwr9IzOwqQ7M%2Bw%3D%3D.RihNFeO7xZD9P1EBuqO9P%2B0ZgqtrThEuqJ2ahiJZGsbxIIlX4nrjf%2F33pPZdBvZj" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a></em></p><p>Nydus 官网: <em><a href="https://link.segmentfault.com/?enc=Uwe296nSgdM8IRmi5hkEeQ%3D%3D.CRHXZlweY8lDa2tG0KpIQswPlOD664OCViS89V84piY%3D" rel="nofollow">https://nydus.dev/</a></em></p><p>Nydus Gihtub Repo: <em><a href="https://link.segmentfault.com/?enc=m8kpnTygs4Y7rB3PM%2FjR%2FQ%3D%3D.XD7fAPnvUGWNCkJH9iq0kuuVVxtJ9Iqk3wu5x9c4Js%2BlX%2BBPe9CsYfb0v0qtIhpN" rel="nofollow">https://github.com/dragonflyoss/image-service</a></em></p><p><strong>Dragonfly Star 一下✨:</strong><br><strong><em><a href="https://link.segmentfault.com/?enc=u8aBYXpV6LdK04rp1xL9Lg%3D%3D.tDbk3CYyNepXwgHQKo%2FMsgKApApSzfCGD3SrxBO0o0MS2K41a%2BGBmkrJ1OOA3uRq" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a></em></strong></p><p><img src="/img/remote/1460000043578948" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=PFmFURf8noCN0Kgr3ixzHw%3D%3D.OHPnK5fz71wYWkGY6pJZ6yJp5b%2FaecDLS7JaMdOiM6HBdV%2BNIDpFPYuVwzq7Pj6JlkLP3klsiEgKkf1KRZdJeJRkKtHq1VzlOZfgMWaLB9%2B7cyzpGKMZiOeCdC%2BDlcsWKSnqj1qGDSrm7ZQPYuA1Gj%2FWf6N52UEvKXqo7cTixCwcmoJK8Fkwktd00xomkVPrcrmcHSy%2BR9vqI%2B2Mia5A4Amu7nKqV1kifFPi63HVq7nTRagLNXgUIRx28c7ipXSlSt7K%2BRuxhiXeUwKsaj%2BOkmQI%2BdOVT8po5Eo7WMYv5gM%3D" rel="nofollow">火山引擎基于 Dragonfly 加速实践</a></em></p><p><img src="/img/remote/1460000043578949" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=JCIaVWsK0g36VJbCe0i52g%3D%3D.gu2jkP%2BtLdXTLtQvDELXz1Rty0oV767zFH6RwuGWeQmgtlnmk7d4W7yYc7P93EnQvNYL9CGmasbQZmzl7DUy2DprKqw0adS9qurZAYV5w6KghzZMLfTBCVDIWn5FSX49F%2BlcfEjegcrtJkW3ar9RwNjP8ZIyTeseQJg%2FKKXtMJU%2BxAhG92UzSh77QI63AM6HokLmcHgnSEHVitGWVc1X30K7ipcZZ85jjfu1DPD0sDxogltqdsf%2FhQFNJtM06rmvriKFugeDDiewtseFyt1s%2BoGdbzaeR03jyIWuTjMEnWoCgIq8992sD0AFBvOlFAc3" rel="nofollow">Dragonfly 基于 P2P 的文件和镜像分发系统</a></em></p><p><img src="/img/remote/1460000043578950" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=87mjwvJqiVt6jx6N7tgo7A%3D%3D.pr2kZaJTuj7Hpc6D%2BLqRG0u%2BYUeOzpzS1KeI5UE7LTodLFZ3sqd8ZqdOTOmS0ugyoraiRrw0oWwP3i66TJxMeDBlIRJ0aGwsG1Tw5nJDWtIcAhER%2FM9vpCxYfHnEq7zbKv%2B%2Fvb82Xc5QSVI2I%2FZBV0CnoX9EEro6TPBPS%2BRZRDG%2FRmSqyWaezC%2BGCh%2FXx9HGMBtIY1oliO0dduXMLhIuabALBnBj1hI4mSj5QQ1z5MzkYU%2Fg1jvektKkLH6cJdpA%2FU6AofpvEtf4SdMpk2YMXz3Uez4eeVJt%2FZEYbIPhc6AWwBLsGrjXfFy5Utde4crw" rel="nofollow">展台招募|“SOFA 五周年”开源集市等你来!</a></em></p><p><img src="/img/remote/1460000043578951" alt="图片" title="图片"></p><p><em><a href="https://link.segmentfault.com/?enc=E1e7X1pTcz%2FlrQYr2hMAeA%3D%3D.tho2U%2B34k6AQ4yNVirAbudjh4TUN4qRKpoRwn7PZ2xJVJ1ObSO7QxvDOSEX6RsMkkaDG57Rd11SNgIHoQMrncpfvYjJRB3gwlrV6YFsotGuq8oPGDBQlmO4CVmwv%2FatMwfzWtY4DwgAwjw9h4Knu%2FTUoBborFQKpQZDQ28HlzIQGFPWSipSYW4ro%2FStXC%2BqWs9rCBPjV1qKSUwTVHOrA4SGmvNbI6gU897gPLmhKb%2B%2BauabexC47CSqIvpRRhPus%2FDQsJAZCfoFrSarH9X%2BXv7qWfcovXSEmOSOeHR55PTyFqQOxP0VI5%2Fvnh%2BzrYkrqqSLs2Qd8Z96jO5AXv1aY0Q%3D%3D" rel="nofollow">Dragonfly 和 Nydus Mirror 模式集成实践</a></em></p>
Seata-go 1.1.0 发布,补齐 AT 模式支持
https://segmentfault.com/a/1190000043537117
2023-03-14T16:00:51+08:00
2023-03-14T16:00:51+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043508668" alt="图片" title="图片"></p><p><strong>发布概览</strong></p><p>Seata-go 1.1.0 版本补齐了 AT 模式下对 Multi Delete、Multi Update、Insert on Update 和 Select for Update 的支持。至此 Seata-go 的 AT 模式与 Seata AT 模式全面对齐。</p><p>此版本给出了在 Dubbo-go/Gin/gRPC 中使用 Seata-go TCC/AT 两种模式的示例。</p><p>示例链接:<em><a href="https://link.segmentfault.com/?enc=6ta65THAtldTiXn6AL7jIQ%3D%3D.TMPI%2Bse8kF0o2O7%2BarC0jWbvkECj9wFW%2Fn5b%2FEVh%2F%2BhWrbFwK88X9PWAi6T%2BrP7QN1Bk41GnbtMgqRc5bqMiiQ%3D%3D" rel="nofollow">https://github.com/seata/seata-go-samples/tree/main/at</a></em></p><p><strong>AT 模式:</strong></p><p>AT 模式支持并集成了 Multi Delete SQL 语法<br>AT 模式支持并集成了 Multi Update SQL 语法<br>AT 模式支持并集成了 Insert on Update SQL 语法<br>AT 模式支持并集成了 Select for Update SQL 语法</p><p><strong>配置文件:</strong></p><p>完善了更多地方读取配置文件功能</p><p><strong>版本的主要更新如下</strong></p><p><strong>Feature:</strong></p><p>[#491] 支持查询全局事务锁<br><a href="https://link.segmentfault.com/?enc=%2F2%2Bb5HUFCmggSZwEJsHd0A%3D%3D.kPZaLgRV%2BSROQE%2FdQUekkrssKxTn0CBlAk9NRdIIw8Lp9rGqOlPfAdadm3AQMmsN" rel="nofollow">https://github.com/seata/seata-go/pull/491</a></p><p>[#482] 支持 AT 模式 Multi Delete SQL 执行器<br><a href="https://link.segmentfault.com/?enc=CLC5N4x%2F74QYg7T6N6ABeA%3D%3D.xsu3vttMP3ywFVkuqaaQrjWy4T0c36iez98X0%2B82UqD%2Bf0yeTUM4O6Hb61qosLP0" rel="nofollow">https://github.com/seata/seata-go/pull/482</a></p><p>[#481] 支持 AT 模式 Multi Update SQL 执行器<br><a href="https://link.segmentfault.com/?enc=K3Y3%2BJQ5daMb7KtC%2BgInYQ%3D%3D.7tUbInpj%2BgZFJ87QSzdjD09C21FOUac%2FBg%2FabXNCDvApe90Jwkae6eV2q3iNfbZv" rel="nofollow">https://github.com/seata/seata-go/pull/481</a></p><p>[#478] 支持 AT 模式 Select for Update SQL 执行器<br><a href="https://link.segmentfault.com/?enc=xgUebT2enBBDZhiSqF0OEw%3D%3D.5moXPNg08N73t8qBCq%2FLE71grk3zMAPAx8ENu6nq84gCXUrHddYjfjmIyIX8J1Kt" rel="nofollow">https://github.com/seata/seata-go/pull/478</a></p><p>[#477] 支持 Undo Log 的 Json 序列化方式<br><a href="https://link.segmentfault.com/?enc=J%2FCqohex2ePA6BRz98D%2FsQ%3D%3D.tAjavzd6ve87a%2BhgaiCwii6mfijTGZbqZh0OAitJT%2FngqQpt3QiqM%2Fs1YC8m6Wg7" rel="nofollow">https://github.com/seata/seata-go/pull/477</a></p><p>[#456] 支持 AT 模式 Insert on Update SQL 执行器<br><a href="https://link.segmentfault.com/?enc=qub2mwICufS83vx2iX%2FnjQ%3D%3D.hvyjdQVF2SpKhBt8vAAweF7muyDXUOO%2BdjfxpyPYCj%2FUJZTipsqUAvIWkki3vlWN" rel="nofollow">https://github.com/seata/seata-go/pull/456</a></p><p>[#444] 支持 BZip 压缩算法<br><a href="https://link.segmentfault.com/?enc=1EQjcFD47%2BT%2BjkLIqtPcoQ%3D%3D.RhsWqOoI6vo17Fyu8cwbXhFNhEAjjJUsY%2B28Y%2B9mIL2vxn4fQ5DkwokVPCwS43YL" rel="nofollow">https://github.com/seata/seata-go/pull/444</a></p><p>[#436] 支持读取 RM 相关的配置文件<br><a href="https://link.segmentfault.com/?enc=ZpVccE5dPmIiH1FQMBh8ww%3D%3D.ucdX2EW4tsuNIWZBmMnKGVcWI%2BM0ShA42FpsiLpbUpmeOa13HcCux1VqcqFJvYPO" rel="nofollow">https://github.com/seata/seata-go/pull/436</a></p><p>[#433] 支持 XA 连接管理器<br><a href="https://link.segmentfault.com/?enc=dQqCbMmRpheTswVF1KBI5w%3D%3D.PfBqHrxUdPZzpTnjRxZVZY0y1%2BN%2BSMrMYkcdjpaAvI0KlHXSMUM4Irh01hdmzBPA" rel="nofollow">https://github.com/seata/seata-go/pull/433</a></p><p>[#430] 支持读取 Getty 相关的配置文件<br><a href="https://link.segmentfault.com/?enc=Vd0gHVnYnU9IP6Eg53r76A%3D%3D.Xf5hqyfIOdJYEXr1%2F5d3FUAKDAB2znSyEgpD5L3rq17QSgWDKFO%2BOUe%2FX9Ubc%2BFn" rel="nofollow">https://github.com/seata/seata-go/pull/430</a></p><p><strong>Bugfix:</strong></p><p>[#509] 修复 AT 模式下执行 Insert on Update 时 Undo Log 的 SQLType 字段的问题<br><a href="https://link.segmentfault.com/?enc=8%2Bk3UCvNM2GjtiHOJCcLuA%3D%3D.VuOl5sfAzGqQ%2BRVVKWONkq5AVqk1CEDLG22%2FWivvjBFCEWKao%2BpcNsQ9ZJdEqrLG" rel="nofollow">https://github.com/seata/seata-go/pull/509</a></p><p>[#495] 修复 Undo Log 的 SQLType 字段的问题<br><a href="https://link.segmentfault.com/?enc=rja29yK6jBT3UEhexGyD9g%3D%3D.4edKKNJ3Ibc06FK4kRFZEQkeDB5TO9kZY34neMQvOepD8sTSTmWfbIvZxl6wH%2Bw1" rel="nofollow">https://github.com/seata/seata-go/pull/495</a></p><p>[#487] 修复 AT 执行时出现的问题<br><a href="https://link.segmentfault.com/?enc=HZEOEvUaLEY1cI4ukuo2gA%3D%3D.jW%2F5Uk8OkvPco%2Bb6yQ32lcrBrVKiIpXZhNw5hJgOBLm3nEU%2BEOQkuDMVgcVNPolB" rel="nofollow">https://github.com/seata/seata-go/pull/487</a></p><p>[#472] 修复全局事务中上下文丢失值问题<br><a href="https://link.segmentfault.com/?enc=SogAo3vzzjpGhn6iu551Wg%3D%3D.z1k5voxIEipZ9X0NbakjkQA1a8C4DRKfAfIdyUv6pD3P8yfn0nSgYmxHhiHuXBWn" rel="nofollow">https://github.com/seata/seata-go/pull/472</a></p><p>[#461] 修复 Error_Code_test 中变量未定义导致的 CI 失败问题<br><a href="https://link.segmentfault.com/?enc=j6yuOscuZOodLhFkOB1vqg%3D%3D.Fi3hWAq%2BwFIMV5iSw%2BZCnNNEq%2BPbO9lvGoj3iSlqChC68smMAhOOUNyzHF7rGQv6" rel="nofollow">https://github.com/seata/seata-go/pull/461</a></p><p>[#459] 修复 Error 日志重复打印问题<br><a href="https://link.segmentfault.com/?enc=9Och%2BoIlMI5NOyxGok%2BgEA%3D%3D.D2tbo0R9DrD8kaOl41IpUD6gN7WNlZlwQAa3QY0YBRSZoMIWzYyDyEjCLD7lGowf" rel="nofollow">https://github.com/seata/seata-go/pull/459</a></p><p>[#452] 修复 AT 模式执行 Insert SQL 时 ID 增的报错问题<br><a href="https://link.segmentfault.com/?enc=ec73IcQFBIDOoPAQFmp%2BXA%3D%3D.rTa%2FdnF1%2BBxTrOniwSyw4F1rb%2FOukrU1oIXZv4BQxoZoK%2FnKKgFV8gXzXWJ0imoU" rel="nofollow">https://github.com/seata/seata-go/pull/452</a></p><p><strong>Optimize:</strong></p><p>Seata-go 的示例项目已经全部迁移到新的仓库:<a href="https://link.segmentfault.com/?enc=H994N%2Fo97QTSp2alVVelBg%3D%3D.dBZpFCfgHD8EZdpL5ceWNC7XhcDgvwl7o67BMuFTXvC9SfOnlmqpD5ADhv95tqTA" rel="nofollow">https://github.com/seata/seata-go-samples</a></p><p>[#507] 优化 AT 模式 Multiple Update SQL 执行器<br><a href="https://link.segmentfault.com/?enc=1Fteog2APb9P1gS5GJlSNA%3D%3D.7fTOdBn9Zg7XjiJn1gwZGavSb9daD%2BwLGD9I8IIS4tW3UnH%2F9GJXn3rLD3xkRtYG" rel="nofollow">https://github.com/seata/seata-go/pull/507</a></p><p>[#505] 优化 AT 模式 Multi SQL 执行器<br><a href="https://link.segmentfault.com/?enc=nvGdvNEalXUASRn691uQ3g%3D%3D.RRFrE1Oxd3JZg9%2FY0b%2B%2B1yNcceeVaVSQQ00TxBlhFpMzhv%2B%2FY6Wyd0fuTq2MDgo1" rel="nofollow">https://github.com/seata/seata-go/pull/505</a></p><p>[#453] 优化 Message Type 和 Transaction error Code 枚举值<br><a href="https://link.segmentfault.com/?enc=VcMqPJDX6aarDGgZRqFCGQ%3D%3D.8Op5R5UQ%2BAYxKkM5yOX7xlnlVIaT8p4PBPYQMwssvMrA5se%2BtT2xJb0klmJUtrfL" rel="nofollow">https://github.com/seata/seata-go/pull/453</a></p><p>[#447] 优化数据源初始化流程<br><a href="https://link.segmentfault.com/?enc=Z3jNfrO4mHXAqemze8fH7g%3D%3D.wOEN%2FGyo%2FnwxwvhtJfdxa3JcV1UAUloouLmWJYbQIEYJqzIHGC4%2Bzk1KrLWzaB6z" rel="nofollow">https://github.com/seata/seata-go/pull/447</a></p><p>[#466] 优化变量的命名<br><a href="https://link.segmentfault.com/?enc=RbBg2yIjiX%2BhdEeV%2B3UuIA%3D%3D.7N4DYpT97vbtivok2dDIzVv6yJNYTZ2HiqThwtyyXAvt%2B1H0gFDEe7NGAJ8nTjSk" rel="nofollow">https://github.com/seata/seata-go/pull/466</a></p><p><strong>Test:</strong></p><p>[#445] 添加 Transaction error Code 的单元测试<br><a href="https://link.segmentfault.com/?enc=8eB5O%2Bqy75Idl1p4IrMzdQ%3D%3D.qVxyTtQ7rHKxVLcZoclZexPOq7NXXcQwhJgCTWec8tFxu6XD28aJbCXBBNJWf9d0" rel="nofollow">https://github.com/seata/seata-go/pull/445</a></p><p><strong>Doc:</strong></p><p>[#492] 更新 Readme 文件的已完成功能列表<br><a href="https://link.segmentfault.com/?enc=10%2FhlJNi5nj7MVg5Ii9rZA%3D%3D.5VDgjG6Ws3alS4uoDisF3xUAqQJRlaMy5%2FhihFhBHaoglsZjBvAsryko6APpqmw0" rel="nofollow">https://github.com/seata/seata-go/pull/492</a></p><p>[#489] 添加 1.1.0 版本的 Change Log<br><a href="https://link.segmentfault.com/?enc=8Sp0BuCojoCxX72wa2M6GA%3D%3D.N1lsPPnke33R27iSRHwWK4DuUIZUTgbKPTTPZ6MfjaftURb%2F7%2FLT6%2FLCzRLLYLEO" rel="nofollow">https://github.com/seata/seata-go/pull/489</a></p><p>英文版:<a href="https://link.segmentfault.com/?enc=HtM%2Byjs45FSJht9PuxlzPw%3D%3D.5tK7eRDvDkJ0eFKfLQ3rf1OH7A131RCWFzwzilT6OsN85AIDGiR6ERni0XOYTiddmBj5%2B16XQwfeviKH%2FtRHFw%3D%3D" rel="nofollow">https://github.com/seata/seata-go/releases/tag/v1.1.0</a></p><p><strong>致谢</strong></p><p>非常感谢以下 Contributors 的代码贡献。若有无意遗漏,请报告。</p><p>@luky116<br><a href="https://link.segmentfault.com/?enc=zz7CwfFo3d8g5ogFW4hOww%3D%3D.y7BgphXWqSpdCgUZWJsKk4WeWDvplHVNZq7TjyoPIR0%3D" rel="nofollow">https://github.com/luky116</a></p><p>@georgehao<br><a href="https://link.segmentfault.com/?enc=OGnyuS2mMbQBmS9%2FIRQW0A%3D%3D.SOHCUbp0z%2FUUJojzqaRgfEi9BpzGQjnyQCXqCbS3nSQ%3D" rel="nofollow">https://github.com/georgehao</a></p><p>@lxfeng1997<br><a href="https://link.segmentfault.com/?enc=bWjvtVE60cu71w02C2Mp0Q%3D%3D.dsTVXfHlICundoXPWYBKZGTl1%2B36k6ff8ChTXW4P5Hc%3D" rel="nofollow">https://github.com/lxfeng1997</a><br>@106umao<br><a href="https://link.segmentfault.com/?enc=MnnRhr3%2BSHER6OH2c3RTOg%3D%3D.qegpfHDhPT%2BggbHKKdjNjeIW6zlZUFNxamFu%2BluGAOY%3D" rel="nofollow">https://github.com/106umao</a><br>@wang1309<br><a href="https://link.segmentfault.com/?enc=s9OYzwjxrPN7zu0FKTK5fA%3D%3D.Olpzjxe5fUls%2FDRVRKtqd4Vn%2FeTC0fnxXMCMghh9nUY%3D" rel="nofollow">https://github.com/wang1309</a><br>@iSuperCoder<br><a href="https://link.segmentfault.com/?enc=zh%2B4AQmEBTOqd05o%2BZdOGA%3D%3D.fB1WDZoMVYTGLtiKzltkE7wVTusIbIJ1%2FGMkSyFmlCk%3D" rel="nofollow">https://github.com/iSuperCoder</a><br>@Charlie17Li<br><a href="https://link.segmentfault.com/?enc=TDb%2BosxuNed7tpQVnnTE7w%3D%3D.EV3AIKI2jH2EAP7GS8itCpDvRlKfCTFBvWLhlDTrle4%3D" rel="nofollow">https://github.com/Charlie17Li</a><br>@Code-Fight<br><a href="https://link.segmentfault.com/?enc=OF247cUIpLxLivfJsE%2FBbg%3D%3D.35yD2tJw8OgiO95rNqRXzkpXfdNiP677bPpM5EQy2xs%3D" rel="nofollow">https://github.com/Code-Fight</a><br>@Kirhaku<br><a href="https://link.segmentfault.com/?enc=rsblwGUceJXqINpMX3DqBQ%3D%3D.c5SRf7hwqRMSYI9OpnBSV%2F7Mx5XUw7Fd24tJnAvUGOA%3D" rel="nofollow">https://github.com/Kirhaku</a></p><p>@Vaderkai<br><a href="https://link.segmentfault.com/?enc=J57PalAQZjpLl%2FWhEC86tA%3D%3D.UHd5pnSD641CETYc2vYbyUOZLvx419OojKszHOuEO%2FQ%3D" rel="nofollow">https://github.com/VaderKai</a></p><p>@springrain<br><a href="https://link.segmentfault.com/?enc=2v5FZeK5HRMeeTHI4kAH8w%3D%3D.994WrvB6GAG5yA7slf7Kn71auG5qG%2BwV4WEWwDKunQM%3D" rel="nofollow">https://github.com/springrain</a></p><p>@Shaozhou Hu<br><a href="https://link.segmentfault.com/?enc=67ijcgztpNf1rUu%2BI%2Fh9gQ%3D%3D.AvTKDPnGxcyzLF4Vus8QBZldpwfXJKBARFyRRPuYPWk%3D" rel="nofollow">https://github.com/raspberry-hu</a></p><p>@finkyky<br><a href="https://link.segmentfault.com/?enc=eNkhh5NRzabh18m0MMn2FQ%3D%3D.hf2DnrGhV5q1mPDlspzDVDs%2FtVo5iad09Bs8hVbRCSE%3D" rel="nofollow">https://github.com/Finkyky</a></p><p>同时,我们收到了社区反馈的很多有价值的 issue 和建议,非常感谢大家。</p><p><strong>未来展望</strong></p><p>Seata 社区近期与不少国内 go 语言微服务框架以及 ORM 框架背后的开发社区达成合作,比如 GORM 框架,已经集成到了 Sample 中,后续会将更多的 ORM 框架集成在 Seata-go-Samples 项目中。与 MOSN 社区的合作也在推进中,可实现真正的基于 Seata 的 Transaction Mesh。</p><p>Seata-go-samples 集成到 Seata-go GitHub Actions 的集成测试环境,目前已经在进行中,用于测试每个 PR,保证系统的兼容性与稳定性。</p><p>预计在 4 月中旬发布的 Seata-go v1.2.0,将实现 XA 分布式事务模式。Seata-go 正在开发中的 Saga 模式,将在现有的 Seata Saga 模式之上实现一定的突破:实现基于工作流的微服务编排能力。</p><p>当前的 Saga 模式仅实现了服务编排的正向推进与反向 Rollback 能力,更进一步的服务编排则可以实现 DAG、定时任务、任务批量调度,覆盖工作流的所有流程,提升用户在 Seata 这个平台上的使用体验。目前 Seata-go 依赖于 Seata Java 的 TC,按照这个工作计划,可能需要在未来的 Seata-go 版本中实现一个功能更强大的 TC 调度。</p><p>Seata 多语言版本,目前发展最快的就是 Seata-go,其次成熟度较高的是 Seata-php,目前发展比较快的还有 Seata-js,欢迎对开源感兴趣的朋友加入 Seata 开源建设中来。</p><p><strong>常用链接</strong></p><p><strong>Seata:</strong><br><a href="https://link.segmentfault.com/?enc=AjS61b%2B2BenwZkMtQlshqg%3D%3D.UBGwmBG41MuuEeFKbfsDUbkkp59GZv6OyUiJ69zuq9c%3D" rel="nofollow">http://github.com/seata/seata</a><br><a href="https://link.segmentfault.com/?enc=y2zdKRBb%2BGtT19p51GT6RQ%3D%3D.VmoKAj6hbbehkjUTRlzlbv9mjdhie%2F8iSHBSnjsR3xB8f9SQfRrG3KOlgSgKD313" rel="nofollow">https://github.com/seata/seata-php</a><br><a href="https://link.segmentfault.com/?enc=du8kXV1%2B0Pzz7vywHmdLIA%3D%3D.nPP4Zy75TSj5FnmSYkMdheMbG6eDNa8eNfsMkNT8caNC%2FSORMnMndhHdMAwmF8gO" rel="nofollow">https://github.com/seata/seata-js</a><br><a href="https://link.segmentfault.com/?enc=PAHbWqG9sfrlpDvEVdxIhg%3D%3D.kIHfqR6Np1Qj%2FS6flMwoZj9YOdqx3w73%2FyGMbcsHpttMJYzP1mrT1gW9%2FBZiMxfP" rel="nofollow">https://github.com/seata/seata-go</a></p><p><strong>Samples:</strong><br><a href="https://link.segmentfault.com/?enc=ZYOw0S2%2FSs1B%2BMaPeXL4Dg%3D%3D.jg1cJa2W1%2FKsstDLpapg%2Be23jOVU9wSemZVe2ubfQMbE0agFFuqe5XaZB0HIHgiZ" rel="nofollow">https://github.com/seata/seata-samples</a><br><a href="https://link.segmentfault.com/?enc=XV3LZwHaOGiZfD0OOOAYvA%3D%3D.Sf5xPtUxhId888ceHLaBf4RLjh6k9N0%2BFKqV9InjxcB36x%2BKMYQeVTbLLMRngNSr" rel="nofollow">https://github.com/seata/seata-go-samples</a></p><p><strong>官网:</strong><br><a href="https://link.segmentfault.com/?enc=sMsXtWedUr8JrG2NSCytyg%3D%3D.Q3HV6hJDI4piCZI%2Bfgv3WfhECOIhuYNg4389LymAbq0%3D" rel="nofollow">https://seata.io/</a></p><p><strong>投稿</strong></p><p>欢迎大家将 Seata/Seata-go/Seata-php/Seata-js 相关的实践文章投稿至:<a href="https://link.segmentfault.com/?enc=SPecON63Cx%2FSqfMRfQ4NWA%3D%3D.5s3lqwOshzrO%2Fv0wDDuF0B1agmEPDsjvDDjNZVwDHe9M7CKURfvuUFcrTHtb5cHcYsF1HPVtTG9%2BFBbkNfM9kg%3D%3D" rel="nofollow">https://www.yuque.com/fred-x/ngfgiz/le1h4u5kn0xyhhoh</a></p><p><strong>Seata Star 一下</strong><br><a href="https://link.segmentfault.com/?enc=AFck21m8sjAB8%2F7kH6eCQA%3D%3D.pwd%2Fw6NfXlFGxInNr2HiJTJ%2FZUejfhiocKnTVwt0wySKko7XhiH84qzdUvgM%2FMZ3" rel="nofollow">https://github.com/seata/seata-go</a></p><p><strong>本周推荐阅读</strong></p><p><img src="/img/remote/1460000043537119" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=%2BfUZzoMOsxRLqKk0qRD16w%3D%3D.5SYtl9mltKT767NMbqp7FIRHIi1tRBLZKi44in9JoPz83aZbMJKDwTw6gwvbpgj6F%2FAkIfZ%2BpAssQ66Q1zzDe4%2B0DaM3R2inAWm2aI%2BbW%2BFS8wJpwrbmYZ%2FP6ySH23HnheJErzgkRKcgOwcA9ldcTyZidpLg%2BNfLGGk4Fy%2FF11RqqTiU4L%2BXS2njRB4J5%2FDJdpOPkNZ9ddlkTq77PxpuTcXuGav8WbtW6sqNzP3HHARS2QsPCY7TN%2B1kw%2BwJs0eSqoPTWAt%2FM%2B5xLB3orD8EmnMJiyyX98vsMhBwZ1t0lEsbA3TThrLcSkIS5pC30eu7ybglxzlRxOFxhBjHhuv2MQ%3D%3D" rel="nofollow">Seata-go 发布 1.0.3,补齐 AT 模式</a></p><p><img src="/img/remote/1460000043537120" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=ULfVVbSVgNg3NojyySPRQQ%3D%3D.g9EeSZFtYNP%2BYD99VT6k%2FNatl%2BjpmCJcM7lydRnXrED3%2B9RNUDVUmPZ6UiXnsCqDm5L7rmypuFB09xb3lfJKezSbLct2EmQhpbfuxxbJTxtT22osSBSs6sQh5YyegEBbwoZ5a7L4e6kwIpSiuiqVCtTJ2%2FRgmIJUMomKIFr6N0gNXqPWumeTRLlyK3iCAQWGYG1J4k2j5CterqwqvDptiCzm4BxWUpuzJtQfMjeATi%2B%2BT7Y8jNAqfdf2xVtj3E%2BBw3Nhr8YZK20Ru5q7rmyXbm1XVI6388pmRTEKiX5rhCs%3D" rel="nofollow">Seata-go 1.0 重磅发版</a></p><p><img src="/img/remote/1460000043537121" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=FwjD%2BS9REDu13ftsnWIzXw%3D%3D.74oiBObtOL28ZsNMiwAV%2BoaRVm%2FqXWFwRG%2F2zw0ye9KqZWMGWFxgyLkVU3SIdl%2FlgAD8NlL4wH4BYT0hXzs1DDGSXDZa0AJ4kAnhaOySh2dQqrW3GVabpRzQ6vrsk5M0ogA34M7zfw4qifRH6lMvzGeTAJCQ2dpyXVFV9Tdr7ieQV6A1G5qiRVWv9BSqN2ocZ44M7WykBsYNkp1aEJRUgqoPyz1GxchIKEZ8k0dZRx%2B99yOUYe94EKEVXbxHQxgyNshL83w6ElvNDrRBuvsnNaJ%2FXVVj0f%2FWzKbAkKPogi6Q8sy4pztcxWfRg8wuWDcDInEOubc17y5weXMYkH2HLg%3D%3D" rel="nofollow">Seata 多语言体系建设</a></p><p><img src="/img/remote/1460000043537122" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=Uyh%2BAtsofy3%2FgkuPrfGWLg%3D%3D.YdTZzDwCNpw%2BXP9POxqH%2BUeL3GgXp5QTr7%2FpCG5ILTIgVtGUw4MzAngIhQdp%2FmBLMbMI9kEuVPAna%2FDA926HfJU1HU8pLBKcDY2pHs7haMYXBgQbj0Brp18I%2BU37PEkLaQUu%2Ft4ZKB%2FI3uGHS886qGAtBbm6zWmCZQT4tEr38OsXcO6wCi9jvWZBqdR8HtxDj7NXvqJYw4GuQAa1gnRqp0hdKStR7s1JwR%2B4PR6IRhtVVEx9k7Q78gqYbWeAI%2BftqSSehyMq5XLWZF84nh%2FQ4ibFY0L4eEFx7zynpSxZNdDy9C3WCwAP4zI16U1P0bE8VPwGOGRUOw3KeIkBXarb%2FA%3D%3D" rel="nofollow">Seata AT 模式代码级详解</a></p><p><img src="/img/remote/1460000043513018" alt="图片" title="图片"></p>
DLRover:蚂蚁开源大规模智能分布式训练系统
https://segmentfault.com/a/1190000043513006
2023-03-08T10:36:30+08:00
2023-03-08T10:36:30+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043508668" alt="图片" title="图片"></p><blockquote><p>文|沙剑</p><p>蚂蚁集团高级技术专家</p><p><em>专注分布式深度学习领域</em><br><em>主要负责蚂蚁大规模分布式训练引擎的设计和开发</em></p><p><strong>本文 4491 字 阅读 12分钟</strong></p></blockquote><p>本文整体介绍了 DLRover 的项目动机与核心能力,未来我们会发布一系列文章,来从同步/异步弹性训练,优化策略服务,多种集群和训练框架对接,策略定制开发等多个角度来介绍 DLRover 的更多细节,敬请期待。</p><p><strong>01</strong></p><p><strong>技术背景</strong></p><p>2022 年 6 月,蚂蚁集团决定全面引入 ESG 框架,启动并确立了“数字普惠”、“绿色低碳”、“科技创新”、“开放生态”四位一体的可持续发展战略。针对“绿色低碳”,设立了 4 个子议题,包括绿色运营、科技助力产业碳中和、生态保护与修复绿色低碳生活。</p><p>在此背景下,绿色 AI 也成为蚂蚁 AI Infra 团队的一个重要工作方向。作为绿色 AI 的重要板块,工程提效项目致力于打造高性能离在线 AI 工程体系,通过提升算力效率和资源利用率,最终达到节省资源降低碳排放的目的。</p><p>当前,用户提交分布式训练作业的工具有 Yarn 或者 KubeFlow/Training-Operator。在提交作业时,用户需要在作业中指定作业资源,包括不同角色的节点数量和资源规格(CPU 核数、内存、GPU 等)。</p><p>在训练作业提交后,作业可能遇到如下问题:</p><ul><li>集群资源不足以启动作业的所有节点,作业只能等待。</li><li>训练作业的节点可能会出错,比如被高优任务抢占、机器故障、IO 故障等,导致作业失败。</li></ul><p>出现这些问题后,用户只能修改作业资源来重新提交作业。</p><p>针对这两个问题,蚂蚁集团早期基于 Kubernetes 开源了 ElasticDL 项目来支持 K8s 上 TF 2.x 分布式训练的弹性容错。在项目落地过程中我们又发现了如下问题:</p><ul><li>用户配置的资源可能过少引起 OOM 和训练性能差。</li><li>用户为了保障作业成功率和速度,通常会配置超额资源导致利用率低。</li><li>越来越多的用户使用 PyTorch 或其他 TF 之外的框架来开发和训练模型。</li><li>越来越多的分布式集群开始支持 AI 作业,比如 Ray、Spark 集群,能否适配任意计算集群?</li><li>在线学习越来越被广泛采用的情况下,如何运用一套系统同时解决兼容离在线训练?</li></ul><p>前两个问题使得集群 CPU 利用率通常只有 20% 上下,同时算法开发人员需要投入很多人工运维成本,为了解决训练端资源提效的需求,支持在不同集群上针对在离线多种训练模式,给不同框架的分布式训练作业自动地寻找最优资源配置。</p><p>蚂蚁 AI Infra 团队基于 ElasticDL 弹性容错的思路,升级扩展并开源了 DLRover,其目标在于提升分布式模型训练的智能性,目前很多公司的训练作业都是跑在混部的集群中,运行环境复杂多变,正如其名,DLRover 作为分布式训练领域的火星车,帮助用户驾驭崎岖的训练之路。</p><p><img src="/img/remote/1460000043513008" alt="图片" title="图片"></p><p><strong>02</strong></p><p><strong>整体方案</strong></p><p>DLRover 提出了 “ML for System” 的理念来提升分布式训练的智能性,那么这样的系统应该具备哪些能力呢?</p><p>我们认为主要体现在如下几个方面:</p><ul><li>解耦:不和底层训练框架耦合在一起,只依赖接口抽象,遵循依赖倒置原则。(<em>i.e. Elastic Runtime</em>)</li><li>资源调度:具备上帝视角的资源调度管控能力。和建立在对作业精准画像的决策能力。</li><li>数据驱动:同时收集掌握集群资源数据,也掌握训练作业数据。以数据驱动智能。</li><li>作业交互:以对训练作业以及模型白盒化的理解,动态根据实际情况,对训练作业进行优化调整。超越简单机械的弹性容错!</li><li>智能:通过对集群以及作业信息的收集,结合算法模型+固定策略产出精准的作业优化策略。</li></ul><p>我们希望设计并实现一个系统,让用户完全摆脱资源配置的束缚,专注于模型训练本身。在没有任何资源配置输入的情况下,DLRover 仍然可以为每个训练作业提供最佳资源配置。考虑到用户可能会以不同的方式运行他们的训练作业,DLRover 除了面向训练平台进行作业统一管理的 Cluster Mode,也提供 Single-Job Mode 方便独立的算法开发者也能享受到弹性容错等基本特性。</p><p><strong>03</strong></p><p><strong>系统架构</strong></p><p>DLRover 由四个主要组件组成:ElasticJob、Elastic Trainer、Brain 服务和 Cluster Monitor。</p><p><img src="/img/remote/1460000043513009" alt="图片" title="图片"></p><p>上图显示了 DLRover 如何在 K8s 集群上管理深度学习训练作业。DLRover 以 ElasticJob CRD 的形式将作业提交到集群。收到 CRD 后,ElasticJob Operator 会拉起一个 Master Pod 作为 Elastic Trainer。其从 Brain 服务中获取初始资源计划。Elastic Trainer 用它来创建 Scale CRD,并应用 Scale CRD 通知 ElasticJob Controller 启动所需的 Pod,每个 Pod 将在其上启动一个 Elastic Agent。</p><p>在训练过程中,Elastic Trainer 的 Training Master 将数据分片分发给 Worker。同时,Cluster Monitor 监控每个作业的运行状态(<em>i.e.每个节点的 Workload</em>)和集群状态(<em>i.e. 资源水位</em>)。这些数据将定期报告给 Brain,Brain 将数据持久化到数据库中。</p><p>然后 DLRover Brain 根据作业的运行状态,选择合适的算法生成新的资源计划,并通知 Elastic Trainer 开始资源调整。</p><p>总的来讲,DLRover 可以帮助分布式训练作业自动化运行在集群中,可以看作分布式作业的自动驾驶,模型开发者只需要关注模型的算法设计,DLRover 目前开源版则可以为用户提供如下能力:</p><ul><li>自动资源推导:帮助用户自动初始化训练资源,提升资源利用率与作业稳定性。</li><li>动态训练数据分片:针对不同 Worker 性能不通造成的木桶效应,根据实际消费速度分配训练数据,可配合 Failover 记录消费位点,数据不丢失。</li><li>单点容错:提供单点容错的能力,不需要完整重启作业。</li><li>资源弹性:支持运行时 Pod 级和 CPU/Memory 级的资源弹性扩缩容,动态全局优化决策。</li></ul><p><strong>04</strong></p><p><strong>DLRover 能带来什么</strong></p><p><strong>1.作业零资源参数配置</strong></p><p>用户提交分布式作业时无需提供任何资源信息,DLRover 会自动对作业进行画像,推导出最优的资源配置,同时运行时可以根据实际情况(<em>集群资源、样本流量、当前利用率、...</em>)自动对资源进行调整。下面展示了两种提交脚本的配置对比:</p><p><img src="/img/remote/1460000043513010" alt="图片" title="图片"></p><p><strong>2.</strong> <strong>单点容错提升作业稳定性与恢复效率</strong></p><p>DLRover 支持单点恢复 Parameter Server 和 Worker 角色的失败退出而不需要整体作业重启,对于非用户代码和数据类型的错误可以实现用户无感的重启。例如集群中,很常见的一类错误是由于用户配置了不足的内存,导致训练 OOM。在 DLRover 的帮助下,我们可以自动拉起一个优化配置的节点来恢复失败的 Node。在真实环境下,DLRover 管理的训练作业,相比基线的 Kubeflow TF-Operator 作业,训练成功率从 84% 提升到了 95% 以上。</p><p><img src="/img/remote/1460000043513011" alt="图片" title="图片"></p><p><strong>3.</strong> <strong>自动扩缩容提升作业训练性能</strong></p><p>DLRover 针对 Parameter Server 和 Worker 级别都支持在训练运行时进行自动的调节训练资源以提升训练性能。通过监控作业节点的 Workload,DLRover 可以分析资源配置的瓶颈。常见的资源瓶颈有:节点抢占、Workload 不平衡、CPU 不足导致算力低下、节点数目不足。DLRover 可以通过动态的资源热更新来持续优化训练性能。</p><p><img src="/img/remote/1460000043513012" alt="图片" title="图片"></p><p><strong>4. 自动扩缩容提升作业资源利用率</strong></p><p>通常不同的模型训练作业,需要不同的资源配置。然而用户倾向于超额配置作业的资源以保障作业的成功率。这通常会导致大量的资源浪费。DLRover 的自动扩缩容能力,可以自动根据作业的真实需求配置资源,以最少的资源达到最优的训练性能,从而减少资源浪费。下图显示了自动资源对比手动资源的资源利用率曲线对比:</p><p><img src="/img/remote/1460000043513013" alt="图片" title="图片"></p><p><strong>5.</strong> <strong>动态数据分发解决慢节点问题</strong></p><p>混部集群存在资源超卖和抢占的情况,部分节点消费数据慢,快节点需要等待慢节点,降低训练速度。DLRover 可以通过数据动态分发给慢节点少分发一些数据,减少等待。此外 DLRover 应该保证训练任务尽可能按照用户配置参数消费数据,避免重复消费/丢失数据,这会给训练带来不确定性,影响模型性能。</p><p>当扩容或者缩容时,需要有个全局协调者知道记录节点当前消费数据详情。当节点失败重启后,全局协调者需要知道节点已经消费和尚未消费的数据。如果这些逻辑让训练节点来做,训练节点和训练节点之间需要交互,增加训练节点逻辑的复杂性。DLRover Master 充当了这个全局协调者的角色。</p><p>总而言之,在我们看来,通过动态数据可以简化训练节点逻辑的复杂性,训练节点只管从 DLRover Master 获取 Shard,然后读取数据,不需要处理其他的逻辑。</p><p><strong>6.</strong> <strong>统一离线与在线学习范式</strong></p><p>上述动态数据分片特性,实际上帮助我们将 Data Source 和训练作业进行了解耦,在此基础上 DLRover 可以同时支持离线训练,也可以支持消费实时样本流的在线学习作业。(<em>可以通过 Dlrover.trainer 直接对接样本流,也可以作为流计算引擎的训练 Sink 节点</em>)</p><p>在蚂蚁的实践中,DLRover 可以作为一个理想的组件,来帮助我们构建出一个端到端的在线学习系统。DLRover 可以提供数据源消费位点记录与恢复,在线学习长跑作业稳定性与性能保障,资源利用率保障等一系列实际问题。我们的开源仓库中也提供了简单的范例,后续我们也会开放更多周边组件。</p><p><strong>7. 支持异步和同步训练模式</strong></p><p>训练集群中每天都运行着不同业务域性质各异的训练作业:推荐系统的大规模稀疏模型通常运行在 PS/Worker 架构的训练模式下进行异步参数更新,资源也多以 CPU 计算为主。CV/NLP 领域的稠密模型则多以数据并行的方式在 GPU 服务器上进行同步训练,这时只有 Worker 一种角色。</p><p>DLRover 在设计上,可以同时支持同步和异步更新模式,做到针对各种训练范式的统一。</p><p><strong>8. 同训 DL 练框架解耦</strong></p><p>DLRover 支持用户使用任何自己的训练框架,底层训练代码通过提供约定的 API 接口以实现自动弹性扩缩等需要同底层分布式代码深度交互。集群中部署完成后,终端算法同学基本可以无感接入。</p><p><strong>05</strong></p><p><strong>总结 & 未来计划</strong></p><p>DLRover 目前已经在蚂蚁大规模落地,集群资源利用率相对于基线稳定获得了 15% 以上的提升。同时也有效解决了由于资源配置不合理造成的训练吞吐不及预期的问题。我们希望通过 DLRover 的开源可以帮助更多同行一起推行低碳、绿色、AI 的理念。同时也切实降低模型开发中的运维成本,释放更多的生产力去解决业务的问题。</p><p>当前 DLRover 的调优算法,以及资源,作业画像策略主要针对蚂蚁内部技术栈优化。考虑到不同机构实际技术栈的多样性,在设计上,DLRover 在 API 层做了统一接口抽象,具体调优算法与作业画像策略则可灵活自定义。我们欢迎不同机构的开发者也能根据自身特点,同我们一起共建 DLRover 项目,将其发展壮大。</p><p><strong>了解更多...</strong></p><p><strong>DLRover Star 一下✨:</strong><br><em><a href="https://link.segmentfault.com/?enc=mur4h5RgS%2FvpzIKYJf5v2Q%3D%3D.0pUonpaJapBxJE8ss36cVrE%2BlnxqVd9ImM7uGydWPEET4SFcu8ytCjkANXEw2kyQ" rel="nofollow">https://github.com/intelligent-machine-learning</a></em></p><p><strong>本周推荐阅读</strong> </p><p><img src="/img/remote/1460000043513014" alt="图片" title="图片"></p><p><a href="https://link.segmentfault.com/?enc=2a3ij%2B%2FDzMmG%2BIBsfz77dA%3D%3D.fKHCiLZtdShsNg5Nc2iz0tFrIq5RJ81adKN3R18GDAiXPK7DdVU3w%2FPZ2OzQmPxE8LkXuUklg6QY97SDpGT42VsqXVigdmwF6zAizDzWeAKuezmiDTE6WZb9zmRs3ytCih%2B6VyygHrLZFPh7B8O6hRK8upGzrkqGkKdrqsNAFa%2BooLHg8ofAPrw4o6dYKdpgbQ4sApfDCVR56bmHg8DiZmbFevk31aLbJGDtK9A1LkCUwBCKPHTEuUqasTeycXAl2JHP5BtkBk3pRzQmGUl48ANvrZiLz80d5%2BJW%2BI989LxEysp51wuBqp5FkwjpbVpy" rel="nofollow">Go 原生插件使用问题全解析</a></p><p><img src="/img/remote/1460000043513015" alt="图片" title="图片"></p><p><a href="https://link.segmentfault.com/?enc=cWrXVctGtJl%2Bj4xpr4kvzQ%3D%3D.aTNM1xmsvX7Z8ivcvvcJt5MgvIy7ObbSUJrC8jUnaYYAj5c%2Fbo2lrTL976ZbsNRxwAcoCIkhLcN35WfUteSZlcggRtgySeKSzpqR9AmpcrN%2F2u45Sw%2B3AVlCcc3kxKHZ3Pm8a1AzH9oBfTQkKHLKfkmTE%2F0oafB0ZylG%2FQLYNOrCZGJ45XYuez1IzfQUSqMTMdoNPByq%2BVJlqXWhacA8OCXLMAqkbC5DOEo4ZsUnFviSCcNNh5LzTAOP1Uo%2FUa4k7gs7dMneH9sBJW0cJzBOWfFDL79SNgZlGYHOXNJlRD9Pt%2FQRhlY585l1EARVvYd0" rel="nofollow">MOSN 构建 Subset 优化思路分享</a></p><p><img src="/img/remote/1460000043513016" alt="图片" title="图片"></p><p><a href="https://link.segmentfault.com/?enc=CA%2FQfIsxMQH8VzMs4bPqcg%3D%3D.v2B%2BZKgTAtyq3eTFXdMK%2B0d%2FpMh%2FRYuZyoYRc0v1ZNoTxaYczIAixVsBtKwpujHAXakztc8CBrNdZPgXRWqBCJaadjV78NBZ5%2BSG%2BfrUNEkjDb07fhaLoevw%2FzYsvQxPJPfVM3gmUY%2BHErjzVVbWagJc1aNhWIrk3ECE0GH%2BcpmuyylB8v0TGNkT7ULqlHvski2HGlca722lIoEH82xahlwQSA0f3v%2BgcTIYCiEIoc%2FlI%2BNGPln5W8lK85D3sesLNTvGnpu9TanmVT0KxBFnGBC%2FZfaP97BrcGov%2FEKQRkDlNrO5gLdipkdOqBUDLC72" rel="nofollow">MOSN 文档使用指南</a></p><p><img src="/img/remote/1460000043513017" alt="图片" title="图片"></p><p><a href="https://link.segmentfault.com/?enc=LXq6LPTtcUecS0rxN%2FktRQ%3D%3D.WO6qMq%2B4lG4TQOdA1E%2FdASVlQB8dZAbJAHkWE2n8eplHfBqaNVNF4vzKbZ3K13bzvvA3%2FEPSbQL2pJ3zVQZBTp3N11xsRa2qS0fFmUFeJqd8WAlh1i0cfdbZMbPrYR3JH8BCbfL6jqyiBgZhMJM8BEnd2BS2iTKG6XwxHrriBaFxcLOJIlgvVa7%2FNh8djXtwcrHZbY48jNhypUVG2fjf5pSnASREAsk8jXEAGe2z%2BFyi%2F6cNPSoSYeIUm2pXYLxptFy%2FrH4uDWHP2fPZx31rYWqPLVh8yVakG3sDdAJc4Q0%3D" rel="nofollow">MOSN 1.0 发布,开启新架构演进</a></p><p><img src="/img/remote/1460000043513018" alt="图片" title="图片"></p>
Wasm 原生时代已经来到
https://segmentfault.com/a/1190000043508666
2023-03-07T12:11:47+08:00
2023-03-07T12:11:47+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043508668" alt="图片" title="图片"></p><p><em>Ending 定律:一切可编译为 WebAssembly 的,终将被编译为 WebAssembly(Any application that can be compiled to WebAssembly, will be compiled to WebAssembly eventually)。</em></p><p><strong>PART. 0</strong></p><p><strong>前言</strong></p><p>WebAssembly 作为一种新兴的网页虚拟机标准,它的设计目标包括:高可移植性、高安全性、高效率。</p><p>2018 年 WebAssembly 第一个规范草案诞生,2019 年成为 W3C 第四个标准语言。到了 2022 年底可以说我们已经进入了 Wasm 原生时代……</p><p><strong>PART. 1</strong></p><p><strong>Ending 定律和 Wasm 原生</strong></p><p><strong>1、什么是 Ending 定律</strong></p><p>Ending’s law: “Any application that can be compiled to WebAssembly, will be compiled to WebAssembly eventually.” </p><p>Ending 定律:“一切可编译为 WebAssembly 的,终将被编译为 WebAssembly。”</p><p>Ending 定律也称为终结者定律,它是 Ending 在 2016 年 Emscripten 技术交流会上针对 WebAssembly 技术给出的断言。Ending 定律的威力不仅仅在语言层面。WebAssembly 是第一个虚拟机世界标准,以后将人手至少一个 Wasm 虚拟机。 </p><p>不过和之前被大家鄙视的 JavaScript 语言大举入侵各个领域的情况不同,这次 Python、Ruby 这些语言将彻底拥抱 WebAssembly 技术,因为它是一个更底层、也更加开放的新兴生态平台。从 Ending 定律可以预测 Wasm 原生时代迟早都会到来。</p><p><strong>2、什么是 Wasm 原生</strong></p><p>Wasm 原生可以类比云原生的定义:就是天生就是为 WebAssembly 平台设计的程序和语言。比如专门为 WebAssembly 设计的 AssemblyScript 语言和凹语言就是 Wasm 原生的编程语言。如果一个应用天生就是考虑了 WebAssembly 的生态支持,那么就是 Wasm 原生的应用。一个 Wasm 原生应用很容易支持纯浏览器环境,因此不支持纯浏览器环境的应用大概率不是 Wasm 原生。</p><p>现在 Docker 已经开始支持 Wasm 程序,因此 Wasm 原生软件天然也是云原生的软件,但是反之则不能成立。而云原生因为受限于云的环境、导致其应用的场景和领域有较大的限制,比如云原生应用强依赖网络因此无法在很多单片机环境、甚至是本地环境运行,因此云原生更多是在互联网企业流行。但是 Wasm 原生的程序则可以轻松在 Arduino 等受限环境、本地台式机机环境、个人智能手机环境和 Kubernetes 等云原生环境执行。可以说未来 Wasm 原生应用将无处不在!</p><p><strong>PART. 2</strong></p><p><strong>WebAssembly 简史</strong></p><p>WebAssembly (<em>简称 Wasm</em>) 是 W3C 定义的第 4 个标准,是 Web 的第 4 种语言。说 WebAssembly 是一门编程语言,但实际上它更像一个编译器,其实它是一个虚拟机,它还包含了一门低级汇编语言和对应的虚拟机体系结构,而 WebAssembly 这个名字从字面理解就说明了一切:“Web 的汇编语言”。</p><p>简而言之,WebAssembly 是一种新兴的网页虚拟机标准,它的设计目标包括:高可移植性、高安全性、高效率(<em>包括载入效率和运行效率</em>)、尽可能小的程序体积。</p><p><img src="/img/remote/1460000043508669" alt="图片" title="图片"></p><p><strong>1、Emscripten 项目</strong></p><p>WebAssembly 的前身是 Mozilla 创建的 Emscripten 项目<em>(2010年)</em>——通过将 C/C++ 通过 LLVM 编译到 JavaScript 的 asm.js 子集来提速!JavaScript 作为弱类型语言,由于其变量类型不固定,使用变量前需要先判断其类型,这样无疑增加了运算的复杂度、降低了执行效能。因为 asm.js 仅包含可以预判变量类型的数值运算,有效的避免了 JavaScript 弱类型变量语法带来的执行效能低下的顽疴。根据测试,针对 asm.js 优化的引擎执行速度和 C/C++ 原生应用在一个数量级。</p><p>2015 年 6 月 Mozilla 在 asm.js 的基础上发布 WebAssembly 项目,随后 Google、Microsoft、Apple 等各大主流的浏览器厂商均大力支持。WebAssembly 不仅拥有比 asm.js 更高的执行效能,由于使用了二进制编码等一系列技术,WebAssembly 编写的模块有更小的体积和更高的解析速度。目前不仅 C/C++ 语言编写的程序可以编译为 WebAssembly 模块,Go、Kotlin、Rust、Python、Ruby、Node.js、AssemblyScript、凹语言等新兴的编程语言都开始对 WebAssembly 提供支持。</p><p><strong>2、 WebAssembly 1.0 草案</strong></p><p>WebAssembly 技术自诞生之日就进入高速发展阶段。在 2018 年 7 月 WebAssembly 1.0 草案正式发布,在 2019 年 12 月正式成为 W3C 国际标准,成为与 HTML、CSS 和 JavaScript 并列的唯四前端技术。2019 年,同样诞生了 Wasi (<em>WebAssembly System Interaface</em>)规范,用于将基本的系统调用带入到 Wasm 生态。2022 年 Docker 对 Wasm 提供支持,目前 WebAssembly 已经是一个独立的生态。</p><p><strong>3、 WebAssembly 生态大图</strong></p><p>下面是 “WebAssembly 将引领下一代计算范式” 展示的生态大图:</p><p><img src="/img/remote/1460000043508670" alt="图片" title="图片"></p><p>可以看到从工具链、基础设施、到应有和 Web3 均有涉及,生态已经非常丰富。正和 JVM 构建的生态类似,WebAssembly 也在构建自己的庞大生态。</p><p><strong>PART. 3</strong></p><p><strong>Wasm 社区 22 年的变化</strong></p><p>2022 年,国内外自媒体社区对 WebAssembly 的评价态度可谓是完美遵循了欲扬先抑的剧本。</p><p>先是有热文爆大佬 WebAssembly 创业失败引发质疑,然后是传出社区分裂、应用争议再引发炒错的方向等争论,然后随着 Docker 对 Wasm 支持的预览版发布带来风向 180 度大转弯,简直是要把不明真相的群众彻底忽悠拐了。其实 WebAssembly 从诞生之日起,真正的从业人员始终在稳步推进,完全没有自媒体想象和策划的这些剧本演义。</p><p><strong>1、WebAssembly 2.0 草案</strong></p><p>4 月 20 日,W3C 公布了 WebAssembly 2.0 的第一批公共工作草案。主要包含向量类型、引用类型、多返回值、多 Table 支持、Table 和内存指令增强等。</p><p>向量类型的支持可以用于优化纯计算类型的并发程序、引用类型可以用于和外部的浏览器 DOM 对象等更好的交互、多返回值可以可以简化某些程序的表示(<em>比如凹语言后端依赖该特性</em>)、多 Table 支持可能用于灵活支持多模块连接等。可以说 WebAssembly 标准是该生态的统一基准平面,而且这些特性的实现已经相对普及,可以作为实验特性试试用。</p><p>比如凹语言后端依赖该特性)、多 Table 支持可能用于灵活支持多模块连接等。可以说 WebAssembly 标准是该生态的统一基准平面,而且这些特性的实现已经相对普及,可以作为实验特性试试用。</p><p><img src="/img/remote/1460000043508671" alt="图片" title="图片"></p><p>完整文档参考:<a href="https://link.segmentfault.com/?enc=dbq01ZFS2vxlSL2eGNbxWg%3D%3D.fa6usPXb3uB%2FtH4lIHDo%2BF6tC6WGydNbSo2FbOOlvVT8xrZpWuQDY8YPCgh%2FSY%2Fc" rel="nofollow">https://www.w3.org/TR/wasm-core-2/</a></p><p><strong>2、Docker 支持 WebAssembly</strong></p><p>2019 年,Docker 创始人 Solomon Hykes 发布了一条推文,他说如果 2008 年就诞生 WebAssembly 和 Wasi 的话,Docker 就没有必要诞生了。</p><p><img src="/img/remote/1460000043508672" alt="图片" title="图片"></p><p>其实作者在 2018 年写作《WebAssembly 标准入门》时, 通过推演也得出过类似的结论:当时的结论是 WebAssembly 更大的生命力在浏览器之外,如果配合文件系统、网络系统将得到一个更为迷你的操作系统无关的运行平台。</p><p>Docker 与 WasmEdge 合作创建了一个 containerd shim,该运行时支持运行 Wasm 程序。下面是 Docker 对 Wasm 的支持原理图:</p><p><img src="/img/remote/1460000043508673" alt="图片" title="图片"></p><p>Docker 执行 Wasm 需要指定一些额外参数:</p><pre><code>$ docker run -dp 8080:8080 \
--name=wasm-example \
--runtime=io.containerd.wasmedge.v1 \
--platform=wasi/wasm32 \
michaelirwin244/wasm-example</code></pre><p>首先 runtime 参数指定 Wasmedge 运行时,然后 platform 指定采用 Wasi/wasm32 规范(<em>指定有哪些宿主 API</em>)。</p><p>完整的信息可以参考 Docker 的官方文档:<em><a href="https://link.segmentfault.com/?enc=5%2B4om3wJMnMBeq6ixdfwYA%3D%3D.ukcEApy%2BLMiEPBeNBwVW7TgpfAzIr%2F4cs0deevBhI1SNURbxcmqbcOmWrraEc2sf" rel="nofollow">https://docs.docker.com/desktop/wasm/</a></em></p><p><strong>3、SQLite3 官方支持 WebAssembly</strong></p><p>SQLite3 作为一个纯粹的 C 语言库,其实在 WebAssembly 标准诞生之前就可以通过 Emscripten 技术将 C 代码编译为 asm.js。</p><p>因此,网上很早就有在浏览器的 JS 版本、甚至直接通过 Emscripten 输出 WebAssembly。不过这次是 SQLite3 官方提供了对 WebAssembly 的支持,这表示 WebAssembly 在 SQLite 社区完全进入工业级应用阶段!</p><p>根据官网介绍,主要有 4 个目标:</p><ul><li>绑定一个低级的 SQLite3 API,在使用方面尽可能接近原生 API;</li><li>更高级别的面向对象风格 API,类似于 sql.js 和 node.js 样式的实现;</li><li>基于 Worker 的 API,以支持多线程环境更容易使用 SQLite 功能;</li><li>基于 Worker API 的 Promise 包装,对用户完全隐藏了跨线程通信方面复杂性。</li></ul><p>而不在此列的特性包括不支持 UTF 16、和清除老旧特性等。简而言之,在提供底层 API 能力的同时,针对面向对象、多线程等环节提供简单易用的 API。</p><p>完整的介绍请参考:<em><a href="https://link.segmentfault.com/?enc=kCH3hKPsR3aghSsS1a7m1A%3D%3D.OE31nRyVl%2B6ZZOQLZYxpcppYyHeUGyilwETNQs9Vhx0%3D" rel="nofollow">https://sqlite.org/wasm</a></em></p><p><strong>4、Ruby 3.2 支持 WebAssembly</strong></p><p>12 月发布的 Ruby 3.2 也增加了基于 Wasi 的 WebAssembly 支持。使得 CRuby 二进制内容可用于浏览器、Serverless Edge、以及其他 WebAssembly/Wasi 嵌入环境。</p><p>目前,此功能已通过除 Thread API 之外的 basic 和 bootstrap 测试套件。</p><p><img src="/img/remote/1460000043508674" alt="图片" title="图片"></p><p>虽然目前基于安全原因,还缺少一些功能来实现纤程、异常和垃圾回收的特性,但是这已经让用户可以在浏览器中尝试原生的 CRuby:<em><a href="https://link.segmentfault.com/?enc=nbaGa5NtIi0w8GRup68kTg%3D%3D.hk0IyMea492yMiq0noR4nrEOMOXxlTlHE%2BOOL%2B8lt9MvU2yu4g1FZC39BHrsoDDN" rel="nofollow">https://try.ruby-lang.org/playground/</a></em></p><p><strong>5、Python 3.11 支持 WebAssembly</strong></p><p>和 Ruby 社区的目标类似,Python 社区也在 4 月启动在 Python 3.11 增加对 WebAssembly 的支持。Python 3.11 对 wasm32-emscripten 和 wasm32-wasi 提供了支持,从而也实现了在浏览器执行 Python 的梦想。</p><p>具体细节可参考以下文档:</p><ul><li><em><a href="https://link.segmentfault.com/?enc=62Qn20dkNHRa%2BINqB4hvqQ%3D%3D.Tz2moX3lxgzX80GivPls1lhJRIZLr8LqO2YYVBlGhg%2B6v9yGe6VjiQ7Bwm6yQ60U" rel="nofollow">https://pythondev.readthedocs.io/wasm.html</a></em></li><li><em><a href="https://link.segmentfault.com/?enc=WUgpHrufmtbW3gf4CMemug%3D%3D.A%2FH2otTTN%2F9BOnZUoDuqI5uJ1sZIQ%2B2pjFxFlmdYG3%2BhgrY5g7hFBxRaEpPhcZR5Gh2xH4C8qtWyFnz%2FRJt%2FNeEqFhoqibOaV4yXw5tIzvM%3D" rel="nofollow">https://docs.python.org/3/library/intro.html#webassembly-plat...</a></em></li><li><em><a href="https://link.segmentfault.com/?enc=zOs0KbcPGyLjwDhSDtPEWA%3D%3D.EqjuDo4hio1PBY%2FF6Xt4GSqgIQ8Aiyp0RUA2Ybf4j4cgTANbaTE%2F%2B2ceS6AwmbTiT6FGyDZV%2FUKZX7siFgYwBRZOb%2BT6Imo3tGGlKkbYqu2NM5SrWULnzQf6PpPooc3cSr8n0sF5%2F7%2BI8LILs8RSvg%3D%3D" rel="nofollow">https://speakerdeck.com/tiran/python-3-dot-11-in-the-web-brow...</a></em></li></ul><p>因为有了 WebAssembly 魔法加持,Ruby 和 Python 等脚本语言也终于可以在浏览器玩耍了。</p><p><strong>6、为 WebAssembly 而生的凹语言</strong></p><p>WebAssembly 草案刚刚发布不久,国外就诞生了专门为其设计的 AssemblyScript 语言。</p><p>在 2022 年 7 月,国内 Gopher 也发起了针对 WebAssembly 平台的凹语言。目前凹语言不仅仅提供了在线的 Playground,还上线了用凹语言开发的贪吃蛇小游戏。希望新兴的语言可以为 WebAssembly 注入更多的活力。</p><p>▪凹语言主页:<em><a href="https://link.segmentfault.com/?enc=IDl%2FYupRiLQg5LAUlhTm2A%3D%3D.TjgF5BpTeWCMP7G9fbV4uZlmgTDGLOEFKxQ%2B70DIF7M%3D" rel="nofollow">https://wa-lang.org/</a></em></p><p>▪凹语言仓库:<em><a href="https://link.segmentfault.com/?enc=QE0HB0Ws3AB1iEDOxHRz9Q%3D%3D.%2BiyqDYfPDtcZTp7Fimz3ySxPhHiJx02WBMR9ZDphqvA%3D" rel="nofollow">https://github.com/wa-lang/wa</a></em></p><p>▪凹语言开发的贪吃蛇:<em><a href="https://link.segmentfault.com/?enc=QJxt7D7bdEKEzVhryHgZIw%3D%3D.3uXU2O2T9237FGNurXM1fm07J5QkPCReJplaVKUc%2B48%3D" rel="nofollow">https://wa-lang.org/wa/snake/</a></em> </p><p><strong>PART.4</strong></p><p><strong>Wasm 虚拟机实现</strong></p><p>对于 JavaScript 用户,直接通过浏览器内置的 WebAssembly 模块即可,或者是通过 Node.js 提供的模块 API。我们这里简要介绍的是浏览器环境之外的 Wasm 虚拟机实现,这里介绍的主要有 C/C++、Rust 和 Go 语言几类实现。</p><p>总体来说,大家完全不需要担心 Wasm 虚拟机的选择和切换代价,只要遵循 Wasm 标准原则切换虚拟机就和换个鼠标一样容易。</p><p><strong>1、C/C++ 语言|</strong><strong>WasmEdge、wasm3 和 Wamr</strong></p><p>WasmEdge 和 Wasm3 是 C/C++ 语言实现的具有代表性的两个 WebAssembly 虚拟机(<em>没有包含 V8 的虚拟机</em>)。</p><p>WasmEdge 可以说是目前最受关注的 WebAssembly 虚拟机实现,因为它不仅仅是 CNCF 推荐的 Wasm 虚拟机,更是 Docker 内置的 WebAssembly 虚拟机。WasmEdge 是由美国的袁钧涛(<em>Michael Juntao Yuan</em>)发起,是由 CNCF 托管的云原生 WebAssembly runtime。</p><p>它广泛应用于边缘计算、汽车、Jamstack、Serverless、SaaS、服务网格,乃至区块链应用。WasmEdge 可以进行 AOT(<em>提前编译</em>)编译器优化,是当今市场上最快的 WebAssembly runtime 之一。</p><p>可以预计,随着 Docker Wasm 的普及,WasmEdge 将成为最流行的 Wasm 虚拟机实现之一。</p><p><img src="/img/remote/1460000043508675" alt="图片" title="图片"></p><p>WasmEdge:<em><a href="https://link.segmentfault.com/?enc=f2wP9aroVyEQ61e001O6Hw%3D%3D.%2B85azdouiB1pOY%2BCIph4FZC%2B0vR1DJD5yHGGTYjm9vk%3D" rel="nofollow">https://wasmedge.org</a></em></p><p>袁钧涛 (<em>Michael Juntao Yuan</em>):<em><a href="https://link.segmentfault.com/?enc=rpLxwi9492QozzuB0lnuHg%3D%3D.O99sff0SircIjBhtkAsVOxc%2BaM6FHBwyI%2BkZ1H%2B1pV4%3D" rel="nofollow">https://github.com/juntao</a></em></p><p>Wasm3 是 C 实现的 WebAssembly 引擎,可运行在嵌入式设备上。因为需要的资源比较少,目前可以运行在 Arduino 和树莓派环境。</p><p>Wasm3 仓库:<em><a href="https://link.segmentfault.com/?enc=dEAC48OXPMX5ggjIE7%2BAOw%3D%3D.f2SG3fUVpnhixdVCEtMzqLB6QynBwt86EBRtjpRtmPA%3D" rel="nofollow">https://github.com/wasm3/wasm3</a></em></p><p>由 Mozilla、英特尔、RedHat 和 Fastly 公司宣布成立字节码联盟(<em>Bytecode Alliance</em>)开发的 WebAssembly Micro Runtime(<em>Wamr</em>)也是一个非常优秀的虚拟机实现,其提供 AOT、JIT 等多种不同的优化手段,底层也是依赖 LLVM 后端的一些能力。</p><p>▪Wamr 的仓库:<em><a href="https://link.segmentfault.com/?enc=y09fa7NW5cbOr6ZXpHnx8Q%3D%3D.Vx%2F79WpQhi8ro2vuDmQDIAcNdUHGrCojvTgLZrXmUPgmSbO5BUklYRFNnsJMzDkJ" rel="nofollow">https://github.com/bytecodealliance/wa</a> sm-micro-runtime</em></p><p><strong>2、Rust 语言|Wasmer 和 Wasmtime</strong></p><p>Wasmer 和 Wasmtime 是 Rust 实现的两个流行的 WebAssembly 虚拟机。根据 2022 年 7 月的调查报告 (<em>300人提交问卷</em><em>)</em> 显示,来自字节码联盟的 Wasmtime 最流行、其次为 Wasmer。不过从长期看,作者推测 WasmEdge 将随着 Docker/wasm 成为浏览器外最流行的 Wasm 虚拟机实现。</p><p>▪Wasmtime 仓库:<em><a href="https://link.segmentfault.com/?enc=q6jtWzupfHGBtuigm9JWow%3D%3D.bzCLIXXWePHkPfvh3fH1m6fZJa%2BwWa%2Bi3qj6obGTq3ENsBN4jPei63zUuNk0Nuv7" rel="nofollow">https://github.com/bytecodealliance/wasmtime</a></em><br>▪Wasmer 仓库:<em><a href="https://link.segmentfault.com/?enc=qX9bkSAf8lOHIxspeYZBzQ%3D%3D.63GZqZcEkPj3lcy%2Fi0v4xshC%2BpVLvz7X1no0pu7kBOQ%3D" rel="nofollow">https://github.com/wasmerio</a></em></p><p><strong>3、Go 语言|WaZero</strong></p><p>WaZero 是纯 Go 语言实现的 WebAssembly 虚拟机,因此不需要依赖 CGo 特性。目前凹语言内置的就是 WaZero 虚拟机。</p><p>仓库地址:<em><a href="https://link.segmentfault.com/?enc=kSzUQ7ptx0ar05Neb3zzSg%3D%3D.RX5XlJ0LJjSUrLSpBI3ABBcQSP9KbBqNeMM4j2v4fMHs2FD9oJjSqk5lF4jfq0zm" rel="nofollow">https://github.com/tetratelabs/wazero</a></em></p><p>另外,国内张秀宏著的《WebAssembly 原理与核心技术<em>》</em>讨论了用 Go 语言如何实现 WebAssembly 虚拟机,感兴趣的读者可以参考。</p><p><strong>PART.5</strong></p><p><strong>支持 Wasm 的编程语言</strong></p><p>WebAssembly 允许开发者用几十种编程语言(<em>包括 AssemblyScript、C/C++、Rust、Golang、JavaScript 和凹语言等</em>)编写应用程序。</p><p>支持 Wasm 的编程语言主要分为 3 类:</p><ol><li>首先是专门为 WebAssembly 设计的新语言,比如 AssemblyScript 和凹语言等;</li><li>其次是将语言编译到 WebAssembly 目标平台,比如 C/C++、Rust、Golang 这类语言(<em>和第一类有一定重叠</em>);</li><li>最后是将语言的虚拟机或解释器编译到 WebAssembly 平台,比如 Lua、JavaScript、Ruby 和 Python 这些。</li></ol><p>除此之外,还有一些其它的领域语言也在支持 WebAssembly 平台。</p><p>支持 WebAssembly 的语言列表:<em><a href="https://link.segmentfault.com/?enc=KzluQO9fz4%2BU0Fp5hCO0GQ%3D%3D.bCrrqi22ul2J4j50nFEj11eLs7u2AFK3Tw0Ss1DRbd%2BwnF174Y14s5Bbq5QoSUS%2B" rel="nofollow">https://github.com/appcypher/awesome-wasm-langs</a></em></p><p><strong>1、JavaScript|</strong><strong>WebAssembly 替换的标</strong></p><p>JavaScript 开始其实是 WebAssembly 要替换的目标。但是随着 WasmEdge 等引擎支持 QuickJS 的解释器,JavaScript 逐渐变成了 WebAssembly 平台之上的最流行的编程语言。这里除了有 JavaScript 语言用户比较多的因素,同时 JavaScript 的单线程模型也非常契合 WebAssembly 的单线程模型(<em>只是相对于 Python 等支持多线程的脚本语言,套娃的性能损失至少 10 倍起</em>)。</p><p>JavaScript 和 WebAssembly 无限套娃的事情真在切实发生,同时 JavaScript 也失去了浏览器中的霸主地位,降级为普通公民。</p><p><strong>2、AssmblyScript|为</strong> <strong>WebAssembly 而生的 TypeScript</strong></p><p>AssemblyScript 是一个把 TypeScript 语法搬到 WebAssembly 的编译器。它目前是 WebAssembly 环境非常受欢迎的一个语言。</p><p>AssemblyScript 只允许 TypeScript 的有限功能子集,因此不需要花太多时间就可以上手。同时它与 JavaScript 非常相似,所以 AssemblyScript 使 Web 开发人员可以轻松地将 WebAssembly 整合到他们的网站中,而不必使用完全不同的语言。</p><p>下面是一个 AssemblyScript 程序,和 TypeScript 几乎是一样的:</p><pre><code>export function add(a:i32, b:i32):
return a + b;
}</code></pre><p>不过 AssemblyScript 只有 WebAssembly 支持的基本类型,而复杂的类型通过内置库实现。同时为了提供灵活的扩展能力,AssemblyScript 编译器提供了扩展能力。</p><p>AssemblyScript 主页:<em><a href="https://link.segmentfault.com/?enc=8%2BdGavzsoj%2Ffe93JsfsjZg%3D%3D.n5QbQJ86braq7pYhlt4EAxV1cRbfrUgwWKRvYvv7PCM%3D" rel="nofollow">https://www.assemblyscript.org/</a></em></p><p><strong>3、C/C++|</strong><strong>WebAssembly 为其而生</strong></p><p>C/C++ 是 WebAssembly 该技术前身 Emscripten 诞生时的初始目标。Emscripten 项目,尝试通过 LLVM 工具链将 C/C++ 语言编写的程序转译为 JavaScript 代码,在此过程中创建了 JavaScript 子集 asm.js,asm.js 仅包含可以预判变量类型的数值运算,有效的避免了 JavaScript 弱类型变量语法带来的执行效能低下的顽疴。</p><p>其中的核心魔法使 WebAssembly 和 C/C++ 采用相似的线性内存模型,提供为 JIT 提供了转化为相似代码的可能。</p><p><strong>4、Rust 语言|基于 LLVM 的输出 WebAssembly 能力</strong></p><p>Rust 和 Emscripten 都诞生于 Mozilla 公司,因此目前 WebAssembly 社区和 Rust 社区有着很大的重叠部分。很多 Rust 实现的 WebAssembly 虚拟机,同时 Rust 编译器借助 LLVM 的能力输出 WebAssembly 模块。</p><p>可以说 Rust 技术的发展和抱住 WebAssembly 这个大腿有极大的关系。当然,因为 Rust 兼容 C/C++ 内存模型同时又无 GC 依赖,使得 Rust 可以构造出非常轻量高效的 Wasm 模块。不过 Rust 本身的超高门槛也为初学者带来了极大的挑战。</p><p><strong>5、Go 语言|独立的 WebAssembly 后端</strong></p><p>Go 语言作为云计算等领域的主流语言,从 Go1.11 开始,WebAssembly 开始作为一个标准平台被官方支持,这说明了 Go 语言官方团队也认可了 WebAssembly 平台的重要性和巨大潜力。</p><p>目前 Go 语言社区已经有众多与 WebAssembly 相关的开源项目,比如有很多开源的 WebAssembly 虚拟机就是采用 Go 语言实现的。不过 Go 语言对 WebAssembly 被诟病的一个方面是官方生成的 Wasm 文件不是 wasi 规范,同时因为 GC 等特性导致 Wasm 体积比较大。</p><p>社区有个针对嵌入式环境等 TinyGo 变种,后端同样借助 LLVM 的能力输出 WebAssembly 模块。不过因为 LLVM 的依赖非常重,导致 TinyGo 的单个命令行将近 100MB、同样的原因导致无法方便在浏览器环境使用。可以说 TinyGo 本身并不 Tiny,只是其目标平台是针对 Tiny 的单片机和 Wasm 等平台。</p><p><strong>6、凹语言|</strong><strong>为 WebAssembly 而生的国产语言</strong></p><p>凹语言是为 WebAssembly 而设计的新语言,是国内 Gopher 发起的纯社区构建的开源国产编程语言项目。</p><p>同时凹语言也是国内第一个实现纯浏览器内编译、执行全链路的自研静态类型的编译型通用编程语言。凹语言不仅仅点亮了 Arduino Nano 33 开发板,同时也通过实现了 BrainFuck 虚拟机证明了其图灵完备的能力,最近还验证了通过凹语言开发 Web 版本贪吃蛇的能力。</p><p><strong>7、KCL|向 WebAssembly 迁移的领域语言</strong></p><p>Kusion 配置语言(<em>KCL</em>)是由来自蚂蚁的徐鹏飞负责设计的、基于约束的记录及函数语言。KCL 通过成熟的编程语言技术和实践来改进对大量繁杂配置比如云原生场景的编写,致力于构建围绕配置的更好的模块化、扩展性和稳定性,更简单的逻辑编写,以及更快的自动化集成和良好的生态延展性。</p><p>作为领域语言,KCL 目前也是基于 LLVM 的能力输出 WebAssembly 模块,通过 WebAssembly 模块良好的隔离性和跨平台特性,KCL 可以轻易实现在浏览器当中运行。<br>KCL 语言的主页:<em><a href="https://link.segmentfault.com/?enc=5%2FCKpb6Ei%2Fa818sSC129qw%3D%3D.Ap5AgDwxxmwW2i%2FIgXPVoYivksHSsUha9Shw7LgBjfY%3D" rel="nofollow">https://kcl-lang.io/</a></em></p><p><strong>8、其它支持 Wasm 的语言</strong></p><p>比如 Zig 等一些语言也支持 Wasm,本质上它们和 C/C++/Rust/TinyGo 一样,都是依赖 LLVM 的输出 Wasm 的能力。当然,也有一些采用类似 AssemblyScript 路线,通过 Binaryen 输出 Wasm。</p><p>还有一些特殊场景,比如 OPA 的 Rego 是独立实现输出 Wasm 的能力。Wasm 虽然和 C/C++ 采用相似的内存模型,但是依然有一些细微的差异,如果希望发挥其最大优势需要从语言设计和后端输出两个方便考虑,这也是很多新语言正在探索的方向。</p><p><strong>PART. 6</strong></p><p><strong>Wasm 的一些场景</strong></p><p><strong>1、Web 应用</strong></p><p>随着 WebAssembly 的成熟,Web 应用不再是 JavaScript 的天下。比如之前就有国外大牛基于 Wasm 技术将 Windows 2000 搬到了浏览器中。而像 AutoCAD 和谷歌地球这些重量级的应用均通过 WebAssembly 支持了浏览器。</p><p>当然,不仅仅是重量级的 Web 应用,随着 Wasm 原生编程语言的成熟,可以预期会有更多的其他语言开发的 Web 应用。</p><p>比如,下面是采用凹语言开发的贪吃蛇小游戏就是基于 WebAssembly:</p><p><img src="/img/remote/1460000043508676" alt="图片" title="图片"></p><p>贪吃蛇游戏在线地址:<a href="https://link.segmentfault.com/?enc=GV5czEDLmrzvWotT223EcA%3D%3D.FhbnZ48MP3Cod7Qk0usSzcPP5Tv0HAzLZZInyzMYhu4%3D" rel="nofollow">https://wa-lang.org/wa/snake/</a></p><p><strong>2、Web3 和元宇宙应用</strong></p><p>随着 Web3 和元宇宙概念的兴起,WebAssembly 也将作为其中的关键技术,甚至是基石技术。目前 Web3 相关的区块链行业有大量的技术基于 WebAssembly 构建,甚至专门定制 EWASM 技术标准。</p><p>而元宇宙作为数字化和现实完全融合的新社会生态,其底层的软件系统更是非常依赖纯开源软件和平台无关的通用技术,因此作者推测 GPL 开源协议和 WebAssembly 技术将会是元宇宙的两大关键支柱。</p><p><strong>3、Serverless 应用</strong></p><p>Serverless 强依赖高度优化的冷启动,Wasm 非常适合作为下一代无服务器平台运行时。SecondState、Cloudflare、Netlify 和 Vercel 等公司都支持通过其边缘运行时部署 WebAssembly 功能。</p><p>下图是 AWS Lambda 中的 WebAssembly Serverless 函数工作原理:</p><p><img src="/img/remote/1460000043508677" alt="图片" title="图片"></p><p>具体细节可以参考这个文章:<em><a href="https://link.segmentfault.com/?enc=EUGZdlKJ27kryOzycivilA%3D%3D.NUYyzV%2FZSm4ODQeea9Er5IMqhN7E9bn1NzliiCj2vjiyxAM%2Flige1HL6b%2BGhbsorLOs5rog%2BKIk5kJ0h5%2BIKIt%2BaVK8e80azX7lvH2f2AzoVbdeOcYXuo9wXX3hXOgbs" rel="nofollow">https://www.cncf.io/blog/2021/08/25/webassembly-serverless-fu...</a></em></p><p><strong>4、插件系统应用</strong></p><p>得益于 Wasm 的跨平台的特性,很多系统和框架在考虑通过 Wasm 开发插件系统。比如基于 eBPF 和 Wasm 技术实现给 Linux 打动态的补丁。</p><p>比如阿里云刚开源的 Higress 网关的插件系统也支持 Wasm,对比传统 Nginx 网关使用 lua 进行扩展,Wasm 的多语言和安全沙箱特性带来了革命性的变化。同时对比传统 Nginx 网关,修改 lua 代码后需要 reload 才能生效,Higress 可以实现插件的热分发和热加载,插件逻辑发生变化对流量完全无损,长连接也不会断开。</p><p>使用 Go 开发 Higress 插件可参考:<em><a href="https://link.segmentfault.com/?enc=1hF4OoJRiya2q%2Bc%2BmzLTnQ%3D%3D.CScTyoiC%2BNl8Ebo46jLznOc2ZESb9YL1%2FWA05%2FLt4x0tb%2Fio9wuWpf9%2BVtcSF3y4" rel="nofollow">https://higress.io/zh-cn/docs/user/wasm-go.html</a></em></p><p>比如蚂蚁开源的 MOSN (<em>Modular Open Smart Network</em>),是一款主要使用 Go 语言开发的云原生网络代理平台。</p><p>MOSN 就支持通过 Wasm 插件来扩展其能力。下图是 MOSN 插件的工作原理图:</p><p><img src="/img/remote/1460000043508678" alt="图片" title="图片"></p><p>MOSN 插件的细节可参考:<em><a href="https://link.segmentfault.com/?enc=Kvcbg0Cgoz91HXrrvs%2B6sw%3D%3D.YnOpu7wWbWj5ZzIigEvD174wSinCGooy%2BnPKwTEt%2F1i6Ejqgt22Sj5Xm5zLJ3AOL" rel="nofollow">https://mosn.io/blog/posts/mosn-wasm-framework/</a></em></p><p><strong>5、单片机应用</strong></p><p>Wasm 不仅仅应用在浏览器、云计算等行业,在边缘计算等嵌入式领域也有应用场景。比如 Wasm3 虚拟机就针对 arduino 提供的更精简的虚拟机,用户可以通过 Wasm 技术为不同的单片机开发应用。</p><p><img src="/img/remote/1460000043508679" alt="图片" title="图片"></p><p>比如可以通过凹语言结合 Wasm3-arduino 来开发 arduino 的例子,下图是本地模拟环境代码和执行效果图:</p><p><img src="/img/remote/1460000043508680" alt="图片" title="图片"></p><p>Wasm3-arduino 仓库:<em><a href="https://link.segmentfault.com/?enc=N%2FX9ddvUGCMEcrC2yPAdHQ%3D%3D.VELubkjMQSDFsmeg%2Fjn3VRYbWKzCUiX59mi6cMCMjiXUdKhcrXKps9eId%2B3i7PaM" rel="nofollow">https://github.com/wasm3/wasm3-arduino</a></em></p><p><strong>PART. 7</strong></p><p><strong>Wasm 教程推荐</strong></p><p>WebAssembly 属于这个新生态的根技术、而目前正是处于根技术生态的构建阶段。因此,这类推荐的更多是偏向 WebAssembly 规范、原理和实现的教程。我们希望当 WebAssembly 技术正在普及之后,用户可以通过流行的编程语言直接开发 WebAssembly 应用而不需要关系根技术的细节。</p><p><strong>1、《WebAssembly 规范》|2022</strong></p><p>WebAssembly 规范 1.0 草案在 2018 年发布,现在最新的 WebAssembly 2.0 在 2022 年发布。</p><p>WebAssembly 规范是市面上所有该技术的实现和实践的参与源头。任何希望追根溯源、获取最前沿的 WebAssembly 发展方向的同学不仅仅推荐精读该规范,甚至还建议跟踪规范的讨论和诞生的过程。</p><p><img src="/img/remote/1460000043508681" alt="图片" title="图片"></p><p>该文档并非正式出版的图书,目前规范只有在线电子版,建议自行打印。</p><p><strong>2、《WebAssembly 标准入门》|2018</strong></p><p>本书是本文作者和前同事于 2018 年合著,主要讲解了 WebAssembly 的基础知识,其内容涵盖了 Wasm 的历史背景、Wasm 中汇编语言和虚拟机指令、浏览器对 Wasm 的支持、其它高级语言对 Wasm 的支持等。</p><p><img src="/img/remote/1460000043508682" alt="图片" title="图片"></p><p>本书适合想要掌握 WebAssembly 技术、构建对应虚拟机工具、编程语言或希望了解底层细节的用户学习。</p><p><strong>3、《WebAssembly The Definitive Guide》|2021</strong></p><p>这是 Oreilly 出版的相对较新的 WebAssembly 专著,不仅仅覆盖了规范本身同时结合了主流编程语言的案例。</p><p><img src="/img/remote/1460000043508683" alt="图片" title="图片"></p><p>目前国内还没有中文版本,大家可以阅读英文版本。</p><p><strong>4、《WebAssembly 原理与核心技术》|2021</strong></p><p>这是国内虚拟机实现专家张秀宏写的一本讲述如何实现 WebAssembly 虚拟机的专著。它不仅对 WebAssembly 的工作原理、核心技术和规范进行了全面的剖析和解读,而且给出了实现 WebAssembly 解释器和 AOT 编译器的思路和代码。</p><p><img src="/img/remote/1460000043508684" alt="图片" title="图片"></p><p>对于希望尝试自己实现 WebAssembly 的同学建议阅读本书。</p><p><strong>PART. 8</strong> </p><p><strong>2023 年展望</strong></p><p>对于 WebAssembly 来说,2022 年是真正润物细无声开始落地的过程:从新的 2.0 标准到 Ruby、Python 两大主流脚本语言开始官方支持,从 SQLite3 开始官方支持、从 Docker 开始官方支持等,到为其而生的凹语言等,到真正的商业应用都有巨大的发展 (<em>而完全不是因为某个大佬的项目黄了就断言 Wasm 要凉的节奏</em>)。</p><p>在商业应用上,Figma 基于 WebAssembly 打造在浏览器中的高性能应用,此后被 Adobe 以 200 亿美元收购,而 Adobe 也在向浏览器迁移。此外,WebAssembly 也是云厂商、边缘计算和 Serverless 的候选人。</p><p>随着 WebAssembly 的普及,有一些相关技术流行趋势也日趋明朗化。作者做 2 个小小的趋势预测:</p><ol><li>首先是 WasmEdge 将成为浏览器外最流行的运行时;</li><li>其次是 JavaScript 将成为 WebAssembly 平台上最流行的编程语言。</li></ol><p>不过这只是 5 年内的短期预测,更长的发展趋势还需要看 WebAssembly 生态其他的基础设施和编程语言发展状态。</p><p>尽管目前 WebAssembly 发展喜人,但百废待兴仍有许多工作要做。我们希望大家更多的是参与到 WebAssembly 建设中去,而不是仅仅作为围观者。作为凹语言作者,我们希望在 2023 年真正解决语言的可用性和易用性的问题,让 WebAssembly 应用构建更加简单。</p><p>WebAssembly 作为一个新兴的赛道,作为一个基础设施必将带来更大的生态洗牌,这是一个值得关注和投入的方向,让我们一起携手共建 Wasm 原生时代。 </p><p><strong>本周推荐阅读</strong> </p><p><img src="/img/remote/1460000043508685" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=fYdURqaEcBs%2BrWoy3CzgAg%3D%3D.80dpzVZ4PB1t7wuSRaHpfYvQ0K1XI2033FTe%2F97R7aADR92WpnkvVDR%2B59UP7sS4vPtay8ZCNk2W%2Fh6CKMgmGNeetmOhWGeJT%2FBoQdNHk6iK0u%2BcxtQQRCnmm1DZA1aAj2n5qVeP%2BoF9eziKGs7C9WX9ReQ6B3%2BjJAczv8RbcCedgnpzAapWNTkagayO9jSngGvq7Eaf43tka81ImMn4MbXRtG%2FcdCNNeuFcLd0g07W4S642gOc1KREaWWKw29BpzGAr0aQhP7exTkW4xZGBVqFXfO0UZFXBz6ilgnQi2jHq3TW%2BUTDbbCzQ8zzvuEAFxgYFHlHbVnSW1%2FqhxnS02A%3D%3D" rel="nofollow">SOFARegistry | 聊一聊服务发现的数据一致性</a></p><p><img src="/img/remote/1460000043508686" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=YdLA9U9LRNy2%2Fe%2FmIY9O6w%3D%3D.CZczghXpjWGx%2Bfb%2BXwoy%2Fx5r9uviQZ81y4Jo4DRxUBw8UvtpTQ%2Fk3A5HPJoXDrI7CS9Cm%2F1z7vU2MXNOFRUDf%2FvrRTvFcfguJeGi1UsjUksolefmnSC1ALUnEnk05%2FOz8LHyk918dAe5Fi5ab0TbU%2FNU2qGezMR1BVqZnXGAKurb9TjN%2FYkYNi5LAT3yOrvO4YLD4Bp%2BXFoJEBiaMX0E5uWtp%2FEBvqNVrF3YW5fUzaznLddMqglLKceLj4L%2FrVNlNo%2FJ3cdKHdsq3vb%2BfHX1DVjIsWPNXMwN85jQB%2FnuOVsN9z6bU3K25bRnhXIwbxTW4B6qMHfZz2VaxPuKqpNEDg%3D%3D" rel="nofollow">SOFARegistry | 大规模集群优化实践</a></p><p><img src="/img/remote/1460000043508687" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=lbziUrOMuXHOv3W2KbSp8g%3D%3D.lA%2BHpU7wliE79XcfESuGE9TUTucYPOEpX4jcMDih3RWM3UyADhjRivKqZAlF0KHhhH2b5YzHyTu7l6uWmw3Wk3uldgIFjMOcSFSvFbptdSxT93GPaEg%2Bjqu3ndmb8wKQOc3EuzvSLrqgMmLi9n87%2FZqyBOyEexYxQguy3ajRBN8YMVo4ogthVBe5gkWqQZpKHYp1u5a5I2u%2FfHmLuyimg9U4z0TzG7pKjVgwszGzjf4rwedDBkswY74DDtXcHuCR%2FYN3xBgaja9g1uAU5SqDC3t7NCjGyZCU9YgLS%2FWIdmxmcDZJ9RzIUX6DLidzbbPq0AjMq5xzw0p17t%2BSLVHF7A%3D%3D" rel="nofollow">MOSN 反向通道详解</a></p><p><img src="/img/remote/1460000043508688" alt="图片" title="图片"><br><a href="https://link.segmentfault.com/?enc=oub0krpalDtZtPHlaGT7cg%3D%3D.G6lZMxsLi7sm0yPYm2071Stc0rBzhNq6ydHlEeMb%2BSaqEauPjAX8MgPscPrAId6AC2a5eQAAMgUilIkfrSrBrqY0yfrfxumxI1VJKpdZiEh%2F3k8UxvKaAMg%2B3ty%2BP%2FNxfHOfxBb9tiK%2B%2FD6W32jvMZWFqrh%2FC7VaPDq2skylW64R%2F%2BHA%2B3cM6oFsWQu45z9zjN3C2HWKN0gglA%2Fdh51f0rre06WgICAi%2BWNKnDdY8D%2B3xTRPsTcH17hHSCxJT2uOlOV41Q37c5QPwvRNU1v8%2BdPLYyWpJ0aGmM8zOZjjhJk%2Bkoi1u4PJWc4%2BqBaXW2clDS245UQpD8PWXgg3oVHUyw%3D%3D" rel="nofollow">如何看待 Dapr、Layotto 这种多运行时架构?</a></p>
Nydus 在约苗平台的容器镜像加速实践
https://segmentfault.com/a/1190000043487627
2023-03-01T16:10:58+08:00
2023-03-01T16:10:58+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043487629" alt="图片" title="图片"><br>文 | 向申约苗平台运维工程师 <br>关注云原生领域本文字数 <br>9574阅读时间24分钟</p><p>本文是来自向申同学的分享,介绍了其在 K8s 生产环境集群部署 Nydus 的相关实践。</p><p>Nydus 是蚂蚁集团,阿里云和字节等共建的开源容器镜像加速项目,是 CNCF Dragonfly 的子项目,Nydus 在 OCI Image Spec 基础上重新设计了镜像格式和底层文件系统,从而加速容器启动速度,提高大规模集群中的容器启动成功率。详情文档请参考如下地址:</p><p>Nydus 官方网站:<a href="https://link.segmentfault.com/?enc=3PkaZ9XsqgyWILtgXHv65A%3D%3D.SmsyV7UfPsFT8r%2BQkNh%2FRgqqqas%2FVK71lXvRWLM3oiw%3D" rel="nofollow">https://nydus.dev/Nydus</a> <br>Github:<a href="https://link.segmentfault.com/?enc=olZ71ZYUap5%2BBvt14uNTsg%3D%3D.y1HesfPyuRRIC1aMVuXc1CVpkYewgi%2FsF4bXhWSn91kzKDKdMRZoUNggF7QOrlxd" rel="nofollow">https://github.com/dragonflyoss/image-service</a></p><p><strong>PART.1</strong><br><strong>容器镜像的概念</strong></p><ol><li>容器镜像</li></ol><p>容器镜像有一个官方的类比,"生活中常见的集装箱",虽然拥有不同的规格,但箱子本身是不可变的(Immutable),只是其中装的内容不同。</p><p>对于镜像来说,不变的部分包含了运行一个应用软件(如 MySQL )所需要的所有元素。开发者可以使用一些工具(如 Dockerfile)构建出自己的容器镜像,签名并上传到互联网上,然后需要运行这些软件的人可以通过指定名称(如 example.com/my-app)下载、验证和运行这些容器。</p><ol start="2"><li>OCI 标准镜像规范在</li></ol><p>OCI 标准镜像规范出台之前,其实有两套广泛使用的镜像规范,分别是 Appc 和 Docker v2.2,但“合久必分,分久必合”,有意思的是两者的内容已经在各自的发展中逐步同化了,所以 OCI 组织顺水推舟地在 Docker v2.2 的基础上推出了 OCI Image Format Spec,规定了对于符合规范的镜像,允许开发者只要对容器打包和签名一次,就可以在所有的容器引擎上运行该容器。</p><p>这份规范给出了 OCI Image 的定义:<br>This specification defines an OCI Image, consisting of a manifest, an Image Index (optional), a set of filesystem layers, and a Configuration.</p><ol start="3"><li>容器的工作流程<br><img src="/img/remote/1460000043487630" alt="图片" title="图片"></li></ol><p>一个典型的容器工作流程是从由 Developers 制作容器镜像开始的(Build),然后上传到镜像存储中心(Ship),最后部署在集群中(RUN)。 </p><p><strong>PART.2</strong><br><strong>OCI 镜像格式</strong></p><p>通常所说的镜像文件其实指的是一个包含了多个文件的“包”,“包”中的这些文件提供了启动一个容器所需要的所有需要信息,其中包括但不限于,容器所使用的文件系统等数据文件,镜像所适用的平台、数据完整性校验信息等配置文件。当我们使用 Docker pull 或者 Nerdctl pull 从镜像中心拉取镜像时,其实就是在依次拉取该镜像所包含的这些文件。</p><p>Nerdctl 依次拉取了一个 Index 文件、一个 Manifest 文件、一个 Config 文件和若干个 Layer 数据文件。实际上,一个标准的 OCI 镜像通常就是由这几部分构成的。</p><p>其中,Layer 文件一般是 tar 包或者压缩后的 tar 包,其包含着镜像具体的数据文件。这些 Layer 文件会共同组成一个完整的文件系统(也就是从该镜像启动容器后,进入容器中看到的文件系统) 。</p><p>Config 文件是一个 JSON 文件。其中包含镜像的一些配置信息,比如镜像时间、修改记录、环境变量、镜像的启动命令等等。</p><p>Manifest 文件也是一个 JSON 文件。它可以看作是镜像文件的清单,即说明了该镜像包含了哪些 Layer 文件和哪个 Config 文件。</p><p>下面是一个 Manifest 文件的典型例子:</p><pre><code>
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:0584b370e957bf9d09e10f424859a02ab0fda255103f75b3f8c7d410a4e96ed5",
"size": 7636
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:214ca5fb90323fe769c63a12af092f2572bf1c6b300263e09883909fc865d260",
"size": 31379476
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:50836501937ff210a4ee8eedcb17b49b3b7627c5b7104397b2a6198c569d9231",
"size": 25338790
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:d838e0361e8efc1fb3ec2b7aed16ba935ee9b62b6631c304256b0326c048a330",
"size": 600
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:fcc7a415e354b2e1a2fcf80005278d0439a2f87556e683bb98891414339f9bee",
"size": 893
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:dc73b4533047ea21262e7d35b3b2598e3d2c00b6d63426f47698fe2adac5b1d6",
"size": 664
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:e8750203e98541223fb970b2b04058aae5ca11833a93b9f3df26bd835f66d223",
"size": 1394
}
]
}</code></pre><p>Index 文件也是一个 JSON 文件。它是可选的,可以被认为是 Manifest 的 Manifest。试想一下,一个 tag 标识的镜像,比如 Docker.io/library/nginx:1.20 ,会针对不同的架构平台 (比如 Linux/amd、Linux/arm64 等等) 有不同的镜像文件,每个不同平台的镜像文件都有一个 Manifest 文件来描述,那么我们就需要有个更高层级的文件来索引这多个 Manifest 文件。</p><p>比如,Docker.io/library/nginx:1.20 的 Index 文件就包含一个 Manifests 数组,其中记录了多个不同平台的 Manifest 的基本信息:</p><pre><code>{
"manifests": [
{
"digest": "sha256:a76df3b4f1478766631c794de7ff466aca466f995fd5bb216bb9643a3dd2a6bb",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:f46bffd1049ef89d01841ba45bb02880addbbe6d1587726b9979dbe2f6b556a4",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v5"
},
"size": 1570
},
{
"digest": "sha256:d9a32c8a3049313fb16427b6e64a4a1f12b60a4a240bf4fbf9502013fcdf621c",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 1570
},
{
"digest": "sha256:acd1b78ac05eedcef5f205406468616e83a6a712f76d068a45cf76803d821d0b",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 1570
},
{
"digest": "sha256:d972eee4f12250a62a8dc076560acc1903fc463ee9cb84f9762b50deed855ed6",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:b187079b65b3eff95d1ea02acbc0abed172ba8e1433190b97d0acfddd5477640",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "mips64le",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:ae93c7f72dc47dbd984348240c02484b95650b8b328464c62559ef173b64ce0d",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:51f45f5871a8d25b65cecf570c6b079995a16c7aef559261d7fd949e32d44822",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "s390x",
"os": "linux"
},
"size": 1570
}
],
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}
</code></pre><p><strong>PART.3</strong><br><strong>OCI 镜像所面临的问题</strong><br><img src="/img/remote/1460000043487631" alt="图片" title="图片"></p><ol><li>启动容器慢</li></ol><p>我们注意到镜像层需要全部堆叠后,容器才能看到整个文件系统视图,所以容器需要等到镜像的每一层都下载并解压之后才能启动。有一篇 FAST 论文研究分析[1] 说镜像拉取占了大约容器 76% 的启动时间,但却只有 6.4% 的数据是会被容器读取的。这个结果很有趣,它激发了我们可以通过按需加载的方式来提高容器启动速度。另外,在层数较多的情况下,运行时也会有 Overlay 堆叠的开销。</p><p>一般来说容器启动分为三个步骤:<br>下载镜像;<br>解压镜像;<br>使用 Overlayfs 将容器可写层和镜像中的只读层聚合起来提供容器运行环境。</p><ol start="2"><li>较高的本地存储成本</li></ol><p>每层镜像是由元数据和数据组成的,那么这就导致某层镜像中只要有一个文件元数据发生变化,例如修改了权限位,就会导致层的 Hash 发生变化,然后导致整个镜像层需要被重新存储,或重新下载。</p><ol start="3"><li>存在大量相似镜像</li></ol><p>镜像是以层为基本存储单位,数据去重是通过层的 Hash,这也导致了数据去重的粒度较粗。从整个 Registry 存储上看,镜像中的层与层之间,镜像与镜像之间存在大量重复数据,占用了存储和传输成本。</p><p><strong>PART.4</strong><br><strong>Nydus 镜像解决方案</strong></p><p>Nydus 镜像加速框架是 Dragonfly[2](CNCF 孵化中项目)的子项目。它兼容了目前的 OCI 镜像构建、分发、运行时生态。Nydus 运行时由 Rust 编写,它在语言级别的安全性以及在性能、内存和 CPU 的开销方面非常有优势,同时也兼具了安全和高可扩展性。<br><img src="/img/remote/1460000043487632" alt="图片" title="图片"></p><ol><li>Nydus 基础架构</li></ol><p>Nydus 主要包含一个新的镜像格式,和一个负责解析容器镜像的 FUSE 用户态文件系统进程。</p><p><img src="/img/remote/1460000043487633" alt="图片" title="图片"></p><ol start="2"><li>Nydus 工作流程<br><img src="/img/remote/1460000043487634" alt="图片" title="图片"></li></ol><p>Nydus 镜像格式并没有对 OCI 镜像格式在架构上进行修改,而主要优化了其中的 Layer 数据层的数据结构。</p><p>Nydus 将原本统一存放在 Layer 层的文件数据和元数据 (文件系统的目录结构、文件元数据等) 分开,分别存放在 “Blob Layer” 和 “Bootstrap Layer” 中。并对 Blob Layer 中存放的文件数据进行分块 (Chunk) ,以便于懒加载 (在需要访问某个文件时,只需要拉取对应的 Chunk 即可,不需要拉取整个 Blob Layer) 。</p><p>同时,这些分块信息,包括每个 Chunk 在 Blob Layer 的位置信息等也被存放在 Bootstrap Layer 这个元数据层中。这样,容器启动时,仅需拉取 Bootstrap Layer 层,当容器具体访问到某个文件时,再根据 Bootstrap Layer 中的元信息拉取对应 Blob Layer 中的对应的 Chunk 即可。</p><ol start="3"><li>Nydus 优势</li></ol><ul><li>容器镜像按需下载,用户不再需要下载完整镜像就能启动容器。 </li><li>块级别的镜像数据去重,最大限度为用户节省存储资源。 </li><li>镜像只有最终可用的数据,不需要保存和下载过期数据。 </li><li>端到端的数据一致性校验,为用户提供更好的数据保护。 </li><li>兼容 OCI 分发标准和 artifacts 标准,开箱即可用。 </li><li>支持不同的镜像存储后端,镜像数据不只可以存放在镜像仓库,还可以放到 NAS 或 者类似 S3 的对象存储上。 </li><li>与 Dragonfly 的良好集成。<br> <br><strong>PART.5</strong><br><strong>Nydus 在约苗生产实际运用</strong></li></ul><p>约苗平台作为国内领先的疾病预防信息与服务平台,以疫苗预约服务为核心,提供包括疫苗预约、疾病防控科普、“宫颈癌&乳腺癌”筛查预约等专业、全面的疾病预防信息与服务。</p><p>截止 2023 年 2 月约苗平台累计注册用户 3700 万+ 人,覆盖 28 个省及直辖市, 200+ 地级市,关联全国社区公共卫生服务机构 4000+ 家,提供疫苗预约&订阅服务 1.1 亿 + 次。 </p><p>约苗业务全部基于 Kubernetes 进行微服务构建,在 Kubernetes 平台上已经平稳运行了超过 4 年时间,并且紧随 Kubernetes 的版本迭代及时更新。约苗的集群规模超过 60 个 Node 节点,目前相关服务容器 POD 已经超过了 1000+,同时每天更有上万个临时 Cronjob 类型的 POD 进行创建和销毁。对平台的运维发布的效率有较高的要求。</p><ol><li>问题</li></ol><p>Kubernetes 拉取镜像时间非常慢,在沿用 OCI 镜像时通过观察,镜像拉去时间可达 30s。</p><ol start="2"><li>容器启动慢</li></ol><p>通过线上观察,一个 POD 从创建到准备就绪需要等待 30s 甚至更多,甚至节点没有缓存,时间将会更久。</p><ol start="3"><li>更新迭代块</li></ol><p>在更新迭代中,每次批量更新多个服务,迭代周期短而频繁,在更新多个服务时镜像仓库压力大。随着以上问题的产生,经过多方面的调研以及相关测试,公司决定采用开源项目 Nydus 进行对当前业务优化。</p><p><strong>PART.6</strong><br><strong>Nydus 部署实践</strong></p><p>Nydus 镜像加速,可以直接对接 OCI 镜像,同时 Containerd 也支持 Nydus 插件,识别 Nydus 镜像,一般在微服务场景下,使用 CICD ,我们需要在 Docker 打包镜像上部署 Nydus 转换镜像的服务,镜像转换后直接会在 Harboar 仓库生成 Nydus 的镜像,我们这里是用的 CICD 使用的 Jenkins,这里我就直接把服务部署在 Jenkins 的物理机上。</p><ol><li>下载相关组件</li></ol><p>下载链接:<a href="https://link.segmentfault.com/?enc=yKPyIZR5nFrFkkgrjIU2EA%3D%3D.PvEpMkSngtZDYjXlLSLx%2Fl3pUyZtM3uVW0f9aqPvMh%2BlKneDRheQFt9sJfMlyW%2BaKT36LyfXwQVlBRpxqZqYVg%3D%3D" rel="nofollow">https://github.com/dragonflyoss/image-service/releases</a></p><pre><code>cd /nydus-static
sudo install -D -m 755 nydusd nydus-image nydusify nydusctl nydus-overlayfs /usr/bin</code></pre><ol start="2"><li>OCI 镜像转换 Nydus</li></ol><pre><code>nydusify convert --source dockerharboar/nginx:1.2 --target dockerharboar/nginx:1.2-nydus</code></pre><p>注意: </p><ul><li>Source 这里表示源 Docker-Harboar 仓库的镜像,这个镜像必须私有仓库已经存在。</li><li>Target 这里表示将源仓库镜像转换为 Nydus 镜像。</li></ul><p>当使用这条命令后,镜像仓库在同一个目录层级会生成两份镜像,一份源 OCI 镜像,一份 Nydus 镜像。</p><p><strong>PART. 7</strong><br><strong>Nydus 对接 </strong></p><p>K8s 集群K8s 集群使用的运行时为 Containerd ,而Containerd 也支持使用插件 Nydus Snapshotter 来识别 Nydus 镜像,同时在使用 Nydus 功能时, Nydus 也是支持原生的 OCI 镜像,只是没有按需加载相关功能。</p><ol><li>K8s 集群节点部署</li></ol><p>Nydus官方说明:<a href="https://link.segmentfault.com/?enc=Isxvv8NG07MMtgltQHWDNg%3D%3D.W4i%2BWncTL3MSvlSksaA8brQiNxxpkk3nVn2dLN4vFhX9y5RVuO%2B6MHkPyBYsD%2FlzzzCHl2Lo3wqhc43vdOyHCLhNN%2B3%2BHb4ZQSLx6DTIaNV%2FCVverRe5r8GL%2BXcQZ6QO" rel="nofollow">https://github.com/dragonflyoss/image-service/blob/master/docs/containerd-env-setup.md</a></p><p>注意:要使用 Nydus 功能,K8s 的每个 Node 节点都需要部署 Nydus Snapshotter,除开 K8s-Master 节点。</p><p>下载安装包:<br><a href="https://link.segmentfault.com/?enc=EZ9sHP7ImxT3lATiAnoObw%3D%3D.FP0KBoCjNhukA7ulURLQtymluvhlsQMyK4GAUInEJfv30L8C5Z27ueD6MeLv4Js967ykTLbDHl9kN5%2B3QxK3Xg%3D%3D" rel="nofollow">https://github.com/dragonflyoss/image-service/releases</a><br><a href="https://link.segmentfault.com/?enc=uGLnuSyglhimAlvkSh%2FpVA%3D%3D.QY8QgN3Gp0qnG0aUpA0wIVojtnQvijhiJ2NWSammnXU6idQRM%2B9ZlP3pe6v2%2FfBlNOTuTUml5gTLnsAn1kCTrg%3D%3D" rel="nofollow">https://github.com/containerd/nydus-snapshotter/releases</a></p><pre><code>tar -xf nydus-snapshotter-v0.5.1-x86_64.tgz
tar -xf nydus-static-v2.1.4-linux-amd64.tgz</code></pre><pre><code>安装相关软件
sudo install -D -m 755 nydusd nydus-image nydusify nydusctl nydus-overlayfs /usr/bin
sudo install -D -m 755 containerd-nydus-grpc /usr/bin</code></pre><pre><code>创建必要目录
mkdir -p /etc/nydus && mkdir -p /data/nydus/cache && mkdir -p $HOME/.docker/</code></pre><pre><code>
创建nydus配置文件
sudo tee /etc/nydus/nydusd-config.fusedev.json > /dev/null << EOF
{
"device": {
"backend": {
"type": "registry",
"config": {
"scheme": "",
"skip_verify": true,
"timeout": 5,
"connect_timeout": 5,
"retry_limit": 4
}
},
"cache": {
"type": "blobcache",
"config": {
"work_dir": "/data/nydus/cache"
}
}
},
"mode": "direct",
"digest_validate": false,
"iostats_files": false,
"enable_xattr": true,
"fs_prefetch": {
"enable": true,
"threads_count": 4
}
}
EOF</code></pre><pre><code>增加docker-harboar认证
sudo tee $HOME/.docker/config.json << EOF
{
"auths": {
"docker-harboarxxx": {
"auth": "xxxxxx"
}
}
}
EOF
chmod 600 $HOME/.docker/config.json
docker-harboarxx #私有仓库地址
auth 里是 base64 编码的 user:pass</code></pre><p>2. 启动 Nydus</p><pre><code>cd /data/nydus
nohup /usr/bin/containerd-nydus-grpc --config-path /etc/nydus/nydusd-config.fusedev.json --log-to-stdout &</code></pre><pre><code>验证nydus是否支持
ctr -a /run/containerd/containerd.sock plugin ls | grep nydus</code></pre><ol start="4"><li>修改 Containerd</li></ol><pre><code>containerd配置文件新增
[proxy_plugins]
[proxy_plugins.nydus]
type = "snapshot"
address = "/run/containerd-nydus/containerd-nydus-grpc.sock"
[plugins."io.containerd.grpc.v1.cri".containerd]
snapshotter = "nydus"
disable_snapshot_annotations = false</code></pre><ol start="5"><li>重启 Containerdsudo </li></ol><p><code>sudo systemctl restart containerd</code></p><p><strong>PART.8</strong><br><strong>最终数据测试结果使用原生 OCI 镜像</strong></p><p>使用原生 OCI 镜像</p><p><img src="/img/remote/1460000043487635" alt="图片" title="图片"></p><p>使用 Nydus 镜像</p><p><img src="/img/remote/1460000043487636" alt="图片" title="图片"></p><p>POD 从 Create 到 Ready:OCI -> 20s<br>POD 从 Create 到 Ready:Nydus -> 13s</p><p>目前业务镜像尺寸并不大,大约 200MB,使用 Nydus 已有提升效果,在使用超大镜像的场景,例如 AI 计算等,Nydus 能带来的加速效果会非常的明显。 </p><p><strong>PART.9</strong><br><strong>总结与未来期望</strong></p><p>Nydus 是来自 CNCF 的优秀开源项目,更进一步说,约苗也将继续对该项目进行更多投入,并与社区展开深入合作,使得约苗平台变得更加强大和可持续。云原生技术是基础设施领域的一场革命,尤其是在弹性和无服务器方面,我们相信 Nydus 一定会在云原生生态中扮演重要角色。</p><p>相关链接</p><p>[1] 《Fast Distribution With Lazy Docker Containers》<a href="https://link.segmentfault.com/?enc=qsQFLfWzfhZkRX9%2FKZSR7Q%3D%3D.pMwM%2B%2BTYSBEXDnaEC8amcUZrJKPR2aaBvYUSpLxH3DH%2FDa6PHSzeS3oJ4%2BuQENWEwbArLXSgDpVcsZzU1U%2F%2BZhsBsZMp9zYErAmUbV7QCKA%3D" rel="nofollow">https://www.usenix.org/conference/fast16/technical-sessions/p...</a><br>[2] Dragonflyh<br><a href="https://link.segmentfault.com/?enc=CYMalDOFKpIcJemoVLvdnA%3D%3D.U35gzbG2r%2BGZDRKupB5zFOdloTQYRdmgBhEO1q2INkIsinttZVo2DKY1Lcw94yiz" rel="nofollow">https://github.com/dragonflyoss/image-service</a></p>
Tongsuo|铜锁再获 IOS 和 Linux 平台商用密码认证资质
https://segmentfault.com/a/1190000043373822
2023-02-01T17:07:34+08:00
2023-02-01T17:07:34+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043373824" alt="图片" title="图片"> </p><p>文|杨洋(花名:凯申 )</p><p>铜锁开源密码库创始人蚂蚁集团高级技术专家 </p><p>文|张成龙(花名:刻一)</p><p>蚂蚁集团技术专家 </p><p><strong>本文 1657 字 阅读 5 分钟</strong></p><hr><p>点击查看原文<a href="https://link.segmentfault.com/?enc=BBVLMaw1EysW2FWjt62Wag%3D%3D.ayIp48OBr9HKH0oWyf2lo43l78XmKDolK9Z3gE0CchHEVqUE7WnQlO0%2FgRo7A%2FKkL7ll5yai%2FvYvsReX7zm9LA%3D%3D" rel="nofollow">《Tongsuo|铜锁再获 IOS 和 Linux 平台商用密码认证资质》</a></p><hr><p>2022 年底,我们借助文章<a href="https://link.segmentfault.com/?enc=WCOzX6NSwXk%2BQxXgLOa%2FLQ%3D%3D.%2BvOkZxpBotrr2WDwhSehTKr9DD1D4MsSIBY1qoKnpoRmRuLAL28LUk3tLxS%2F3eLLyZtBy3Hh1V%2FJK19EajBGOZZUQeA1WIW16dSnes1orKvY7PXjX9xnE%2Fs0gtCyZ9R4VCxy6o69kl6jDFlZOxDwBu7bP1cBMAL0ejHTrNh2ryCUqOQrLZ8sLYT1hEWd0GAf4za18k1Shd3fYJuziO2DG%2FvZJpA%2B8GF9jQsDWvLPtffmax1aNkwC6rNvFjq3JT5%2FmVey%2FHdfGozd6OkBAq0vTqcU6616PBayr0yjwDjenaM%3D" rel="nofollow">《Tongsuo | 全球首个商用密码产品认证的密码学开源项目》</a>,向外界公布铜锁密码库获得了软件密码模块安全一级的资质,也就是铜锁的 Android 平台一级资质。</p><p>2023 兔年伊始,铜锁再次获得了两个商用密码认证证书,即《BabaSSL IOS 端软件密码模块》和《应用安全软件密码模块(Linux 版)》,分别对应 IOS 平台和 Linux 平台,同先前获得的 Android 平台资质一样,此次获得的两个国密资质也无偿开放给铜锁的用户使用。</p><p>自此,铜锁开源密码库集齐了 Android、IOS 两个移动客户端和更常用的 Linux 平台的商用密码/国密资质,用户可以集成铜锁密码模块于自己的产品和系统中,实现对商用密码技术使用的合规。</p><p><img src="/img/remote/1460000043373825" alt="图片" title="图片"></p><p>全部资质 PDF 和高清图片版点击最下方“阅读原文”或输入下方链接即可下载。</p><p><em><a href="https://link.segmentfault.com/?enc=lv1VBqevaLr2kQf0lCfDrw%3D%3D.5r3iiZbD0zyBjrOkB45jTTAFSYpKgbIbIk9TUrDxElzKsxAK7zySwFgaNv6TLbCbZHlV%2BkN14ZJJDji2FaMjdQ%3D%3D" rel="nofollow">https://www.yuque.com/tsdoc/m...</a></em></p><hr><p><strong>铜锁密码库的资质情况说明</strong></p><p>截止 2023 年 1 月底,铜锁密码库一共取得了国家密码管理局商用密码检测中心颁发的三个商用密码产品认证证书,也即俗称的国密资质。这三个资质均属于 GM/T 0028《密码模块安全技术要求》安全一级。</p><p><strong>BabaSSL 移动端软件密码模块</strong></p><p>适用平台:Android</p><p>产品名称和版本号:ANTGROUP-BabaSSL-MC 8.2.1</p><p>持有单位:蚂蚁科技集团股份有限公司</p><p>颁发日期:2022.11.30</p><p>有效期至:2027.11.29</p><hr><p><strong>BabaSSL IOS 端软件密码模块</strong></p><p>适用平台:IOS</p><p>产品名称和版本号:ANTGROUP-BabaSSL-MIOSC 8.3.0</p><p>持有单位:蚂蚁科技集团股份有限公司</p><p>颁发日期:2023.1.17</p><p>有效期至:2028.1.16</p><hr><p><strong>应用安全软件密码模块(Linux 版)</strong> </p><p>适用平台:Linux</p><p>产品名称和版本号:AS-TSCM 8.3.1</p><p>持有单位:蚂蚁科技集团股份有限公司</p><p>颁发日期:2023.1.16</p><p>有效期至:2028.1.15</p><p>关于以上国密资质对应的铜锁产品开源代码的编译和使用,请加入铜锁用户交流群咨询,钉钉搜索群号:<strong>44810299</strong>。</p><hr><p><strong>什么是铜锁/Tongsuo?</strong></p><p>铜锁(Tongsuo)是一个提供现代密码学算法和安全通信协议的开源基础密码库,为存储、网络、密钥管理、隐私计算等诸多业务场景提供底层的密码学基础能力,实现数据在传输、使用、存储等过程中的私密性、完整性和可认证性,为数据生命周期中的隐私和安全提供保护能力。 </p><p>铜锁诞生于蚂蚁集团并广泛的应用在蚂蚁集团内部以及外部的多种业务当中,提供了 TLS、数据存储、国密合规等关键的密码学相关能力,确保了各项业务平稳、安全、合规的运行。</p><p>铜锁同时还在前沿密码学领域进行了支持,包括隐私计算场景下所需的多种半同态加密算法以及为了应对量子计算而产生的后量子密码学算法等。</p><p><img src="/img/remote/1460000043373826" alt="图片" title="图片"></p><p>铜锁做为国内稀缺的密码学开源项目,填补了相关领域产品的空白,是我国建设国产密码学开源大生态、解决密码学技术和产品供应“卡脖子”问题、发展前沿密码学技术的关键一环。同时基于支付宝海量的用户场景,其性能和稳定性也达到了互联网生产级别。 </p><p>铜锁开源项目的前身是 BabaSSL 开源密码库。2022 年, BabaSSL 更名为铜锁,详见:</p><p>《<a href="https://link.segmentfault.com/?enc=4FBWLyfVto7gS7cTswlPog%3D%3D.AEH7ktQY5OkmnIsRau7LNMs%2BpL3XdPAQ82CWoplyapzQz1fHsGLPwAR%2BrA3duwgXoZf8CN1P%2Fs1chzpQMVrQinH2t3YCAWOUXi1si9qWjgDYUD3AGNxV5BCNvegLS9qE8ZgE7qBC3408A3wXA5i5zev%2BT6LaTVChVXgSGnP1HRNagyGcKgi%2Fnm8J3i2aZhMQh39gbj3Fs%2BRyNj%2B2KKsd2xGky7vfQvliEGtbzTsM%2FNzsHunFYs7lUfo%2F72r3tT7xxhyXiTTeJNG%2BRUSA0yEyIjwp0QRsMmeBBoFmqy4uJMc%3D" rel="nofollow">你好,我的新名字叫“铜锁/Tongsuo”</a>》。</p><p>铜锁项目于 2022 年通过开放原子开源基金会技术委员会 TOC 的答辩,目前正处于向开放原子开源基金会的捐赠流程中。</p><p>钉钉用户交流群群号:<strong>44810299</strong></p><p><strong>了解更多...</strong></p><p><strong>铜锁/Tongsuo Star 一下✨:</strong> <br><em><a href="https://link.segmentfault.com/?enc=gbQ4VOWgSZ05Kuo7vjKZGA%3D%3D.b7o3CURKn8uuS0qk78LEByc9KPH5aU8wBUQp2XcK8SbsExsoTz%2BFnU9onk3WSwkj" rel="nofollow">https://github.com/Tongsuo-Pr...</a></em></p>
Nydus 加速镜像一致性校验增强
https://segmentfault.com/a/1190000043370354
2023-01-31T18:08:11+08:00
2023-01-31T18:08:11+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p>导言:</p><blockquote>GitLink 编程夏令营是在 CCF 中国计算机学会指导下,由 CCF 开源发展委员会(CCF ODC)举办的面向全国高校学生的暑期编程活动。<br>这是去年(2022)的夏令营活动中,王瑞同学参加 Nydus 开源项目的总结,主要介绍了为 Nydus 支持镜像与文件系统一致性校验所做的相关工作。</blockquote><h2>Nydus 简介</h2><p>Nydus 是 CNCF 孵化项目 Dragonfly 的子项目,它提供了容器镜像,代码包,数据分析按需加载的能力,无需等待整个数据下载完成便可开始服务。 </p><p>Nydus 在生产环境已经支撑了每日百万级别的加速镜像容器创建,在启动性能,镜像空间优化,网络带宽效率,端到端数据一致性等方面相比 OCIv1 格式有着巨大优势,并可扩展至例如 NPM 包懒加载等数据分发场景。 </p><p>目前 Nydus 由蚂蚁集团,阿里云,字节跳动联合开发,Containerd,Podman 社区接受了 Nydus 运行时作为其社区子项目,也是 KataContainers 以及 Linux v5.19 内核态原生支持的镜像加速方案。</p><h2>Nydus 架构及原理</h2><p>OCI 容器镜像是当前容器镜像的实现标准。它采用了分层的设计,每个镜像可包含多个镜像层。新层包含的是在旧层的基础上,新增加或者修改的文件或者被删除的文件。这种设计方式比较简单,不过也有着一定的缺陷。如需要镜像层全部堆叠后才能看到整个文件系统的视图,但不是所有数据都会被读取;同时可能已经删除或者被修改旧层中的文件,但是仍需要完整地下载和解压旧层;文件元数据修改导致整个镜像层被重新存储等。 Nydus 兼容目前的 OCI 生态,旨在通过细粒度的数据分割、去重和按需加载机制加速容器 的启动和分发,同时降低资源的消耗。 </p><p>Nydus 的整体架构如下图所示。它可以通过 FUSE 给 runc 容器提供运行时的按需加载能力,也可以通过 virtiofs 承载 FUSE 协议,给 Kata Containers 等基于 VM 的容器运行时提供按需加载的能力。它可以从容器 Registry,OSS,NAS,Dragonfly supernode 等多个镜像源拉取镜像,同时内部会有本地的缓存加速容器的创建。 </p><p><img src="/img/remote/1460000043370356" alt="img" title="img"></p><p><img src="/img/remote/1460000043370357" alt="img" title="img"> </p><p>在用户空间文件系统,Nydus 采用了数据和元数据分离的设计思想,元数据的修改不会导致整个镜像层的更新。原先的镜像层只存储文件的数据部分,同时数据被分块存储。拉取镜像是不需要拉取整层,只需要拉取所需文件对应的数据块即可。这也使得层与层之间,镜像与镜像之间共享数据块更加容易。上图展示了 Nydus 数据和元数据的存储格式。其中元数据以 merkle tree 的形式存储在 bootstrap 中,包含了容器启动所需要的信息。数据以 1MB 分块存储,不同镜像可以共享同一数据块。</p><h2>Nydus 镜像校验意义及流程</h2><p>Nydus 镜像在构建完成后,由于网络、磁盘等故障或者镜像被恶意修改,无法保证容器启动前镜像是合法的,所以需要对镜像的格式进行校验。当前的校验使用 nydusify 工具。主要分为三个部分:</p><ol><li>对 Nydus 镜像的 bootstrap 进行校验,会通过 BootstrapRule 调用 nydus-image 二进制文件。nydus-image 首先检查 bootstrap 的 SuperBlock 格式是否正确,然后会从根结点开始按照文件系统层级结构检查文件或者目录的 inode 是否合法或被修改。</li><li>对镜像的 manifest 进行校验,会通过 ManifestRule 检验 Nydus 的 manifest 是否合法,ImageConfig 是否与原始 OCI 镜像一致等。</li><li>对镜像进行文件系统校验,会通过 FilesystemRule 分别挂载原始 OCI 镜像和 Nydus 镜像,然后进行校验。对于原始镜像,会使用 docker pull 拉取镜像,然后指定 lowerdir 和 upperdir,通过 OverlayFS 挂载 Rootfs;对于 Nydus 镜像,会使用 Nydusd 挂载。挂载完成后,会分别遍历两个目录,比较元数据和数据是否一致。</li></ol><p>目前 Nydus 的校验方式仍有一定的限制,如元数据检查不完全,需要 docker 拉取镜像等。该项目旨在增强 nydusify 和 nydus-image 的校验功能,使校验更加易用和全面。</p><h2>文件系统校验方案</h2><p>该项目当前分为以下三部分:</p><ol><li>当前 nydusify check 在应用 FilesystemRule 进行校验时,对于文件元数据只检查文件路径、大小、模式和权限位以及 xattrs 是否和原始镜像一致,同时对文件数据用 blake3 计算得到哈希值并进行比较。但是由于校验内容不完整,可能会出现元数据不一致校验通过的情况。故对该结构体添加 dev、rdev、symlink、project id、uid、gid、nlink、ctime 等字段,实现对文件元数据更全面的检查。</li></ol><p><img src="/img/remote/1460000043370358" alt="img" title="img"></p><ol start="2"><li>当前 nydusify check 在应用 FilesystemRule 进行校验时,需要手动指定 source 和 Backend Type, Backend Config 才能成功应用 Nydusd 挂载并进行文件系统校验,在校验数据时,也会再次检查 Backend Type 是否指定。在大多数情况下,Backend Type 为 Registry,Backend Config 可以通过查看 Registry 的 config 文件获取相关信息,如 http.addr 字段获取地址,auth 字段获取认证信息等获取。因而用户在很多情况下并不需要手动输入上述参数。该任务旨在简化该命令,实现 Backend Type,Backend Config 的自动推断,使得用户更方便地进行校验。</li></ol><p><img src="/img/remote/1460000043370359" alt="img" title="img"></p><p>(3) 当前 nydusify check 在应用 FilesystemRule 进行校验时,需要用户安装 docker,因为要使用 docker pull 命令拉取镜像。在没有 docker 的环境下,无法完成校验。可以修改该部分代码,手动下载、解压镜像,并使用 OverlayFS 挂载,从而去除对 docker 的依赖。 </p><p><img src="/img/remote/1460000043370360" alt="img" title="img"></p><h2>文件系统校验实现细节</h2><h3>增加校验字段</h3><p>该部分的实现较为简单。首先在原 Node 结构体增加 rdev, symlink, uid, gid, mtime 等字段。 </p><p><img src="/img/remote/1460000043370361" alt="img" title="img"></p><p>然后在遍历文件系统时,使用 Readlink 获取文件的软链接,通过 Lstat 系统调用获取 文件更详细的元数据信息(rdev, uid, gid, mtime 等),从而在进行比较时增加对上述字段的校验。值得注意的是 dev 不同是正常的,nlink 由于 OverlayFS 的问题无法进行校验。此外,还需要修改异常错误信息,从而遇到不一致时能够打印完整的文件元数据信息。 </p><p><img src="/img/remote/1460000043370362" alt="img" title="img"></p><h3>简化校验参数</h3><p>该部分需要实现 Backend Type 和 Backend Config 的自动推断,即如果镜像存储在 registry 中,用户无需指定上述两个参数即可完成校验。 </p><p><img src="/img/remote/1460000043370363" alt="img" title="img"></p><p>首先,我们需要添加上述结构体,即镜像源为 Registry 时的 Backend Config。对于 FilesystemRule 结构体,还需添加 Target 和 TargetInsecure 字段,用于填充 Backend Config。 </p><p><img src="/img/remote/1460000043370364" alt="img" title="img"></p><p>在挂载 Nydus 镜像时,我们需要正确填充 Nydusd 的 config,其中便包含 Backend Config 和 Backend Type。因此我们对用户传入的参数进行判断,如果用户没有传入 Backend Type,那么我们默认镜像源为 registry,如果没有传入 Backend Config,那么我们通过 target 提取 host 和 repo,然后读取 docker 的 config 获取 auth 相关的信息,最后生成 Backend Config。 </p><p>除此之外,由于我们目前的测试代码中不涉及用户鉴权,所以额外添加了 testBasicAuth 测试样例,用于检验在用户不指定 Backend Config 时,我们是否能够正确提供 鉴权信息。在测试样例中,我们模拟生成了用户名、密码和 docker config,并正确设置了环境变量 。 启动 docker 时 ,额外指定REGISTRY_AUTH_HTPASSWD_PATH , REGISTRY_AUTH 等用于鉴权。</p><h3>实现无需 docker 拉取镜像</h3><p>当前拉取原始镜像时,我们需要事先安装 docker,然后通过 docker pull 指令拉取。我们可以手动的拉取每个镜像层,然后解压、挂载,从而去除对 docker 的依赖。 </p><p>首先我们需要在 FilesystemRule 结构体中添加 SourceParsed, SourcePath, SourceRemote 等字段,指定原始镜像的相关信息和存储路径。在拉取原始镜像时,我们通过 SourceParsed 获取到镜像层的信息,然后多线程下载每个镜像层并解压。 </p><p><img src="/img/remote/1460000043370365" alt="img" title="img"></p><p>因为镜像的存储路径是事先确定的,同时我们也可以获取到每个镜像层的信息,所以在挂载镜像时,我们不需要运行 docker inspect 命令获取镜像的分层信息,可以直接拼接每一层的路径,使用 OverlayFS 进行挂载。 </p><p><img src="/img/remote/1460000043370366" alt="img" title="img"></p><p>后续发现 OverlayFS 挂载单层镜像时存在一定的问题,因而上述代码进行了一定程度 的修改和重构。</p><h2>收获与展望</h2><p>这个项目的代码量不是很大,但是我从中学习到了很多。首先通过阅读代码和跟踪调试,我了解了 Nydus 的设计思想和镜像的生成及校验的流程。在完成项目的过程中,我对 go 语言的使用更加熟练,对于容器镜像的分层存储格式及拉取、挂载的流程有了更加 细化的认识。通过解决测试过程中遇到的各种问题,我发现问题、定位问题、解决问题的能力也有了一定的提升。希望之后有机会可以继续参与到 Nydus 项目之中,为开源贡献力量。</p><h2>作者有话说</h2><p>哈喽大家好,我是王瑞,本科毕业于北京邮电大学计算机科学与技术专业,现就读于多伦多大学,从事日志压缩相关研究。本科时曾在字节跳动公司实习,参与过自动化运维平台、存储系统内存管理相关的开发工作。也曾在 VMware 公司实习,为开源数据库 GreenPlum 贡献过代码。因为对云原生比较感兴趣,所以非常高兴可以参与到 Nydus 这个项目。感谢严松老师在过程中提供的指导和帮助。 </p><p><strong>Nydus Star 一下✨:</strong></p><p><em><a href="https://link.segmentfault.com/?enc=RNldRYiuuF1ojN28DcUqbw%3D%3D.o5dfdiuZYnOLB31rIaFZVaus4NqwqVX17B0Dt3iW8lt%2BRld6QL8x8DfPwqoUqtIBzgyNEiNXoWMzF8Gn8kmydRqgfJDY7x7cawi8UwZMrFT4ngQBJhizEgql4elKsw68" rel="nofollow" title="https://github.com/dragonflyoss/image-service">github.com/dragonflyos…</a></em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=7KpK3aXZzhrP8Eltq4tQ6g%3D%3D.mLjmPNftUnr7oxNkELi2XIO%2B40ostGi6DLLs%2BGbB0582DvttAFcfRp6zpSMndqAMCYSWjt%2FE2s2IJzwArlKTLlCZsuNBJ%2BEJKz4Gmlv8ReIaVnels9Jku5omY94lxvgMLdW4Kq2nRg9XVU1lPa7EPlRlS7KqfgZDP4oX%2BZrmaTUvPOjOmBy2AH09b6sm9n47CTEa2K6RmhJ60XBnqAkyR0xxpTyU4AqsBqZGB4y3EuCvt4DAt%2BmWbOCwmkK8TjNeCfs8vNScEkTSbZC1kb9qEg0hNt6Kl3f%2B7hny6uMY18vErPS2qCWAhKhPCJ3SkkB5gaQolSnA4BIxsr%2FrcS5mnS%2FZq1b9B%2BHHsmFxXyO6F0%2BpdQZdoWARDUodeZR867Mh" rel="nofollow" title="http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247520024&idx=1&sn=e87a70a49cabde775b3c6651db652404&chksm=faa360c2cdd4e9d46b19c2cc037e7379ff40dc7c1b2fef98de37fc524f8931d9704046b36b4c&scene=21#wechat_redirect">Nydus 镜像扫描加速</a></p><p><a href="https://link.segmentfault.com/?enc=OrS2VwMeZH8Iy7i5JSc64g%3D%3D.FLGy3hAg4KCCNbRPPCGnlIHX7D9bnIeliWeYmuD1SeURNYNzhOea24Wd1RhJsLrnOAd9EMU%2BoaywPbgz82KhNWwEiDbTqY6bP7FTMk4HaHGNz5NFqL6c8%2BLqrUK%2FNsgchzjHt2Cn4MC8Qm%2B7pX635y1lYTIoVCMXZdMysOe6c95enNe8EBOoP6L9bByNEwscxr73nW1X6tzdiQveXQ%2F11%2BmyeqODwuK%2BZNRSTCniwTLMlEwUDEkfs72Q7cMV8f7cNmz8GCjYqP7iv07b5FUQ6T6yJ1hIEikTgVTwkQbKcVGv4QiDAqIopATRxuVaUwjEp6I4%2BvYAFuey7vRr02TYmhxFh52m5VKf9hHRxk9x4UMbEkoyOS5j75cNlLGYuYM3" rel="nofollow" title="http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247504035&idx=1&sn=320b77bf5f3c6cf0da309f7527b98e64&chksm=faa33f79cdd4b66f184d273a2d7460c41320711eab47af849e386c359e71eeebc6c7f21c1e0f&scene=21#wechat_redirect">Nydus 镜像加速插件迁入 Containerd 旗下</a></p><p><a href="https://link.segmentfault.com/?enc=aiaNM2nuzOtVmkqD%2FvdIwQ%3D%3D.odPKtDOaRWmvhTq1n84sGmV6uff3spgYdqnhsrf0H5DTKwXOf9ZMhFr9OFQXr2B23%2BGOWv0W0ZcnaS9WNmCvMX78cnapiPv09WXP1CsUdXOIq%2F8nCwnCQfCKAHKxaie%2B51z8JyylS3UWRfjKuG2B%2F8Yd6wHiWZyrVTZVyWPft2Hwdb14wBGkzexh1aKTDgmEsCDi9XfVHv%2FKR2uQbkmRdhPFEH%2BIpBBs2WqgaSKVl7Nm7PgBbZcMu35Qng145j8UDPtN3yuCXEBrWOYimkXn6WkAF0ryPAZSqo%2BaHYbXaX6KSimRGRYU5U46cfBALXEV80u1nvplk5GoWnNq0HSIJLcQh%2FeOD2JwHUmgFVVooNiZVzEnRX2f1vBNSOmOha30" rel="nofollow" title="http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247517164&idx=1&sn=28f50763db2883839908057125a7b497&chksm=faa36c36cdd4e52050796d00f2f5bf357471692c2da8727cc44ae47856cd925e599b6e954314&scene=21#wechat_redirect">Nydus | 容器镜像基础</a></p><p><a href="https://link.segmentfault.com/?enc=AwrIe7tL4lG%2BG2DEeoAi3w%3D%3D.qRuaGrX2Ik4lnChh0PxwjjJobhux%2FYjIgU6acTqA7aVUjbD6EGK5c%2F65n3CJWeuOAVFJ5LA8niLJbL29YJAtmteq1j2%2FOMxDusEhm04pBbaqt0kfMhVRY%2BiU4v8%2FjdovnWQRdO7nNqBbD8swHz5JS2RHuXHDEDx%2F2bIeYFOxQMK52iB%2BodtU%2BcOQfwfdivZd7VbQ8uvOPFsEEWSO0%2BzHkr8rxwwljFVQUaGqTuGUcSO7wwS6oqUrHJJxOiDNBT4sCvS%2FWFE4eyhUzi%2BqjMWPRettGw6UHj4EUqsU24FlI6MszjsqYe%2FWE3a1zogXQ7399numPu5rTekbJhkdLRUCWfByfQTms%2FtAEySLJlKHFup%2FFb68fx7AmHN9dJfNliOL" rel="nofollow" title="http://mp.weixin.qq.com/s?__biz=MzUzMzU5Mjc1Nw==&mid=2247518517&idx=1&sn=74f6426f0b280cbd4aafd2d37eaaec04&chksm=faa366efcdd4eff9b50d52dad855ab593034b4af94896c2386705a316f063a1825d1729da36a&scene=21#wechat_redirect">Dragonfly 和 Nydus Mirror 模式集成实践</a></p>
一个 go-sql-driver 的离奇 bug
https://segmentfault.com/a/1190000043345801
2023-01-18T11:20:36+08:00
2023-01-18T11:20:36+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043345803" alt="图片" title="图片"> </p><p>文|郝洪范</p><p>京东技术专家 </p><p>Seata-go 项目共同发起人</p><p><em>微服务底层技术的探索与研究。</em></p><p>本文 <strong>3482</strong> 字 阅读 <strong>7</strong> 分钟</p><blockquote>对于 Go CURD Boy 来说,相信 <code>github.com/go-sql-driver/mysql</code> 这个库都不会陌生。基本上 Go 的 CURD 都离不开这个特别重要的库。我们在开发 Seata-go 时也使用了这个库。不过最近在使用 go-sql-driver/mysql 查询 MySQL 的时候,就出现一个很有意思的 bug, 觉得有必要分享出来,以防止后来者再次踩坑。</blockquote><h2>PART. 1 问题详述</h2><p>为了说明问题,这里不详述 Seata-go 的相关代码,用一个单独的 demo 把问题详细描述清楚。</p><h3>1.1 环境准备</h3><p>在一个 MySQL 实例上准备如下环境:</p><pre><code class="Go">CREATE TABLE `Test1` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP,
-PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;</code></pre><p>从这个 SQL 语句中可以看出来, create_time 是 timestamp 类型,这里要特别留意 timestamp 这个类型。</p><p>现在插入一条数据,然后查看刚插入的数据的值。</p><pre><code class="Go">insert into Test1 values (1, '2022-01-01 00:00:00')</code></pre><p>查看下 MySQL 当前的时区。请记好相关值,草蛇灰线,伏笔于此。</p><pre><code class="Go"> show VARIABLES like '%time_zone%';</code></pre><p>查询结果:</p><p><img src="/img/remote/1460000043345804" alt="图片" title="图片"></p><p>接下来使用 MySQL unix_timestamp 查看 create_time 的时间戳:</p><pre><code class="Go">SELECT unix_timestamp(create_time) from Test1 where id = 1;</code></pre><p>查询结果:</p><p><img src="/img/remote/1460000043345805" alt="图片" title="图片"></p><h3>1.2 测试程序</h3><p>有如下 demo 程序,示例使用 go-sql-driver 读取 create_time 的值:</p><pre><code class="Go">package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
var user = "user"
var pwd = "password"
var dbName = "dbname"
dsn := fmt.Sprintf("%s:%s@tcp(localhost:3306)/%stimeout=100s&parseTime=true&interpolateParams=true", user, pwd, dbName)
db, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
defer db.Close()
rows, err := db.Query("select create_time
from Test1 limit 1")
if err != nil {
panic(err)
}
for rows.Next() {
t := time.Time{}
rows.Scan(&t)
fmt.Println(t)
fmt.Println(t.Unix()) }}</code></pre><p>我们运行个程序会输出下面的结果:</p><pre><code class="Go">2022-01-01 00:00:00 +0000 UTC1640995200</code></pre><h3>1.3 问题详述</h3><p>发现问题所在了吗?有图如下,把结果放在一块,可以详细说明问题。</p><p><img src="/img/remote/1460000043345806" alt="图片" title="图片"></p><p>图中红色箭头指向的两个结果,用 go-sql-driver 读取的结果和在 MySQL 中用 unix_timestamp 获取的结果明显是不一样的。</p><h2>PART. 2 问题探案</h2><p>1.3 小节中最后示图可以看出,数据库中 create_time 的值 <code>2022-01-01 00:00:00</code> 是东八区的时间,也就是北京时间,这个时间对应的时间戳就是 <code>1640966400</code> 。但是 go-sql-driver 示例程序读出来的却是 <code>1640995200</code> , 这是什么值?这是 0 时区的 <code>2022-01-01 00:00:00</code>。</p><p>对问题的直白描述就是:MySQL 的 create_time 是 <code>2022-01-01 00:00:00 +008</code> ,而读取到的是 <code>2022-01-01 00:00:00 +000</code> ,他俩压根就不是一个值。</p><p>基本能看出来 bug 是如何发生的了。那就需要剖析下 go-sql-driver 源码,追查问题的根源。</p><h3>2.1 go-sq-driver 源码分析</h3><p>这里就不粘贴 <code>"github.com/go-sql-driver/mysql"</code> 的详细源码了,只贴关键的路径。</p><p><img src="/img/remote/1460000043345807" alt="image.png" title="image.png"></p><p>Debug 的时候详细关注调用路径中红色的两个方块的内存中的值。</p><p><img src="/img/remote/1460000043345808" alt="图片" title="图片"></p><pre><code class="Go">// https://github.com/go-sql-driver/mysql/blob/master/packets.go#L788-
L798
func (rows *textRows) readRow(dest []driver.Value) error {
// ...
// Parse time field
switch rows.rs.columns[i].fieldType
{
case fieldTypeTimestamp,
fieldTypeDateTime,
fieldTypeDate,
fieldTypeNewDate:
if dest[i], err = parseDateTime(dest[i].([]byte), mc.cfg.Loc);
err != nil { return err } }}</code></pre><pre><code class="Go">func parseDateTime(b []byte, loc *time.Location) (time.Time, error) { const base = "0000-00-00 00:00:00.000000" switch len(b) { case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
year, err := parseByteYear(b)
month := time.Month(m)
day, err := parseByte2Digits(b[8], b[9])
hour, err := parseByte2Digits(b[11], b[12])
min, err := parseByte2Digits(b[14], b[15])
sec, err := parseByte2Digits(b[17], b[18])
// https://github.com/go-sql-driver/mysql/blob/master/utils.go#L166-L168 if len(b) == 19 { return time.Date(year, month, day, hour, min, sec, 0, loc), nil } }}</code></pre><p>从这里基本上就能明白,go-sql-driver 把数据库读出来的 create_time timestamp 值当做一个字符串,然后按照 MySQL timestamp 的标准格式 "0000-00-00 00:00:00.000000" 去解析,分别得到 <code>year, month, day, hour, min, sec</code>。最后依赖传入 time.Location 值,调用 Go 系统库 time.Date() 再去生成对应的值。</p><p>这里表面看起来没有问题,其实这里严重依赖了传入的 time.Location。这个 time.Location 是如何得到的呢?进一步阅读源码,可以明显的看出来,是通过解析传入的 DSN 的 Loc 获取。</p><p>其中关键代码是:<em><a href="https://link.segmentfault.com/?enc=4PYE%2FN9RQkzVoFeIT3Q3WA%3D%3D.Nh3VxcwxM4Fydd1Ni4W4ULjtVJStyi4VuFuW7q7%2FUAAVpOoLunhIZBiIOE7r12m%2FSBThkrKtX%2BTX7781HmxDOkdG1QMEpg4sTNK2mpjIU00%3D" rel="nofollow">https://github.com/go-sql-driver/mysql/blob/master/dsn.go#L467-L474</a></em> 。</p><p><img src="/img/remote/1460000043345809" alt="图片" title="图片"></p><p>如果传入的 DSN 串不带 Loc 时,Loc 就是默认的 UTC 时区。</p><p><img src="/img/remote/1460000043345810" alt="图片" title="图片"></p><h3>2.2 抽丝剥茧</h3><p>回头看开头的程序,初始化 go-sql-driver 的 DSN 是 <code>user:password@tcp(localhost:3306)/dbname?timeout=100s&parseTime=true&interpolateParams=true</code>,该 DSN 里面并不包含 Loc 信息,go-sql-driver 用使用了默认的 UTC 时区。然后解析从 MySQL 中获取的 timestamp 字段了,也就用默认的 UTC 时区去生成 Date,结果也就错了。</p><p>因此,问题的主要原因是:go-sql-driver 并没有按照数据库的时区去解析 timestamp 字段,而且依赖了开发者生成的 DSN 传入的 Loc。当开发者传入的 Loc 和数据库的 time_Zone 不匹配的时候,所有的 timestamp 字段都会解析错误。</p><p>有些人可能有疑问,如果 go-sql-driver 为什么不直接使用 MySQL 的时区去解析 timestamp 呢?</p><p>我们已经提了一个 issue,商讨更好的解决方案:<em><a href="https://link.segmentfault.com/?enc=Q0X2uuj1098mTWhdjs4mcw%3D%3D.JBpU6Wazz5DNHxXrmTx36Z9YV8jP0I7hZvk4HHaTnu3Wl2329Ep4az5JcUn5ICps83oVuHs3Ty4S2hoAphbkUA%3D%3D" rel="nofollow">https://github.com/go-sql-dri...</a></em>。</p><h3>PART. 3 最后结论</h3><p>在 MySQL 中读写 timestamp 类型数据时,有如下注意事项:</p><ol><li>默认约定:写入 MySQL 时间时,把当前时区的时间转换为 UTC + 00:00(世界标准时区)的值,读取后在前端展示时再次进行转换;</li><li>如果不愿意使用默认约定,在现阶段使用 go-sql-driver 的时候,一定要特别注意,需要在 DSN 字符串加上 "loc=true&time_zone=*" , 和数据的时区保持一致,不然的话就会导致 timestamp 字段解析错误。</li></ol><p><strong>| 参考文档 |</strong> </p><p>《The date, datetime, and timestamp Types》 </p><p><em><a href="https://link.segmentfault.com/?enc=WqrCV%2BTIZ%2FKkHVChSeL2bg%3D%3D.aJUbae9RkNsz6OOFs%2B7vC3oFQ67I4wxVsG0X0l0w1NDArUwll%2BgcEgxPCFMmTGwrFjid523VDIv1SALWtzlHrw%3D%3D" rel="nofollow">https://dev.mysql.com/doc/refman/8.0/en/datetime.html</a></em> </p><p>《MySQL 的 timestamp 会存在时区问题?》</p><p><em><a href="https://link.segmentfault.com/?enc=i%2FpMWH%2FWATrXR2uc%2B9h1cg%3D%3D.mwe3mctNOtR12hMsT82Hxqrx%2F4RvrevwlBMKqn2BWy0caaWZlhZkyMUVacWX5mcz" rel="nofollow">https://juejin.cn/post/7007044908250824741</a></em> </p><p>《Feature request: Fetch connection time_zone automatically》</p><p><em><a href="https://link.segmentfault.com/?enc=rxxipNkqTm1UveYXKj5Ssg%3D%3D.RzG22JCUuZvJkEfGw8qHCvFfh4q6c253RCuwSv2C5Sjx5e3Vh5m%2BvAlGnXXkcjQWArNiql1JvoqYbwafATy9Yg%3D%3D" rel="nofollow">https://github.com/go-sql-driver/mysql/issues/1379</a></em></p><p><strong>社区讨论群</strong></p><p>细节处见真章,</p><p>Seata-go 社区认认真真做开源,</p><p>做对用户负责任的高质量的项目。</p><p><strong>了解更多...</strong></p><p><strong>Seata Star 一下✨:</strong> <br><strong><a href="https://link.segmentfault.com/?enc=k3qkrQNaqv41auOTIok%2FgQ%3D%3D.M5PMxhWtoZZllguwbV6vfgNCjFCIzV%2B9w9FpvvWqrzrx4ukeM29GkPu0BOWeaBxM" rel="nofollow">https://github.com/seata/seata-go</a></strong></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=UzX%2BChxL3WO4w0LlqFpJvA%3D%3D.NrjlOF6ep0qVxJSqptXy1fThiAxHMkHHK32dX5cOtM9EPeH1fLWXT2v0YXkdaugCPvtFbxRiGmuFDjByt3STXavOpjF5jevwku2NX9CYPQBM2AoC3VIxi%2BzYYcBVAf%2BjBFnQPRwDteOuOOZiJETELog3UtggCJDnHr1VOOmOW%2FAj0qOE%2BVWKVO33aXR9mGlOhinq6JCEEh5YZAiReNmjh0yfY9EhJrVLqBQYQtBUdAJqk1VIfh13izyBW1qG3PRFeCRZJTNOa40t0aP4BriL5A%3D%3D" rel="nofollow">Seata AT 模式代码级详解</a></p><p><a href="https://link.segmentfault.com/?enc=vPj49%2Fc938tg06uYSVMARA%3D%3D.AeU2USAGqFEj6GhSW5lrdg9qXXIdTru02O%2FPKBxCYAAtY%2Bt5N9U%2FQgbyp78WE2DMtUe4VwJPTTfVu4C8Z9k1EYWFfWgHWuPrHeGqL5OjKRFGzjEBbsjyucZw9ON1Jx6oyhU8hypzbaoXTxBnCI3G3awE1ggDWdUOGrWLsj6fk1LH%2FeDDntk%2B7evXGJl7z0ba5znj8JAQIrUpcWJN3axtdJo053IXYz7a4VATM5%2BBQ5%2BBB6RYSZrARy97h49PMr1tLxIL9%2F2GZ14ke2W6aYIEDw%3D%3D" rel="nofollow">蚂蚁集团境外站点 Seata 实践与探索</a></p><p><a href="https://link.segmentfault.com/?enc=r%2FmqTGZzQTXkc0hrMpm2Eg%3D%3D.ZI0ohd4zxIzINQqL1dkkvFQXgxxp8zO14UwnW1tGzqelEeqfrtXhNUhiyPnce%2Fe1JBDIGRBBUaLVFA2tJtt95qN2qtSNO5XBGq9HWbCtbu7w1r0zXblRoyeMoq58g2qItgl5tJJneDJMbslvsfmDZ2y51dFvJWTAiytjLHriq4bjBM0lN2FzisVopzzO9kyBOYrGCRhSy92KfD7jAoODOYukq1J1sL9hZsmP0bAXRSKYSuZ4ZVNR1L%2FIqui%2FSgFqBS1ChqmMm%2BVqT9LQEMxvlQ%3D%3D" rel="nofollow">Seata 多语言体系建设</a></p><p><a href="https://link.segmentfault.com/?enc=Jw%2B8bIAfygiQQ7xqn3TcJQ%3D%3D.YAr16wQ24tmBAiDPDqiGH8p5QT83aDKcM4RZoVARWnmJmO6EmugL%2BRquSR4%2FmwWZShOHOfI%2FAvc6yrCfc7V85xbgAcko7MubJh9mW2DRoFV5BINVwcfJvBiUjWgiqF2cR95C1DnbAfrQXZq88p1R%2FLQT6p2QQl65RqQkBE0pry3RB%2FCqgt%2B8G6OfLTVEQUSG%2FTKZcimQtqlkU1sVvLemB3iA3tcS3WWUKL0c3GewMACxfqvuL2FzHLvhSN2V41r4xoZ3VfFcxPvy3vMN8YzYbg%3D%3D" rel="nofollow">Seata-php 半年规划</a></p>
Nerdctl 原生支持 Nydus 加速镜像
https://segmentfault.com/a/1190000043302547
2023-01-11T10:53:58+08:00
2023-01-11T10:53:58+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043302549" alt="图片" title="图片"> </p><p>文|李楠(GitHub ID : @loheagn)</p><p>北京航空航天大学 21 级研究生 </p><p><em>云原生底层系统的开发和探索工作。</em> </p><p>本文 <strong>6369</strong> 字 阅读 <strong>16</strong> 分钟</p><p><img src="/img/remote/1460000043302550" alt="图片" title="图片"></p><p>OSPP 开源之夏是由中科院软件研究所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动。旨在鼓励在校学生积极参与开源软件的开发维护、促进优秀开源软件社区的蓬勃发展、培养和发掘更多优秀的开发者。 </p><p>这是去年(2022)的开源活动中,李楠同学参加 Nydus 容器开源生态集成课题的相关工作。</p><p><strong>| 作者有话说 |</strong></p><blockquote>大家好!我是来自北京航空航天大学的 2021 级研究生李楠,对云原生技术很感兴趣,GitHub ID 是 @loheagn。在今年上半年,我参加了 Linux 基金会的春季实习,完成了 CNCF - Kubevela: Management of Terraform state 项目的工作,并因此培养了对开源工作的兴趣。在开源之夏 2022 开放题目后,我了解到了 Nydus 项目,感觉这是一个很酷的项目,由于我之前对容器和镜像的底层技术并不是很熟悉,觉得这是个很不错的学习机会。于是便尝试投递了简历申请,最终很幸运地通过了筛选,并在严松老师的帮助下顺利完成了题目。</blockquote><h2>PART. 1 项目背景</h2><h3>Nerdctl</h3><p>Nerdctl 是一个对标 Docker CLI 和 Docker Compose 的、用于与 Containerd <em>(当下最流行的容器运行时,Docker 的后端也是调用的 Containerd,通常作为守护进程出现)</em> 交互的命令行工具。</p><p>用户可以像使用 Docker CLI 一样使用 Nerdctl 与 Containerd 进行交互,比如使用 <code>nerdctl pull <image_name></code> 来拉取镜像、使用 <code>nerdctl run <image_name></code> 来运行容器等等。</p><p>相比于 Containerd 本身提供的 CTR 工具,Nerdctl 默认提供了更友好的用户体验,并尽量保持其使用方式与 Docker 一致。对于从 Docker 迁移到 Containerd 的用户,往往只需要 <code>alias docker=nerdctl</code> 就可以与之前获得一致的使用体验。</p><h3>OCI 镜像格式</h3><p>OCI 镜像格式是 OCI <em>(Open Container Initiative,开放容器计划)</em> 的重要组成部分。它给出了一个厂商无关的镜像格式规范,即一个镜像应该包含哪些部分、每个部分的数据结构是如何的、这些各个部分应该以怎样的方式进行组织等等。</p><p>OCI 镜像格式脱胎于 Docker 镜像格式,它与 Docker 镜像格式有着非常类似的结构;但它比 Docker 镜像格式有更好的兼容性,并得到了各个厂商的普遍认同。</p><p>因此,在这里主要介绍一下 OCI 镜像格式的主要内容。</p><p>通常所说的镜像文件其实指的是一个包含了多个文件的“包”,“包”中的这些文件提供了启动一个容器所需要的所有需要信息,其中包括但不限于,容器所使用的文件系统等数据文件,镜像所适用的平台、数据完整性校验信息等配置文件。当我们使用 <code>docker pull</code> 或 <code>nerdctl pull</code> 从镜像中心拉取镜像时,其实就是在依次拉取该镜像所包含的这些文件。</p><p>例如,当我们使用 <code>nerdctl pull</code> 拉取一个 OCI 镜像时:</p><p><img src="/img/remote/1460000043302551" alt="image.png" title="image.png"></p><p>从 Log 中可以清晰地看到,Nerdctl 依次拉取了一个 index 文件、一个 manifest 文件、一个 config 文件和若干个 layer 数据文件。实际上,一个标准的 OCI 镜像通常就是由这几部分构成的。</p><p>其中,layer 文件一般是 tar 包或者压缩后的 tar 包,其包含着镜像具体的数据文件。这些 layer 文件会共同组成一个完整的文件系统(<em>也就是从该镜像启动容器后,进入容器中看到的文件系统)</em> 。</p><p>config 文件是一个 JSON 文件。其中包含镜像的一些配置信息,比如镜像时间、修改记录、环境变量、镜像的启动命令等等。</p><p>manifest 文件也是一个 JSON 文件。它可以看作是镜像文件的清单,即说明了该镜像包含了哪些 layer 文件和哪个 config 文件。</p><p>下面是一个 manifest 文件的典型例子:</p><pre><code class="JSON">{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:0584b370e957bf9d09e10f424859a02ab0fda255103f75b3f8c7d410a4e96ed5",
"size": 7636
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:214ca5fb90323fe769c63a12af092f2572bf1c6b300263e09883909fc865d260",
"size": 31379476
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:50836501937ff210a4ee8eedcb17b49b3b7627c5b7104397b2a6198c569d9231",
"size": 25338790
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:d838e0361e8efc1fb3ec2b7aed16ba935ee9b62b6631c304256b0326c048a330",
"size": 600
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:fcc7a415e354b2e1a2fcf80005278d0439a2f87556e683bb98891414339f9bee",
"size": 893
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:dc73b4533047ea21262e7d35b3b2598e3d2c00b6d63426f47698fe2adac5b1d6",
"size": 664
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:e8750203e98541223fb970b2b04058aae5ca11833a93b9f3df26bd835f66d223",
"size": 1394
}
]
}</code></pre><p>index 文件也是一个 JSON 文件。它是可选的,可以被认为是 manifest 的 manifest。试想一下,一个 tag 标识的镜像,比如 <code>docker.io/library/nginx:1.20</code> ,会针对不同的架构平台 <em>(比如 linux/amd、linux/arm64 等等)</em> 有不同的镜像文件,每个不同平台的镜像文件都有一个 manifest 文件来描述,那么我们就需要有个更高层级的文件来索引这多个 manifest 文件。</p><p>比如,<code>docker.io/library/nginx:1.20</code> 的 index 文件就包含一个 manifests 数组,其中记录了多个不同平台的 manifest 的基本信息:</p><pre><code class="JSON">{
"manifests": [
{
"digest": "sha256:a76df3b4f1478766631c794de7ff466aca466f995fd5bb216bb9643a3dd2a6bb",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "amd64",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:f46bffd1049ef89d01841ba45bb02880addbbe6d1587726b9979dbe2f6b556a4",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v5"
},
"size": 1570
},
{
"digest": "sha256:d9a32c8a3049313fb16427b6e64a4a1f12b60a4a240bf4fbf9502013fcdf621c",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
},
"size": 1570
},
{
"digest": "sha256:acd1b78ac05eedcef5f205406468616e83a6a712f76d068a45cf76803d821d0b",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
"size": 1570
},
{
"digest": "sha256:d972eee4f12250a62a8dc076560acc1903fc463ee9cb84f9762b50deed855ed6",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "386",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:b187079b65b3eff95d1ea02acbc0abed172ba8e1433190b97d0acfddd5477640",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "mips64le",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:ae93c7f72dc47dbd984348240c02484b95650b8b328464c62559ef173b64ce0d",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "ppc64le",
"os": "linux"
},
"size": 1570
},
{
"digest": "sha256:51f45f5871a8d25b65cecf570c6b079995a16c7aef559261d7fd949e32d44822",
"mediaType": "application\/vnd.docker.distribution.manifest.v2+json",
"platform": {
"architecture": "s390x",
"os": "linux"
},
"size": 1570
}
],
"mediaType": "application\/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2
}</code></pre><p>综上,组成镜像的各个文件相互之间形成了一个树状结构,树的上层节点持有对下层节点的引用。从最上层的 index 文件或 manifest 文件开始,就可以“顺藤摸瓜”地索引到镜像的所有文件。</p><p><img src="/img/remote/1460000043302552" alt="图片" title="图片"></p><p>需要注意的是,OCI 镜像规范是一个开放的格式,它只规定了文件的组织形式,但没规定数据文件的具体内容。我们完全可以将一些其他类型的文件打包成一个 OCI 镜像的格式,当做 OCI 镜像进行分发,从而充分利用 DockerHub 等镜像注册中心的能力。</p><h2>PART. 2 关于 Nydus</h2><p>Nydus 是 CNCF 孵化项目 Dragonfly 的子项目,它提供了容器镜像、代码包、数据分析按需加载的能力,无需等待整个数据下载完成便可开始服务。</p><p>Nydus 在生产环境已经支撑了每日百万级别的加速镜像容器创建,在启动性能、镜像空间优化、网络带宽效率、端到端数据一致性等方面相比 OCI v1 格式有着巨大优势,并可扩展至例如 NPM 包懒加载等数据分发场景。</p><p>目前 Nydus 由蚂蚁集团、阿里云、字节跳动联合开发,Containerd、Podman 社区接受了 Nydus 运行时作为其社区子项目,也是 Kata Containers 以及 Linux v5.19 内核态原生支持的镜像加速方案。</p><h3>Nydus 镜像格式</h3><p>Nydus 镜像格式是对下一代 OCI 镜像格式的探索。它的出现是基于以下的事实:在用户启动容器时,容器运行时会首先从远程 Registry 中下载完整的镜像文件 <em>(通常这一过程是容器启动时最耗时的部分)</em> ,然后才能对镜像的文件系统进行解包和挂载,最后完成容器的启动。</p><p>但实际上,用户在运行容器过程中,并不会用到文件系统中的全部文件,数据使用率通常只有 6% 左右;也就是说,花费大量时间拉取的镜像文件,却大概率最终不会用到。</p><p>因此,如果能在容器运行时,不提前拉取完整镜像,而只是在需要访问某些文件再动态拉取,将大大提高容器的启动效率,并带来网络带宽效率、镜像空间优化等更多好处。</p><p>下图是相同内容的 Nydus 镜像和 OCI 镜像在创建容器时的耗时对比:</p><p><img src="/img/remote/1460000043302553" alt="image.png" title="image.png"></p><p>Nydus 镜像格式并没有对 OCI 镜像格式在架构上进行修改,而主要优化了其中的 layer 数据层的数据结构。Nydus 将原本统一存放在 layer 层的文件数据和元数据 <em>(文件系统的目录结构、文件元数据等)</em> 分开,分别存放在 “Blob layer” 和 “Bootstrap layer” 中。并对 Blob layer 中存放的文件数据进行分块 <em>(chunk)</em> ,以便于懒加载 <em>(在需要访问某个文件时,只需要拉取对应的 chunk 即可,不需要拉取整个 Blob layer)</em> 。</p><p>同时,这些分块信息,包括每个 chunk 在 Blob layer 的位置信息等也被存放在 Bootstrap layer 这个元数据层中。这样,容器启动时,仅需拉取 Bootstrap layer 层,当容器具体访问到某个文件时,再根据 Bootstrap layer 中的元信息拉取对应 Blob layer 中的对应的 chunk 即可。</p><p><img src="/img/remote/1460000043302554" alt="image.png" title="image.png"></p><p>从更上层的视角上,Nydus 镜像格式相比于 OCI 镜像格式的变化:</p><p><img src="/img/remote/1460000043302555" alt="image.png" title="image.png"></p><p>可以看到,Nydus 镜像对外依旧可以表现出 OCI 镜像格式的组织形式,因此,Nydus 镜像可以充分利用原有的 Docker 镜像和 OCI 镜像的存储分发的生态。</p><h3>Nydus Daemon</h3><p>从前文的讨论中可以看出,从 Nydus 镜像生成的容器,当其访问文件系统中的文件时,实际上访问的不是文件系统,而是一个“网络文件系统”——实际访问的是这个“网络文件系统”在本地的缓存或 Registry 等存储后端中的数据。</p><p>Nydus 中作为这个“网络文件系统”中的“本地客户端”的工具是 Nydus Daemon <em>(简称 Nydusd)</em> 。</p><p>下图中的 Nydus Framework 中起主要作用的就是 Nydusd。</p><p><img src="/img/remote/1460000043302556" alt="图片" title="图片"></p><p>Nydusd 是一个用户态进程,它可以通过 FUSE、VirtioFS 或 EROFS 等方式将网络文件系统挂载到容器的 Rootfs 上。</p><p>下图是使用 FUSE 的情况:</p><p><img src="/img/remote/1460000043302557" alt="image.png" title="image.png"></p><h3>Nydus Snapshotter</h3><p>Nydus 镜像格式虽然在架构上与 OCI 格式保持一致,但对数据的解析、具体的 layer 文件的 MediaType 等方面与 OCI 格式有很大区别。现有的容器运行时 <em>(典型的如 Containerd、Podman、CRI-O 等)</em> 及其配套的工具并不能直接与拉取和运行 Nydus 镜像。</p><p>例如,对于 Containerd:</p><p>在 Containerd 拉取镜像时,会根据镜像的 manifest 中的描述将所有的层文件都拉取下来。这首先就失去了 Nydus 按需加载的意义。</p><p>Containerd 在完成镜像每个层文件的拉取后,会调用 Snapshot 服务将每一层解包,以读取其中的文件。但 Nydus 镜像的 Blob layer 使用了自定义的 MediaType,Containerd 在处理时会直接报错。</p><p>Containerd 在运行容器时,会将镜像所属的各个解包后的 Snapshot 目录作为 OverlayFS 的 Lower Dir,挂载到容器的 Rootfs 上。Nydus 必须能够 hack 这一过程,将 Nydusd 提供的网络文件系统作为 OverlayFS 的 Lower Dir 挂载。</p><p>幸运的是,Containerd 在设计之初就考虑到了对多种文件系统的支持,支持用户自定义 Snapshot 插件,并在拉取和运行镜像时指定使用对应的 Snapshot 插件,以实现用户所期望的功能。Nydus 所提供的这样的 Snapshot 插件就是 Nydus Snapshotter。</p><p><strong>PART. 3</strong> </p><p><strong>我的工作</strong></p><p><strong>Nerdctl 支持运行 Nydus 镜像</strong></p><p>在开源之夏的题目发布时,当需要使用 Nerdctl 来运行 Nydus 镜像时,不能像普通的 OCI 镜像或 Docker 镜像一样,直接使用 <code>nerdctl run</code> 等来运行 <em>(执行 <code>nerdctl run</code> 会直接报错)</em> ,必须首先使用 Nydus 自己提供的工具 ctr-remote 拉取镜像,然后才能进一步使用 <code>nerdctl run</code> 运行。</p><p>通过阅读和调试 Nerdctl 的代码发现,当本地没有要运行的镜像时,Nerdctl 会执行 pull 命令从 Registry 中拉取镜像,而直接使用 <code>nerdctl run</code> 引发的报错正是在这个 pull 阶段产生的。因此,问题转化为解决 <code>nerdctl pull</code> 的报错。同时,想到 ctr-remote 其实主要就是在做 pull 镜像的功能。于是,进一步阅读 ctr-remote 的代码可以发现,ctr-remote 通过在镜像的拉取过程中,对 manifest 包含的各个 layer 添加相应的 annotation,使 Nydus Snapshotter 可以正确处理拉取的镜像。因此,只需要将 ctr-remote 中的这部分逻辑抽离出来,并添加到 <code>nerdctl pull</code> 的工作流程中即可。</p><p><strong>Nerdctl 支持转换 Nydus 镜像</strong></p><p>相比于 Docker CLI,Nerdctl 原生支持一些更多样化的命令,比如 <code>nerdctl image convert</code> 。顾名思义,该命令的作用是将一种格式的镜像转换为另一种格式。其最基础的用法是使用 <code>nerdctl image convert --oci <src_image_tag> <target_image_tag></code> 将一个常见的 Docker v2 格式的镜像 <em>(也就是大家平常用的镜像格式)</em> 转换为一个标准的 OCI 格式的镜像。</p><p>除了 Docker v2 和 OCI,截止到开源之夏题目发布时,<code>nerdctl image convert</code> 还支持将镜像转换到 eStargz 格式。因此,“ <code>nerdctl image convert</code> 支持 Nydus” 这个题目的含义就是,拓展 <code>nerdctl image convert</code> 命令,使其支持将常见的 Docker V2 格式和 OCI 格式的镜像转换为 Nydus 格式的镜像。这个题目是本次项目最重要的一部分工作。</p><p><code>nerdctl image convert</code> 的实现主要是借助 Containerd 本身对外开放的 API 中的 Convert 能力实现的。Containerd 对镜像转换的处理流程是,按照镜像组织的树形结构,从基础的 layer 和 config 开始,到 index 层结束,一层层进行转换,从而最终生成一个新的镜像;其中,调用者可以自定义数据层的转换逻辑,<code>nerdctl image convert</code> 已有的对 eStargz 格式 的支持就是通过这种方式实现的。</p><p>但目前类似 eStargz 格式的这种实现其实是默认转换后的镜像和转换前的镜像的各层之间一一对应,而 Nydus 镜像除了有与转换前的 OCI 镜像中的数据层一一对应的 Blob layer 之外,还有一个 Bootstrap layer。幸好 Containerd 的处理流程中,在完成了每一层的转换后,会调用一个回调函数,给调用者机会做进一步的处理。因此,可以利用 Containerd 对 manifest 层处理完的回调,在该回调中,额外生成一个 Bootstrap layer,并相应地修改 manifest 层和 config 层中的内容,从而最终构建出一个合法的 Nydus 镜像。</p><p>在开发完基本逻辑后,测试过程中发现了转换后的 Nydus 镜像文件生成后又被意外删除的现象。甚至在一步步调试时,在函数返回前,转换后的 Nydus 镜像文件依旧存在,但函数返回后文件奇迹般的消失了。</p><p>对此,我依次尝试了以下排查思路:</p><p>Nydus 镜像文件的删除是在另一个协程中进行的,因此我在当前协程的断点没有调试到删除操作。但多次调试后发现,删除动作一定会发生在函数返回前,这与协程的不可预测性不符。</p><p>Nydus 镜像文件触发了 Containerd 守护进程的某种 GC 操作。我使用 Inotify 监控镜像文件的创建和删除操作对应的进程,发现确实是 Containerd 守护进程的操作。</p><p><strong>但问题是,Nerdctl 执行的代码也会与 Containerd 进行 RPC 通信,这一操作是 Containerd 进程自己的内置逻辑呢,还是 Nerdctl 通知 Containerd 做的呢?不得而知。</strong></p><p>在花费了一周时间排查 bug 后,发现是函数返回前执行了函数体前部分的 Defer 操作触发了 Nydus 镜像文件的删除操作,而在 Defer 的函数体中没有设置断点,因此没有调试到。最终,通过分析 Defer 函数体中的逻辑,问题得以解决。</p><p>总结下来,还是具体的编程经验不足,没有在一开始就想到所有可能得方面,导致绕了很大的弯路。</p><p><strong>小结</strong></p><p>上述两项工作的 PR 都已合入了 Nerdctl 的主分支 <em>(>=0.22)</em> ,基本实现了 Nerdctl 原生支持 Nydus 镜像加速的能力。</p><p>大家可以移步文档作进一步了解: <em><a href="https://link.segmentfault.com/?enc=%2B4xWZaTEmhRx4%2FPVSwlWIA%3D%3D.VGcN6YFxuNt2qXTDZ2Ud7mZ%2FUVtz6s%2FydDU4RrveRZbLgR%2BQ3XmuK6hSj4tLDxTIMgJwekX%2FmWS9WIrFiFKXuw%3D%3D" rel="nofollow">https://github.com/containerd/nerdctl/blob/master/docs/nydus.md</a></em>。</p><h2>PART. 4 项目总结与展望</h2><p>Containerd 是当前最流行的容器运行时之一,Nerdctl 作为 Containerd 的社区中的核心项目提供了完善的使用体验。它们都是容器领域中非常重要的基础项目。</p><p>通过本次项目的开发工作,我逐渐了解了 OCI 镜像的组成部分,每部分的作用和基本格式;知道了以及 Nydus 镜像和 OCI 镜像之间的差异,并且理解了 Nydus镜像中 Blob layer 和 Bootstrap layer 之间的区别;能够通过检查本地镜像相关文件排查一些简单的程序 bug。</p><p>在项目进行的过程中,我阅读了 Nerdctl 和 Containerd 的代码,学到了一些实用的编程技巧,并最终向 Nerdctl 提交并成功合入了两个 PR。在向 Nerdctl 提交和修改 PR 的过程中,Nerdctl 的 Maintainer 们对待代码的严谨态度让我大受裨益———他们甚至会 review <code>go.sum</code> 每一行改动!</p><p>不仅如此,这些的开源工作经历为我揭开了“顶级开源项目的神秘的面纱”,增强了我的信心,让我更有自信和兴趣进一步参与到云原生项目的相关工作中。</p><p>本次项目的完成,将使得用户能非常方便地使用 Nerdctl 和 Containerd 来构造、拉取、运行 Nydus 镜像,这无疑会对 Nydus 镜像格式的普及和进一步发展起到非常好的推动作用。</p><p><strong>| 致谢 |</strong></p><p>非常感谢项目组织老师赵新、本题目 Mentor 严松老师和项目指导助理姚胤楠同学在本次项目进行过程指导和帮助,特别是严松老师细致入微的解答和指导,每次我的一个看起来很简单甚至很愚蠢的问题都能得到严松老师详细的解答,并且言辞中经常包含着肯定和鼓励,让人如沐春风。 </p><p>在后续的学习和工作中,我希望能持续参与到 Nydus 相关的开发工作中,继续为社区贡献 issue 和代码。</p><p><strong>了解更多...</strong></p><p><strong>Nydus Star 一下✨:</strong> </p><p><em><a href="https://link.segmentfault.com/?enc=hvHdI7B3oWHAVZqjGmd03A%3D%3D.xsitXBbIoj%2BPKXyTCzxb45KiV9g0iGCWRvWAH78E9xHGEBAlqYb73Qs2PayvyX3f" rel="nofollow">https://github.com/dragonflyoss/image-service</a></em></p><p><strong>更多福利...</strong></p><p><a href="https://link.segmentfault.com/?enc=mUPOMRBisxMGzl7lkULRGQ%3D%3D.TmzDkH8G3Y555t38E2%2F5s3sc5nMZr7Zxts5Kev%2FSRx28x%2BNfn0uZxigZDquG4SjwoP57kQXayPEvHPVuqfme5%2BXawb7k6od7%2F%2BQSZuBh3vn6Ixq7kIAiJ%2FzzJk0zhsrKcaB8UG29wh5l%2FORF4d%2FvUhM9GQydky9%2FISy843C3BS%2FL%2B1HyTDyaihhnGYF9APbgHMYfqB14UL4bmSv8SMyZ%2Fr33kR%2FKBQEi9mAMtRZNLylH%2FTKbax9KJgux9%2B1Gl95%2B8B3oe8Y09TBSvUWIwffNwz9Y3QRLnPZZqNXcXaYYH%2F4%3D" rel="nofollow"><strong>《SOFAStack 社区 2022 年报》</strong></a>已发出</p><p>扫描下方二维码填写问卷</p><p>有机会获得 SOFAStack <strong>新年限定周边</strong> 哦!</p><p><img src="/img/remote/1460000043302558" alt="图片" title="图片"></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=YeZq4WESR98u7pfAIfhOMw%3D%3D.%2BvZIn8P9tAZUR%2FrrarzudDbefA%2FE8V9sS%2B7u3TfDIGPOC7QvVYxiErpGjGgnFT93FV%2FyTMJEWEnrLap2iaYpiNPhk4llM3pZsPCKT0k77Z8Z4UkqTZzwgSZOpObXL5%2BfXUaR5%2FOR28KiEWSXvl3X6XnUzoKloEWayJLRjimwRahbCJM0j0wVUbs%2BK1ktQE5Un%2BVv47D95%2B3Rh2tNsMW6vCpQS1dezLmKhpvkRTC2I8dWNdaEoh2K6cF9bi9KAA511ZSx%2BwS0IITlTiA04S0hmLmyZsBjms1332wKenafQ6w%3D" rel="nofollow">Nydus 镜像扫描加速</a></p><p><a href="https://link.segmentfault.com/?enc=I%2Baeq4DIm4YqkHU61cMr5A%3D%3D.I0tlTpMYzqVrqkFuKYxP3FRLRXaTpS1FOZA0lpXi4PoV7ygVMwL3j2ango1AznYlc5MENYwU9XY4AlNkyk3Rx0B6pINU%2BfIB6cA4XjEBQoYU1WS6CMmxDW38KsSOX%2FgsvrAQgiyxhdCKbDoXomB9ke88wn5IJHxACxUzha96ehczhBXCYGJJDkuGcyOmhHydzDrqo2c0qHAmu3yDxMqsya7PaF1um5PkX8cNB3GouWogB7D7UrGnsyJmi6oYqLS1uRgCl5T%2BBF6iXQnByYyBwVoS2ol3SuoEYumHquuQXR8%3D" rel="nofollow">Nydus 镜像加速插件迁入 Containerd 旗下</a></p><p><a href="https://link.segmentfault.com/?enc=NXI8GG%2FF3VvzRBTSw7sytA%3D%3D.khf0ECyuIReernoyDOjmpd29HD6LlFMuiqWS%2BwxA59VqKgKUzxy4ORTbhPmoFvvn4wvGtoO0MHYrKQ0QO1iaAPrhxx%2Fg93lRtVlXBQ%2BqjI%2FxQOXWdaxoiyo8f5gb9VRUPeXq85KfXdz0Ik1q3anZ056Ge5dlxROhHuAeqjCAvlqoJwXna%2BUSbnHkC6Vv0%2FwSR5nfkdo3EGFgPSyjwZ8uovQ8cnbREeO8zL6dvA0zllonMlbm32Gbky1V4J0u6FRk2ZzJFzmBe4jly%2Fm3ljjzWeJufwDma7b%2F%2B4EJHTDlMYk%3D" rel="nofollow">Nydus | 容器镜像基础</a></p><p><a href="https://link.segmentfault.com/?enc=FJ0E2Q16XMNWPu%2BmuZElPg%3D%3D.Elw1%2Betk8kRtPy%2Bd6roYbb%2B9xNXNuGVCUnyIVyL%2FsnfwgkU75%2FaKrh7LhQXHpTLapCgItPr97WGlMnupiCuSyQQi3ui9yoruofK4EVpbR5AdVvtmsPQbcNaBrBRebstmDxXZ3A0WaXjssdYDAnSvWastHu4ziu8GMgKrbZjMPBzXhaF98YiQnwDsDY7qDM1glEhLbd55g9ef1YsZPJ%2FAbua8H45tzNAZe468VNTd%2FXSAkxka4%2BlnDb9NyQEA5%2FewKIZdhvBZQzPe4MAIbhx1eviIBRjS7Fcm1AP2whf%2FiNs%3D" rel="nofollow">Dragonfly 和 Nydus Mirror 模式集成实践</a></p>
来自开发者的点赞,SOFAStack 入选 2022 中国技术品牌影响力企业榜
https://segmentfault.com/a/1190000043255912
2023-01-05T18:50:34+08:00
2023-01-05T18:50:34+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043255914" alt="图片" title="图片"></p><p><img src="/img/remote/1460000043255915" alt="图片" title="图片"></p><p>2022 年 1 月 4 日,中国技术先锋年度评选 | 2022 中国技术品牌影响力企业榜单正式发布。</p><p>作为中国领先的新一代开发者社区,SegmentFault 思否依托数百万开发者用户数据分析,各科技企业在国内技术领域的行为及影响力指标,最终评选出 30 家上榜企业。</p><p><strong>SOFAStack 入选 2022 中国技术品牌影响力企业榜。</strong></p><p><img src="/img/remote/1460000043255916" alt="图片" title="图片"></p><p>SOFAStack 社区于 2018 年 4 月 19 日开源,截至目前,共有 <strong>426</strong> 位贡献者,<strong>30000+</strong> Star。</p><p>SOFAStack 开源社区能够快速构建金融级云原生架构,同时在多个金融场景里均锤炼出最佳实践,具备以下特点:</p><p><strong>开放</strong>:技术栈全面开源共建、 保持社区中立、兼容社区、兼容开源生态,组件可插拔, SOFAStack 组件与其它开源组件可相互集成或替换;</p><p><strong>金融级</strong>:包含构建金融级云原生架构所需的各个组件,让用户更加专注于业务开发,满足用户场景的现状和未来需求,经历过大规模场景的锤炼,特别是严苛的金融场景;</p><p><strong>云原生</strong>:基于 SOFAStack 可快速搭建云原生微服务体系,快速开发更具可靠性、扩展性、更加易于维护的云原生应用。</p><p>SOFAStack 社区已有 <strong>50+</strong> 公司用户,<strong>400+</strong> 有效贡献者,其中外部贡献者有 <strong>356</strong> 人,来源包含知名企业、高校等,社区也与清华大学、北京大学、上海交通大学、北京邮电大学等高校均有合作。</p><p>社区一直都关注着开发者们的需求和感受。目前社区的整体参与者总计达几十万人,通过 PR、提 issue、社区活动(包含不限于 Meetup、Workshop、直播、圆桌会议等)等参与到社区中。</p><p>感谢大家过去一年在 SOFA 社区中的参与和贡献,让 SOFA 社区在这刚刚过去的极其不寻常的一年里继续蓬勃发展。新的一年已经来临,待到春暖花开时,我们将迎来 SOFA 五周年的生日,届时又可以在线下相聚了,期待各位 SOFAer 的光临!</p><p><a href="https://link.segmentfault.com/?enc=hMI%2FcMC%2F7E3EIPvn1etcUw%3D%3D.o8lvBW%2FSYpLIgwImUxtJQPCIJzW58pMeRiq4ti8Wp%2FMnP0sDqB0pMpjO5d9Yslb9L4WgVpvLEvKCr0wjXdMbExpq3HOFkVPDVodp0TWZoXyX4TKGim2iosvOCztHT8xTPN0Ib5pecKcm%2BtWDQD96jtcq2So%2BDus065yPopOQkB8SRfHx%2BOoeJG6N9ihCH3ylFDSzNFTtgiVpy4fDWMZON%2B99Y2XP1ahAtM5UG0bxTV1uBdeQRIjsYzQAoDPnLSJeF1bBOGy8XPV5Tu7Yh90uaw%3D%3D" rel="nofollow">《SOFAStack 社区 2022 年报》</a>已发出,扫描下方二维码或<a href="https://link.segmentfault.com/?enc=fzWmIJwQa7johtx6QaiKaw%3D%3D.ImBxiZuv9ojdf5i%2BlUyWrirCmVkLoGpjvWtkQtqpRXv1VFAeP4TtgEWXS3VRT%2BpK" rel="nofollow">点击此处填写问卷</a>,有机会获得 SOFAStack 新年限定周边哦!</p><p><img src="/img/remote/1460000043255917" alt="图片" title="图片"></p>
Nydus 镜像扫描加速
https://segmentfault.com/a/1190000043239674
2023-01-04T17:46:32+08:00
2023-01-04T17:46:32+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043239676" alt="图片" title="图片"> </p><p>文|余硕</p><p>上海交通大学22届毕业生阿里云开发工程师</p><p><em>从事云原生底层系统的开发和探索工作。</em> </p><p>本文 <strong>6369</strong> 字 阅读 <strong>16</strong> 分钟</p><p><img src="/img/remote/1460000043239677" alt="图片" title="图片"></p><blockquote><p>GitLink 编程夏令营是在 CCF 中国计算机学会指导下,由 CCF 开源发展委员会(CCF ODC)举办的面向全国高校学生的暑期编程活动。 </p><p>这是今年的夏令营活动中,余硕同学参加 Nydus 开源项目的总结,主要介绍了 Nydus 为支持镜像扫描与修复所做的研究与相关工作。</p></blockquote><h2>PART. 1 课题背景</h2><h3>Nydus 开源镜像加速框架</h3><p>Nydus 是 CNCF 孵化项目 Dragonfly 的子项目,它提供了容器镜像,代码包按需加载的能力。Nydus 应用时无需等待全部数据下载完成便可开始服务。</p><p>Nydus 在生产环境中已经支撑了每日百万级别的加速镜像容器创建。它在容器启动性能、镜像空间占用、网络带宽效率、端到端数据一致性等方面相比 OCI v1 格式有着巨大优势,并可扩展至其它数据分发场景,比如 NPM 包懒加载等。</p><p>目前 Nydus 由蚂蚁集团、阿里云、字节跳动联合开发。Containerd、Podman 社区已经接受了 Nydus 运行时作为其社区子项目,它也是 Kata Containers 以及 Linux v5.19 内核态原生支持的镜像加速方案。</p><p>有关 Nydus 镜像加速开源项目的详细介绍,可以参考:<a href="https://link.segmentfault.com/?enc=GOmhb5zufEHI8GOTvA6yqg%3D%3D.cv6UEtRwFJfW9YDnAbG9dBUFYVNZw8%2Bz55USjVi2IywLNHGzGRKHIAJvzQDRMFAXjVnbByRSx4Q63r%2FBJDHdB6hju3P2c1YufoulU3x4xNwuV4i9ghcy9V8pa0mu1D27q1ORKkWjPt3nFhAP52Agiahp0Hb1yfGDSNw%2FSi6oY3ER7JYjRvtEK8%2F13awo7wAHvlQHclNg0C3UTsIUqshbPkyxycAosEKK%2FJwW7w0EpMO%2B1MDbDr%2B%2FFGIMbvtJEGimiBYKnD0FO1c%2Brla20cgjr7rkNCawjTwVdAY%2Bi2nW3Sc%3D" rel="nofollow">Nydus——下一代容器镜像的探索实践</a>。</p><h3>项目描述</h3><p>为 Nydus 镜像增加一个扫描和修复的命令行工具,包含以下功能:</p><ul><li>提供一个 Nydus 镜像 url 和需要替换的文件列表;</li><li>使用工具拉取镜像 Bootstrap;</li><li>找到镜像中对应的文件并替换;</li><li>打包成新的镜像并上传回 Registry。</li></ul><p>概括来说,原有项目目标是为 Nydus 镜像实现一个扫描和修复的命令行工具,其中这些功能是这个工具或者工具组的实现流程。</p><p>但此项目具有一定的实验性,其核心是为 Nydus 格式的镜像提供扫描和修复的功能或者指引。如果存在更好的方式,项目最终不一定要按照原有项目描述去实现。因此在接下来的课题完成过程中,我们首先对已有镜像扫描/修复的工具与服务进行了调研,来确定此课题方案的最终形态。</p><h3>镜像扫描工具及服务</h3><h4>扫描和修复</h4><p>镜像扫描和修复可以被拆解为两个过程。扫描不更改原有的镜像内容,只需要寻找扫描目标是否存在某种缺陷;镜像修复则需要根据缺陷改动镜像,包括但不限于镜像内容,镜像层级组织等。我们调研发现,当前主流开源镜像扫描引擎包括云厂商镜像安全服务都只涉及镜像扫描的功能, 不会主动添加镜像修复的功能。</p><p>我们分析主要的原因有:</p><ul><li>直接进行镜像修复可能引入新的安全问题;</li><li>镜像扫描的内容存在不同种类,很可能需要为不同种类的镜像安全问题设计不同的修复方式;</li><li>镜像修复功能可以通过重新打包镜像替代。</li></ul><p>所以,镜像安全服务暂时只是提供扫描结果的安全报告,具体的修复操作由用户自行决定。我们也准备沿用这样的思路:在本课题中,为镜像实现安全扫描的功能,并提供报告作为用户镜像修复的参考依据。</p><blockquote>我们也探索讨论了镜像修复的支持,或者 Nydus 特性在镜像修复场景下的增强,比如镜像内包替换后的去重与重组等,或许这些可以是未来 Nydus 中增加的功能特性。</blockquote><h3>镜像扫描介绍</h3><h4>镜像扫描的原因与内容</h4><p>容器镜像是当前容器/软件分发的基础,里面包含了容器隔离环境以及软件执行环境的相关内容。因此保障其安全性变得十分重要。镜像扫描即是要扫描镜像中的所有内容,及时发现可能包含的安全漏洞或者隐私泄露。</p><p>综合来看,镜像扫描需要关注镜像的安全性与健壮性,扫描的内容主要分为三类:</p><p><strong>1. 安全漏洞。</strong> 包括系统软件包和应用软件库中可能存在的安全漏洞。可以通过比对镜像中这些库/软件包的来源、版本等与 CVE 数据库中报告的漏洞的包的来源、版本来定位可能存在的安全漏洞。</p><p><strong>2. 配置。</strong> 包括镜像运行的环境配置和镜像中相关内容组合可能带来的问题。帮助尽早定位配置错误以及配置错误可能带来的安全风险。</p><p><strong>3. 隐私。</strong> 需要扫描的是用户指定的一些隐私信息。比如用户不小心将密钥等信息存入镜像。如果在扫描配置中进行指定,扫描过程可能发现这些隐私信息,避免隐私泄露以及可能带来的安全问题。</p><h4>扫描引擎</h4><p>常见的扫描引擎有 Trivy、Synk 等。Docker 官方使用的是 Synk,但它比较商业化;Trivy 是 CNCF 的项目,开放性较好。</p><h4>镜像扫描的使用方式</h4><p>在我们的调研中,镜像扫描主要应用方式有三种:</p><p><strong>1. 基础使用。</strong> 镜像扫描的过程可以直接通过集成了镜像扫描引擎的容器运行时或者镜像扫描引擎命令行触发。比如运行 <code>$ docker scan image-url</code> ,可以扫描镜像,并输出相应的报告。</p><p><img src="/img/remote/1460000043239678" alt="图片" title="图片"></p><p>source:<em><a href="https://link.segmentfault.com/?enc=KaxajZ%2BE7xFi84JaGP0rRw%3D%3D.7I1xxK5Vc16DBYEurS7zkOzLXy%2FYqX2pfYVA4w4KB82RPJGGlbT85AYTASejpDJK" rel="nofollow">https://docs.docker.com/engine/scan/</a></em></p><p><strong>2. 流程集成。</strong> 镜像扫描的过程可以集成到镜像中心或者 CI/CD 流程中。比如将镜像扫描集成到数据中心,在每次镜像上传到数据中心时触发镜像扫描,可以保证从数据中心下载的镜像总是经过安全扫描的;集成在 CI/CD 流程中,设置触发条件,可以保证镜像生成,镜像部署等过程所使用的镜像的安全性。</p><p><img src="/img/remote/1460000043239679" alt="图片" title="图片"></p><p>source: <em><a href="https://link.segmentfault.com/?enc=6%2BGcfBKMWjX2E6xVfqALww%3D%3D.dconxx2r%2BP3NmZPyAvk7boq1zr7Br7gLjhiAFPNKV8F8SF3t9h7ymdmqchPJXWpGrJq3r8c%2FzvW7UFrvcAnWyw%3D%3D" rel="nofollow">https://www.containiq.com/post/container-image-scanning</a></em></p><p><strong>3. 扫描服务。</strong> 云厂商提供了镜像安全的服务。它们背后可能是基于前两种使用方式实现,但是还可以有很多种功能的增强。用户想使用镜像扫描的功能也可以直接购买类似的安全服务,并进行灵活的配置。</p><p>source: <em><a href="https://link.segmentfault.com/?enc=w6ZMbIliBzgsEBwC3Kwuxw%3D%3D.3aS8uVkuvjgGPePPm%2BLxSdDOsPPkI7siVm4FtPVZd9%2FcvTer1yKQd4%2BuawoefyBTBnewrUekOY9bNI3yxC%2B3rQx3dtVi9sq7miBwM0ViueQ%3D" rel="nofollow">https://cloud.google.com/container-analysis/docs/on-demand-scanning-howto</a></em></p><h2>PART. 2 课题解决思路</h2><h3>基本思路</h3><p>课题首先要解决的基本思路是,如项目描述一般为 Nydus 实现一个专属的镜像扫描工具;还是复用已有的镜像扫描引擎,在 Nydus 侧实现对接支持,从而完成 Nydus 镜像扫描的功能实现。</p><p>我们最终选择了结合已有镜像扫描引擎的实现思路。尽管为 Nydus 实现一个专属的镜像扫描工具可以更好的利用 Nydus 的特性,但是从镜像功能生态上考虑,这并不是一个很好的方式。复用或者集成到现有的镜像扫描工具,一方面可以直接使用已实现的镜像扫描引擎中全面的内容扫描能力;另一方面,与上层镜像安全服务的对接也不用再重写相关接口。这样也可以减少一些功能定制,减少用户使用的负担。因此此课题选择了后一种实现的基本思路。</p><h3>扫描思路:FileSystem vs Image</h3><h4>Trivy 扫描功能实现的框架</h4><p><strong>1. 控制路径。</strong></p><p><img src="/img/remote/1460000043239680" alt="图片" title="图片"></p><p>Trivy 在镜像扫描上由以下路径控制,每触发一次命令,会由一个 Scanner 控制。其中关键的是 <code>artifact</code> 和 <code>driver</code> 。扫描引擎一般可以支持多种格式的扫描,比如 OCI v1 镜像或者镜像 Rootfs,同时一般也支持本地或者远端存储信息等。这些一般可由 <code>ScannerConfig</code> 配置或者自动解析。</p><p><code>atifact</code> 存储着镜像 <em>(包括文件系统)</em> 元信息,如果已经扫描过其中的内容,还可以存储部分解析后的信息。另外,load 到本地的 CVE 数据等也可通过 Artifact 获取。Driver 里的 Scan 方法表示的则是应用在特定扫描过程中的检查方法。</p><p><strong>2. 关键动作。</strong></p><ul><li>local.Scanner</li><li>Applier</li></ul><p>Trivy 中 Local Scanner 是前文提到的本地进行扫描的控制结构。可以看出 Scanner 里定义的行为是 Apply Layer。也就是将对镜像逐层进行扫描。Applier 保存了 Artifact 信息,是联系具体扫描方法和存储信息的结构体。</p><p><strong>3. 解析镜像。</strong></p><p>上述两个过程理解了总体 Scan 的过程控制,以及具体针对镜像的扫描方法。与镜像相关的还有一个关键过程是如何针对性的解析扫描镜像信息。这一过程实现在 Artifact 中。</p><ul><li>Artifact</li></ul><p><img src="/img/remote/1460000043239681" alt="图片" title="图片"></p><ul><li>Walker-Analyzer</li></ul><p><img src="/img/remote/1460000043239682" alt="图片" title="图片"></p><p>Artifact 中有镜像元信息,还有保存解析镜像信息的 Cache 等。主要要考虑的是当前 Trivy 支持的不同种类镜像而进行不同的设置。</p><p>另一关键的是 Analyzer。Analyzer 对应的是镜像分层的操作。对不同种类的镜像操作不同。比如对于 OCI v1 的镜像,可能就是每层镜像拉取的完整数据流进行分析。对于 FileSystem,就是层次遍历文件树。</p><h4>方案选择</h4><p>了解了 Trivy 的实现后,我们发现直接把 Nydus 打包成 OCI v1 类似的镜像去扫描并不合适。Nydus 镜像内容中层次组织已经发生了变化,也具有按需加载的特点。若直接拉取,不一定保证拉取信息的完备,想要完备支持镜像的拉取也会丢失 Nydus 的特性。</p><p>因此我们最终选择在 FS Artifact 方式下优先拓展 Nydus 的镜像扫描能力。</p><p>相关的指令是:</p><pre><code class="Bash">$trivy image nydus-image-url ✗
$trivy fs /path/to/nydus_imgae_mountpoint ✓</code></pre><p>这样做的好处一是可以利用 Nydus 按需加载的特性。我们发现对于很多软件包的扫描并不需要完整的文件内容的下载,很多时候一些局部信息甚至元信息即可判断。这一特点可以利用上 Nydus 的按需加载,从而加快整个镜像扫描的过程。二是特殊格式的镜像都会有挂载文件系统这一操作。这样的方式可以推广到更多的特殊格式镜像。</p><p>本课题最终是以工具形式为 Nydus 集成了加速镜像文件系统挂载的能力,这能够适配主流的镜像扫描框架,未来我们可以考虑为社区镜像扫描方案做更深度的集成,比如 Trivy、Clair、Anchore Engine 等,支持直接指定 Nydus 镜像 Reference 做扫描,优化端到端的用户体验。</p><h4>方案实现</h4><p>在 Nydus 侧提供镜像扫描的支持,简单指令过程为:</p><pre><code class="Bash">$ nydusify view localhost:5000/ubuntu:latest-nydus
/path/to/root_path
[比起容器简单,直接拿到文件树]
$ trivy fs /path/to/rootpath</code></pre><p>Nydusify 是 Nydus 提供的镜像转换,校验与镜像文件系统挂载工具,使用方式可以参考:<em><a href="https://link.segmentfault.com/?enc=zLYTnzhKMlmAP5zqzwImPw%3D%3D.4XGG8%2F9wAbt8G1VTj9SMlLaa%2Fl6roEGchicGnCxc9IcDx2ST54ZMveauQy7TOohO1eRm%2FENPb6bcOl59iPv2Ly0nGRcKo7MjvoErkq0JVPo%3D" rel="nofollow">https://github.com/dragonflyoss/image-service/blob/master/docs/nydusify.md</a></em>。</p><p>上面实现在 Nydusify 中的核心功能由 <code>FileSystemViewer</code> 结构体控制:</p><p>此结构体需要保存的成员变量有此过程的一些输入信息和配置信息。<code>SourceParser</code> 用于解析镜像 url。<code>MountPath</code> 是可以指定的文件系统 Mount 的地址。<code>NydusdConfig</code> 是 Mount 过程 Nydus Daemon 的配置。<code>image-url</code> 是必须提供的输入信息。<code>View()</code> 是调用方法。</p><p>当 <code>nydusify view</code> 被调用时,主要将发生三个步骤:</p><ol><li>解析 <code>image-url</code> ;</li><li>根据解析信息,拉取文件系统元数据 Bootstrap;</li><li>根据 Bootstrap,Nydusd Mount 文件系统到指定路径。</li></ol><p>之后 <code>trivy fs</code> 被调用时,可能发生:</p><ol><li>层次遍历挂载的文件系统;</li><li>打开镜像挂载点中某个文件时,会触发 FUSE 请求到 Nydus Daemon <em>(Nydusd)</em> ,Nydusd 会从远端镜像中心按需拉取文件的 Chunk 数据,以提供给扫描引擎分析文件内容。</li></ol><h2>PART. 3 课题展示</h2><h3>Demo 展示</h3><p>我们实现了这一功能,如 demo 中演示:</p><p><img src="/img/remote/1460000043239683" alt="图片" title="图片"></p><h3>性能测试</h3><p>我们在 Ubuntu、Wordpress、Tensorflow 等镜像上进行了测试。 </p><p>测试结果如下:</p><p><img src="/img/remote/1460000043239684" alt="图片" title="图片"></p><p>以扫描时延作为衡量标准,可以看出 Nydus 的安全扫描时间要显著少于基本 OCI v1 镜像。这其中优化的幅度与镜像文件系统复杂程度,测试网络环境等因素有关。 </p><p>我们还从实际镜像扫描场景了解到,很多时候出现安全问题时,例如一些 0day 漏洞,我们需要对大批量镜像进行特定的少量文件元数据或数据的侦测。这种情况就更能体现出 Nydus 镜像懒加载的优势,安全扫描速度能够大幅度提高。</p><p>当然,Trivy 对 OCI v1 镜像的扫描优化会影响对比结果。我们 Trivy Image 多次对同一镜像进行扫描,可得到下面的结果:</p><p><img src="/img/remote/1460000043239685" alt="图片" title="图片"></p><p>可以看出,多次扫描之后花费的时延下降。如之前提到,扫描信息匹配会存到 local Cache 中。这是会根据 Blob ID 做为 key 来存储的。因此 Trivy 在扫描同一镜像时可以直接去查询 Cache 而不必重复拉取镜像形成优化。我们在未来也想集成类似的优化,这也是之后我们也想在扫描引擎社区推动更好的支持的原因。</p><h2>PART. 4 课题拓展</h2><h3>描述</h3><p>除了扫描镜像内容的安全性,Nydus 本身提供了指令工具 <code>nydus-image inspect</code> 对 Nydus 镜像进行检查。 <code>nydus-image inspect</code> 指令通过检索 Nydus 镜像中包含的文件系统元数据信息,可以查看 Nydus 镜像的组织情况。进一步可以判断 Nydus 镜像是否损坏等。</p><p>随着 Nydus 的发展,它支持文件系统 Layout 也从 v5 扩展到 v6。RAFS v6 可以兼容 EROFS,由此获得性能上的进一步提升。 <code>nydus-image inspect</code> 在 RAFS v5 时已经设计,扩展到 RAFS v6 之后,一些子命令为了兼容进行了扩展。但是这样的扩展是为 v5/v6 分开写成的,也就是需要执行子命令时还需要对文件系统格式进行一次判断。</p><p>此外,还有一些子命令没有做到兼容性扩展。这样的不完全支持存在一定的遗留问题,没有做到功能的完备和统一。这样的代码组织也损失了易读性,不利于后续的维护,不同 RAFS 格式相关功能的迭代升级中也比较容易产生问题。</p><p>事实上,Nydus 已经为这两种格式的文件系统元数据实现了统一的 API 接口。</p><p>比如对 <code>RafsSuperInodes</code>, <code>RafsSuperBlock</code> 等结构有统一的方法进行信息的获取,对不同的底层实现进行了屏蔽。因此在此基础之上,有必要对 <code>nydus-image inspect</code> 指令的相关功能进行一次重构。</p><h3>实现</h3><p>原有的架构中,存在一个 <code>Executor</code> 结构根据子命令判断调用哪一个具体的方法。比如 <code>Executor</code> 收到子命令 stats 时会调用 cmd_stats 函数,执行并完成结果的输出显示。</p><p>我们需要重构的是针对每一条子命令所调用的方法,上层的调用逻辑保持不变,具体而言子命令方法有:</p><ul><li>cmd_stats</li><li>cmd_list_dir</li><li>cmd_change_dir</li><li>cmd_stat_file</li><li>cmd_list_blobs</li><li>cmd_list_prefetch</li><li>cmd_show_chunk</li><li>cmd_check_inode</li></ul><p>这里不再对每一条子命令调用方法的修改进行详细描述。大概做法是使用 RAFS mod 中统一的 API 实现所有原本的功能逻辑。</p><h3>测试</h3><p>我们实现了两种测试。两种测试的目的都是为了保持重构前后功能实现的一致。</p><p><strong>测试一:</strong> 交互形式比对输出结果。</p><p>这是一种功能测试。针对每条指令每种可能出现的情况,选取代表性镜像进行测试与比对。</p><p><strong>测试二:</strong> 集成冒烟测试。</p><p>这是一项冒烟测试。<code>nydus-image inspect</code> 命令还有 <code>request mode</code> 可以将一条子命令的输出序列化成 json 格式的结果。我们将原有代码的结果序列化成 json 格式作为参考输出,再将重构后代码的输出与之一一比对。选取的镜像为 Nydus 仓库中用于冒烟测试的镜像。这一部分的测试也集成在 Nydus 仓库中,在以后的 CI 里也可以保证这项功能的正确性没被破坏。</p><h3>成果</h3><p><strong>Prompt Mode</strong></p><p>Stats</p><p><img src="/img/remote/1460000043239686" alt="image.png" title="image.png"></p><p>ls</p><p><img src="/img/remote/1460000043239687" alt="图片" title="图片"></p><p>Cd</p><p><img src="/img/remote/1460000043239688" alt="图片" title="图片"></p><p>Stat File</p><p><img src="/img/remote/1460000043239689" alt="图片" title="图片"></p><p>Blobs</p><p><img src="/img/remote/1460000043239690" alt="图片" title="图片"></p><p>Prefetch</p><p><img src="/img/remote/1460000043239691" alt="图片" title="图片"></p><p>Chunk Offset</p><p><img src="/img/remote/1460000043239692" alt="image.png" title="image.png"></p><p>Icheck Index</p><p><img src="/img/remote/1460000043239693" alt="image.png" title="image.png"></p><p><strong>Request Mode</strong></p><p>Stats</p><p><img src="/img/remote/1460000043239694" alt="image.png" title="image.png"></p><p>Prefetch</p><p><img src="/img/remote/1460000043239695" alt="图片" title="图片"></p><p>Blobs</p><p><img src="/img/remote/1460000043239696" alt="图片" title="图片"></p><h2>PART. 5 收益与展望</h2><h3>收益</h3><p>我非常荣幸能参加这次项目的开发,也要向项目组织老师赵新、项目指导助理姚胤楠、严松老师以及 Nydus 社区表示衷心的感谢。</p><p>通过这次项目的开发,我收获了许多:知识上,我复习并更深入了解了文件系统,学习了容器镜像格式的组织,还首次尝试了 Go 语言开发,也开始了解软件测试;技能上,我锻炼了协调时间,开放讨论,问题定位的能力。</p><p>更重要的是,我认为参与这次项目增加了我对容器场景应用的见识,也提升完整解决方案与系统设计的能力。当然,可能最大的收益是收获了一次快乐的体验!体会了开源合作的快乐,也体会到了开源价值产生的快乐。</p><h3>展望</h3><p>此次项目主要是为 Nydus 添加了镜像扫描功能的支持,另外重构了 Nydus 的 Inspect 分析工具。如前文所说,后续还要继续探索 Nydus 镜像扫描集成到扫描引擎的方式,镜像扫描和修复的优化也值得探索。关于 Nydus 镜像格式细节,用户态文件系统,EROFS 等,我还有很多需要学习。</p><p>希望能一直参与社区,不断丰富自己容器存储方面的知识,同时也能为社区做出更大的贡献。</p><p><strong>了解更多...</strong></p><p><strong>Nydus Star 一下✨:</strong> </p><p><em><a href="https://link.segmentfault.com/?enc=wth9MeZfdI%2F8zp3RLVvwGA%3D%3D.5ASB56F%2FhRq6n1DEH0NPcCwlkSWHC%2FNqAIUQiavZNUH%2BWr%2BpyQQ7UIKibNGI7xhs" rel="nofollow">https://github.com/dragonflyoss/image-service</a></em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=NEiR5Lmb8u8bXsXrBfoRqw%3D%3D.hoa%2BG2cuEqxnfGVaSxwC5N5UNyA5U1e8WTRXcpvVsxQwhB5P2mhJTYCTXEnjGegJHpCzhrF7cAaETL2L30N1MHfIgxglKuK1J%2B8n0Il6iaaYsID%2Fi4QXOFkwL2XcQc0Su%2FGxcDYFeNn0MG7jbwRcamhRGRKU2EbrqWPLXXYZhseeB2ZxKgib8hB9wT6dQHQo5B%2B2VFpMMfTMOTUnpa1I74JwJgjBWrP7QfoSem3i%2FqIbOsOBVOcZh%2FrRgDXsLl%2B38wt1IhENCynC9QDVIsyXWw%3D%3D" rel="nofollow">Nydus —— 下一代容器镜像的探索实践</a></p><p><a href="https://link.segmentfault.com/?enc=KNpbbPEOwvgLdvrEHf3PGA%3D%3D.Frsc%2F6qSb3BLucWlvYbRnu8I%2Bvn0r95Gye6Mf5k32yK76r8mCAiNSrhHVgbpbwRX%2BCa1nrROTVHQk9kURfcm40jo7JPamzotBu2nEpAKBQBGHRP6wBUdUuKaxU5h5H5BAFQoBfCHEIuxKk3%2BUMmhs6BFNgvFQEzcBuFXXnbo7%2Bef%2B3589OsWbpHW9e%2BtqDSlGU1rKX834wVUvy6rFJyOiyBWg%2FRodoFzYu6Pl5WrtnwSTHjkkYSxcTkh85hDwjddcChCIA5JS9t8xGdZZ7Alpw%3D%3D" rel="nofollow">Nydus 镜像加速插件迁入 Containerd 旗下</a></p><p><a href="https://link.segmentfault.com/?enc=uTTTJ%2Fx4MkxJ%2FmXluPJywA%3D%3D.J5AMkeRjv9POUUsONS8lrBF%2B%2FVTK7wMyGa%2Bw8bZF3vLltnFV1S9H05UGmtmF6a6CnZVr1uUFtCHr0kfbvjyxiVeCp4b3slkIA47CZEW2wzJ94WYfUuY6r05vysC%2Bk9Ldr5n%2FzWcsQwMQafijB5OsA3B5D0emz6dbXOkxLWMjZ%2BAlnMlZaNWrNwToVKgb74ndfOIG1BcRbf6BXzha6EOMLL30H3aQUYL04Tn7%2FdJeuoHfvjrIxH71PX%2BpmMFelZQng8%2FzcMBxlo7zIGlhy%2FYghA%3D%3D" rel="nofollow">Nydus | 容器镜像基础</a></p><p><a href="https://link.segmentfault.com/?enc=J9ATkTOvoBiNO1y%2B%2BqVvOg%3D%3D.ykZudaleZ5Qr1NHBUG6V8%2BmEG0HsAIvm7eVSMKiSuyTvX2pd0GJe2kaJ%2Faq1rZ3y5z2PMD%2FUsnQU7BcYwDuMECTT2aRLPTx6j9gMSewvFbWu2g1YHrL4goW8R2k2%2FbAwX%2BRYDARR04FGk%2BT4uhTIhf5UniK0UhFVv9Xr1vXysUgT2JFBa0LemQsClP7U2E32mC5%2FtqCU980Ysz4Vme5NCMqxojbjUIxeIf2Hp7wxuD9%2Fr4w7lcpbw%2B1uP1Kvda0D6%2FnJF0WuP9kDUvEuINtbDw%3D%3D" rel="nofollow">Dragonfly 和 Nydus Mirror 模式集成实践</a></p>
10 分钟带你一览 SOFAStack 的 2022!
https://segmentfault.com/a/1190000043207913
2023-01-02T13:00:57+08:00
2023-01-02T13:00:57+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<h2>SOFAStack 社区 2022 开源年报来啦~~~</h2><p><em><a href="https://link.segmentfault.com/?enc=42UQl6qSP5T4zqO2HnfJjg%3D%3D.5pupel3ay92HHHPPqpHceWa8%2Bbd3Px%2F%2F00vN0O7RVLRMZ63ZGsMcZSTzgfo4FXue" rel="nofollow">点击此处参与 SOFA 年度问卷</a>还有机会获得社区 <strong>2023 限量新年周边哦!</strong></em></p><p><a href="https://link.segmentfault.com/?enc=V9mciB4V0QlErvfZFBF0Ug%3D%3D.HGgJMJjPcTnR6suky5WDFA9c8wPL2TfXfaufqACn%2BNLvBEK8XrUgz6P8YbRO%2Bhzw" rel="nofollow"><img src="/img/remote/1460000043207915" alt="screenshot" title="screenshot"></a></p>
已来到 “后云原生时代” 的我们,如何规模化运维?
https://segmentfault.com/a/1190000043151270
2022-12-26T12:13:17+08:00
2022-12-26T12:13:17+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043151272" alt="图片" title="图片"></p><p>文|李大元 <em>(花名:达远)</em></p><p>Kusion 项目负责人 </p><p><img src="/img/remote/1460000043151273" alt="图片" title="图片"></p><p><em>来自蚂蚁集团 PaaS 核心团队,PaaS IaC 基础平台负责人。</em> </p><p>本文 <strong>4331</strong> 字 阅读 <strong>11</strong> 分钟</p><h2>PART. 1 后云原生时代</h2><p>距离 Kubernetes 第一个 commit 已经过去八年多了,以其为代表的云原生技术早已不再是什么新技术,而是现代化应用的“标配”。现代化应用依赖的基础服务远不止 Kubernetes 一种,稍微复杂点的应用往往会同时使用到 Kubernetes 生态云原生技术、IaaS 云服务、企业内部自建系统等各种异构基础设施,可能还会有多云、混合云的部署需求,我们已经进入到了 ”后云原生时代”,只针对 Kubernetes 的运维工具早已不能满足我们的诉求。</p><p><img src="/img/remote/1460000043151274" alt="图片" title="图片"> <br>更复杂的是,在企业内部,这些服务一般是由不同团队维护的,一次规模化运维需要多个团队的成员互相配合才能完成,但是 App Dev、Platform Dev、SRE 各个团队之间缺少高效的协作方式。技术自身的复杂性加上低效的团队协作,使得 “后云原生时代” 的规模化运维难度有了指数级的提高。</p><h2>PART. 2 规模化运维的问题一直都在</h2><p>复杂异构基础设施的规模化运维,这并不是后云原生时代特有的问题,自分布式系统诞生以来,一直都是一个难题,只是在后云原生时代,这个问题变得更加困难。十多年前业界提出了 DevOps 理念,无数企业基于此理念构建了自己的 DevOps 平台,希望解决此问题,但在实际落地的过程中往往不尽人意,Dev 团队和 Ops 团队之间如何合作?职责如何划分?几十人的平台团队,如何支持几万工程师的运维诉求?底层基础设施复杂多样,能力日新月异,如何快速让一线 Dev 享受到技术红利?这些问题一直没有得到很好的解决,最近又有人提出了 DevOps 已死,Platform Engineering 才是未来的说法。抛开概念定义,无论是 DevOps 还是 Platform Engineering,本质上都是企业规模化运维这同一命题下的不同理念,我们更需要的是一个符合技术发展趋势,能解决当前问题的解决方案。</p><h2>PART. 3 传统架构不再适用</h2><p>在传统的运维思路中,解决上述问题的方法一般是构建一个 PaaS 平台,例如我们早期的蚂蚁 PaaS 平台,他是一个 Web 控制台,有 UI 界面,用户(通常是 App Dev 或 SRE)通过 UI 交互可以完成诸如发布、重启、扩缩容等操作。在技术实现上大体可以分为三部分,一个前端系统,提供用户交互的能力,作为系统入口;中间是一个后端系统,对接各个基础设施;底层是各个基础设的 API。这种架构运行了近十年,一直运行的很好,既有用户友好的交互界面,又可以屏蔽基础设施复杂性,而且各个团队之间还职责分明,但是到了如今后云原生时代,这种架构不再适用,暴露出有两个致命缺点, <strong>“费人”</strong> 、 <strong>“费时”</strong> 。</p><p><img src="/img/remote/1460000043151275" alt="图片" title="图片"></p><p>举一个常见的例子,网络团队为其负责的 Loadbalancer(负载均衡器)开发了一种新的负载算法,需要提供给用户使用。</p><p>在上述架构下,整个工作流程是这个样子:</p><p>1.网络团队开发好能力,提供出 API;</p><p>2.PaaS 后端通过编码对接底层 API,进行互联互通,屏蔽复杂性,提供面向用户的更高级别 API;</p><p>3.PaaS 前端根据新功能修改 UI,利用后端 API 把能力透出给最终用户。</p><p>这里存在一个问题,即使一个再小的功能,也需要 PaaS 后端和前端修改代码,整个流程下来最快也要一周才能上线,而且涉及的基础设施团队越多,效率越低。这个问题在十年前并不算是一个问题,但是在今天就是一个很大的问题,一个后云原生时代的现代化应用,使用三个云原生技术(Kubernetes + Istio + Prometheus),两个云服务(Loadbalancer + Database),一个内部自建服务,已经是一个很常见的形态了,复杂应用只会依赖的更多。如果每个基础设施都由 PaaS 团队硬编码对接一遍,PaaS 团队的人再扩大十倍也不够用。</p><p><img src="/img/remote/1460000043151276" alt="图片" title="图片"></p><p>“费人”讲完了,我们再看看“费时”的问题。上面例子里的一个小功能,需要进行两次跨团队的协作,一次基础设施和 PaaS 后端,另一次是 PaaS 后端与 PaaS 前端。团队协作是一个很难的问题,有时候比技术本身还要难。应用的架构已经很复杂了,如果要做一次规模化运维,一次运维 100 个应用,这要和多少个团队沟通协作?要花费多少时间?没有好的协作机制,这就变成了一个不可能完成的任务。</p><h2>PART. 4 探索与实践</h2><p>我们在蚂蚁集团内部进行了近两年的探索,kustomize、helm、argoCD、Terraform 这些常见的工具我们都实践过,甚至还为一些工具自研了一些辅助系统,但结果并不尽人意。这些工具要么太局限于 Kubernetes 生态,运维不了其他类型的基础设施,要么就是支持了异构基础设施,但对于 Kubernetes 生态支持的不友好,无法发挥出云原生技术的优势,而且只是运维工具的升级对于团队协作效率几乎没有提升,我们需要一套更加体系化的方案。</p><p>回到问题本身,针对“费人”、“费时”的问题,我们提出两个思路:</p><p>1.能否不让 PaaS 做中转,而是由 App Dev 通过一种高效自助的方式,使用到互联互通的各种基础设施能力?</p><p>2.能否构建一个中心化的协作平台,用技术的手段规范大家的行为,标准化的进行沟通?</p><p>从技术的角度来看,PaaS 平台需要提供灵活的工具链和工作流,基础设施所有能力都通过模块化的方式暴露出来,App Dev 利用这些平台的基本能力,自己组合、编排来解决自己的问题,过程中不需要平台层的参与。并且整个过程中涉及的所有团队,都使用统一的语言和接口进行交流,全程无需人工参与。</p><h2>PART. 5 我们的实践</h2><p><img src="/img/remote/1460000043151277" alt="图片" title="图片"></p><p>经过在蚂蚁内部 PaaS 平台近两年的探索与实践,沉淀出一套端到端的完整方案, 取名为 KusionStack,目前已经开源。试图从统一异构基础设施运维与团队协作两个角度解决传统 PaaS “费人”、“费时”的问题。</p><p>整个体系主要分为三个部分:</p><p>1.Konfig:Git 大库,是多团队协作的中心化平台,存放着各个团队的运维意图;</p><p>2.KCL:蚂蚁自研的配置策略 DSL,所有团队间交流的工具;</p><p>3.Kusion:KusionStack 引擎,负责所有的运维操作。</p><p>Platform Dev 通过 KCL 定义好基础能力模型,App Dev 通过 import、mixin 等语言特性在应用配置模型(AppConfig)里复用这些预定义好的能力,快速在 Konfig 里描述运维意图。AppConfig 是一个精心设计过的模型,只暴露 App Dev 需要关心的属性,屏蔽基础设施的复杂性。</p><p>永远不要低估基础设施的专业性与复杂性,即使已经成为云原生技术标准的 Kubernetes,对普通用户来说依然有很高的门槛,一个 Deployment 就有几十个字段,再加上自定义的 labels、annotations 就更多了,普通用户根本无法全部理解。或者说普通 AppDev 不应该去了解 Kubernetes,他们需要的只是发布,甚至不需要关心底层是不是 Kubernetes。</p><p>AppConfig 经过编译后会生成多个异构基础设施的资源,通过 CI、CLI、GUI 等方式把数据传输给 KusionStack 引擎。引擎是整个体系的核心,负责所有运维操作,是运维意图真正生效到基础设施的地方。他通过统一的方式对接异构基础设施,并对这些资源进行校验、编排、预览、生效、观测、健康检查等一系列操作。</p><p>值得一提的是,整个过程对运维 Kubernetes 资源非常友好。使用过 Kubernetes 的同学都知道,由于其面向终态、自调和的特点,apply 成功并不代表资源已经可以用,需要等资源调和成功后才能提供服务,如果调和失败了,还需要登陆到集群中,通过 get、describe、log 等命令查看具体报错,整个过程十分繁琐。</p><p>我们通过技术的手段对这些操作进行了简化,把调和过程中的重要信息以用户友好的方式透露出来。下面的动图是一个简单的示例,当命令下发后,可以清晰的看到所有资源及其关联资源调和过程,直到资源真正的可用。</p><p><img src="/img/remote/1460000043151278" alt="图片" title="图片"></p><p>整个体系有如下几个特点:</p><p><strong>1.以应用为中心</strong></p><ul><li>应用全方位配置管理,包括计算、网络、存储等所有与应用有关配置;</li><li>应用全生命周期管理,从第一行配置代码到生产可用。</li></ul><p><strong>2.统一运维“后云原生时代”应用的异构基础设施</strong></p><ul><li>Kubernetes 友好的工作流,为 Kubernetes 资源提供可观测性、健康检查等高阶能力,释放云原生技术红利;</li><li>复用 Terraform 生态,统一的工作流运维 Kubernetes、Terraform 多运行时资源。</li></ul><p><strong>3.规模化协同平台</strong></p><ul><li>灵活的工作流,用户可利用平台的基本能力,自己组合、编排来解决自己的问题;</li><li>App Dev 和 Platform Dev 关注点分离,底层能力迭代无需平台介入,直接供 App Dev 使用;</li><li>纯客户端方案,风险“左移”,尽早发现问题。</li></ul><h2>PART. 6 一切才刚刚开始</h2><p>这套体系经过近两年的探索,已广泛应用在蚂蚁多云应用交付运维,计算及数据基础设施交付,建站运维,数据库运维等多个业务领域,目前 400+ 研发者直接参与了 Konfig 大库代码贡献,累计近 800K Commits,其中大部分为机器自动化代码修改,日均 1K pipeline 任务执行和近 10K KCL 编译执行,全量编译后可产生 3M+ 行的 YAML 文本。</p><p>不过,这一切才刚刚开始,后云原生时代也才刚刚到来,我们把这套系统开源的目的也是希望邀请业内各方的力量,一起构建一个符合技术发展趋势,能真正解决当下企业规模化运维这个难题的解决方案。蚂蚁 PaaS 团队还有很多经过内部规模化验证的技术沉淀,后续也会开源出来,只有我们远远不够,真诚地邀请大家一起来玩。</p><p><strong>| 相关链接 |</strong> </p><p>Kusion:</p><p><em><a href="https://link.segmentfault.com/?enc=WthkWPT7JnYLzEsksGF%2BMQ%3D%3D.6HmHYxNJqqQ5Q%2F8O1u16KkGTNK4UokZiuc9E3z2Smz%2FjP7jFNnYxwivnhdXbYt0S" rel="nofollow">https://github.com/KusionStack/kusion</a></em></p><p>KCL:</p><p><em><a href="https://link.segmentfault.com/?enc=BJjLFAvPrhWPSAOnR7QuUQ%3D%3D.qYUHE9LnA6iZdnAy6SsM7SDPrLdjnchQ%2BFd0J5tN7SPipzpXw3fcvyiD20O%2BbCFv" rel="nofollow">https://github.com/KusionStack/KCLVM</a></em></p><p>Konfig:</p><p><em><a href="https://link.segmentfault.com/?enc=GcubXrX2rlsmGqTIuwH9VA%3D%3D.KtsTHxY%2BFjjE96C%2FCLYHGAMXL3wdHCkfJcl3omRXU6N4Bo0lTYdXQuTxpghtPHv7" rel="nofollow">https://github.com/KusionStack/konfig</a></em></p><p>官网:<em><a href="https://link.segmentfault.com/?enc=Aq7mVhXYreu07ceJoDhWrA%3D%3D.%2Bz8r645IofZqLWanoaQb3l471u9I%2F84avH%2Fd5b%2FbBy8%3D" rel="nofollow">https://kusionstack.io</a></em></p><p>PPT:KusionStack:“后云原生时代” 应用模化运维解决方案</p><p><em><a href="https://link.segmentfault.com/?enc=YAHOtduWU53zQHqr29rLfw%3D%3D.8FXE50iAUPHZiU9IBA%2FNLL2YQy90nHGNUwFA77nS5pbP%2F9ZhCUNUscl5QPulsHFU1kxljmugYHQrM1hUoPlU7szDGdB2gYwF3n9oMmZkL8gIW7UwtDwHTBm9GU3ZWWbvZrkv8kA%2BX53qyw9RHE2UrQ%3D%3D" rel="nofollow">https://kusionstack.io/blog/2022-kusionstack-application-scale-operation-solution-in-the-post-cloudnative-era/</a></em></p><p><strong>了解更多...</strong></p><p><strong>KusionStack Star 一下✨:</strong> <br><strong><a href="https://link.segmentfault.com/?enc=xB7ADUW1zuSiHi7XiWbl0g%3D%3D.NUck%2FzIbvOnDtLHxDshh%2FZRvIBDQ4%2BIwaKc%2FtigVLx127OCnU7KZ5lMjxPHFR%2FQl" rel="nofollow">https://github.com/KusionStack/kusion</a></strong></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=HQ8s9l7i6kZj%2FddIQKpS2g%3D%3D.j2xdlmJwAkzTia%2FynGHMm57ly8EpmaAwHUf1slWXOvEVxcyQmBtDqOLrwpv9SSRrGXoSuSZuCtwRjvH4oOEl%2F7os9PI9sirPmn3ljxuJ701ZeJVzNzAgQjaqbeFzT2CXM7I2xGBI1brK%2BXMpATOLQMcInSAODezL%2Fu5ewM0UyRVUwjLfKyuRF8rbJrnXaR2eCr4bXINIlX6G6GXVzziAgA1BXxS%2FSl2rgvhaEAU%2FHPzFWazXU9hQCmQCD1ECA5%2BAp9zEazQLdVwQ8mdzPCe6lQ%3D%3D" rel="nofollow">从规模化平台工程实践,我们学到了什么?</a></p><p><a href="https://link.segmentfault.com/?enc=5LLINa15p8JgVi4CgxswHw%3D%3D.cVt%2FH5UCK6ZGlq6%2Fu9D%2ByE6gjeG2KrcUjDCKorjemVeAVpSzDyovyu49qDsge0HKttYs2U3USB3GkwiMlAjeXFYeH0pCKRnjisHmGiB8cJJnu5KcDIIbQ4Oo2QtaqGja9F5wXNjo5rb65Zcj9kVAs3nyLFXW75AIpxoYHnqUYKYCtQloG9lKhGLJSfMx8ndfJHv40vyEcrwVC%2F5UdkKvq50hPOftRgjvEwexu1h0Z1NII368ylAhLQCLO9%2BFRry0JjA0FwzXzDRLRcloxvHwAw%3D%3D" rel="nofollow">KusionStack 开源有感|历时两年,打破“隔行如隔山”困境</a></p><p><a href="https://link.segmentfault.com/?enc=2u388SCotkvn9ec83j5wwg%3D%3D.FcYvMJbFkmUJrTGChip0bclKuVkrq0yHwmPtGtbcOQTK4RFM0Uy3EGomQMVrzIKikLZRebxawUqIN44Jh9LMeHTDcjM9KbbEGeCgYQdO9tjvGdYN2raxTOJ%2F2tPJ3tN3KEF1M9E5FK9K3kZXDff38kdw9tbVDh5kZk1HySra03N8puUY8CCHYIEhfhgnbGklMnbH61z3bTIdpDM7T9kwadJFIKVUbmyppPHdzOvmZVtfKRPOhqrdkpE9bmFn0qWyNAgrgDLP88Udi7kCc%2FUzcw%3D%3D" rel="nofollow">Go 代码城市上云——KusionStack 实践</a></p><p><a href="https://link.segmentfault.com/?enc=Jn1P%2FR38n9BIYPajianvHg%3D%3D.CnP1vesYqb2PKa5kZ7VbW4bjoSgfEnnqwPze%2B1PY%2FIsAV%2FdyTaG7SQSCIXBim1%2BPW3Cym0T6hjb207DTUhGBLU4y7GSmiURJ5PdsMfttlw%2BRSEcL1xugx3eAMQIjZKBNcPGgo6334VwlnY8Eui5csofrgBi9Y5gOPPkBfShgiZdudtMTvfsI%2FX0xQ8PaNrzCfkqxjGtNwznymB0RKTRik7KQ%2Bn4NigDJIRd54g8hyiOU7m2fQ5Ehy0kdBVS5uJ7d2QrJ7fQvxW4sz9Wy8jF81Q%3D%3D" rel="nofollow">KusionStack 开源|Kusion 模型库和工具链的探索实践</a></p>
降本增效: 蚂蚁在 Sidecarless 的探索和实践
https://segmentfault.com/a/1190000043077267
2022-12-14T13:23:06+08:00
2022-12-14T13:23:06+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000043077269" alt="图片" title="图片"></p><p>文|王发康 <em>(花名:毅松 )</em></p><p>蚂蚁集团技术专家、MOSN 项目核心开发者 </p><p><img src="/img/remote/1460000043077270" alt="图片" title="图片"></p><p><em>深耕于高性能网络服务器研发,目前专注于云原生 ServiceMesh、Nginx、MOSN、Envoy、Istio 等相关领域。</em> </p><p>本文 <strong>5574</strong> 字 阅读 <strong>14</strong> 分钟</p><h2>前言</h2><p>从单体到分布式,解决了日益增长的业务在单体架构下的系统臃肿问题;从分布式到微服务,解决了服务化后的服务治理问题;从微服务到服务网格,解决了应用和基础设施耦合下的研发效能及稳定性问题。</p><p>从微服务架构的演进历史来看,任何架构都不是一成不变,总是随着应用在不同阶段的痛点以及周边技术的发展而持续演进,而服务网格 <em>(ServiceMesh)</em> 概念从提出到生产落地至今已 6 年多了,那它的下一代架构应该是什么样?对此业界也有不同的声音 <a href="https://link.segmentfault.com/?enc=LLJVlJgXRBnFX4a%2BdYHAsw%3D%3D.0fCUmQw7bUPFItRYma8dgVJnOHR8bPisA5FAev7eM52AmKDjazrd4salMyLZ8Ut5%2BKmtH3yVnj%2BR3PWGyYrzT3lYOGIHETgfw1U3Tm6WQbO6Z8IBw%2FVzS%2B3ZLQbTwyNUrJqBJf2uBGfI3%2BqndVKc%2BAVLyrMNLDkTYp67S0w%2BWpfCew4V%2BzFhNFn4qU1QSmfIiszvBIDaf7wM94GbnSUX%2BW6x%2Ba6Czt7LOPwu3PzKbXJdW2Qe5kOdPkZo8dhoEJ8%2FQiz3nkf0Kk3MErCrLRp6SCSSbwfLWsHlcS3LD77hJzo%3D" rel="nofollow">Service Mesh 的下一站是 Sidecarless 吗</a>[1] ,本文主要介绍蚂蚁在这方面的探索和实践,最终如何帮业务降本增效,提升安全保障水位。</p><h2>一、 问题及挑战</h2><p>谈起 ServiceMesh,大家的脑海中应该会呈现出下面这幅图,即 ServiceMesh 的 Sidecar 形态,它非常好的解决了应用和基础设施耦合下的系列问题,为业务的提效及稳定性等方面然禹功不可没。</p><p>但是随着 ServiceMesh 的规模化增大,Sidecar 架构也随之暴露了一些劣势,譬如应用和 Sidecar 资源耦合导致相互抢占、生命周期绑定,导致应用扩缩容涉及额外 Sidecar 容器的创建及销毁开销、Sidecar 数量随着应用 Pod 数等比增加,导致其资源无法充分复用等。</p><p><img src="/img/remote/1460000043077271" alt="图片" title="图片"></p><p>引用 <em>redhat.com/architect/why-when-service-mesh</em></p><h3>1.1 资源耦合</h3><p>Sidecar 形态虽然从代码维度是解耦了基础设施与应用的耦合,但是在资源方面目前仍然是和应用 Pod 绑定在一起,这就导致其资源管理成为一个难题。对此蚂蚁 ServiceMesh 在 Sidecar 资源管理[2] 进行改善,解决了初期的资源分配及管理。但随着业务的发展也暴露一些隐患,一方面 Sidecar 和应用相互抢占 CPU 可能导致引发时延毛刺问题,另一方面固定的 1/4 内存资源可能无法满足机房规模增大引发的网络连接数膨胀等系列问题。</p><p><img src="/img/remote/1460000043077272" alt="图片" title="图片"></p><h3>1.2 生命周期绑定</h3><p>Sidecar 和应用属于同一个 Pod,而 K8s 是以 Pod 为最小单位进行管理的。这导致应用在扩缩容操作时会涉及额外 Sidecar 容器的创建及销毁开销,而 Sidecar 中的进程是基础设施软件本不应该随着应用的销毁而销毁。</p><p><img src="/img/remote/1460000043077273" alt="图片" title="图片"></p><h3>1.3 资源复用不充分</h3><p>Sidecar 数量随着应用 Pod 数等比增加,而 Sidecar 上运行的都是基础设施通用能力,这将导致 Sidecar 中应用无关逻辑的 CPU 和内存等开销都在每个 Pod 中重复消耗。另外对于一些本能通过软件集中优化或硬件集中卸载的计数也无法充分施展。</p><p><img src="/img/remote/1460000043077274" alt="图片" title="图片"></p><h3>1.4 安全及业务侵入性</h3><p>Sidecar 同应用同属一个 Pod,这无论对于 Sidecar 还是应用自身都是增大了安全攻击切面,另一方面 Sidecar 形态的服务治理也不算是完全业务无感的,至少要做一次 Sidecar 的注入,然后重启应用 Pod 才生效。</p><p><img src="/img/remote/1460000043077275" alt="图片" title="图片"></p><h2>二、思考及分析</h2><p>对于上述 Sidecar 形态下的四个痛点:资源耦合、生命周期绑定、资源复用不充分、安全及业务侵入性,其实归根结底还是其架构导致。</p><p>如果将 Sidecar 从应用 Pod 中剥离出来,对于资源耦合、生命周期绑定、安全侵入性问题都会迎刃而解,而资源复用粒度取决于剥离后部署的集中化程度。对于剥离后的集中化程度也并不是越集中越好,因为中心化后会增加交互时延以及能力的完整性缺失,所以在中心化和 Sidecar 化之间做一次折中,即通过 Node 化部署形态来解决,这样既可以做到同 Node 上的 Mesh 资源复用,又可以做到网络交互不跨 Node 的相关优化。</p><p><img src="/img/remote/1460000043077276" alt="图片" title="图片"></p><p>虽然解决了 Sidecar 形态下的痛点,但是 Node 化后也引入了一些新的 “问题”。对于问题我们需要用辩证的眼光去看待,有时问题的背后可能是一个新机会。</p><p>譬如 Sidecar 形态下的服务发现都是 Pod 级别,随着 Pod 规模的增大其服务条目成倍速增加,如果 Node 化后服务发现使用 Node IP 这样是不是可以成本的缩减服务条目,亦达到网络连接数的复用及收敛。</p><p><strong>- 隔离性、多租户:</strong> 多个应用共享 Mesh 后,涉及的一些应用维度的配置、内存、CPU 等资源如何相互隔离,另外对于 Sidecar 中各种基础设施组件如果自身来支持多租户,则改造成本是不可预估的。</p><p><strong>- 服务 pub/sub:</strong> 之前 Sidecar 形态下,Sidecar 和 APP 的 IP 是同一个 <em>(Container 网络模式)</em> ,Node 化后 IP 变成 Daemonset 容器的 IP,服务发现应如何管理?以及 Pod 和 Node 之间的流量如何管理。</p><p><strong>- 运维及安全合规</strong></p><p> a. Node 化后涉及到 Daemonset 自身以及应用维度的配置升级发布流程如何管控,同时出现故障时如何缩小爆炸半径; </p><p> b. Node 化后涉及到的安全合规问题如何保证,如网络连通性如何隔离、身份等;</p><p> c. Node 化后,Daemonset 所占用的机器成本如何规划 <em>(超卖?独占?)</em> 以及各个应用的资源消耗如何计算。</p><h2>三、方案介绍</h2><p>将应用 Pod 中的 Sidecar 下沉到 Node,以 Daemonset 方式在每个 Node 部署。我们将该 Daemonset 命名为 NodeSentry,其定位是作为 Node 的网络基础设施,承载网络安全、流量治理、Mesh 下沉、连接收敛等 Node 相关网络治理平台。</p><p>本文主要介绍 NodeSentry 承载 Mesh 下沉相关方案,Node 化后需要数据面 Proxy 能够高效、稳定的承载多个 Pod 的流量治理。这就要求数据面 Proxy 具备高处理性能及高研发效能,为此我们在 2021 年就其做了相关技术布局 MOSN2.0,详细介绍见 <a href="https://link.segmentfault.com/?enc=XgGhuxPgoj0sGYF0pNnfqQ%3D%3D.cxGyU7%2FokfQu5cUadmLj2Fa%2BrgxvWwks%2FRKIsVxStbpigI4e2PACBZ2Y7Z6c6tZPkjZHY2RwZ1d%2BqYiSFMaJrVLxE0XtOqQ5cgLsqPt9uA1AlQ7pZXRJNPM4h1iyDItoxFdbVXflnHhp2bkXYqXM7O3S7jWbzLEtcvsOM1NiVJ63sotVMeQM7QdiT0tT7FiGS%2BE09BhG%2FBhjI%2FOmZEH6ff7c3VKjcXwirH0dJJ3JPOlw1KGmJqHDzL7lKHT1d9a%2B9%2FHugX9ZOlZH9wEynu3YvjC9oLvZtkFM3QvgcwefDNg%3D" rel="nofollow">开启云原生 MOSN 新篇章 — 融合 Envoy 和 GoLang 生态</a>[3] 。</p><p>Mesh 下沉至 NodeSentry 其架构如上图所示,在新的架构下不仅能解决 Sidecar 形态的痛点,同时也具备了如下收益:</p><p><strong>- 资源解耦</strong>:解耦应用和基础设施的资源配额,多应用的 Mesh 资源池化、削峰填谷;</p><p><strong>- 资源复用</strong>:同 Node 同应用 Mesh 共享,ingress 方向连接收敛 、egress 方向连接共享、Node 级粒度 cache 命中率更高等;</p><p><strong>- Node 服务发现体系</strong>:解决注册中心等基础设施服务端压力以及调用端的内存消耗;</p><p><strong>- 提升启动速度</strong>:新架构下应用无需每次都启动 Mesh 的容器及镜像下载、SDK 初始化共享等额外操作;</p><p><strong>- 集中优化</strong>:云原生 L7 网络协议栈,软硬件结合性能优化,支持 IPV6,蚂蚁卡,RDMA or QUIC 等;</p><p><strong>- 解耦应用和网络:</strong> 同 Zero Trust Networking[4] 相关技术结合实现全局调度,有限互通等。</p><h3>3.1 微隔离/多租户</h3><p>利用 MOSN 2.0 的高性能网络处理能力,在其之上以动态库的方式拉起各个应用的 Mesh 实例 <em>(注:多个 Mesh 实例和 MOSN 2.0 是在同一个进程中,如下图所示)</em> 。</p><p>在资源上做到各个 Mesh 实例之间 runtime 级别的微隔离 <em>(如 GC、P、信号等资源)</em> ;在配置方面通过对 runtime 环境变量的劫持做到各个应用实例的差异化;在稳定性上通过劫持 runtime exit 来避免 APP1 的 Mesh 实例挂掉影响 APP2,缩小爆炸半径等。</p><p>同时也通过单进程多实例的方式支持了基础设施组件的多租户能力,更多细节见一种进程内的多租户插件隔离与通信技术[5] 。微隔离及多租户架构,如下图所示,当流量从 MOSN 2.0 进来后,通过一定的策略路由到各个应用的 Mesh 实例进行后续处理。</p><p><img src="/img/remote/1460000043077277" alt="图片" title="图片"></p><h3>3.2 同应用 Mesh 共享</h3><p>对于同一个 Node 上同一个应用的多个 Pod 可以共享 NodeSentry 上面的 ServiceMesh 实例,如下图所示应用 A 和 B 各自两个 Pod 分别共享 NodeSentry 上的 2 个 Mesh 实例。</p><p>这样既解决了不同应用 Mesh 差异 <em>(如证书等配置、身份信息)</em> ,又达到资源共享,避免同一个应用依赖的资源多次初始化开销。当然共享后的收益也不止这些,比如网络连接复用收敛、资源池化削峰填谷以及降低基础设施服务端压力等等。</p><p><img src="/img/remote/1460000043077278" alt="图片" title="图片"></p><h3>3.3 流量劫持</h3><p>流量转发这块分为 ingress 和 egress 场景 <em>(注:基于 APP 侧视角)</em> ,从 Sidecar 演进为 Node 形态后,应用 Pod 和 Mesh 之间的流量转发路径发生了改变。为了屏蔽该变化对业务的感知,我们通过流量劫持组件将其 Pod 流量劫持到 NodeSentry 上然后分发到对应的 Mesh 实例进行后续的处理。</p><p>关于流量劫持组件目前由于蚂蚁这边的容器有多种形态 <em>(runc、runsc、rund 等)</em> 所以单纯的 ebpf 或 tproxy 方案并不适用,当前阶段我们是通过在应用容器中注入相关的环境变量来显示指定流量转发到 Node 上,未来对于流量劫持这块我们会通过策略路由的方式来更透明的支持流量劫持及身份管理。</p><h3>3.4 资源管理</h3><p>对于 NodeSentry 资源这块,目前阶段我们是小规模试点阶段,相关资源是和同 Node 上的 Daemonset 共享的,但是随着接入应用的增多,这块资源是需要有一定保障的。当前业界 ambient-mesh[6] 通过将 L7 进一步从 Daemonset 剥离到中心化 Gateway 通过 HPA[7] 的思路来解决容量问题。</p><p>如下介绍的是 NodeSentry 后续如何通过 VPA[8] 方案进行 Daemonset 的容量规划。在应用扩容时 NodeSentry 资源如下做 VPA 弹性管理,缩容流程类似: <br>1、在扩容 Mesh Node 化应用时,Kubectlscheduler 选择资源满足 A+B 的 Node 执行 Pod 调度。</p><p><strong>-</strong> 代表应用自身需要的资源</p><p><strong>-</strong> 代表该应用在 mesh 上需要消耗的资源</p><p>2、满足条件的 Node 在创建应用 Pod 的同时触发 NodeSentry 资源进行 VPA 弹性扩容。</p><p><img src="/img/remote/1460000043077279" alt="图片" title="图片"></p><h3>3.5 运维管理</h3><p>从 Sidecar 下沉为 Node 模式后,我们在应用发布运维流程上也做了一些适配,用来屏蔽其流程的变化导致的运维成本及用户体验问题。</p><p>对于应用扩缩容、应用重启、MOSN 重启、MOSN 版本升级等运维操作通过 Container Lifecycle Hooks[9] 提供的 postStart 和 preStop hook 点嵌入相关交互流程完成 NodeSentry 上的 Mesh 实例管理。</p><p>另外一块是 NodeSentry 自身底座升级,目前是通过 K8s console 触发 Node 上的相关应用关流,然后进行 NodeSentry 升级,待其过程就绪后再恢复 APP 引流。该流程虽然可以做到流量无损,但可能会导致个别应用的容量问题,这个我们后续会通过应用层协议支持 goaway 等方案来更优雅地做到底座的无损升级。</p><p><img src="/img/remote/1460000043077280" alt="图片" title="图片"></p><h2>四、业务效果</h2><p>当前 NodeSentry 正在试点阶段,其架构如下图所示。将某应用的 Sidecar 下沉到 NodeSentry 后应用启动提速 37%。</p><p>对于 Mesh 资源复用,目前一组 Mesh 裸启动内存占用 200MB~ ,通过亲和调度后目前可做到同应用同 Pod 的 Mesh 复用度为 3~ 即每个 Node 可节省 2~ 个裸 Mesh 占用的资源,同时 Mesh 复用后相关的网络连接数也是成倍减少。而且随着接入的应用增多,复用的 Mesh 也会增多,资源节省也会更为客观。</p><p><img src="/img/remote/1460000043077281" alt="图片" title="图片"></p><h2>五、未来展望</h2><p>NodeSentry 承载 Mesh 下沉只是开始,接下来除了支持更多的应用接入,也会支持更多场景下沉,譬如 ODP、Cache、Message 等,同时在方案上也会持续演进,做到更稳定、易用、安全、业务无感。</p><p>另外通过 NodeSentry 统一收口 Node 网络流量做相关安全审计、治理、集中优化等,进一步为业务降本增效、提升安全保障水位,结合身份体系,共同构建零信任网络。</p><p><img src="/img/remote/1460000043077282" alt="图片" title="图片"></p><p>MOSN 团队秉着标准、开放的态度,也将其底层通用能力贡献给 Envoy 官方[10],方便开发者在 Envoy 之上使用 GoLang 来开发业务相关插件,从而获得高研发效能及高处理性能。同时接下来我们也会在其标准之上打通 MOSN L4/L7 filter 处理框架,让用户更方便地使用 MOSN filter 及更清爽研发体验。</p><h2>参考文档</h2><p>[1] Service Mesh 的下一站是 Sidecarless 吗:<em><a href="https://link.segmentfault.com/?enc=E%2BQ5qS%2Bd%2BaCvuYWIncCvOQ%3D%3D.C34e%2BiRrTIA8wGsQ%2BJg6dDTahGDIcxkXcH8r0AfUjYPSme8bTMvFAntQXNTCsPu2J3iIQY3SZ3s6cjiGf2fXMQ%3D%3D" rel="nofollow">https://mp.weixin.qq.com/s/v774kTV46dxBc6_n2c5A_w</a></em></p><p>[2] 蚂蚁 ServiceMesh 在 Sidecar 资源管理:<em><a href="https://link.segmentfault.com/?enc=8tXvXdwsaxZPzIGC8LV9ew%3D%3D.5fv8DwcEi787UgmNXO3LRGey51GRR2aLSXpSdVoFcaUifkAjPib6JyD4OVFKyCgvTXHT0fyXYDIMdeGQbabYjFE8AZ%2Fy7C3pHYS6Xzh2R9FKIk3l%2B3F%2Fo21pHa1yGmSk35SdW%2BPtGcbSgnuq5CwyKA%3D%3D" rel="nofollow">https://www.sofastack.tech/blog/service-mesh-practice-in-production-at-ant-financial-part3-operation</a></em></p><p>[3] 开启云原生 MOSN 新篇章 — 融合 Envoy 和 GoLang 生态:<em><a href="https://link.segmentfault.com/?enc=H2XWLsiBjr0HATjFkhY11w%3D%3D.Kkf6R8CwdcRiTptqgmr1r3njuOGuywoHYqdFP3Iw3fT1nQvOkwH1J1%2BzrKUwpqEhWhSFlweLmAz2k1OIFqIeCvPpKj1B3f%2BZKMrVq%2FLpgXpbFtAO5faKvhMhjv4CBKV23RiV7vayO6rskQnmsr%2BNGmI%2FcxjwgZvG2OR3bFVvAIg%3D" rel="nofollow">https://www.sofastack.tech/blog/opening-a-new-chapter-of-cloud-native-mosn-converging-envoy-and-golang-ecosystems</a></em></p><p>[4] Zero Trust Networking:<em><a href="https://link.segmentfault.com/?enc=R97MXqD0Nx5WTr9eGp8iUw%3D%3D.qY1wF%2F71qXC3J2cZg8S43xrRZxSA1bA16Re2wTBiXgw%2BKC7LD9O%2FWIivUadPO0MVNqSKWmtlMkirVv01fUEZHA%3D%3D" rel="nofollow">https://en.wikipedia.org/wiki/Zero_trust_security_model</a></em></p><p>[5] 一种进程内的多租户插件隔离与通信技术:<em><a href="https://link.segmentfault.com/?enc=GmbI42WLGh99uM%2BB8zHqNA%3D%3D.bQDsExSkt%2BS%2B9bLeQP9gxI8myb9ntqzg3pTJqlYMz8U%3D" rel="nofollow">http://www.soopat.com</a></em></p><p>[6] ambient-mesh:<em><a href="https://link.segmentfault.com/?enc=a8aas6iK2DaFlBnF%2FC5z1Q%3D%3D.M8dv54%2FSMkq1KwLj43%2B6fKKb%2FR5TNwE4llIvNaXQqOp0iecYw%2FfzfnrSE0Zk6c5aD6FZdekPKciqvPPVO9D3xg%3D%3D" rel="nofollow">https://istio.io/latest/blog/2022/introducing-ambient-mesh</a></em></p><p>[7] HPA:<em><a href="https://link.segmentfault.com/?enc=LnXp8lL02X84L6CoN3s4ZA%3D%3D.556LXJY%2ByY4QSGU0sVxD%2FW1P5ujflvqt71RxciDPO3OQASz73kiAW5hr5qzQ0h8vtYs45TreBCL6NRFTcNCUpl97R6bSd8u9v2OWZutjeJ8%3D" rel="nofollow">https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale</a></em></p><p>[8] VPA:<em><a href="https://link.segmentfault.com/?enc=G4nznjpNzVcTvIwJTnie9g%3D%3D.e%2FsCHLJir2OKKoKYHkuIEisN5RlLzueQhQEEqAnH%2BvBcFb4UPS9MigO%2BufQtXPYka%2FwH%2BVI%2BB7xgLU5PqJfvfA%3D%3D" rel="nofollow">https://www.kubecost.com/kubernetes-autoscaling/kubernetes-vpa</a></em></p><p>[9] Container Lifecycle Hooks:<em><a href="https://link.segmentfault.com/?enc=MSJXf4%2F%2BdO3ZhsmtfcDLug%3D%3D.bx6znGVJOYdorVQylUMhrl1YgR%2FqnPSIxvokGsw%2Fg4tKBnDU%2FAHmyGRrWsWifPtnE6TDmyy1W%2BTfMTDJpw9B3Y1q8K5ANC9oqczHsGpjRYI%3D" rel="nofollow">https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks</a></em></p><p>[10] Envoy's L7 Go extension API:<em><a href="https://link.segmentfault.com/?enc=L%2FQXu79JUrOb3wtbcD6DQg%3D%3D.6vOmTpMIwkLVzSiveNoSFZoomw%2FyxzN71HI4VPyGT0ZO2tRAu6doV83su82IYTPv" rel="nofollow">https://github.com/envoyproxy/envoy/pull/22573</a></em></p><p><strong>了解更多</strong></p><p><strong>MOSN Star 一下✨:</strong> <em><a href="https://link.segmentfault.com/?enc=Ywkfd4rGPieZRRVRZED8Mw%3D%3D.sNa15iYecM4%2F4ixnNBQOti5GQwmisLEueAgtHikk3FE%3D" rel="nofollow">https://github.com/mosn/mosn/</a></em></p><h2>本周推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=sV29J4y43rJbuew3QQYg6A%3D%3D.4e4DTBBT7SW49O5ivRunRwTKhNd%2FHScmUkLooEsOT2UYq6A1GZ59WVWomGfTVKrEuZrtkNzhIX05lVHvPle8opxN9%2FL6U7ECQCY8z24xWxD0Svg23sO2rwhZaNene5c35tzjyu2zIdK8po%2BHcZgU888Dk5sZQBQDKAZ30caokS5TQGNKWbUfJFgI4G3u6mxBym2DwBX5Nsfu34I83kAEeW1%2FbbOhaacTNLgzsRMAISPlTay%2FCrNQ13eZ47lWHbqFGC7dvINVWrNvNuhCNj5syEUkRynvJb50ZsDEuPrq5r8%3D" rel="nofollow">Service Mesh 的下一站是 Sidecarless 吗?</a></p><p><a href="https://link.segmentfault.com/?enc=5mlT2xHa%2BiN0XWFvOljM3Q%3D%3D.pB7BJBIpHHVfSWMoWW005T8ksYHZbH7a4dD4uelBOmdk4iFPG8cqPqD8XHOtu%2FiE2isVT1Dn55URl36faaOSv%2Fy6eHWENjMu4ly%2FZosr85qCy0ElNa7RP6ADRBQE9Y1B8unAZYcHVkIj38r1hd499PAqt2DNe%2FSvNxB1G%2F0VaoyIwJvkVCeMmQzyNQTGWAs9GfTySXToROfc5%2B5yJn0QXtpyM99E70iFdumy6BBYtsqOgHvJm%2BpTBa3Jo5TKkkIGL%2BR3ctwv1uezRtC3r65MCwbYpN0sddevV02x7jjq8nc%3D" rel="nofollow">社区文章|MOSN 构建 Subset 优化思路分享</a></p><p><a href="https://link.segmentfault.com/?enc=s5lac3SulbbCZbivJEPYyg%3D%3D.5rZ2JVbokYoYjm%2FxWa65vDGvFSOdBvqsvbUjM2rPVvRD8Lpww1rjVIXstLMKZIN72ajcUsdKvCIkUT3CcKXE8lP7R695Vx9JE0x3xvQomWHpyu84AixPiFGwM95wHVBhn%2BPkAW7m%2FuBzpxrgO0WCCT9f2v4b9jaKumn7nftQdzs8Oc5DkWtFEGYEJ74FYYqpAw4n7Pl5QP0HOG9E931TOcQrsvwAQuQym4wfm9jaHJ4vNxi2isUBns7iIATIFmMorASkYc%2B1ZklYmTKaaZ8Dw%2BO6Mj%2FY9gS6nhhMIyuV1Eo%3D" rel="nofollow">cgo 机制 - 从 c 调用 go</a></p><p><a href="https://link.segmentfault.com/?enc=4NIeQvNl2hhItexSKk%2FqjQ%3D%3D.WFHti%2FdsupqTSGSY%2F1ljsBFoK6CAOImJlQa0ZBePrsOSD6Bm4P7%2FGye0ZKqFeofbEVUuHPh9tFYyffM83MdIqJ2rMedw%2FMf1w%2BAYtoFaHwrgsTuowfaTWs3cSHoThJMbbu6ZO7to%2F37zld3v7jOzQaESZGLK7NFwDLuzAsSSvutgmiTkh3JCX8D1kd%2BRaTRCVfMNVjalKLIKgDRZgE%2FOSWMdXtV%2Flw2VkBHYaQRHJARSdFcxzt22DRfcroUr6IUD3z%2FOGWFW33EWHckNpQ8EOcahdTF7Efsxzjn59K3LjQo%3D" rel="nofollow">如何看待 Dapr、Layotto 这种多运行时架构?</a></p>
Service Mesh 的下一站是 Sidecarless 吗?
https://segmentfault.com/a/1190000042931474
2022-11-30T11:48:50+08:00
2022-11-30T11:48:50+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042931476" alt="图片" title="图片"> </p><p>文|田阳 <em>(花名:烈元)</em></p><p>MOSN Maintainer</p><p>专注云原生等技术领域</p><p>本文<strong>3042</strong>字 阅读 <strong>10</strong> 分钟</p><h2>1. 背景</h2><p>Service Mesh 被越来越多的公司认可并实践,在实际落地过程中也遇到了形形色色的问题,同时架构也在持续演进去解决这些问题:有的从初始的 DaemonSet mode 转变为 Sidecar mode,如 Linkerd ;有的从做 CNI 延伸到 Service Mesh 场景, 结合 eBPF 使用 DaemonSet mode,如 Cilium ;如今 Istio 也新增了 Ambient Mesh ,支持 DaemonSet mode 作为其主推模式。</p><p>不难看出一个演进趋势就是围绕着是否需要 Sidecar 而展开,那么 Service Mesh 的下一站将会是 Sidecarless 吗?本文将对目前的社区趋势做一个简要分析, 最后也将介绍蚂蚁在这方面的探索和实践。</p><h2>2. 社区趋势</h2><h3>2.1 Cilium</h3><p>Cilium[1] 是目前最火的云原生网络技术之一,基于革命性的内核技术 eBPF,提供、保护和观察容器工作负载之间的网络连接。</p><p>在 6 月份,Cilium 发布了 1.12 版本,其中发布了 Service Mesh 能力、Sidecarless 架构,它提供了两种模式:</p><p><img src="/img/remote/1460000042931477" alt="图片" title="图片"></p><p>通过图表我们可以发现:针对 L3/L4 的能力,Cilium 使用内核的 eBPF 直接支持;对于 L7 的部分能力,将使用 DaemonSet 部署的 Envoy 来支持。Cilium 认为大部分能力都不需要 L7 的参与,通过 eBPF 就能满足,所以 Cilium 也称自己为内核级服务网格。</p><p><img src="/img/remote/1460000042931478" alt="图片" title="图片"></p><p>针对于此 Cilium 也有一个解释,结合应用程序 TCPD 最终被合入 linux kernel 发展为 iptables 为例,认为 Mesh 也应该作为基础能力下沉到 linux kernel 作为网络的基础组件,就类似于 TCP,作为 Linux 的一部分透明地提供的服务。</p><p>在当需要 L7 代理能力的时候,会构建 DaemonSet Envoy 处理 L7 的能力。Envoy 也已经有了 Namespace 的初步概念,它们被称为监听器。监听器可以携带单独的配置并独立运行,从而可以支持多租户的配置隔离 <em>(但目前还做不到资源和故障的隔离)</em> 。</p><p>Cilium 认为 DaemonSet 相比 Sidecar 最明显的好处就是代理数大大减少,减少资源和管理成本。</p><p>可以看出 Cilium Service Mesh 的发展历程是由下而上,从内核层慢慢向业务层扩展自己的服务边界,由 eBPF 来支持服务网络也是有一定的立场因素。但 eBPF 并不是银弹,DaemonSet mode 也是有一些其他的问题,收益和损失都是相对的。</p><h4>2.2 Linkerd</h4><p>当然,Cilium 这个架构也不乏有挑战者,其中来头最大的就是 Linkerd[2] <em>(Service Mesh 概念的提出者)</em> 的创始人 William Morgan ,比较有意思的是 Linkerd 最开始的架构是 DaemonSet mode ,在后面的版本才换成 Sidecar mode ,对于此,作为逆行者的他应该最有发言权。</p><p>在 William Morgan 的最新文章[3] 中也客观提出了 eBPF 的一些局限性,为了保证 eBPF 的安全执行而不影响 kernel ,需要通过验证器验证是否有不正确的行为,这就导致 eBPF 的编写存在一定的潜规则,比如不能有无界循环;不能超过预设的大小等,代码的复杂性也就受到了一定限制。所以较复杂的逻辑用 eBPF 来实现也是有较高的成本的。</p><p>文章中也提到了 DaemonSet 的一些弊端:</p><p><strong>- 资源管理不可评估</strong>:这取决于 K8s 调度多少 Pod 到该 Node; </p><p><strong>- 隔离性</strong>:所有应用公用一个 Proxy ,相互影响稳定性;</p><p><strong>- 爆炸半径变大</strong>:影响整个 Node 的 Pod 实例;</p><p><strong>- 安全问题更复杂</strong>:比如 Proxy 保存有整个 Node 的秘钥。</p><p>简而言之,Sidecar 模式继续贯彻了容器级别的隔离保护 —— 内核可以在容器级别执行所有安全保护和公平的多租户调度。容器的隔离仍然可以完美的运行,而 DaemonSet 模式却破坏了这一切,重新引入了争抢式的多租户隔离问题。</p><p>当然他也认为 eBPF 可以更好的促进 Mesh 的发展,eBPF+Sidecar 的结合是 Mesh 的未来。</p><blockquote>我们也比较认可他对于 eBPF 的看法, eBPF 就像是一把瑞士军刀,小巧精湛,作为胶水把各种网络数据面连接起来,提供基础网络能力,比如提供访问加速,透明劫持,网络可观察性等能力。但要开发复杂的业务能力,在实操之后,感觉还是有点力不从心。目前我们团队也正在使用 eBPF 开发 K8s Service 和透明拦截等基础网络能力。</blockquote><p>William Morgan 的说法看着也不无道理,我们先不急着站队,再来看看 Istio 是怎么做的,看是否会有新的想法~</p><h3>2.3 Istio</h3><p>在 9 月份,Service Mesh 领域的当家花旦 Istio 毫无征兆的发布了 Ambient Mesh ,并作为自己后续的主推架构,简单来讲就是把数据面从 Sidecar 中剥离出来独立部署,Sidecarless 架构,以彻底解决 Mesh 基础设施和应用部署耦合的问题。</p><blockquote>比较好奇 Istio 在没有经过社区讨论和落地案例的情况下,是怎样决策笃定这个新的架构方向的呢?</blockquote><p>Istio 认为 Sidecar mode 存在如下三个问题:</p><p><strong>- 侵入性</strong> </p><p>必须通过修改应用程序的 Kubernetes pod spec 来将 Sidecar 代理 “注入” 到应用程序中,并且需要将 Pod 中应用的流量重定向到 Sidecar 。因此安装或升级 Sidecar 需要重新启动应用 Pod ,这对工作负载来说可能是破坏性的。</p><p><strong>- 资源利用不足</strong> </p><p>由于每个 Sidecar 代理只用于其 Pod 中相关的工作负载,因此必须针对每个 Pod 可能的最坏情况保守地配置 Sidecar 的 CPU 和内存资源。这导致了大量的资源预留,可能导致整个集群的资源利用不足。</p><p><strong>- 流量中断</strong> </p><p>流量捕获和 HTTP 处理 通常由 Sidecar 完成,这些操作的计算成本很高,并且可能会破坏一些实现和 HTTP 协议不完全兼容的应用程序。</p><p>Envoy 的创始人也来凑了个热闹,他对 Sidecar 架构也是颇有微词。</p><p>我们在落地过程中也是遇到了类似的痛点,比如随着机房规模和应用规模的变大,应用的连接数继续膨胀导致 CPU 和 MEM 资源占用也在持续增加,但这一切都不是应用本身想去关心的。</p><p>那么让我们来解开 Ambient Mesh 架构真面目,是怎样来解决 Sidecar mode 的问题, 架构主要提出了分层:</p><p><img src="/img/remote/1460000042931479" alt="图片" title="图片"></p><p>从图中可以看出,跟 Cilium 有一些类似,这儿的两层数据面都是基于 Envoy 来构建的,Secure Overlay Layer 主要处理 L4 场景,DaemonSet 部署,L7 processing Layer 主要处理 L7 场景,以 gateway 形式通过 Pod 部署,一个应用部署一个 gateway。</p><p><img src="/img/remote/1460000042931480" alt="图片" title="图片"></p><p>图中的 ztunnel 就是 L4 <em>(DaemonSet 部署)</em> ,waypoint 就是 L7 <em>(Pod 部署)</em> ,L4 和 L7 都是可选的,可以根据业务场景灵活组合,比如没有 L7 的场景,直接就用 L4 即可。</p><p>注:图中的 ztunnel 就是L4 <em>(</em>DaemonSet<em> 部署)</em> ,waypoint 就是 L7 <em>(Pod 部署)</em> 。</p><p>无形之中,Ambient Mesh 架构对 William Morgan 评论中的问题也做了一定的解决和反驳:</p><p><strong>- 资源评估</strong></p><p>Istio 认为 L4 资源占用少,然后 L7 的资源占用是通过 Pod 部署,可以更好的弹性。</p><p><strong>- 隔离性</strong></p><p>每个应用都将有一个 L7 集群,相互不影响。</p><p><strong>- 爆炸半径</strong></p><p>L4 逻辑简单相对比较稳定,L7 独立部署,也只影响自身应用。</p><p><strong>- 安全问题</strong></p><p>Istio 认为 Envoy 作为被世界上最大的网络运营商使用的久经考验的成熟软件,它出现安全漏洞的可能性远低于与它一起运行的应用程序。DaemonSet 模式下,出现安全问题时的影响范围并不比任何其他依赖每节点密钥进行加密的 CNI 插件差。有限的 L4 攻击面和 Envoy 的安全特性,Istio 觉得这种风险是有限和可以接受的。</p><p>针对 William Morgan 提到的 DaemonSet 增加了安全风险,我们也持保留意见,就拿证书场景为例,在没有统一接入层 <em>(南北向接入网关)</em> 这个产品之前 <em>(15 年前,还没有 K8s )</em> ,应用的 HTTPS 证书和私钥都是放在跟应用一起部署的 Tengine 上,就类似于 Sidecar 模式,但接入层诞生的一个原因恰恰就是为了集中管理证书和私钥来减少安全风险,通过证书和私钥的分离架构,私钥单独存放在更加安全的 key 集群,并且通过 QAT 硬件加速,HTTPS 性能也更加高效。</p><p><strong>把 HTTPS 和 L7 服务治理能力从应用空间解耦出来下沉为基础设施,也让我们有更多的机会去做集中的优化和演进,同时也对应用更加透明,那个时代的以应用为中心。</strong></p><p><strong>统一接入层和目前 Service Mesh 的 DaemonSet mode 有着不少相似之处,DaemonSet mode 也可以认为是一个东西流量的 Node 接入层。</strong></p><p><strong>网络通信作为基础设施,和应用完全解耦后,可以更好的优化和演进,也能更加透明高效的为应用提供相关基础能力,比如网络连接治理,可信身份,链路加密,流量镜像,安全隔离,服务治理等,更好的以应用为中心。</strong></p><p>从 Cilium 到 Linkerd,再到 Istio,几大社区相互切磋,归根结底还是大家的业务场景不一样,也或者是立场不一样。在安全性,稳定性,管理成本,资源占用上,总是会有一个侧重点,这是需要根据不同的业务场景去选择,脱离业务场景谈架构,还是比较空洞。</p><h2>3. 下一站</h2><p>没有最好的架构,只有最适合自己的架构,在大家的业务场景,你会选择 Sidecar ,还是 Sidecarless ,你认为的下一站是什么呢?</p><p>下周我们即将发布 《降本增效: 蚂蚁在 Sidecarless 的探索和实践》,一起来聊聊蚂蚁在这个方向的探索和演进,期待和大家的交流~</p><h2>4. 引用</h2><p>[1]Cilium :</p><p><em><a href="https://link.segmentfault.com/?enc=gviIo755EvElhrYYOJra2w%3D%3D.zztea3aHJ1A1dApdoRlUoD3xCrKuhTFyW0%2B9snKGLv42GbkzBN48raYA1SlWDKqfBBwgEbhqj3CionqG3h7mdg%3D%3D" rel="nofollow">https://istio.io/latest/blog/2022/introducing-ambient-mesh/</a></em></p><p>[2]Linkerd :</p><p><em><a href="https://link.segmentfault.com/?enc=hgwy4JiTbyKi49DqENEBMQ%3D%3D.gGCWK%2FTL7GCH2EfRbpaWaE8ZQcxUckyO7qZkTLRnaGP0gwzWnetC2Durtyv3BVKvpbqRHigORyeQd5F2Y6oM8w%3D%3D" rel="nofollow">https://isovalent.com/blog/post/cilium-service-mesh/</a></em></p><p>[3]William Morgan 的最新文章:</p><p><em><a href="https://link.segmentfault.com/?enc=%2BzKHECa4iwQNr4h8d1ccgw%3D%3D.5p7KYFniSLsFQ%2FcRHTqVBEEJ8Q5uVVU40OvCd8Zch20I%2BsSzAhDpS0jyULnAbn42ytSzWwv%2FkgENkzkadPJQQOYE%2FqXNXnzdnitluaV2EWM%3D" rel="nofollow">https://buoyant.io/blog/ebpf-sidecars-and-the-future-of-the-service-mesh</a></em></p><p><strong>MOSN Star 一下✨:</strong> </p><p><strong><a href="https://link.segmentfault.com/?enc=hPpOt7gTdf1u3BOGS7h7IQ%3D%3D.DNkD9C8MhTd0iwva0Q3viMMnSVyeXY0sUqz5ffi7oYY%3D" rel="nofollow">https://github.com/mosn/mosn</a></strong></p><h2>本周推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=gqpUJO8rm0kQIuMaGUAA%2Bw%3D%3D.GgXt3n96P1n5EhwxgQTeUGb1RwhaZs%2FFbUvvSqgUbWkk3WIGonVtJjMjOdoaqUtL0oE1hMk5oyH2vrlHRD%2BVFGNT3hcIjIs0PKD%2FVpLR10WLmJGMjSF8k2fwMQ15AmvvP9Yemcz1v1Hx6P%2FPIl1UjIXbRrvvcMoi8FTY%2B1n6jT8HvMEIhY19Vky6fUJQh9bpKs4s%2FmhFhaiWMDHmpSxTl50zgL4V4UZcFCC%2BfOnF8piEQJ4CZgwl66TjjbTS0sLg3uduBVYuKAtzh5fapz13eA%3D%3D" rel="nofollow">蚂蚁集团 Service Mesh 进展回顾与展望</a></p><p>[顺丰科技 Service Mesh:落地半年,最初目标已经实现,将在更多场景进行大规模探索<br>](<a href="https://link.segmentfault.com/?enc=Y7alQk0Gh%2FVKtnEGto3nqA%3D%3D.hUGg9gO1IeWgsCDn2Dpgz7gXyRvSiGVUsZiOlh6m2DiVpU90wcyriYETrx%2FADjDNTvcD3ksnjKuIak3TSkH7L5sB2RX%2B4zCaYyCaylrua5KEBmaJShb%2FyzEb2v2Sje1r04%2FJntunh2sF0HDn3hXgvco6GtD1coSqZPQenQMo%2Bk0qnkGZX2BPlzrYEpMmcn2i5XBQbbuGfeqOXFmPGU8%2Br1S34hM56i9bg76%2FDEE%2F3TPspb0%2Bo%2BuSsigW1N%2BGwcPxBTL4MZv32jBlrGuhB2RoRg%3D%3D" rel="nofollow">http://mp.weixin.qq.com/s?__b...</a>)</p><p><a href="https://link.segmentfault.com/?enc=eoCKpSRSrVRNZ0NkDGh64Q%3D%3D.rTY9jylu2sEhHCA6Fn9TppnjfvTAFGKB6Ues2W0Wpe2jEYwKnLPDQr3jDWkaDCn8cUFaNDQ0hMVK6nKFn5UvO1nczmWUo7%2FClEKTjfvXPF7%2F162ez5QHOFnu2k%2FOOuW0AMwrSROKvezxsHOA9BpJrrC%2FFiHhV8rfvbycYXa0rN4c9jG7%2BG%2BAwdjGHSHg1qKFW8%2FwhDNl3weXvGGObHi3sJdHjfCch%2FFZRF3NoIt0jBy00QsCIxI3wgSe%2BF529zA9heHoPe1vkkqsz%2F6CStcsmg%3D%3D" rel="nofollow">「网商双十一」基于 ServiceMesh 技术的业务链路隔离技术及实践</a></p><p><a href="https://link.segmentfault.com/?enc=F5GJBFVQGrh5T1tx79c51g%3D%3D.GENnhmeK4a2dKwja%2F6D%2BSBBqWmyc6PGqGyVQnWs%2F2u2tv0Wan3wD4%2Fi%2BdcSRi%2FsptVAqPuaykc1SRsSLWsyBnWZbAqmJDalXa1zhwLOcseIqC4U4ppHiT%2FL9U99DQkhWIGGGzqEe2hjHrAbtBImyS92aOMydJTECJ4v4TnFPsowud3Poi%2BT6Qbe0TuKYCoZnRMDLnmoHZznNlLD6zAYd255OP2ZPwYPNgn8e5ClDpDhT1c2mCq%2FrD8iUS%2BjVrdThP67PuE5X7t%2FlyopeeBzM0g%3D%3D" rel="nofollow">MOSN 反向通道详解</a></p>
Tongsuo 支持半同态加密算法 Paillier
https://segmentfault.com/a/1190000042876659
2022-11-23T15:47:39+08:00
2022-11-23T15:47:39+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042876661" alt="图片" title="图片"> </p><p>文|王祖熙(花名:金九 )</p><p>蚂蚁集团开发工程师 </p><p><em>负责国产化密码库 Tongsuo 的开发和维护</em></p><p><em>专注于密码学、高性能网络、网络安全等领域</em> </p><p>本文 <strong>4316</strong> 字 阅读<strong>10</strong> 分钟</p><h2>1. 背景</h2><p>在<a href="https://link.segmentfault.com/?enc=vZTutYFpO1XbVYW84Eez8w%3D%3D.HKRPk9ZTF9RsOD%2F18vt6MWwEj9633HpA7R%2BQjnw1Olt7%2F3%2BhOdxfS3Pek9PANbmfO882OLPSzyoQjNvEjcnTOCNvblSYm1sfERHpmsAi0ujGt0MtnebESXuK%2FkL%2Feeqmjjt31ccEDKxqNH5cL2TAYyGtw7aYym7wNTprp8Id7Q1KyXy1pBukNcoosvMhfVl45fODftLDwRUE32mSFFe98qkpVMEVQQ0l2HMHgm1iZCSnpmWPrhq9whx585d2O1b10mRClvKvAoLYGssnXBv9bJME4f0S1bEN57bUAi1Aoz72VYx7WQU4WY%2FTzV1apb5U4xazszoZQ6Nm7t9%2BjaB0Qw%3D%3D" rel="nofollow">《Tongsuo 支持半同态加密算法 EC-ElGamal》</a>中,已经阐述了同态和半同态加密算法的背景和原理,可以移步查阅。总之,同态算法在隐私计算领域有着重要的作用,目前应用比较广泛的是 Paillier 和 EC-ElGamal 半同态加密算法,它们接口类似且只支持加法同态。</p><p>但是它们两者的性能和原理有很大的差异:</p><p><strong>原理方面</strong>,Paillier 是基于复合剩余类的困难性问题 <em>(大数分解难题)</em> 的公钥加密算法,有点类似 RSA;而 EC-ElGamal 是基于椭圆曲线数学理论的公钥加密算法,其安全性理论上要比 Paillier 要更好。</p><p><strong>性能方面</strong>,EC-ElGamal 的加密和密文加法性能要比 Paillier 好;而 Paillier 的解密和密文标量乘法性能要比起 EC-ElGamal 要更好更稳定 <em>(EC-ElGamal 的解密性能与解密的数字大小有关系,数字越大可能需要解密的时间越长,这与 EC-ElGamal 解密用到的解密表有关系,而 Paillier 的解密就没有这个问题。)</em> 。</p><p>所以这两个产品各有优劣,大家可以根据自己的业务特点选择使用 Paillier 还是 EC-ElGamal。</p><h2>2. Paillier 原理</h2><h3>2.1 密钥生成</h3><p>1.随机选择两个大素数 p、q,满足 <img src="/img/remote/1460000042876662" alt="图片" title="图片">,且满足 p 和 q 的长度相等;</p><p>2.计算<img src="/img/remote/1460000042876663" alt="图片" title="图片">以及 <img src="/img/remote/1460000042876664" alt="图片" title="图片">,<img src="/img/remote/1460000042876665" alt="图片" title="图片">表示最小公倍数;</p><p>3.随机选择整数<img src="/img/remote/1460000042876666" alt="图片" title="图片">,一般 g 的计算公式如下: </p><p>a. 随机选择整数<img src="/img/remote/1460000042876667" alt="图片" title="图片">; </p><p>b. 计算:<img src="/img/remote/1460000042876668" alt="图片" title="图片">,为了简化和提高性能,k 一般选 1,g=1+n;</p><p>4.定义 L 函数:<img src="/img/remote/1460000042876669" alt="图片" title="图片">,计算:<img src="/img/remote/1460000042876670" alt="图片" title="图片">;</p><p>5.公钥:(n, g),私钥:(λ, μ)。</p><h3>2.2 加密</h3><ol><li>明文 m,满足 −n<m<n;</li><li>选择随机数 r,满足 0≤r<n 且 <img src="/img/remote/1460000042876671" alt="图片" title="图片">;</li><li>计算密文:<img src="/img/remote/1460000042876672" alt="图片" title="图片">。</li></ol><p><strong>2.3 解密</strong></p><ol><li>密文 c,满足<img src="/img/remote/1460000042876673" alt="图片" title="图片">;</li><li>计算明文:<img src="/img/remote/1460000042876674" alt="图片" title="图片">。</li></ol><p><strong>2.4 密文加法</strong></p><ol><li>密文:c1 和 c2,<img src="/img/remote/1460000042876675" alt="图片" title="图片">,c 就是密文加法的结果。</li></ol><p><strong>2.5 密文减法</strong></p><ol><li>密文:c1 和 c2,计算:<img src="/img/remote/1460000042876676" alt="图片" title="图片">,c 就是密文减法的结果。</li></ol><p><strong>2.6 密文标量乘法</strong></p><ol><li>密文:c1,明文标量:a,计算:<img src="/img/remote/1460000042876677" alt="图片" title="图片">,c 就是密文标量乘法的结果。</li></ol><h2>3. 正确性</h2><h3>3.1 加解密正确性</h3><p>公式推导需要用到 Carmichael 函数和确定合数剩余的公式,下面简单说明一下:</p><p>● Carmichael 函数 </p><p>a. 设 n=pq,其中:p、q 为大素数;</p><p>b. 欧拉函数:ϕ(n) ,Carmichael 函数:λ(n);</p><p>c. 当 <img src="/img/remote/1460000042876678" alt="图片" title="图片">和<img src="/img/remote/1460000042876679" alt="图片" title="图片"> 时,</p><p>其中:<img src="/img/remote/1460000042876680" alt="图片" title="图片"> 。</p><p>对于任意 <img src="/img/remote/1460000042876681" alt="图片" title="图片">,有如下性质:<img src="/img/remote/1460000042876682" alt="图片" title="图片">。</p><p>● 判定合数剩余</p><p>a. 判定合数剩余类问题是指 n=pq,其中:p、q 为大素数,任意给定<img src="/img/remote/1460000042876683" alt="图片" title="图片">,使得<img src="/img/remote/1460000042876684" alt="图片" title="图片">,则说 z 是模 <img src="/img/remote/1460000042876685" alt="图片" title="图片"> 的第 n 次剩余;</p><p>b. 第 n 项剩余的集合是 <img src="/img/remote/1460000042876686" alt="图片" title="图片"> 的一个<img src="/img/remote/1460000042876687" alt="图片" title="图片"> 阶乘法子集;</p><p>c. 每个第 n 项剩余 z 都正好拥有 n 个 n 阶的根,其中只有一个是严格小于 n 的 <em>(即</em> <em><img src="/img/remote/1460000042876688" alt="图片" title="图片">)</em> ;d. 第n项剩余都可以写成 <img src="/img/remote/1460000042876689" alt="图片" title="图片">的形式。 </p><p>● 正确性验证 </p><p><img src="/img/remote/1460000042876690" alt="图片" title="图片"></p><p>解密:</p><p><img src="/img/remote/1460000042876691" alt="图片" title="图片"></p><h3>3.2 密文加法正确性</h3><p><img src="/img/remote/1460000042876692" alt="图片" title="图片"></p><h3>3.3 密文减法正确性</h3><p><img src="/img/remote/1460000042876693" alt="图片" title="图片"></p><h3>3.4 密文标量乘法正确性</h3><p><img src="/img/remote/1460000042876694" alt="图片" title="图片"></p><h2>4. 算法实现</h2><h3>4.1 接口定义</h3><p>●对象相关接口 </p><p>○公/私钥对象:<code>PAILLIER_KEY</code> ,该对象用来保存 Paillier 公钥和私钥的基本信息,比如 p、q、n、g、λ、μ 等信息,私钥保存所有字段,公钥只保存 n、g,其他字段为空或者 0。相关接口如下:</p><pre><code>// 创建 PAILLIER_KEY 对象
\PAILLIER_KEY *PAILLIER_KEY_new(void);
// 释放 PAILLIER_KEY 对象
void PAILLIER_KEY_free(PAILLIER_KEY *key);
// 拷贝 PAILLIER_KEY 对象,将 src 拷贝到 dest 中
PAILLIER_KEY *PAILLIER_KEY_copy(PAILLIER_KEY *dest, PAILLIER_KEY *src);
// 复制 PAILLIER_KEY 对象
PAILLIER_KEY *PAILLIER_KEY_dup(PAILLIER_KEY *key);
// 将 PAILLIER_KEY 对象引用计数加1,释放 PAILLIER_KEY 对象时若引用计数不为0则不能释放其内存
intPAILLIER_KEY_up_ref(PAILLIER_KEY *key);
// 生成 PAILLIER_KEY 对象中的参数,bits 为随机大素数 p、q 的二进制位长度
int PAILLIER_KEY_generate_key(PAILLIER_KEY *key, int bits);
// 获取 key 的类型:公钥 or 私钥
// PAILLIER_KEY_TYPE_PUBLIC 为私钥,PAILLIER_KEY_TYPE_PRIVATE 为私钥
int PAILLIER_KEY_type(PAILLIER_KEY *key);</code></pre><p>○上下文对象:<code>PAILLIER_CTX</code>,该对象用来保存公私钥对象以及一些其他内部用到的信息,是 Paillier 算法其他接口的第一个参数。相关接口如下:</p><pre><code>// 创建 PAILLIER_CTX 对象,key 为 paillier 公钥或者私钥,threshold 为支持最大的数字阈值,加密场景可设置为 0,解密场景可使用默认值:
PAILLIER_MAX_THRESHOLDPAILLIER_CTX *PAILLIER_CTX_new(PAILLIER_KEY *key, int64_t threshold);
// 释放 PAILLIER_CTX 对象
void PAILLIER_CTX_free(PAILLIER_CTX *ctx);
// 拷贝 PAILLIER_CTX 对象,将 src 拷贝到 dest 中
PAILLIER_CTX *PAILLIER_CTX_copy(PAILLIER_CTX *dest, PAILLIER_CTX *src);
// 复制 PAILLIER_CTX 对象
PAILLIER_CTX *PAILLIER_CTX_dup(PAILLIER_CTX *src);</code></pre><p>○密文对象: <code>PAILLIER_CIPHERTEXT</code> ,该对象是用来保存 Paillier 加密后的结果信息,用到 <code>PAILLIER_CIPHERTEXT</code> 的地方,可调用如下接口:</p><pre><code>// 创建 PAILLIER_CIPHERTEXT 对象
PAILLIER_CIPHERTEXT *PAILLIER_CIPHERTEXT_new(PAILLIER_CTX *ctx);
// 释放 PAILLIER_CIPHERTEXT 对象
void PAILLIER_CIPHERTEXT_free(PAILLIER_CIPHERTEXT *ciphertext);</code></pre><p>●加密/解密接口</p><pre><code>// 加密,将明文 m 进行加密,结果保存到 PAILLIER_CIPHERTEXT 对象指针 out 中
int PAILLIER_encrypt(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *out, int32_t m);
// 解密,将密文 c 进行解密,结果保存到 int32_t 指针 out 中
int PAILLIER_decrypt(PAILLIER_CTX *ctx, int32_t *out, PAILLIER_CIPHERTEXT *c);</code></pre><p>●密文加/减/标量乘运算接口</p><pre><code>// 密文加,r = c1 + c2
int PAILLIER_add(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c1, PAILLIER_CIPHERTEXT *c2);
// 密文标量加,r = c1 * m
int PAILLIER_add_plain(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r, PAILLIER_CIPHERTEXT *c1, int32_t m);
// 密文减,r = c1 - c2
int PAILLIER_sub(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c1, PAILLIER_CIPHERTEXT *c2);
// 密文标量乘,r = c * m
int PAILLIER_mul(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c, int32_t m);</code></pre><p>●编码/解码接口 <br>同态加密涉及到多方参与,可能会需要网络传输,这就需要将密文对象 <code>PAILLIER_CIPHERTEXT</code> 编码后才能传递给对方,对方也需要解码得到 <code>PAILLIER_CIPHERTEXT</code> 对象后才能调用其他接口进行运算。 </p><p>接口如下:</p><pre><code>// 编码,将密文 ciphertext 编码后保存到 out 指针中,out 指针的内存需要提前分配好;
// 如果 out 为 NULL,则返回编码所需的内存大小;
// flag:标志位,预留,暂时没有用size_t PAILLIER_CIPHERTEXT_encode(PAILLIER_CTX *ctx, unsigned char *out,
size_t size,
const PAILLIER_CIPHERTEXT *ciphertext,
int flag);
// 解码,将长度为 size 的内存数据 in 解码后保存到密文对象 r 中
int PAILLIER_CIPHERTEXT_decode(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r, unsigned char *in, size_t size);</code></pre><p>以上所有接口详细说明请参考 Paillier API 文档:<em><a href="https://link.segmentfault.com/?enc=H1mgBtS3qPBd9FC%2BB2FkyQ%3D%3D.OyA2pFtKT9vvyVXm6Ioan4at%2FRUxJEFYBnC3sFsez4867likYi6%2B83M47h%2F8geQl" rel="nofollow">https://www.yuque.com/tsdoc/api/slgr6f</a></em></p><h3>4.2 核心实现</h3><p>●Paillier Key</p><p>Paillier 不像 EC-ElGamal,EC-ElGamal 在 Tongsuo 里面直接复用 EC_KEY 即可,Paillier Key 在 Tongsuo 里面则需要实现一遍,主要功能有:公/私钥的生成、PEM 格式存储、公/私钥解析和文本展示,详情请查阅代码:</p><p>crypto/paillier/paillier_key.c、</p><p>crypto/paillier/paillier_asn1.c、</p><p>crypto/paillier/paillier_prn.c。 </p><p>●Paillier 加解密、密文运算</p><p>Paillier 的加解密和密文运算算法非常简单,主要是大数的模幂运算,使用 Tongsuo 里面的 BN 相关接口就可以,需要注意的是,负数的加密/解密用到模逆运算,不能直接按公式计算 <em>(<img src="/img/remote/1460000042876695" alt="图片" title="图片">)</em> ,这是因为 OpenSSL 的接口 <code>BN_mod_exp</code> 没有关注指数 <em>(上面公式的 m )</em> 是不是负数,如果是负数的话需要做一次模逆运算:<img src="/img/remote/1460000042876696" alt="图片" title="图片">,这里计算出 之后做一次模逆运算 <em>( <code>BN_mod_inverse</code> )</em> 再与相乘;解密的时候,需要确认是否检查了阈值 <em>(</em> <em><code>PAILLIER_MAX_THRESHOLD</code> )</em> ,超出则说明是负数,需要减去 n 才得到真正的结果。密文减法也需要用到模逆运算,通过密文减法的公式 <em>()</em> 得知, 需要进行模逆运算 <em>(</em> <em><code>BN_mod_inverse</code> )</em> 再与 相乘。</p><p>详情请查阅代码:</p><p>crypto/paillier/paillier_crypt.c </p><p>●Paillier 命令行 <br>为了提高 Paillier 的易用性,Tongsuo 实现了如下 Paillier 子命令:</p><pre><code>
$ /opt/tongsuo-debug/bin/openssl paillier -help
Usage: paillier [action options] [input/output options] [arg1] [arg2]
General options:
-help Display this summary
Action options:
-keygen Generate a paillier private key
-pubgen Generate a paillier public key
-key Display/Parse a paillier private key
-pub Display/Parse a paillier public key
-encrypt Encrypt a number with the paillier public key, usage: -encrypt 99, 99 is an example number
-decrypt Decrypt a ciphertext using the paillier private key, usage:-decrypt c1, c1 is an example ciphertext
-add Paillier homomorphic addition: add two ciphertexts, usage: -add c1 c2, c1 and c2 are tow example ciphertexts, result: E(c1) + E(c2)
-add_plain Paillier homomorphic addition: add a ciphertext to a plaintext, usage: -add_plain c1 99, c1 is an example ciphertext, 99 is an example number, result: E(c1) + 99
-sub Paillier homomorphic subtraction: sub two ciphertexts, usage: -sub c1 c2, c1 and c2 are tow example ciphertexts, result: E(c1) - E(c2)
-mul Paillier homomorphic scalar multiplication: multiply a ciphertext by a known plaintext, usage: -mul c1 99, c1 is an example ciphertext, 99 is an example number, result: E(c1) * 99
Input options:
-in val Input file
-key_in val Input is a paillier private key used to generate public key
Output options:
-out outfile Output the paillier key to specified file
-noout Don't print paillier key out
-text Print the paillier key in text
-verbose Verbose output
Parameters:
arg1 Argument for encryption/decryption, or the first argument of a homomorphic operation
arg2 The second argument of a homomorphic operation</code></pre><p>主要命令有:</p><p><strong>- keygen:</strong> 生成 Paillier 私钥; </p><p><strong>- pubgen:</strong> 用 Paillier 私钥生成公钥; </p><p><strong>- key:</strong> 文本显示 Paillier 私钥;</p><p><strong>- pub:</strong> 文本显示 Paillier 公钥; </p><p><strong>- encrypt:</strong> 对数字进行加密,输出 Paillier 加密的结果,需要通过参数 -key_in 参数指定 Paillier 公钥文件路径,如果加密负数则需要将 <code>-</code> 用 <code>_</code> 代替,因为 <code>-</code> 会被 OpenSSL 解析成预定义参数了 <em>(下同)</em> ;</p><p><strong>- decrypt:</strong> 对 Paillier 密文进行解密,输出解密结果,需要通过-key_in参数指定 Paillier 私钥文件路径; </p><p><strong>- add:</strong> 对两个 Paillier 密文进行同态加法操作,输出同态加法密文结果,需要通过参数 -key_in 参数指定 Paillier 公钥文件路径;</p><p><strong>- add_plain:</strong> 将 Paillier 密文和明文相加,输出同态加法密文结果,需要通过参数 -key_in 参数指定 Paillier 公钥文件路径;</p><p><strong>- sub:</strong> 对两个 Paillier 密文进行同态减法操作,输出同态减法密文结果,需要通过参数 -key_in 参数指定 Paillier 公钥文件路径;</p><p><strong>- mul:</strong> 将 Paillier 密文和明文相乘,输出同态标量乘法密文结果,需要通过参数 -key_in 参数指定 Paillier 公钥文件路径。</p><p>通过以上命令即可在命令行进行 Paillier 算法实验,降低入门门槛,详情请查阅代码:apps/paillier.c。</p><p>另外还实现了 Paillier 的 speed 命令,可以进行性能测试,详情请查阅代码:apps/speed.c。</p><h2>5. 用法&例子</h2><h3>5.1 demo 程序</h3><pre><code>#include <stdio.h>
#include <time.h>
#include <openssl/paillier.h>
#include <openssl/pem.h>
#define CLOCKS_PER_MSEC (CLOCKS_PER_SEC/1000)
int main(int argc, char *argv[])
{
int ret = -1;
int32_t r;
clock_t begin, end;
PAILLIER_KEY *pail_key = NULL, *pail_pub = NULL;
PAILLIER_CTX *ctx1 = NULL, *ctx2 = NULL;
PAILLIER_CIPHERTEXT *c1 = NULL, *c2 = NULL, *c3 = NULL;
FILE *pk_file = fopen("pail-pub.pem", "rb");
FILE *sk_file = fopen("pail-key.pem", "rb");
if ((pail_pub = PEM_read_PAILLIER_PublicKey(pk_file, NULL, NULL, NULL)) == NULL)
goto err;
if ((pail_key = PEM_read_PAILLIER_PrivateKey(sk_file, NULL, NULL, NULL)) == NULL)
goto err;
if ((ctx1 = PAILLIER_CTX_new(pail_pub, PAILLIER_MAX_THRESHOLD)) == NULL)
goto err;
if ((ctx2 = PAILLIER_CTX_new(pail_key, PAILLIER_MAX_THRESHOLD)) == NULL)
goto err;
if ((c1 = PAILLIER_CIPHERTEXT_new(ctx1)) == NULL)
goto err;
if ((c2 = PAILLIER_CIPHERTEXT_new(ctx1)) == NULL)
goto err;
begin = clock();
if (!PAILLIER_encrypt(ctx1, c1, 20000021))
goto err;
end = clock();
printf("PAILLIER_encrypt(20000021) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);
begin = clock();
if (!PAILLIER_encrypt(ctx1, c2, 500))
goto err; end = clock();
printf("PAILLIER_encrypt(500) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);
if ((c3 = PAILLIER_CIPHERTEXT_new(ctx1)) == NULL)
goto err;
begin = clock();
if (!PAILLIER_add(ctx1, c3, c1, c2))
goto err; end = clock();
printf("PAILLIER_add(C2000021,C500) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);
begin = clock();
if (!(PAILLIER_decrypt(ctx2, &r, c3)))
goto err; end = clock();
printf("PAILLIER_decrypt(C20000021,C500) result: %d, cost: %lfms\n", r, (double)(end - begin)/CLOCKS_PER_MSEC);
begin = clock();
if (!PAILLIER_mul(ctx1, c3, c2, 800))
goto err;
end = clock();
printf("PAILLIER_mul(C500,800) cost: %lfms\n", (double)(end - begin)/CLOCKS_PER_MSEC);
begin = clock();
if (!(PAILLIER_decrypt(ctx2, &r, c3)))
goto err; end = clock();
printf("PAILLIER_decrypt(C500,800) result: %d, cost: %lfms\n", r, (double)(end - begin)/CLOCKS_PER_MSEC);
printf("PAILLIER_CIPHERTEXT_encode size: %zu\n", PAILLIER_CIPHERTEXT_encode(ctx2, NULL, 0, NULL, 1));
ret = 0;
err: PAILLIER_KEY_free(pail_key);
PAILLIER_KEY_free(pail_pub);
PAILLIER_CIPHERTEXT_free(c1);
PAILLIER_CIPHERTEXT_free(c2);
PAILLIER_CIPHERTEXT_free(c3);
PAILLIER_CTX_free(ctx1);
PAILLIER_CTX_free(ctx2);
fclose(sk_file);
fclose(pk_file);
return ret;
}</code></pre><h3>5.2 编译和运行</h3><p>先确保 Tongsuo 开启 Paillier,如果是手工编译 Tongsuo,可参考如下编译步骤:</p><pre><code># 下载代码
git clone git@github.com:Tongsuo-Project/Tongsuo.git
# 编译参数需要加上:enable-paillier
./config --debug no-shared no-threads enable-paillier --strict-warnings -fPIC --prefix=/opt/tongsuo
# 编译
make -j
# 安装到目录
/opt/tongsuo sudo make install</code></pre><h3>5.3 编译 demo 程序</h3><pre><code>gcc -Wall -g -o paillier_test ./paillier_test.c -I/opt/tongsuo/include -L/opt/tongsuo/lib -lssl -lcrypto</code></pre><h3>5.4 生成 Paillier 公私钥</h3><pre><code># 先生成私钥
/opt/tongsuo/bin/openssl paillier -keygen -out pail-key.pem#
用私钥生成公钥
/opt/tongsuo/bin/openssl paillier -pubgen -key_in ./pail-key.pem -out pail-pub.pem</code></pre><h3>5.5 运行结果</h3><pre><code>$ ./paillier_test
PAILLIER_encrypt(20000021) cost: 3.202000ms
PAILLIER_encrypt(500) cost: 0.442000ms
PAILLIER_add(C2000021,C500) cost: 0.047000ms
PAILLIER_decrypt(C20000021,C500) result: 20000521, cost: 0.471000ms
PAILLIER_mul(C500,800) cost: 0.056000ms
PAILLIER_decrypt(C500,800) result: 400000, cost: 0.464000ms
PAILLIER_CIPHERTEXT_encode size: 0</code></pre><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=R3hDe2KDL4iPJMxYQ0qPnA%3D%3D.Q25uiU42m9MecZeznGy0QS8oMcCRz9cZs0bAJEWgXZH8c2aQcbJJ2H%2Fw%2FwaW6p%2BJVE2fMcNuppqdeqD1RznO2lXnW5s7PPCjmcJNEwqtZAM5BPXkPgrfcGtPj4KrPg1%2F%2B5Su1kdPNaPnLvK1gu2pc%2FQNaBodhzmPuTJH1nl6mZb2SAqN0xRbhXNge1u0dvItlHCgfkR59JkrOWqC0yCjtCgJ%2FMw8FnQitDIPMoq2%2Ffw3MyO0yZkj8fCtUMSCs9%2FNut64GUfOVmPAGDLTw0APjQ9CrXgpucbyNlZqVIJjGzA%3D" rel="nofollow">你好,我的新名字叫“铜锁/Tongsuo”</a></p><p><a href="https://link.segmentfault.com/?enc=FqwD3XfutUxQ9oOxFIaJLA%3D%3D.vxZ2JmDUNOzYovQm5YFL1g0LXuS%2F5euuZlrjc7qJweqcftXiqCQQYdhc4AgWMbaFk2akwP2z9LvBbX1c4x5P0D7cLcVEiJq9qeh%2BHiGUPq0DXZsLh0ZUuu2kixouV1MGi7L326ksh1FkFl7lwDCsqKiODFZW70fvOR%2FGXsbzN0kqYphhn7nXmIoXwnwaSUCRwEBHp4KjeHLF6f1eNnmF71lZ3Hr4dc%2FaCUnmb2Rw2%2BMF6mnGqec0m0vOyMg0gev6Fddyc9Jfzm4gCNcG4S3i1Cwxb9%2FjgiQibXl%2BUsYeJDI%3D" rel="nofollow">BabaSSL:支持半同态加密算法 EC-ElGamal</a></p><p><a href="https://link.segmentfault.com/?enc=emPT5wCWrJwTnms7ifheCg%3D%3D.rbAKHHyo6qI%2Bl5e6qO6lQq%2F7h0c%2BtgrY02MTTtNb1FArrT7DbTp%2B%2BSV9zqGEN8Rl6C9JgdbJTYEppOnNVIxX2y4i6GrqOIAIwheiEJIGILtt9dVnJO11zsLETHPsn%2Bbesu%2Fuq112qcKA%2BneTAq1jZhVtnfl73Y4iEMF5iXjvvegoJxQQ0gNPsJXAkIj3RHTsD%2FoXF1PWhMQgE%2BP7dqFm9SQw0u%2Fb35uAJD%2BkVURZxXrAhsEkCNq%2BC9Y%2F%2FxMEofm0i7bB27VtzPWEX%2BU2OjDVIEkfYr%2BWbM0gVknwAFwQ9mc%3D" rel="nofollow">BabaSSL 发布 8.3.0|实现相应隐私计算的需求</a></p><p><a href="https://link.segmentfault.com/?enc=9LJVbOqTEjYkR3lbwGM8cg%3D%3D.w%2F9e1K7UNiS7F4%2BuhJOM%2FvgL%2F%2F%2BOr%2F1vbEGqwA%2FCikpeEYQ1fL5n6gLCzZzqA%2BkKneF8VbsoaHvWpo9iMUcgTBDmOyJUZzhBkZeicOXieu6mr6jb8K0MdvxyWWIgX2EKb%2B4VRc5Zz%2BWQhimxQGJbRVQ5P5iageBGIwMp%2FJ3dpGmmxVNC90GwhhfjR6BVF%2FKe7FW3TV842AAIAxOajjfZtGf96zw%2B%2BkhyGIAbr8%2BynwfuSAhSphJG77WObOoo6DRKtjfcTCbMTKi5Ik8k3%2F2%2BwWRYDAAN5K%2FLyatKN0qbLX8%3D" rel="nofollow">开源项目文档社区化!Tongsuo/铜锁实践</a></p>
Dragonfly 中 P2P 传输协议优化
https://segmentfault.com/a/1190000042826934
2022-11-16T12:51:02+08:00
2022-11-16T12:51:02+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042826936" alt="图片" title="图片"> </p><p>文|孙珩珂</p><p>上海交通大学 </p><p>本文<strong>1987</strong>字 阅读 <strong>10</strong> 分钟</p><h3>01 优化背景</h3><p>此前 Dragonfly 的 P2P 下载采用静态限流策略,相关配置项在 <code>dfget.yaml</code> 配置文件中:</p><pre><code># 下载服务选项。
download:
# 总下载限速。
totalRateLimit: 1024Mi
# 单个任务下载限速。 perPeerRateLimit: 512Mi</code></pre><p>其中 <code>perPeerRateLimit</code> 为单个任务设置流量上限, <code>totalRateLimit</code> 为单个节点的所有任务设置流量上限。</p><p>静态限流策略的理想情况是: <code>perPeerRateLimit</code> 设置为20M , <code>totalRateLimit</code> 设置为 100M ,且该节点目前运行了 5 个或更多的 P2P 下载任务,这种情况下可以确保所有任务总带宽不会超过 100M ,且带宽会被有效利用。</p><p><img src="/img/remote/1460000042826937" alt="图片" title="图片"></p><p>这种限流策略的缺点是:若<code>perPeerRateLimit</code> 设置为 20M , <code>totalRateLimit</code> 设置为 100M ,并且当前该节点只运行了一个下载任务,那么该任务的最大下载速度为 20M ,和最大带宽 100M 相比,浪费了 80% 的带宽。</p><p><img src="/img/remote/1460000042826938" alt="图片" title="图片"></p><p>因此,为了最大限度地利用带宽,需要使用动态限流来确保任务数量少时能能充分利用总带宽,而任务数量多时也能公平分配带宽。最终,我们设计出一套根据上下文进行动态限流的算法,其中上下文指各任务在过去一秒内使用的带宽,此外,算法还考虑到了任务数量、任务剩余大小、任务保底带宽等因素,性能相比原来的静态限流算法有显著提升。</p><h3>02 相关代码分析</h3><p><code>perPeerRateLimit</code> 配置项最终赋值给 <code>peerTaskConductor</code> 的<code>pt.limiter</code> ,由 <code>peerTaskConductor</code> 的 <code>DownloadPiece()</code> 函数里进行限速,<code>pt.waitLimit()</code> 进行实际限流工作,底层调用 Go 自带的限流函数 <code>WaitN()</code> 。</p><p><code>TotalRateLimit</code> 配置项则在创建 <code>Daemon</code> 时被赋值给 <code>pieceManager</code> 的<code>pm.limiter</code> ,在 <code>pieceManager</code> 的 <code>DownloadPiece()</code> 和 <code>processPieceFromSource()</code> 函数中用到的 <code>pm.limiter</code> ,而这两个函数都会由 <code>peerTaskConductor</code> 调用,也就是说 P2P 下载会先进行总限速,之后再进行每个任务单独限速。</p><p><img src="/img/remote/1460000042826939" alt="图片" title="图片"></p><p>根据以上分析,Dragonfly 进行任务限速的逻辑为,每个peer task(<code>peerTaskConductor</code>)会有单独的限速 <code>perPeerRateLimit</code> ,同时 <code>pieceManager</code> 会有 <code>TotalRateLimit</code> 的总限速,以此达到单任务单独限流,同时限制所有任务总带宽的效果。</p><p>### 03 优化方案</p><p>为了解决此前静态限流算法总带宽利用率不佳的缺点,需要将其改进为动态限流算法,即总带宽限速仍恒定,但每个任务的单独带宽限速需要根据上下文适度、定期调整,已达到最大化利用总带宽、同时相对公平分配带宽的目的。</p><p>在经过数个改版后,最终我们确定了根据上下文进行限流的 sampling traffic shaper 动态限流算法。具体方案为,每个任务的单任务限流交由 <code>TrafficShaper</code> 组建进行统一管理, <code>TrafficShaper</code> 维护当前正在运行的所有任务,并且定期(每秒)更新这些任务的带宽。</p><p>具体来说,上下文指每个任务在上一秒使用的带宽、每个任务的剩余大小、任务数量、任务保底带宽(不能低于 <code>pieceSize</code> )等因素, <code>TrafficShaper</code> 会根据这些上下文公平地、效率最大化地为每个任务分配其下一秒的带宽(具体分配方案详见下一小节),实现动态限流的效果。</p><h3>04 优化实现</h3><p>定义 <code>TrafficShaper</code> 接口如下:</p><pre><code>// TrafficShaper allocates bandwidth for running tasks dynamically
type TrafficShaper interface {
// Start starts the TrafficShaper
Start()
// Stop stops the TrafficShaper
Stop()
// AddTask starts managing the new task
AddTask(taskID string, ptc *peerTaskConductor)
// RemoveTask removes completed task
RemoveTask(taskID string)
// Record records task's used bandwidth
Record(taskID string, n int)
// GetBandwidth gets the total download bandwidth in the past second
GetBandwidth() int64
}</code></pre><p>该接口有两种实现,第一种是 <code>samplingTrafficShaper</code> 即基于上下文的 traffic shaper ,第二种是 <code>plainTrafficShaper</code> 只记录带宽使用情况,除此之外不做任何动态限流工作,用于和 <code>samplingTrafficShaper</code> 对比性能提升。</p><p>同时,将相关配置项修改为如下内容:</p><pre><code># 下载服务选项。
download:
# 总下载限速。
totalRateLimit: 1024Mi
# 单个任务下载限速。
perPeerRateLimit: 512Mi
# traffic shaper类型,有sampling和plain两种可选 trafficShaperType: sampling</code></pre><p><img src="/img/remote/1460000042826940" alt="图片" title="图片"></p><p>Traffic shaper 的具体运行逻辑为,由<code>peerTaskManager</code>维护<code>trafficShaper</code>,在创建<code>peerTaskManager</code>时,根据配置初始化<code>trafficShaper</code>,并且调用<code>Start()</code>函数,启动<code>trafficShaper</code>,具体来说,新建<code>time.NewTicker</code>,跨度为 1 秒,也即每秒<code>trafficShaper</code>都会调用<code>updateLimit()</code>函数以动态更新所有任务的带宽限流。</p><p><code>updateLimit()</code> 函数会遍历所有运行中的任务,得出每个任务上一秒消耗的带宽以及所有任务消耗的总带宽,随后根据任务上一秒使用的带宽、任务剩余大小等因素,按比例分配带宽,具体来说首先根据上一秒该任务使用带宽以及该任务剩余大小的最大值确定下一秒该任务带宽,接着所有任务带宽根据总带宽按比例缩放,得到下一秒的真实带宽;同时需要确保每个任务的带宽不低于该任务的 <code>pieceSize</code> ,以免出现持续饥饿状态。</p><p>在 <code>peerTaskManager</code> 的 <code>getOrCreatePeerTaskConductor()</code> 函数中,若新建任务,需要带宽,那么调用 <code>AddTask()</code> 更新所有任务的带宽,即按照已有任务的平均任务分配带宽,然后再根据总带宽上限将所有任务的带宽等比例进行缩放;根据平均带宽分配新任务带宽的优势为,避免了已经有一个任务占满了所有带宽,有新任务进来时,带宽会被压缩到很小 **的情况;同时,不是平均分配带宽,而是按需等比例分配,可以确保带宽需求量大的任务仍然带宽最多。在 <code>peerTaskManager</code> 的 <code>PeerTaskDone()</code> 函数中,任务完成,不再占用带宽,调用 <code>RemoveTask()</code> 按比例扩大所有任务的带宽。</p><p>最后, <code>peerTaskManager</code> 停止时,调用 <code>Stop</code> 函数,停止运行 traffic shaper 。</p><h3>05 优化结果</h3><p>测试 traffic shaper 相比原有的静态限流策略在单个任务、多个任务并发、多个任务交错等多种情况下的性能提升,测试结果如下:</p><p><img src="/img/remote/1460000042826941" alt="图片" title="图片"><br><em>注:若不特殊注明,单任务限流为4KB/s,总限流为10KB/s</em></p><p>可以看到, traffic shaper 在单任务、多任务不相交、单任务低带宽等情况下相比静态限流策略性能提升明显,为 24%~59% 。在多个任务并发、多个任务交错等情况下和静态限流策略性能相当。综上,实验证明 sampling traffic shaper 能很好地解决任务数量较少时总带宽被大量浪费的情况,同时在任务数量较多以及其他复杂情况时依旧能保证和静态限流算法持平的效果。</p><p>PR 链接(已合并):<br><em><a href="https://link.segmentfault.com/?enc=EAGEtC1LkCtB%2F1YD8xBWzw%3D%3D.Hwcw4AmqIm2okPs6TQ%2BAXL4W7V%2BnDBu%2B5XNCpGjIFJp%2BcRyvumdsje4XRTFK6JDA3JGUAdAQv6AFHbXvl0O6QQ%3D%3D" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2/pull/1654</a></em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=2tiJsi%2FarnNlR0NRzuHrDA%3D%3D.HVjSafgK%2FLTkCTGzDT5c4LFOPbIHdXuCmfvbRgsy6IWbm8GOzQL2uGQLh8yGH%2B8HDz655lRIQOX4B3yiyYmiMKStFNZEBXv1o1oFnR%2BLG5RHgT8ZRBjc2aSKGl%2FlIfW6WWkIdSxEO9DGHwecD34q0JWF9qfrWceLBpgJvm9pfZId%2BuDnErRn%2FE6g4co7GcN2IzNgbYRQ4w3%2BpTmYfRGUxBXisJqY2lSvClYgReyTvMFqoZesfgRVC4x7TmwPmHqdrfDO25mh%2BzBNDRzp3gdT6o90h%2FfwKydPs3YzuOPgNFY%3D" rel="nofollow">Dragonfly 基于 P2P 的文件和镜像分发系统</a></p><p><a href="https://link.segmentfault.com/?enc=LmuXty%2F%2BGaQQymdhtUqlFg%3D%3D.kS3BScXHcSwkA9Gmz7tTEpiQRiktofbzlaIpNTOSAuoCIWsFfhd%2Fzj6gDvDpoZOiWKCsoBVRjmZlYGKtc%2F5G4D%2FDhyG4j3lA3ptzB%2BloNHyXurAGrvvafmA9RnvoduH1aD4Mf3pHvd5UoZDJq5KXIhAP08%2B0f7Tafr0NojoMZq%2FJ3O4sli7VaxDyIEsVLs%2B0CBg3gD7Gp%2BHznNOBVry9jLrnEGCsmzFd9Y4cZ%2FSMpki7%2FEBMPiCJe6SihuWzEvCprOo6MtQQxZSzUpttcSXLNwDvVum4ykMTwGRSGEK6WYc%3D" rel="nofollow">深入 HTTP/3(2)|不那么 Boring 的 SSL</a></p><p><a href="https://link.segmentfault.com/?enc=C1f8G8X2h%2FPiEaZI%2F0gc2g%3D%3D.j01NsyKbd9Yh4cylAByr3400VVtKGDi72IOUMhw91OgQYPsU8DUtE58COnUObe84y1xMIiZvO8wAP%2Bao8kRGpIS9i4J8X7bj%2FDKZYi0qMRDU0DGFH4vWaP75QhNmkoiNasBfWcM9aRWxfnoaru8TigMSg%2BC%2BPK5P13psSxmbcesv7JVDY3cDR33fKwvLi%2F1L5UazEbZFKVoyo9Yzsy9%2FRItKDY1Uh5%2BLgDsR4Ze790LPLafT2DhGk%2BlroCDFp1ylbDyF%2FXlUxdlriTP0wNXeIS4C2G71O9WCvEwkJP78XmY%3D" rel="nofollow">Go 代码城市上云——KusionStack 实践</a></p><p><a href="https://link.segmentfault.com/?enc=TY17VmumMlvSjidZ0TMfMg%3D%3D.oETkbf7285Uz0H0CadCj%2B2w0NMjAi2qLUTCx9b7%2FuUjoWg4ZiGhI24LS284IzImRDn%2BP3AZBMrHHO%2BAbcO8APcTWDwSTdOaitHebGCludRBXZ2rhdhBglbriTR5Hvw41vpczqRSglQWidnSqcI%2Fv1Q6sVwFfMn29Nit9YRE3NWY7XCF%2FSxbfMDvKD80nA8lr%2F0ZKLg3AclpAtVjPXEEi%2F6fo1hsajKWC3fDmoq3gvc3I39WvXG6Gvr7WRvFehy4kiLp8pB0WMYMfj%2BwTzkIlulA9lv9gcMCwRqZlPqCYDyc%3D" rel="nofollow">MOSN 反向通道详解</a></p>
Nydus | 容器镜像基础
https://segmentfault.com/a/1190000042775128
2022-11-09T11:33:42+08:00
2022-11-09T11:33:42+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p>文|唐斌</p><p>字节跳动基础架构研发工程师 </p><p>Nydus 与 Nydus snapshotter 社区贡献者,专注存储,云原生技术。 </p><p>本文 <strong>6964</strong> 字 阅读 <strong>15</strong> 分钟</p><hr><h2>1 Nydus</h2><h3>1.1 存在的问题</h3><p><strong>对于容器镜像使用者</strong></p><p><strong>问题一:</strong> 启动容器慢:容器启动慢的情况普遍发生在当用户启动一个很大的容器镜像时,由于在容器准备阶段需要三步(以 overlayfs 为例):</p><p><strong>-</strong> 下载镜像;</p><p><strong>-</strong> 解压镜像;</p><p><strong>-</strong> 使用 overlayfs 将容器可写层和镜像中的只读层聚合起来提供容器运行环境。</p><p>其中,下载镜像阶段需要下载整个镜像文件,不能实现文件数据按需加载。再加上下载镜像本身受限于网络带宽,当容器镜像达到 GB 级别时,下载时间会较长,破坏了容器原本优秀的用户体验。</p><p><strong>问题二:</strong> 较高的本地存储成本:不同镜像之间可以共享的最小单位是镜像中的层,缺点之一是重复数据的处理效率较低。</p><p>原因如下:</p><p><strong>-</strong> 首先,层内部存在重复的数据;</p><p><strong>-</strong> 其次,层与层之间可能存在大量重复的数据,即使有微小的差别,也会被作为不同的层;</p><p><strong>-</strong> 再次,根据 OCI imagespec 对删除文件和 hardlink 的设计,镜像内部已经被上层删除的文件可能仍然存在于下层,并包含在镜像中。</p><p><strong>对于镜像提供者</strong></p><p>这里的提供者主要指容器服务的镜像中心。</p><p><strong>问题一:</strong> 巨大的存储资源浪费。</p><p><strong>-</strong> <strong>存在大量相似镜像,造成这种情况有两个原因:</strong></p><ul><li>首先,上面提到的层的缺点,导致在容器镜像中心存在许多相似镜像;</li><li>其次,OCI image 使用了 tar+gzip 格式来表示镜像中的层,而 tar 格式并不区分 tar archive entries ordering,这带来一个问题,如果用户在不同机器上 build 同一个镜像,最终可能会因为使用了不同的文件系统而得到不同的镜像,用户上传之后,镜像中心中会存在若干不同镜像的实质内容是完全相同的情况。</li></ul><p><strong>- 镜像去重效率低</strong></p><p>虽然镜像中心有垃圾回收机制来实现去重功能,但其仍然以层为单位,所以只能在有完全相同 hash value 的层之间去重。</p><p><strong>问题二:</strong> 云原生软件供应链带来的新需求。</p><p>随着时间推移,和软件供应链一起发展的还有对软件供应链环节的多样性攻击手段。安全防护是软件供应链中非常重要的组成,不光体现在对软件本身的安全增强,也体现在对供应链的安全增强。因为应用运行环境被前置到了容器镜像中,所以对容器镜像的安全,包括对镜像的漏洞扫描和签名成为了容器服务提供者的必要能力。</p><p><strong>OCI 镜像规范的缺陷</strong></p><p>主要的缺陷有两点:</p><p><strong>- tar 格式标准</strong></p><ul><li>tar 格式并不区分 tar archive entries ordering,这带来一个问题,即如果用户在不同机器上 ;build 同一个镜像,最终可能会因为使用了不同的文件系统而得到不同的镜像,比如在文件系统 A 上的 order 是 foo 在 bar 之前进入 tar ,在文件系统 B 上的 order 是 bar 在 foo 之前进入tar ,那么这两个镜像是不同的;</li><li>当 tar 被 gzip 压缩过之后不支持 seek ,导致运行之前必须先下载并解压 targz 的 image layers,而不能实现文件数据按需加载。</li></ul><p><strong>- 以层为镜像的基本单位</strong></p><ul><li>内容冗余:不同层之间相同信息在传输和存储时都是冗余内容,在不读取内容的时候无法判断到这些冗余的存在;</li><li>无法并行:每一层是一个整体,对同一个层既无法并行传输,也不能并行提取;</li><li>无法进行小块数据的校验,只有完整的层下载完成之后,才能对整个层的数据做完整性校验;</li><li>其他一些问题:比如,跨层数据删除难以完美处理。</li></ul><h3>1.2 Nydus 基础</h3><p>在容器的生产实践中,偏小的容器镜像能够很快部署启动。当应用的镜像达到 GB 级以上的时候,在节点上下载镜像通常会消耗大量的时间。Dragonfly 通过引入 P2P 网络有效地提升了容器镜像大规模分发的效率。然而,用户必须等待镜像数据完整下载到本地,然后才能创建自己的容器。</p><p>Nydus 是在最新的 OCI Image-Spec 基础之上设计的容器镜像加速服务,重新设计了镜像格式和底层文件系统,从而加速容器启动速度,提高大规模集群中的容器启动成功率。Nydus 由阿里云和蚂蚁集团的工程师合作开发,并大规模部署在内部的 生产环境中。</p><p>Nydus 优化了现有的 OCI 镜像标准格式,并以此设计了一个用户态的文件系统。通过这些优化,Nydus 能够提供这些特性:</p><ul><li>容器镜像按需下载,用户不再需要下载完整镜像就能启动容器</li><li>块级别的镜像数据去重,最大限度为用户节省存储资源</li><li>镜像只有最终可用的数据,不需要保存和下载过期数据</li><li>端到端的数据一致性校验,为用户提供更好的数据保护</li><li>兼容 OCI 分发标准和 artifacts 标准,开箱即可用</li></ul><p>支持不同的镜像存储后端,镜像数据不只可以存放在镜像仓库,还可以放到 NAS 或者类似 S3 的对象存储上</p><ul><li>与 Dragonfly 的良好集成</li></ul><h3>1.3 Nydus 架构</h3><p><img src="/img/remote/1460000042775130" alt="图片" title="图片"></p><p>Nydus 的架构主要包含两部分内容:</p><p><strong>- 新的镜像格式(Rafs)</strong></p><p><img src="/img/remote/1460000042775131" alt="图片" title="图片"></p><p><strong>- 负责解析容器镜像的 FUSE 用户态文件系统进程</strong></p><p>Nydus 兼容多种文件系统,能够解析 FUSE 和 virtiofs 协议来支持传统的 runc 容器、 Kata容器。对于存储后端,支持使用容器仓库( Registery )、OSS 对象存储 、NAS、Dragonfly 的超级节点和 Peer 节点作为 Nydus 的镜像数据存储后端。此外,为了加速启动速度,Nydus 还可以配置一个本地缓存,避免每次启动容器时都从远端数据源拉取数据。</p><h3>1.4 Nydus 特性</h3><p><strong>容器启动速度变快</strong></p><p>用户部署了 Nydus 镜像服务后,由于使用了按需加载镜像数据的特性,容器的启动时间明显缩短。在官网的测试数据中,Nydus 能够把常见镜像的启动时间,从数分钟缩短到数秒钟。理论上来说,容器镜像越大,Nydus 体现出来的效果越明显。</p><p><img src="/img/remote/1460000042775132" alt="图片" title="图片"></p><p><strong>提供运行时数据一致校验</strong></p><p>在传统的镜像中,镜像数据会先被解压到本地文件系统,再由容器应用去访问使用。解压前,镜像数据是完整校验的。但是解压之后,镜像数据不再能够被校验。这带来的一个问题就是,如果解压后的镜像数据被无意或者恶意地修改, 用户是无法感知的。而 Nydus 镜像不会被解压到本地,同时可以对每一次数据访问进行校验,如果数据被篡改,则可以从远端数据源重新拉取。</p><p><img src="/img/remote/1460000042775133" alt="图片" title="图片"></p><p>从图中可以看出,对容器镜像数据进行运行时一致性校验是通过对每个数据块计算 SHA 256 实现的,这得益于 Nydus 采用分块的方式管理镜像数据。如果在镜像文件非常大的时候,对整个镜像文件计算哈希值非常不现实。</p><h3>1.5 Nydus 镜像格式:RAFS</h3><p>RAFS 是对 EROFS 文件系统的增强,拓展在云原生场景下的能力,使其适应容器镜像存储场景。RAFS v6 是内核态的容器镜像格式,除了将镜像格式下沉到内核态,还在镜像格式上进行了一系列优化,例如块对齐、更加精简的元数据等。</p><p><strong>RAFS v6 镜像格式</strong></p><p><img src="/img/remote/1460000042775134" alt="图片" title="图片"></p><h3>1.6 Nydus -snapshotter</h3><p>Nydus snapshotter 是 containerd 的一个外部插件,使得 containerd 能够使用 Nydus 镜像加速服务。在 containerd 中, snapshot 的工作是给容器提供 rootfs,Nydus snapshotter 实现了 containerd 的 snapshot 的接口,使得 containerd 可以通过 Nydus 准备 rootfs 以启动容器。由于 nydus-snapshotter 实现了按需加载的特性,在 containerd 启动容器时,只需要根据容器镜像的元数据信息准备 rootfs ,部分目录对应的数据并未存储在本地,当在容器中访问到(本地访问未命中)这部分数据时,通过 Nydusd 从镜像 registry 拉取对应数据内容。</p><p><img src="/img/remote/1460000042775135" alt="图片" title="图片"></p><h2>02 FUSE</h2><p>用户态文件系统( filesystem in userspace, 简称 FUSE )使得用户无需修改内核代码就能创建自定义的文件系统。FUSRE 催生了著名的 fuse-overlayfs,其在 rootless 容器化中扮演重要的角色。</p><p>用户态文件系统并不完全在用户态实现,由两部分组成:内核模块和用户态进程。</p><ul><li>内核模块:文件系统数据流程的功能实现,负责截获文件访问请求和返回用户态进程处理请求的结果</li><li>用户态进程:负责处理具体的数据请求,对应处理函数由内核模块触发</li></ul><p>FUSE 的工作流程如下图:</p><p><img src="/img/remote/1460000042775136" alt="图片" title="图片"></p><p>其中,fuse_user 是运行在用户态的文件系统进程,该程序会在启动时注册实现的数据请求处理接口,如 ls 、cd 、mkdir 等,同时,程序在某个路径挂载 fuse 文件系统 /tmp/fuse_fs ,当对 /tmp/fuse_fs 执行相关操作时:</p><ul><li>请求会经过 VFS(虚拟文件系统) 到达 fuse 的内核模块</li><li>内核模块根据请求类型,调用用户态进程注册的函数</li><li>当程序完成对请求的处理后,将结果通过 VFS 返回给系统调用</li></ul><h2>03 Containerd</h2><p>Containerd 最开始是 Docker Engine 中的一部分,后来,containerd 被分离出来作为独立的开源项目,目标是提供更开放、稳定的容器运行基础设施。分离出来的 containerd 将具有更多的功能,涵盖整个容器运行时管理的所有需求。</p><p>Containerd 是一个行业标准的容器运行时,强调简单性、健壮性和可移植性,可以作为守护进程运行在系统中。</p><p>Containerd 的功能主要包括以下内容:</p><ul><li>管理容器的生命周期(从创建容器到销毁容器)</li><li>拉取/推送容器镜像</li><li>存储管理(管理镜像及容器数据的存储)</li><li>调用 runc 运行容器(与 runc 等容器运行时交互)</li><li>管理容器网络接口及网络</li></ul><p>Containerd 采用 C/S 架构,服务端通过 unix domain socket 暴露低层 gRPC 接口,客户端通过这些 gRPC 接口管理节点上的容器,containerd 负责管理容器的镜像、生命周期、网络和存储,实际运行容器是由容器运行时(runc 是其中一种)完成。</p><p><img src="/img/remote/1460000042775137" alt="图片" title="图片"></p><p>Containerd 将系统划分成不同的组件,每个组件由一个或多个模块协作完成(Core 部分),不同模块都以插件的形式集成到 containerd 中,插件之间相互依赖。</p><p><img src="/img/remote/1460000042775138" alt="图片" title="图片"></p><p>Containerd 的组件可以分成三类:Storage、Metadata 和 Runtimes,snapshot 属于 Storage 组件中的一个插件,用来管理容器镜像的文件系统快照,镜像中的每一层都会被解压成文件系统快照。在使用 Nydus 的 containerd 环境中,Nydus-snapshotter 负责完成这部分工作。</p><h2>04 Erofs + fsache</h2><p><strong>Erofs over fscache 基本概念</strong></p><p>Erofs over fscache 是 Linux 内核原生的镜像按需加载特性,于 5.19 版本合入 Linux 内核主线。</p><p>已有的用户态方案会涉及频繁的内核态/用户态上下文切换,以及内核态/用户态之间的内存拷贝,从而造成性能瓶颈。这一问题在容器镜像已经全部下载到本地的时候尤其突出,容器运行过程中涉及的文件访问,都会陷出到用户态的服务进程。</p><p>事实上我们可以将按需加载的</p><p>(1)缓存管理和 </p><p>(2)缓存未命中的时候通过各种途径 (例如网络) 获取数据,这两个操作解耦开。缓存管理可以下沉到内核态执行,这样当镜像在本地 ready 的时候,就可以避免内核态/用户态上下文的切换。而这也正是 erofs over fscache 技术的价值所在。</p><p>fscache/cachefiles (以下统称 fscache ) 是 Linux 系统中相对成熟的文件缓存方案,广泛应用于网络文件系统,erofs over fscache 技术使得 fsache 能够支持 erofs 的按需加载特性。</p><p>容器在访问容器镜像的时候,fscache 会检查当前请求的数据是否已经缓存,如果缓存命中 ( cache hit ),那么直接从缓存文件读取数据。这一过程全程处于内核态之中,并不会陷出到用户态。</p><p><img src="/img/remote/1460000042775139" alt="图片" title="图片"></p><p>缓存未命中 ( cache miss )时 需要通知用户态的 Nydusd 进程以处理这一访问请求,此时容器进程会陷入睡眠等待状态;Nydusd 通过网络从远端获取数据,通过 fscache 将这些数据写入对应的缓存文件,之后通知之前陷入睡眠等待状态的进程该请求已经处理完成;之后容器进程即可从缓存文件读取到数据。</p><p><strong>Erofs over fscache 优势</strong></p><p><strong>- 异步预取</strong></p><p>容器创建之后,当容器进程尚未触发按需加载 (cache miss) 的时候,用户态的 Nydusd 就可以开始从网络下载数据并写入缓存文件,之后当容器访问的文件位置恰好处于预取范围内的时候,就会触发 cache hit 直接从缓存文件读取数据,而不会再陷出到用户态。用户态方案则无法实现该优化。</p><p><img src="/img/remote/1460000042775140" alt="图片" title="图片"></p><p><strong>- 网络 IO 优化</strong> </p><p>当触发按需加载 (cache miss) 时,Nydusd 可以一次性从网络下载比当前实际请求的数据量更多的数据,并将下载的数据写入缓存文件。例如容器访问 4K 数据触发的 cache miss,而 Nydusd 实际一次性下载 1MB 数据,以减小单位文件大小的网络传输延时。之后容器访问接下来的这 1MB 数据的时候,就<strong>不必再陷出到用户态</strong>。</p><p>用户态方案则无法实现该优化,因为即使触发 cache miss 的时候,用户态的服务进程同样实现了该优化,下一次容器访问位于读放大范围内的文件数据的时候,同样会陷出到用户态。</p><p><img src="/img/remote/1460000042775141" alt="图片" title="图片"></p><p><strong>- 更佳的性能表现</strong></p><p>当镜像数据已经全部下载到本地的时候 (即不考虑按需加载的影响), erofs over fscache 的性能表现显著优于用户态方案,同时与原生文件系统的性能相近,从而实现与原生容器镜像方案 (未实现按需加载) 相近的性能表现。</p><h2>05 环境安装</h2><p><strong>nerdctl 安装</strong></p><pre><code class="rust"># git clone https://github.com/rootless-containers/rootlesskit.git
# cd rootlesskit
# make && sudo make install
wget https://github.com/containerd/nerdctl/releases/download/v0.22.2/nerdctl-full-0.22.2-linux-amd64.tar.gz
sudo tar -zxvf nerdctl-full-0.22.2-linux-amd64.tar.gz -C /usr/local
sudo systemctl enable --now containerd
sudo systemctl enable --now buildkit
# sudo apt-get install uidmap -y
# containerd-rootless-setuptool.sh install
sudo nerdctl version # 需要使用sudo,不然会提示安装 rootless</code></pre><p><img src="/img/remote/1460000042775142" alt="图片" title="图片"></p><p><strong>Nydus 安装</strong></p><p>装主要的3个工具(也可以直接下载所有工具的二进制文件,编译安装时默认没有没有 Nydusify ):</p><p>- <code>nydusify</code> 将 OCI 格式的容器镜像转换为 Nydus 格式( RAFS )容器镜像的工具。</p><p>- <code>nydus-image</code>将解压后的容器镜像转换为 Nydus 格式镜像的工具。</p><p>- <code>nydusd</code> 解析 Nydus 格式镜像并提供 FUSE 挂载点以供容器访问的守护程序。<code>nydusd</code> 也可以用作 virtiofs 后端,使得 Guest 可以访问 Host 的文件。</p><pre><code class="rust">git clone https://github.com/dragonflyoss/image-service.git
cd image-service
make && make install
# 默认没有安装 nydusify
wget https://github.com/dragonflyoss/image-service/releases/download/v2.1.0-rc.1/nydus-static-v2.1.0-rc.1-linux-amd64.tgz
mkdir nydus-static
tar -zxvf nydus-static-v2.1.0-rc.1-linux-amd64.tgz -C nydus-static
sudo cp nydus-static/nydusify /usr/local/bin
sudo cp nydus-static/nydus-overlayfs /usr/local/bin
nydus-image --version
nydusify --version
nydusd --version</code></pre><p><strong>安装 Nydus-snapshotter</strong></p><pre><code class="rust">git clone github.com/containerd/nydus-snapshotter.git
cd nydus-snapshotter
make && make install
sudo systemctl enable nydus-snapshotter
sudo systemctl start nydus-snapshotter
systemctl status nydus-snapshotter</code></pre><p><img src="/img/remote/1460000042775143" alt="图片" title="图片"></p><p>Nydus-snapshotter 以 service 的形式运行 /usr/local/bin/containerd-nydus-grpc 可执行文件,配置信息位于 /etc/nydus/config.json 文件。</p><p>默认 address 位置:</p><p>/run/containerd-nydus/containerd-nydus-grpc.sock</p><p>默认工作目录:</p><p>/var/lib/containerd-nydus-grpc</p><p>默认缓存目录:</p><p>/var/lib/containerd-nydus-grpc/cache</p><p><strong>部署本地镜像仓库(测试用)</strong></p><pre><code class="rust"># sudo docker run -d -p 5000:5000 \
# --restart=always \
# --name registry \
# -v /var/lib/registry:/var/lib/registry \
# -d registry
sudo docker run -d --name=registry --restart=always -p 5000:5000 registry
sudo docker logs registry -f</code></pre><p><strong>将 OCI 格式的镜像转换为 RAFS 格式镜像</strong></p><pre><code class="rust">sudo nydusify convert \
--nydus-image $(which nydus-image) \
--source ubuntu:16.04 \
--target localhost:5000/ubuntu:16.04-nydus</code></pre><p>Nydusify 基本命令:</p><p><img src="/img/remote/1460000042775144" alt="图片" title="图片"></p><p>转换后的镜像层文件位于当前目录下的 tmp 文件夹:</p><pre><code class="rust">sudo tree tmp -L 4</code></pre><p><img src="/img/remote/1460000042775145" alt="图片" title="图片"></p><p>将 OCI 标准的镜像格式转换为 Nydus 使用的 RAFS 镜像格式后,可以使用 Nydusd 解析并提供 fuse 挂载点供容器使用。编写配置文件 registry.json,使得 Nydus 使用 容器镜像 registry (已经搭建本地容器镜像 register 用于测试)作为存储后端。</p><pre><code class="rust">{
"device": {
"backend": {
"type": "registry",
"config": {
"scheme": "http",
"host": "localhost:5000",
"repo": "ubuntu"
}
},
"digest_validate": false
},
"mode": "direct"
}</code></pre><p>挂载 RAFS 镜像 为 fuse 挂载点,--bootstrap 参数传递位于 tmp/bootstraps 下的文件路径:</p><pre><code class="rust">sudo nydusd \
--config ./registry.json \
--mountpoint /mnt \
--bootstrap ./tmp/bootstraps/4-sha256:fb15d46c38dcd1ea0b1990006c3366ecd10c79d374f341687eb2cb23a2c8672e \
--log-level info</code></pre><p>查看挂载情况: </p><p><img src="/img/remote/1460000042775146" alt="图片" title="图片"></p><p>输出日志信息:</p><p><img src="/img/remote/1460000042775147" alt="图片" title="图片"></p><p>除了使用 Nydusify 直接将 OCI 标准格式镜像转换为 Nydus 格式镜像,Nydus-image 工具也支持直接对已经解压的 OCI 容器镜像文件层转换为 Nydus 格式镜像。</p><p><strong>(1)获取 OCI 镜像元数据信息:</strong></p><pre><code class="rust">docker pull ubuntu:16.04
sudo docker inspect -f "{{json .GraphDriver }}" ubuntu:16.04 | jq .</code></pre><p>Docker 使用 overlay2 存储驱动,通过所需的 lowerdir 、upperdir 、merged 和 workdir 结构自动创建 overlay 挂载点。</p><p><img src="/img/remote/1460000042775148" alt="图片" title="图片"></p><p>对于 Nydus 来说,目录树(通常是一个镜像层)由两部分组成:</p><ul><li>bootstrap:存储目录的文件系统元数据信息</li><li>blob:存储目录中的所有文件数据</li></ul><p><strong>(2)建立生成 Nydus 镜像的目录:</strong></p><pre><code class="rust">mkdir -p nydus-image/{blobs,layer1,layer2,layer3,layer4}</code></pre><p><strong>(3)转换最底层的镜像层:</strong></p><pre><code class="rust">sudo nydus-image create \
--bootstrap ./nydus-image/layer1/bootstrap \
--blob-dir ./nydus-image/blobs \
--compressor none /var/lib/docker/overlay2/78f2b3506072c95ca3929a0a797c1819e8966b8bbf5ce8427b671296ca1ad35a/diff
tree -L 2 ./nydus-image</code></pre><p><img src="/img/remote/1460000042775149" alt="图片" title="图片"></p><p><strong>(4)转换第 2 底层镜像层,--parent-bootstrap 指父层,即刚才转换好的镜像层:</strong></p><pre><code class="rust">sudo nydus-image create \
--parent-bootstrap ./nydus-image/layer1/bootstrap \
--bootstrap ./nydus-image/layer2/bootstrap \
--blob-dir ./nydus-image/blobs \
--compressor none /var/lib/docker/overlay2/373ea430abb0edd549583f949ec8259806d9eb7d0a0416ec1494d2fc7efeeedc/diff</code></pre><p><img src="/img/remote/1460000042775150" alt="图片" title="图片"></p><p><strong>(5)转换第 3 层和第 4 层,每次都需要指定 --parent-bootstrap 为上一次生成的镜像层:</strong></p><pre><code class="rust">sudo nydus-image create \
--parent-bootstrap ./nydus-image/layer2/bootstrap \
--bootstrap ./nydus-image/layer3/bootstrap \
--blob-dir ./nydus-image/blobs \
--compressor none /var/lib/docker/overlay2/05424b8c067c59368c11ad5674d68d95365e87487bdf10e3d9842b1016583369/diff
sudo nydus-image create \
--parent-bootstrap ./nydus-image/layer3/bootstrap \
--bootstrap ./nydus-image/layer4/bootstrap \
--blob-dir ./nydus-image/blobs \
--compressor none /var/lib/docker/overlay2/942c712e7276be5bde4fb7b30f72583c4a9cf0b2aaa14215cd690daf893a630e/diff</code></pre><p>将 Nydus 镜像挂载到目录:</p><pre><code class="rust">sudo nydusd \
--config ./localfs.json \
--mountpoint /mnt \
--bootstrap ./nydus-image/layer4/bootstrap \
--log-level info</code></pre><p>其中, localfs.json 文件的内容为:</p><pre><code class="rust">{
"device": {
"backend": {
"type": "localfs",
"config": {
"dir": "/<YOUR-WORK-PATH>/nydus-image/blobs"
}
}
},
"mode": "direct"
}</code></pre><p>Dir 为生成的 Nydus 镜像文件中 blobs 目录的绝对路径。</p><h2>06 通过 Nydus+snapshotter 启动容器</h2><p><strong>添加配置文件</strong></p><p>Nydus 提供了 containerd 远程快照管理工具 containerd-nydus-grpc 用于准备 Nydus 镜像格式的容器 rootfs ,首先将 Nydusd 配置保存到 /etc/nydus/config.json 文件。</p><pre><code class="rust">sudo tee /etc/nydus/config.json > /dev/null << EOF
{
"device": {
"backend": {
"type": "registry",
"config": {
"scheme": "http",
"skip_verify": false,
"timeout": 5,
"connect_timeout": 5,
"retry_limit": 2,
"auth": ""
}
},
"cache": {
"type": "blobcache",
"config": {
"work_dir": "cache"
}
}
},
"mode": "direct",
"digest_validate": false,
"iostats_files": false,
"enable_xattr": true,
"fs_prefetch": {
"enable": true,
"threads_count": 4
}
}
EOF</code></pre><p>containerd-nydus-grpc 会自动从 $HOME/.docker/config.json 中读取 docker login auth ,如果不想使用这个值,需要直接替换配置文件中的内容。</p><p>直接从终端启动 containerd-nydus-grpc,如果已经通过 containerd-nydus-grpc service 启动,则可以跳过此步骤:</p><pre><code class="rust">sudo /usr/local/bin/containerd-nydus-grpc \
--config-path /etc/nydus/config.json \
--shared-daemon \
--log-level info \
--root /var/lib/containerd/io.containerd.snapshotter.v1.nydus \
--cache-dir /var/lib/nydus/cache \
--address /run/containerd-nydus/containerd-nydus-grpc.sock \
--nydusd-path /usr/local/bin/nydusd \
--nydusimg-path /usr/local/bin/nydus-image \
--log-to-stdout</code></pre><p><strong>修改 containerd 配置文件</strong></p><p>proxy_plugins.nydus 的 address 和 containerd-nydus-grpc 的对应。</p><pre><code class="rust">sudo tee -a /etc/containerd/config.toml << EOF
[proxy_plugins]
[proxy_plugins.nydus]
type = "snapshot"
address = "/run/containerd-nydus/containerd-nydus-grpc.sock"
[plugins.cri]
[plugins.cri.containerd]
snapshotter = "nydus"
disable_snapshot_annotations = false
EOF
sudo systemctl restart containerd
sudo ctr -a /run/containerd/containerd.sock plugin ls | grep nydus</code></pre><p><strong>通过 Nydus 启动容器</strong></p><pre><code class="rust"># 转换镜像并上传到本地 registry
sudo nydusify convert --nydus-image /usr/local/bin/nydus-image --source ubuntu --target localhost:5000/ubuntu-nydus
sudo nerdctl --snapshotter nydus pull localhost:5000/ubuntu-nydus:latest
sudo nerdctl --snapshotter nydus run --rm -it localhost:5000/ubuntu-nydus:latest bash</code></pre><p><img src="/img/remote/1460000042775151" alt="图片" title="图片"></p><p><strong>重启主机之后启动环境</strong></p><pre><code class="rust">sudo systemctl restart nydus-snapshotter
sudo systemctl restart containerd
sudo docker rm -f registry
sudo docker run -d --name=registry --restart=always -p 5000:5000 registry && sudo docker logs registry -f</code></pre><h2>07参考资料</h2><p>[1]OCI 镜像标准格式: <br><em><a href="https://link.segmentfault.com/?enc=o%2FOwMMQt8iObHKslOiXJDw%3D%3D.OKaje3yxtzNy%2BDueLmWgKQlh0uWfGyi0%2Fwitz9XFEOG%2FhVkgXxeu1QbKaefykYwu" rel="nofollow">https://github.com/opencontainers/image-spec</a></em></p><p>[2]自校验的哈希树: <br><em><a href="https://link.segmentfault.com/?enc=FBXxSfOOPAs22fbZTLA1VQ%3D%3D.aCqnLVwd4J9ZOPScK7N1qjS%2FZEJU6v0vDa2Nr6FfpZALepjvLJiGwfCNw5fWeunQ" rel="nofollow">https://en.wikipedia.org/wiki/Merkle_tree</a></em></p><p>[3]FUSE: <br><em><a href="https://link.segmentfault.com/?enc=yJtN6D3O5VKhwi9bro7WCg%3D%3D.PTsuc1sMDfJQOL7k%2B2ahxuZ4ww3g1nVMUlTxUOTZGpX94P52lfA9WLuaVuVTkNWjxv9FFsZhlN5lgtq7bUKoBQ%3D%3D" rel="nofollow">https://www.kernel.org/doc/html/latest/filesystems/fuse.html</a></em></p><p>[4]virtiofs:<br><em><a href="https://link.segmentfault.com/?enc=6J%2FApPEEsLXbX1scvQHDPA%3D%3D.r3tfCV5KbIXKbujP%2BHASyhprFO2PrSISy4CvBIVKbf8%3D" rel="nofollow">https://virtio-fs.gitlab.io/</a></em></p><p>[5]runc 容器: <br><em><a href="https://link.segmentfault.com/?enc=TZf6jVkAvofismptLuIUQw%3D%3D.DAzZvt4Xs2Nyn%2FRVwi8sSmXiIgWrs0YU%2F7%2BzU5pDjJjqCqo9DNJB7Qe8%2BAqqLBSa" rel="nofollow">https://github.com/opencontainers/runc</a></em></p><p>[6]Kata 容器: <br><em><a href="https://link.segmentfault.com/?enc=rtaJPWLLf92UED6sVKZfyg%3D%3D.dLKUXfDo3RyL9UDLVS6uvwFCGuzrYZJR05UxNw26Jtc%3D" rel="nofollow">https://katacontainers.io/</a></em></p><p>[7]OSS 对象存储: <br><em><a href="https://link.segmentfault.com/?enc=urc5uZrKQRwo23vqsgSPBQ%3D%3D.9UUxjZZhtkJfZc9SolgJAQzm7xsIW44t2wRJhABe8INUCBnW4DXbTm1ZlsNCyQPX" rel="nofollow">https://www.alibabacloud.com/product/oss</a></em></p><p>[8]Nydus-snapshotter: <br><em><a href="https://link.segmentfault.com/?enc=3QNS%2BfnA4MrBOyzI6JZ4jA%3D%3D.zD7TD3%2BW74uRKHz3jYwhLCtwqtGP7kvSTxsXvyl8QONLoSWgaVzTt43VsaevKivE" rel="nofollow">https://github.com/containerd/nydus-snapshotter</a></em></p><p>[9]fuse-overlayfs: <br><em><a href="https://link.segmentfault.com/?enc=3BuECHBBMLvoXgu0qzk9kw%3D%3D.FCtfX32zQzoCjs5pRs9wvnUy6T9QoehAi7MlAap%2BC0CZpcQW036erNEHJ%2F2wDbjZ" rel="nofollow">https://github.com/containers/fuse-overlayfs</a></em></p><p>[10]5.19 版本:<br><em><a href="https://link.segmentfault.com/?enc=n3DxoUl2gJQR9mkfjxqzig%3D%3D.qHtPGddgNW6jj66UGfgUTV96A%2FOXHa1fz4260IG8bK4MpE8qrNXHDF%2BMzSlSBwRVhJ%2F88h%2FIbgMUV6R%2FkBmAWwuHpeb8GNGTy7lPYyN9kqsX96tlSnPXi2o9M8zc3blcS0qhdQEWWYw%2FhBjLVP1TfkE1%2BIjgp1mTI3gGhhjzN00%3D" rel="nofollow">https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=65965d9530b0c320759cd18a9a5975fb2e098462</a></em></p><p>[11]对于容器镜像使用者:<br><em><a href="https://link.segmentfault.com/?enc=VUKG%2BF5sFnpEmsLtTR0AHw%3D%3D.iO%2B%2FSZ%2F%2FnQ%2FIl37VMa3VUbc1OUgOfcshgKR%2B6Bc6lQcuJ7ddoeZbrW%2FvoQIYa5zLQCW79wpkR2e9DtR3zGSVWQ%3D%3D" rel="nofollow">https://mp.weixin.qq.com/s/yC-UmMSDja959K9i_jSucQ</a></em></p><h2>本周推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=oNjTCNvC2d7o%2FwO4Ls4fRg%3D%3D.PERPFAjh2Lbaz4z%2BAx4ABvqjvkv%2BoC50jRDMpnR1DILgz2rNbQWBPKG8jLfOyziGTuyoBYMvTnG5pCQ3vw%2Fk8eNmmWPubbFH7JZKxNU0h2nMND8Tc3CW%2BBLgtCD1gM7sllJoUPw1KZ1vhC6sIivzt4MEZ25R8jyLXbVhRdd4a67CZexIpNbLK6pMdDKm%2F9j2cu%2Bn2vx2iNgjG44v2Nc%2F%2Bu7zA0zHKHcAR6aQcVRDknfQLvK3uwv5ixcU%2BFDbew6%2BfZvVwgl4wv75zG6OLTBZM%2BQossbTjTOE0LyG1HqcOkQ%3D" rel="nofollow">Nydus —— 下一代容器镜像的探索实践</a></p><p><a href="https://link.segmentfault.com/?enc=nerte4XGEisBTb5kBwV3NQ%3D%3D.dEALg%2FTPrTQ9CsqnAygub%2FopBKvcUaUJmV4c0d7ThWmzPIXC1K3PVIavJx5CWdjAnexYznsympZ8UbSJwdNC7mFeqo4FFh7JPjvpcwuKe3MU8xGVKOADLPHaMtKfoGEfjNcPd2pWOj6mixdlemIXU6iIYWhPLawFxNwvoNWaw%2BQzWK3HmK8byNd68UaS048C1IySYNufZHyaa05tz935%2Bt5pn33WIybD4AKmHBQERc5hno%2F6OhuMvw3o2DZ9W3CRu2Xy07Uzp6CG4yL2aWEsOlAzG%2FK3ezW54KvoQSYb9mA%3D" rel="nofollow">Nydus 镜像加速插件迁入 Containerd 旗下</a></p><p><a href="https://link.segmentfault.com/?enc=AxurhLnpsyZ9KVVOkqu%2FBA%3D%3D.ZCGSkU95mbNhB3mjBooygvD%2FVcwnjluGeBmGcTgZL7POwR6Q4eQN6NDApOCXBvBMEdyzFTRdbQrNq4h9y19xXRWRz9l8TGUCwOvHVu0zqM87Gt50SVj4mPhErFH%2BAyeVDO5Xc567SeiGJHw5Manh8y8zIce2Cq4hdyeiDV%2BwPv34V3%2FOMWjhi%2FnlAHYWD9CcGW0otRWoX4YAvfzL1vpvKVg1Rv%2FOAej199WvUVcfNG4ybnFpFzcxtPbpsiOm%2BfBTTCBHPb5qVb%2BgOCjGvK6Tcm6hNzpIVDJdsCcjKxv0bQI%3D" rel="nofollow">cgo 机制 - 从 c 调用 go</a></p><p><a href="https://link.segmentfault.com/?enc=4nRpsNwZ4TNignwgGzi2Gg%3D%3D.cH3twvHGCoB%2BirarMyL7o4aBMR%2FczojhV5OAC4Qo3WSCR2%2BJwYeCeuv%2BAAuecRBuFdn03by9szENECbubAYlwK94bILU62gf8lMWsAoW%2F0W11DtJpsAcx4G1C1Uygq3OWH6vEPOZSxR6YzeNS3XtpDnpBakRqTIWlca40Xqb4M1W4hnd6sIETy7SGc1uNjmI5NXRX9G6vk0EwUzJiJvLUp%2BhYJHr1PqPnY7LEJ2C%2BDVfsN8PYai6lzG7Dxv22N98lh%2BNP7EGtIeSLq7wqn8C7Guq1hn5Vy6VxlNw2AuKVNw%3D" rel="nofollow">从规模化平台工程实践,我们学到了什么?</a></p>
SOFARegistry | 大规模集群优化实践
https://segmentfault.com/a/1190000042736139
2022-11-03T10:51:24+08:00
2022-11-03T10:51:24+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042736141" alt="图片" title="图片"> </p><p>文|李旭东</p><p>专注于 SOFARegistry 及其周边基础设施的开发与优化</p><p>本文 <strong>7016</strong> 字 阅读 <strong>15</strong> 分钟</p><h2>1 前言</h2><p>SOFARegistry 在蚂蚁内部迭代升级过程中,每年大促都会引来一些新的挑战,通过不断的优化这些在大规模集群遇到的性能瓶颈,我们总结出一些优化方案,来解决大规模集群遇到的性能问题。 <br>通过阅读这篇文章,读者可以学习到一些 Java 和 Go 语言系统的优化技巧,在系统遇到瓶颈的时候,能够知道有哪些优化手段针对性的进行优化。</p><h2>2 大规模集群的挑战</h2><p>随着业务的发展,业务的实例数在不断增长,注册中心所需要承载的数据量也在快速的增长 ,以其中 1 个集群为例,2019 年的数据为基准数据,在 2020 年 pub 接近千万级。下图是该集群历年双 11 时的数据对比。 <br>相比 2019 年双 11,2021 年双 11 接口级的 pub 增长 200%,sub 增长 80%。实例数和数据量的增长带来推送量的二次方形式的增长,SOFARegistry 每一年大促都会经历新的挑战。</p><p><img src="/img/remote/1460000042736142" alt="图片" title="图片"></p><p>比如,在某一年的新机房压测过程中,由于新机房规模特别大(普通机房的 4 倍),导致注册中心的推送压力变大了十倍多,出现了 : <br><strong>-</strong> DataServer 的网络被打爆,导致大量数据变更没有及时通知到 Session ,推送延迟飙升; <br><strong>-</strong> 因为数据包过大, SessionServer 与客户端之间出现了大量的 channel overflow 推送失败,推送延迟飙升; <br><strong>-</strong> 由于实例数量过多,注册中心的推送包以及内部传输的数据包过大,很容易打满单机的网络处理上限,序列化数据也会占用大量的 CPU ; <br><strong>-</strong> 由于地址列表扩大了几倍,导致对应推送接收端 MOSN 也出现了问题,大量机器出现 OOM , 出现大量 CPU 毛刺影响请求延迟; <br><strong>-</strong> 注册中心常见瞬间大量并发的请求,比如业务大规模重启,很容易导致瞬时注册中心自身处理能不足,如何进行限流,以及如何快速达到数据最终一致。</p><h2>3 优化方案</h2><p>针对上述大规模集群遇到的挑战,我们做了以下的优化方案:</p><h3>3.1 横向扩展支撑大规模集群</h3><p>在大规模集群场景下,单纯采用扩大机器规格的纵向扩展方式往往会遇到瓶颈,单机的配置是有上限的,超大的 heap gc 时也可能产生较高的暂停时间,而且恢复与备份会花费很长时间。</p><h4>3.1.1 双层数据架构进行数据分片</h4><p>双层数据架构: Session (会话层:分散链接)、Data (数据层:分散数据)来实现横线扩展的能力,通过对链接和数据进行分片,SOFARegistry 可以通过横向扩容很容易的支撑更大的集群。单机采用小规格的机器,在容灾以及恢复方面也可以取得很好的效果。 <br>SOFARegistry 的架构可以参见: <em><a href="https://link.segmentfault.com/?enc=bAsQMYwktNLRI92rvr%2BiEg%3D%3D.vU%2FyGCTsemZbxO6L9u3rgXIgYvaD158dbVvrVQrHBb%2Blpxr3Zi3kWVwGE4ZZENykWIgV3uX159cOjoyYu8Df%2FeEtGsCuRb7aD93yHN0ngnE%3D" rel="nofollow">https://www.sofastack.tech/blog/explore-sofaregistry-1-infrastructure/</a></em></p><h3>3.2 应对瞬时大量请求</h3><p>注册中心存在瞬时处理大量请求的场景,比如当在大量应用同时发生运维或者注册中心自身发生运维的时候,会有大量的注册请求发送到 Session 。 <br>同时有一些依赖注册中心的基础设施会通过变更发布数据到注册中心来通知到每一个订阅端。为了应对这种不可预见的瞬时大量请求变更,注册中心需要有一定的策略进行削峰。</p><h4>3.2.1 队列攒批处理</h4><p>贴合蚂蚁的业务,为大规模集群而生,在大数据量,高并发写入下提供稳定的推送延迟,通过添加队列并进行攒批处理,提高吞吐量,对瞬间高并发请求进行削峰。 </p><p>举例:</p><p><strong>-</strong> <strong>Session 接收到大量 Publisher ,攒批发请求到 Data</strong> <strong>[1]</strong></p><p>a.利用 BlockingQueue 存储需要发送 的请求,同时会配置最大容量防止 OOM</p><p>b.独立的 Worker 线程从 BlockingQueue 中取出多个请求,创建多个 BlockingQueue 和 Worker 提高并发度</p><p>c.按照分片规则进行分组,打包成一个请求发往不同的 DataServer</p><p><strong>-</strong> <strong>Session 接收到大量 Subscriber ,聚合去重后创建推送任务</strong> <strong><em><em>[2]</em></strong></em></p><p>a.Subscriber 存储到 Map<datainfoid, map> 的数据结构中,可以进行去重的避免短时间一个实例重复注册创建大量推送任务</p><p>b.定时从 Map 中取出 Subscribers ,进行分组创建推送任务</p><p>c.最大数据量是 Session 单机的所有 Subscriber ,容量可控</p><p><strong>-</strong> <strong>用 Map 存储 DataServer 上发生变化数据的 DataInfoId ,聚合通知 Session 进行推送[3]</strong></p><p>a.短时间 DataServer 的数据可能变化多次,比如大量 Publisher ,数据修复定时任务等等</p><p>b.对这些数据变化记录 DataInfoId , 短时间只会对 Session 通知一次变更创建推送任务</p><p>c.最大数据量是 Data 单机全部的 DataInfoId</p><p><strong>-</strong> <strong>用 Map 存储 PushTask 进行去重,避免数据连续变化触发大量推送任务[4]</strong></p><p>a.添加了 Map size 的检查,过多的推送任务会直接丢弃,防止 OOM</p><p>b.同样的推送任务高版本会替换掉低版本</p><h3>3.3 减少网络通讯开销</h3><h4>3.3.1 LocalCache</h4><p>Session 和 Data 之间会有大量的数据通讯,通过添加 LocalCache 可以在不增加代码架构复杂度的前提下大幅度提升系统的性能。 <br>对于注册中心,服务数据可以通过 dataInfoId + version 唯一标识。Session 在创建推送任务时会从 Data 拉取最新的服务数据,利用 Guava 的 <code>LoadingCache</code> ,大量推送任务被创建时,缓存的利用率会比较高,可以减少很多从 Data 拉取数据的开销。</p><p><strong>-</strong> <strong>Session 利用 LoadingCache 从 Data 拉取数据[5]</strong></p><p>a.会传入创建推送任务时的版本(一般由 Data 的变更通知带过来)对比 Cache 内的数据是否足够新;</p><p>b.如果不够新,清理缓存后利用 LoadingCache 从 Data 拉取一次数据;</p><p>c. LoadingCache 会配置 maximumWeight 防止数据过多导致 OOM 。</p><h4>3.3.2 压缩推送</h4><p>在集群规模比较大的时候,比如有一个应用发布了 100 个接口,每个接口的发布数据有 150B ,该应用有 8000 个实例,每个接口有 2w 订阅方。那么每次变更这个应用的机器造成的全量推送,每个推送包 1MB , 累积需要发出 200w 个推送包,即使 Session 可以横向扩容到 100 台, Session 单机也需要在 7 秒内发出 20GB 的流量,严重阻塞 Session 的网络队列,也会很快打爆 netty buffer ,造成大量的推送失败,这么多推送包的序列化也会耗费 Session 大量的 CPU 。 </p><p>对于 Data ,如果 Session 的数量过多,每次变更需要给每台 Session 返回大量的大数据包,也会产生大量的出口流量,影响其他请求的成功率。 </p><p>由于每个实例发布的数据的相似度很高,几乎只有 IP 不一致,所以当采用压缩推送时压缩率会非常高,能压缩到 5% 的大小以下,此时 Session 的出口流量可以大幅度降低。 <br>SOFARegistry 内部有两个地方用到了压缩,并且都有压缩缓存,可以极大的减少序列化和压缩的 CPU 开销。 <br>Session 在开启压缩缓存后,压缩在 CPU 占比获得了大幅度的降低 (9% -> 0.5%)。 <br>对于 Data 由于数据包被提前序列化+压缩进行缓存,整体性能获得了大幅度的提升,可以轻松承载 300 台以上的 Session ,支撑亿级数据量的机房。 </p><p><strong>-</strong> <strong>Session 在创建推送包的时候进行了压缩加缓存[6]</strong> <br><strong>-</strong> <strong>Data 返回服务数据给 Session 的时候进行了压缩加缓存[7]</strong></p><h3>3.4 面向错误设计</h3><p>在实际生产环境中,机器故障是很常见的事情:物理机宕机、网络故障、 OOM , 系统从设计上就需要考虑出错的场景能自动恢复。</p><h4>3.4.1 重试</h4><p>在一个分布式系统中,失败是一个很常见的现象,比如因为网络或者机器变更等问题造成请求失败,通过添加重试队列,加入次数有限的重试可以极大程度上进行容错 </p><p><strong>-</strong> <strong>Data 变更通知 Session 失败会加入重试队列最多重试 3 次[8]</strong> <br><strong>-</strong> <strong>Session 推送给 Client 失败时会加入队列最多重试 3 次[9]</strong></p><h4>3.4.2 定时任务</h4><p>然重试可以一定程度上提高成功率,但毕竟不能无限的重试。同时各个攒批操作本身也会有容量上限,瞬间大量的请求会造成任务被丢弃,因此就需要有定时任务来对因失败造成不一致的状态进行修复。 </p><p>简要介绍一下 SOFARegistry 内部相关的定时任务是如何设计,从而实现数据的最终一致性: </p><p><strong>- 增量数据同步</strong></p><p>Session 作为客户端同步写入数据的角色,可以认为他的 pub/sub 数据是最最准确的整个数据的同步过程是一个单向流,利用定时任务做到最终一致性client -> Session -> dataLeader -> dataFollower</p><p><strong>-</strong> <strong>Data 定时 (默认 6s) 与所有的 Session 对比并同步 pub 数据[10]</strong> </p><p>a.作为 Session 发送到 Data 上的 pub、unpub、clientoff 等修改数据的请求失败的兜底措施</p><p>b.同时会在 slot leader 迁移到新的 Data 上或者 slot follower 升级成 slot leader 的时候主动发起一次同步,确保 slot 数据的完整性 </p><p><strong>-</strong> <strong>Data slot follower 定时(默认 3min) 与 Data slot leader 对比并同步 pub 数据[11]</strong></p><p>更详细的分析可以参考 <em><a href="https://link.segmentfault.com/?enc=teTHR%2FnERgcvKyD8luJVZg%3D%3D.EdYccvf4U73mNeaVDvb%2Bf15IL2%2B0Iefm2ZlLbr8vb%2BtpqDcBRAQ5z4svXQg8%2BLSlugov7B6NxKLwq6b0KKVOSH28OWAYjws07MNrFRSC19uyjgC0RJGViKLYOPfeuNgmwHSpcxD6d6kkK61zPSoZ8Q%3D%3D" rel="nofollow">https://www.sofastack.tech/projects/sofa-registry/code-analyze/code-analyze-data-synchronization/</a></em> </p><p><strong>- 推送补偿</strong></p><p>由于存在各种场景导致推送失败,下面每一个场景都会导致服务数据没有正确推送到每个客户端上 </p><p>a. Session 写入到 Data 失败</p><p>b. Data 写入数据后通知 Session 失败</p><p>c. Session 因为推送任务过多导致丢弃任务</p><p>d. Session 推送客户端失败,比如客户端 fgc ,或者网络波动 </p><p><strong>-</strong> <strong>Session 定时(默认 5s)与 Data 对比推送版本触发推送任务[12]</strong></p><p>a. Session 聚合所有 Subscriber 的 lastPushVersion ,发送到 Data</p><p>b. Data 会返回最新数据的 version</p><p>c. Session 通过对比 Data 的上数据的 version 来判断是否要触发推送任务</p><h3>3.5 减少内存占用与分配</h3><h4>3.5.1 WordCache</h4><p>业务发送给注册中心的数据通常有大量的重复内容的 String ,比如接口名称,属性名称等等,这些字符串占用了注册中心很大一部分的内存空间。 <br>SOFARegistry 内会利用 WordCache 进行对这些字符串进行复用,采用 guava 的 WeakInterner 实现。通过 WordCache ,可以大大减轻常驻内存的压力。</p><pre><code>public final class WordCache {
private static final Interner < String > interners = Interners.newWeakInterner();
public static String getWordCache(String s) {
if (s == null) {
return null;
}
return interners.intern(s);
}
}
public final class PublisherUtils {
public static Publisher internPublisher(Publisher publisher) {
...
publisher.setDataId(publisher.getDataId());
...
return publisher;
}
}
public abstract class BaseInfo implements Serializable, StoreData < String > {
public void setDataId(String dataId) {
this.dataId = WordCache.getWordCache(dataId);
}
}</code></pre><h4>3.5.2 临时对象复用*</h4><p>对于高频使用场景,对象复用对内存优化是比较大的。 </p><p>举例:</p><p><strong>-</strong> 使用了 ThreadLocal 来对 StringBuilder 进行复用,对于高并发场景,能减少很多临时内存的分配; </p><p><strong>-</strong> 下面的代码中 join 重载了多份,而没有使用 <code>join(String... es)</code> 这种的写法,也是因为避免函数调用的时候需要临时分配一个 array 。</p><pre><code>public final class ThreadLocalStringBuilder {
private static final int maxBufferSize = 8192;
private static final transient ThreadLocal < StringBuilder > builder = ThreadLocal.withInitial(() - > new StringBuilder(maxBufferSize));
private ThreadLocalStringBuilder() {}
public static StringBuilder get() {
StringBuilder b = builder.get();
if (b.capacity() > maxBufferSize) {
b = new StringBuilder(maxBufferSize);
builder.set(b);
} else {
b.setLength(0);
}
return b;
}
public static String join(String e1, String e2) {
StringBuilder sb = get();
sb.append(e1).append(e2);
return sb.toString();
}
public static String join(String e1, String e2, String e3) {
StringBuilder sb = get();
sb.append(e1).append(e2).append(e3);
return sb.toString();
}
...
}</code></pre><h3>3.6 线程池死锁</h3><h4>3.6.1 独立 Bolt 线程池</h4><p>根据请求类型不同拆分线程池可以大幅度提高抗并发的能力,SOFARegistry 内分了多个独立的线程池,不同请求和事件使用同一个线程池处理,造成死锁:</p><p><strong>- Session</strong> </p><p>a. accessDataExecutor : 处理来自注册中心客户端的请求</p><p>b. dataChangeRequestExecutor :处理 data 通知变更</p><p>c. dataSlotSyncRequestExecutor : 处理 data 向 Session 发起同步的请求</p><p>...</p><p><strong>- data</strong></p><p>a. publishProcessorExecutor : 处理 Session 写数据的请求</p><p>b. getDataProcessorExecutor : 处理 Session 拉取数据的请求</p><p>...</p><h4>3.6.2 KeyedThreadPoolExecutor</h4><p>代码[13]对于一个线程池内,可以对 task 添加 key ,比如推送用的线程池,按照推送的 IP 地址作为 key , 避免对一个客户端短时间产生过多的推送。</p><pre><code>public class KeyedThreadPoolExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(KeyedThreadPoolExecutor.class);
private final AbstractWorker[] workers;
protected final String executorName;
protected final int coreBufferSize;
protected final int coreSize;
public < T extends Runnable > KeyedTask < T > execute(Object key, T runnable) {
KeyedTask task = new KeyedTask(key, runnable);
AbstractWorker w = workerOf(key);
// should not happen,
if (!w.offer(task)) {
throw new FastRejectedExecutionException(
String.format(
"%s_%d full, max=%d, now=%d", executorName, w.idx, coreBufferSize, w.size()));
}
w.workerCommitCounter.inc();
return task;
}
}</code></pre><h3>3.7 其他常见优化</h3><h4>3.7.1 倒排索引</h4><p>SOFARegistry 内对部分数据需要按某些属性进行查找,比如根据 IP 查询发布和订阅的数据,用于业务运维时的提前摘流,Session 单机往往包含了接近百万的数据量,如果每次查询都需要遍历全量数据集,在高频场景,这个开销是无法接受的。 <br>因此 SOFARegistry 内设计了一个简单高效的倒排索引来做根据 IP 查询这件事,可以提高成千上万倍的摘流性能,能够支撑上千 Pod 同时运维。</p><p>详细分析可以参考:</p><p><em><a href="https://link.segmentfault.com/?enc=8jpSObzn%2BiyfVyqlF%2BZ57w%3D%3D.viwGuwJ4TDFdDsvAnK%2F50YL1YyDIVcM%2BpPXnZhiQT24csOqjIQAYxMaGepVedvkBHQDjNcpabrSgWXdguSU2UkVDEf8MEHLrRZH%2F%2BUE3Rvgy9T4HpiDsp1sp0Iw5P%2B1oiV3wXfssiV0jM6dafPE9Bw%3D%3D" rel="nofollow">https://www.sofastack.tech/projects/sofa-registry/code-analyze/code-analyze-data-inverted-index/</a></em></p><h4>3.7.2 异步日志</h4><p>SOFARegistry 内部的日志输出量是比较大的,每一个推送变更都在各个阶段都会有日志,各个组件之间的交互也有详细明确的错误日志,用于自动化诊断系统对系统进行自愈。</p><p>异步日志输出相对同步日志会带来很大的性能提升。</p><p>SOFARegistry 是一个基于 SpringBoot 的项目,之前是采用默认的 logback 作为日志输出组件,在某次故障注入压测后,发现 logback AsyncAppender 的一个 bug[14] , 在磁盘注入故障时,logback 因为类加载失败导致异步输出线程挂掉了,在 Error 级别日志队列被打满整个进程进入卡死的状态,所有的线程全部卡在 Logger 上,所以在新版本中改成了采用 log4j2 async logger[15] 的实现。</p><h3>3.8 异常带来的额外开销</h3><h4>3.8.1 hessian 反序列化</h4><p>下图为我们在某次压测中的火焰图,发现大量的 CPU 消耗在 hessian 解析失败触发的异常上:</p><p><img src="/img/remote/1460000042736143" alt="图片" title="图片"></p><p>经排查,是我们的响应包里的 List 使用了 <code>Collections.unmodifiableList</code>, hessian 无法构造 <code>UnmodifiableList</code> 会降级到 <code>ArrayList</code> ,但降级过程会抛出异常导致耗费了大量的 CPU 。</p><h4>3.8.2 fillInStackTrace</h4><p>在某些高频调用的地方 throw Exception , Throwable 默认的 fillInStackTrace 开销很大:</p><pre><code>
public class Throwable implements Serializable {
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
}</code></pre><p>建议 override 掉 fillInStackTrace 方法,比如线程池的 RejectedExecutionException ,</p><pre><code>public class FastRejectedExecutionException extends RejectedExecutionException {
public FastRejectedExecutionException(String message) {
super(message);
}
@Override public Throwable fillInStackTrace() {
// not fill the stack trace return this;
}
}</code></pre><h3>3.9 Client 优化技巧</h3><p>大规模集群时,不光是注册中心,注册中心客户端乃至更上层的逻辑也会遇到瓶颈,蚂蚁内部主要的场景是 MOSN ,下面介绍一些在 SOFARegistry 迭代过程中 Go 语言相关的优化技巧。</p><h4>3.9.1 对象复用</h4><p><strong>- 解析 URL 参数优化</strong></p><p>SOFARPC 框架下,发布到注册中心的数据是 url 格式,MOSN 端在接收到注册中心推送的时候就需要解析 url 参数,采用 go 标准库的 <code>url.Values</code> 解析大量的 url 在 CPU 和 alloc 方面都不佳,替换成基于 sync.Pool 实现,可以进行对象复用的 <code>fasthttp.Args</code> 可以减少大量的 CPU 和 alloc 开销。</p><p><strong>- 局部 slice 内存复用</strong>go 的 slice 设计十分精巧, 通过 <code>a = a[:0]</code>可以很轻松的复用一个 slice 底层 array 的内存空间,在高频场景下,一个局部变量的复用能节省很多的内存开销:</p><p><em><a href="https://link.segmentfault.com/?enc=XmN42osgld2cXMZX70R2Nw%3D%3D.Mr4L92LI1e2b6zgF%2B6PNXYYR8zbwDIa3c2FBhJX%2B6Tw4dftU%2FdI7Pqijbtzfmd7P" rel="nofollow">https://github.com/mosn/mosn/pull/1794/files</a></em></p><h4>3.9.2 string hash</h4><p>代码中,很常见对一个 string 计算 Hash ,如果采用标准库,由于入参大多为为 <code>[]byte</code>,因此需要做 <code>[]byte(s)</code> 把 string 转化为 <code>[]byte</code>, 而这一步往往比部分 Hash 算法本身的开销还高。</p><p>可以通过开发额外的直接对 string 计算 Hash 的函数来优化,比如 fnv Hash 对应的优化库:<em><a href="https://link.segmentfault.com/?enc=GIK6pv6k8mltuXu4CZjgDw%3D%3D.zWYvaY8pFWUUtfIAk4yWDUj%2Bn%2BYFDGxfkM2cDr3o3YhFfmJBs3aW4vmXHckbc%2BUc" rel="nofollow">https://github.com/segmentio/fasthash</a></em></p><h4>3.9.3 减少字符串拼接</h4><p>在采用多个 string 共同作为 map 的 key 的时候,常见把这几个字符串拼接成一个字符串作为 key ,此时可以采用定义一个 struct 作为 key 的方式来减少临时的内存分配。</p><pre><code>key1 := s1 + s2 + s3</code></pre><pre><code>type Key struct{
s1 string
s2 string
s3 string
}</code></pre><h4>3.9.4 Bitmap</h4><p>bitmap 作为一个很常见的优化手段,在合适的场景进行使用在 CPU 以及 memory 方面都会有比较大的改善。 <br>MOSN 的代码中就有利用 bitmap 优化用于路由匹配的 subsetLoadbalancer 的案例,大大降低了注册中心推送期间 MOSN 变更的开销,详细可以看: </p><p><em><a href="https://link.segmentfault.com/?enc=4nvpxe%2Fk%2BTrTvRBlRrjWuw%3D%3D.SUkNqOmYgIyKmr0Z6bkZu%2BfC69kw7tHSleqQDBBA%2FMiTjYbfGCCem7vhOhvwY%2FKMwzrcKXdaryPXhu%2FPsugZAA%3D%3D" rel="nofollow">https://www.sofastack.tech/blog/build-subset-optimization/</a></em> <br><em><a href="https://link.segmentfault.com/?enc=R46s4RBPFHLvf6djO8rKug%3D%3D.sT0%2B7FNIWWRSKOQchd2n3cQxqPb3tWG7iAILge2OkUJzN2HIe1dIwWw5TlXmwjdP" rel="nofollow">https://github.com/mosn/mosn/pull/2010</a></em></p><h4>3.9.5 Random</h4><p>golang 标准库 <code>math/rand</code> 提供的是一个非线程安全的随机种子,为了在并发场景使用他,需要加上互斥锁,而互斥锁会带来比较大的开销。</p><p>对于随机种子安全要求不高,但性能要求比较高的场景下,有其他的两个选择: </p><p><strong>-</strong> <em><a href="https://link.segmentfault.com/?enc=vsFyGRskgKBhzwuz4y66xQ%3D%3D.P%2FhAnzYl4S9ZF2hEWR9a79zyxpphGsrpN513Gx0hVlBZtST3ue0iLOiEcCRQ9z0M" rel="nofollow">https://github.com/valyala/fastrand</a></em> </p><p>使用 sync.Pool 实现,支持并发使用无需加锁; </p><p><strong>-</strong> <em><a href="https://link.segmentfault.com/?enc=HSfwSqvWlIlRFowXT2fYcw%3D%3D.7djK%2BFlIT8MnZ4GfCO4JAChkJBWfGUrVyrW4%2Bsh1OtnGs4rHWoBu4bo9Iww4i%2B1xo1jBdk1z%2BKICTDdswnJld0WYXFFzKl02OABn4hjRs%2Fw%3D" rel="nofollow">https://github.com/golang/go/blob/master/src/runtime/stubs.go#L154</a></em> </p><p>go runtime 的非导出方法,threadlocal 的实现,直接使用 runtime 内的 m.fastrand 属性</p><p>使用 link 指令可以进行导出</p><pre><code>//go:linkname FastRandN runtime.fastrandn
func FastRandN(n uint32) uint32</code></pre><p>对比一下这 3 个 rand 的性能</p><pre><code>BenchmarkRand
BenchmarkRand/mutex_rand
BenchmarkRand/mutex_rand-12 16138432 75.3ns/op
BenchmarkRand/fast_rand
BenchmarkRand/fast_rand-12 227684223 5.32 ns/op
BenchmarkRand/runtime_rand
BenchmarkRand/runtime_rand-12 1000000000 0.561 ns/op
PASS</code></pre><p>相比标准库的 math.rand , runtime.fastrandn 如此的快,因为他直接使用了go runtime 中 m.fastrand 作为种子,没有加锁操作,是 threadlocal 的实现,对于 randn 的取模操作也进优化,改用乘加移位实现 : <em><a href="https://link.segmentfault.com/?enc=o9CF8nfjspGVDDghfygIVQ%3D%3D.EtnOT9DhfVyWFbYESyjGcGZOVfxCvbZptnfR9Q5%2F%2Fxxx%2Fdl%2BN%2Fu9ecuVT4nd7VLdrjvYD5gNuc95EC52hzDW3jHqObOC3jaSurkDKzAvoU4%3D" rel="nofollow">https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction</a></em></p><h2>4 总结与展望</h2><p>最新版本的 SOFARegistry ,通过上述优化,我们支撑起了千万级别数据量的集群的服务发现,整体资源开销相比于老版本也有了很大的下降,当然未来还有一些优化点:</p><p><strong>-</strong> 由于大量的使用了固定延迟的批处理,导致推送延迟还是偏高,推送变更延迟会有 5s 左右,而市面上常见的注册中心 watch 的延迟一般在 1s 以下,未来希望可以通过识别数据量,减少批处理的固定延迟,减少整体变更推送延迟。</p><p><strong>-</strong> 目前对于单机房注册中心的规模支撑已经完全无压力,但后续 SOFARegistry 会支持多机房数据同步的功能,这部分功能在生产落地还需要我们继续优化 SOFARegistry 的性能。</p><h2>5 相关链接</h2><p>[1]Session 接收到大量 Publisher ,攒批发请求到 Data:<br><em><a href="https://link.segmentfault.com/?enc=ISB0PNQvjqplQI1IfKo2Vw%3D%3D.w9pt%2Bf80eR730BwF5mSNYbyreY9mQNEs1lctJwU3AfLs8amdgupdvJESXvz04p9wIMlmO50a54TyTZXKArPks8KZOegQsadRAPqHSMHSdV35jwGHlrc2WU9YNM38rnZ71btX018UN8qyLYG0gr4UKhsmr34PHyPSBdhwXYFtq2bAqyOkdrjUtku%2BObX%2BogzsA3OdAztO1cejZGnHqmEf0TJyWHk7wWw2r%2Fk3U97on8fwVrN1suEwuoW013ORJmk2I0iI%2BS4wHPruzGM09mo0VQ%3D%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/node/service/DataNodeServiceImpl.java#L108</a></em></p><p>[2]Session 接收到大量 Subscriber ,聚合去重后创建推送任务:<br><em><a href="https://link.segmentfault.com/?enc=ciBMmtyleJs3QnBQrp%2BHqQ%3D%3D.dDrd%2BhL2sxyYP0eZL%2FyDev2pw0kYlLbsxjZtZL9VS7lE%2BhQ0uN7rsBfozFHcAZCeC9zgq%2BtCGcpDPOZC2PEQVgJOdl81EYIW8syogVNgOd%2FemkVVWS9RWvjyLn2piw37BmMlJTcVPbbAke8MOe3ecDYv9NelzXCKU5x4r6m0SoGW33mgoiimPqYYFlCU8hIjluY3sQj0NWe40NdO2AXyFrWCaChvNbxIkbkD2F3rYcRt34jNJXIReufBJzDpJXOq" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/push/RegProcessor.java#L54</a></em></p><p>[3]用 Map 存储 DataServer 上发生变化数据的 DataInfoId ,聚合通知 Session 进行推送<br><em><a href="https://link.segmentfault.com/?enc=m9Yh4odJscKckeH9Y%2FgHZQ%3D%3D.K7mzHcOozdVjLRWuFTQrTQ%2Fcsz6Ki%2BcQWGD7iZO6JzT%2FUfAhejD4VsL2z2jgKLjjEvO8hQpDemiwpIPkp9u7iKARDMB0%2FwIAmoTy0UgNcIRttFR82vUVGjfqWHaUuboZY%2BovFAHCd1BCfjsKnwav1AV0J3hqtrhYoZX2e0wlChQ66y9TOp%2BcoETS9PVJ9vnbvXQ5cWfqW%2FqOLifwVnVfWj6uYm3inQRh5htxjjNvccU%2BlYCkbFKWfKRBO6U%2BXGRNu%2F%2FvS1ayXxM4gjmS3HsCIw%3D%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/data/src/main/java/com/alipay/sofa/registry/server/data/change/DataChangeEventCenter.java#L112</a></em></p><p>[4]用 Map 存储 PushTask 进行去重,避免数据连续变化触发大量推送任务<br><em><a href="https://link.segmentfault.com/?enc=UBrxY8iBWIgJNehvDJwGyQ%3D%3D.%2FDyPB7LIQeo6Ev%2Fe2R5veCuTTaIgEsbIyNLpO9qZw7McDb6%2BimDL%2Fo%2BIPoq4lwM%2F8l2sq3e2CRct0Ce46Pc6B53%2BusR%2FMVDQX5GNRvvFNZeNZCOFkdCcfak78fA1vA5db1Uup4kdVMGUor4wLURpJ3e5QfJBM68zLAPXaVhLT75p9qQHXUeTTyhLuPPVk5j41iuMIDlq9hUEvP4c6q5y%2B%2FleiMpPEr1tshdCPfx1BEbEKnumPJWUtJjbEuzJmxvEkcl8hznKPd2F785Xs%2Fb4wg%3D%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/push/PushTaskBuffer.java#L51</a></em></p><p>[5]Session 利用 LoadingCache 从 Data 拉取数据<br><em><a href="https://link.segmentfault.com/?enc=yGX3W7j1p%2BVCOR7rkZd3ng%3D%3D.VQIx24usYLY%2BoKjickMIt33GbZcGnq%2BP0lbHEY3tS48mFE4G2EIv914YETa1Pfrl0it%2BMmBcrShx3E89Hg7i2Wc3D7Xhlw0DpZgCAbSgN7%2F3Iz5v2P%2BLfU%2BxddmxqKsm%2FfGCcxXgDgyWFPEi%2B2w63gaZV%2FUobQXHdhKzs3qPvFu26mJDRgf4JZ8z9d%2FcoR%2FjWgXLNCpxzzgrfKU%2F3At%2FdDTEANl2RpHeqy56tOF97PpA7TInq6zp05KgLSFfJ0ah" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/push/FirePushService.java</a></em></p><p>[6]Session 在创建推送包的时候进行了压缩加缓存<br><em><a href="https://link.segmentfault.com/?enc=zwrqnU6IFqMldxiDwGrUHw%3D%3D.S2QZyZ%2ByMvi8APDgcVyTZCLu2jrUmmT4SI11lGXisCeF1V3AcNL2%2B0L5fSjUCaTf9iVauh%2FdhBxYmsgnubXSEmPzaNOudSCidB%2FfN5gUx%2FVetVvpXi58pX35rdNBo1UfSn%2BE%2BcRFlCUvJ1Puvfh9%2BbMnpqWRPiLXxvSHdiZc3pvmz%2Bg5arBqkAGqg4DH5%2F04zwAmUZCd7AFIIcWbFoQ%2FcRKvUcjfAkt%2Fb0OEERi5hopP%2BDhy4tVpkSU4Y0%2FSyloWa9ZnYQfCibirTXTj35Yd%2Bv07RIRNrTNnxfU0OTvA5pY%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/converter/pb/ReceivedDataConvertor.java#L81</a></em></p><p>[7]Data 返回服务数据给 Session 的时候进行了压缩加缓存<br><em><a href="https://link.segmentfault.com/?enc=m2NL5Xkj7OXPv3oWQyHHQw%3D%3D.Oo1UsadN6npL3bXdW3yS7ct0vlBr4znQl4puJLTjj2yModDDYkjWJHyHHwWkmGx%2FPqKnEP0hcb3b8E0wAjOKPWLrI9ZQMEHsnFlq1yXjFMQHtDD2FKaI0EWP%2FFKKWemrbhZmba3eMLovY4GUz2iuoiR69j6tHjJZfYVuxdGGG%2Fi8%2FrM%2BPWElwNkBVjyZiOe6HXtPRZkfsBw54S%2BEXr4F0L81p%2FYZEnxXy68x7orUORASNSepmtkv3hhVSwz06ZEb" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/shared/src/main/java/com/alipay/sofa/registry/server/shared/util/DatumUtils.java#L149</a></em></p><p>[8]Data 变更通知 Session 失败会加入重试队列最多重试3次<br><em><a href="https://link.segmentfault.com/?enc=i9VvbifpElENJWoklQFTfg%3D%3D.kA4tH9I1wglN21F54qT002yJGfA1moNqG0XRLzNKVsapgEy6vOcXlUQh7zYwewlSRNKHRDF4RMxgt%2BYdAgXinEa0ungF7Au5cGgKNnzfjPO8oJY5RVKNvl18aUahrWPNd3tUTEuGqEE%2BJopc6XsgXjuU%2FqeG0gsjbMivkIFMrllSGxC%2FIQuELc43sOS3lSERrvIapr1oS4zEzPrUSjd%2FTlK8nE2gzBgFn9%2BnzAo6UgRPlggyenoKCt4qQ%2FqnnTnGLr4qaIQeKSO5vT%2Bp308ysQ%3D%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/data/src/main/java/com/alipay/sofa/registry/server/data/change/DataChangeEventCenter.java#L199</a></em></p><p>[9]Session 推送给 Client 失败时会加入队列最多重试3次<br><em><a href="https://link.segmentfault.com/?enc=pnLfB%2Bx%2BmJoaMMKViSoCtQ%3D%3D.PSCeXJ83RxRCqSjj7qhq96aQLWJboqRsfxpw%2F5HEzJz7zzPXD9z%2F1mlJWa798qSvL4%2BmKXzP7NnE%2Fc0x9TM%2B435Gnf4SN%2FuF45lf%2BguD37fsQQK%2FYtm%2BmKKYH9VRrgmEbD2%2F3ifgWvOwEKqVNqdE425Ts5PyE2ueKfD6CvtFTsoNn%2BEZScVtK1%2FvTa3m77H2AXM2Lp3VUUDjLKh2UpHaLC7SACA0oPW7bN%2B4wcriz0KO4F5DJqtX5RdJzI%2B7rHjrri8jx1PkHjMtndJBBHpJjg%3D%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/push/PushProcessor.java#L494</a></em></p><p>[10]Data 定时 (默认 6s ) 与所有的 Session 对比并同步 pub 数据<br><em><a href="https://link.segmentfault.com/?enc=1p%2BXF%2B%2FM3JBcreM8jVGejA%3D%3D.gikhTiHvz20Vs911InZ4x3QPMcLojZaqAn%2BWnlWE18OZxlpetkeFHcLFHPC0wR4arqmN4Z5%2FHscv8x0HWNj77a9KtsxTLVNavYILSX8ak6k7i93DOpkuMqSzwYMzstb11xzkrNDENEjerx5gUx6LV3rsHB16Vdv6NaV0eKCJuIurGsqR47DW%2FbXRddwDGADFtTYWuQcZ%2BueaGlYvixt2ymqeZGLf63DPkohwYPYa5uL6D4vRunnRJzXouXIWJcKF" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/data/src/main/java/com/alipay/sofa/registry/server/data/slot/SlotManagerImpl.java#L374</a></em></p><p>[11]Data slot follower 定时(默认 3min) 与 Data slot leader 对比并同步 pub 数据<br><em><a href="https://link.segmentfault.com/?enc=c%2ByZMx8z70pPVpxm7oV4hA%3D%3D.PhS8PWSikdD10HH8YHKdRrbYkB1ZTrajeyznE2noC2IjRTihFL1m7S9SZICrui5OWbQl%2BOFnOvAO9VHa2%2Bl%2FNokrSVEJQBoQBia7mPvQe6Gdyvs4gPAmAaIQXk8Ou6XtIX3OO%2Fu0L%2B%2FchzIcwUiHOp4bA8FW%2BEAgQGG8ueQUex8KKeSOq2bd6ZAlMmFqe%2B9%2Fm8YSLtd1kvibbJFkRhi7KukTfVyGm4mX1IvDIpVrsqy4j4IqykA%2Bv%2FbYM4P5kZ%2F7" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/data/src/main/java/com/alipay/sofa/registry/server/data/slot/SlotManagerImpl.java#L376</a></em></p><p>[12]Session 定时(默认 5s )与 Data 对比推送版本触发推送任务<br><em><a href="https://link.segmentfault.com/?enc=lIrWJaMTOZGD%2B%2Bp%2B%2Bgi3Fw%3D%3D.ZyNI7FIWUw0%2FzuKCLhv3XvJBqEiOjFMBJHmmsQJBuunFiM38vK3%2B0ksF1hXcNIOCtHtnIqdYom%2FfYWNyuvfRPhdCS6KDZwwvyMbpT8HmQ7jJCIe53YLp4tXnEX8tbejV5K9%2BjyXmJs4E0sg118MSrcI2bPAntWxKovHEcSXTnixFqeyPrRvPgxMm%2BV7h15onZ5eMR98DzCTsq7uLtvzYOMOioxxjuRUGW1%2Fh05f5yYqHF0Du%2F1YI%2BKYo9XVvk%2Fr2%2FSNgNtpbYVPGJ3GzymwzTg%3D%3D" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/server/session/src/main/java/com/alipay/sofa/registry/server/session/registry/SessionRegistry.java#L360</a></em></p><p>[13]代码<br><em><a href="https://link.segmentfault.com/?enc=sgelcb%2BhyXJjgVMx%2F6FsnQ%3D%3D.M7WgXIGyQRvlEGULETT9%2BHmLwes7G8G1kZsI3BFJ55slGQJjPkREs0BWYGvbrCdLlABhJFhZud%2BbdIGS8droBUcByoKHqKqJJIajPp%2B1neyd4N3NDzhhQazF3NnJuOvYfo8YMOJ1qqy%2BddiIwxaBysJ%2BRYPCD7rFY6zI7mxR%2BP%2BdyIJ6J2qiz6NAS8AsCaZXuIFF%2FgJG1CxzecBWta5W6IGmPqZj2yGltf2lUujSZ%2BBsznrLMt3VOTa0sFXC5iji" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/common/util/src/main/java/com/alipay/sofa/registry/task/KeyedThreadPoolExecutor.java#L176</a></em></p><p>[14]bug<br><em><a href="https://link.segmentfault.com/?enc=YU0ja%2FWQR1WckPFYbnn%2BKg%3D%3D.yc5A%2B7nwfFgz8mR47T9ycMHttySL5j6hM5Tb0bDtiiR1vzCb4G499XrhOfj8ELX13Tn03XMEQJGy4A6At1%2Bjbnd7%2FSrChGt9pNx%2B9LnWWLM%3D" rel="nofollow">https://jira.qos.ch/projects/LOGBACK/issues/LOGBACK-1358?filter=allopenissues</a></em></p><p>[15]log4j2 async logger<br><em><a href="https://link.segmentfault.com/?enc=5X8kdol78z3Ttwcw9snEwA%3D%3D.gpAfDJLKFrkbZKBlo7ErSlr055rgWlUlsgOiQj%2F%2BoaXsgLr8TRRt4Z53Dssw8yWVW2bDpcO97Q5xTJRwcfSt8Wmcerv2NCRFufuQUJkcJIlsG5uP5wdlBLY6O5ilGr7a9Ay%2BDTEM0YrIpR%2Bf34jYBiClrcmp7s9uiAG2ikMLNRwB5gZX7bGi2OoD64qLlh%2FW" rel="nofollow">https://github.com/sofastack/sofa-registry/blob/d9ca139595f6cc5b647f53297db7be8c14390c2b/server/distribution/all/bin/base/start_base.sh#L24</a></em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=ROwXo7AuZP%2B92f7c5HzVMQ%3D%3D.ivNiLEYZLomKvEAWFOwTqH8gmLb2uLkmK7RQEpm%2FnIvlkV%2FfQd%2BGtLDcfo4Gi%2F4QviV1gsxP%2Bf6uXYl06e0iwyEODU57lj5hsmuqAAtTrMOAbnOek5mth1Z7Chi4Q0ZlMY6icX0eoIneuEG%2Bro%2Fp%2F%2FMdIMZ02BY2HK4OvgLkL1ePgvmzrQWbD4qYN%2Facu3uhIXtbrbPXyKo76hYgRVoPIAHLVYfU%2BfGsdcBLlMEiBR3BFZ2l3Dnn54bvLuf9s48%2BmvXFCDBa%2B7MdNlxfaHwOAbM93A0GxP%2FNBAj%2BtjAK5ik%3D" rel="nofollow">SOFARegistry 源码|数据分片之核心-路由表 SlotTable 剖析</a></p><p><a href="https://link.segmentfault.com/?enc=LQVgIEgO0IpFVM8Heed0Iw%3D%3D.rNFCWPPKR4UVDt%2FSw0TQp60LzWx%2F0vWCRy%2BOafHQHTNY6U26WSTKHtF0KZ28suX7lMiAV6LBElaWm8uU6KvcEKOPEPuBNpyrfeKLB5FMrVHza4wHPDrTaRFZovEV1WVU9To1ayF3KzqIM%2FAL79j4gNmRvE8ccEySOahN34jqGTvhimnNiqLrZ9l12axOHTO4dZeUbj8nXuDubOag8I2JAIKOaB3EAtV0VbQGJbOQo7uSnWvtbURSGm5uLAf1yr6uYbAFD1wRYsGj%2FZlP37NEdQvUmm%2BVLWnxqJWFoMH8NFU%3D" rel="nofollow">探索 SOFARegistry(一)|基础架构篇</a></p><p><a href="https://link.segmentfault.com/?enc=8SZ%2FJQC6vbphQPozeU0kcw%3D%3D.2mklBtWPdkmSNc%2BfcLkX3GrlFF67drHMfqp4qCT8OLy8SilxeuKBphIOZ1Zp8CUtJ6CEsSXN3IKfRPv1dZWCOCLEF%2Fy%2B%2FmKYJGSQr%2FV3qNKUDyboH89Jmy3zKxBC4g2DqH48PUnQcw99IXsHveS%2BuE%2B5dHfYY5x%2FsDs75%2FimhPM5h9eOICB%2FHKG8Ls7CGh86JCyncEkl9%2BiUjYNIE8bD1cLRMlQVOtAY3OhQAyX2CT7G7SLYgF7zrIht5%2BY2uWBvB6ajRwzY1uiVS0qIFUddjDgNdUnHitYXSJtVkTZOwTY%3D" rel="nofollow">SOFARegistry 源码|数据同步模块解析</a></p><p><a href="https://link.segmentfault.com/?enc=PW883UHlsvuhG7MinBsnjg%3D%3D.ZPpUdf6qdjCQdBK8WVZUJueukVgfz9FinDgftYY0QgJHNYsMGaEPBkk5brsxg%2FSHJ3QSdu5iEsSlYPVPmfthq8RAUkEVam6jxEy6ApiEGVGRxijcp3eRev3Ll78fBB7M2tBbr69NfKcsNdZMeWhieBf6X3mYnW5iB60bcRpmlGn%2BnDyk1jaVhxpjWviJyBd8x5XgkxNjutOlA8X4va3urV9fJtdypWvcnZ2jhH7RYcenx3n14ws7CKTNTYIrGjqgfDXIA9nNFQdbHaQUsVmmuEgeMlJMqAK%2BSZILsUkhbfA%3D" rel="nofollow">直播预告 | SOFAChannel#30《Nydus 开源容器镜像加速服务的演进与未来》</a></p>
cgo 机制 - 从 c 调用 go
https://segmentfault.com/a/1190000042579550
2022-10-08T11:43:34+08:00
2022-10-08T11:43:34+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042579554" alt="图片" title="图片"></p><p>文|朱德江(GitHub ID:doujiang24)</p><p>MOSN 项目核心开发者蚂蚁集团技术专家</p><p><img src="/img/remote/1460000042579555" alt="图片" title="图片"></p><p><em>专注于云原生网关研发的相关工作</em></p><p><strong>本文</strong> <strong>4656</strong> <strong>字 阅读 12</strong> <strong>分钟</strong></p><h2>一、前言</h2><p>去年刚学 go 语言的时候,写了这篇 cgo 实现机制[1] ,介绍了 cgo 的基本情况。主要介绍的是 <code>go=>c</code> 这个调用方式,属于比较浅的层次。随着了解的深入,发现 <code>c=>go</code> 的复杂度又高了一级,所以有了这篇文章。</p><h2>二、两个方向</h2><p>首先,cgo 包含了两个方向, <code>c=>go</code> ,<code>go=>c</code> 。</p><p>相对来说,<code>go=>c</code> 是更简单的,是在 go runtime 创建的线程中,调用执行 c 函数。对 go 调度器而言,调用 c 函数,就相当于系统调用。执行环境还是在本线程,只是调用栈有切换,还多了一个函数调用的 ABI 对齐,对于 go runtime 依赖的 GMP 环境,都是现有的,并没有太大的区别。</p><p>而 <code>c=>go</code> 则复杂很多,是在一个 c 宿主创建的线程上,调用执行 go 函数。这意味着,需要在 c 线程中,准备好 go runtime 所需要的 GMP 环境,才能运行 go 函数。以及,go 和 c 对于线程掌控的不同,主要是信号这块。所以,复杂度又高了一级。</p><h2>三、GMP 从哪里来</h2><p>首先简单解释一下,为什么需要 <code>GMP</code> ,因为在 go 函数运行的时候,总是假设是运行在一个 goroutine 环境中,以及绑定有对应的 <code>M</code> 和 <code>P</code> 。比如,要申请内存的时候,则会先从 P 这一层 cache 的 span 中的获取,如果这些没有的话,go runtime 就没法运行了。</p><p>虽然 <code>M</code> 是线程,但是具体实现上,其实就是一个 <code>M</code> 的数据结构来表示,对于 c 创建的协程,获取的是 <code>extra M</code> ,也就是单独的表示线程的 <code>M</code> 数据结构。</p><p>简单来说,c 线程需要获取的 <code>GMP</code> ,就是三个数据对象。在具体的实现过程中,是分为两步来的:</p><p>1. <code>needm</code> 获取一个 extra M</p><p>开启了 cgo 的情况下,go runtime 会预先创建好额外的 <code>M</code> ,同时还会创建一个 goroutine,跟这个 <code>M</code> 绑定。所以,获取到 M,也就同时得到了 G。</p><p>而且,go runtime 对于 M 并没有限制,可以认为是无限的,也就不存在获取不到 M 的情况。</p><p>2.<code>exitsyscall</code> 获取 <code>P</code></p><p>是的,这个就是 <code>go=>c</code> 的反向过程。只是 <code>P</code> 资源是有限的,可能会出现抢不到 P 的情况,此时就得看调度机制了。</p><h2>四、调度机制</h2><p>简单情况下,<code>M</code> 和 <code>P</code> 资源都顺利拿到了,这个 c 线程,就可以在 M 绑定的 goroutine 中运行指定的 go 函数了。更进一步,如果 go 函数很简单,只是简单的做点纯 CPU 计算就结束了,那么这期间则不依赖 go 的调度了。</p><p>有两种情况,会发生调度:</p><h3>1. exitsyscall 获取不到 P</h3><p>此时没法继续执行了,只能:</p><p>1.将当前 extra M 上绑定的 g ,放入全局 g 等待队列</p><p>2.将当前 c 线程挂起,等待 g 被唤起执行</p><p>在 g 被唤起执行的时候,因为 g 和 M 是绑定关系:</p><p>1.执行 g 的那个线程,会挂起,让出 P ,唤起等待的 c 线程</p><p>2.c 线程被唤起之后,拿到 P 继续执行</p><h3>2. go 函数执行过程中发生了协程挂起</h3><p>比如,go 函数中发起了网络调用,需要等待网络响应,按照之前介绍的文章,Goroutine 调度 - 网络调用[2] 。当前 g 会挂起,唤醒下一个 g ,继续执行。</p><p>但是,因为 M 和 g 是绑定关系,此时会:</p><p>1. g 放入等待队列</p><p>2.当前 c 线程被挂起,等待 g 被唤醒</p><p>3. P 被释放</p><p>在 g 被唤醒的时候,此时肯定不是在原来的 c 线程上了</p><p>1.当前线程挂起,让出 P,唤醒等待的 c 线程</p><p>2.c 线程被唤醒后,拿到 P,继续执行</p><p>直观来说,也就是在 c 线程上执行的 goroutine,并不像普通的 go 线程一样,参与 go runtime 的调度。对于 go runtime 而言,协程中的网络任务,还是以非阻塞的方式在执行,只是对于 c 线程而言,则完全是以阻塞的方式来执行了。</p><p>为什么需要这样,还是因为线程的调用栈,只有一个,没有办法并发,需要把线程挂起,保护好调用栈。</p><p>PS:这里的执行流程,其实跟上面抢不到 P 的流程,很类似,底层也是同一套函数在跑(核心还是 <code>schedule</code>)。</p><h2>五、信号处理</h2><p>另外一大差异是,信号处理。</p><ol><li>c 语言世界里,把信号处理的权利/责任,完全交给用户了。</li><li>go 语言,则在 runtime 做了一层处理。</li></ol><p>比如,一个具体的问题,当程序运行过程中,发生了 segfault 信号,此时是应该由 go 来处理,还是 c 来响应信号呢?</p><p>答案是,看发生 segfault 时的上下文:</p><p>1.如果正在运行 go 代码,则交给 go runtime 来处理</p><p>2.如果正在运行 c 代码,则还是 c 来响应</p><p>那具体是怎么实现的呢?信号处理还是比较复杂的,有比较多的细节,这里我们只介绍几个核心点。</p><h3>1. sighandler 注册</h3><p>首先,对于操作系统而言,同一个信号,只能有一个 handler 。再看 go 和 c 发生 sighandler 注册的时机:</p><ol><li>go 编译产生的 so 文件,被加载的时候,会注册 sighandler(仅针对 go 需要用的信号),并且会把原始的 sighandler 保存下来。</li><li>c 可以在任意的时间,注册 sighandler,可以是任意的信号。</li></ol><p>所以,推荐的做法是,在加载 go so 之前,c 先完成信号注册,在 go so 加载之后,不要再注册 sighandler 了,避免覆盖 go 注册 sighandler。</p><h3>2.信号处理</h3><p>对于最简单的情况,如果一个信号,只有 c 注册了 sighandler,那么还是按照常规 c 信号处理的方式来。</p><p>对于 sigfault 这种,go 也注册了 sighandler 的信号,按照这个流程来:</p><p>1.操作系统触发信号时,会调用 go 注册的 sighandler(最佳实践中,go 的信号注册在后面);</p><p>2.go sighandler 先判断是否在 c 上下文中(简单的理解,也就是没有 g,实际上还是挺复杂的);</p><p>3.如果,在 c 上下文中,会调用之前保存的原始 sighandler(没有原始的 sighandler,则会临时恢复 signal 配置,重新触发信号);</p><p>4.如果,在 go 上下文中,则会执行普通的信号处理流程。</p><p>其中,2 和 3 是最复杂的,因为 cgo 包含了两个方向,以及信号还有 sigmask 等等额外的因素,所以这里细节是非常多的,不过思路方向还是比较清晰的。</p><h2>六、优化</h2><p>上篇 cgo 实现机制[1] ,提过优化一些思路,不过主要针对 <code>go => c</code> 这个方向。因为 <code>c => go</code> 的场景中,还有其他更重要的优化点。</p><h3>1.复用 extra M</h3><p>通常情况下,最大的性能消耗点在获取/释放 <code>M</code>。</p><p>1.上面提到,从 c 进入 go,需要通过 <code>needm</code> 来获取 <code>M</code> 。这期间有 5 个信号相关的系统调用。比如:避免死锁用的,临时屏蔽所有信号,以及开启 go 所需要的信号。</p><p>2.从 go 返回 c 的时候,通过 <code>dropm</code> 来释放 <code>M</code> 。这期间有 3 个信号相关的系统调用。目的是恢复到 <code>needm</code> 之前的信号状态(因 needm 强制开启了 go 必须的信号)。</p><p>这两个操作,在 MOSN 新的 MOE 架构的测试中,可以看到约占整体 2~5% 的 CPU 占用,还是比较可观的。</p><p>了解了瓶颈之后,也就成功了一半。</p><p>优化思路也很直观,第一次从 go 返回 c 的时候,不释放 <code>extra M</code> ,继续留着使用,下一次从 c 进入 go 也就不需要再获取 <code>extra M</code> 了。因为 <code>extra M</code> 资源是无限的,c 线程一直占用一个 <code>extra M</code> 也无所谓。</p><p>不过,在 c 线程退出的时候,还是需要释放 <code>extra M</code> ,避免泄漏。所以,这个优化,在 windows 就不能启用了,因为 windows 的 pthread API 没有线程退出的 callback 机制。</p><p>目前实现了一版在 CL 392854[3] 。虽然通过了一个大佬的初步 review,以及跑通了全部测试,不过,估计要合并还要很久...因为这个 PR 已经比较大了,被标记 L size 了,这种 CL 估计大佬们 review 起来也头大...</p><p>在简单场景的测试中,单次 c => go 的调用,从 <code>~1600ns</code> 优化到了 <code>~140ns</code>,提升 10 倍,达到了接近 go => c 的水平( <code>~80ns</code> )效果还是挺明显的。</p><p>实现上主要有两个较复杂的点:</p><p>1.接收到信号时,判断在哪个上下文里,以及是否应该转发给 c。因为 cgo 有两个方向,而且这两个方向又是可以在一个调用栈中同时发生的,以及信号还有 <code>mask</code> ,系统默认 handler 之分。这里面已经不是简单的状态机可以描述的,go runtime 在这块有约 100 + 行的核心判断代码,以应对各式各样的用法。估计没几个人可以全部记住,只有碰到具体场景临时去分析。或者在跑测试用例失败的时候,才具体去分析。</p><p>2.在 c 线程退出,callback 到 go 的时候,涉及到 c 和 go function call ABI 对齐。这里主要的复杂度在于,需要处理好不同的 CPU 体系结构,以及操作系统上的差异。所以工作量还是比较大的。比如 arm ,arm64 , 期间有一个有意思的坑,Aarch64 的 stack pointer 必须是 16 byte 对齐的,否则会触发 bus error 信号。(也因此 arm64 的压栈/出栈指令,都是两个两个操作的)</p><h3>2.获取不到 P</h3><p>从 c 进入 go,获取 GMP 的过程中,只有 <code>P</code> 资源是受限的,在负载较高时,获取不到 <code>P</code> 也是比较容易碰到的。</p><p>当获取不到 <code>P</code> 时,c 线程会挂起,等待进入全局队列的 <code>g</code> 被唤醒。这个过程对于 go runtime 而言是比较合理的,但是对于 c 线程则比较危险,尤其当 c 线程中跑的是多路复用的逻辑,则影响更大了。</p><p>此时有两个优化思路:</p><p>1.类似 <code>extra M</code> ,再给 c 线程绑一个 <code>extra P</code> ,或者预先绑定一个 <code>P</code> 。这样 c 线程就不需要被挂起了。这个思路,最大的挑战在于 <code>extra P</code> ,是不受常规 <code>P</code> 数量的限制,对于 go 中 <code>P</code> 的定义,是一个不小的挑战。</p><p>2.将 <code>g</code> 不放入全局队列,改为放到优先级更高的 <code>P.runnext</code> ,这样 g 可以被快速的调度到,c 线程可以等待的时间更短了。这个思路,最大的挑战则在于,对这个 <code>g</code> 加了优先级的判断,或许有一点有悖于 g 应该是平等的原则。不过应该也还好, <code>P.runnext</code> 本来也是为了应对某些需要优先的场景的,这里只是多了一个场景。</p><p>这个优化方向,还没有 CL,不过我们有同学在搞了。</p><h3>3.尽快释放 P</h3><p>当从 go 返回 c 的时候,会调用 <code>entersyscall</code> ,具体是,<code>M</code> 和 <code>P</code> 并没有完全解除绑定,而是让 <code>P</code> 进入 <code>syscall</code> 的状态。</p><p>接下来,会有两种情况:</p><p>1.很快又有了下一个 c=>go 调用,则直接用这个 P ;</p><p>2.sysmon 会强制解除绑定。对于进入 <code>syscall</code> 的 P ,sysmon 会等 20 us => 10 ms,然后将 P 抢走释放掉。等待时间跨度还是挺大的,具体多久就看命了,主要看 <code>sysmon</code> 是否之前已经长时间空闲了。</p><p>对于 go => c 这方向,一个 syscall 的等待时间,通常是比较小的,所以这套机制是合适的。但是对于 c => go 这个方向,这种伪 syscall 的等待时间,取决于两个 c => go 调用的间隔时间,其实不太有规律的。所以,可能会造成 <code>P</code> 资源被浪费 20us => 10ms。</p><p>所以,又有一个优化方向,两个思路:</p><p>1.从 go 返回 c 的时候,立即释放 <code>P</code> ,这样不会浪费 <code>P</code> 资源。</p><p>2.调整下 sysmon,针对这种场景,有一种机制,能尽量在 20 us 就把 P 抢走。</p><p>其中,思路 1 ,这个 CL 411034 里顺便实现了。这个本来是为了修复 go trace 在 cgo 场景下不能用的 bug ,改到这个点,是因为跟 Michael 大佬讨论,引发的一个改动(一开始还没有意识到是一个优化)。</p><h2>七、总结</h2><p>不知道看到这里,你是否一样觉得,c => go 比 go => c 的复杂度又高了一级。反正我是有的。</p><p>首先,c 线程得拿到 GMP 才能运行 go 函数,然后,c 线程上的 g 发生了协程调度事件的时候,调度策略又跟普通的 go 线程不一样。另外一个大坑则是信号处理,在 go runtime 接管了 sighandler 之后,我们还需要让 c 线程之前注册的 sighandler 一样有效,使 c 线程感觉不到被 go runtime 接管了一道。</p><p>优化这块,相对来说,比较好理解一些,主要是涉及到 go 目前的实现方式,并没有太多底层原理上的改进。复用 extra M 属于降低 CPU 开销;P 相关的获取和释放,则更多涉及到延时类的优化(如果搞了 extra P,则也会有 CPU 的优化效果)。</p><h2>八、最后</h2><p>最后吐个槽,其实目前的实现方案中,从 c 调用 go 的场景,go runtime 的调度策略,更多是考虑 go 这一侧,比如 goroutine 和 P 不能被阻塞。但是,对 c 线程其实是很不友好的,只要涉及到等待,就会把 c 线程挂起...</p><p>因为 go 的并发模型中,线程挂起通常是可以接受的,但是对于宿主 c 线程而言,有时候被阻塞挂起则是很敏感的。比如,在 MOSN 的 MOE 架构中,对于这类可能导致 c 线程被挂起的行为,需要很小心的处理。</p><p>那有没有办法改变,也是有的,只是改动相对要大一点,大体思路是,将 c 调用 go 的 API 异步化:</p><pre><code>g = GoFunc(a, b)
printf("g.status: %d, g.result: %d\n", g.status, g.result)</code></pre><p>意思是,调用 Go 函数,不再同步返回函数返回值,而是返回一个带状态 <code>g</code>,这样的好处是,因为 API 异步了,所以执行的时候,也不必同步等待 g 返回了。如果碰到 g 被挂起了,直接返回 <code>status = yield</code> 的 g 即可,goroutine 协程继续走 go runtime 的调度,c 线程也不必挂起等待了。</p><p>这样的设计,对于 c 线程是最友好的,当然也还得有一些配套的改动,比如缺少 P 的时候,得有个 <code>extra P</code> 更好一些,等其他的细节。</p><p>不过,这样子的改动还是比较大的,让 go 官方接受这种设计,应该还是比较难的,以后没准可以试试,万一接受了呢~</p><h2>九、相关链接</h2><p>[1] cgo 实现机制:<em><a href="https://link.segmentfault.com/?enc=zZCw3YTMUt2QNHcph7r2Sw%3D%3D.cpEujp9IIhdBuzRPK1DVt2x5QxjkIhMq%2B7OLLq%2BPqq%2FE0xZPbDIuRaIHi64pVq%2F5" rel="nofollow">https://uncledou.site/2021/go...</a></em></p><p>[2] Goroutine 调度 - 网络调用:<em><a href="https://link.segmentfault.com/?enc=wFf6rW89xTTBIyPqjU8v4g%3D%3D.lu9bAmPw8AKrCmy7NxsZ%2BkaxYH0ljRgTpuuApmYpA7IO7CzgXoW1jLs65v1Qj8Z%2BKs8GW4aw1J2YO%2F5PvNmdhA%3D%3D" rel="nofollow">https://uncledou.site/2021/go...</a></em></p><p>[3] CL 392854 :<em><a href="https://link.segmentfault.com/?enc=fH7c8wpMF6TgATCsXJlhKA%3D%3D.1eG4MCJ1zp6REQQ7FcmlIgy2zCoC7yz%2BK04gmL7Z3jO9BP8I3TGjbl73KxC%2BrjCrkWuaRltuNSjCSX0KHSQaaQ%3D%3D" rel="nofollow">https://go-review.googlesourc...</a></em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=1ZYA4mETXHC33Zs9nIGwhQ%3D%3D.SJeky1QBN5f9PdC3%2BoGm4AK0So8UkLXPVTvI2ONG6w5Ly9wtT1QlPoY5R29mz9SPANFXnbniH2erqBVkB2jelZ8dql8goYbXGFY3sPq1uFV6AF9x0MUkWCVDATnFE15Lka2pWkaUFgc8jfxkjGRi33u9d7mJNR5hO3gmSXtT8yMY50y4cx0j6%2B%2BCvUwVGuFjxICK3PTCHJdhDivkybmYThNu7BNlufNmsBDzC6GmpbQdj%2FCN%2FpjhFSPnNP%2B7RyIkvwzq157mrUhIE56qoywKpI1QRiDVUZ3ntM8tMbM6LPE%3D" rel="nofollow">MOSN 反向通道详解</a></p><p><a href="https://link.segmentfault.com/?enc=%2FzKKPw92WVPUpkV4g6QUnQ%3D%3D.ZyuW0tNW3vi3Gtma9pBYh4MP%2FrQnIr7pCi503JReUmsOZRZkbg%2F0RoPp1W9EZHnSLKDAaQHxbSfLRaTH%2BzsadPZrA%2BwAbp%2FKSwbh%2BeVqOph8ABlpFGUk5hia88SFckKWdLbdlgiU7tnkBRLPVKqd8OwdwQzW%2F6VBwiaHjZANKIWrdRbks4L7Bmg%2BYR2GzY4iZBdkGS8Dtt4sP6cb7efM802SRF7EhLKdXGWUIjh0EPT%2Fw9DSt7Qp%2BTETqC2gwfG0hKxn9v35HjNAkS%2FCEOwkOD7X7c0txvysUNmJGWjTU6U%3D" rel="nofollow">Go 原生插件使用问题全解析</a></p><p><a href="https://link.segmentfault.com/?enc=TRxasRO5eQFvnOn%2FYhHD3A%3D%3D.NszoB1d7KSLwMw9DuoA8K%2BBOBK5uF2uOSZNfKDyqW61QgYfdpxsMU9x8QPKMH2w4gG5MJFh5yulmbgIu2dMhZYilMcR6%2FY6%2FpUZQO8IAxTKX2HDOZd%2F1wB6Oq9AjOfKWlPDurgZ1Nb4QnX7AGCf2IOfTzIi6co8wz9U%2B9lET%2FWpt30QEfWgonGnuQycZrZEajxw9khsukOVnexjPguwfILVS4eGlLX%2Fic2Fa5aclj8P5O1hgcLT%2FiRs%2Bj5Nu8%2FZV4sibn8FE0BEu%2FQ9CMNBx7cTIl5ijRN0j%2FP2OQs2wzj8%3D" rel="nofollow">Go 内存泄漏,pprof 够用了么?</a></p><p><a href="https://link.segmentfault.com/?enc=iuI9%2BQtc01%2BQHWsgHAKNwQ%3D%3D.szC7fW%2F1%2FYPEiHnWnJRxWOwHpQSjFIOvUdVUBkV7VFXiR1FOIILCTL9I1GhEcBZZPXjejU8FOTs8YChsuQQ2xoH5OKUgIS9yua0ZyuU9fabD1g%2F94L61iXlBUnumN52FhxBV3fwN9HmFRJAxvt9MHWIphaNLNARgx%2Bfh8Em82irQNzBFgfv5kFqSf0ZHJaxuX9Lz0QNWuoEO3Y0FRKRJDglexj%2Bg99ErqEyPOG2xjgVZvtJC2G2VvmdQ1xa0sulVhmc5hr9F7FCns%2F7%2B9bdFxK2%2Ftz0vEioo7KaWhF%2B9EYc%3D" rel="nofollow">从规模化平台工程实践,我们学到了什么?</a></p>
从规模化平台工程实践,我们学到了什么?
https://segmentfault.com/a/1190000042552882
2022-09-27T18:38:32+08:00
2022-09-27T18:38:32+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042552884" alt="图片" title="图片"></p><p>文|朵晓东(花名:奕杉 )</p><p>KusionStack 负责人、蚂蚁集团资深技术专家</p><p>在基础设施技术领域深耕,专注云原生网络、运维及编程语言等技术工作</p><h2>一、摘要</h2><p>本文尝试从平台工程、专用语言、分治、建模、自动化和协同文化等几个角度阐述规模化平台工程实践中的挑战和最佳实践。希望通过把我们平台工程的理念和实践分享给更多企业和团队,一起让一些有意思的变化发生。<br>本文基于 KusionStack[1] 技术栈在蚂蚁平台工程及自动化中的实践总结而成。</p><h2>二、平台工程:让企业级 DevOps 发生</h2><p>DevOps 理念在 10 多年前被提出,从 KVM 到容器再到云原生时代,大量企业投入 DevOps 运动以期望解决内部规模化运维效率和平台建设效率的困境。其中大部分陷入过某种基于对 DevOps 朴素认知的 Anti-Pattern ,同时也有部分公司探索出自己的路径。我经历过如下图简示的 Anti-Patterns ,Dev 与 Ops 团队各行其是,或者简单的强制 Dev 团队独立完成 Ops 工作。在 DevOps Anti-Types[2] 中可以找到更多更典型分类。</p><p>企业内规模化 DevOps 难以推行的原因多种多样,特别是在<strong>企业内自持基础设施</strong>、<strong>同时采用云上技术平台</strong>的公司阻力最大。其中以这几种情况尤为常见:</p><p><strong>-</strong> 研发团队和运维团队由于部门墙、领导者缺少洞察等等原因各自为政,难以达成一致意见;</p><p><strong>-</strong> 研发团队低估了基础设施技术、运维、稳定性工作的专业性、复杂性和快速变化,以朴素的 DevOps 理解强制应用研发者成为专家;</p><p><strong>-</strong> 领导者建立了专职的 DevOps 团队,但沦为中间的执行者,没能让 Dev 和 Ops 团队各自向前一步,紧密协同;<br><strong>-</strong> 平台研发团队对规模化带来的业务复杂性以及技术演进带来的技术复杂性应对不足,无法对应用研发者提供有效的技术支撑;</p><p>不同于面向云上托管基础设施服务和 DevOps-as-a-Service 产品工作的小型团队,中大型企业往往需要根据自身团队架构和文化建立适当的 DevOps 体系。从成功案例看,无论是 Meta 公司由 Dev 完全承担 Ops 职能,还是 Google 公司引入 SRE 团队作为中间层,<strong>平台工程</strong> <em>(</em> <em>Platform Engineering</em> <em>[3] )</em> <strong>都扮演了非常重要的角色</strong>。</p><p>平台工程旨在帮助企业构建面向应用研发者的自服务运维体系,尝试通过工程化的技术手段和工作流程解决以下关键问题:</p><p><strong>-</strong> 设计合理的抽象层次,帮助应用研发者降低对 Infra 、platform 等技术以及运维、稳定性工作的认知负担;</p><p><strong>-</strong> 为应用研发者提供统一的工作界面和空间,避免用户陷入割裂的平台产品界面、复杂的工作流中;</p><p><strong>-</strong> 帮助研发者通过有效的工作流程和推荐路径基于 内部工程平台[4] 快速开展工作;</p><p><strong>-</strong> 帮助研发者通过配套的 CI 、CD 、CDRA 等产品自服务管理应用生命周期;</p><p><strong>-</strong> 帮助平台产品研发团队简单、高效、一致的开放其平台基础能力;</p><p><strong>-</strong> 通过培训、布道、运营等手段营造协同工作和分享的文化。</p><p><strong>事实上,不是所有人都应该或者能够成为这个领域的专家,这非常困难!</strong></p><p>实际上平台技术团队的专家通常也仅擅长自己的专业领域而已,特别是在云原生理念和技术广泛应用的今天,面向大量高度开放、可配置的平台技术带来的成百上千的应用配置,PaaS 领域的业务复杂性,以及高稳定性和统一治理的要求,而平台工程的目的正是为了让应用研发者尽可能简单无痛的参与到这样规模化的 DevOps 工作中。</p><p>在蚂蚁的实践中,我们更趋向于以下这种合作状态,在团队架构和工作模式上更靠近 Google 的最佳实践。平台研发者及 SRE 成为 “ Enabler ” 支持应用研发者自服务的完成研发及交付运维,同时应用研发者使其应用可交付运维的工作结果也成为运维人员可以接手应用运维工作的基础。最终 SRE 、应用研发及运维人员把工作过程中的问题和痛点反馈给平台研发者形成正向循环。</p><p><img src="/img/remote/1460000042552885" alt="图片" title="图片"></p><h2>三、专用语言:工程化方式的一极</h2><p>有什么比一种专用语言更适合开放的、自服务的、面向领域业务的问题定义,同时需要满足自动化、低安全风险、低噪音、易治理的企业内部要求吗?正如记录音乐有五线谱,存储时间序列数据有时序数据库一样,在平台工程的特定问题域内,一批配置和策略语言用于编写和管理规模化复杂配置及策略。</p><p>不同于混合编写范式、混合工程能力的高级通用语言,这类专用语言的核心逻辑是以收敛的有限的语法、语义集合解决领域问题近乎无限的变化和复杂性,将规模化复杂配置和策略编写思路和方式沉淀到语言特性中。</p><p>在蚂蚁的平台工程实践中,我们强化了客户端的工作方式,将围绕应用运维生命周期的模型、编排、约束和策略稳定、可扩展的通过专用语言 KCL[5] 编写维护在共享仓库 Konfig[6] 中。</p><p>KCL 是一种面向有编程能力的应用研发者的<strong>静态强类型语言</strong>,提供现代高级语言的编写体验和围绕领域目的有限功能。在平台工程实践中 KCL 不是一种仅用于编写 K-V 对的语言,而是一种面向平台工程领域的专用语言。应用研发者、 SRE 、平台研发者面向 Konfig 协同研发,通过 KCL 原生功能编写应用配置,以及在 PaaS 领域更为高频和复杂的 模型抽象[7] 、 功能函数[8] 和 约束规则[9] ,即编写稳定、可扩展的业务模型、业务逻辑、防错约束和环境规则。</p><p>Konfig 仓库则成为统一的编程界面,工作空间和业务层载体,而基于 KCL 的安全、低噪音、低副作用、一致的编写范式更有利于长期管理和治理。</p><p><img src="/img/remote/1460000042552886" alt="图片" title="图片"></p><h2>四、分治:解构规模化问题</h2><p>分治思路是解决规模化问题的钥匙,从 MapReduce 到 Kubernetes 无不体现其功效。在规模化交付运维领域,经典运维平台试图在统一的黑盒平台产品中,以内置的统一模型、编排、 provision 技术来应对全量业务场景。这样的实践可以<strong>快速启动</strong>,<strong>在小范围内奏效</strong>,但随着不同业务主体采用率提升引入差异化需求,同时随着持续变化的平台技术逐渐进入疲态。</p><p><img src="/img/remote/1460000042552887" alt="图片" title="图片"></p><p>在蚂蚁的实践中,Konfig monorepo 是内部工程平台向研发者开放的编程界面和工作空间,帮助应用研发者以统一的编程界面编写围绕应用运维生命周期的配置和策略,从而编排和使用存量和新增的平台基础设施,按需创建管理云原生环境以及基于 RBAC 的权限,并通过 GitOps 方式管理交付过程。</p><p>Konfig monorepo 为不同场景、项目、应用提供了独立的白盒的编程空间,其内生的扩展性来源于:</p><p><strong>-</strong> 灵活、可扩展、独立的客户端的 工程结构设计[10] ;</p><p><strong>-</strong> 独立配置块自动合并技术[11] 支持任意分块、可扩展的配置块组织;</p><p><strong>-</strong> 静态类型系统[12] 技术提供现代编程语言可复用、可扩展的类型化建模和约束功能;</p><p><strong>-</strong> 项目粒度的 GitOps CI 工作流程定义支持;</p><p><strong>-</strong> 基于 Kusion[13] 引擎的 provision 技术选择。</p><p>Konfig monorepo 提供了分治的、可组合的工程结构设计、代码组织、建模方式、工作流程定义和 provision 技术选择支持,同时又以一致的研发模式和工作流承载了可扩展的业务需求。这样客户端的工作方式在保证灵活性、可扩展性、可移植性的同时也降低了对服务端<strong>扩展机制</strong>,如 Kubernetes API Machinery ,持续增长的压力。</p><p>下图示意了一种 Konfig monorepo 中 GitOps 方式的典型的自动化工作流程,从面向应用的代码变更开始,通过可配置的 CI 、CD 过程触达运行时,这样的机制相比中心化的黑盒产品方式更加开放、可定制、可扩展,也免去了针对不同业务场景、不同项目、应用设计笨拙的配置文件管理 portal 的必要。</p><p><img src="/img/remote/1460000042552888" alt="图片" title="图片"></p><h2>五、建模:边际收益和长尾问题</h2><p>有了分治的白盒化的工程结构设计、代码组织方式、建模方式、工作流程定义和 provision 技术选择,以怎么的策略面向平台 API 工作是另一个需要考虑的问题。在企业内典型的争议在于直面平台细节还是设计一种抽象,或者上升到显式 <em>(explicit)</em> 和隐式 <em>(implict)</em> 的理念的争议。</p><p>抽象的隐式的方式是运维平台工程师们面向非专家型终端用户的普遍选择,他们希望能设计出易于理解、便于使用的应用模型或 Spec 抽象,与具体的平台技术细节隔离,降低用户认知负担,并通过降低细节感知防错。但大部分运维平台的研发者倾向于设计一种强大的、统一的应用模型或 Spec 抽象,在实践中往往会遇到这些阻碍:</p><p><strong>-</strong> 随着企业内不同业务主体采用率的提升,统一建模难以落地。在蚂蚁内部最典型的案例是 Infra 基础技术类组件和 SaaS 应用间的巨大差异性,SaaS 应用便于统一,Infra 应用往往需要单独设计。</p><p><strong>-</strong> 面向企业内大量的平台技术,统一模型自身难以稳定,特别是应对持续变化的业务需求和平台技术驱动的需求增长。在蚂蚁的实践中,交付运维受多种因素影响有较强的不稳定性,同时围绕应用的 deliverable 、runtime 、security 、instrumentation 的业务需求也在增长。以 instrumentation 为例,近两年对应用运行时可观察性、SLO 定义的需求快速增长直接驱动了终端用户使用的变化。</p><p><strong>-</strong> 抽象模型的共性问题是需要面向用户设计出合理的模型,面向平台 API 细节保持同步。</p><p>在蚂蚁的实践中,面向终端用户即应用研发者我们采用了抽象模型的方式,通过如下思路解决几个关键问题:</p><p><strong>-</strong> 面向典型应用场景 <em>(如蚂蚁的 SOFA 应用)</em> 建模,这些模型由平台研发者、平台 SRE 主导开发,与应用研发者共同维护,达到用户体验、成本和标准兼容的平衡,在蚂蚁的实践中抽象模型的信息熵收敛比约为 1:5 ,通过广泛的高频使用保证建模投入的边际收益。</p><p><strong>-</strong> 对于非典型用户场景或应用,由平台研发者、平台 SRE 支持应用研发者完成针对应用的模型设计。KCL schema[7] 和 mixin[14] 等机制帮助用户建模、抽象、继承、组合、复用,减少重复代码,事实上这样的建模设计工作也是应用 PaaS 领域的重点之一,但对于这样的场景我们需要更合理的分工。最终大量 “非标” 平台技术在蚂蚁内部首次以一致的方式被纳管,有效解决了长尾问题。在典型协同模式下,平台研发者、平台 SRE 编写平台能力基础组件成为 “ Enabler ”,帮助应用研发者使用平台能力基础组件快速“搭积木”,完成其应用模型的研发工作。</p><p><strong>-</strong> 面向平台技术,我们提供了平台 API Spec 到 KCL 类型代码的 生成技术[15] ,并通过 组合编译技术[16] 原生支持对不同 Kubernetes API 版本的编译时选择,在内部实践中解决了应用抽象模型面向不同版本 Kubernetes 集群工作的灵活需求。同时,KCL 支持 in-schema 约束[17] 和 独立环境规则[9] 的编写。此外,KCL 还提供了 deprecated 装饰器[18] 支持对已下线模型或模型属性的标注。通过在客户端健壮的、完备的模型和约束机制,在编译时暴露如配置错误、类型漂移等常见问题。相对于运行时左移的发现问题,避免推进到集群时发生运行时错误或故障,这也是企业内,特别是高风险等级企业,对生产环境稳定性的必须要求。</p><p>对于基础平台技术的专家型用户,他们通常非常熟悉特定的技术领域,更希望以<strong>直面平台细节</strong>的显式的方式工作,<strong>语言提供必要的动态性和模块化支持</strong>,通过类型和约束机制保证稳定性。但这种显式的方式无法解决专家用户不熟悉跨领域平台技术使用细节的问题,也不能解决面向平台技术的扩展性和复杂性叠加的问题。在蚂蚁内部小范围基于 YAML 的显式的工程实践中,面向大量高度开放、可配置的平台技术,复杂性随着平台技术使用率持续叠加,最终陷入难以阅读、编写、约束、测试及维护的僵化状态。</p><h2>六、自动化:新的挑战</h2><p>运维自动化是基础设施运维领域的经典技术范畴,随着云原生理念及技术的推波助澜,可以被自动化集成成为企业运维实践的基本要求,开源开放、高度可配置的 CI、CD 技术逐步被企业采纳,黑盒的、无法被集成的 “产品” 方式逐步被灵活的可编排方式弱化并替代。这种实践的主要优势在于其强大的自定义编排和链接能力,高度的可扩展性和良好的可移植性。</p><p>特别是在 Kubernetes 生态,GitOps 方式有更高的采用率,与可配置的 CI、CD 技术有天然的亲和性。这样的变化也在推进以工单和运维产品为中心的工作流逐步转变为以工程效率平台为中心的自服务工作流,而生产环境的运维能力则成为了工作流中面向生产自动运维的一个重要环节。在开源社区,面向不同研发效率平台的抽象层技术创新也在活跃进行中,平台侧研发者希望通过最短的认知和实践路径打通应用到云环境的 CI 、CD 过程。</p><p>在蚂蚁的工程实践中,工程效率平台深度参与了 Konfig monorepo 的开放自动化实践,我们的实践方向也与工程效率平台技术演进方向高度一致。</p><p>在从几人到几十人再到几百人的协同工作中,面向运维场景的工作流设计,高频的代码提交和 pipelines 执行,实时自动化测试和部署过程,这些对服务于单库的工程效率平台造成了很多的挑战。</p><p>特别是 monorepo 中多样化的业务需要独立且强大的工作流自定义和操作支持,也需要高实时性、强 SLO 保障的并行的工作流执行能力,这些需求与单库模式的需求有巨大的差异,也给我们制造了很多麻烦。</p><p>大部分配置语言是解释型语言,而 KCL 被设计为一种编译型语言,由 Rust 、C 、LLVM 优化器实现,以达到对规模化 KCL 文件提供 高性能[19] 编译和运行时执行的目标,同时支持编译到本地码和 Wasm 以满足不同运行时的执行要求。另外 Git 的存储及架构设计不同于 Citc/Piper[12] 架构,不适用于规模化代码的 monorepo ,所幸今天我们的代码量还没有遇到很大的问题。我们正在一起工作解决这些问题,希望随着实践的深入逐步解决他们。</p><h2>七、协同和文化:更重要的事</h2><p>以上的技术、工具、机制都非常重要,但我必须要说,对于工程化、DevOps 更重要的是<strong>团体与团队的协同</strong>、<strong>合作和分享的文化</strong>,因为这是一种由人组成的工作,人和文化是其中的关键。</p><p>在企业内,如果部门墙、团队壁垒丛生,流行封闭糟糕的工程文化,我们通常会看到大量私有的代码库和私有文档,小群体的判断和工作方式,本该紧密合作的团队以各自目标为导向各行其是,在这样的文化下我认为一切规模化工作都会非常困难。所以如果你所在的公司或团队想采纳规模化 DevOps ,我认为最重要的是做好广泛的沟通并开始文化的建设,因为这绝对不只是几个人的事,并且这很有难度且不可控。</p><p>在蚂蚁的实践中,初期总有各种各样的困难,大家对自服务机制和协同文化的担心尤为突出,例如 “我居然要写代码?” ,“我的代码居然跟其他团队在一个仓库里?” ,“我负责的工作可不简单,这种方式行不通” 都是很典型的担忧。</p><p>所幸我们最终建立了一个面向共同目标的虚拟组织,合作方和领导者给予了充分的支持,我们在理念和工作方式上达成一致并协同工作。在实践过程中,大多数工程师并不是障碍,当然他们会吐槽技术、流程和机制还不够完善,希望获得更好的体验,这无可厚非。</p><p>真正的障碍首先来自于运维平台研发团队自身,我看到一些公司的 DevOps 理想最终回归到运维平台团队替代应用研发者做掉所有工作,甚至不让用户接触到代码和工具链这些生产工具,急于隐藏于已有的 GUI 产品界面,我认为这跑偏了,也低估了用户自身的能力和创造力。</p><p>另外障碍也来自于部分平台技术团队的技术负责人,他们很难放下持续多年的已有工作,难以接受转向到新的用户服务模式。可行的办法是让他们明白这项工作的意义和远景,逐步的分阶段的影响他们。</p><h2>八、小结</h2><p>经过一年多的实践,有 <strong>400+</strong> 研发者直接研发参与了 Konfig monorepo 的代码贡献,管理了超过 <strong>1500</strong> 个 projects,其中平台研发者及平台 SRE 与应用研发者比例不到 <strong>1:9</strong> ,这些应用研发者有些是应用 owner 本人,有些是应用研发团队的代表,这由应用团队自己决定。</p><p>通过持续的自动化能力搭建,基于 Konfig monorepo 每天发生 200-300 次 commits ,其中大部分是自动化的代码修改,以及大约 1K pipeline 任务执行和近 10K KCL 编译执行。</p><p>在今天如果将 Konfig 中全量代码编译一次并输出会产生 300W+ 行 YAML 文本,事实上一次发布运维过程中需要多次不同参数组合的编译过程。通过轻量化,便于移植的代码库和工具链,我们完成了一次意义重大的外部专有云交付,免去了改造、移植输出一系列老旧运维平台的痛苦。在蚂蚁内部我们服务了几种不同的运维场景,正在扩大应用规模并探索更多的可能性。</p><p>最后我想说一说下一步的计划,我们的技术和工具在易用性和体验上还有很大的提升空间,需要更多的用户反馈和持续的改进,体验工作没有快速路径。在测试方面,我们提供了简单的集成测试手段,起到了冒烟测试的作用,但这还不够,我们正在尝试基于约束、规则而非测试的方式保证正确性。</p><p>在工作界面方面,我们希望构建基于 IDE 的线下工作空间,持续规约、优化内部线上产品体验和工作流程。同时我们希望持续提升覆盖范围和技术能力。</p><p>另外我们也希望将实践方式更广泛的应用在 CI 构建,自动化运维等场景,缩短终端用户的信息感知和端到端工作流程。目前 KusionStack 还处于刚刚开源的非常早期的阶段,在未来还有大量的工作要做。最重要的是我们希望把我们平台工程的理念和实践分享给更多企业和团队,一起推动并见证一些有意思的变化发生。</p><p><em>更多详情请参考:</em> <em>[<a href="https://link.segmentfault.com/?enc=rqD%2FKb%2BE6uuO8yiBAH4vFQ%3D%3D.adX80A79pNfbJHtpGZzYIrYd%2F6uRQY2Lq84u9NvBsuPJXgAPsiemeuC3Hi%2Bc1BRVdAcbink36%2FbiAhyaAPJExsTS4n7rZT2AIrjVIjzVWCs%3D" rel="nofollow">https://kusionstack.io/zh-CN/...</a>]</em></p><p><strong>相关链接</strong></p><p>[1]KusionStack:<br><em>[<a href="https://link.segmentfault.com/?enc=CBFYGaF14Hz13D6NFvcrtw%3D%3D.YawuAV9alil45Cs8N4LOSxGDgpmHc0UmgLf%2B5Psuy4gcs4McCcBOPxj60bzylHsW92bwg1o07x90kxOjA4oOig%3D%3D" rel="nofollow">https://kusionstack.io/docs/u...</a>]</em></p><p>[2] DevOps Anti-Types :<br><em>[<a href="https://link.segmentfault.com/?enc=x3riWab4XdlsXTvXEfyMkA%3D%3D.w%2BP2cQ0wdHExysVh0x62lqY7EYOHjOKvhWklFGzsfsTV78JxBBL362jt%2FUXe9B4s" rel="nofollow">https://web.devopstopologies....</a>]</em></p><p>[3] Platform Engineering :<br><em>[<a href="https://link.segmentfault.com/?enc=2YPf2Nt1zqcjDxQB9OBYfw%3D%3D.Gm5jrWtNmHASAwfVVNszk%2BK22AWPrz6gzPpin1OcymJBjH7S1Nf3%2B2LfkZd0R%2FtnaKz2bIY9xvaOHtmUkNjlCCKxYuJ6dYtTIYw3wgpW184%3D" rel="nofollow">https://platformengineering.o...</a>]</em></p><p>[4]内部工程平台:<br><em>[<a href="https://link.segmentfault.com/?enc=%2BZLD40Nikjz0nrpMiQpuVA%3D%3D.HkcYwZLC4H8C3RXVNFk5pFRg92EP4sER7pzALyUz28Kg8sfRt1mZByM5hGSEM1DCyzwSnAhPRuudPRWSyOTv9zeL8GYvhcqDR8y0WvZUz5g%3D" rel="nofollow">https://internaldeveloperplat...</a>]</em></p><p>[5] KCL :<br><em>[<a href="https://link.segmentfault.com/?enc=AWFMkkV62gdlP%2B%2FFBb9uuQ%3D%3D.f62lqwerJ4PVtm%2Bkdmme1htReudcn7BNwZaRQSxYBGlyTmfapzhDvsOn0sPE3lHE" rel="nofollow">https://github.com/KusionStac...</a>]</em></p><p>[6] Konfig :<br><em>[<a href="https://link.segmentfault.com/?enc=FLJEZKIe00AvSRLoH%2BQF3w%3D%3D.t93cC8RwrcBuOzz8R8%2B%2B1Or%2BhLz3Hh2JMVAq5Xe14sxFeW9LiJnuUTYWDsNGsury" rel="nofollow">https://github.com/KusionStac...</a>]</em></p><p>[7] PaaS 模型抽象:<br><em>[<a href="https://link.segmentfault.com/?enc=QCAI3kgUhk9it4ZvphAnKA%3D%3D.j0V%2Fammw%2FWeU%2FWlVMFHrDpx%2BIgn8Hf1yKfRzXeUoDLAGnh2XvLzdW7GhNOsww2rJ11KWiKPKtoSlkqw74JKMVQ%3D%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[8]功能函数:<br><em>[<a href="https://link.segmentfault.com/?enc=8kShxTXbl6G4T3uKF%2ByCpw%3D%3D.2Knuaanz7b%2BbVq9hq9U0Eq6c0XxTBuTCQr4T%2Fl6qPnwqbHxYmjxKP8OjrR0W%2BqRGTxiA8JSpPslCdmC4WByGdg%3D%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[9]约束规则:<br><em>[<a href="https://link.segmentfault.com/?enc=XaReHUJ4nhHecaihhg0vrw%3D%3D.MjNuKoQ3VvNvLEF%2FOBFd7NzdAGSFx9JLLle16NlkLUjPl3fGsDgxSMF3kR5AAZ2zfGTAr5RB0B8uLW39lLC11g%3D%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[10]工程结构设计:<br><em>[<a href="https://link.segmentfault.com/?enc=eQfSMUu%2BD3XTBA7kOrfgWg%3D%3D.AbLFuJPC6jg232Ht0LSmRKOVVYk4U3XPMV3Mxz90XSN4eCIFlkePWTwHVu0zxNbBxUT5bD%2BrFxEryOVCWUVRiA%3D%3D" rel="nofollow">https://kusionstack.io/docs/u...</a>]</em></p><p>[11]自动合并技术:<br><em>[<a href="https://link.segmentfault.com/?enc=CIw0pAMS3f%2FrAn7OaLjmCg%3D%3D.hJ9e%2FYs3h2XyB6wdeu4U43QSUc4H7Q0jq4VOIr0OEwYw%2FRo6XFeRhWn8Vkk0a038Gn8pcztHzPhIxNWN9Xuv1exLGpCAM5aU4RpCBYRNWJU%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[12]静态类型系统:<br><em>[<a href="https://link.segmentfault.com/?enc=FqzzDxT%2Bb2jF3xRLnnTCsA%3D%3D.Sm92%2FakHW6GoR9WIOzYtYuIn5jXOBKskxLBLP9YfEDejkDmsHYgCkBAnThoHX3PCHt%2FeA05mP6MLHe%2FXawcaCvQwsR2hCTiIHXEdQygoQkc%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[13] Kusion :<br><em>[<a href="https://link.segmentfault.com/?enc=TWs73dYrYUzwxmKsTphfEQ%3D%3D.AFkqhCueXSdBcRn5fzP3KMd7wbElaYLCRxbAcvdrJ1aSIaw8v90%2Be1L1ytmqauf7" rel="nofollow">https://github.com/KusionStac...</a>]</em></p><p>[14] mixin :<br><em>[<a href="https://link.segmentfault.com/?enc=iXp6DO8U7w9eH%2BN8hs6Aig%3D%3D.Lo7OwZmNRYk3gXhAY75YovOPPpaGjHVWrqF%2BL%2BICyuOONsHeB3KXAtzj%2FP0iJa2cX1etPdIwyvl3FbXykWEZotjVpeU3kvjTON2OYk5FdVo%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[15] 生成技术:<br><em>[<a href="https://link.segmentfault.com/?enc=7JxAYM997uv8QSuaFDpcEA%3D%3D.RI3qV4LyQe9NMyeZgLgL%2BHf23C%2FrFT4J%2FOxYjoBrcYFXOhQiIx3dKVl4ZBy2fAL1hL3CntbLNecBmubBJkvNBw%3D%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[16]组合编译技术:<br><em>[<a href="https://link.segmentfault.com/?enc=HnneTdciBgEAWVk8dDW%2BlQ%3D%3D.TvbGT96%2BCvyFBaP97idAXJYqBmd%2BGoSZuBZX110x7PeVW6sVkTJZ7uAcUy4zRdzf%2B5UYHe8v7PFodwL5sgOdE1okMQyjZPwZ9Kr857Pop04%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[17] in-schema 约束:<br><em>[<a href="https://link.segmentfault.com/?enc=GEPQ%2BYzv43ATWOsfRXlUZA%3D%3D.AT6Snp8ZhTkI3Ry2w0LdcvyMS5obS%2Fzfts%2BENyti6yYSG%2BaFuPCSeNs0%2BwxgoKSxTi63KAAGDaHAN833Tb8MddWvhA%2BVmkC1%2BCqvZ0zMcKY%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[18] deprecated 装饰器:<br><em>[<a href="https://link.segmentfault.com/?enc=XHgmFEPn0BSelarT3eEw%2Fg%3D%3D.%2BEW0hGDlz%2FsRwW2L3eOagibHeqEp3UlW88UBhyaoZSby%2BOFiqW5hXdmCUxJnqIy5nt8RXqEa%2Fa0fGVJoGts%2Bu5dKdhHd9CTO3lANxaq%2FsoY%3D" rel="nofollow">https://kusionstack.io/docs/r...</a>]</em></p><p>[19] 高性能 :<br><em>[<a href="https://link.segmentfault.com/?enc=Spo8jRBwZyFRSpr%2BDu5eMg%3D%3D.FbZVkKwLR07R1eJP4Hp4pOMLEq7DIerG3Zr0THwJRBKnOvQ3OfvxKm4OHZ87AiViUho2jz%2FhVPkbcqgNltBeda%2FeQoGpKekZ2nNOdlVk6yQ%3D" rel="nofollow">https://kusionstack.io/blog/2...</a>]</em></p><p>[20] Citc/Piper :<br><em>[<a href="https://link.segmentfault.com/?enc=10dcba8b%2FJlqOkvs9IClpA%3D%3D.YRcqtsu7HQu8oku2%2B8CW45VMxB4g%2FYSsNsb3E%2FL1m22XCnXyeJ4cEDrpyOg0T0hMIx1wtO%2BUpBQovTJLRSbmso04Rslfv0dICvusW66xrqt7SF27SRObTQrkBSr0ob%2BAghHPlbx5zGiZPAHmJaY6FKsAy7XF8ld%2F9V4nWq9ne%2FA%3D" rel="nofollow">https://cacm.acm.org/magazine...</a>]</em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=%2FTG%2FdYFaNa%2BFgdBbY5%2FbRA%3D%3D.lEC%2FHj0rDP1Syw16nzWoso2qAghF5p3brP8M5cnICvQZzWfkqaXldOGDNUmjfXV9xi8u8aj3a0Oals8EMFTzbHD0WpYbXMYvFqVCK%2FOspHEfzgjaqqF8Elf%2BZFNpDTzZsKovypLvKNycQBlwK6Bap0BpHiE%2FyuOxue9pXOtr%2Ba7%2F90vjs5g4DeMO6N5uc8pEXG%2BZpM0Lr%2FHDvYq1B04kxnhlEfESugi%2B%2FzoPGXG4%2BpwvX9ToFN7cMIHALj1NMW%2BwotnmuKohUZULyDJ%2FtHDyjg%3D%3D" rel="nofollow">KusionStack 在蚂蚁集团的探索实践 (上)</a></p><p><a href="https://link.segmentfault.com/?enc=I%2BIawHFBxWbjdjj16AYCVw%3D%3D.Fya7olzMdj1FkIPXD%2BIwIT7lyKN0anJhyhm1ibrKK4kTCuJUnpNnThpYrVXKCaFCwWgTx9s%2FhfS4nkaCyOrMqantFbPFrdxe7J3ASWFLmoUo1T2o4Z6ZUurQcTCRBkFaecuYmpMS0hJeV0iUVpwSfhibyFJWZkPzb0QaOKc6vfcv6XbWDGfBhGJw50tQVjAElpFgVHpJ1Mu%2FlyKzS0mPHWMwHp1eddPfIbMD24mztbhVY407lnymwKtUEIRHKti8Ng6fDrmuFZvmviJQIVgiFQ%3D%3D" rel="nofollow">Kusion 模型库和工具链的探索实践</a></p><p><a href="https://link.segmentfault.com/?enc=vI8F3mLPMzavpodMB08KgA%3D%3D.8YcDwQmz4Jc4GT%2FUWD%2FLOdGCFUlwv4Ldwrih0Rq%2BS1RHMqU3s7g85lE60Xgd%2FM2kUSFMo8xOdrTP1sMr2U%2FubbGFBOOs7d2fJVU6%2FYyEEIjW2Vp4RPam2urzeb3v7rqEd%2Fpp2bArL8BK7whjd6HJ4TQNvlFkIhkvBS%2BhTXGsCuB0e41cKfNIidBSQlUsKqv8QCM%2BY%2Fqibo7F5ay6IEHRcAYPELHRXnd0q1k7R5NOLyzMQkcYe8iQNtdLCo9RMjw8NXyCrC86Tp4FZXbOCYzP%2BQ%3D%3D" rel="nofollow">Go 内存泄漏,pprof 够用了么?</a></p><p><a href="https://link.segmentfault.com/?enc=B0QppDFmT%2FF8Ay5NVZiFWA%3D%3D.WahpfAM8jrQs%2FndAf972mK89cppPj0tKIM%2BwFUQkgWN%2FBJIgzDZfPvhMmBBgb2RGJcI%2FwqWHc%2Bgb8NaGgiqYKF2fWwyIv4ynbC9UvPq64kv3cCSwadI1Ej1gs431NqgjFORNzKpmlV8fczj%2BSqKOvCbFNRfYAY6bW2vB7A%2BSd8aZ2X%2FJet6UCKdM8OrC576LUu%2FLYVGivc5gKAYDeiJFIcI%2FvxUsirGqrc6%2BQNvduYL6ofPOPtUvKAan7%2BDorXL4tayC%2FzJ6ZjLomVP2HyoISA%3D%3D" rel="nofollow">Go 代码城市上云——KusionStack 实践</a></p>
Seata AT 模式代码级详解
https://segmentfault.com/a/1190000042520340
2022-09-21T15:06:34+08:00
2022-09-21T15:06:34+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p>文|</p><p>刘月财</p><p>seata-go 项目负责人</p><p>北京小桔科技有限公司【滴滴】开发工程师</p><p>赵新(花名:于雨 )</p><p>蚂蚁集团 Seata 项目开源负责人</p><p>本文<strong>5343</strong>字 阅读 <strong>14</strong>分钟</p><h2>背景</h2><p>Seata 四种事务模式中,AT 事务模式是阿里体系独创的事务模式,对业务无侵入,也是 Seata 用户最多的一种事务模式,兼具易用性与高性能。</p><p>目前,Seata 社区正大力推进其多语言版本建设,Go、PHP、JS 和 Python 四个语言版本基本完成了 TCC 事务模式的实现。参照 Seata v1.5.2 版本的 AT 模式的实现,并结合 Seata 官方文档,本文尝试从代码角度详解 Seata AT 事务模式的详细流程,目的是梳理 Seata Java 版本 AT 模式的实现细节后,在多语言版本后续开发中,优先实现 AT 事务模式。</p><h2>1、什么是 AT 模式?</h2><p>AT 模式是一种二阶段提交的分布式事务模式,它采用了本地 undo log 的方式来数据在修改前后的状态,并用它来实现回滚。从性能上来说,AT 模式由于有 undo log 的存在,一阶段执行完可以立即释放锁和连接资源,吞吐量比 XA 模式高。用户在使用 AT 模式的时候,只需要配置好对应的数据源即可,事务提交、回滚的流程都由 Seata 自动完成,对用户业务几乎没有入侵,使用便利。</p><h2>2、AT 模式与 ACID 和 CAP</h2><p>谈论数据库的事务模式,一般都会先谈论事务相关的 ACID 特性,但在分布式场景下,还需要考虑其 CAP 性质。</p><h3>2.1 AT 与 ACID</h3><p>数据库事务要满足原子性、一致性、持久性以及隔离性四个性质,即 ACID 。在分布式事务场景下,一般地,首先保证原子性和持久性,其次保证一致性,隔离性则因为其使用的不同数据库的锁、数据 MVCC 机制以及相关事务模式的差异, 具有多种隔离级别,如 MySQL 自身事务就有读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、序列化(Serializable)等四种隔离级别。</p><h4>2.1.1 AT模式的读隔离</h4><p>在数据库本地事务隔离级别<strong>读已提交(Read Committed)</strong> 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是<strong>读未提交(Read Uncommitted)</strong> 。</p><p>如果应用在特定场景下,必须要求全局的<strong>读已提交</strong>,目前 Seata 的方式是通过 SELECT FOR UPDATE 语句的代理。 </p><p>SELECT FOR UPDATE 语句的执行会查询<strong>全局锁</strong>,如果<strong>全局锁</strong>被其他事务持有,则释放本地锁(回滚 SELECT FOR UPDATE 语句的本地执行)并重试。这个过程中,查询是被 block 住的,直到<strong>全局锁</strong>拿到,即读取的相关数据是<strong>已提交</strong>的,才返回。</p><p>出于总体性能上的考虑,Seata 目前的方案并没有对所有 SELECT 语句都进行代理,仅针对 FOR UPDATE 的 SELECT 语句。</p><p>详细例子参考 Seata 官网:<em><a href="https://link.segmentfault.com/?enc=8KTDk2cwG7wPMwyPLFsNtA%3D%3D.ZyX9IVsFI9xGjNZqFS%2BggD9xXE78u7qljLcYfV9XwcEoAssUeqAyji7XCBUnX5aU%2Bt0eUl3e0e7VME5QmSZOOg%3D%3D" rel="nofollow">https://seata.io/zh-cn/docs/dev/mode/at-mode.html</a></em></p><h4>2.1.2 AT 模式的写隔离</h4><p>AT 会对写操作的 SQL 进行拦截,提交本地事务前,会向 TC 获取全局锁,未获取到全局锁的情况下,不能进行写,以此来保证不会发生写冲突:</p><p><strong>-</strong> 一阶段本地事务提交前,需要确保先拿到<strong>全局锁</strong>;</p><p><strong>-</strong> 拿不到<strong>全局锁</strong>,不能提交本地事务;</p><p><strong>-</strong> 拿<strong>全局锁</strong>的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。</p><p>详细例子参考 Seata 官网:<em><a href="https://link.segmentfault.com/?enc=8oVYabnM6tZlAgdwqje%2BXA%3D%3D.EyF7OsLrxOJrqLa2NJMJ6GkLZ%2BMTDgXwMbWTPUgnAPgznw5DkVIj4PyubflkToDKJKbHxjgavK8Q5RyNd1JHEw%3D%3D" rel="nofollow">https://seata.io/zh-cn/docs/dev/mode/at-mode.html</a></em></p><h3>2.2 AT 与 CAP</h3><p>Seata 所有的事务模式在一般情况下,是需要保证 CP,即一致性和分区容错性,因为分布式事务的核心就是要保证数据的一致性(包括弱一致性)。比如,在一些交易场景下,涉及到多个系统的金额的变化,保证一致性可以避免系统产生资损。</p><p>分布式系统不可避免地会出现服务不可用的情况,如 Seata 的 TC 出现不可用时,用户可能希望通过服务降级,优先保证整个服务的可用性,此时 Seata 需要从 CP 系统转换为一个保证 AP 的系统。</p><p>比如,有一个服务是给用户端提供用户修改信息的功能,假如此时 TC 服务出现问题,为了不影响用户的使用体验,我们希望服务仍然可用,只不过所有的 SQL 的执行降级为不走全局事务,而是当做本地事务执行。</p><p>AT 模式默认优先保证 CP,但提供了配置通道让用户在 CP 和 AP 两种模式下进行切换:</p><p><strong>-</strong> 配置文件的 tm.degrade-check 参数,其值为 true 则分支事务保证 AP,反之保证 CP;</p><p><strong>-</strong> 手动修改配置中心的 service.disableGlobalTransaction 属性为 true,则关闭全局事务实现 AP。</p><h2>3、AT 数据源代理</h2><p>在 AT 模式中,用户只需要配置好 AT 的代理数据源即可, AT 的所有流程都在代理数据源中完成,对用户无感知。 </p><p>AT 数据源代理的整体类结构如下图:</p><p><img src="/img/remote/1460000042520344" alt="" title=""></p><p> AT 事务数据源代理类结构图【from <em><a href="https://link.segmentfault.com/?enc=T4oSLSMmYOymE7dOLfEmFQ%3D%3D.%2FRMc%2FoeJrZ5Mggnt6cKKSAW3jLSxJcE4o1dy6%2BK%2BxHFq1Usmmg8doLzsNOIRZjBoRVCzZWnGM4w2JvbjxzzzDg%3D%3D" rel="nofollow">https://seata.io/zh-cn/docs/dev/mode/xa-mode.html</a></em>】</p><p>AT 的数据源代理中,分别对目标数据库的 DataSource 、 Connection 和 Statement 进行了代理,在执行目标 SQL 动作之前,完成了 RM 资源注册、 undo log 生成、分支事务注册、分支事务提交/回滚等操作,而这些操作对用户并无感知。</p><p>下面的时序图中,展示了 AT 模式在执行过程中,这几个代理类的动作细节:</p><p><img src="/img/remote/1460000042520345" alt="" title=""></p><p>注:图片建议在 PC 端查看</p><h2>4、AT 模式流程</h2><p>以下是 AT 模式的整体流程,从这里可以看到分布式事务各个关键动作的执行时机,每个动作细节,我们后面来讨论:</p><p><img src="/img/remote/1460000042520346" alt="" title=""></p><p>注:图片建议在 PC 端查看</p><h3>4.1 一阶段</h3><p>在 AT 模式的第一阶段, Seata 会通过代理数据源,拦截用户执行的业务 SQL ,假如用户没有开启事务,会自动开启一个新事务。如果业务 SQL 是写操作(增、删、改操作)类型,会解析业务 SQL 的语法,生成 SELECT SQL 语句,把要被修改的记录查出来,保存为 “before image” 。然后执行业务 SQL ,执行完后用同样的原理,将已经被修改的记录查出来,保存为 “after image” ,至此一个 undo log 记录就完整了。随后 RM 会向 TC 注册分支事务, TC 侧会新加锁记录,锁可以保证 AT 模式的读、写隔离。RM 再将 undo log 和业务 SQL 的本地事务提交,保证业务 SQL 和保存 undo log 记录 SQL 的原子性。</p><p><img src="/img/remote/1460000042520347" alt="" title=""></p><h3>4.2 二阶段提交</h3><p>AT 模式的二阶段提交,TC 侧会将该事务的锁删除,然后通知 RM 异步删除 undo log 记录即可。</p><p><img src="/img/remote/1460000042520348" alt="" title=""></p><h3>4.3 二阶段回滚</h3><p>如果 AT 模式的二阶段是回滚,那么 RM 侧需要根据一阶段保存的 undo log 数据中的 before image 记录,通过逆向 SQL 的方式,对在一阶段修改过的业务数据进行还原即可。</p><p>但是在还原数据之前,需要进行脏数据校验。因为在一阶段提交后,到现在进行回滚的中间这段时间,该记录有可能被别的业务改动过。校验的方式,就是用 undo log 的 after image 和现在数据库的数据做比较,假如数据一致,说明没有脏数据;不一致则说明有脏数据,出现脏数据就需要人工进行处理了。</p><p><img src="/img/remote/1460000042520349" alt="" title=""></p><h2>5、关键代码模块</h2><p>如下是 AT 模式整个流程的主要模块,我们从中可以了解开发 AT 模式需要做哪些事情:</p><p><img src="/img/remote/1460000042520350" alt="" title=""></p><h3>5.1 Undo log数据格式</h3><p>undo log 存在表 undo_log 表中,undo_log 表的表结构如下:</p><p><img src="/img/remote/1460000042520351" alt="" title=""></p><p>rollback_info 存放了业务数据修改前后的内容,数据表存放的是经过压缩后的格式,他的明文格式如下:</p><pre><code>{
"branchId":2828558179596595558,
"sqlUndoLogs":[
{
"afterImage":{
"rows":[
{
"fields":[
{
"keyType":"PRIMARY_KEY",
"name":"id",
"type":4,
"value":3
},
{
"keyType":"NULL",
"name":"count",
"type":4,
"value":70
}
]
}
],
"tableName":"stock_tbl"
},
"beforeImage":{
"rows":[
{
"fields":[
{
"keyType":"PRIMARY_KEY",
"name":"id",
"type":4,
"value":3
},
{
"keyType":"NULL",
"name":"count",
"type":4,
"value":100
}
]
}
],
"tableName":"stock_tbl"
},
"sqlType":"UPDATE",
"tableName":"stock_tbl"
}
],
"xid":"192.168.51.102:8091:2828558179596595550"
}</code></pre><h3>5.2 UndoLogManager</h3><p>UndoLogManager 负责 undo log 的新加、删除、回滚操作,不同的数据库有不同的实现(不同数据库的 SQL 语法会不同),公共逻辑放在了 AbstractUndoLogManager 抽象类中,整体的类继承关系如下图:</p><p><img src="/img/remote/1460000042520352" alt="" title=""></p><p>注:图片建议在 PC 端查看</p><p>插入和删除 undo log 的逻辑都比较简单,直接操作数据表就行。这里重点看下回滚 undo log 的逻辑:</p><p><img src="/img/remote/1460000042520353" alt="" title=""></p><p>源码分析如下:</p><pre><code>@Override
public void undo(DataSourceProxy dataSourceProxy, String xid, long branchId) throws TransactionException {
Connection conn = null;b
ResultSet rs = null;
PreparedStatement selectPST = null;
boolean originalAutoCommit = true;
for (; ; ) {
try {
conn = dataSourceProxy.getPlainConnection();
// The entire undo process should run in a local transaction.
// 开启本地事务,确保删除undo log和恢复业务数据的SQL在一个事务中commit
if (originalAutoCommit = conn.getAutoCommit()) {
conn.setAutoCommit(false);
}
// Find UNDO LOG
selectPST = conn.prepareStatement(SELECT_UNDO_LOG_SQL);
selectPST.setLong(1, branchId);
selectPST.setString(2, xid);
// 查出branchId的所有undo log记录,用来恢复业务数据
rs = selectPST.executeQuery();
boolean exists = false;
while (rs.next()) {
exists = true;
// It is possible that the server repeatedly sends a rollback request to roll back
// the same branch transaction to multiple processes,
// ensuring that only the undo_log in the normal state is processed.
int state = rs.getInt(ClientTableColumnsName.UNDO_LOG_LOG_STATUS);
// 如果state=1,说明可以回滚;state=1说明不能回滚
if (!canUndo(state)) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, ignore {} undo_log", xid, branchId, state);
}
return;
}
String contextString = rs.getString(ClientTableColumnsName.UNDO_LOG_CONTEXT);
Map<String, String> context = parseContext(contextString);
byte[] rollbackInfo = getRollbackInfo(rs);
String serializer = context == null ? null : context.get(UndoLogConstants.SERIALIZER_KEY);
// 根据serializer获取序列化工具类
UndoLogParser parser = serializer == null ? UndoLogParserFactory.getInstance()
: UndoLogParserFactory.getInstance(serializer);
// 反序列化undo log,得到业务记录修改前后的明文
BranchUndoLog branchUndoLog = parser.decode(rollbackInfo);
try {
// put serializer name to local
setCurrentSerializer(parser.getName());
List<SQLUndoLog> sqlUndoLogs = branchUndoLog.getSqlUndoLogs();
if (sqlUndoLogs.size() > 1) {
Collections.reverse(sqlUndoLogs);
}
for (SQLUndoLog sqlUndoLog : sqlUndoLogs) {
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dataSourceProxy.getDbType()).getTableMeta(
conn, sqlUndoLog.getTableName(), dataSourceProxy.getResourceId());
sqlUndoLog.setTableMeta(tableMeta);
AbstractUndoExecutor undoExecutor = UndoExecutorFactory.getUndoExecutor(
dataSourceProxy.getDbType(), sqlUndoLog);
undoExecutor.executeOn(conn);
}
} finally {
// remove serializer name
removeCurrentSerializer();
}
}
// If undo_log exists, it means that the branch transaction has completed the first phase,
// we can directly roll back and clean the undo_log
// Otherwise, it indicates that there is an exception in the branch transaction,
// causing undo_log not to be written to the database.
// For example, the business processing timeout, the global transaction is the initiator rolls back.
// To ensure data consistency, we can insert an undo_log with GlobalFinished state
// to prevent the local transaction of the first phase of other programs from being correctly submitted.
// See https://github.com/seata/seata/issues/489
if (exists) {
deleteUndoLog(xid, branchId, conn);
conn.commit();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log deleted with {}", xid, branchId,
State.GlobalFinished.name());
}
} else {
// 如果不存在undo log,可能是因为分支事务还未执行完成(比如,分支事务执行超时),TM发起了回滚全局事务的请求。
// 这个时候,往undo_log表插入一条记录,可以使分支事务提交的时候失败(undo log)
insertUndoLogWithGlobalFinished(xid, branchId, UndoLogParserFactory.getInstance(), conn);
conn.commit();
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log added with {}", xid, branchId,
State.GlobalFinished.name());
}
}
return;
} catch (SQLIntegrityConstraintViolationException e) {
// Possible undo_log has been inserted into the database by other processes, retrying rollback undo_log
if (LOGGER.isInfoEnabled()) {
LOGGER.info("xid {} branch {}, undo_log inserted, retry rollback", xid, branchId);
}
} catch (Throwable e) {
if (conn != null) {
try {
conn.rollback();
} catch (SQLException rollbackEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", rollbackEx);
}
}
throw new BranchTransactionException(BranchRollbackFailed_Retriable, String
.format("Branch session rollback failed and try again later xid = %s branchId = %s %s", xid,
branchId, e.getMessage()), e);
} finally {
try {
if (rs != null) {
rs.close();
}
if (selectPST != null) {
selectPST.close();
}
if (conn != null) {
if (originalAutoCommit) {
conn.setAutoCommit(true);
}
conn.close();
}
} catch (SQLException closeEx) {
LOGGER.warn("Failed to close JDBC resource while undo ... ", closeEx);
}
}
}
}
</code></pre><p>备注:需要特别注意下,当回滚的时候,发现 undo log 不存在,需要往 undo_log 表新加一条记录,避免因为 RM 在 TM 发出回滚请求后,又成功提交分支事务的场景。</p><h3>5.3 Compressor 压缩算法</h3><p>Compressor 接口定义了压缩算法的规范,用来压缩文本,节省存储空间:</p><pre><code>
public interface Compressor {
/**
* compress byte[] to byte[].
* @param bytes the bytes
* @return the byte[]
*/
byte[] compress(byte[] bytes);
/**
* decompress byte[] to byte[].
* @param bytes the bytes
* @return the byte[]
*/
byte[] decompress(byte[] bytes);
}</code></pre><p>目前已经实现的压缩算法有如下这些:</p><p><img src="/img/remote/1460000042520354" alt="" title=""></p><h3>5.4 UndoLogParser 序列化算法</h3><p>Serializer 接口定义了序列化算法的规范,用来序列化代码:</p><pre><code>public interface UndoLogParser {
/**
* Get the name of parser;
*
* @return the name of parser
*/
String getName();
/**
* Get default context of this parser
*
* @return the default content if undo log is empty
*/
byte[] getDefaultContent();
/**
* Encode branch undo log to byte array.
*
* @param branchUndoLog the branch undo log
* @return the byte array
*/
byte[] encode(BranchUndoLog branchUndoLog);
/**
* Decode byte array to branch undo log.
*
* @param bytes the byte array
* @return the branch undo log
*/
BranchUndoLog decode(byte[] bytes);
}
</code></pre><p>目前已经实现的序列化算法有如下这些:</p><p><img src="/img/remote/1460000042520355" alt="" title=""></p><h3>5.5 Executor 执行器</h3><p>Executor 是 SQL 执行的入口类, AT 在执行 SQL 前后,需要管理 undo log 的 image 记录,主要是构建 undo log ,包括根据不同的业务 SQL ,来组装查询 undo log 的 SQL 语句;执行查询 undo log 的 SQL ,获取到镜像记录数据;执行插入 undo log 的逻辑(未提交事务)。</p><pre><code>public interface Executor<T> { /** * Execute t. * * @param args the args * @return the t * @throws Throwable the throwable */ T execute(Object... args) throws Throwable;}</code></pre><p>针对不同的业务 SQL ,有不同的 Executor 实现,主要是因为不同操作/不同数据库类型的业务 SQL ,生成 undo log 的 SQL 的逻辑不同,所以都分别重写了 beforeImage() 和 afterImage() 方法。整体的继承关系如下图所示:</p><p><img src="/img/remote/1460000042520356" alt="lQLPJxa0Js5RE3jNB7vNDLmwHf8cY0rctjYDKDoGXoCRAA_3257_1979.png" title="lQLPJxa0Js5RE3jNB7vNDLmwHf8cY0rctjYDKDoGXoCRAA_3257_1979.png"></p><p>注:图片建议在 PC 端查看</p><p>为了直观地看到不同类型的 SQL 生成的 before image SQL 和 after iamge SQL ,这里做个梳理。假如目标数据表的结构如下:</p><pre><code>
public interface Executor<T> {
/**
* Execute t.
*
* @param args the args
* @return the t
* @throws Throwable the throwable
*/
T execute(Object... args) throws Throwable;
}</code></pre><p><img src="/img/remote/1460000042520357" alt="" title=""></p><p>注:图片建议在 PC 端查看</p><h3>5.6 AsyncWorker</h3><p>AsyncWorker 是用来做异步执行的,用来做分支事务提交和 undo log 记录删除等操作。</p><p><img src="/img/remote/1460000042520358" alt="" title=""></p><h2>6、关于性能</h2><p>并不存在某一种完美的分布式事务机制可以适应所有场景,完美满足所有需求。无论 AT 模式、TCC 模式还是 Saga 模式,本质上都是对 XA 规范在各种场景下安全性或者性能的不足的改进。Seata 不同的事务模式是在一致性、可靠性、易用性、性能四个特性之间进行不同的取舍。</p><p>近期 Seata 社区发现有同行,在未详细分析 Java 版本 AT 模式的代码的详细实现的情况下,仅对某个早期的 Go 版本的 Seata 进行短链接压测后,质疑 AT 模型的性能及其数据安全性,请具有一定思辨能力的用户朋友们在接受这个结论前仔细查阅其测试方法与测试对象,区分好 “李鬼” 与 “李逵”。</p><p>实际上,这个早期的 Go 版本实现仅参照了 Seata v1.4.0,且未严格把 Seata AT 模式的所有功能都予以实现。话说回来,即便其推崇的 Seata XA 模式,其也依赖于单 DB 的XA 模式。而当下最新版本的 MySQL XA 事务模式的 BUG 依然很多,这个地基并没有其想象中的那样百分百稳固。</p><p>由阿里与蚂蚁集团共建的 Seata,是我们多年内部分布式事务工程实践与技术经验的结晶,开源出来后得到了多达 150+ 以上行业同行生产环境的验证。开源大道既长且宽,这个道路上可以有机动车道也有非机动车道,还可以有人行道,大家携手把道路拓宽延长,而非站在人行道上宣传机动车道危险性高且车速慢。</p><h3>7、总结</h3><p>Seata AT 模式依赖于各个 DB 厂商的不同版本的 DB Driver(数据库驱动),每种数据库发布新版本后,其 SQL 语义及其使用模式都可能发生改变。随着近年 Seata 被其用户们广泛应用于多种业务场景,在开发者们的努力下,Seata AT 模式保持了编程接口与其 XA 模式几乎一致,适配了几乎所有的主流数据库,并覆盖了这些数据库的主要流行版本的 Driver:真正做到了把分布式系统的 “复杂性”留在了框架层面,把易用性和高性能交给了用户。</p><p>当然,Seata Java 版本的 XA 和 AT 模式还有许多需要完善与改进的地方,遑论其它多语言版本的实现。欢迎对 Seata 及其多语言版本建设感兴趣的同行参与到 Seata 的建设中来,共同努力把 Seata 打造成一个标准化分布式事务平台。</p><h2>本周推荐阅读</h2><p><a href="https://link.segmentfault.com/?enc=7%2FiT1fChb7j9bIIQYWRqFg%3D%3D.ZRhnyuCCeUAJJbGOBgcrjKvvdJI%2BCmAFO4TuONxn5UgJ%2BuH7k3BKqZA%2F1eBAOjZ3im4b57G%2FeNL65Q6UOLWCTQ0KopXUZAcDCPuQavhXxdst1UJDvXFA3ZrEJPOj1JrlzPdWs2SUJAdDIAFiYpzaG%2FMLI1rkvVZW1SnQh1YO7ugUVevw1%2B6%2B%2BtDOyqPVX4T8l3cXQCKOaQ6Pdixt8IiP0gZf5KfOeorNqNXp90Jqx6Nu3QpFMVWWpU1L4DGfvXFnWdS8%2FTqExi6Mfi1ozlD55g%3D%3D" rel="nofollow">Go内存泄漏,pprof 够用了么?</a></p><p><a href="https://link.segmentfault.com/?enc=szSWQMiGVXHoTp%2FAZoY3dw%3D%3D.o5Oe5Xp%2FWPAtnD56bXQsGClyUebJ7zaspUwlHuykYq0b4%2FmnF%2BiEXC4CHTCSBEHTkFXorn6iaFZolwsPId8od0%2Fk15jdjYlHSSdb9xUnsN11NRvujK127UAxDnTiIrUzqEjYvArsLHzeFOdaRiXIA2nHAxRb0wCV0k7RybUGHT6Ior%2BVdbBHjZRRvDjkaLDYihYKHgK5JctDlgn35dc99zlSEkI3Kcm3gLxug078X7tXeK3q6YumKdBdsz1fQWHfYgeffBzER887N2zFrWkQhQ%3D%3D" rel="nofollow">Go 原生插件使用问题全解析</a></p><p><a href="https://link.segmentfault.com/?enc=yZNm%2F5PbXOJg7jMMDtDBxA%3D%3D.r56d0oR0ZKhI9Cg7l1k0V3nuP1OHHshfrWbWQKS2QKK4YzMtrIWbl82R1eCEOpjiv%2BlVPY3P7gUQ6BgZ0AKidg4RU5zC0xkAsMX3dAlrsdkryizayTj%2FhgUllHs2IchYC%2BiyKsmhPIFxQim6i5rdYfRBxKF6PuvgwMqMTCRWC%2FwFxSYIdxM7Vker9VLHDmkKsJeZm2aEshVnwrTowdHChMCKc%2FWmWJAJ7YqIy%2B5OiX5tKsWoHKdADOZi3Zp44zFLJq346Cp56yDrqNJ%2Bm563nw%3D%3D" rel="nofollow">Go 代码城市上云--KusionStack 实践</a></p><p><a href="https://link.segmentfault.com/?enc=CeqB50lGtKiA0jvFf%2BG6Fg%3D%3D.GcTBtct0AqwiOB4VIxPTDd%2BNAQiS2Kr9jrVEkM124AeWFXSbTr3x3vzQAN%2F4vw1buBDuZznlXX6iKaInfN%2FyfeeH3V15R6lHfcE4EJUGVkIofce9KTHMf7rjCBjtIOrvwyBbiwlKdjkobJJUPOuo9oZDw2xTty8BPtYIgO3%2BuSXVBtWUBZBiExLCCJDmOukx9aV99PuN%2FXbpday%2BkWaB7PlAWaWFl%2BkOT%2FfgkEEKg7WMqXV8n2gPgu8z%2FiKFYzSYq0i0bFXriODQnq8hWR%2FNXkpppMHUzLCP51ArHqN13Sw%3D" rel="nofollow">Seata-php 半年规划</a></p><p><img src="/img/remote/1460000042520359" alt="" title=""></p>
Go 内存泄漏,pprof 够用了吗?
https://segmentfault.com/a/1190000042510437
2022-09-19T15:41:08+08:00
2022-09-19T15:41:08+08:00
SOFAStack
https://segmentfault.com/u/sofastack
1
<p><img src="/img/remote/1460000042510439" alt="图片" title="图片"></p><p>文|朱德江(GitHub ID:doujiang24)</p><p>MOSN 项目核心开发者、蚂蚁集团技术专家</p><p><em>专注于云原生网关研发的相关工作</em></p><p><img src="/img/remote/1460000042510440" alt="图片" title="图片"></p><p><strong><em>本文 2651 字 阅读 8 分钟</em></strong></p><p><em>MOSN 是主要使用 Go 语言开发的云原生网络代理平台,在蚂蚁集团有着几十万容器的大规模生产应用。在这种大规模的应用中,经常会遇到各种内存问题,通常情况下 pprof heap profile 可以很好帮助分析问题。不过,有时候 pprof 也不够用,也就需要我们有更合适的工具了。</em></p><h2>Part.1--出生证 vs 暂住证</h2><p>首先 pprof 确实很好用,设计实现也都很精巧,有兴趣的可以查看这篇《Go 语言 pprof heap profile 实现机制》[1]。</p><p>用 pprof 来分析内存泄漏,通常情况下是够用了,不过有时候,也会不够用。</p><p>这是为什么呢?因为 <strong>pprof 只是记录了内存对象被创建时的调用栈,并没有引用关系</strong>。也就是说,没有办法知道,内存对象是因为被谁引用了而导致没有被释放。对此,我的同事--烈元同学有一个很形象的比喻,pprof 只能看到出生证,却查不了暂住证。</p><h2>Part.2--需要引用关系</h2><p>有些场景下,我们知道了泄漏的内存是从哪里申请的,但是翻了半天代码,也搞不清楚内存为什么没有释放。比如,内存对象经过复杂的调用传递,或者复杂的内存池复用机制,又或者传给了某个不熟悉的第三方库,在第三方库中有非预期的使用……</p><p>在这些情况下,我们会有一个很直觉的想法是,想看看这些内存对象的引用关系。</p><h2>Part.3--内存引用关系火焰图</h2><p>内存引用关系火焰图,是一种内存对象引用关系的可视化方式,最早应用于 OpenResty XRay 产品。这个工具确实是内存分析神器,给不少的客户定位过内存问题,感兴趣的可以移步 OpenResty 官方博客[2]。</p><p>下图是由一个 MOSN 服务产生的,自下而上表示的是从 GC root 到 GC object 的引用关系链,宽度表示的是对象大小 <em>(也包括其引用的对象的大小之和)</em> 。</p><p>有了这样的可视化结果,我们就可以直观的看到内存对象的引用关系。</p><p>比如下图最宽的部分,表示的是 MOSN 中 cluster_manager 全局变量中引用的 cluster 内存对象:</p><p><em><a href="https://link.segmentfault.com/?enc=LSdAWyF3F15C6xGrHWfnjA%3D%3D.Eg30jpTA%2BaCl1cgO%2Fz8ul6yfk711%2BvlKsw7zKurFOxHs65HRhugkfOtiighS%2BgqJ9EH8ePyb%2FpG8BBJz%2FtedSxJ2Jkdbk3Ii2I22%2FUla6GNknQhYCfdEI1PMZlMceQzt0SznEIrGDU7PjGxIvyd8qrH6nTjphSjNYGphcVtXASQ%3D" rel="nofollow">https://github.com/mosn/mosn/blob/aecc93c4b2b4801e7992387f245fe9eefa45733d/pkg/upstream/cluster/cluster_manager.go#L82</a></em></p><p><img src="/img/remote/1460000042510441" alt="图片" title="图片"></p><h2>Part.4--实现原理</h2><p>在生成火焰图之前,首先我们需要提取两个关键信息:</p><p><strong>-</strong> 每个内存对象之间的引用关系;</p><p><strong>-</strong> 每个内存对象的类型。</p><h3>引用关系</h3><p>获取引用关系比较简单,首先,我们可以在 heap 中找到所有的 GC 对象。然后遍历所有的对象,再结合 bitmap 信息,获取这个对象引用的其他对象。基本原理跟 GC mark 是类似的,虽然实现上很不一样,但因为这个是离线工具,可以简单粗暴的实现。</p><h3>类型推导</h3><p>Go 语言作为编译型静态语言,是不需要为每个内存对象存储类型信息的 <em>(有点例外的是 interface)</em> 。如果是动态类型语言,比如 Lua,则会方便很多,每个 GC 对象都存储了对象的类型。</p><p>所以,要获取每个对象的类型,还是比较麻烦的,也是投入时间最多的一块。当然,还是有解决办法的,简单来说就是做逆向类型推导,根据已知内存的类型信息,推导被引用的内存对象的类型信息。</p><p>这块还是比较复杂的,有兴趣的可以看这篇《Go 语言,如何做逆向类型推导》[3]的介绍。</p><h3>生成过程</h3><p>有了这两个关键信息之后,生成过程如下还是比较清晰的:</p><p><strong>1.</strong> 获取所有的内存对象,包括类型、大小,以及他们之间的引用关系,形成一个图;</p><p><strong>2.</strong> 从 root 对象出发,按照层次遍历,形成一棵树 <em>(也就是剪枝过程,每个对象只能被引用一次)</em> ;</p><p><strong>3.</strong> 将这棵树的完整引用关系,当做 backtrace dump 下来,count 是当前节点的总大小 <em>(包括所有子节点)</em> ,也就是火焰图上的宽度;</p><p><strong>4.</strong> 从 bt 文件生成 svg,这一步是 brendangregg 的 FlameGraph 标准工具链。</p><h2>Part.5--使用方式</h2><p>这个工具是基于 Go 官方的 viewcore 改进来的。不过,鉴于 Go 官方不那么热心维护 viewcore 了,MOSN 社区先 fork 了一份,搞了个 mosn 分支,作为 MOSN 社区维护的主分支。</p><p>之前也给 Go 官方 debug 提交了好几个 bugfix,等后面有空,我们再去提交这个 feature。</p><p>所以,使用方式如下:</p><pre><code># 编译 mosn 维护的 viewcore
git clone git@github.com:mosn/debug.git
cd debug/cmd/viewcore
go build .
# 假设已经有了一个 core 文件(CORE-FILE)
# 以及对应的可执行程序文件(BIN-FILE)
viewcore CORE-FILE --exe BIN-FILE objref ref.bt
# 下载 FlameGraph 工具
git clone git@github.com:brendangregg/FlameGraph.git
../FlameGraph/stackcollapse-stap.pl ref.bt | ../FlameGraph/flamegraph.pl> ref.svg
# 浏览器打开 ref.svg 即可看到火焰图</code></pre><p>如果使用碰到问题,可以随时联系我们或提交 issue(<a href="https://link.segmentfault.com/?enc=nr5r8d%2FXhibQt%2FbL5ggXDQ%3D%3D.uVQxfQKKKYCJPGYUiAwhPa9NOJb3zER13OOmAu%2FG4IjQdUshGbg6NmSrFCmT5Vev" rel="nofollow">https://github.com/mosn/mosn/issues</a>)。</p><p>当然,倘若你成功定位了某个问题,也欢迎与我们共同分享,Let's have fun together!</p><p>MOSN 用户钉钉群:<strong>33547952</strong></p><h3>相关链接</h3><p>[1]《Go 语言 pprof heap profile 实现机制》:<em><a href="https://link.segmentfault.com/?enc=N1Tp97B0Vi8ipoieqceLvA%3D%3D.F%2B29q08WjeY9NgQFiL5bl0W8UY3Wc7CtyEKKuilZ8eqkOq%2FYuUzp0fSACVqGiSgA" rel="nofollow">https://uncledou.site/2022/go-pprof-heap/</a></em></p><p>[2]OpenResty 官方博客:<em><a href="https://link.segmentfault.com/?enc=PHQEMiHQEkLpjUCsyikv0w%3D%3D.7r%2FZhgo4GBadRDqovZzBA4s5IKTzKcpzzb3sVfYVynfUZ8avjv22OlB1EaBWCKEvskus2Kq4EgR3LfBrnGOwMA%3D%3D" rel="nofollow">https://blog.openresty.com.cn/cn/openresty-xray-case-yundun/</a></em></p><p>[3]《Go 语言,如何做逆向类型推导》:<em><a href="https://link.segmentfault.com/?enc=apuoiI%2BWlUNeGtUbCuqDRA%3D%3D.xcikH9PHt6MhR85Mcr0jKO66ycxscWXZW4iOy6VNJY%2Buek9vuUdtnL9kVYTi05Ae" rel="nofollow">https://uncledou.site/2022/go-type-derivation/</a></em></p><h3>了解更多…</h3><p><strong>MOSN Star 一下✨:</strong> <em><a href="https://link.segmentfault.com/?enc=kDo3X4hsI4YBG8gCDi1NJQ%3D%3D.oqhKuI5v2UGngumNidSl2vhAy080FiTgsUGdlOZqn70%3D" rel="nofollow">https://github.com/mosn/mosn</a></em></p><p><strong>插播一条好消息!🤩</strong></p><p>对 Go 语言开发感兴趣的小伙伴们,欢迎大家参与到近期正热的 <strong>GoCity</strong> 项目体验</p><p><a href="https://link.segmentfault.com/?enc=jXGnMbdxB6m%2BQiBAJrjjzA%3D%3D.Xt6rPp8STNbxmPqkgj5fOjhheAh4pQgXVsBLO8zmX2o%3D" rel="nofollow">点击此处查看演示视频</a>,快速入门吧🥳</p><h3>本周推荐阅读</h3><p><a href="https://link.segmentfault.com/?enc=39EdF7IDGdI3wZgX3Fl7RA%3D%3D.910ZXu6Lhzw9NrzdGJZo%2FInpgF1S0JWJm0gzVPcfacv%2B4jHI86CVJFc3JgUJtTyiHMKXGRDoQeWY%2BkeP9xtXrk5trHgmbo%2FaRnXxtBmyJQCx3LnFTs0OejrMMDbA%2FMwQXs%2B%2FOv9BDVN94xsV7Dnj2m%2FSr7%2Fv5cWK5MvEeMdjnaBVh3Nue5dogZEDemWGeq3fHUblXc%2BhZ7kIcbr7CXMkXiiMtLJcEIMGJ21eO2cqJ1z014CRgPe3KOgeFfSmH0YN2GM1QH9JwoBpQ0iLODhqZg%3D%3D" rel="nofollow">MOSN 反向通道详解</a></p><p><a href="https://link.segmentfault.com/?enc=n24uH0JxEE86w%2FtZKX0y4Q%3D%3D.GHkLFLytpAdfaiGK1iEuyyvafj6yJDyZPUR4NMGXBl7M9YyWRlrc3fTYhxngJw8lkfVqzNWJTpIUIScEJZx0g5Pa3RLQKwD6xQMedjm3mr%2BQEWyhR3fFIQeIRtCbXT6ttr1tDHc4fVWehWv2oBRTCe054RrNyCuHA7ABhcBAlmPsRX28BdIC39n4P%2BAR6iL1z7kHQVxV5TtAnqHTZ8F2XjcC2IiIwgH5ob3suuih0mlWqVFMiJ38mqp%2FuyttZLJPbhPpEwfnhvE%2BK%2BMcxuHQRQ%3D%3D" rel="nofollow">Go 原生插件使用问题全解析</a></p><p><a href="https://link.segmentfault.com/?enc=XZ5WbjX1%2F3Te2AyE1JqUvA%3D%3D.dVE4%2FsFmoHWBMLa5rLIfSYfTWV8qhzpyOVNEZ5aKRrSbtDfjhg5%2Bq2tughhAv7WcGnnsIsT%2F1GkJuEVNqfSzfL9Ul4dNCQXUN4jPolkd%2Fsw7qTx2TcKnLMjWuVYdEcMqW5xXaZ3l0BHOTuzOM4kFYJVrr%2Bic7o4rHEjw1xwo23PrrhHaCwjZiIX8lgfx9G2SyMSXhAb5kur%2F7P96gAxFz8m9JvznKw7iQ6HDPyPZrgOF2rzWLjiD3gNu2uFNy3tDvInFolQQxiz%2FkW%2FC4Km5RA%3D%3D" rel="nofollow">Go 代码城市上云--KusionStack 实践</a></p><p><a href="https://link.segmentfault.com/?enc=ucXnpkWUiPNKY129RUvhtQ%3D%3D.Q0on0WxgaWLJG6Gin3KKJttfJe4Ak6jHeQWiXkWTOn2gGIgPSsFzknI1rIL1vPC9AwEt8sIHybQQ6yC%2BQIlx2J9q452pvg2x3UGGpsYsw4z7hMhcFfH0dNsrCx2fjuOSWkmc0sSNNwy9DWO2c7JI7AG4lXM8sh3vHeXVtUuCnb0b0f%2BcNAMvdzQbuOjWIdZBlUvW0Flq2JTOLqVniw8QRrM3DbpLrZNqrhE7K3u7jMCo4QLnXTVNk8asDSUGm6f0t7b7V2y5O1rapMRrkmUh6w%3D%3D" rel="nofollow">MOSN 文档使用指南</a></p><p>欢迎扫码关注:</p><p><img src="/img/remote/1460000042510442" alt="图片" title="图片"></p>
Dragonfly 基于 P2P 的文件和镜像分发系统
https://segmentfault.com/a/1190000042441883
2022-09-06T18:30:32+08:00
2022-09-06T18:30:32+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042441888" alt="图片" title="图片"></p><p>文|孙景文、吴迪(<em>Dragonfly Contributor</em>)</p><p><em>本文 4138 字 阅读 10 分钟</em></p><h2>背 景</h2><h3>网络下载</h3><p>提起网络下载领域,你应该首先会想到基于 TCP/IP 协议簇的 C/S 模式。这种模式希望每一个客户机都与服务器建立 TCP 连接,服务器轮询监听 TCP 连接并依次响应,如下图:</p><p><img src="/img/remote/1460000042441889" alt="图片" title="图片"></p><p>上世纪末期,基于 C/S 模式的思想,人们发展了 HTTP, FTP 等应用层协议。然而 C/S 模式的弊端很明显:服务器的负载过大,下载速率过慢。随着互联网规模的增大以及客户对于下载数据大小,下载速率等需求的上升,这些弊端被不断放大。</p><h3>P2P 下载原理</h3><p>基于上述背景,有人结合 P2P 网络与负载均衡的思想,提出 P2P 下载模式。这种模式不再把所有的下载压力丢给服务器,服务器只负责传递文件元数据,真正的文件下载连接建立在客户机与客户机之间。同时一个文件可以被分片为多个块,同一个文件中不同的块可以在不同的客户机之上下载,使得下载文件在 P2P 网络中动态流通,大幅提升了下载效率,如下图:</p><p><img src="/img/remote/1460000042441890" alt="图片" title="图片"></p><p>去中心化的 P2P 下载基于 DHT 技术,它采用分布式全网方式来进行信息的存储和检索。所有信息均以哈希表条目形式加以存储,这些条目被分散地存储在各个节点上,从而以全网方式构成一张巨大的分布式哈希表。在此基础上做到对单服务器的去中心化,哈希表负责对负载的分摊,将全网负载均摊到多个机器之上。</p><h2>项目简介及架构概述</h2><p>Dragonfly 是一款基于 P2P 的智能镜像和文件分发工具。它旨在提高大规模文件传输的效率和速率,最大限度地利用网络带宽。在应用分发、缓存分发、日志分发和镜像分发等领域被大规模使用。</p><h3>原理</h3><p>Dragonfly 结合 C/S 架构与 P2P 架构的优点。它提供面向客户的 C/S 架构下载模式。同时它也提供面向服务器集群的 P2P 回源模式,与传统 P2P 不同的是,对等网络建立在 Scheduler 内部,目标是最大化 P2P 内部下载效率,如下图:</p><p><img src="/img/remote/1460000042441891" alt="图片" title="图片"></p><h3>架构简介</h3><p>Dragonfly 面向镜像分发和文件分发,结合 P2P 网络和服务器集群的思想,向用户提供稳定的,高效的下载服务。Dragonfly 希望在服务器内部构建 P2P 网络,将服务器的不同主机节点分为 Manager、Scheduler、Seed Peer 以及 Peer 四个角色,分别提供不同的功能。</p><p>其中 Manager 提供总体配置功能,拉取其他角色的配置并相互通信。Scheduler 提供下载调度功能,其调度结果直接影响下载速率。Seed Peer 负责回源下载,从外部网络中拉取所需的镜像或文件。Peer 作为 C/S 架构中的服务器,通过多种协议向客户提供下载功能。架构图如下:</p><p><img src="/img/remote/1460000042441892" alt="图片" title="图片"></p><p>其中,Seed Peer 支持使用多种协议从外部网络中回源下载,同时也支持当作集群当中一个 Peer 使用。Peer 提供基于多种协议的下载服务,也提供为镜像仓库或其他下载任务的代理服务。</p><h2>组件详解</h2><h3>Manager</h3><p>Manager 在多 P2P 集群部署的时候扮演管理者的角色,提供前端控制台方便用户进行可视化操作 P2P 集群。其主要提供动态配置管理、维护集群稳定性以及维护多套 P2P 集群的关联关系等功能。对于维护集群整体稳定性 Manager 和各个服务保持 Keepalive 保证能够在实例异常情况下将异常实例进行剔除。动态配置管理可以在 Manager 上面操作各个组件的控制单元,比如控制 Peer 和 Seed Peer 的负载数,Scheduler 调度 Parent 的个数等。</p><p>Manager 也可以维护多套 P2P 集群关联关系,一个 Scheduler Cluster、一个 Seed Peer Cluster 和若干个 Peer 组成一个完整的 P2P 集群,当然不同 P2P 集群可以是网络隔离的。正常情况下采用一个机房一套 P2P 集群,统一由一个 Manager 管理多个 P2P 集群。</p><h3>Scheduler</h3><p>Scheduler 主要工作就是为当前下载节点寻找最优父节点并触发 Seed Peer 进行回源下载。 在适当时候让 Peer 进行回源下载。Scheduler 在启动时,先向 Manager 注册,注册成功后初始化动态配置客户端,并从 Manager 拉取动态配置,接下来启动 Scheduler 自身所需的服务。</p><p>Scheduler 的核心就是选取一组最优 Parent 节点供当前下载 Peer 进行下载。Scheduler 面向 Task,一次 Task 就是一次完整的下载任务,在 Scheduler 中存储 Task 信息和相应 P2P 下载网络的 DAG。调度过程是首先过滤异常 Parent 节点,根据多维度进行过滤,比如判断该 Peer 是否是 BadNode,判断逻辑为假设每个节点的响应时长都遵循正态分布,若一个节点目前的响应时长处于 6σ 范围之外,那么认为该节点是 BadNode,剔除该节点。再根据历史下载特征值对剩余待定 Parent 节点进行打分,返回一组分数最高的 Parent 提供给当前 Peer 进行下载。</p><p><img src="/img/remote/1460000042441893" alt="图片" title="图片"></p><h3>Seed Peer 和 Peer</h3><p>Seed Peer 和 Peer 有很多相似之处。他们都是基于 Dfdaemon,不同的是 Seed Peer 采用 Seed Peer 模式,支持主动触发回源下载。Peer 采用 Peer 模式,作为 C/S 架构中的服务器向用户提供下载功能,支持被 Scheduler 被动触发回源下载。</p><p>这表明 Peer 和 Seed Peer 的关系不是固定的,一个 Peer 可以通过回源使自己成为 Seed Peer,Seed Peer 也可以改动运行状态变为 Peer,Scheduler 会动态地对相应 DAG 进行改动。另外 Seed Peer 和 Peer 都需要参与调度下载过程当中,Scheduler 可能会选取 Seed Peer 或者 Peer 作为父节点向其他 Peer 提供下载功能。</p><h3>Dfstore 和 Dfcache</h3><p>Dfcache 是 Dragonfly 的缓存客户端,它与 dfdaemon 通信并对 P2P 网络中的文件进行操作,其中 P2P 网络充当缓存系统。可以在 Scheduler 中存储相应 Task 和 DAG。</p><p>Dfstore 是 Dragonfly 存储客户端. 其可以依赖不同类型的对象存储服务作为 Backend,提供稳定的存储方案,现在支持 S3 和 OSS 。Dfstore 依赖 Backend 对象存储服务结合 P2P 本身的加速特点。可做到快写快读,并且能够节省回源以及跨机房流量,减少源站压力。</p><h2>优势</h2><h3>稳定性</h3><p>Dragonfly 会自动隔离异常节点来提高下载稳定性,Dragonfly 中各个组件通过 Keepalive 与 Manager 进行联系,Manager 能够保证返回给 Peer 的 Scheduler 地址和返回给 Scheduler 的 Seed Peer 地址都是可用的。不可用的 Scheduler 和 Seed Peer 不会被 Manager 推给需要进行下载任务的 Peer 或 Scheduler,从而达到隔离异常节点的目的,这也是实例维度的异常隔离,如下图:</p><p><img src="/img/remote/1460000042441894" alt="图片" title="图片"></p><p>另外 Dragonfly 在调度时以 Task 为单位,也确保了整个调度过程的稳定性。在收到一个新的 Task 调度请求之后,Scheduler 触发 Seed Peer 进行回源下载;在收到一个已有 Task 的调度请求之后,Scheduler 调度最优 Parent Peer 集合返回给 Peer。这个逻辑确保了无论 Task 是否下载过,Dragonfly 都可以对其进行处理。此外在 Scheduler 调度过程中,对响应时长过慢的 Peer ,认为目前是异常节点,将不会作为 Parent Peer 被返还。这也是 Task 维度的异常隔离。</p><h3>高效性</h3><p>Dragonfly 采用 P2P 进行服务端内部的回源,P2P 下载本身即分摊负载,将每个服务端节点的负载降到最低,有以下几个细节保证了 Dragonfly 下载的高效性:</p><p><strong>-</strong> Scheduler 通过为每个可能的 Parent 打分,返回给 Peer 目前局部最优的 Parent 集合,Peer 基于此集合做下载。 </p><p><strong>-</strong> 下载过程基于 Task,每个 Task 将待下载文件分为多个 Piece,Peer 拿到了最优的 Parent 之后,向此集合广播每个 Piece 的下载请求,集合中的 Parent 收到该请求后返回给 Peer 对应 Piece 的元信息,Peer 将第一个收到的 Piece 元信息所对应的 Parent Peer 作为该 Piece 的实际下载源。该做法考虑到 Scheduler 返回可用 Parent 到触发下载这段时间内可能的变化,同时对不同的 Piece,允许 Peer 向不同的下载源获取数据。 </p><p><strong>-</strong> Dfdaemon 分为 Seed Peer 模式和 Peer 模式,允许 Seed Peer 和 Peer 进行切换,可以根据实际需求改变作为 Seed Peer 和 Peer 的机器数目,动态调整更适应实际情况。</p><h3>简单易用</h3><p>Dragonfly 提供 Helm Charts、Docker Compose、Docker Image 以及二进制的多种部署方式。用户可以快速一键部署进行一次简单 POC,并且也可以基于 Helm Charts 进行大规模生产部署。当然 Dragonfly 各个服务都有完善的 Metrics 也提供现成的 Granafa 模版,方便用户观察 P2P 的流量走势。</p><h2>展望</h2><p>Dragonfly 作为 CNCF 在镜像加速领域标准解决方案,结合 Dragonfly 子项目 Nydus 进行按需加载可以最大限度提升镜像下载速度,未来我们也会继续努力建设镜像加速领域的生态链。感谢所有参与到社区建设的同学,希望有更多对镜像加速领域或 P2P 感兴趣的同学加入到我们的社区当中。</p><p>🔎欢迎搜索群号:<strong>23304666</strong> </p><p>或钉钉扫码入群哦🧸</p><p><img src="/img/remote/1460000042441895" alt="图片" title="图片"></p><h3>【参考文档】</h3><ol><li>项目地址:<em><a href="https://link.segmentfault.com/?enc=a5nllZB9B55j1BGmqw8QfA%3D%3D.D6s%2BEcYvT72y27NNUIeI57p%2BgWPhxIhXD%2BxBlgZlMkhrzd5d%2FEsYILPJJk%2B5OzLV" rel="nofollow">https://github.com/dragonflyoss/Dragonfly2</a></em></li><li>官网:<em><a href="https://link.segmentfault.com/?enc=sofPiFriURAJfBKkT3qbgQ%3D%3D.QPvKC3FgRLSR2OxaeyY9ug%3D%3D" rel="nofollow">https://d7y.io/</a></em></li><li>Slack:<em><a href="https://link.segmentfault.com/?enc=J1w%2B7BXduKwbcTK5GRtAKA%3D%3D.8fC%2FRO%2BFWE4qWYM4IR%2F9kAZGB5gtXrlT0GghUNIhXeUtZ9%2B%2FrCaIi4zeEzDn4YO5XSrGv6TLkqg0Rby17%2FbxqQ%3D%3D" rel="nofollow">https://cloud-native.slack.com/messages/dragonfly/</a></em></li><li>Twitter:<em><a href="https://link.segmentfault.com/?enc=bNxDKOvKQkwhdZtI9pkkDg%3D%3D.jupwYCHzzmAyo8HbZ1ZbcSRVzigzaTLa0CyyoolUuhSPOaI%2BmB1vrSS9hmREHEnc" rel="nofollow">https://twitter.com/dragonfly_oss</a></em></li><li>Developer Group Email:<em><a href="mailto:dragonfly-developers@googlegroups.com">dragonfly-developers@googlegroups.com</a></em></li></ol><h3>本周推荐阅读</h3><p><a href="https://link.segmentfault.com/?enc=BRLjkw6AgEwf0Mm%2BV0QOXA%3D%3D.IVxYyWFfjmvogR8oMI8r6maiR73MoW%2BU5%2FqE5VBrvno%2FUjPt8sp1rV7EzRY3UbY7x%2BDYzu0gBGVwMdQUPGEDn3zSli2ELCMUaPy0XPFQHnE5b5y0Gw5Ai9V4sVdGOgjNzqFPu87c%2FuQB%2BPslqz47jf1rinWgFxnOFlXdtNXhSv0oXL%2BYJmNCYV2r%2FSg8RpMQE9h%2Bzt%2FHZx68kn1ujgy0p%2FJbVMIz5VJdlmNV3nrJ3dTaPQXgJndSG%2FRV%2FskJ4wqoC9Gn4oxm32RqM%2FoYw7Yt6Q%3D%3D" rel="nofollow">Nydus——下一代容器镜像的探索实践</a></p><p><a href="https://link.segmentfault.com/?enc=Q%2FtwGgMDYufssTGI7341Xw%3D%3D.HtYBaWVq5OxtxvTLj8j6gnmh7V8MTWCQtU3hgsZyb9LZppg5GEsBI4EK0Uv%2FZvfhKZoj76053wewDffq22Ay3c5Pf75Q%2Bcp7822mwwDBsFwcvt0V%2Fx8EgUFZU9SwijZmFOVWDe96cps5aZTHZq9gXFuMCDxSJZU2KtOUyRSdVmvthaMuT1Z9ILbpM6eqTJRFZ96gUDzYEByYaeZiX%2FLTuIhvV7n%2BFUF%2Fu%2FeC8PcIejwnHe%2Fjcm%2FB0CiqqX0KrlMoVgMIgLGzX18MoY%2BgbSkKGA%3D%3D" rel="nofollow">深入 HTTP/3(2)|不那么 Boring 的 SSL</a></p><p><a href="https://link.segmentfault.com/?enc=IXhC%2BcvynXK%2FLmj1vm60uQ%3D%3D.PoWWvYcB3mfRJzvEUGZ8QevVG%2BMjQgjR4Me9bdVTCFJHd2gi4T8Ivphgb39aB%2FWX2z7nL%2FL8UGMTbjo2s6Bfy48E2GlTY1Y6kPdg2ycB%2FEfX3TGysH7vbqDeJvuuzMABM91Na5Pb8KIA2n3RtWkkf%2FumTHr%2Fu0auFATF7aJCRiyZies4s5ZpAf7asOEFxnZ8f2M471a0UB5FFGLJMIlLGqynuJolMrlYFI35ei5u1fR5ZgE6hxRuWILNvACcoDJ4IP4UUB51FlqheFaNWkg5wQ%3D%3D" rel="nofollow">Go 代码城市上云——KusionStack 实践</a></p><p><a href="https://link.segmentfault.com/?enc=ieP%2FFziKgnyI%2BvYWagsuMw%3D%3D.SVhOQnqK8TncIGBIrB3YdJAF%2Fk6cSB0N7Av6mSv5aKIPC0Ump8iOCKDAhLOvUW1NlRdNiG3eOZLkWsTt0anLckE6aGHjXk04hlMJlxxVKC8BOsMoyU2HodG7Lg%2BFgqBYAZ1CooRiOsRNt5Mzl%2Fl%2FJ9Y2m%2FPJYSVx6ysHQULebO9PUdG52jcVNkx0pNTCsK6MbRmRDaX07AUkFM%2FiUXiocm093vFOosbK2J%2F1G49U41H4lJCOrvNnWiLsYpylhFCnt0SAY5mLhTi0eVdwBV%2BH8A%3D%3D" rel="nofollow">MOSN 反向通道详解</a></p><p><img src="/img/remote/1460000042332190" alt="图片" title="图片"></p>
HAVE FUN | SOFA 飞船——Layotto 星球登陆计划
https://segmentfault.com/a/1190000042420975
2022-09-02T01:22:12+08:00
2022-09-02T01:22:12+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042420977" alt="" title=""></p><p><img src="/img/remote/1460000042420978" alt="" title=""></p><p><strong>一、 Layotto 星球介绍</strong></p><p>Layotto 是一款使用 Golang 开发的应用运行时, 旨在帮助开发人员快速构建云原生应用,帮助应用和基础设施解耦。Layotto 提供了各种分布式的能力,比如状态管理、配置管理、事件发布订阅等能力,以简化应用的开发。<br>这次 SOFAStack 开源社区启动 SOFA 飞船,踏上充满挑战的星球登陆之旅,旅途第一站来到了 Layotto 星球。星球登陆任务已被打包放在星球上的各个空间站,想要成为开源先锋的小伙伴快来参与探索,做进击的开源人吧!</p><p><strong>二、玩转星球</strong></p><p>我们会持续发掘更新 Layotto 星球上的各个空间站。小伙伴们可以加入自己感兴趣的空间站并进行探索<br>,通过任务实现积累能量值,去成为空间站站长,带领更多本站的小伙伴完成星球登陆吧!</p><p>点击下方<strong>阅读原文</strong>,和我们一起玩转 Layotto 星球。</p><p><strong>三、星球登陆指引</strong></p><ol><li>参与者登陆自己的 GitHub 账号进入活动 issue 页,挑选出自己想要加入的空间站,在感兴趣的星球登陆任务下评论<strong>“/assign”</strong>领取任务开始探索</li><li><strong>在规定时间内</strong>完成领取的任务后提交 PR,“ Layotto 星球领航员”(_Committer_)或所在的空间站的站长(_Reviewer_)会进行 review</li><li>提交的 PR 审核通过后,即达成任务,<strong>获得相应的能量值</strong><br>4.重复以上流程,继续探索空间站,<strong>成为“Layotto 空间站站长”</strong>(_Reviewer_)</li><li>帮助 review 其他小伙伴的任务 PR,带领空间站成员继续探索挑战</li></ol><p><strong>四、探索空间站</strong></p><p>以下为已探寻到的空间站,大家可以选择自己感兴趣的空间站加入并探索哦~</p><p><img src="/img/remote/1460000042420979" alt="" title=""></p><p><strong>五、能量值获取</strong></p><p>本次任务分为 Easy、Medium、 Hard 三个等级,完成不同等级任务可以积累相应能量值:<br><strong>1.</strong> 完成一个 Easy 的空间站任务可收集 2 颗能量值<br> <strong>2. </strong>完成一个 Medium 的空间站任务可收集 4 颗能量值<br> <strong>3. </strong>完成一个 Hard 的空间站任务可收集 10 颗能量值<br>🌟_<strong> </strong><strong>收集能力值可以做什么?</strong>_<br>在单个空间站获取能量值数量 >=10(_当中至少完成 1 个 Medium 任务_)的小伙伴,可以成为该空间站的站长(_Reviewer_),查看更多人的任务 PR,帮助更多小伙伴登陆 Layotto 星球!</p><p><strong>六、空间站福利</strong></p><p>活动到期结束时,我们将公布最终的星球登陆计划进展,为本期活动的“空间站站长”(_Reviewer_)和“星球领航员”(_Committer_)的同学发放终极奖励(_社区 Reviewer/Committer 证书,活动专栏宣传,社区周边礼物_)。<br>另外,我们将统一发放社区<strong>周边小礼品</strong>以鼓励参与“Layotto 星球登陆计划”的其他小伙伴。<br>有任何问题欢迎微信添加 SOFAGirl180419 备注飞船任务,或加入钉群:41585216。</p><p><strong>更多活动详情...</strong></p><p><strong>即刻参与✨</strong><br><em><a href="https://link.segmentfault.com/?enc=FxKDd%2FasmYscHn9lGFCx%2BQ%3D%3D.dIg5bQCTym19mqgjr9cW8iF8Fo8o6nHRpwrzX6LvdHBAzTcTK2vqA0%2FtTaYQ6dGz" rel="nofollow">https://github.com/mosn/layot...</a></em></p><p><strong>源码解析活动圆满结束</strong></p><p>本期源码解析活动要暂告一个段落啦,感谢大家的积极参与。自今年 3 月份活动上线以来,我们陆续发布了 SOFARegistry、Layotto、SOFABoot 以及 SOFAArk 四个项目的源码活动,总计发布 issue 近 30 个,累计参与人数达到了 60+。<br>我们感受到了大家参与活动的满满热情,也和许多参与开源的优秀小伙伴有了更多的联系。还未能及时参与进来的小伙伴不要灰心,我们还会出更多好玩的活动,期待下次再见~<br>也希望大家在线下继续参与项目建设,让我们共创一个更美好的开源生态!<br>如果还对什么项目感兴趣的话,欢迎留言哦🧸</p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=JSPkOrMD6T9tedIFotdk%2BA%3D%3D.Cpe8Wj9o1PuGrmMcS6f0abZvrbeGeWF4A7bVw1TkpvlimscaViAJl637JsFwo9k6piRRe5jPE35fEt6pAxWjbN5HGBjmaoNtLngv88rt%2Bf1UXNLUjg6LIv0haEOuI%2BFxbP49BaPDcB1QnZXiUvxuv1E2Vy%2FT%2FttlHYPiEtgRHa%2FojFhtwi%2FEHkQ214npk6OZdxqWK8pw3Lbx7tl%2Bb4znMWc95S3Scvx7Y2iWbzKXQI1qGjs1UgX0vHjUpAsXQhCHY4INdCbOxpC4DePIOYrRjn0cQUDpjupTstvj0cfhQxHZa8Tclp5an7gkNQA3K8WQWzqxf44xKWkYajVG9ugLtA%3D%3D" rel="nofollow">如何看待 Dapr、Layotto 这种多运行时架构?</a></p><p><a href="https://link.segmentfault.com/?enc=IPayGBQGmM1qMEec%2FxyETQ%3D%3D.bngc1OXHIMjZfa0kn9ekoSFUr03QiWuYkivm%2Bi9G9G4c3qGYkT85Fu7uHaccZNC%2FoPhSjQoYX8gC%2Bq7wN9KUDWu8fE%2FoC4S3YJfHkQ5M1OlHqN3trK50xKRh%2BA0OYx0Kk%2FLIbTKAV63MSTmzQ1%2FQ1loyWtBP%2BkXLDxDC4jIvCjJubw9sB8nrR%2BNviOnhPoKrBBQcn2k3as9WQeb1K5Fv5Qb43oMr6JXeJrogw7xa6Dl08X%2Fx1oiFh0pZDvFFcOSGMK84g8bGP%2BgGnMInIBYfHclZ3yJe4f8k%2BnXFl%2FKjuCIZ5Y3GDJ6jF2DxNl8PDWKwgUfKUBRTJnH%2FPWbCxO4yew%3D%3D" rel="nofollow">SOFARegistry 源码|数据同步模块解析</a></p><p><a href="https://link.segmentfault.com/?enc=hw2W2ynVs%2Fi%2FMpED%2B4n9Mg%3D%3D.snzaSaGfSWEzgAEu5YEHHNB6eBgWNqm8M%2BXr9YNtwC0FTV3026F6H7k2xq040cGP9S%2B5elzj%2Bclq%2FfPlxT6ljDjT1X8bLCYOkNKb5IgsC99laJQfoT8ituJIxGOc0Uu%2BZxUsihIkVBWV2Ndjo0rrHFzMUVvBl5fbSXIjfLMva6IMefODJ9QtJ0P%2BdyNmdpmASrzcVaCgDGIIK8S4YYx9TDbKeMGDkXlyufKSoKtL4WWfs0Ec%2BJ5aUq8sMv8%2BQY23q%2BlJugCzct%2F5J5gP8v8Z6yhZQwrqOAgRFLxOnH4dh31H7KsV%2F5AhZZgjt4vswN9wWqMzmaxMYDssbFRGrSqOMw%3D%3D" rel="nofollow">SOFARegistry 源码|数据分片之核心-路由表 SlotTable 剖析</a></p><p><a href="https://link.segmentfault.com/?enc=IRXpliwoi7HmCToiS66qNA%3D%3D.UQUJM%2Bx6gwMnjgFhQ%2BvG9frDf9TKDgTh75IcGj%2B%2FLtJkoSnHa91QhzpP2Mg64ysuivJh2SgIzgD1ClK5dUP1RExy2DeT%2B0vOQJSn0QYKdcP8BtpzkNb61Cya7bnANRlOOBgvbM1Vwi4QLLEOtMiEw1VD3H9obGa7aMFYPSdif%2F7%2B7TY4Kev2iWuWkDFOE%2F%2Fy%2FkLc%2BvpNEfbSgHcg20u4FA2P5Wbtwv377Sc99HsR2Z9hEltekgEJjS9o6NWVCl4Vq8KYX8Fsh7LT8gCfAJD2QfSR9rT1H3weGs%2Fa7Zm7WoGPhQcuK%2BUH%2FuNfiEznAYYgPyoBoylm35l0YNYwM5OQvg%3D%3D" rel="nofollow">SOFAServerless 体系助力业务极速研发</a></p><p><strong>关注我们</strong></p><p><img src="/img/remote/1460000042332190" alt="" title=""></p>
Go 代码城市上云 ——KusionStack 实践
https://segmentfault.com/a/1190000042407993
2022-08-31T11:31:37+08:00
2022-08-31T11:31:37+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042407995" alt="" title=""></p><p><img src="/img/remote/1460000042407996" alt="" title=""></p><p><strong>导语</strong></p><p>KusionStack 是面向 Kubernetes 云原生场景的 IaC 配置代码化实践的开源一站式可编程协议栈。其基本思想是让「应用方+平台方」的同学能够共同基于 IaC 构建的 Konfig 模型库同一平面进行 DevOps 协同工作。今天我们和大家分享一个好玩的 Go 代码城市应用,以及 KusionStack 是如何一键将其部署到 K8s 云原生环境的。</p><p><strong>KusionStack 项目主仓库:</strong></p><p><a href="https://link.segmentfault.com/?enc=WVtHfbEQG6wZC%2BcvkqzLhQ%3D%3D.YI4%2BlLaXJdff7QDLHUhUOqkinIt6RwJBoHfrNoFWX%2BG1CyW6SmH5RxB6y8JTmBKM" rel="nofollow">https://github.com/KusionStack/kusion</a></p><p><strong>PART. 1</strong></p><p><strong>什么是代码城市(CodeCity)</strong></p><p>CodeCity 代码城市是瑞典工程师 Richard Wettel 开发的创意应用,可以通过类似数字城市的视觉形式展示和度量代码的复杂性。其效果如图:</p><p><img src="/img/remote/1460000042407997" alt="" title=""></p><p>在 3D 形式展示的代码城市中的中心地标非常直观——最大、最高的建筑总是容易追踪的焦点。因为这个极具特色的创意,CodeCity 获得了 2008 年的"Riconoscimento ated-ICT Ticino" 一等奖,同时也可以免费用于非商业的科研和学习用途。</p><p>今天要展示的 GoCity 是 Go 语言版本的代码城市,我们可以通过这种方式评估 KusionStack 等 Go 语言项目的代码复杂度。也可以通过在线的 GoCity 查看 KusionStack/kusion 仓库的展示效果。</p><p><strong>PART. 2</strong></p><p><strong>本地执行 GO 代码城市</strong></p><p>之前的 GoCity 还是在 2021 年 10 月更新的,在最新的 Docker 和 Go1.18 环境有一些小问题,还好 KusionStack 相关同学为其提交了补丁进行了修复(_这也是开源项目的魅力所在,也希望开源社区小伙伴能够参与 KusionStack 的共建_),现在可以执行以下命令安装:go install github.com/rodrigo-brito/gocity@latest。然后通过 gocity open 打开 Github 或本地仓库。</p><p><strong>- 比如打开本地的 KusionStack/kusion 仓库</strong></p><p><img src="/img/remote/1460000042407998" alt="" title=""></p><p><strong>- 然后浏览器打开对应页面</strong></p><p><img src="/img/remote/1460000042407999" alt="" title=""></p><p>本地执行一切正常!</p><p><strong>PART. 3</strong></p><p><strong>Go 代码城市一键上云</strong></p><p>作为一个类似数字城市的应用,在云原生、元宇宙等背景下,部署上云也是一个自然的需求。同时我们也希望通过 GoCity 展示下 KusionStack 的基本用法。在 GoCity 上云之前,我们先尝试如何本地执行该应用。</p><p>相应的容器镜像已经推送到 Docker Hub<a href="https://link.segmentfault.com/?enc=6eOQ918H4P4tMbHaiSV6Gw%3D%3D.1yncE0JFyAfb7NIbNjHDpw0E3x%2BkDZUU03TkhTeM5bgPDS2eZt4i41UmbAxt81P%2B" rel="nofollow">https://hub.docker.com/r/yuanhao1223/gocity</a>,运行命令如下:</p><p><img src="/img/remote/1460000042408000" alt="" title=""></p><p>运行成功后,可打开本地地址<a href="https://link.segmentfault.com/?enc=EPtUtLMM69NwqGBuisos1Q%3D%3D.B0sNistXJPJSnixRAXfGIdEnFs5xDgaxWUDLBGc%2BPNo%3D" rel="nofollow">http://localhost:4000/</a>查看 Go 项目的数字城市 3D 效果。</p><p>容器化成功后,现在准备上云。从本地执行容器的方式可以看出,想要在 Kubernetes 部署,至少需要 Deployment 和 Service 两种资源。其中 Deployment 用来部署 Go 代码城市,Service 暴露端口,访问无状态应用。</p><p>首先参考安装文档<a href="https://link.segmentfault.com/?enc=otC%2BXgD8fpjeaALHgXLqLA%3D%3D.StzRgkb69f1uuHSKEt5es0kZhfxejTYy5ly%2BKD1ZRx8mHegz3WJLIpRgY%2BK8wD8FktbvQNQRZU2RO8w%2B5fY5rg%3D%3D" rel="nofollow">https://kusionstack.io/docs/user_docs/getting-started/install/</a>安装好本地 Kusion 命令,然后通过 kusion init 的在线仓库提供了相应的模板。Kusion 命令支持一键初始化配置:</p><p><img src="/img/remote/1460000042408001" alt="" title=""></p><p>输出类似以下信息:</p><p><img src="/img/remote/1460000042408002" alt="" title=""></p><p>为了方便展示,Kusion 模板已经内置了 CodeCity 的例子。其中 code-city 模板依赖 konfig 大库中抽象化的前/后端模型,code-city 模板无依赖,可以自闭环。我们选择后者:</p><p><img src="/img/remote/1460000042408003" alt="" title=""></p><p>初始化过程中,指定了容器镜像,并且容器端口和 Service 端口均为 4000,现在进入配置目录,目录结构如下:</p><p><img src="/img/remote/1460000042408004" alt="" title=""></p><p><strong>- 完整的代码可以参考 :</strong></p><p><a href="https://link.segmentfault.com/?enc=MiNyIB6kqG0zr4mx62fPOQ%3D%3D.QP3SvSMq0Oo1S%2FhbVna%2BZ1apDSmI6SRUN9znknt%2F3Utk5CWHtZiOSo9tL7iHhxbeCZ4otWRSN4Hgo2o1Y4QQQ9hsRaAEwE5myKaq83iBOMw%3D" rel="nofollow">https://github.com/KusionStack/kusion-templates/tree/main/code-city-demo</a></p><p>为了方便本地测试,可以通过 minikube start 本地启动 MiniKube 服务。然后命令行模式切换到 code-city-kcl 目录,然后执行 kusion apply 命令生效配置。到此,开始正式上云:<br>kusion apply main.k</p><p>输出类似于:</p><p><img src="/img/remote/1460000042408005" alt="" title=""></p><p>检查 Deployment 的状态:</p><p><img src="/img/remote/1460000042408006" alt="" title=""></p><p>输出类似于:</p><p><img src="/img/remote/1460000042408007" alt="" title=""></p><p>使用 kubectl 端口转发:</p><p><img src="/img/remote/1460000042408008" alt="" title=""></p><p>访问本地地址<a href="https://link.segmentfault.com/?enc=I4uWj2EtKOC2L6iBiseFsw%3D%3D.Z1fmc%2FG7PGZpvzwtX%2FZjeYpPng1NKPkS344vfy0fGqw%3D" rel="nofollow">https://localhost:4000/)</a>,点击 Example 处的链接 “KusionStack/kusion”,可以看到和本地执行一样的效果:</p><p><img src="/img/remote/1460000042408009" alt="" title=""></p><p>至此,完成了 Go 代码城市的一键上云。有兴趣的读者,可以基于模型库 Konfig,选择其他模板,探索 KusionStack 支持的其它运维场景,下面我们将探索代码城市内部的原理。</p><p><strong>PART. 4</strong></p><p><strong>认识数字城市中的建筑含义</strong></p><p>说实话代码城市第一眼看上去更像一个电路板,要理解其中的含义需要了解几个基本的参数映射关系,如预览页面的右下角图所示:</p><p><img src="/img/remote/1460000042408010" alt="" title=""></p><p>以上的对应关系在其官方文档中也说明,如下图所示:</p><p><img src="/img/remote/1460000042408011" alt="" title=""></p><p>其中地面的粉红色表示 Go 包对应的目录(_因为包的依赖关系可能再产生叠加_),灰色表示目录内部的文件,而蓝色表示结构体。其中表示文件的灰色建筑物的大小即文件的大小,表示结构体的蓝色建筑物的高度即方法的数量,建筑物的长宽表示结构体中属性的数量,蓝色颜色的深度表示相关代码行数。</p><p>我们可以选择 DiffOptions 结构体对应建筑物查看其相关的属性参数:</p><p><img src="/img/remote/1460000042408012" alt="" title=""></p><p>可以看到该结构体中有 15 个属性、3 个方法、共 156 行代码。通过点击其中的 “Github 链接” 按钮可以跳转到对应的位置:</p><p><img src="/img/remote/1460000042408013" alt="" title=""></p><p>因此通过这种方式我们可以很容易查看全局有无特别高大的建筑,从而判断是否存在某些文件和结构体的代码需要改进。可以说 GoCity 是一个很有趣的代码分析工具,甚至可以集成到 Github PR 代码评审流程中。</p><p><strong>PART. 5</strong></p><p><strong>分析 GoCity 的代码架构</strong></p><p>GoCity 代码架构主要分为代码数据提取和前端模型展示两块,如图所示:</p><p><img src="/img/remote/1460000042408014" alt="" title=""></p><p>首先 Codebase 表示要展示的代码,通过 Git Service 被拉取,然后通过 Parser 和 Position 服务提取得到对应的参数信息,然后通过前端展示。Go 语言代码主要集中在模型数据提取部分,而前端展示主要为 JS 等实现。前端展示资源文件通过 embed.FS 内嵌到程序中,GoCity 命令启动 Web 服务展示页面。代码架构比较清晰,也是一个比较理想可用于 Go 语言学习的开源项目。</p><p><strong>PART. 6</strong></p><p><strong>展望</strong></p><p>我们通过 KusionStack 的方式,配合少量的 KCL 配置代码,完成了 Go 代码城市一键上云的操作。虽然云上的 Go 代码城市和本地的版本看不出什么区别,但是云上程序的整个生命周期管理将大为不同。在后面的例子中我们将展示如何通过 KusionStack 结合 KCL 配置语言进行 IaC 方式的云原生应用的运维操作。感谢关注🙏</p><p><strong>参考链接</strong></p><p>● <a href="https://link.segmentfault.com/?enc=Kx%2B2IwH%2FpXEu9nrH80Nz%2BQ%3D%3D.%2BSAZ7bJUsQNuujespVLQEo0dnYzAPd3GHgOhfYZ4eq8DAgiAKbRAVPkr8wCbVZBo" rel="nofollow">https://github.com/KusionStack/kusion</a></p><p>● <a href="https://link.segmentfault.com/?enc=QpvqgSg9skeHZqu0YEOYeA%3D%3D.CM%2FhJfw8wLDc19PnAPnONbEJyGWDAEANwpyqegRCH9G5nnByGN43%2F%2FhaP1d2DI33" rel="nofollow">https://github.com/KusionStack/examples</a></p><p>● <a href="https://link.segmentfault.com/?enc=78Cp9y6rf4T4Hkt3UAamQQ%3D%3D.OfZ00l2KSQjjb67UC8MHNX9c4DCdhqAczs%2FqO3m8PFnFfRXd75pMv%2Bxr%2B%2BnlQ11V" rel="nofollow">https://github.com/rodrigo-brito/gocity</a></p><p>● <a href="https://link.segmentfault.com/?enc=hKB3WIgbE1STVsEVAzyWxw%3D%3D.hRVFfm7ESuXw%2FJRtuN5aEu7VIUZQjimpH5lN8LCnZnzZzM2JpwgYIvhWHS55u8RF" rel="nofollow">https://wettel.github.io/codecity.html</a></p><p><strong>了解更多...</strong></p><p><strong>KusionStack Star 一下✨:</strong></p><p><a href="https://link.segmentfault.com/?enc=vPOzdv9KZnu%2FAqr%2F1JCsaw%3D%3D.l1aDsvPWqIqhmVtahChrFOewlLwKMwX6BFSO2QLPH9Wq45HV0XkCVcghPXQDx98J" rel="nofollow">https://github.com/KusionStack/Kusion</a></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=Pd64jhis5X2z6buGeHWz5Q%3D%3D.CLGFD%2FqdDL0FHF6MldImG409nXW%2FmANsKGQF3yDwV6dvg3FKFHjuJsMr8LXGi8Eqvm3eDt6gd6Gv%2FP7RsEaTAdUESlLuoSAdDk9sIAcU4aU7pqMl4zIiniBYFFuQPkzCO79fyl%2BPXOTGHfFfIeroMDPrSeeSrqJPnEpL%2BAUmpHIffN0sdNlumZF6S6EmiBeQqwz0V6HMIPzcp6Fc4ayu3BaEja6f745nq2cAIwFG64wNAVLUAEmO2osKS1JSB%2F49FZhokMSJ6IOxjgCTzmhyrOqPLisvm%2BS9qlaVO8XTjNQ%3D" rel="nofollow">KusionStack 在蚂蚁集团的探索实践 (上)</a></p><p><a href="https://link.segmentfault.com/?enc=FiZrCBmvTf6%2Fj5lHBXAbDw%3D%3D.CcYyXxpHYcmt0YchjIljmTmfgjXPExuNmQ95gWSJumcLBSge%2Fcls3QARsYSxEFKF2udqeUzgDcVyi7QmW3hud4sNS6Z9Rx4tlGXrhEEXurdosJc2XFLa97o3KCcPaYb3tJg%2FDW0Z3RjgVCioE6Q6IlK17pbAz5BFV1T8yGBXjHOWeAiqcG0jq7oohFHBypNT4Vpn6Sj1iwio94yuh%2FXCors4zPSmeCUvHflaKaIhX7h77S6lAllBe7FVkTRquw4nc0x%2FUawoWXsiECAqKfV5jCILDlFxKbhHzkj8JfpTJAo%3D" rel="nofollow">Kusion 模型库和工具链的探索实践</a></p><p><a href="https://link.segmentfault.com/?enc=6Ig2uJglKmrZlIeub%2FQ0IA%3D%3D.EuF3%2FG3y4%2Bl9TioH01dm4C6m0Twrs4RmW4vYxjefM8PA1eM5XVxvKnRJn9Yxe5wQkf6AJVD0n%2FAeVGGa9BoakmQaCfoDhi5zrMII9IdOOQj%2B7JuVQhl%2BTdnAeFa5NP1AMpYwECOOd8k9OSyvpk0K2szyzOlTIpmXaBqjvQvZ2AWHfjBLQmZTdfhS2lltRoJgcjaTmrcLsrQmlE%2Bnz1aqiouRVUCEGSsn3RcI%2B63XTRVocs08xSR4wGSqbp2bmiJXuA4iTNsT8YVzQ%2B0ZUYVNcB4yrBnFMpn4ZZfKZO3UJUQ%3D" rel="nofollow">精彩回顾|KusionStack</a></p><p><a href="https://link.segmentfault.com/?enc=wLPeK5kWPgunR4eCd09VGw%3D%3D.umKddd6dLcnN5w4kr1Pm35cyaD51St3C4f%2BFS41gw4h9vkT2zUnl4N6zwUs%2BtGpg0kh4jypCd3fCMmddCxYjwe2bku6GtAp1UtdlHDDEz6C8I0gAsJbeCKUxtbk6FJzLyM%2FvtdXZpf1uXXUOfVHUsWZ7oRVnARuTAHj%2BClcstQkzg%2FXCVdC6AAwPS20ivLQpSK%2Fqi%2B2%2BTZjzBdr6dPOS%2FD3SrDKSQBF4yqBBRRW4ufme1UStPbxUSvagV4g4aU73Ja2SyRurCBd6BeDqRHatJfBNR1Ro4qOjPQJ%2BJWVJypk%3D" rel="nofollow">历时两年,打破“隔行如隔山”困境</a></p><p><strong>欢迎扫码关注我们的公众号</strong></p><p><img src="/img/remote/1460000042332190" alt="" title=""></p>
KusionStack 在蚂蚁集团的探索实践 (上)
https://segmentfault.com/a/1190000042368378
2022-08-24T12:13:52+08:00
2022-08-24T12:13:52+08:00
SOFAStack
https://segmentfault.com/u/sofastack
0
<p><img src="/img/remote/1460000042368381" alt="" title=""></p><p>文|史贵明(花名:莫城)</p><p>蚂蚁集团技术专家</p><p>蚂蚁集团多云配置管理系统技术负责人</p><p><img src="/img/remote/1460000042368382" alt="" title=""><br>云原生基础设施领域,容器服务、配置管理、IaC、PaC、GitOps 等方向</p><p><strong>本文 2369 字 阅读 7 分钟</strong></p><p><strong>背景</strong><br>KusionStack</p><p>要讲 Kusion 在蚂蚁集团的实践,我们首先来了解下蚂蚁集团在此之前的配置管理状况。</p><p><img src="/img/remote/1460000042368383" alt="" title=""></p><p>如上图所示,图中展示的是在结合 Kusion 之前的应用基线配置管理系统。这里提到的 “应用基线配置” 非应用动态开关,而是注入应用依赖的软件版本、中间件配置、网络数据库等基础设施配置。</p><p>从上图可以看到,应用基线管理系统是标准 BS 架构,对用户提供 Console 和 API,<strong>历经 6、7 年发展历史,承担起蚂蚁集团历史上绝大多数应用基线配置需求,</strong>功能丰富且拥有极其复杂的配置计算能力。目前已经支撑了 15000+ 应用、近 400 项应用配置、50+ 全局配置。</p><p>在这个架构下,最上层用户通过表单或者集成系统 API 来与系统交互,以 RDBMS 为存储介质,将应用的配置以类 Key-Value 形式存储。能力层主要包含通用的角色管理、认证鉴权、版本与配置审计等通用能力,还提供模板化的方式来计算应用配置,如将 Deployment 模板化,最终将用户的基线配置渲染为 Deployment,同时模板与基线配置都存在非常复杂而又灵活的继承能力,举个例子,可以给应用配置 Zone_(逻辑机房)_级别的基线,也可以配置环境级别的基线,或者应用级别的基线,前者可以继承后者,就像子类和父类的集成关系。</p><p>除了应用本身的基线配置,同时也管理了全局配置,如全局的 DNS 配置、Load Balance、网路配置等等。这个架构非常经典,并且有效支持了历史上各种配置需求及各种 618、双 11 等场景,这些都是毋庸置疑的。<strong>但是随着蚂蚁集团云原生化进程的推进,上面的经典架构也逐渐出现一些瓶颈。</strong>不知道大家对于这种架构的配置管理,或者架构有没有遇到这样的问题?我来举几个例子:</p><p>● <strong>灵活性:</strong> 业务越来越多,应用的基础设施配置也更加的灵活,各种定制化需求越来越多,原有架构主要解决标准应用的场景和通用场景;</p><p>● <strong>开放性:</strong> 基线系统的核心能力主要代码在 PaaS 同学这边负责,对于多种多样的需求需要内部排期支持,开放性不足,无法复用和沉淀强大的 SRE 团队的经验;</p><p>● <strong>透明性:</strong> 配置计算黑盒,很多配置的计算逻辑都 hardcoding 在代码中,一个配置的变更最终会影响什么、影响有多大无法确定。比如修改了全局 sidecar 版本,导致线上应用批量异常。</p><p><strong>业界对标</strong></p><p>带着上面这些问题,我们在业界做了一些对标和学习:</p><p><strong>1.</strong> 在 Google的《The Site Reliability Workbook》这本书中,Google 同学从自身的实践中总结出一些常见问题,其中非常重要的一点是:<strong>在做配置管理过程中,没有意识到,大规模配置管理问题的本质是编程语言问题。</strong>配置需求的声明、校验都可以通过语言来解决。</p><p><img src="/img/remote/1460000042368384" alt="" title=""></p><p><strong>2.</strong> 从 Google 自身的实践来讲,K8s 是基于 Google 多年大规模集群管理经验贡献的开源产品,但是其内部主要使用的是 Borg,Borg 团队是在 Borg master 之上研发的,Borg 接入的三件套分别是:</p><p>● <strong>BCL:</strong> 用户通过编写 BCL 代码实现对基础设施需要的配置;</p><p>● <strong>Borgcfg:</strong> 通过 Borgcfg 将配置执行到 Borg 集群;</p><p>● <strong>Webconsole:</strong> 通过 Webconsole 查看发布情况。</p><p>经过调研,我们了解到 Google 大量的运维能力、产品、质量生态都是基于上述三件套演进多年。</p><p>基于上述的一些总结,我们推演出类 Borg 的思路来解决蚂蚁集团的基础设施配置管理,我们尝试用语言和工具及服务实现蚂蚁集团下一代配置管理架构。</p><p><strong>下一代配置管理架构</strong></p><p><img src="/img/remote/1460000042368385" alt="" title=""></p><p>在这个新的架构下,我们可以看到整体架构不仅仅是一个简单的 BS 架构,配置的用户界面也从浏览器 Form 表单演进为中央开放配置大库。而配置大库所使用的就是 Kusion,Kusion 的用户使用前面的同学已经讲过了,对于配置大库本身的技术细节我不做过多的展开,这里强调的是大库在设计上支持多站点交付的架构。</p><p><strong>新配置管理架构主要分为以下几个特点:</strong></p><p>● 基于配置代码化理念抽象建设统一的应用配置模型,沉淀可重用模型组件,实现配置代码一次编写多站点可迁移。抽象领域模型:Stack 是配置的最小集合,Project 是一组 Stack 的抽象,不仅囊括 App 的应用基线配置, 也支持其他如 DataBase 配置、负载均衡配置,甚至 Network Policy 等非应用配置。</p><p>● 通过策略控制器机制,创建与组织独特的安全性,合规性和治理要求相对应的防护规则。</p><p>● 声明式自动化,持续监控运行状态并确保符合 Git 中定义的期望状态。</p><p><strong>应用发布案例</strong></p><p>接下来结合一个具体产品案例做阐述,在这个案例中以应用迭代发布为例:</p><p><img src="/img/remote/1460000042368386" alt="" title=""></p><p><strong>1.</strong> 用户在业务迭代中,修改业务代码、提交代码、CI 测试、构建镜像,并将构建的镜像推送到远程镜像中心。然后通过配置管理服务层——这里是 auto-image-updater 组件——自动将配置更新到配置大库对应的配置文件中。</p><p><strong>2.</strong> 触发大库的变更扫描、测试、审核等一些列的质保手段,同时触发一次应用发布流程,应用发布流程是具有风险体系的、可视化的发布流程,包括推进流程要从预发、仿真、灰度逐步推进,最后进入生产环境。</p><p>在每个推进阶段,需要从配置大库获取到配置代码并同时使用配置管理服务层获取 KCL 的编译结果,也就是 Spec 模型,然后通过产品化方式将 Spec 与生产环境中真实的 Runtime 进行“Live Diff”以供参与人更好地识别变更内容和变更范围,然后以分组发布等具有风险防控的手段变更到对应环境,如 apply 到 K8s 集群。</p><p><strong>3.</strong> 我们看下过程中的具体可视化产品,可以看到发布进度、应用配置的 Diff,以及可以看到历史版本的配置。</p><p><img src="/img/remote/1460000042368387" alt="" title=""></p><p><strong>问题与展望</strong></p><p>回顾下我们开始提到的几个问题:</p><p><strong>1. 灵活性:</strong> 即对各种灵活定制化需求的支持; </p><p><strong>2. 开放性:</strong> 通过 KCL 语言及开放配置大库,用户的基础设施配置通过新的用户界面即可自主完成,不需要等待配置管理平台开发人员进行开发;</p><p><strong>3. 透明性:</strong> 变更过程可以通过产品化的“Live Diff”来识别变更风险;</p><p>我们通过上述的一些探索,一定程度上解决了蚂蚁集团在推进云原生进程中的问题,过程中也遇到了方方面面的困难,比如如何从老架构切换到新架构?架构代际演进时新老系统并存问题是必须要解决的,可以通过如双写等方式解决。<strong>在新架构下更值得探讨的有如下问题,全局配置如何管理以及如何更好的扩展配置的维度。</strong></p><p><img src="/img/remote/1460000042368388" alt="" title=""></p><p>站点的全局配置,在老的基线配置的全局配置不仅仅是简单的 Key-Value, 在使用上是非常复杂的,比如 DNSConfig 配置,按照租户、环境、Zone 等做了不同的优先级编排,对于这些的描述比较复杂,使用 KCL 描述如此复杂的配置很困难。</p><p>针对于配置的继承和扩展,以 APP 基线配置为例,目前更多的是支持应用和应用环境级别的配置,针对 Zone 等细粒度的配置需要在 KCL 代码中通过写 if else 来实现,这对于其他粒度的扩展及通过 API 自动化都带来新的难题。</p><p>对于这些问题内部有一些方案,期望在后续的开放性讨论中与大家持续交流。</p><p><strong>相关链接</strong></p><p><strong>Kusion 工具链和引擎:</strong><br><em><a href="https://link.segmentfault.com/?enc=ka4nzOmCmAxDr0FHgUl5Qg%3D%3D.akrX785gcm0HtDC%2FuYDbHqczfYzsz9ojxwZ34axSDd6KABkFy24OtUV%2FpAdScnZT" rel="nofollow">http://github.com/KusionStack...</a></em></p><p><strong>Kusion 模型库:</strong><br><em><a href="https://link.segmentfault.com/?enc=seZVsp9Psioh0BNKoXY%2FXg%3D%3D.ryKL9x8qg2VICQ67HZ7ZjGKpX71DWiiYdlsFpRzyRRki8OzF9Z2iPljg3zbQpeDc" rel="nofollow">http://github.com/KusionStack...</a></em></p><p><strong>Roadmap:</strong><br><em><a href="https://link.segmentfault.com/?enc=VkuDH7R1ndgSCsaCStyUmQ%3D%3D.MrNdtzf7bHBRikSLigKn3dnvlavzEW2Jey6YAQqJRV4PkuAr5BUnXVA7vQX9LscGNobMP1pbXwys7uTzfdF%2FWw%3D%3D" rel="nofollow">http://KusionStack.io/docs/go...</a></em><br>_</p><p><strong>了解更多...</strong></p><p><strong>KusionStack Star 一下✨:</strong><br><em><a href="https://link.segmentfault.com/?enc=aFbd%2Bnqq4Fo%2Bt%2Bxge0Qe7w%3D%3D.XF%2FT%2Fs82si5HB6%2B8AjplspbNE%2Bzv6J7QdWozIGjN8CMOl3eP%2FBZKX0DV0Bavl8nR" rel="nofollow">https://github.com/KusionStac...</a></em></p><p><strong>本周推荐阅读</strong></p><p><a href="https://link.segmentfault.com/?enc=ujzJGL608krgRsRO9i6uKw%3D%3D.Y%2BlsakgYARxaBAHb%2BRXMcAwFQYUmGVnPoM3zagFBZ2esvHUYieeql%2B0Qh3ew5AjodwayPBwdNIHtmXTRCscX82uejUcQT6CX6n9Th8%2FGdtKY22OSramZdDj8UvDka5JYAFNqJWbzR6JNhCaE6EsXiGirijUuYrL5EFQt3dZf4UCAXQERp4Mwez2KQWoTJT104Xb1ECnl198et0AXNqacXaKz30dRb760d2Uih1xCrWT27xPdJIpflVuZJdKMSnoHSE59SgCSL7UIpDSIBQ0gUOOFS6QJSLmiYsMs5QdAdhM%3D" rel="nofollow">KCL:声明式的云原生配置策略语言</a></p><p><a href="https://link.segmentfault.com/?enc=OLZjjZIiv8FRfxZo4BRBng%3D%3D.WHsDXdr%2FdmRz%2BTvShdruo%2F7neLFlQcGjQURehhFEXsvu4UyX%2FK3n7pXbJUeQdJO3cbCS3Q1vNzA5YyAY6CoUtm5num2lbyxW9O52cWmXA14VsDicohHyEuwCtnYAdLleAEeq1%2Fbo0FICX4xExIg11PjEkRK0q29VcCKQ54X4%2BxFWpB98rmjBiIedTC8wOBycRUE6SwwNoaFPfrZyTXR6xoYlU1CYaBqRCkJ6ALMbKLUwcDJqbE40UQWxorTLoTc0HEn7FG8%2FmQ%2Bkt03lD2xooahlDz2i012Vvp6dx7H4KNegKUGY2%2BWbOHOu91fAmnfUsvVMceZE6sZswQRlq8IH6A%3D%3D" rel="nofollow">KusionStack 开源|Kusion 模型库和工具链的探索实践</a></p><p><a href="https://link.segmentfault.com/?enc=v7Ie2VvVLn%2BLlT%2BKQfuYRw%3D%3D.PFKMYO7GCU6nR87hUAjZNjvQ9hkNqwDXK4x0DKOnhGNZR9RQjjd70nEBS2WGlwWATLJKzBnUBpXA6ZUJ%2FLw%2FoIAoSXVozdQNF4PSDo3lPalyN1dEtU8U1vAyyWTMBEBfOIFBDaoMO4muOJflz1ZttNeDKWAe3PRcmxpjy3J50vwbTqVvKdciIxYGodoC%2Ba42EUrqOYPpuyzBfrrDia8ntidBB8oxPNtyGcKLwfS3hkelsMlrAdWpUuaWy89QqvAZ56xjbL0SgP%2BsJ52ILm70TZxddZ%2BHRO3hDKcGAbCp94YD9uNxCEe%2F%2FWZnUvTmi0K1D%2F9Tpp4q%2FBHDxluCrg4FWw%3D%3D" rel="nofollow">精彩回顾|KusionStack 开源啦~</a></p><p><a href="https://link.segmentfault.com/?enc=MWlhHaXTcEp%2FzaxyPF2ghQ%3D%3D.N6d%2BE0V2U97NdbwlKTnpsr0eniYhSHvi0tuIUvyO4hyqfK5w2ZMtu789etrLCNrnInz6qSgiRm8fZ9%2F5gOIl77G1oxfJnoVUjQ1Q%2B7feVNfk3od0k4keWsiFcB5Asz7CMSN54TUQnAUiO9EnyWiUPphqOy245wo%2FbJRWURoHy9TkA%2Bn6SGvdM1zf1FZlwyjlznY3F9UrHlf4XrVnGmQE3UUxzLpwjjFW6gAxhe%2FnDJxZjWgdAsYR0iZaj28G2KuHQyVHyjvZTNACWCX1zQI6mqzSirPvJDxZkOHSBmBiNccA2RNIVfLYr5eN%2FA%2FBEaahhfhDxx1Zb3%2BMzXbXrbrn%2Bw%3D%3D" rel="nofollow">KusionStack 开源有感|历时两年,打破“隔行如隔山”困境</a></p><p><strong>欢迎扫码关注我们的公众号</strong></p><p><img src="/img/remote/1460000042332190" alt="" title=""></p>
Seata-php 半年规划
https://segmentfault.com/a/1190000042332183
2022-08-17T10:57:10+08:00
2022-08-17T10:57:10+08:00
SOFAStack
https://segmentfault.com/u/sofastack
2
<p><img src="/img/remote/1460000042332185" alt="" title=""></p><p>文|</p><p>赵新(花名:于雨 ) :蚂蚁集团 Seata 项目开源负责人、开放原子开源基金会代码贡献之星</p><p>郭成(花名:星北 ) :Seata-php 项目共同发起人、蚂蚁集团技术专家</p><p>刘岳健:Seata-php 项目共同发起人、Hyperf 开发组成员、广东快客电子商务有限公司高级后端工程师</p><p><strong>本文 5894 字 阅读 12 分钟</strong></p><h2>导语</h2><p>通俗地讲,seata-php 是 Seata 的 PHP 语言实现,它实现了 Java 和 PHP 之间的互通,让 PHPer 也能使用 seata-php 来实现分布式事务。</p><p>Seata 是一个非常成熟的分布式事务框架,在 Java 领域是事实上的分布式事务技术标准平台。Seata 目前正在构建其多语言体系[1],整个体系包含了目前常用的五大类语言:Java、Go、Python、JS 和 PHP。目前的态势是后四种语言都依据 Seata Java 版本构建起对应语言的实现。</p><p>除了追求 Seata 多语言体系过程中因为开源价值要求构建 Seata 的 PHP 版本这个原因外,作为构建起 Web 1.0 时代技术基础 LAMP 架构中的要角,PHP 语言在电商和金融交易场景下依然被广泛使用。而这些场景对数据一致性要求非常强烈,这是构建 seata-php 最大的诱因,也是其技术价值所在。</p><h2>PART. 1--Seata 架构与多语言体系</h2><p><img src="/img/remote/1460000042332186" alt="" title=""></p><blockquote>图片来自 Seata 官网</blockquote><p>Seata 总体架构由如下角色构成:</p><p><strong>- 事务协调器 Transaction Coordinator</strong></p><p>简称 TC,维护全局事务和分支事务的状态,驱动全局事务提交或者回滚。</p><p><strong>- 事务管理器 Transaction Manager</strong></p><p>简称 TM,定义全局事务的范围,提交或者回滚全局事务。</p><p><strong>- 资源管理器 Resource Manager</strong></p><p>简称 RM,和分支事务在同一个应用,进行分支事务的注册,报告分支事务的状态,驱动分支事务的提交或者回滚。</p><p>从 C/S 通信架构角度来看,TC 是服务端,TM 和 RM 是客户端。TC 与 TM 以及各个 RM 之间使用 Netty 框架进行长链接通信。具体而言,Seata Java 版本的通信协议是在四层 TCP 协议之上又定义了一套私有的二进制双向通信协议,通信框架使用了 Netty。其他四种语言只要依据 Seata 的通信协议标准实现其通信功能,即可在多语言生态体系内任何语言之间进行通信和服务调用。</p><p>三个角色中,TM 和 RM 以 SDK API 形式供上层 APP 调用,而 TC 是独立进程部署,使用任何语言实现都可以。据说懒惰是程序员的第一美德,在 Seata Java 已经实现了 Java 版本的 TC 的情况下,多语言体系内其他语言就没必要再做重复工作,只需要构建其对应语言的 TM 和 RM 的 SDK API 包,与 Seata Java TC 通信即可。</p><h2>PART. 2--Seata 与 PHP 技术</h2><p>分布式事务技术是微服务技术体系的一环,构建 Seata PHP 首先需要选择其微服务技术平台,seata-php 目前使用的微服务框架是 Hyperf。</p><p>PHP 在业界以入门门槛低著称,目前常用的微服务框架有 Laravel 以及在其上构建的 Lumen。Laravel 框架的最大优点就是其生态丰富,各种组件应有尽有,如果 Laravel 可以和 Spring 框架类比,Lumen 就是 Spring Boot。但其缺点是性能堪忧,例如在普通的 8C 机器上,空跑一个只运行 echo 逻辑的 HTTP 服务,其吞吐量仅有 1K QPS。</p><p>Hyperf 框架是近年内出现的由国人基于 Swoole 开发的一个微服务框架,特点如下:</p><p><strong>1. </strong>类似于 Nginx,Hyperf 以多进程形式常驻内存,每个进程内都有一个弹性线程池。正常情况下 Hyperf 收到调用请求后,可以保证 1ms 之内分配服务线程,而 Lumen 的响应时间常在 10ms 左右;</p><p><strong>2. </strong>因为 Hyperf 服务常驻内存的特点,其稳定性好,资源利用率当然比以 CGI 机制运行的 Lumen 低很多;</p><p><strong>3. </strong>Hyperf 的对请求的处理过程借鉴了 Go 语言机制,其 runtime 层面以异步方式执行上层的用户同步调用,相比 Lumen 其吞吐率高而延迟低。例如在同样环境下使用 Hyperf 实现同样的 echo HTTP 服务,可以轻松达到 60K QPS;</p><p>除了 Hyperf 自身稳定性与高性能外,依赖于 Hyperf 服务进程常驻内存的特点,TC 可以很方便的对seata-php 的 RM 发起二阶段事务处理,即作为 Server 的 Java TC 对作为 Client 的 PHP 版本的 RM 发起 RPC 回调。如果使用 Lumen 作为 seata-php 的微服务框架,几乎不可能实现这个技术点。</p><h2>PART. 3--快速入门 seata-php</h2><p>基于 Hyperf 微服务框架,seata-php 已经实现了 AT 事务模式,并给出了测试用例。本章节的目的是基于现有实现,让对 seata-php 这个项目感兴趣的同学能够快速入门 seata-php。</p><h3>3.1--搭建 PHP 开发环境</h3><p>使用 Hyperf/Box 这个工具能够快速创建开发环境,并且能够与其他自建开发工具链隔离,避免污染日常的开发环境。</p><h4>3.1.1 下载 Hyperf/Box</h4><pre><code># Mac
wget https://github.com/hyperf/box/releases/download/v0.0.3/box_php8.1_x86_64_macos -O box
# Linux x86_64
wget https://github.com/hyperf/box/releases/download/v0.0.3/box_php8.1_x86_64_linux -O box
# Linux aarch64
wget https://github.com/hyperf/box/releases/download/v0.0.3/box_php8.1_aarch64_linux -O box
sudo mv ./box /usr/local/bin/box
sudo chmod +x /usr/local/bin/box
# 在 https://github.com/settings/tokens/new 创建 token 后,配置到 box 中
box config set github.access-token <Your Token>
</code></pre><p><strong>注意</strong>:</p><p><strong>- </strong>如果你是 Mac 用户首次使用的话,需要在“系统偏好设置”-->“安全性与隐私”中给 Box 工具进行授权;</p><p><strong>- </strong>已经测试过,X86 的 Box,可以在 M1 版本的 Mac 上使用;</p><p><strong>- </strong>使用 Box 时,创建 GitHub access token 权限需要 repo、workflow。</p><h4>3.1.2 配置 PHP 环境</h4><p>当 Box 下载好后,继续下载 PHP 8.0 版本</p><pre><code># 下载 php8.0
box get php@8.0
# 将 box 设置为 php8.0 版本
box config set-php-version 8.0</code></pre><p><img src="/img/remote/1460000042332187" alt="" title=""></p><h4>3.1.3 下载 composer</h4><pre><code># 下载 composer
box get composer</code></pre><p><img src="/img/remote/1460000042332188" alt="" title=""></p><h3>3.2--运行 seata-php</h3><p>环境搭建完毕过后,找一个目录来存放 seata-php 项目的代码。</p><pre><code># 找个地方创建一个目录
mkdir ./seata
# 进入到目录内
cd ./seata
# 下载 seata 骨架包
git clone https://github.com/PandaLIU-1111/seata-skeleton
# 下载 seata/seata-php 组件包
git clone git@github.com:seata/seata-php.git
# 进入到 seata骨架包内
cd seata-skeleton
# 执行 composer 更新项目内的组件包
composer update -o
# 查看是否与 seata/seata-php 建立软连接
ls -al vendor/hyperf/ | grep seata
# 查看命令执行后是否有以下内容
...
seata -> ../../../seata-php/ // 与 seata/seata-php 包建立软连接
...
# 启动项目
box php bin/hyperf.php start</code></pre><p><img src="/img/remote/1460000042332189" alt="" title=""></p><p>至此,即可看到 seata-php 运行成功,在命令行中可以看到 seata-php 客户端与 Seata Java 服务端 TC 的交互报文。</p><h3>3.3--项目代码风格</h3><p>Seata-php 遵循 PSR-1 代码规范[2]。</p><p>社区提供了一个类似于 Go 语言 gofmt 一样的代码格式化工具——composer cs-fix,具体使用方式是:</p><pre><code># 格式化某个文件
composer cs-fix ${FileName}
# 格式化某个目录
composer cs-fix ${DirName}</code></pre><h3>3.4--测试用例</h3><p>目前,seata-php 仅提供了单测用例,放置在项目 tests 目录中,可直接通过 composer test 命令执行这些单测用例。我们近期就会把这些单测用例配置在 GitHub action 上,用于测试每个提交的 PR。</p><p>下一步,我们会像 seata-go 一样补充集成测试用例,并配置在 GitHub action 上用于自动测试项目的每个 PR。</p><h2>PART. 4--下半年规划</h2><p>Seata-php 目前已有的工作仅仅是迈出了下半年长征的第一步,尚未达到生产可用的状态。下半年的整体目标是:</p><p><strong>1.【事务模式】</strong>对齐将于 9 月份发布的 Seata Java v1.6.0 的 TCC、AT、SATA 和 XA 模式;</p><p><strong>2.【测试用例】</strong>单测覆盖率达 70% 以上,并实现两种模式下的 Seata Java 中已有的集成测试用例的 PHP 版本;</p><p><strong>3.【代码 samples】</strong>实现两种模式下的 Seata Java 中已有的 samples 示例的 PHP 版本;</p><p><strong>4.【文档建设】</strong>构建 API 接口级别的详细说明文档;</p><p><strong>5.【生产案例】</strong>实际生产用户 3 家以上;</p><p><strong>6.【社区建设】</strong>培养 Seata Committer 5 人以上。</p><p>上述目标可以理解为 seata-php 社区的 KPI。为达成目的,有可分为“三步走” 的如下执行 plan。</p><h3>4.1--发布一个可用版本</h3><p>这是第一个阶段。我们计划在国庆节前后发布第一个 GA 版本,详细的技术点如下:</p><p><strong>1. 实现 TM 与 RM</strong></p><p>作为分布式事务的发起方,TM 在与下游的微服务应用在通信时,能够在 HTTP 协议与 gRPC 协议中,传递事务上下文,下游的服务也可以随时加入到事务中。</p><p><strong>2. 实现分布式锁 API</strong></p><p>用于避免业务数据在一阶段与二阶段之间,由于并发被修改,导致二阶段提交、回滚失效。</p><p><strong>3. 实现 TCC 与 AT 模式</strong></p><p><strong>完全实现 TCC 模式</strong>。而 AT 模式依赖于具体的 DB 类型和 DB 版本,我们把 DB 限定为 MySQL v5.7,在此之上支持最基本的 INSERT 与 UPDATE 语句,基本可以完成大部分的实际应用场景覆盖。</p><p><strong>4. 支持注册中心</strong></p><p>支持注册中心的目的,是方便 TM 和 RM 对 TC 进行微服务发现。将会支持 File 与 Nacos 两种服务发现方式。</p><p>优先支持 File 服务发现方式。其好处是,在 K8s 环境下,可以通过环境变量或者是挂载 configmap,实现动态配置,不依赖人力变更。</p><p>其次支持 Nacos 作为注册中心的服务发现方式。目前,国内的阿里云、腾讯云、华为云等主流云厂商都支持 Nacos 注册中心,可以方便广大用户进行服务联通。</p><p><strong>5. 其他</strong></p><p>如自动化的单元测试,集成测试和项目的 samples。</p><p>社区已经将第一阶段涉及到的所有任务都作为 task 发布在 seata-php issue 上,可以方便的查看任务负责人,并及时跟踪项目进度_(直接查看当前进度)_。</p><h3>4.2--技术能力全面对齐</h3><p>这是半年目标的第二阶段。这个阶段产出的版本,将会是一个比较完善的版本,能够覆盖绝大部分的业务场景,降低开发者在使用 seata-php 的门槛与成本。关键技术点如下:</p><p><strong>1. 实现 XA 与 SAGA 模式</strong></p><p>除了补齐这两个模式外,还将继续完善 AT 模式支持的 SQL ,能够做到支持大部分的 SQL 语句。</p><p><strong>2. 支持配置中心</strong></p><p>支持配置中心的目的,是方便拉取事务相关的配置。初步计划支持 File、Nacos、Apollo 三种配置方式。</p><p><strong>3. 支持 gRPC</strong></p><p>计划于 9 月份发布的 Seata Java v1.6,将支持 gRPC 通信方式。Seata-php 在第二阶段也将支持这一 RPC 调动方式进行事务传播。</p><p><strong>4. 其他数据库</strong></p><p>首先支持更多的 MySQL 版本,如 v8.0。并支持 PostgreSQL、OceanBase、Redis 等更多类型的 DB。</p><p><strong>5. 事务异常处理</strong></p><p>提升分布式事务防悬挂的能力,自动处理请求幂等、空提交、空回滚、资源悬挂等事务异常逻辑。</p><p>第二阶段的时间节点的 deadline 大概是在本年 11 月底左右。</p><p><strong>4.3--社区建设</strong></p><p>前两个步骤,主要集中在 seata-php 自身的技术能力建设上。到此,seata-php 在技术上可以认为已经成熟。</p><p>这两个步骤的推进,首先依赖于社区自身的健康发展,毕竟开源项目的事情需要社区同学来推进。当下社区由<strong>于雨</strong>同学负责发展壮大,项目总体由<strong>星北</strong>同学来负责推进实施,目前已有代码 Contributor 4 人。</p><p>当然,我们欢迎更多的同学参与到 seata-php 的代码建设中来。提交 issue 和 PR 时,建议尽可能详尽的描述相关细节。比如:</p><p><strong>提交 bug issue 时</strong></p><p><strong>- </strong>标题可以写:bugfix:NotFoundClass Redis with PHP version is 7.2</p><p><strong>- </strong>内容可以提交 bug 的详细情况、发生现象的详细情况、对应的堆栈信息、预期的情况、以及当前的环境情况、发生的事、修复意见、以及补充的信息、当前的环境情况等信息。</p><p><strong>提交 PR 时</strong></p><p><strong>- </strong>标题可以写:Feature: AT mode need to support pgsql</p><p><strong>- </strong>内容可以写明:这个 Feature 的意义,以及期望的用法,还包括其他相关信息等。</p><p>这一步骤与前两个步骤相生相伴,同步进行。</p><h2>PART. 5--总结</h2><p>Seata-php 有 Seata Java 这个标杆在,初期以推进代码进度为主。</p><p>作为一个开源项目,seata-php 的开源价值当然是在用户的生产环境使用起来,而生产用户也是社区建设的一部分。目前已有两家用户愿意在其开发测试环境对 seata-php 进行验证,帮助提升项目的稳定性、易用性和代码质量。</p><p>为保持项目和社区的健康可持续发展,开源项目的贡献者,不仅包含 coding 的代码贡献者,还应当包括进行文档贡献、产品宣传和品牌推广等方面的贡献者。我们将组织社区热心参与者在各大技术论坛发表博客,在语音、视频网站和技术大会上进行技术干货以及生产案例的推广宣传。欢迎对这些工作感兴趣的朋友加入社区钉钉群<strong> 44788115</strong>,与我们联系沟通。</p><h3>【参考文档】</h3><p>1.《Seata 多语言体系建设》:<a href="https://link.segmentfault.com/?enc=7edRlMW7G6wCLAriALwxtA%3D%3D.HnTSiGVkeGWNl3pkqDRYLFj9mPNQGMTgITUW077ZwOzF9OJu28HtSW0JHXC9l9jfa97GLW%2FZQmIORtN7bNsMVA%3D%3D" rel="nofollow">https://mp.weixin.qq.com/s/UwzscqfuCYtsSdWYj-t-uQ</a><br>2.《PHP PSR-1 代码规范》:<a href="https://link.segmentfault.com/?enc=H61QKNLeIFZUUDx9s6ITZg%3D%3D.xtqLjWmKz%2B%2FHtMnLxnNXBKtmS2%2BXtoE4KbHB6CdAOtlMFEprhGa%2BstvDG%2F4Qm0He" rel="nofollow">https://www.php-fig.org/psr/psr-1/</a></p><h3>本周推荐阅读</h3><p><a href="https://link.segmentfault.com/?enc=0qfonwZYsJxfZbX1FvDEDw%3D%3D.0umizU%2F%2BNd5%2B9FE0UA8HwKXxm%2Fnf71BoCTUq7CDpKpfX%2FQmQNBj52avMDsojPyu94AmZXq7xHBM6fAf3YtX7amxsz9gOQtOYiCcASkcZ6SpnDB11A6XNH%2B48dip3gNdWoXmflj7yiem8tpza8Lu3vnkdAe%2BNJOj1P2Zkz%2FBdxNQEdNkTStF7H6rlIZbQdvzA09GPdzbmNZCMFSLNLx2fhHxorYjo9wUjQyA1qiNiXVS%2BqRQjlKt7B8hrIVn5ZWqhRLXyOXcquXPnWb3f2tlwLA%3D%3D" rel="nofollow">Seata 在蚂蚁国际银行业务的落地实践</a></p><p><a href="https://link.segmentfault.com/?enc=CHPLcpJwKRaq%2B6M2Uw8YYw%3D%3D.%2B385epLijURAwUQlo0wyR8pjyHZzH6XtONAsK8QCAg5o8MolsFxMJqu%2BNMiN2d8wFg42vbZe9MHS9A%2Fgs%2Ffs%2BCQDR671OOx4Fgk7Pb6%2FvG6X2zUQ8aU6OdwVVGw%2F5ND8cUx0dm1sV5qwJTEYdFxXyynP7%2B5qfUs3GxrlVRv2SNv4TWdd%2BWE3RGXyg7XCuOE7GnEcpj8aLSzeCvvS9Enz%2FG2sZmhpiD9%2B9QYKJYUqoArAVaLYsGW8ut2AtZuxX2ZLqTFZ2h59Q2JTBj5iKBzSxw%3D%3D" rel="nofollow">Seata 多语言体系建设</a></p><p><a href="https://link.segmentfault.com/?enc=BIhe3GQ729acU3xPr4oAEA%3D%3D.hnx7zjGmugCGppiiuE0TPBIT1KUllYz80D6fSJ4EqYF27z0h2vWPhfz9BIGvuI7n%2BM5TiXRVdMzPFT1tLB%2BCLtvS5iR%2BhY6Qg0J%2Bfpln5wgItPx8Nx6HikAh9bQeWd1CNyed00SJSjHiT1Q9YMTX5OTcNJBfTZtZGRnsEbgaxe4HEao2hgWiOlDjGD5MauTMC9Gkc0P6u7c4QkRnWLbs2z8GDc%2FI26NGDFB5k6wcRcOD3FiBROHxzvyKp5XztTDNPJEB1fwOi%2BqgtasHcsht0A%3D%3D" rel="nofollow">深入 HTTP/3(2)|不那么 Boring 的 SSL</a></p><p><a href="https://link.segmentfault.com/?enc=Oq6o2KbYtg%2BDtNfnDuTr9g%3D%3D.2MbxCJAamdMn5dz1ME7%2B7Y%2B7l8GnIwAf3TQVuRY05Vjp9vd8vGg3KFhDAsMj2O0TEZnvE%2Fr0Uvjn8QqB5yeGqUi6PDkRo6ORwFRBIjHi1r9zMBJ1MayjZV7ZYxw5CR69BkUvp%2BE65AiAh8fs8JFuF%2Bk0%2FGN1IU9S1BE6qrx7Ka8Egq0RP%2BmlGndVHZJKKOAM86rGeC1uHbKUAiG0DyydAlk6ZGa6VLo19ngGxTMoqt2ukrX6KFouFVCASM6zRwDVa3f9DO6ULk1Xli8jMJ2cLA%3D%3D" rel="nofollow">Go 原生插件使用问题全解析</a></p><p>欢迎扫码关注我们的公众号:</p><p><img src="/img/remote/1460000042332190" alt="" title=""></p>