网站建设属于前端还是后台,百度企业网盘,网站开发绩效考核与薪酬,wordpress博客非插件早在 2011 年#xff0c;阿里巴巴内部便开始了应用容器化#xff0c;当时最开始是基于 LXC 技术构建容器#xff0c;然后逐渐切换到 Docker#xff0c;自研了大规模编排调度系统。到了 2018 年#xff0c;我们团队依托 K8s 体系开始推进“轻量级容器化”#xff0c;同时投…早在 2011 年阿里巴巴内部便开始了应用容器化当时最开始是基于 LXC 技术构建容器然后逐渐切换到 Docker自研了大规模编排调度系统。到了 2018 年我们团队依托 K8s 体系开始推进“轻量级容器化”同时投入了工程力量跟开源社区一起解决了诸多规模与性能问题从而逐步将过去“类虚拟机”的运维链路和阿里巴巴整体应用基础设施架构升级到了云原生技术栈。 到了 2019 年Kubernetes 基础设施底盘在阿里巴巴经济体中已经覆盖了阿里巴巴方方面面的业务规模化的接入了包括核心电商、物流、金融、外卖、搜索、计算、AI 等诸多头部互联网场景。这套技术底盘也逐步成为了阿里巴巴支撑 618、双11 等互联网级大促的主力军之一。 目前阿里巴巴与蚂蚁金服内部运行了数十个超大规模的 K8s 集群其中最大的集群约 1 万个机器节点而其实这还不是能力上限。每个集群都会服务上万个应用。在阿里云 Kubernetes 服务ACK上我们还维护了上万个用户的 K8s 集群这个规模和其中的技术挑战在全世界也是首屈一指的。 我们的 Kubernetes 面临的新挑战
在规模和性能等基础设施领域问题逐步解决的同时规模化铺开 Kubernetes 的过程中我们逐步发现这套体系里其实还有很多意想不到的挑战。这也是今天分享的主题。 第一个是 K8s 的 API 里其实并没有“应用”的概念 而且Kubernetes API 的设计把研发、运维还有基础设施关心的事情全都糅杂在一起了。这导致研发觉得 K8s 太复杂运维觉得 K8s 的能力非常凌乱、零散不好管理只有基础设施团队也就是我们团队觉得 Kubernetes 比较好用。但是基础设施团队也很难跟研发和运维解释清楚 Kubernetes 的价值到底是什么。 我们来看个实际的例子。 就拿上图中的 replica 为 3 来说开发人员怎么知道实例数应该配几个呢如果运维想要改replica敢不敢改能不能改如果 replica 还能理解的话那像 shareProcessNamespace 这种字段真是灵魂拷问了。 开发人员仅从字面意思知道这个可能跟容器进程共享有关那么配置了这个应用会有什么影响呢会不会有安全问题 在阿里巴巴内部很多 PaaS 平台只允许开发填 Deployment 的极个别字段。为什么允许填的字段这么少是平台能力不够强吗其实不是的本质原因在于业务开发根本不想理解这众多的字段。 所以这个 PaaS 平台只允许用户填个别字段其实反倒是帮助业务开发人员避免了这些灵魂拷问。但反过来想屏蔽掉大量字段真的就解决问题了吗这种情况下整个组织的基础设施能力还如何演进应用开发和应用运维人员的诉求又该怎么传递给基础设施呢 实际上归根到底Kubernetes 是一个 Platform for Platform 项目它的设计是给基础设施工程师用来构建其他平台用的比如 PaaS 或者 Serverless而不是直面研发和运维同学的。从这个角度来看Kubernetes 的 API其实可以类比于 Linux Kernel 的 System Call这跟研发和运维真正要用的东西Userspace 工具完全不是一个层次上的。你总不能让本来写 Java Web 的同学每天直接调用着 Linux Kernel System Call还给你点赞吧 第二 K8s 实在是太灵活了插件太多了各种人员开发的 Controller 和 Operator 也非常多。 这种灵活性让我们团队开发各种能力很容易但也使得对于应用运维来说 K8s 的这些能力管理变得非常困难。比如一个环境里的不同运维能力实际上有可能是冲突的。 我们来看一个例子基础设施团队最近开发上线了一个新的插件叫做 CronHPA一个具体的 Spec 如下所示。
作为基础设施团队我们觉得这种 K8s 插件很简单 CRD 也很容易理解。就像这个 CronHPA 的功能从早上六点开始到下午七点钟这个实例最少有 20 个、最多有 25 个到第二天早上六点钟最少 1 个、最多有 9 个在每个阶段会根据 CPU 这个指标衡量调整实例数。 然而就在我们美滋滋的上线这个插件后不久应用运维同学就开始跟我们抱怨了
“这个能力到底该怎么使用呢它的使用手册在哪里是看 CRD 还是看文档呢”“我怎么知道这个插件在某个集群里有没有装好呢”“我们运维不小心把 CronHPA 和 HPA 绑定给同一个应用结果发现这个应用是会抽风的。为什么你们 K8s 非要等到这种冲突发生的时候才报错呢你们就不能设计个机制自动检查一下这些插件的使用过程有没有发生冲突吗”其实这个我们后来确实做了解决方法是给我们的 K8s 加了 20 多个 Admission Hook。
第三也是阿里巴巴上云之后我们团队特别痛的一个点。 我们需要处理的应用的交付场景除了公有云以外还会有专有云、混合云、IoT 等各种复杂的环境。各种各样的云服务在这种复杂场景下连 API 都是不统一的这个时候我们就需要专门的交付团队来进行弥补一个一个的去对接、去交付应用。对他们来说这是一件非常痛苦的事情“不是说好的 Docker 化了之后就能‘一次打包、随处运行’了吗”说白了K8s 现在并没有一个统一的、平台无关的应用描述能力。 阿里巴巴的解决办法
在 2019 年我们团队开始思考如何通过技术手段解决上述应用管理与交付相关的问题到现在已经取得了一定的成果。 不过在讲解阿里巴巴如何解决上述问题的方案之前有必要先介绍一下我们推进所有这些方案的理论基础。在这里我们主要遵循的是 CNCF 倡导的“应用交付分层模型”如下图所示 这个模型的基础假设是Kubernetes 本身并不提供完整的应用管理体系。换句话说基于 K8s 的应用管理体系不是一个开箱即用的功能而是需要基础设施团队基于云原生社区和生态自己构建出来的。这里面就需要引入很多开源项目或者能力。 而上面这个模型的一个重要作用就是能够把这些项目和能力以及它们的协作关系非常清晰地分类和表达出来。
比如 Helm 就是位于整个应用管理体系的最上面也就是第 1 层还有 Kustomize 等各种 YAML 管理工具CNAB 等打包工具它们都对应在第 1.5 层
然后有 Tekton、Flagger 、Kepton 等应用交付项目包括发布部署的流程配置管理等目前比较流行的是基于 GitOps 的管理通过 git 作为“the source of truth”一切都面向终态、透明化的管理也方便对接对应在第 2 层
而 Operator 以及 K8s 的各种工作负载组件Deployment、StatefulSet 等具体来说就像某个实例挂了这些组件自动拉起来一个弥补上原来所需要三个的实例数包括一些自愈、扩缩容等能力对应在第 3 层
最后一层则是平台层包括了所有底层的核心功能负责对工作负载的容器进行管理、封装基础设施能力、对各种不同的工作负载对接底层基础设施提供 API 等。
这些层次之间通过相互之间的紧密协作共同构建出一套高效、简洁的应用管理与交付体系。在这些层次当中目前阿里巴巴在今年 KubeCon 时已经宣布开源了第三层的 OpenKruise 项目。最近我们则正在联合微软等更广泛的生态和整个社区一起推进第一层“应用定义”相关的工作。 应用定义到底该怎么做
其实关于应用定义无论是开源社区还是在阿里巴巴内部都已经做了不少尝试比如一开始我提到 Docker 解决了单机应用交付它就通过 Docker 镜像把单机应用定义的很好。 围绕 Kubernetes 我们也试过使用 Helm 以及 Application CRD 来定义应用。但是现在的云原生应用往往会依赖云上的资源像数据库会依赖 RDS、访问会依赖 SLBHelm 和 Application CRD 只是单纯地将 K8s 的 API 组合在一起无法描述我们对云上面资源的依赖当我们用 CRD 来描述云上资源依赖的时候它其实是 freestyle 的没有一个很好的规范和约束无论是用户、开发、运维还是平台资源提供方都没有一个共识自然也就无法协作和复用。 另一方面它们既然是简单的对 K8s API 的组合那么 K8s API 本身“不面向应用研发和运维设计”的问题就依然存在这并不符合我们所希望的“应用定义”应该走的方向。此外像 Application CRD它虽然是 K8s 社区的项目但是却明显缺乏社区活跃度大多数修改都停留在一年前。 试了一圈我们发现“应用定义”这个东西在整个云原生社区里其实是缺失的。这也是为什么阿里巴巴内部有很多团队开始尝试设计了自己的“定义应用”。简单地说这个设计其实就是把应用本身的镜像、启动参数、依赖的云资源等等全部描述起来分门别类的进行放置并通过一个模板最终渲染出一个配置文件文件里有上千个字段完整描述了一个应用定义的所有内容。这个配置文件大概长下面这个样子 除了基本的 Deployment 描述字段这种 in-house 应用定义往往还会包含云上资源的声明比如使用哪种 ECS 套餐、如何续费、使用的是哪种磁盘和规格等等一系列额外的描述。这些资源的定义是一大块并且上面的例子里我们已经尽量精简了另一大块就是运维能力的描述比如自动扩缩容、流量切换、灰度、监控等涉及到一系列的规则。 然而你也不难看到这种定义方式最终所有的配置还是会全部堆叠到一个文件里这跟 K8s API all-in-one 的问题是一样的甚至还更严重了。而且这些应用定义最终也都成为了黑盒除了对应项目本身可以使用其他系统基本无法复用自然就更无法使得多方协作复用了。 吸取了这些教训以后我们团队决定从另一个方向开始设计一个新的应用定义。 具体来说相比于其他“应用定义”给 K8s 做加法、做整合的思路我们认为真正良好的应用定义应该给 K8s API 做“减法”。更准确的说是我们应该通过“做减法”把开发者真正关心的 API 给暴露出来把运维、平台关心的 API 给封装起来。 也就是说既然 K8s API 为了方便基础设施工程师已经选择把各方的关注点混在了一起。那么当基础设施工程师想要基于 K8s 来服务更上层应用开发和运维人员时其实应该考虑把这些关注点重新梳理出来让应用管理的各个参与方重新拿到属于自己的 API 子集。 所以我们开始在 K8s API 的基础上增加了一层很薄的抽象从而把原始的 K8s API 按照现实中的协作逻辑进行合理的拆分和分类然后分别暴露给研发和运维去使用。这里的原则是研发拿到的 API 一定是研发视角的、没有任何基础设施的概念在里面而运维拿到的 API一定是对 K8s 能力的模块化、声明式的描述。这样在理想情况下运维或者平台就能够对这些来自双方的 API 对象进行组合比如应用 A Autoscaler X应用 B Ingress Y。这样组合完成后的描述对象其实就可以完整的来描述“应用”这个东西了。 Open Application Model OAM
在同社区进行交流和验证中我们发现上面的这个思路正好跟当时微软 Brendan Burns Kubernetes 项目创始人和 Matt Butcher Helm 项目创始人团队的思路不谋而合。所以我们双方在面对面交流了几次之后很快就决定共建这个项目并把它开源出来跟整个社区生态一起来推进这件非常具有意义的事情。 今年 10 月 17 号阿里云小邪和微软云 CTO Mark 共同对外宣布了这个项目的开源它的官方名字叫做 Open Application ModelOAM同时我们还宣布了 OAM 对应的 K8s 实现——Rudr 项目。 具体来说在设计 OAM 的时候我们希望这个应用定义应该解决传统应用定义的三个问题
第一不能有运行时锁定。一套应用定义必须可以不加修改跑到不同运行环境当中无论是不是基于 K8s这是解决我们在应用交付时所遇到的问题的关键。这才是真正的“一次定义、随处运行”
第二这个应用定义必须要区分使用角色而不是继续延续 K8s 的 all-in-one API。 我们已经深刻了解到我们所服务的应用开发人员实际上很难、也不想关心运维以及 K8s 底层的各种概念我们不应该让他们原本已经很苦逼的日子变得更糟
最后一个这个应用定义必须不是在一个 YAML 里描述所有东西。一旦一个应用定义里把所有信息全部耦合在一起就会造成应用描述和运维描述被杂糅在一起从而导致这个定义的复杂度成倍提升也会让这个定义完全无法复用。我们希望这些不同领域的描述能够分开然后平台可以自由地组合搭配。在这个思路下我们最后设计出来的应用定义主要分为三个大块
第一部分是应用组件的描述包括应用组件怎么运行和该组件所依赖的各种资源。这个部分是开发负责编写的第二部分是运维能力的描述比如应用怎么 scale、怎么访问、怎么升级等策略。这个部分是运维负责编写的第三部分是把上述描述文件组合在一起的一个配置文件。比如“ 一个应用有两个组件组件 A 需要运维能力 X 和能力 Y组件 B 需要运维能力 X”。所以这个配置文件其实才是最终的“应用”。这个配置文件也是运维编写并且提交给平台去运行的当然平台也可以自动生成这个文件。
下面我们通过实例来看下以上三个部分对应的 YAML 文件到底长什么样子它们究竟怎么玩儿 备注如果你想跟我一样实际操作体验这个流程你只需要在 K8s 集群里装上 Rudr 项目就可以实操了。 第一部分Component 首先我们可以看到Component 定义的是开发关心的事情没有任何运维相关的概念。 它的 Spec 主要分为两大块 第一个参数块是应用描述包括 WorkloadType 字段这个字段就是表达应用使用什么 Workload 运行在我们设计里有六种默认 Workload分别是 Server、Worker、Job 以及他们对应的单例模式Workload 也可以扩展。Server 代表这是一个可以自动伸缩的并且有一个端口可以访问的模式。接下来就是容器的镜像、启动参数之类的这部分包含完整的 OCI spec。 第二块是 parameters 如何运行可扩展的参数如环境变量和端口号。这一块参数的特点是它们虽然是开发定义的但是都允许运维后续覆盖。这里的关键点是关注点分离并不等于完全割裂。所以我们设计了 parameters 列表其实就是希望开发能告诉运维哪些参数后续可以被运维人员覆盖掉。这样的话就很好地联动起来了开发人员可以向运维人员提出诉求比如运维应该使用哪些参数、参数代表什么意思。 像这样一个 Component 可以直接通过 kubectl 安装到 K8s 中。 然后我们可以通过 kubectl 工具查看到已经安装好的组件有哪些 所以说我们当前的 K8s 集群支持两种“应用组件”。需要指出的是除了我们内置支持的组件之外开发自己可以自由定义各种各样的组件然后提交给我们。Component Spec 里的 Workload Type 是可以随意扩展的就跟 K8s 的 CRD 机制一样。 第二部分 Trait
说完了开发能用的 API我们再来看运维用的 API 长什么样。 在设计应用的运维能力定义的过程中我们重点关注的是运维能力怎么发现和管理的问题。 为此我们设计了一个叫做 Trait 的概念。所谓 Trait也就是应用的“特征”其实就是一种运维能力的声明式描述。我们能通过命令行工具发现一个系统里支持哪些 Traits运维能力。 这时候运维要查看具体的运维能力该怎么使用是非常简单的 可以看到他可以在 Trait 定义里清晰的看到这个运维能力可以作用于哪种类型的 Workload包括能填哪些参数哪些必填哪些选填参数的作用描述是什么 你也可以发现OAM 体系里面Component 和 Trait 这些 API 都是 Schema所以它们是整个对象的字段全集也是了解这个对象描述的能力“到底能干吗”的最佳途径反正基础设施团队的文档写的也不咋地。 上面这些 Trait 也都是用过 kubectl apply 就可以安装到集群当中的。 既然 Component 和 Trait 都是 Schema那么它们怎么实例化成应用呢 第三部分Application Configuration
在 OAM 体系中Application Configuration 是运维人员或者系统本身也可以执行应用部署等动作的操作对象。在 Application Configuration 里运维人员可以将 Trait 绑定到 Component 上执行。 在 Application Configuration YAML 里面运维可以把 Component 和 Trait 组装起来从而得到一个可以部署的“应用” 在这里我们可以看到运维实例化的应用里面包含了一个叫 hellowworld-python-v1 的 Component它有两个参数一个是环境变量 target一个是port。需要注意的是这两个参数是运维人员覆盖了原先 Component yaml 中开发定义的两个可覆盖变量。 同时这个 Component 绑定了 2 个运维能力一个是水平扩容一个是 Ingress 域名访问。 运维人员通过 kubectl 即可把这样一个应用部署起来 这时候在 K8s 里面你就可以看到 OAM 插件会自动为你创建出对应的 Deployment。 同时这个应用需要的 Ingress 也被自动创建起来了 这里其实是前面提到的 Rudr 插件在起作用在拿到 OAM 的 Application Configuration 文件以后识别出其中的 Component 和 Trait将其映射到 K8s 上的资源并拉起K8s 资源相应的生命周期都随着 OAM 的配置去管理。当然由于 OAM 定义是平台无关的所以除了 K8s 本身的资源Rudr 插件的实现中也会加入外部资源的拉起。 OAM YAML 文件 一个自包含的软件安装包
最终我们可以通过像乐高积木一样组装复用 OAM 的不同模块实例化出一个 OAM 的应用出来。更重要的是这个 OAM 应用描述文件是完全自包含的也就是说通过 OAM YAML作为软件分发商我们就可以完整地跟踪到一个软件运行所需要的所有资源和依赖。 这就使得现在对于一个应用大家只需要一份 OAM 的配置文件就可以快速、在不同运行环境上把应用随时运行起来把这种自包含的应用描述文件完整地交付到任何一个运行环境中。 这不仅让我们前面提到的软件交付难题得到了很好的解决也让更多非 K8s 平台比如 IoT、游戏分发、混合环境软件交付等场景能享受到云原生应用管理的畅快。
原文链接 本文为云栖社区原创内容未经允许不得转载。