本文根据作者在 CSDN 云原生 Meetup 深圳站的演讲内容整理,分享云原生趋势下网易数帆在私有化场景下大规模应用的交付实践,包括在实践过程中遇到的问题,如何实现标准化、高效率且高质量的交付方案,以及取得效果。
3. 基于 Helm 的应用封装、交付、升级与环境维护软件私有化交付部署是建立在企业自有基础设施的基础之上的,是为一个企业客户单独使用而构建的硬件/软件运行环境;因而能够提供对数据安全、合规审计和服务质量的有效控制。
软件的私有化是由市场供需关系决定的。也分为甲方和乙方,甲乙双方各取所需从而使面向企业的私有化市场正常运转,例如以下双方的一些诉求:政策性的行业合规和安全化诉求。
企业网络完全隔离限制,不能和互联网建立通讯,无法使用公有云产品。例如金融企业内网。
企业运营的数据具有敏感性,不适合直接运行在公共互联网。例如政府企业沟通交流的工具使用企业微信、企业钉钉等数据要求绝对保密。
头部互联网企业的云技术能力强,投入力度大,产品整体相对较好,期望使用领先的云技术能力。例如期望在内部使用阿里云、腾讯云、华为云以及数帆数帆轻舟或大数据等产品。
传统企业希望进行数字化转型,但是自研体系能力欠缺,从头开发成本太高且周期较长,希望采购相关成熟的产品助力快速数字化转型。
企业有自己的机房和服务器,但资源分散且利用率不高,希望一些私有产品能充分利用这类资源。
业务管理与协同沟通割裂,着力搭建一体化成本适宜、安全高效组织协同管理平台。
缺乏统一标准支撑平台,基础供给能力产品容易重复建设,用户体验参差不齐。
难以快速响应业务创新。多年信息化的堆叠,垂直化的建设导致集成能力和开放能力不适合业务创新环境。
其他由于特殊场景需要却缺失的能力,希望通过合作的方式由其他供应商来协助建设。
通过以上甲乙方的诉求可以知道,如果乙方刚好可以满足甲方的一个或多个需求,那么就有了合作和软件私有化交付的场景出现。是否有遇到过如下场景?
测试环境与生产环境隔离。一些企业由于内部的流程或安全规范的要求,测试环境与生产环境是隔离的状态。例如运行节点间隔离或网络策略的隔离,甚至是物理上的隔离需要更换设备才能访问生产环境,这时在测试环境测试验证的服务如何上线到生产环境?生产环境禁止访问 Git 执行流水线。代码是企业的核心资产之一,保障源代码的安全是企业重点保障的资产;生产环境一般有自己的机房,或者一些第三方的公有云环境,企业一般不会允许将核心源代码暴露到公网上的,这些情况下生产环境是禁止访问内网的 Git 代码托管平台的。而且,在生产环境重新执行CI流水线构建的镜像,并不是 QA 团队在测试环境测试回归验证过的镜像,即使他们的源码相同,但本质上是两个镜像。在 Kubernetes 中部署应用时涉及较多类型资源管理问题。在 Kubernetes 集群中部署服务并不是简单的将 Image 镜像运行起来就可以了,实际上还会涉及一些其他相关的 Kubernetes 的资源,例如常见的资源有 Deployment、Service、Ingress、Secret、ConfigMap、PV/PVC、ServiceAccount、RBAC等等以及其他扩展服务的相关资源,如 Prometheus-operator 的 ServiceMonitor。这么多类型的文件该如何维护管理才能高效且不出错呢?应用上线需要符合公司规范。为了保障上线的顺利,符合上线的质量、安全等要求,企业都会对业务上线制定一些流程或者规范。同时,在多人协作的团队中,如果没有一定的规范参考指导,会因为信息不一致导致应用管理混乱。比如最常见的就是镜像的命名规范,同一个服务可能会出现 myapp:v20211129、myapp:v1.3.11、myapp:1.3.11、myapp:v1.3.11-20211129、my_app:v1.3.11 、my-app:v1.3.11 甚至是 yourapp:v1.3.11 。这些镜像 tag 实际上是指同一个 Git Release v1.13.11 的代码版本,但由于没有规范约束导致管理难,而且容易出错。多部门或子公司开发的产品复用。在企业内部一个部门开发了好的一款优秀的应用,其他部门也希望能够使用这个应用,特别是大型企业有多个子公司时尤为明显。这类应用例如:日报周报系统、设备日志系统、发票管理系统、ERP系统等等。这时如何将这些应用分享给其他部门会成为一个挑战,如果交付过程太过复杂就会导致推广受限,进而在集团层面产生资源的浪费。软件私有化交付到客户环境。如前文提到的 ERP 系统、网易数帆轻舟平台等,这些 toB 私有化产品在交付时会有很多的困难,如何高效高质量的将交付软件到客户环境成为这类企业核心关注的重点之一。本文主要探讨的就是网易数帆轻舟团队在私有化场景下大规模应用的交付实践,在实践过程中同样会涉及到上述的这些问题,通过本文提供的实践给上述的场景提供一些参考。软件私有化交付时一般都不会太顺利,在不同的阶段,不同的角色或维度会有各种各样问题,这些问题有些可能会决定整个项目是否延期,有些问题甚至影响项目能否成功。在项目早期,如果能评估到一些痛难点,提前准备相关的应对策略,将会给产品的私有化交付提供很大的帮助。站在交付方,以私有化交付全局的维度来分析项目交付前后的痛难点,可以分三大类,分别是用户侧、交付侧和工程售后侧。产品功能需求多样。标准的产品不满足企业的需求,需要软件提供商按照企业的需求进行产品的修改或适配,但是跨企业的协同效率相对较低,经常出现返工修改以及环境频繁升级的问题。
资源准备周期长。时间长,或者资源变更修改时流程长。由于企业内部资源申请需要经过多层的审核和审批,如果不符合企业规范要求调整申请又需要从新审批流程,这个周期可能是周,甚至是月。
企业业务上线规范。用户有自己的上线运维规范,交付软件需要满足他们的运维或安全的规范。例如有等保三级,性能指标、安全扫描、上线时间窗口短等等规范要求,但这些在做产品时可能没有全部考虑到。
用户侧的变更。由于设备搬迁、网络变更、设备升级、关机等没有,导致无法展开交付。
没有按照部署规划要求准备资源。讨论好的资源需求申请,由于客户内部的某些原因无法按照前期规划的方案交付,或用户准备的资源环境不稳定。
交付人员要求高。交付过程涉及操作系统、Docker/Kuberentes、交付产品本身以及用户流程和基础设施等方面问题,需要交付人员有较强的技术基础和用户沟通的能力。
测试验证复杂。在部署完成后,为了保障交付质量的,一般都会要求有测试验证的流程,对于自动化测试无法覆盖还需要人工手动回归来保障交付的环境质量。交付产品越复杂,测试验证也就会越复杂。
跨企业协同困难。在项目开始部署前,都会设计部署实施方案并和用户沟通,但由于双方没有对齐导致的方案和资源不一致,现场实施时受阻。即使可以调整方案,但依旧会给项目带来延期的风险。
用户的网络隔离与限制。企业考虑到安全等原因,网络无法访问互联网,在交付实施过程中遇到问题时查找资料,寻求远程协助以及下载更新文件都是受限的。
部署包较大,上传文件时间长。使用容器化私有交付时,一般都是将程序构建成离线的 Image 镜像。Image 镜像即使通过镜像层的复用降低了整体的大小,但由于交付系统本身的复杂导致依旧会有大量无法复用的层,结果就是离线镜像包或部署包比较大。特别是传统企业用户的网络质量和带宽并没有互联网企业那样好,这就导致了项目交付用到的部署包即使用移动硬盘复制到客户现场,上传到实际的部署运行环境依旧会花费较多的时间。
基础设施多样。在私有化场景下,不同的企业客户有自己独有的基础设施。比如不同的硬件,有的客户采购了华为的服务器硬件,有的客户采购了 HP 的服务器硬件,也有的客户自己定制服务器。操作系统也是各有不同,例如在企业内部常见的操作系统有 CentOS/Debian/Ubuntu/Redhat/统信 UOS/麒麟OS等等。CPU 架构也可能不同,有 x86 的服务器,也有 ARM 的服务器。
私有化的产品复杂。如果私有化的产品本身比较复杂,在私有化版本发布时需要真正的能够理解产品并对私有化交付体系有较深刻认识的负责人来把整体控私。否则由于私有化技术选型、版本的管理、自动化的封装、场景的认知不足、准备的材料缺失或者其他考虑不周全导致产品的交付和管理复杂且混乱。
产品本身质量不高。由于新功能的加入频繁的需求变动,导致产品功能不稳定 Bug 数量比较多,QA 的测试把控不到位的话,会导致现场交付时分析定位和解决 Bug 。现场定位解决问题不仅不方便,同样会消耗较多的时间。
文档材料缺失。在做私有化时,建议提供匹配的文档工程师。企业在进行外部采购时,有些材料是强制要求有的,否则不会验收该项目。而且,如果每次用户有文档需求时都是首次准备,质量是否高暂且不说,给用户的感觉也不好,显得不够专业。
项目数量逐年增长。项目数量的增长本身说明产品卖的好,但也会带来运维和售后的成本增长。在合适时间应该做好人力的评估,及时扩充团队和人员,否则会给当前的员工带来较为繁重的工作任务进而导致人员流动较大。
客户环境离线,后期维护难度大。由于网络离线无法及时收取异常告警信息,需要用户收取到告警反馈给售后人员,可能由于技术的差异导致问题定位和修复过程不顺利。由于离线,一些预期内的变更或升级需要出差客户现场,支持的成本比较高。
用户基础设施影响。如果交付的是软件产品,由于基础设施如机器、网络、电源等都是由用户其他部门维护,基础设施异常时,会影响上层的软件的稳定运行。
由于上述的一些原因导致软件在私有化交付时交付周期长,交付质量差、交付成本高。如果成本太高,这个项目可能就由于投入太多而亏损。为了保障软件私有化的正常交付,在进行架构设计和技术选型时应该结合当前主流技术体系,选择合适的解决方案。软件的交付部署有多种方式,按照交付方式可以分为传统基础交付、自动化交付和云原生交付三种。传统基础交付。传统的应用交付是应用交付的基础方式,比如常用的 rpm 软件包或者直接二进制的方式安装运行,比较适合场景相对固定的基础设施。如果有较多的软件包依赖,一般还会搭建一些 YUM 或 DEB 的软件源来快速安装依赖。这类服务的运行一般可以通过 systemd 或者 supervisor 的方式来管理。自动化交付和云原生交付也是在这种方式之上构建的。自动化交付。如果软件数量较多,安装部署过程有着复杂的逻辑时,使用手动 rpm/yum 安装会比较繁琐且易出错。一般都会使用自动化的方式封装,如 shell 和 Ansible 就是主流的自动化工具。云原生交付。由于云原生的概念兴起,加上早期的 DevOps 理念的长期熏陶,应用又有了新的交付模式。在云原生的场景下,使用基于流水线的 CI/CD(持续集成与持续交付)模式成为了新的主流,提供了快速高效的应用迭代节奏。云原生交付运行的环境基本上都是基于 Kubernetes 平台,所以也有一些工具可以直接管理和部署应用,比如 KubeVela 。但是,目前真正在企业内生产环境使用的,并且已经属于 CNCF 毕业的项目只有 Helm 。按照交付部署时是否能够联通各个系统,可分为在线交付和离线交付。在线交付。在线交付是指在交付部署过程中全部或者部分材料运行在其他服务器上,在安装部署时可以通过网络的方式获取到这些数据。比如常见的基于Nginx 的 YUM 软件源,或者企业内部的 Harbor 镜像仓库,内部的 Gitlab 代码参考,或者公网的 Maven 仓库等。在线交付的好处是不需要提前准备准备这些资源,需要什么资源就到对应的服务器上获取即可。离线交付。离线交付和在线交付刚好相反,部署时依赖的资源无法外部获取,需要单独准备依赖的资源。没有 YUM 软件源就本地临时搭建一套或者把离线的软件包以及依赖的软件包都下载下来准备好一起安装。在离线场景下有很多服务需要部署时搭建,这也给项目的交付增加了很多的困难。复杂的客户基础设施带来了复杂的交付场景。如果基于传统和自动化的方式交付,会有一个长长的适配支持兼容清单,例如硬件设备、CPU架构、操作系统等等,这无疑是给企业带来了大量的人力成本消耗。但是,如果选择了云原生的方式交付应用,就有机会将这种差异的场景人力消耗降到最低。基于容器化的交付。面对用户环境的多样,基于 Docker 容器化的方式封装应用能够屏蔽底层基础设施的差异,使交付的制品根据有普适性。基于 Kubernetes 的运行平台。容器的运行调度、故障的发现与自动处理、弹性的扩缩容、简单的网络环境等环境比较薄弱,而 Kubernetes 刚好解决这些痛点问题。使用 Kubernetes 作为容器的运行环境,能大大降低交付和运维的复杂度。基于 Helm 的应用构建与交付部署。Helm 能够管理应用的多个不同的 Kubernetes 资源,按照一定的策略来使其在集群中生效。同时,和 rpm/deb 类似也能标准化定义应用,有 Helm 定义的标准化应用就是 Chart。首选 Helm3 。在已经有 Kubernetes 集群的情况下,只需要一个 kubeconfig 就可以完成环境的交付部署。Helm 的版本选择有 Helm2 和 Helm3 两种。如果您是新手,当看到这篇文章时,强烈建议您直接选择 Helm3;如果您已经使用了 Helm2,也同样建议您尽快升级到 Helm3。Helm3 不仅在架构上简单了很多,在功能上,使用和易用上都有了很大的优化。Helm3 于2019 年 11 月 13 日发布。Helm3 公开发布 12 个月后,对 Helm 2 的支持正式结束。当使用 Helm Chart 来定义云原生应用时,应该也很希望类似 RPM/DEB 软件包一样可以定义一定的规范标准。幸运的是 Helm 也是支持的,Helm 定义标准分两种,强制标准和推荐标准。强制标准是指在 Helm Chart 的目录结构上,有一些文件名称放置的位置,内置的函数或变量,模板的渲染方式和资源的优先级等是必须准守的,否则 Helm 无法正常的工作。比如 values.yaml 是默认的参数配置文件,templates 目录是 Kubernetes 的资源模板目录等。推荐标准是指在强制标准基础之上,企业根据业务需求和管理规范来定义的 Chart 标准。例如应用的命名规范、Kubernetes 资源文件命名规范、环境变了的命名规范、安装升级的逻辑规范等。对于强制规范是必须遵守的,否则 Chart 无法正常工作。而对于推荐标准是通过人为约束或定义脚手架的方式来管理。新应用使用脚手架的方式创建手,就已经是符合标准的应用。1.定义变量:export XDG_DATA_HOME=/root/.helm2.脚手架路径:/root/.helm/helm/starters/chartstarter3.新建应用:helm create --starter chartstarter myapp即使在后续的迭代的过程中,应用的一些配置和标准偏移了,也同样可以使用自动化的 Check 的方式来扫描是否违反了标准。在云原生场景下的软件包制品常见的有 code 源码、编译或打包的可执行文件、构建的系统软件包(可选,如RPM包)、构建的镜像、以及 Helm Chart 包。最小原子化。在制作 Chart 包时,根据业务的场景特性决定一个 Chart 中包含的业务服务数量。轻舟的 Helm Chart 是基于应用代码仓库作为最小单元,即一个 Chart 中只有一个功能性程序镜像。如果业务系统不是很大,只有几个应用的话,也可以考虑使用子Chart的方式来管理多个服务,如 WordPress Helm Chart。版本可追溯。最小原子化后,在结合一定的内部规范就可以做到 GitRelease,镜像 Tag,ChartAppVersion 一致,部署或运行时可根据环境 Chart 版本快速溯源代码版本。1.Git Release:myapp Rlease/v1.23.6
2.Image: myapp:v1.23.6
3.Chart: myapp-v1.23.6.tgz
Helm Chart 的安装时可以指定多个 values.yaml 文件,命令行参数右边的 values.yaml 内容会覆盖左边 values.yaml 的内容。Chart 中默认的 values.yaml 。values.yaml 只有 myapp 自己 Chart 会用到的配置项 ,配置项尽量以默认的方式配置在values.yaml 中,如默认:1.global.imagePullSecret
2.global.clusterDnsDomain
3.myapp.resources
4.myapp.mysql.dbname
values-global.yaml 。支持所有的配置项,但并不需要所有配置,如环境差异配置:1.global.imageRegistry.addr(project,user,passwd)
2.global.mysql.host(port,user,passwd)
values-myapp.yaml。在 values-global.yaml 定义了该字段,但是因为某些原因需要调整的内容。比如有 10 个应用使用到了 MySQL ,但是其中有9个使用一套 MySQL 集群,另外一个应用单独使用一套独立的 MySQL,这时可以单独给这个应用定义一个差异化的 Values-myapp.yaml 来覆盖 values-global.yaml 中的 MySQL 字段即可。如global.mysql.host(port,user,passwd)
例如 myapp-v1.23.6.tgz Chart 安装命令helm install -n ns1 myapp -f values-global.yaml -f values-myapp.yaml ./myapp-v1.23.6.tgz
Chart 中默认的 values.yaml < values-global.yaml < values-myapp.yaml在单个 Helm Chart 安装时可以使用 helm install 的方式安装,但是如果 Chart 数量非常多,而且有顺序依赖时手动安装并不合适,这时推荐编写一个自动化的工具来管理和安装,可以是 shell 脚本或者安装部署平台。轻舟部署使用的 Sail 系统就是一套自研的私有化交付系统,它能够提供环境的部署规划、配置的渲染、安装逻辑的处理、部署任务的执行以及环境信息的统一管理等功能。软件交付无论是早期的自动化交付还是当今的 Helm 云原生应用交付,带有结构化数据的应用升级都不是一件容易的事情。经常会因为升级时涉及到表字段的更新增大了升级的风险。在云原生应用交付场景下,Kubernetes 资源文件声明式支持,一般不需要特殊处理,Kubernetes 的滚动更新与 Helm 的差异内容更新可以很好的解决。但对于 SQL 的处理,由于涉及到业务本身的逻辑,Helm 作为工具也是无能为力。但 Helm 为我们提供了 Hook 的功能,可以让我在安装或升级的某个阶段中插入一定的任务。对于这个任务可以由业务方来决定如何处理 SQL。SQL文件按照如下规则定义逻辑,Job 使用 pre-upgrade hook 作为升级依据:备份待升级的数据库
获取环境运行中 Release 信息中的 Chart 版本(外部工具获取)
使用 .Chart.appVersion 表示目标版本
获取环境 Release 版本和目标 Chart 版本的缺少版本
判断是否满足升级兼容性
按照版本依次执行 SQL 导入
维护初始的全量SQL文件,如果大于1M就拆分成多个 SQL 文件
每个版本的增量都作为独立的 SQL 文件,SQL 文件规范命名和版本规范保持一致。如 v1.1.0.sql/v1.1.1.sql/v1.1.2.sql
使用 ConfigMap 来挂载一个大版本的全量和增量 SQL 到 Job 容器的固定目录 (如:/data/upgrade/sql/)
那么在升级时就可以通过如下命令来自动升级到目标的版本$ helm upgrade -n ns1 -f values-global.yaml -f values-myapp.yaml -set release.chartVersion=v1.23.1 myapp ./myapp-v1.23.7.tgz
在基于 Helm Chart 的云原生应用交付的模式下,业务环境的定义可以简单理解为就是 Chart列表和部署时的 Values.yaml 文件。按照规范定义的 Chart 列表能够说明环境中部署的产品和组件都有哪些以及使用的版本是什么。Values.yaml 中保存了当前环境中所有的差异性配置,结合 Chart 中默认的配置,就可以还原该环境运行的服务所有的配置信息。在私有化场景下,环境信息维护尤为重要。在无法实时访问客户环境时,部署时维护的 Chart 列表和 Values 文件能够帮助售后快速的定位和解决问题,在进行 bug 修复或版本升级时也能快速的输出升级材料。由于早期 Helm3 还未发布前,轻舟私有化交付是基于 Ansible 的方式来交付的,在越来越多的项目交付起来后,场景的差异也越来越多,兼容适配的成本也越来越高。在 Helm3 发布后,轻舟所有的服务全部进行 Helm 化的改造,以当前的状态和之前相比有很大的进步:单个应用版本发布周期:6个月 ==> 1个月
场景化适配 :无法预测的多种 OS 系统适配 ==> Docker 镜像单次x86/ARM 适配即可
项目交付效率:平均 2周/套 ==> 平均 2~3天/套
交付质量:平均每套环境交付问题数 大于10个 ==> 小于3个
交付的产品能力涵盖云原生的容器云、微服务、服务网格、CICD、API 网关、分布式事务、APM 以及 PaaS 中间件等产品。基于这套私有化交付工程,支持了公司内外大量的私有化客户项目,保障了项目的顺利交付。商业私有化交付时客户环境基础设施种类繁多、场景复杂,基于容器和 Kubernetes 能极大的降低场景适配和后期运维的工作量。非在线场景下 Helm 是当前云原生应用的打包和交付的最佳选择之一。优秀的 Helm Chart 实践经验需要案例积累,并能够传承,推荐基于代码的方式传承经验。作者简介:赵文宇,网易数帆轻舟交付解决方案专家。负责网易数帆轻舟项目交付和解决方案相关的工作,从0到1构建了轻舟私有化交付体系,给客户提供从项目规划到交付落地的技术支撑。对云原生场景下组件高可用方案与应用打包交付有着丰富经验,对轻舟生态技术体系有着深入的理解,熟悉云原生场景下软件私有化交付模式。