OpenShift中GitOps的深入和思考


  为了实现高可用性,不少客户会在生产环境中遇到多集群多实例的部署问题,相对比较简单的做法是分别部署多套独立的集群,然后通过cicd的流水线,比如jenkins把应用发布到不同的多套集群环境中间去,这种做法多了以后,存在的问题也比较明显,主要存在于

  • 后端发布的拓扑结构存在于发布流水线脚本中,一旦需要调整,很难运维。
  • 中间有后端环境变化,比如需要多加一个集群,那可能还需要修改流水线,重新运行流水线
  • 发布结构的一致性很难保证,有时候为了查错或者应对突然的负载情况,修改了应用的实例或者其他配置,这些改动和前期的规划很难保持一致。

分布式环境最大的问题触发点还是在于规模,在很多客户环境下,部署30-50套应用,采用各种方式其实区别是不明显的,因为应用有限,运维的人数也有限,大家基本上对情况有个基本了解。

但一旦部署规模上去以后,在一个大规模的分布式环境中,面对上百上千的应用,我们要考虑的是如何把握所有应用部署的架构,出现问题后如何最快的恢复,随着时间的推移怎样保证部署架构和设计的一致性。

  GitOps就是帮助我们去实现Infrasture as a code的利器,他是一个专门针对多集群的环境,采用yaml文件的描述性的持续发布平台。OpenShift gitops主要是基于ArgoCD的组件进行产品化,覆盖了如下的功能:

  • 自动化的ArgoCD的安装和升级
  • 多集群的配置管理
  • 应用部署结构,环境,以及发布历史的管理
  • 和OpenShift pipeline(基于teckton)端到端的集成

1.基本部署单元Application(CRD)

  作为ArgoCD本身的项目来说,他的部署单元是一个Application(ArgoCD带来的CRD),这个Application的配置主要是包含了四个方面

  • 代码的Git Repository
  • Revision/Branch
  • 部署的集群和命名空间信息
  • 同步策略

比较典型的一个例子如下:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: pricelist-app
  namespace: argocd
spec:
  destination:
    namespace: argocd
    server: https://kubernetes.default.svc
  project: default
  source:
    path: app-of-apps/apps
    repoURL: https://github.com/christianh814/gitops-examples
    targetRevision: master
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

 Application的部署支持Helm和Kustomize(缺省), ArgoCD本身就是以Application这个单元为中心的。所以你在ArgoCD里看到的视图也是基于Application来进行展示

 Application的问题和限制:

  • 每个应用只能定义一个Cluster
  • 只有一种资源类型,或者选择git,或者选择helm
  • 只能定义一个git repo和helm chart

如果我是在一个实际的环境中,可能是一堆的应用,需要部署在3,5个集群上,那就需要针对这些环境每个都去写Application,如果我有10个应用,那可能就需要写10*5个Application了。。。。

为了解决这个问题,在ArgoCD中引入了ApplicationSets的概念。

2.复杂部署场景下的ApplicationSets

核心点如下:

  • App Pattern支持定义批量的应用(用yaml和Helm都可以,一键部署一批)
  • 管理Application的生命周期
  • 构建Application的Factory模式

  主要引入了Generators的概念,主要有三类

  • List Generators
  • Cluster Generators
  • Git Generators

List Generators

简单来看List Generators, 发布应用到多集群

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: bgd
  namespace: openshift-gitops
spec:
  generators:
  - list:
      elements:
      - cluster: cluster1
        url: https://api.cluster1.chx.osecloud.com:6443
      - cluster: cluster2
        url: https://api.cluster2.chx.osecloud.com:6443
      - cluster: cluster3
        url: https://api.cluster3.chx.osecloud.com:6443
  template:
    metadata:
      name: '{{cluster}}-bgd'
    spec:
      project: default
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
      source:
        repoURL: https://github.com/christianh814/gitops-examples
        targetRevision: master
        path: applicationsets/list-generator/v0.1/overlays/{{cluster}}
      destination:
        server: '{{url}}'
        namespace: bgd

Cluster generator

ArgoCD 将集群信息保存到Secret中间去,因此查看Secret的信息知道目前的集群

$ oc get secrets -n openshift-gitops -l argocd.argoproj.io/secret-type=cluster
NAME                                           TYPE DATA   AGE
cluster-api.cluster1.chx.osecloud.com-74873278 Opaque   3  23m
cluster-api.cluster2.chx.osecloud.com-2320437559   Opaque   3  23m
cluster-api.cluster3.chx.osecloud.com-2066075908   Opaque   3  23m


$ argocd cluster list
SERVER                                  NAME    VERSION  STATUS  MESSAGE
https://api.cluster1.chx.osecloud.com:6443  cluster1 1.20 Successful  
https://api.cluster2.chx.osecloud.com:6443  cluster2 1.20 Successful  
https://api.cluster3.chx.osecloud.com:6443  cluster3 1.20 Successful

构建ApplicationSet

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: bgd
  namespace: openshift-gitops
spec:
  generators:
  - clusters: {} # targets all clusters
  template:
    metadata:
      name: '{{name}}-bgd' # the "name" is the name field in the secret
    spec:
      project: default
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
      source:
        repoURL: https://github.com/christianh814/gitops-examples
        targetRevision: master
        path: applicationsets/cluster-generator/overlays/dev/
      destination:
        server: '{{server}}' # "server" from the field in the secret
        namespace: bgd

也可以基于label模式部署到特定的集群,比如开发集群。

Git Generators

Git Generators包含两类,一种是目录类型,一种是文件类型,从例子上看,目录类型更多的是针对了部署多个应用,例如

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: pricelist
  namespace: openshift-gitops
spec:
  generators:
  - git:
      repoURL: https://github.com/christianh814/gitops-examples
      revision: master
      directories:
      - path: applicationsets/git-dir-generator/apps/*
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
      source:
        repoURL: https://github.com/christianh814/gitops-examples
        targetRevision: master
        path: '{{path}}'
      destination:
        server: https://api.cluster1.chx.osecloud.com:6443
        namespace: pricelist

在apps下,可以看到三个应用,支持通过不同的模式部署

$ tree applicationsets/git-dir-generator/apps
applicationsets/git-dir-generator/apps
├── pricelist-config
│   ├── kustomization.yaml
│   ├── pricelist-config-ns.yaml
│   └── pricelist-config-rb.yaml
├── pricelist-db
│   ├── Chart.yaml
│   └── values.yaml
└── pricelist-frontend
    ├── kustomization.yaml
    ├── pricelist-deploy.yaml
    ├── pricelist-job.yaml
    ├── pricelist-route.yaml
    └── pricelist-svc.yaml
3 directories, 10 files

而对于文件方式,需要指定到一个json文件config.json

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: bgd
  namespace: openshift-gitops
spec:
  generators:
  - git:
      repoURL: https://github.com/christianh814/gitops-examples
      revision: master
      files:
      - path: "applicationsets/git-generator/cluster-config/**/config.json"
  template:
    metadata:
      name: '{{cluster.name}}-bgd'
    spec:
      project: default
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
      source:
        repoURL: https://github.com/christianh814/gitops-examples
        targetRevision: master
        path: applicationsets/git-generator/overlays/{{cluster.overlay}}
      destination:
        server: '{{cluster.server}}'
        namespace: bgd
$ tree applicationsets/git-generator/
applicationsets/git-generator/
├── appset-bgd.yaml
├── base
│   ├── bgd-deployment.yaml
│   ├── bgd-namespace.yaml
│   ├── bgd-route.yaml
│   ├── bgd-svc.yaml
│   └── kustomization.yaml
├── cluster-config
│   ├── cluster1
│   │   └── config.json
│   ├── cluster2
│   │   └── config.json
│   └── cluster3
│       └── config.json
└── overlays
    ├── cluster1
    │   ├── bgd-deployment.yaml
    │   └── kustomization.yaml
    ├── cluster2
    │   ├── bgd-deployment.yaml
    │   └── kustomization.yaml
    └── cluster3
        ├── bgd-deployment.yaml
        └── kustomization.yaml

config.json更多的指定了被部署的集群信息。

 3.状态的同步

ArgoCD缺省会每个3分钟进行当前状态的同步,如果你直接修改了集群中的配置,比如replicas, 和配置不符会由ArgoCD直接修改过来。

这个同步时间应该是可以设置,暂时没有研究。

4.一些思考

GitOps是一个很好的理念,在ArgoCD中也针对实际的场景进行实现,并且功能继续在增强过程中,但在我目前接触的客户中相对来说比较少的应用,自己觉得主要是几个方面原因:

  • 很多客户的部署规模有限,环境复杂性不高,基本运维人员能搞定
  • 在生产环境中,大家还是习惯了直接去改,毕竟改一个集群,不会影响到其他的集群,万一出错,还有别的集群顶着,而如果修改git里的配置,那可能会影响到其他集群,带来关联性影响。

关于第二点,我认为更好的办法是生产环境中停掉自动同步功能,在确认修改一个没有问题以后,再通过修改git,通过argocd同步到其他环境。

总的来说,除了多集群发布和同步的功能,我们需要仔细考虑的一点是,如果一觉醒来,当我们的环境出现各种问题,需要重新构建的时候,从平台方出发,我们如何能在最短的时间内将现有的应用重新发布。

我们不太可能要求在让所有应用团队基于流水线都进行一遍发布,那唯一可行的就是,通过gitops定义现有的部署结构的完整视图,然后在新的环境中批量重建,也许这种场景下的最佳办法。