为了解决传统应用升级缓慢、架构臃肿、不能快速迭代、故障不能快速定位、问题无法快速解决等问题,云原生这一概念横空出世。云原生可以改进应用开发的效率,改变企业的组织结构,甚至会在文化层面上直接影响一个公司的决策,可以说,云时代的云原生应用大势已来。在容器领域内,Kubernetes 已经成为了容器编排和管理的社区标准。它通过把应用服务抽象成多种资源类型,比如 Deployment、Service 等,提供了一个云原生应用通用的可移植模型。在这样的背景下,我们如何在云原生的环境下实践更高效的 DevOps 来达到更有生产力的表现就成为了一个新的课题和诉求。
与 GitOps 这个概念相比,大家可能对 DevOps 的概念已经耳熟能详了。起初 DevOps 是为了打破开发测试、运营这些部门之间的壁垒,通过自动化的构建、程式化的脚本,最低限度减少人工误差,一定程度上提高应用版本的迭代效率;容器技术出现以后,轻量、标准化的能力使得 DevOps 技术才有了突飞猛进的发展。不管技术怎样更新迭代,DevOps 最主要的核心诉求是不变的,那就是提高应用迭代的频率和降低成本。GitOps 就是 DevOps 的逻辑扩展,它的核心目标是为了更加高效和安全的应用发布。
首先我们提取出一些用户在做 devops 的过程中遇到的痛点进行分析。第一个问题是如何自动化推进应用在环境栈中的无差别发布. 这里我列举了三种环境,测试环境、生产环境和预发环境,对于一个应用来说,我们通常的设定都是把不同分支部署到对应环境,比如 master 分支的源码对应的是线上环境,latest 分支对应的是预发环境,其他开发分支对应地部署到测试环境;目前大多数的做法是创建不同的 job,拉取不同的源码分支、部署到不同的环境,或者同一个 job,通过添加不同的构建参数来决定进行怎样的构建和发布动作。非常容易产生混乱和不便于管理。
第二个问题就是,生产环境的发布权限一般都是需要严格控制的,通常只有应用管理员或者运维管理员才有生产发布权限。我们在跟一些客户的交流中发现,一种方式是在同一套 cicd 环境中创建不同的 job,然后通过基于角色访问控制策略来做 job 的隔离,只有管理员权限的人员才能看到用于发布生产的 job;更直接的一种做法就是再建一套 cicd 环境专门做生产环境的发布,但这样既浪费资源又降低了应用迭代的频率。
第三个问题是说我们想要提高应用迭代的频率进而降低人力成本、时间成本、把精力放在新业务或创新业务的拓展上,但目前我们的开发测试人员在应用运行状态或测试结果的同步与反馈上有一定的隔阂,另外一个是线上业务出现问题的时候,如何快速定位、复现和回滚,这是一个我们可以重点思考的地方。以上三点只是我列举出来的我们用户在实际使用 cicd 的过程中的一些痛点的子集,那接下来我们就带着这些问题来看一下 gitops 模型的设计思路是怎样的
我们在设计 gitosp 发布模型的时候是有以下这些核心诉求的:第一个是版本管理,我们希望每一个发布的应用的版本号都能跟 git commit id 关联,这样的好处就是每一个变更都有历史记录查询、可以更快进行故障定位和修复,第二个是基线管理,这里我们一会儿会讲到分两种类型的基线,第三个是怎么做安全发布,包括发布权限管理以及安全审批的内容;最后一个是如何让开发测试人员快速获取反馈
首先 gitops 的核心思想就是将应用系统的声明性基础架构和应用程序存放在 Git 版本库中,所有对应用的操作变更都来源于 Git 仓库的更新,这也是 gitops 这个名称的由来。另外一个问题是,按照以往通用的做法,我们可能会把应用如何构建如何部署的脚本以及配置文件跟应用源码本身存放在同一个仓库里,这样带来的问题有两个,一是开发人员可能还需要维护这个部署脚本或配置文件,不能把精力集中到产品开发上,另外一个问题是部署脚本有时候会涉及环境敏感信息,安全性不够,所以我们这里一定要把应用源码仓库与构建仓库分开管理。
接下来就是基线管理,基线管理分两种,一种是环境栈基线,如图所示,我们的设定是,生产环境只能部署 master 分支的代码,预发环境只能部署 latest 分支的代码,预览环境用来部署其他开发分支,这里有个名词叫预览环境,其实也就是测试环境,但我们会在开发分支通过测试、通过验证成功合并到 latest 分支以后动态销毁这个测试环境,当然这在 kubernetes 容器集群下是非常容器做到的,在其他具体的场景下可以用不同的策略。这个基线我们可以把它称为小基线,它是用来明确管理应用在预览、预发、生产环境中的推进的。大基线是针对线上发布版本的管理的,这能保证我们在线上出现故障的时候能快速回滚到上一个稳定的版本。这在生产发布管理中是必不可少的,在 gitops 中我们还能快速定位故障精确到某个 git commit。
然后是应用发布的权限管理和安全审批,gitops 中的权限管理是通过代码合并的控制来做的,在这个模型中,普通的开发人员没有 cicd 环境比如 jenkins 的访问权限,更精确地说的话是只有日志查看的权限,在 git 这一端,普通开发者只有向开发测试分支推送代码的权限,并可以申请向 latest 分支合并代码,即提交 MR/PR 的权限,当普通开发者新建 MR/PR 后,就会触发构建把应用部署到预览环境,管理员通过查看这个新分支的构建部署是否通过一系列测试和验证来决定是否接受这个 MR/PR,只有管理员接受 MR/PR 的合并后,latest 分支代码才会重新构建和部署到预发环境,这样就通过 MR/PR 的接受和拒绝来达到应用发布安全审批的目的。
最后是如何进行快速反馈和团队成员间的互动,这包括两部分内容:一个是普通开发测试人员在推送源码后,能通过邮件、钉钉、slack 等工具实时地获取构建结果,对自己的应用进行高效开发测试,;另一方面是能在 MR/PR 的页面上查看自动化测试的反馈结果、应用预览链接、其他团队成员的 comment 等。
下面是使用 GitOps 管理应用发布到不同 kubernetes 集群的架构图和时序图。首先是应用源码与构建源码分离。最上面有一条虚线,虚线上面是普通开发者能看到的,或者说是有权限进行操作的部分,剩下其他的部分都是管理员才有权限做的,绿色区域是 Jenkins 的流水线任务。普通开发者没有 Jenkins 环境的创建 Job 和构建 Job 的权限,他有的只是构建 Job 的日志查看权限。这个普通应用是在 Git 仓库里,它有不同的分支,有一定设定的关系,每次有构建的时候会从另外一个 Git 仓库里做,比如 preview-plpeline、prod-plpeline,在这里面可以存放一些信息,只有应用管理员才能看到,普通开发者没有权限看到信息。然后我们需要设置应用发布环境栈,这在个示例中我们有预览环境、预发环境、生产环境的设置,应用在预发环境和生产环境中的发布是需要经过管理员安全审批的。
最后是一个时序图,开发人员提交新的 feature,创建指向 latest 分支的 MR,创建 MR 的动作会触发 preview-plpeline 的构建,构建会拉取 preview-plpeline 的构建仓库,构建仓库存放的是构建脚本以及要部署的环境信息。然后就是自动化的构建流程,首先会从应用源码仓库把应用源码拉取下来做构建,静态代码测试、单元测试,测试结果会反馈到 MR 上,然后打包容器镜像并把镜像推送到镜像仓库,最后会把应用通过文件部署到 Kubernetes 的集群里并进行功能测试,测试结果反馈到 MR 上,部署之后会收集应用相关信息,通过钉钉通知发送到开发群里。开发人员收到钉钉通知,可以直接点击链接查看应用状态,如果有问题,可以返回来自己重新开发,再重新进行提交,把前面的流程再走一遍,没问题就可以请求管理员进行审批,把代码合并到 latest 分支。latest 分支和 master 分支有更新时,就会触发与前面的构建类似的流程把应用推进到预发环境和生产环境。
本文作者:流生
阅读原文
本文为云栖社区原创内容,未经允许不得转载。