如果你仍在本地终端中使用kubectl apply命令来操作集群,那么你实际上并不是在管理集群,而是在“照看”它而已。

我曾经花了很多个夜晚盯着终端屏幕,试图弄清楚为什么 staging 环境会突然出问题,而其实根本没有人修改过相关配置。

我们都经历过这种情况吧?在这里手动修改一些设置,在那里快速应用一个临时修复方案,结果突然间,你的 Git 仓库就不再是一个可靠的数据来源了,而变成了记录过去系统运行状态的历史文件。

如果没有一套可靠的策略,Kubernetes 部署很快就会陷入混乱:配置逐渐偏离初始设定,回滚操作变得十分麻烦,而且也根本无法追踪所有的变更过程。我是通过惨痛的经验才认识到,仅仅将配置文件存储在 Git 中是远远不够的。如果你的集群没有实时响应你所做的代码修改,那么你就仍然存在漏洞。

GitOps 能够填补这个漏洞。它能让你的集群成为你 Git 仓库的“镜像”——如果某项配置不在 Git 中,那就意味着它根本不存在。

在这个教程中,你不仅仅会学习相关理论,还会从零开始实践一个“零干预”的部署流程。我们将使用 Argo CD、GitHub Actions 以及 Argo CD Image Updater 来构建一个系统:每当你执行 git push 命令时,这个系统就会立即编译代码、添加标签并完成部署。

一个完整的 GitOps CI/CD 工作流程示意图。开发人员将代码推送到 GitHub 仓库,GitHub Actions 会自动构建新的 Docker 镜像并将其上传到 DockerHub;Argo CD Image Updater 会定期从 DockerHub 获取最新的镜像标签,并将这些变更同步回 GitHub 仓库;最后 Argo CD 服务器会将更新后的配置文件应用到实际的 Kubernetes 集群中。

目录

  1. 先决条件

  2. GitOps 的真正含义

  3. Argo CD 是什么?它是如何实现 GitOps 的?

  4. 准备应用程序的源代码及仓库结构

  5. 使用 GitHub Actions 自动化镜像构建过程

  6. 如何安装并使用 Argo CD

  7. 了解 Argo CD 的工作原理

  8. 部署应用程序的配置文件

  9. 使用 Argo CD Image Updater 自动化更新过程

  10. 总结

先决条件

在开始之前,请确保你的环境中已经具备了以下条件:

  • 一个 GitHub 仓库:你需要一个仓库(例如 my-gitops-demo)作为你的“单一真实数据源”。如果你是从零开始学习本教程的,可以先创建一个空仓库。

  • 一个 DockerHub 账户:这个账户将用作你的容器注册平台。你需要使用它来构建、推送以及存储 GitHub Actions 生成的 Docker 镜像。

  • 一个正在运行的 Kubernetes 集群:你可以使用像 Minikube 或 Kind 这样的本地解决方案,也可以使用 Amazon EKS 或 GKE 这类云托管服务。

  • Kubernetes 工具:确保已经安装了 kubectl,并且配置好了它与你的集群之间的通信。

  • 基本的 Kubernetes 知识:你应该熟悉 Pod、Deployment 和 Service 这些基本的 Kubernetes 概念。

已有项目的读者须知

如果你已经有一个项目,并且想要将其迁移到这种 GitOps 工作流程中,你不需要从头开始。你可以按照以下三个步骤来调整你的现有仓库:

  1. 标准化你的配置文件:将所有现有的 Kubernetes YAML 文件放到项目根目录下的专门用于存储配置文件的文件夹中。

  2. 为你的服务创建 Docker 镜像:确保每一个你打算部署的服务在其对应的子目录中都有一份 Dockerfile(例如,/main-api/Dockerfile)。

  3. 为自动化流程做好准备:准备好用我们在后续章节中介绍的自动化标签策略来替换当前 CI 流程中的手动 kubectl apply 操作。

GitOps 的真正含义

从本质上讲,GitOps 是一种运营框架,它使用 Git 作为你的基础设施和应用程序的“单一真实数据源”。在传统的部署环境中,你可能会在笔记本电脑上运行 kubectl apply -f deployment.yaml 这样的命令。这种做法会导致无法追踪谁修改了什么内容,进而使得集群状态变得混乱,没有人能够复现这些配置。

GitOps 遵循四个关键原则:

  1. 声明性配置:你只需要描述你期望看到的系统状态(例如“部署 3 个 Nginx 实例”),而不需要指定具体的操作命令。

  2. 版本控制与不可变原则:你的所有系统配置都存储在 Git 中。如果部署失败,你可以使用 git revert 命令恢复到之前的正常状态。

  3. 自动拉取更新:专门的软件代理会自动从 Git 仓库中获取最新的配置信息。

  4. 持续同步:系统会不断检查配置是否发生偏差。如果开发人员手动修改了集群中的某个服务,Argo CD 会自动将其恢复到与 Git 中的配置一致的状态。

什么是 Argo CD 以及它是如何实现 GitOps 的

在开始具体配置之前,我们先来了解一下我们将要使用的这个工具。

Argo CD 是专为 Kubernetes 设计的一种声明性、基于 GitOps 的持续交付工具。作为 Cloud Native Computing Foundation (CNCF) 的官方项目,它已经成为了管理现代基础设施的行业标准。

可以把Argo CD看作是驻留在你的集群内部的一个持续监控工具。要理解它为何如此强大,我们就需要看看它与Jenkins或GitHub Actions这类传统的CI/CD工具有什么不同。

“推送”模式与“拉取”模式

像我上面提到的那些传统工具,都是采用“推送”模式来运行的。在这种模式下,外部管道会向你的集群发送命令(比如`kubectl apply`)。这种做法存在风险,因为你必须将敏感的集群管理凭证保存在外部的CI工具中;如果这些工具被黑客入侵,那么你的集群也会面临安全威胁。

而Argo CD则采用了“拉取”模式来完成任务:

  • 中间桥梁: Argo CD充当着你Git仓库中的代码与实际集群状态之间的桥梁。

  • 持续监控: 它会24小时不间断地监视你的Git仓库。一旦检测到有新的提交,就会立即从集群内部拉取这些变更并应用它们。

  • 自动修复功能: 如果有人手动修改了集群中的配置设置,Argo CD会自动发现这种差异,并将其恢复为Git仓库中保存的原始状态。

这种做法不仅更加安全,因为集群的访问凭证永远不会离开原来的环境;同时,它还能确保你的基础设施能够完美地反映你代码中的配置信息。

准备应用程序源代码

在开始自动化构建流程之前,我们的仓库中必须已经有实际的代码。我们将创建两个简单的微服务:一个主API和一个辅助服务。

仓库结构

请确保你的仓库严格按照这个结构进行组织。文件命名的统一性对于自动化脚本能够正确找到目标文件来说至关重要。

GITOPS-ARGOCD-DEMO/
├── .github/workflows/main.yml
├── auxiliary-service/
│   └── Dockerfile
├── main-api/
│   └── Dockerfile
├── Kubernetes-manifest/
│   ├── aux-api.yaml
│   ├── kustomization.yaml
│   └── main-api.yaml
├── application.yaml
└── image-updater.yaml

创建Dockerfile文件

在每个服务对应的文件夹中,都需要创建一个简单的Dockerfile文件,这样我们的构建流程才能有东西可以构建。

main-api/Dockerfile

FROM nginx:alpine
RUN echo "

主API - 1.0版本

" > /usr/share/nginx/html/index.html EXPOSE 80

auxiliary-service/Dockerfile

FROM nginx:alpine
RUN echo "

辅助服务 - 1.0版本

" > /usr/share/nginx/html/index.html EXPOSE 80

使用GitHub Actions自动化镜像构建过程

在专业的GitOps工作流程中,Kubernetes配置文件和应用程序源代码通常会保存在同一个仓库中(或者相互链接)。虽然Argo CD负责部署任务,但你仍然需要一种方法将代码转换成Docker镜像。这时,持续集成技术就派上了用场。

在这个演示中,我加入了一个 GitHub Actions 工作流来自动化这一过程。每当您将代码推送到 main 分支时,这个管道就会构建相应的镜像,并将其上传到 DockerHub。

CI 流程工作原理

.github/workflows/main.yml 文件中添加以下内容:

name: 构建镜像并上传至 DockerHub

on:
  push:
    branches:
      - main
    # 对于用于更新镜像的提交,跳过构建过程
    paths-ignore:
      - 'Kubernetes-manifest/**'

jobs:
  docker_build:
    name: 构建并推送 ${{ matrix.service }}
    environment: argocd-demo
    runs-on: ubuntu-latest
    permissions:
      contents: write

    strategy:
      matrix:
        include:
          - service: aux-service
            dockerfile: auxiliary-service/Dockerfile
          - service: main-service
            dockerfile: main-api/Dockerfile

    env:
      DOCKER_USER: ${{ secrets.DOCKERHUB_USERNAME }}
      RUN_TAG: ${{ github.run_number }}

    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 设置 Docker Buildx 环境
        uses: docker/setup-buildx-action@v3

      - name> 登录 Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ env.DOCKER_USER }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: 构建并推送 ${{ matrix.service }}
        uses: docker/build-push-action@v6
        with:
          context: .
          file: ${{ matrix.dockerfile }}
          push: true
          tags: \({{ env.DOCKER_USER }}/\){{ matrix.service }}:${{ env.RUN_TAG }}
          cache-from: type=gha,scope=${{ matrix.service }}
          cache-to: type=gha,mode=max,scope=${{ matrix.service }}

提示: paths-ignore 部分非常重要。之后,Argo CD 的镜像更新工具会将更改内容写回 Kubernetes-manifest/ 文件夹中。如果没有这条规则,您的管道就会无限循环地不断执行构建操作。

注意: 您必须将 DOCKERHUB_USERNAMEDOCKERHUB_PASSWORD 添加到 GitHub 仓库的“设置”>“秘密”中。

如何安装并使用 Argo CD

现在您的集群已经运行起来了,您可以开始安装 Argo CD 了。安装过程会使用 Argo 项目提供的标准 Kubernetes 配置文件来完成。

步骤 1:创建命名空间并应用配置文件

在 Kubernetes 中,将管理工具与应用程序分开部署是一种最佳实践。您需要创建一个名为 argocd 的专用命名空间,然后应用 Argo 项目提供的官方安装脚本。该脚本会自动配置所需的服务账户、角色和部署资源。

在终端中执行以下命令:

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

你会看到有一长串资源正在被创建中。请稍等一两分钟,让这些Pod初始化完成。之后你可以验证Argo CD的所有核心组件是否都已经运行起来:

kubectl get all -n argocd

在继续下一步操作之前,请确保所有Pod的状态都显示为Running

步骤2:访问Argo CD用户界面

要访问控制面板,我们需要使用一种称为端口转发的技术。由于Argo CD服务器运行在集群的私有网络中,因此你的浏览器目前还无法直接访问它。端口转发会在你的本地机器上的某个端口(8080)与集群服务上的另一个端口(443)之间建立一条安全的“隧道”,这样你就可以与内部服务进行交互,而不会让这些服务暴露在公共互联网上。

运行以下命令:

kubectl port-forward svc/argocd-server -n argocd 8080:443

现在你可以打开浏览器,访问https://localhost:8080。由于使用了自签名证书,你的浏览器可能会提示连接不安全,但你可以放心地点击“Advanced”继续访问该页面。

步骤3:如何登录

Argo CD的默认用户名是admin,密码在安装过程中会自动生成,并以Kubernetes秘密的形式被安全存储起来。

要获取这个密码,请打开一个新的终端标签页(以确保端口转发命令仍在运行中),然后运行以下命令:

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

将输出的结果复制下来,用它作为密码登录到控制面板中。

了解Argo CD应用程序

Argo CD的应用程序实际上是一种自定义资源(CRD),它充当了你Git仓库与集群之间的“桥梁”。这种应用程序定义了以下内容:

  • repoURL & path:这些参数告诉Argo CD应该监控哪个Git仓库,以及该仓库中的哪个文件夹包含了你的YAML配置文件。

  • destination:这个参数指定了应用程序应该部署在哪个位置。我们使用https://kubernetes.default.svc来指向安装了Argo CD的本地集群。

  • syncPolicy:这是GitOps机制的核心。将automated设置为selfHeal: true,就可以让Argo CD在有人手动修改配置时自动进行修复;而prune: true则能确保你在Git中删除文件后,该文件也会从集群中被删除。

应用程序配置文件

在项目根目录下创建一个名为application.yaml的文件:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: gitops-argocd-demo
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com//.git
    targetRevision: HEAD
    path: Kubernetes-manifest
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd-demo-ns
  syncPolicy:
    automated:
      selfHeal: true
      prune: true
    syncOptions:
      - CreateNamespace=true

部署应用程序配置文件

现在我们将在Kubernetes-manifest/文件夹中定义Kubernetes资源。

main-api.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: main-deployment
  namespace: argocd-demo-ns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: main-api
  template:
    metadata:
      labels:
        app: main-api
    spec:
      containers:
      - name: main-service
        image: /main-service:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: main-service-lb
  namespace: argocd-demo-ns
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: main-api

aux-api.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: aux-deployment
  namespace: argocd-demo-ns
spec:
  replicas: 2
  selector:
    matchLabels:
      app: aux-service
  template:
    metadata:
      labels:
        app: aux-service
    spec:
      containers:
      - name: aux-service
        image: /aux-service:latest
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: aux-service
  namespace: argocd-demo-ns
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: aux-service

推送并同步代码

步骤1:应用应用程序配置文件

使用kubectl将此配置文件部署到argocd命名空间中:

kubectl apply -f application.yaml -n argocd

步骤2:将代码推送到你的仓库中

为了触发初始部署,并确保Argo CD与你的代码源保持同步,请将最新的更改添加到、提交到并在你在配置文件中指定的GitHub仓库中推送这些更改:

git add .
git commit -m "初始部署Argo应用程序"
git push origin main

步骤3:在Argo CD中验证结果

一旦你完成了代码的推送操作,就可以前往Argo CD的控制面板。你会看到gitops-argocd-demo应用程序出现在列表中。在完成初始同步后,控制面板会显示绿色的状态提示,说明你的生产集群状态与Git仓库中的代码是完全一致的。

Argo CD控制面板显示gitops-argocd-demo应用程序处于正常且已同步的状态。资源树展示了集群中运行的服务、部署任务、副本集以及Pod的层次结构。

注意:如上图所示,Argo CD以可视化的形式展示了你的Kubernetes对象——服务、部署任务和Pod——之间的关联关系,并确认它们已经与你的Git仓库保持同步。

使用Argo CD的镜像更新工具自动化更新流程

既然我们已经实现了自动部署功能,接下来就需要解决最后一个需要人工操作的环节:每当有新的构建版本被上传到DockerHub时,就要自动更新我们的配置文件中的镜像标签。

步骤1:安装ArgoCD镜像标签更新工具

将这个工具安装在argocd命名空间中:

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

确认该工具已经成功运行:

kubectl get pods -n argocd | grep image-updater

注意:从1.1版本开始,ArgoCD镜像标签更新工具采用了基于CRD的自定义资源机制(即ImageUpdater资源),而非早期版本中使用的注释配置方式。本指南介绍的是使用CRD的方法。

步骤2:创建GitHub个人访问令牌

该工具需要Git登录凭据,才能将更新后的提交推送到你的仓库中。

  1. 进入GitHub → 设置 → 开发者设置 → 个人访问令牌 → 令牌(经典格式)

  2. 点击“生成新令牌”按钮

  3. 选择仓库权限范围(即可完全控制私有仓库)

  4. 复制生成的令牌代码

步骤3:创建Git登录凭据秘密文件

将GitHub登录凭据保存为Kubernetes的秘密配置,在argocd命名空间中创建该文件:

kubectl -n argocd create secret generic git-creds \
  --from-literal=username= \
  --from-literal=password=

请将替换为你的实际用户名和密码。

步骤4:在配置文件中添加自定义化脚本

ArgoCD镜像标签更新工具会使用Kustomize的images字段来写入更新的标签信息。如果你的Kubernetes-manifest/目录中只包含普通的YAML文件,那么你需要为这些文件创建一个kustomization.yaml文件作为包装层。

创建一个kustomization.yaml文件:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - main-api.yaml
  - aux-api.yaml

工作原理:当镜像标签更新工具检测到新的标签信息时,它会在这份文件中添加一个images部分:

images:
  - name: /main-service
    newTag: "12"
  - name: /aux-service
    newTag: "12"

随后,Kustomize会在部署时自动替换这些镜像标签,而不会修改你原有的配置文件。

我们之所以选择使用Kustomize,是因为它能够让镜像标签更新工具以一种更加规范、清晰的方式来管理这些标签信息。该工具并不会直接修改你的main-api.yaml文件,而是仅更新kustomization.yaml文件,之后Argo CD会在部署过程中应用这些更改。

步骤5:创建ImageUpdater自定义资源

在项目根目录下创建image-updater.yaml文件:

apiVersion: argocd-image-updater.argoproj.io/v1alpha1
kind: ImageUpdater
metadata:
  name: gitops-argocd-demo-updater
  namespace: argocd
spec:
  commonUpdateSettings:
    updateStrategy: newest-build
    allowTags: "regexp:^[0-9]+$"
  applicationRefs:
    - namePattern: "gitops-argocd-demo"
      writeBackConfig:
        method: "git:secret:argocd/git-creds"
        gitConfig:
          branch: main
          writeBackTarget: "kustomization:."
      images:
        - alias: main-service
          imageName: /main-service
        - alias: aux-service
          imageName: /aux-service

这个ImageUpdater资源就是我们自动化标签系统的“大脑”。下面具体解释这些字段的作用:

updateStrategy:

  • newest-build: 这个设置告诉更新器始终根据创建时间在DockerHub上查找最新的镜像版本。

writeBackConfig: 这里就是关键所在。它使用我们之前创建的git-creds秘密信息,来授权更新器将更改内容写回你的代码仓库中。

writeBackTarget:

  • kustomization: 我们明确指示更新器修改manifests文件夹中的kustomization.yaml文件,而不是直接修改部署配置文件。

images: 我们为这些镜像设置了别名(main-service和aux-service),这样更新器就能清楚地知道DockerHub上的哪些镜像对应着我们Kubernetes配置文件中的哪些容器。

将ImageUpdater资源应用到集群中:

kubectl apply -f image-updater.yaml -n argocd

接下来,需要将kustomization.yaml文件推送到你的Git仓库中(因为Image Updater会克隆该仓库,所以远程仓库中必须存在这个文件):

git add Kubernetes-manifest/kustomization.yaml
git commit -m "添加用于图像更新回写的kustomization.yaml文件"
git push origin main

步骤6:验证Image Updater的功能

查看Image Updater的日志,确认其是否正常工作:

kubectl logs -n argocd deployment/argocd-image-updater-controller --tail=20

成功的输出示例如下:

msg="正在开始图像更新流程,当前有1个应用需要更新"
msg="将新镜像设置为YOUR_DOCKERHUB_USERNAME/main-service:11"
msg>"成功将镜像'YOUR_DOCKERHUB_USERNAME/main-service:7'更新为'YOUR_DOCKERHUB_USERNAME/main-service:11'"
msg="将新镜像设置为YOUR_DOCKERHUB_USERNAME/aux-service:11"
msg="正在提交gitops-argocd-demo应用的配置更新"
msg>"git push origin main"
msg>"成功更新了应用程序的配置信息"
msg="处理结果:共1个应用需要更新,实际更新了2个镜像,没有跳过任何镜像,错误数为0"

结论

你已经成功从零开始搭建了一个专业级别的GitOps流程。通过集成GitHub Actions、Argo CD以及Argo CD Image Updater,你成功架起了源代码与生产环境之间的桥梁。

仔细思考一下你刚刚构建的这个工作流程:

  1. 你将代码推送到了GitHub上。

  2. GitHub Actions会生成一个新的Docker镜像并为其添加标签。

  3. Argo CD Image Updater会检测到这个新标签,并自动将其应用到你的Git配置中。

  4. Argo CD会下载这些更改,然后使你的集群状态与新的期望状态保持一致。

再也不用手动执行`kubectl apply`命令了,也不再会出现配置不一致的问题,更不会在凌晨2点遇到莫名其妙的故障。现在,你的Git仓库真正成为了所有配置信息的唯一来源。如果某项配置信息不存在于Git中,那么它也就根本不会出现在你的集群中——这才是DevOps带来的真正强大功能。

Comments are closed.