乐趣区

关于服务端:云音乐-GitOps-最佳实践

作者:奇洛森

近些年随着微服务、kubernetes 等技术的倒退,越来越多的厂商将单体架构的我的项目进行微服务化。然而随着原有我的项目的一直拆分,微服务的数量越来越多,部署的频率也越来越高,传统手工运维的劣势越发显著,效率低、部署品质没有保障。在云原生时代,是否有一种更加高效、稳固的部署形式,能够帮忙咱们改良部署和治理流程呢?

随着咱们对运维办法的调研,咱们发现 GitOps 可能很好的解决这些问题。GitOps 是一种合乎 DevOps 思维的运维形式,GitOps 以 Git 仓库作为惟一的事实起源,贮存申明式配置,并通过自动化工具实现环境和利用的自动化治理。Git 实现了版本控制、回滚、多人合作;申明式配置保障了配置的可读性和事务性;自动化部署打消了人为谬误,调高了部署效率和准确性,同时也保障了多环境的一致性。所以 GitOps + 申明式配置 可能很好的解决传统运维的痛点,进步部署效率,保障部署品质。

主机部署的缺点

在传统的云主机部署模式下,通过工单创立运维申请,运维人员接管到工单后,通过 Ansible 等运维工具手动进行运维操作。这种形式在实际操作过程中遇到了许多问题,比方因为 Ansible 基于 SSH 下发文件,所以须要给每台机器配置 SSH;因为机器底层的异构,导致运维须要批改配置文件;或是因为脚本执行程序谬误,导致须要从新执行整个部署流程;手工操作,导致部署效率低,容易出错,无奈保障部署品质。

总的来说,云主机时代运维存在以下缺点:

  1. 环境不统一:须要 step-by-step 的编写脚本,构想指标环境中的各种状况,编写脚本时须要思考各种状况,比方机器是否曾经部署过,机器是否曾经配置过 SSH,机器是否曾经装置过依赖等等。并且脚本运行在不同环境中可能会有不同的后果。
  2. 无事务保障:装置脚本不能被打断,如果中途遇到问题,服务可能处于不可用的中间状态。
  3. 合作艰难:须要另行编写文档形容运维流程,如果多人同时保护一个脚本,合作往往十分艰难。
  4. 回滚艰难:部署流程难以回滚,如果部署过程中呈现问题,须要手动执行逆向操作。
  5. 权限管控与审核:通常运维须要指标主机的 root 权限,难以限度运维人员的权限,同时也难以对整个运维动作进行审核。

云原生时代部署特点

云原生的代表技术包含容器、服务网格、微服务、不可变基础设施和申明式 API。云原生时代,微服务架构利用成为了支流。微服务架构的特点是将利用拆分成多个服务,每个服务都有本人的数据库和配置文件。每个微服务都是独立部署的,这大大提高了部署的频率,带来了新的挑战:

  1. 部署频繁:微服务利用被拆分成了多个服务,每个服务都须要独立部署,部署频率大大提高,须要更高的部署效率
  2. 多正本:微服务通过扩容正本的形式来进步可用性,通常须要部署多正本,有时甚至须要部署数百个正本
  3. 多环境:通常须要部署多个环境,比方开发环境、测试环境、预发环境和生产环境

为满足以上需要,咱们须要一种全新的部署形式

  1. 其应该有较高的 自动化 程度,可能缩小人工参加,缩小出错,进步部署效率
  2. 应该有良好的 版本控制 ,不便 回滚
  3. 应该保障多环境 统一,疾速在多个环境中拉起雷同的利用,不便测试和验证
  4. 应该可能保障 事务,防止部署过程中出错,导致服务不可用
  5. 便于 多人合作,进步部署效率

什么是 GitOps

如果咱们须要本人实现一种满足需要的部署形式,咱们须要本人实现一个版本管理系统,这须要很大的工作量。然而事实上市面上曾经存在一个非常优良的版本管理系统,那就是 Git。
能不能间接基于 Git 进行部署呢?咱们顺着这个思路持续调研基于 Git 的部署形式,最终发现了 GitOps,GitOps 可能很好的满足以上需要。
那么到底什么是 GitOps 呢?
GitOps 的要害是应用 Git 仓库 贮存 申明式配置 ,通过 自动化工具 将 Git 仓库中的配置利用到指标环境中。Git 仓库 满足了对于 版本治理 回滚 多人合作 的需要,申明式 配置满足了对于 事务性 一致性 的需要,而 自动化 工具进步了部署的 自动化 程度。所以 GitOps 可能很好的满足云原生时代的部署需要,是一种优良的部署形式。

Git 仓库

Git 仓库所有开发者都很相熟,它是一个分布式的版本控制系统,能够不便的进行版本治理和回滚。在 GitOps 中,Git 仓库作为惟一的事实起源,贮存所有的配置信息。
应用 Git 仓库贮存配置,能够不便的进行 版本治理 回滚 ,并且人造反对 多人合作,同时批改配置文件。并且通过 Pull Request 提交批改,能够基于 Code Review 保障批改的正确性和品质。

申明式配置

申明式配置应用配置文件间接形容零碎的冀望状态,使用者不须要思考执行流程和指标环境的差别,易于编写、了解、代码 review 和进行版本治理。并且申明式配置人造具备幂等性,能够反复利用而不会导致系统状态发生变化。具备 事务性 ,要么全副利用胜利,要么什么都不做。以 Kubernetes 资源配置文件为例,使用者只须要指定 CPU 和 Memory 的大小,而不须要关怀底层执行细节和环境差别,保障了各个环境中部署 统一

apiVersion: apps/v1
kind: Deployment
metadata:
  name: example-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: example-app
  template:
    metadata:
      labels:
        app: example-app
    spec:
      containers:
      - name: example-container
        image: example-image
        resources:
          limits:
            cpu: 1
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 256Mi

自动化工具

GitOps 中的自动化工具负责将 Git 仓库中的配置利用到指标环境中。自动化工具能够是 Gitlab CI、Github Action 这类流水线工具,也能够是 Argo CD 这类专门用于 GitOps 的工具,自动化工具能够依据 Git 仓库中的配置,自动化的实现部署、回滚、监控、告警等工作。

以 ArgoCD 为例,用户只须要创立 Git 仓库和 ArgoCD Application,ArgoCD 就会主动的将 Git 仓库中的配置利用到指标环境中。并且 ArgoCD 会实时监听 Git 仓库的变动,一旦 Git 仓库中的配置发生变化,ArgoCD 也能够进行主动同步。

自动化工具可能进步部署 自动化 程度、效率,缩小人工参加,缩小出错,进步部署效率。满足了咱们对高自动化程度的需要。

GitOps 实际

通过下面的形容,置信大家曾经对 GitOps 有了一个初步的意识。上面,咱们将通过云音乐外部的实际,展现 GitOps 在生产环境中的利用与劣势。
在云音乐全面推动容器化的过程中,须要装置部署许多的管控面组件,比方 Grafana、Argo Rollout 等,并且须要在多个环境中装置雷同的利用。出于对 GitOps 理念的认同,咱们设计了设计了一套基于 Gitlab CI 流水线的 GitOps 部署体系。

管控面运维

在云音乐的容器化过程中,咱们应用了许多开源的管控面组件,比方 Prometheus、Grafana、Argo Rollout 等。咱们有许多 kubernetes 集群,很多 kubernetes 集群中都须要装置雷同的组件,通过手工装置费时费力,而且很难保障所有环境的配置统一。并且,在运维过程中,可能会呈现多人批改同一个利用配置的状况,如何防止批改抵触和笼罩?Kubernetes 非常弱小,但同时其也非常复杂,有海量的配置项能够批改,如何保障配置的正确性?

为了解决以上问题,咱们设计了一套基于 GitOps 的自动化运维流程。每个线上组件都会有两到三个对应的仓库,别离是:代码仓库、配置仓库、Helm Chart 仓库。其中,代码仓库寄存组件的源代码,如果是开源组件,则间接应用开源的 release,没有对应仓库;Helm Chart 仓库寄存组件的 Helm Chart,配置仓库通过 Helm Dependency 援用 Helm Chart 仓库中的 Chart,并寄存了 values.yaml 文件,用于配置组件的参数。
将通用配置抽出来,放到同一个 Helm Cart 中,并在部署仓库中援用该 Chart,能够无效防止因多环境导致的配置不统一问题。

当须要批改配置时,开发者只须要批改配置仓库中的 values.yaml 文件,并向原仓库提交 MR,这时会触发流水线,验证批改是否正确。通过验证后,开发者须要申请团队中的其他人帮忙 review。通过查看后,将 MR 合并到 master 分支,这时会触发流水线,运行批改之后的配置,应用 Helm Upgrade 命令将组件更新到环境中。公布实现后,如果开发者本次更新降级有任何问题,能够通过运行上一次的部署流水线,将组件回滚到上一次的版本。

为了强制执行以上过程,通常会回收开发者对于 master 分支的更新权限。开发者只能通过向原仓库提交 MR 的形式,来配置批改。这样,就能够保障配置和环境的一致性。为了防止开发者没有合入权限,咱们开发了一个 review 机器人,当开发者提交 MR 时,机器人会在评论区进行评论,要求其他人 review 并投票。当该 MR 取得足够的票数(通常是两票)后,机器人会主动合入 MR。

须要留神的是,这里抉择了应用一个仓库对应一个环境,然而实际上,也能够应用一个仓库对应多个环境,只须要在仓库中创立多个分支,每个分支对应一个环境,而后在流水线中,依据分支名称,抉择对应的环境。为什么抉择仓库对应环境的形式而不是分支对应环境的模式呢?次要是因为仓库对应环境的模式在权限管控方面比拟有劣势,因为开发者可能须要不同环境领有不同的权限,如果抉择分支对应环境,那么就须要在部署流程中对不同环境的权限进行管控,这样很麻烦。

并且通过引入测试流水线和 Reviewer 升高了出错的概率,通过从新运行部署流水线实现了疾速回滚,通过 commit 记录每次部署的操作者,通过自动化部署缩小了人工染指,解决了绝大部分传统部署过程中的问题。

Horizon 利用部署

以上实际,部署大量的管控面组件还是比拟不便的,但当部署的利用数量增多时,就会变得比拟麻烦。因为每个利用都须要创立对应的仓库和创立流水线,这样就会导致仓库、流水线数量过多,保护老本过高。
为了优化云音乐大量利用的部署流程,咱们开发了 Horizon CD 平台,Horizon 基于 GitOps、ArgoCD 部署利用。通过 Horizon,开发者只须要在 Horizon 平台上创立利用,配置利用的参数,就能够实现利用的部署,并且也能够享受到 GitOps 带来的益处。

开发者通过填写表单即可在 Horizon 上创立利用,表单中蕴含了利用的根本信息和部署信息,例如利用名称、利用形容、镜像地址、正本数、部署环境等。Horizon 会为利用创立对应的 GitOps 仓库,并将用户输出以及其它创立利用必要的信息一并写入到 GitOps 仓库中。GitOps 仓库中的每个分支对应不同的环境,方便管理多环境。
用户能够依据以上创立的利用,创立对应的利用实例,利用实例对应 Kubernetes 中的一系列相干资源。Horizon 会为该利用实例创立 GitOps 仓库和 ArgoCD Application。该 GitOps 仓库有两个 branch —— master 和 gitops。master 和 gitops 分支都寄存了利用的配置。用户批改利用配置后,Horizon 会将批改记录到 gitops 分支中。用户公布利用时,Horizon 会将 gitops 分支合并到 master 分支中,并触发 ArgoCD 同步,将 GitOps 仓库中的配置利用到 Kubernetes 中。这样,就实现了一次部署。

如果有人手动批改了 kubernetes 中相干资源或者批改了 GitOps 仓库但并未执行同步,ArgoCD 会感知到 master 分支配置与 kubernetes 中资源配置不统一,会将该利用标记为 OutOfSync,在 Horizon 上,用户也能够察看到该利用状态不失常,不便用户及时发现问题,并与 Horizon 管理员分割,及时排查解决。

Horizon 依赖于 GitOps 仓库实现回滚,Horizon 的每次发布会在 master 分支生成一条 commit 记录,当用户须要回滚利用实例时,只须要找到过后的部署记录,即一条 Pipelinerun 记录,代表了一次流水线运行。Horizon 通过该 Pipelinerun 记录找到对应的 commit 记录,而后将该记录之后的所有 commit 记录 revert,最初触发 ArgoCD 的同步,这样就实现了一次回滚。

GitOps 仓库

Horizon 通过拓展 Helm Chart,设计了一套 Template 零碎。Template 蕴含三个局部,Helm Chart,JsonSchema 和 ReactJsonSchemaForm。Horizon 会通过 ReactJsonSchemaForm 渲染表单,获取用户输出,并应用 JsonSchema 验证用户输出,确认无误后,记录到 GitOps 仓库的相干文件中。GitOps 仓库是一个 Helm Chart 仓库,部署时,ArgoCD 通过 Helm 渲染 Manifest,并将 Manifest 利用到 Kubernetes 中。Horizon 管理员能够通过自定义 Template,实现部署各种类型的利用,非常灵活。

以下为 GitOps 仓库构造

Chart.yaml 文件是 Helm Chart 的规范,通过 dependency 字段,援用事后定义的 Horizon Template。

apiVersion: v2  
name: demo  
version: 1.0.0  
dependencies:  
  - name: deployment  
    version: v0.0.1-ec06d596  
    repository: https://horizon-harbor-core.horizon.svc.cluster.local/chartrepo/horizon-template

application.yaml 蕴含了用户通过 ReactJsonSchemaForm 表单填写的数据。

deployment:
  app:
    envs:
    - name: test
      value: test
    spec:
      replicas: 1
      resource: x-small

pipeline-output.yaml 蕴含了 CI 阶段的输入,因为在 Horizon 中 CI 也是能够自定义的,所以该文件的内容也是不固定的。默认的 CI 脚本输入如下:

deployment:
  image: library/demo:v1
  git:
    branch: master
    commitID: 28992d8f35a6ef38d59181080b3728df9540d8d6
    url: https://github.com/horizoncd/springboot-source-demo.git
参数 形容
.Values.image CI 阶段构建 image 的全门路
.Values.git.{ref} 源代码仓库的援用类型,能够是 branch、tag、commit
.Values.git.commitID 构建代码的 commit ID
.Values.git.url 源代码的援用链接

pipeline.yaml 蕴含了 CI 阶段的配置信息,Horizon 管理员能够通过自定义 CI 以反对更多的构建类型

pipeline:
  buildType: dockerfile
  dockerfile:
    path: ./Dockerfile
参数 形容
.Values.buildType 该利用的构建类型,默认是“dockerfile”
.Values.dockerfile.path dockerfile 绝对于源代码仓库的门路
.Values.dockerfile.content dockerfile 的内容

sre.yaml 蕴含了一些管理员配置,比方 ingress、默认的超售比等等。应用 sre.yaml 文件批改管理员配置,既能够做到关注点拆散,又保障了 GitOps 利用批改的体验对立。比方能够通过配置 nodeAffinity,将利用部署到特定的节点上。SRE 在批改sre.yaml 后,也须要提交 PR 到 GitOps 仓库,并通过 review 机器人实现多人审核,合入到公布分支,最初在 Horizon 上执行公布,即可实现变更。

deployment: 
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: cloudnative/demo
            operator: In
            values:
            - "true"

system 目录下的文件记录了部署的元信息

env.yaml 记录了部署环境相干的信息

参数 形容
.Values.env.environment 环境名
.Values.env.region Kubernete 名
.Values.env.namespace Namespace
.Values.env.baseRegistry image 仓库的地址
.Values.env.ingressDomain ingress 域名
deployment:
  env:
    environment: local
    region: local
    namespace: local-1
    baseRegistry: horizon-harbor-core.horizon.svc.cluster.local
    ingressDomain: cloudnative.com

horizon.yaml 蕴含了该利用在 Horizon 中的信息

参数 形容
.Values.horizon.application 利用名
.Values.horizon.clusterID 集群 ID(这里的集群指利用实例,利用为配置汇合)
.Values.horizon.cluster 集群
.Values.horizon.template.name 模板名
.Values.horizon.template.release 模板版本
.Values.horizon.priority 优先级

restart.yaml Horizon 通过批改该文件内容,重启所有 Pod

参数 含意
.Values.restartTime 重启工夫
deployment:
  restartTime: "2023-01-06 18:28:49"

结语

通过以上部署模式,咱们能够很不便的治理数十个环境上百管控面管控面组件的部署,而且每个环境的配置都是独立的,批改对应的配置仓库不会影响其余环境的部署。同时,基于 Horizon 平台,咱们实现了部署的自动化,用户公布利用时,只须要填写表单,即可实现利用公布,无需运维人员染指,运维效率晋升 10 倍以上。Horizon 平台现在每天的公布数量曾经达到了 1000+,这是传统运维模式难以企及的。在达成高效公布的同时,也保障了公布的品质,每次公布都有对应的流水线记录,能够不便的回滚到任意版本。Horizon 将开发、运维、测试等多个团队的工作流程串联起来,实现了 DevOps 的理念。

GitOps 在云音乐的实际中,体现出了十分好的成果,但同时在实践中咱们也发现了一些问题:

  1. 批改不便:构想,如果咱们有多个环境,每个环境都对应一个配置仓库,那么一旦须要批改一个对立的值,那么须要批改所有仓库。
  2. 明码治理:Git 仓库中数据都是明文显示,并且 Git 仓库会记住所有的历史批改,所以放在 Git 仓库中的明文信息应该加密。尽管社区外面开发了一些相似于 git-secret 的工具,但应用起来还是不太不便。这里须要留神的是,明码治理指将密钥搁置于 Git 仓库中,和 Gitlab、Github secret 并不统一。将明码放在 secret 中,就失去了 Git 仓库提供的 版本治理、回滚、审计等能力。
  3. 规范不对立:对于回滚的实现,到底是批改配置,reset 到对应版本;还是通过运行 CI,重新部署到环境中?对于不同环境的雷同利用部署,到底是抉择多个仓库,还是一个仓库多个环境?这些都没有对立的规范,须要依据本身状况抉择。

所以 GitOps 并不是银弹,使用者任须要基于本身状况判断,抉择最适宜本人的计划。然而 GitOps 作为随着云原生出世的 DevOps 办法,还在疾速倒退中,置信以上提到的问题,当前都会逐步被解决。咱们也会继续关注、尝试 GitOps 畛域的最新技术和解决方案。如果你对 Horizon 或者 GitOps,能够退出咱们,和咱们一起探讨。

本文公布自网易云音乐技术团队,文章未经受权禁止任何模式的转载。咱们长年招收各类技术岗位,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe(at)corp.netease.com!

退出移动版