@gaoxiaoyunwei2017
2019-06-18T15:21:20.000000Z
字数 4658
阅读 750
北哥
郝树伟
- 阿里巴巴高级研发工程师
2014年到2017年初在IBM做开源云平台开发,现在在阿里云容器云团队主要做CI/CD解决方案和Kubernetes安全集群工作研发。Kubernetes集群技术,已经成为了容器编排和容器管理的社区标准,通过把应用服务抽象成各种不同的资源类型,比如暴露服务的service概念,做成云原生通用的模型,给我们应用的部署和管理提供了非常强大的能力。
在这样的背景下,CI/CD的方案或者DevOps的方案有哪些不足之处?有哪些痛点?GitOps方案又做了哪些改进?这是本文要分享讨论的主体。分三部分:
GitOps概念算是比较新,大家对DevOps概念已经耳熟能详。起初DevOps是为了打破开发、测试、运营部门之间的壁垒,通过自动化的构建、城市化的脚本,最低限度减少人工误差,一定程度上提高应用版本的迭代效率。但构建基本都是在业界构建,由程序员提交代码,如果想看测试结果和部署结果,可能要等到第二天。
容器技术出现以后,DevOps技术才有了突飞猛进的发展。容器的镜像技术可以让应用有一个标准化的打包。容器轻量级的特性,让DevOps应用的迭代速率有了加倍提升。再到Kubernetes集群的高可用性,能提升弹性伸缩的能力,包括集群调度、节点调度,让构建资源动态的创建和销毁。应用管理可以保证高可用,多租户功能也可以保证一定安全,包括鉴权方面的特性。
Kubernetes在提供多维管理能力的前提下,我们在DevOps或CI/CD的方案上就可以做一些事情。不管技术是多么的更新迭代,DevOps最主要的两个核心诉求还是不变的,一个是提高应用迭代的频率,另一个是降低成本,包括人力成本、时间成本、财力成本、资源成本等等。
GitOps其实是DevOps的逻辑扩展,它是一个方法论,并没有特别新的工具或其他比较高深的模型。我们只是在DevOps的基础上做了一些扩展。可以看到现在的容器技术,包括容器集群技术,以及持续集成持续交互结合起来,是可以满足大部分的需求,但是还是会有一些痛点。
第一个痛点,如何自动化推进应用在环境栈中的无差别发布?有三种环境:测试环境、预发环境和生产环境。测试环境是日常用的开发测试环境,生产环境是运行线上业务的环境,预发环境在两者之间,可以把它当成集成测试的环境,又要比集成测试的环境更正式一些。我们要保证预发环境跟生产环境的底层资源和环境资源一致,而且要保持这上面运行的应用版本一致或者预发环境要比生产环境高一个版本,业务上生产环境之前,要在预发环境下经过充分的测试。很多用户的普遍做法是将一个应用的发布创建三个Job,每个Job跟Git分支源码对应,如Job1部署测试环境,Job2部署预发环境,Job3部署生产环境。这三个Job的发布,没有明显的联动效果,更粗暴的做法是创建一个Job,通过不同的构建参数,控制本次的构建是把应用发布到预发环境还是线上环境。
第二个痛点,生产环境是需要有一定安全权限。如何权限控制/审批应用在不同环境中的发布?生产环境只有极少数的应用管理员或运维管理员才能拥有的权限。在跟客户的交流过程中通用的有两种做法:一个是在同一套CI/CD环境里创建不同的Job,通过角色访问控制的策略控制哪些用户可以访问到构建开发测试的Job,哪些用户可以访问生产环境的发布。另一个是更直接的做法,创建两套不同的CI/CD环境,一套只给开发测试人员用,另外一套专门做线上发布用。这不仅效率低而且还浪费资源。
第三个痛点,如何进一步提高应用的平均部署时间和恢复时间?从开发、测试、提交源码到应用发布到线上,全链路所有流程都可以做优化。怎样在线上环境发生故障的时候快速定位,并且回滚到上一个稳定的版本?这是需要我们思考的地方。
做GitOps的发布模型有一些核心诉求如下:
GitOps的核心思想是把应用系统的声明性基础架构和应用程序存放在Git版本库里,如果想运行构建任务,唯一的入口就是Git源码有变更,包括部署脚本的变更,都需要在Git端做更改。
通常的做法是把部署脚本文件和应用源码放在同一个Git仓库里,这个会有两个问题:
因此,一定要把构建源码和应用源码分开管理。
环境栈的基线管理。上图中有一个设定,生产环境只能部署来自于Master的代码,预发环境部署来自latest的代码,预览环境跟测试环境是同一个概念。我们使用了Kubernetes动态分配和销毁资源的能力,普通开发者在提交新代码分支的时,在做PR或者MR的会触发构建,把自己的代码部署到预览环境,自己先预览应用和功能模块是否正常工作,如果通过了自己既定的测试和验证,才可以向有有更高权限的管理员做通知,请求他合并。管理员查看测试和验证结果都通过,就把代码合并到latest分支。这有一个动作是动态的把预览环境销毁,保证开发人员每次提交代码时都是全新的测试环境。
环境栈的基线管理,主要是为了管理开发代码在往latest和Master分支合并的时候,把应用程序可以同步从预览环境推进到预发环境和生产环境的过程。
发布基线管理,主要是为了管理线上的应用版本,当线上发现故障或者bug的时候,可以快速回滚到上一个版本。回滚在生产发布里非常重要,大多数的发布都使用这种方式,这是共识。
预发布环境与生产环境需要有一定的权限控制,图中模型里每一个环境从哪开始部署,都跟源码分支有一一对应的关系,要想控制生产环境或者预发布环境能不能有新的代码部署,只需要控制源头就可以了,我们是在Git的MR或PR这里做控制。流程是普通开发者没有直接往latest和Master分支推送代码的权限,只能创建新的以feature为开头的分支。推送分支后,可以申请合并到latest分支,触发构建把代码进行合并,部署到预览环境,再自动化跑一些测试做一些验证。当测试结果和验证结果都通过了后,反馈到MR,管理人员可以直接看结果来决定是否符合可以合并代码的标准。latest分支到Master分支的权限合并,相当于能不能控制新代码进入到生产环境。应用发布的权限控制和安全审批,我们是完全使用Github或GitLab提供的能力来实现的。
快速反馈有两个方面:
这是我们常用的开源工具,可以帮助我们把工作流更快捷更频繁的提升。如Skaffold可以帮我们简化流程,Kaniko是推送工具,Helm是一个管理工具。还有常用的引擎,如argo、Jenkins。很多工具的使用和GitOps里面的应用还是非常成熟的,大家有时间可以试试。
图中是一个普通Java应用的发布流程。首先是应用源码与构建源码分离。最上面一条虚线上面是普通开发者有权限进行操作的部分,剩下的部分都是管理员才有权限做的。绿色区域是Jenkins的流水线任务。普通开发者没有Jenkins环境的创建Job和构建Job的权限,只有查看构建Job的日志的权限。GitProvider应用是在Git仓库里,有不同的分支,有一定的设定关系,每次有构建的时候会从另外一个Git仓库里做,比如preview-plpeline、prod-plpeline,这里面存放的一些信息,只有应用者才能看到。
实践二是设置应用发布环境栈,我们有预览环境、预发环境、生产环境,一个是为了提高应用debug的效率,另一个是能最大程度控制应用上线后的风险。我们的应用上线在预览环境有测试,在预发环境有更完全的测试,在应用版本上有一定对齐。当线上有一些问题的时候,可以快速在预发环境做一些验证或定位。一个应用的发布从开发测试环境到线上环境,我们经过了环境栈的投递过程,经过了充分验证。
实践三是应用在环境栈中的安全推进。线上环境的权限,一定要尽量收到少数人手。流程是这样的,普通开发者要提交自己的分支,然后创建MR,之后把应用部署到预览环境,同时需要给出有部署访问链接、MR的RD信息。如果应用开发有问题,可以在MR上重新构建部署。应用在不同环境栈之间进行推送,一定要经过管理员的权限审批。在权限审批之前还会有包括代码review、测试结果以及验证结果的评估。
这是时序图,开发人员提交新的feature,创建指向latest分支的MR,创建MR的动作会触发preview-plpeline的构建,拉preview-plpeline的项目,这里面存放的是构建脚本以及要部署的环境信息。下一步,就是自动化的构建流程。首先会从应用源码仓库把应用源码拉取下来做构建,打包、生成容器镜像、推送镜像,然后进行测试并反馈到MR上,最后会把应用通过文件部署到Kubernetes集群,部署之后会收集应用信息和集群信息,通过钉钉通知发送到开发群中。开发人员收到钉钉通知,可以直接点击链接查看应用状态,如果有问题,可以返回来重新开发,重新进行提交,把前面的流程再走一遍。如果没问题就会通知管理员,把代码合并到latest分支。latest分支代码的拉取过程跟preview-plpeline一样,只不过这个流程开发人员不涉及去看的过程。预发环境和生产环境,都是管理员和运维人员去做的。这个流程里都有构建源码和应用源码做构建、部署、通知的过程,最终是把应用发布到线上环境。现在有一些组件,就是通过这个流程做测试、验证、发布的。
本文主要是依托于开源的技术,分享下GitOps的基本使用。我们内部也有一些自研的产品,主要是提供给公有云上有Jenkins使用经验的用户。这个过程还在不断完善中,同时Jenkins有越来越多的新特性,后续也会尝试把这些新特性往方案里做集成,提供更好的实践方案。