第87节 OpenIM 使用 ArgoCD 生产级部署


❤️💕💕新时代拥抱云原生,云原生具有环境统一、按需付费、即开即用、稳定性强特点。Myblog:http://nsddd.topopen in new window


[TOC]

前言

这一期是一个简单的实战,实践。下一期深入学习 GitOps 的设计理念,流程和工具。

开始

ArgoCD 之前也介绍过很多。我们直接开始部署:

kubectl create namespace argocd

安装清单包括引用 argocd 命名空间的 ClusterRoleBinding 资源。如果您要将Argo CD安装到不同的名称空间中,请确保更新名称空间引用。

然后,部署 ArgoCD。

kubectl apply -n argocd -f https://ghproxy.com/https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

https://ghproxy.com 是一个代理加速

最后,等待 argocd 命名空间所有的工作负载处于就绪状态。

kubectl wait --for=condition=Ready pods --all -n argocd --timeout 300s

请注意,由于云厂商 Kubernetes 版本存在差异,所以如果你在安装过程中发现 argocd-repo-server 工作负载一直无法启动,可以尝试删除 argocd-repo-server Deployment seccompProfile 节点的内容。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: argocd-repo-server
spec:
  template:
    spec:
      securityContext:
        seccompProfile:
          type: RuntimeDefault

安装 ArgoCD CLI

为了更加方便地配置 ArgoCD,官方还为我们提供了 CLI 工具。不同操作系统的安装方法有所差异,使用 BIN 安装

  • https://github.com/argoproj/argo-cd/releases

应用程序定义、配置和环境应该是声明性的,并受版本控制。应用程序部署和生命周期管理应该是自动化的、可审计的和易于理解的。

如果您对UI、SSO、多集群功能不感兴趣,那么您可以只安装核心Argo CD组件:

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/core-install.yaml

这个默认安装将有一个自签名证书,如果没有一些额外的工作,就无法访问。执行以下操作之一:

  • 按照说明配置证书(并确保客户端操作系统信任它)。
  • 将客户端操作系统配置为信任自签名证书。
  • 在本指南的所有Argo CD CLI操作中使用 --insecure 标志。

下载Argo CD CLI

从https://github.com/argoproj/argo-cd/releases/latest下载最新的Argo CD版本。更详细的安装说明可以通过CLI安装文档找到。

本地访问 ArgoCD

要在本地访问 ArgoCD,最简单的方式是通过端口转发来完成。你可以使用下面的命令来进行端口转发。

kubectl port-forward service/argocd-server 8080:80 -n argocd

接下来,使用浏览器打开:http://127.0.0.1:8080,这样就可以访问 ArgoCD 控制台了,如下图所示。

ArgoCD 的默认账号为 admin,密码可以通过下面的命令来获取。

$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

Argo CD是作为一个kubernetes控制器实现的,它可以持续监控正在运行的应用程序,并将当前的实时状态与所需的目标状态(如Git repo中所指定的)进行比较。活动状态偏离目标状态的已部署应用程序被视为 OutOfSync 。Argo CD报告并可视化差异,同时提供自动或手动将实时状态同步回所需目标状态的工具。

在Git存储库中对目标状态所做的任何修改都可以自动应用并反映在指定的目标环境中。

GitOps 工作流总览

到这里,你是不是已经迫不及待想要构建工作流了?别急,在创建 GitOps 工作流之前,我们先来认识一下一个完整 GitOps 工作流都需要哪些关键步骤。

image-20231113152036458

我们可以把这个完整的 GitOps 工作流分成三个部分来看。

  • 第一部分是开发者推送代码到 GitHub 仓库,然后触发 GitHub Action 自动构建。
  • 第二部分是 GitHub Action 自动构建,它包括下面三个步骤。
1. 构建示例应用的镜像。
2. 将示例应用的镜像推送到 Docker Registry 镜像仓库。
3. 更新代码仓库中 Helm Chart values.yaml 文件的镜像版本。

第三部分的核心是 ArgoCD,它包括下面两个步骤。

4. 通过定期 Poll 的方式持续拉取 Git 仓库,并判断是否有新的 commit。
5. 从 Git 仓库获取 Kubernetes 对象,与集群对象进行实时比较,自动更新集群内有差异的资源。

在之前的课程中,我们已经为示例应用创建好了 GitHub Action 来自动构建镜像,但还缺少自动更新 Helm Chart values.yaml 文件的镜像版本逻辑,我会在稍后进行配置。

现在,我们开始创建 GitOps 工作流中的第三部分,也就是创建 ArgoCD 应用,实现 Kubernetes 资源的自动同步。

创建 ArgoCD 应用

我们以示例应用为例子来创建 ArgoCD 应用,这里主要分成两个步骤。

  • 配置仓库访问权限。(需要注意的是,OpenIM 项目是公开的,所以没必要配置仓库的访问权限)
  • 创建 ArgoCD 应用。

配置 ArgoCD 仓库访问权限(可选)

在实际场景下,我们存放应用定义的仓库一般都是私有仓库,这就需要为 ArgoCD 配置仓库访问权限。

你可以通过下面的 ArgoCD CLI 工具来为 ArgoCD 添加仓库访问权限。

在使用 ArgoCD CLI 工具之前,你需要先执行 argocd login 命令登录。

argocd login 127.0.0.1:8080 --insecure

注意,这里我们指定了 ArgoCD 的服务端地址为 127.0.0.1:8080,并且使用了 --insecur 参数来跳过 SSL 认证,你需要保持在上面运行的端口转发命令才能够顺利登录。

登录成功后,通过 argocd repo add 命令添加你的示例应用仓库。

$ argocd repo add https://github.com/lyzhang1999/kubernetes-example.git --username $USERNAME --password $PASSWORD

这里要注意将仓库地址修改为你实际的 GitHub 仓库地址,并将 $USERNAME 替换为 GitHub 账户 ID,将 $PASSWORD 替换为 GitHub Personal Token。你可以在这个页面创建 GitHub Personal Token,并赋予仓库相关权限,如下图所示。

image-20231113155844653

接下来,就可以创建 ArgoCD 应用了。ArgoCD 同时支持使用 Helm Chart、Kustomize 和 Manifest 来创建应用,这里我们以示例应用的 Helm Chart 为例。

你可以通过 argocd app create 命令来创建应用。

$ argocd app create example --sync-policy automated --repo https://github.com/lyzhang1999/kubernetes-example.git --revision main --path helm --dest-namespace gitops-example --dest-server https://kubernetes.default.svc --sync-option CreateNamespace=true

这里我简单解释一下每个参数的作用。

  • –sync-policy 参数代表设置自动同步策略。automated 的含义是自动同步,也就是说当集群内的资源和 Git 仓库 Helm Chart 定义的资源有差异时,ArgoCD 会自动执行同步操作,实时确保集群资源和 Helm Chart 的一致性。
  • –repo 参数表示 Helm Chart 的仓库地址。这里的值是示例应用的仓库地址,注意需要替换成你实际的 Git 仓库地址。
  • –revision 参数表示需要跟踪的分支或者 Tag,这里我们让 ArgoCD 跟踪 main 分支的改动。
  • –path 参数表示 Helm Chart 的路径。在示例应用中,存放 Helm Chart 的目录是 helm 目录。
  • –dest-namespace 参数表示命名空间。这里指定了 gitops-example 命名空间,注意,这是一个不存在的命名空间,所以我们额外通过 --sync-option 参数来让 ArgoCD 自动创建这个命名空间。
  • –dest-server 参数表示要部署的集群,https://kubernetes.default.svc 表示 ArgoCD 所在的集群。

查看 ArgoCD 同步状态

创建好应用之后,GitOps 工作流中的自动同步部分也就建立起来了。现在,你可以打开 ArgoCD 控制台,进入左侧的“Application”菜单来查看示例应用详情。

image-20231113161506439

在应用详情页面,我们需要重点关注三个状态。

APP HEALTH:应用整体的健康状态,它包含下面三个值。

  • Progressing:处理中
  • Healthy:健康状态
  • Degraded:宕机

CURRENT SYNC STATUS: 应用定义和集群对象的差异状态,也包含下面三个值。

  • Synced:完全同步
  • OutOfSync:存在差异
  • Unknown:未知

LAST SYNC RESULT:最后一次同步到 Git 仓库的信息,包括 Commit ID 和提交者信息。

访问应用

当应用健康状态变为 Healthy 之后,我们就可以访问应用了。

在这之前,如果你已经在 example 命名空间下手动部署了示例应用,为了避免 Ingress 策略冲突,你需要先删除这个命名空间。

$ kubectl delete ns example

然后,使用浏览器访问 http://127.0.0.1,你应该能看到示例应用的界面,如下图所示。

连接 GitOps 工作流

在完成 ArgoCD 的应用配置之后,我们就已经将示例应用的 Helm Chart 定义和集群资源关联起来了,但整个 GitOps 工作流还缺少非常重要的一部分,就是我在上面提到的自动更新 Helm Chart values.yaml 文件镜像版本的部分,我在下面这张示意图中用“❌”把这个环节标记了出来。

image-20231113161933798

在这部分工作流没有打通之前,提交的新代码虽然会构建出新的镜像,但是 Helm Chart 定义的镜像版本并不会产生变化,这会导致 ArgoCD 不能自动更新集群内工作负载的镜像版本。

要解决这个问题,我们还需要在 GitHub Action 中添加自动修改 Helm Chart 并重新推送到仓库操作。

接下来,我们修改示例应用的 .github/workflows/build.yaml 文件,在“Build frontend and push”阶段后面添加一个新的阶段,代码如下。

- name: Update helm values.yaml
  uses: fjogeleit/yaml-update-action@main
  with:
    valueFile: 'helm/values.yaml'
    commitChange: true
    branch: main
    message: 'Update Image Version to ${{ steps.vars.outputs.sha_short }}'
    changes: |
      {
        "backend.tag": "${{ steps.vars.outputs.sha_short }}",
        "frontend.tag": "${{ steps.vars.outputs.sha_short }}"
      }

在这里,我使用了 GitHub Action 中 yaml-update-action 插件来修改 values.yaml 文件并把它推送到仓库。如果你是使用 GitLab 或者 Tekton 构建镜像,可以调用 yq 命令行工具来修改 YAML 文件,再使用 git 命令行将变更推送到仓库。

到这里,一个完整的 GitOps 工作流就建立好了。

体验 GitOps 工作流

接下来,你可以尝试修改 frontend/src/App.js 文件,例如修改文件第 49 行的“Hi! I am a geekbang”。修改完成后,将代码推送到 GitHub 仓库 main 分支,此时,GitHub Action 会自动构建镜像,并且还会更新代码仓库中 Helm values.yaml 文件的镜像版本。

image-20231113162058980

ArgoCD 默认每 3 分钟会拉取仓库检查是否有新的提交,你也可以在 ArgoCD 控制台手动点击 Sync 按钮来触发同步。

image-20231113162159315

ArgoCD 同步完成后,我们可以在“LAST SYNC RESULT”一栏中看到 GitHub Action 修改 values.yaml 的提交记录,当应用状态为 Healthy 时,我们就可以访问新的应用版本了。

image-20231113162210506

从截图可以看出,前端界面输出内容为“Hi, I am GitOps workflow”,说明 ArgoCD 已经将新版本的应用部署到集群中了。

监听镜像

在开发和发布分工明确的团队中,我更推荐你将源码和应用定义分离,考虑到安全性和发布的严谨性,也尽量不要通过 CI 直接修改应用定义。

更合理的研发规范设计应该是这样的:开发负责编写代码,并通过 CI 生成制品,也就是 Docker 镜像,并对生成的制品负责。而基础架构部门或者 SRE 团队则对应用定义负责。在发布环节,开发可以随时控制要发布的镜像版本,而无需关注其他的应用细节,他们之间的工作流程图如下。

image-20231113163746574

从上面这张工作流程图我们可以看出,开发和 SRE 团队各司其职,只操作和自己相关的 Git 仓库,互不干扰。但 SRE 团队要怎么知道开发团队什么时候发布以及发布什么版本的镜像呢?

最原始的办法是:开发在需要发布的时候将镜像版本告诉 SRE 团队,SRE 团队手动修改 Helm Chart 镜像版本并推送到 Git 仓库,等待 ArgoCD 同步完成。

借助 ArgoCD Image Updater,我们可以让 ArgoCD 自动监控镜像仓库的更新情况,一旦工作负载的镜像版本有更新,ArgoCD 就会自动将工作负载升级为新的镜像版本,并且还可以自动将镜像的版本号回写到 Helm Chart 仓库中,保持应用定义和集群状态的一致性。

这节课,我会进一步改造在上一节课创建的 GitOps 工作流,并加入 ArgoCD Image Updater,实现自动监听镜像变更以及回写 Helm Chart。

image-20231113163823286

此外,由于在日常开发中,我们一般会采用多分支进行开发,这就随时可能产生新的镜像版本。为了将开发过程和需要发布到生产环境的镜像区分开,我们会为 Main 分支构建出来的镜像增加一个 Prefix 标识,例如 main-${commit_Id},并配置 ArgoCD Image Updater 只监控包含特定标识的镜像版本。

最终实现的效果是,当开发将代码提交到 Git 仓库 Main 分支后,将触发自动构建,并将新的镜像版本推送到镜像仓库。ArgoCD Image Updater 会以 Poll 的方式每 2 分钟检查一次工作负载的镜像是否有新的版本,如果有,那么就将工作负载的镜像更新为最新版本,并将镜像版本号写入到存放 Helm Chart 的仓库中。

安装:

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml

创建 Image Pull Secret(可选)

由于 ArgoCD 会主动 Poll 镜像仓库来检查是否存在新版本,如果你使用的是私有镜像仓库,那么你需要创建 Secret 对象,以便为 ArgoCD 提供访问镜像仓库的权限。

以 DockerHub 仓库为例,执行下面的命令来创建 Secret 对象。

$ kubectl create -n argocd secret docker-registry dockerhub-secret \
  --docker-username $DOCKER_USERNAME \
  --docker-password $DOCKER_PERSONAL_TOKEN \
  --docker-server "https://registry-1.docker.io"

注意将 $DOCKER_USERNAME$DOCKER_PERSONAL_TOKEN 替换为 Docker Hub 用户名和个人凭据。如果你忘记如何创建 Docker Personal Token 了,可以查看第 16 讲的内容。另外关于如何为其他镜像仓库类型配置凭据,你可以查看这份文档。

创建 Helm Chart 仓库

接下来,我们需要为示例应用的 helm 目录单独创建一个 Git 仓库,在将 kubernetes-example克隆到本地后,执行下面的命令。

 $ cp -r ./kubernetes-example/helm ./kubernetes-example-helm

然后,进入 kubernetes-example-helm 目录并初始化 Git。

$ cd kubernetes-example-helm && git init

前往 GitHub 创建一个新的仓库,将其命名为 kubernetes-example-helm。

将 kubernetes-example-helm 提交到远端仓库中。

$ git add .
$ git commit -m "first commit"
$ git branch -M main
$ git remote add origin https://github.com/lyzhang1999/kubernetes-example-helm.git
$ git push -u origin main

创建 ArgoCD Application

创建好 kubernetes-example-helm 仓库之后,接下来我们需要使用它创建一个新的应用。

删除旧应用(可选)

在正式创建新的应用之前,为了避免 Ingress 策略冲突,如果你已经按照上节课的内容创建了 ArgoCD example 应用,需要先删除应用及其资源,你可以使用下面的命令来删除应用。

$ argocd app delete example --cascade

配置仓库访问权限

此外,上节课我们创建 ArgoCD 应用时,虽然同样配置了仓库访问权限,但这里的步骤额外还实现了一个重要的功能:为 ArgoCD Image Updater 提供回写 kubernetes-example-helm 仓库的权限。

要配置仓库访问权限,你可以使用 argocd repo add 命令。

$ argocd repo add https://github.com/lyzhang1999/kubernetes-example-helm.git --username $USERNAME --password $PASSWORD

注意要将仓库地址修改为你新创建的用于存放 Helm Chart 的 GitHub 仓库地址,并将 $USERNAME 替换为 GitHub 账户 ID,将 $PASSWORD 替换为 GitHub Personal Token。你可以在这个页面创建 GitHub Personal Token,并赋予仓库相关权限。

创建 ArgoCD 应用

接下来我们正式创建 ArgoCD 应用。在上一节课中,我们是使用 argocd app create 命令创建的 ArgoCD 应用 。实际上,它会创建一个特殊类型的资源,也就是 ArgoCD Application,它和 K8s 其他标准的资源对象一样,也是使用 YAML 来定义的。

在这里,我们直接使用 YAML 来创建新的 Application,将下面的文件内容保存为 application.yaml。

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: example
  annotations:
    argocd-image-updater.argoproj.io/backend.allow-tags: regexp:^main
    argocd-image-updater.argoproj.io/backend.helm.image-name: backend.image
    argocd-image-updater.argoproj.io/backend.helm.image-tag: backend.tag
    argocd-image-updater.argoproj.io/backend.pull-secret: pullsecret:argocd/dockerhub-secret
    argocd-image-updater.argoproj.io/frontend.allow-tags: regexp:^main
    argocd-image-updater.argoproj.io/frontend.helm.image-name: frontend.image
    argocd-image-updater.argoproj.io/frontend.helm.image-tag: frontend.tag
    argocd-image-updater.argoproj.io/frontend.pull-secret: pullsecret:argocd/dockerhub-secret
    argocd-image-updater.argoproj.io/image-list: frontend=lyzhang1999/frontend, backend=lyzhang1999/backend
    argocd-image-updater.argoproj.io/update-strategy: latest
    argocd-image-updater.argoproj.io/write-back-method: git
spec:
  destination:
    namespace: gitops-example-updater
    server: https://kubernetes.default.svc
  project: default
  source:
    path: .
    repoURL: https://github.com/lyzhang1999/kubernetes-example-helm.git
    targetRevision: main
  syncPolicy:
    automated: {}
    syncOptions:
      - CreateNamespace=true

然后,使用 kubectl apply 命令创建 ArgoCD Application,效果等同于使用 argocd app create 命令创建应用。

$ kubectl apply -n argocd -f application.yaml

ArgoCD Image Updater 通过 Application Annotations 标签来实现对应的功能,我简单解释一下每一个标签的作用。

  • argocd-image-updater.argoproj.io/image-list:指定需要监听的镜像,这里我们指定示例应用的前后端镜像 lyzhang1999/frontend 和 lyzhang1999/backend,同时为前后端镜像法指定了别名,分别为 frontend 和 backend。这里的别名非常重要,会影响下面所有的设置。
  • argocd-image-updater.argoproj.io/update-strategy:指定镜像更新策略。注意,latest 并不代表监听 latest 镜像版本,而是以最新推送的镜像作为更新策略。此外,semver 策略可以识别最高语义化版本的标签,digest 策略可以用来区分同一 Tag 下不同镜像 digest 的变更。
  • argocd-image-updater.argoproj.io/write-back-method:表示将镜像版本回写到镜像仓库。注意,这里对仓库的写权限来源于使用 argocd repo add 命令为 ArgoCD 配置的仓库访问权限。
  • argocd-image-updater.argoproj.io/< 镜像别名 >.pull-secret:为不同的镜像别名指定镜像拉取凭据。
  • argocd-image-updater.argoproj.io/< 镜像别名 >.allow-tags:配置符合更新条件的镜像 Tag,在这里我们使用正则表达式匹配那些镜像 Tag 以 main 开头的镜像版本,其他镜像版本则忽略。
  • argocd-image-updater.argoproj.io/< 镜像别名 >.helm.image-tag:配置 Helm Chart values.yaml 镜像版本所在的节点,在示例应用中,backend.tag 和 frontend.tag 是 values.yaml 配置镜像版本的节点,ArgoCD 在回写仓库时将会覆盖这个值。

体验 GitOps 工作流

接下来,你可以尝试修改 frontend/src/App.js 文件,例如修改文件第 49 行的“Hi! I am a geekbang”内容。修改完成后,将代码推送到 GitHub 的 main 分支。

此时会触发两个 GitHub Action 工作流。其中,当 build-every-branch 工作流被触发时,它将构建 Tag 为 main 开头的镜像版本,并将其推送到镜像仓库中,如下图所示。

image-20231113165517265

和我们上一节课介绍的另一个 GitHub Action 工作流不同的是,它也不会去主动修改 kubernetes-example-helm 仓库的 values.yaml 文件,在完成镜像推送后工作流也就结束了。

与此同时,ArgoCD Image Updater 将会每 2 分钟从镜像仓库检索 frontend 和 backend 的镜像版本,一旦发现有新的并且以 main 开头的镜像版本,它将自动使用新版本来更新集群内工作负载的镜像,并将镜像版本回写到 kubernetes-example-helm 仓库。

在回写时,ArgoCD Image Updater 并不会直接修改仓库的 values.yaml 文件,而是会创建一个专门用于覆盖 Helm Chart values.yaml 的 .argocd-source-example.yaml 文件。

image-20231113165601860

当我们看到这个文件时,说明 ArgoCD Image Updater 已经触发了镜像更新,并且成功将镜像版本回写到了镜像仓库。同时,这个文件记录了详细的覆盖 values.yaml 值的策略。

helm:
  parameters:
  - name: frontend.image
    value: lyzhang1999/frontend
    forcestring: true
  - name: frontend.tag
    value: main-b99bc73
    forcestring: true
  - name: backend.image
    value: lyzhang1999/backend
    forcestring: true
  - name: backend.tag
    value: main-b99bc73
    forcestring: true

这样,当 ArgoCD 在做自动同步时,会将这份文件的内容覆盖 values.yaml 对应的值,比如 frontend.tag 的值会被覆盖为 main-b99bc73,这样,回写后的 Helm Chart 和集群内资源对象就仍然能够保持一致性。

到这里,我们就完成了通过监听新镜像版本来触发 GitOps 工作流的整个过程。

值得注意的是,在实际的业务场景中,我们一般会使用多分支的模式来开发。这意味着每个分支的每个提交都会产生新的镜像版本,所以,为了区分开发过程的镜像和需要被发布到生产环境的镜像,我在这个例子中约定了以 main 开头的镜像版本即为需要发布到生产环境的镜像版本。你可以根据项目的实际情况做调整,例如使用诸如 v1.0.0 的版本号来区分,同时更新 argocd-image-updater.argoproj.io/< 镜像别名 >.allow-tags 字段的正则表达式。

Features

  • 将应用程序自动部署到指定的目标环境
  • 支持多种配置管理/模板工具(Kustomize、Helm、Jsonnet、plain-YAML)
  • 能够管理和部署到多个群集
  • SSO集成(OIDC,OAuth2,LDAP,SAML 2.0,GitHub,GitLab,Microsoft,LinkedIn)
  • 用于授权的多租户和RBAC策略
  • Rollback/Roll-anywhere到Git存储库中提交的任何应用程序配置
  • 应用程序资源的健康状况分析
  • 自动配置漂移检测和可视化
  • 自动或手动将应用程序同步到所需状态
  • 提供应用程序活动实时视图的Web UI
  • Webhook集成(GitHub,BitBucket,GitLab)
  • 用于自动化的访问令牌
  • 应用程序事件和API调用的审计跟踪
  • 参数覆盖用于覆盖Git中的helm参数

END 链接