SREWorks持续交付云原生化: 镜像构建 原创
作者:邓洋杰
背景
在应用运维领域中,CI/CD已逐步演化成持续集成(Continous Integration)、持续交付(Continous Delivery)和持续部署(Continous Deployment)三个核心阶段,以支持更加复杂的大型系统建设。
CI依然代表应用开发人员的开发、测试、合并等自动化阶段,而CD已分化成持续交付和持续部署两个关联阶段,为云原生背景下的应用运维提供了更多的可操作空间。其中,持续交付在不同的阶段,都有相对应的事实标准,大致可分为“On-Machine”阶段,“On-Container”阶段以及目前的“On-CloudNative”阶段。
本文主要讲解一下SREWorks在持续交付阶段,镜像构建云原生化的解决方案,一句话概括就是:SREWorks在执行镜像构建时,按需启动一个Pod去执行镜像构建任务。
发展阶段
“On-Machine”阶段:交付应用往往以一个编译后能正确运行的语言包组成,比如Jar包等;
“On-Container”阶段:容器技术的出现,此时,容器镜像Image作为应用交付的最终形态,解决了语言包对机器运行环境的强依赖,进一步提升了应用运维的效率;
“On-CloudNative”阶段:是以“On-Container”为基础的,是前者在云原生时代的高级形态。“On-CloudNative”阶段最终交付物不仅包含最基本的容器镜像Image,还包含了一系列的运维特性,以支持容器编排等云原生场景需求。
持续交付云原生化
云原生是以分布式和云架构为基础的,但其对各种技术架构提出了更标准化的要求。在云原生思想逐步扩展开后,云原生能为传统的CI/CD带来什么影响,成为SREWorks考虑的主要问题之一。
其中最基本的,我们希望持续交付可以基于底座资源池去做弹性构建(按需启动一个构建pod,构建完释放资源),而不用常置一台特定的构建机去做构建。
SREWorks团队在经过不断的探索实践之后,认为云原生化的持续交付应包含以下几个显著的特点:
- 资源弹性:根据构建需要从资源池申请cpu等资源,提高资源利用率;
- 安全性:实时动态地执行构建任务,有明确的归属和权限隔离管控;
- 免运维:不再需要管理特定的构建机器或进程,机器管理统一由底座集群完成;
- 快速交付:底座集群保证构建任务容器能正确调度执行,自动规避故障节点,快速响应构建请求。
SREWorks正是基于上述的几个点,逐步完成自己的镜像构建云原生化建设。
架构演进
Docker build 机制
按照Docker官方文档给出的架构图,Docker主要分为Client,Host,Registry三个部分。
Docker按照C/S架构,通过Client与Host进行通信。Host作为后端,负责处理所有Client请求以及后端模块的调度及管理工作。Registry作为中心化的镜像仓库,存储所有需要保存的镜像。
此处,我们主要关注 docker build 命令。可以看到Host中Docker daemon模块负责接受和处理Client的命令请求。Docker daemon内部架构此处不做详细描述,只需要知道Docker daemon在接受到 docker build 命令之后,会启动一个create job去执行构建。在构建过程中,可能会需要到远程Regiestry中心镜像仓库拉取基础镜像(如果本地不存在的话)。在构建完成之后,Host会将构建好的镜像保存在本地的Repository中。
Docker daemon作为docker的服务端,由于需要系统的ROOT权限等要求,导致其不能天然地运行在另一个docker容器之中。这也为镜像构建云原生化提出了新的技术问题。
初始态:Docker out of K8S
在Docker build机制的支持下,在最初阶段是通过在K8S外搭建构建机器做镜像的构建任务,其架构如下:
AppManager:是SREWorks的中心管控服务,负责与K8S底座交互,应用构建发布及生命周期管理等核心功能。
在初始态,AppManager在需要执行镜像构建时,直接连接部署在K8S之外的某个ECS上Docker daemon来执行镜像构建。依然处于“On-Machine”的发展阶段。
这种方式的缺点包括但不限于:
- 安全性差:当这台ECS被劫持之后,恶意程序通过在镜像构建过程中注入非法代码,能够影响所有部署在云上的应用;
- 效率较低:当构建任务过多时,一起抢占ECS机器资源,会引起构建拥塞。
过渡态:Docker out of Docker
Docker out of Docker,下面简称“DooD”,依然使用docker build机制的远程构建能力,每个执行构建任务的pod都连接到本地node的Docker daemon实现镜像构建。
通过DooD的方式,AppManager可以动态的在集群层面拉起pod去执行镜像构建任务,而不用再去跟集群外部服务打交道。镜像构建任务可以均匀的分布到每台node,避免构建拥塞。且DooD通过操作基础设施node节点,实现了一种“伪弹性构建”的能力。
DooD的缺点也是显而易见的:
- 实施成本高:需要人工为每台Node节点安装Docker daemon程序,并保证其正确运行;
- 安全性差:构建Pod有能力通过node节点Docker daemon进程影响其他Pod;
- 资源浪费:由于每台node节点都常置Docker daemon服务进程,不仅没有减少资源消耗,反而加重了资源浪费,这也是为什么称其为“伪弹性构建”。
终态:Docker in Docker
Docker in Docker,下面简称“DinD”,与DooD是对应的,表示容器内client直接连接容器自身的Docker daemon进程来完成镜像构建任务。其架构如下:
DinD的架构,实现了按需拉起执行镜像构建任务的Pod,并在任务完成后及时归还Pod资源。构建任务的Pod也不再感知Node机器,所有管控及调度工作由K8S等云底座统一实现。其优点包括:
- 弹性构建:所有构建Pod所需资源由K8S等云底座统一调度和回收;
- 安全可靠:构建Pod满足所有资源及权限管控要求,更加安全可控;
- 无服务器:Pod是容器化的,不再运行在ECS等机器之上;
- 快速交付:在集群层面完成构建任务调度,更加及时且可扩展。
基于Kaniko的设计实现
目前支持在容器内部进行镜像构建的开源技术有很多:Kaniko,BuildKit,Img等,其中Kaniko作为Google官方开源的容器化构建解决方案,目前收获了最高的star数量,社区活跃度也最高。排名其后的BuildKit和Img也通过其自身的技术优点吸引了一大批粉丝。
从架构上看,BuildKit和Img都是通过内置一个非Root权限的嵌套容器去执行构建。而Kaniko没有拉起嵌套容器去做构建,而是直接实现了Docker daemon中create job的功能,更加的轻量、高效、安全。因此,SREWorks基于Kaniko去做云原生下的镜像构建。
在引入Kaniko之后,SREWorks的中心管控appmanager服务按照如下架构来对接ApiServer拉起和管控执行构建任务的pod。
AppManager关键模块讲解:
- Provider模块:AppManager内置了一系列的Provider负责处理Client客户端的请求;
- 构建任务状态管理器(ComponentPackageTaskStateActionManager):负责监听各类构建任务状态更新事件,进而调用不同构建阶段的子处理器;
- RunningState,FailureState,SuccessState等模块:具体负责处理不同构建阶段的事务;
- ComponentPackageBuilderService模块:负责具体的镜像构建、构建Pod管控等工作。
这里主要讲一下镜像构建的主要工作流程:
- 将应用Component包 (每个应用可包含多个Component,每个Component又可包含多个Container) 拆解为多个Container构建任务;
- 启动构建Pod Informer监听;
- 下载每个Container构建源代码,并渲染Dockerfile文件;
- 渲染并下发构建Pod yaml并监听构建pod状态;
- 当构建pod到达终态时,进行Component交付包生成、资源清理等工作。
设计反思
通过以上基于Kaniko架构设计和实现,SREWorks提出了持续交付云原生化的设计要求,并为此设计并实现了自己的解决方案。在此罗列一些目前的架构Feature供大家深入发掘一下:
- 内置了Dockerfile参数动态渲染功能,这为运维人员提供了可操作空间。
- 由于目前云底座K8S等,都已支持所有实现了Container Runtime Interface(CRI)的容器运行时(例如Docker、containerd、CRI-O),所以本文所提DinD,在目前云原生形态下称其为DinC(Container)更为准确。
- SREWorks的持续交付物的核心是镜像,在某些特殊场景下,交付物即使转变为机器时代的Jar包或者比镜像更高级的形态,但是本文的弹性架构设计依然适用。
- 共享PV下发构建tar包:可以去除MinIO的依赖,执行构建任务的pod直接从共享PVC内读取构建tar包。但是由于在某些云底座上,无内置“ReadWriteMany”访问模式的PV,所以暂时关闭了这项功能。
- 构建Pod替换成Job:使用Pod进行构建的初衷是足够轻量化,且AppManager具有绝对的管控权限。目前还没有足够的替换理由。
云原生建设感悟浅谈
广义地来看,可以说现在的几乎所有云产品都是云原生的,因为他们都或多或少满足了云原生所标榜的微服务、容器、持续交付、DevOps。但是,狭义地来看,在某些场景下,其往往又不能完全满足云原生的某些特性:例如可复用、免运维、资源池化等。
正如CNCF对云原生的定义:“云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API”。在作者看来,其内在含义代表着一种以产品能力为导向的最佳上云实践。
脱离“产品”这个导向,云原生化往往是没有意义的;“最佳实践”代表云原生化是不断迭代的;以本文镜像构建的演进为例,也是在产品能力的指导下,不断迭代和完善的,这就是云原生化的意义。
同时也欢迎大家多提issue,协力将SREWorks打造得更好,欢迎各位加入钉钉群或微信群分享和交流~
SREWorks开源地址:
https://github.com/alibaba/sreworks
更多开源项目合集:
https://www.aliyun.com/activity/bigdata/opensource_bigdata__ai
更多推荐:
SREWorks v1.1 版本发布 | 组件插拔场景化部署能力
基于Elasticsearch生长的SREWorks数据化运维体系
更多详情请关注微信公众号:阿里智能运维!!