引言

随着云原生技术的迅猛发展,Kubernetes 已成为主流的容器编排平台。如何高效、自动化地构建与部署应用,成为开发与运维团队的关注重点。结合 GitLab 强大的 CI 能力与 ArgoCD 的 Kubernetes 原生 GitOps 持续交付方案,能够构建出高效且可靠的 CI/CD 流程。本文将深入探讨如何利用 GitLab 和 ArgoCD 实现 Kubernetes 环境下的 CI/CD 流程。

基础概念介绍

工具 优势
GitLab 提供代码管理、CI 流水线、镜像构建与推送,支持与 Kubernetes 集群的连接与部署。
ArgoCD GitOps 实践的代表,自动同步 Git 仓库中的 Kubernetes 配置到集群,支持便捷的回滚操作。

明确概念:GitLab 做 CI,ArgoCD 做 CD

在 Kubernetes 持续集成与持续交付的整体流程中,GitLab 和 ArgoCD 各自承担不同职责,协同完成自动化发布。

工具 主要职责 说明
GitLab 持续集成(CI) 负责代码构建、依赖安装、单元与集成测试、镜像构建与推送等任务。
ArgoCD 持续交付(CD) 负责监控 Git 仓库中 Kubernetes 配置变更,自动将新镜像部署或滚动更新到集群。

以前端项目发版为例
假设你的前端小程序项目通过 uniapp 构建,构建产物镜像托管在私有 Docker 镜像仓库,部署在 Kubernetes 集群中运行,整个 CI/CD 流程如下:

1. GitLab 负责 CI 过程

代码提交(例如修改某个页面文件)会触发 GitLab Runner 执行 CI 流水线:

阶段 具体操作
代码拉取 从 GitLab 仓库拉取最新代码
依赖安装 执行 npm install 或相关依赖安装命令
代码构建 执行 uniapp 编译生成静态文件
测试 运行单元测试、集成测试,确保构建产物质量
镜像构建 使用 Dockerfile 构建包含前端产物的镜像
镜像推送 将镜像推送到私有 Docker Registry,标签通常为 commit-shaversion

CI 成功后,镜像和变更的 Kubernetes 配置(如新版本的镜像标签)会同步提交到 Git 仓库管理的部署配置中(例如 GitLab 的另一个仓库或者同仓库的 k8s/ 目录)。

2. ArgoCD 负责 CD 过程

ArgoCD 持续监控 Kubernetes 配置 Git 仓库:

步骤 具体操作
监控配置 监控 Git 仓库中的前端应用 Kubernetes 部署配置文件,如 deployment.yaml
发现变更 检测到镜像标签更新为新版本时
自动同步 自动将最新的 deployment.yaml 配置同步到 Kubernetes 集群,触发滚动更新
状态反馈 通过 ArgoCD Web UI 反馈当前应用的部署状态
回滚支持 如果出现异常,支持回滚到之前的稳定版本

关键优势

职责分明,流程清晰:CI 专注构建与测试,CD 专注部署与版本管理。

自动化与可追溯:代码、镜像、部署配置均由 Git 管理,生成完整的审计链。

灵活扩展多环境:通过 Git 分支或子目录管理不同环境配置,ArgoCD 可轻松同步。

快速回滚保障:基于 Git 版本管理的 ArgoCD 实现快速回滚功能。

CI:gitlab ci部分

GitLab Runner 安装部署

以下示例以常用的 Linux 环境为例,你可以根据需要选择对应的操作系统安装方式。

2.1 安装 GitLab Runner

可以通过官方提供的二进制包或包管理器安装。

# 下载并安装
curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
chmod +x /usr/local/bin/gitlab-runner

如果这里下载失败,请自行下载gitlab-runner的rpm安装包进行安装,也可以私信博主要这个rpm包

注册 GitLab Runner

安装完毕后,需要在 GitLab 中注册 Runner,使其能接收你的项目任务。

gitlab-runner register --non-interactive \
  --url "http://192.168.150.11/" \
  --registration-token "XTKC4XgCb2FEH3h8UMdu" \
  --executor "docker" \
  --docker-image alpine:latest \
  --description "docker-runner" \
  --tag-list "ci220-0626" \
  --run-untagged="true" \
  --locked="false" \
  --access-level="not_protected" \
  --docker-privileged="true" \
  --docker-volumes "/var/run/docker.sock:/var/run/docker.sock" \
  --docker-volumes "/etc/docker:/etc/docker" \
  --docker-volumes "/cache"

参数说明

参数 说明
--non-interactive 非交互式注册,适合自动化脚本
--url GitLab 实例 URL
--registration-token 项目或组的注册 Token
--executor Runner 执行器类型,这里使用 Docker
--docker-image 默认 Docker 镜像
--description Runner 描述名称
--tag-list Runner 标签,任务触发时指定
--run-untagged 是否执行未打标签的任务,true 表示执行
--locked 是否锁定 Runner,false 表示不锁定,允许多个项目共享
--access-level 访问级别,not_protected 表示适用于非受保护分支
--docker-privileged 以特权模式运行 Docker 容器,便于构建镜像等需要特权的操作
--docker-volumes 挂载的 Docker 卷,支持多次指定

这边需要说明一下,–url 和 --registration-token两个参数从你的gitlab的这里获取

在这里插入图片描述
在这里插入图片描述
gitlab-runner注册完成以后,你应该可以在你的gitlab runner页面看到你刚刚注册的gitlab-runner
在这里插入图片描述
绿色代表可以正常使用

如果你能正常看到这个页面,并且你的gitlab-runner状态显示为绿色,那恭喜你!
你的gitlab-runner可以正常使用了。

现在正式进入gitlab-runner的ci部分

首先把你的业务代码clone下来

git clone http://192.168.150.11/ops/ruoyi-cloud.git

进入到你的业务代码里创建一个名为.gitlab-ci.yml的文件

这里简单介绍一下.gitlab-ci.yml这个文件:
.gitlab-ci.yml 是 GitLab 整合持续集成和持续交付能力的关键配置文件,它定义了你的代码如何自动化构建、测试、打包及部署,从而加速软件开发和发布流程,提高稳定性和效率。

ok,现在让我们在这个文件中创建一些基础的环境变量和ci运行时的步骤:

1. 第一步:构建前端项目(build-ui)

任务说明:
使用 Node.js 镜像(node:16)
仅对 master 分支执行
进入 ruoyi-ui 目录
安装依赖(npm install,使用镜像加速地址)
生产环境打包(npm run build:prod)
生成产物 ruoyi-ui/dist/,作为 artifact 保留一个小时

[root@k8s-master ruoyi-cloud]# cat .gitlab-ci.yml 
# .gitlab-ci.yml (兼容 GitLab 11.1.4)

stages:
  - build
  - package
  - update-cd

variables:
  HARBOR_URL: "填写你的harbor地址"
  HARBOR_PROJECT: "ruoyi-cloud"
  RUDYI_UI_IMAGE_NAME: "ruoyi-ui"
  GIT_CONFIG_REPO_URL: "http://192.168.150.11/ops/ruoyi-cloud-cd.git"
  GIT_CONFIG_FILE_PATH: "ruoyi-ui/ruoyi-ui.yml"

build-ui:
  stage: build
  image: node:16  # 使用 Node.js 16.x 版本
  only:
    - master
  cache:
    key: "$CI_COMMIT_REF_SLUG-node-modules"
    paths:
      - ruoyi-ui/node_modules/
  script:
    - echo "======== 开始构建 ruoyi-ui 项目 ========"
    - node -v || echo "❌ Node.js 未安装"
    - npm -v || echo "❌ npm 未安装"
    - cd ruoyi-ui || { echo "❌ ruoyi-ui 目录不存在"; exit 1; }
    - echo "📦 安装依赖中..."
    - npm install --registry=https://registry.npmmirror.com --loglevel info || { echo "❌ npm install 失败"; exit 1; }
    - echo "🚀 开始打包..."
    - npm run build:prod || { echo "❌ 构建失败"; exit 1; }
    - echo "✅ ruoyi-ui 项目构建完成"
  artifacts:
    paths:
      - ruoyi-ui/dist/
    expire_in: 1 hour

完事以后,保存提交一下

git add .
git commit -m "提交说明"
git push -u origin master

如果提交正常,你可以在gitlab的cicd的工作流页面看到有任务在执行
在这里插入图片描述
点击具体的流水线编号进去,你可以看到你定义的作业名称
在这里插入图片描述
点进去具体看一下
在这里插入图片描述
这里如果你构建成功的话就会看到这样的提示,如果有报错的话请百度或者ai

如果你能正常进行到这一步并且成功的话,恭喜你,基本距离成功已经不远了,因为接下来的任务基本就是依样画葫芦了!!

2. 第二步:打包 Docker 镜像(package-ui)

任务说明:

使用 Docker 官方镜像(docker:20.10.16)
不使用 Docker TLS 功能,方便镜像推送
仅 master 分支执行,指定 runner 标签 ci220-0626
使用当前时间创建镜像 tag
登录 Harbor 镜像仓库(凭借 CI/CD 环境变量 HARBOR_USER, HARBOR_PASSWORD)
构建 Docker 镜像,基于 ruoyi-ui/Dockerfile 和前一步的产物
推送镜像至 Harbor
将镜像标记和完整路径写入文件供下一步使用
保存 image_tag.txt 和 image_full_name.txt 作为 artifact

package-ui:
  stage: package
  image: docker:20.10.16
  variables:
    DOCKER_TLS_CERTDIR: ""
  only:
    - master
  tags:
    - ci220-0626
  script: |
    echo "========= 开始打包 Docker 镜像推送到 Harbor ========="

    export DATE_VERSION=$(date +"%Y%m%d%H%M")
    export IMAGE_TAG="${DATE_VERSION}"
    echo "📌 使用时间戳作为版本号: $IMAGE_TAG"

    export IMAGE_REGISTRY="${HARBOR_URL}"
    export IMAGE_NAME="${HARBOR_PROJECT}/${RUDYI_UI_IMAGE_NAME}"
    export IMAGE_FULL_NAME="${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"

    echo "🔖 镜像 Tag:${IMAGE_TAG}"
    echo "🧩 镜像名称:${IMAGE_NAME}"
    echo "🌐 镜像地址:${IMAGE_FULL_NAME}"

    echo "${HARBOR_PASSWORD}" | docker login ${IMAGE_REGISTRY} -u ${HARBOR_USER} --password-stdin || { echo "❌ Docker 登录失败"; exit 1; }

    docker build -f ruoyi-ui/Dockerfile -t ${IMAGE_FULL_NAME} ./ruoyi-ui || { echo "❌ Docker 构建失败"; exit 1; }

    docker push ${IMAGE_FULL_NAME} || { echo "❌ Docker 推送失败"; exit 1; }

    echo "${IMAGE_TAG}" > image_tag.txt
    echo "${IMAGE_FULL_NAME}" > image_full_name.txt

    echo "📄 保存的文件内容:"
    echo "image_tag.txt: $(cat image_tag.txt)"
    echo "image_full_name.txt: $(cat image_full_name.txt)"
    echo "当前目录: $(pwd)"
    ls -la

    echo "✅ 镜像构建并推送完成"
  artifacts:
    paths:
      - image_tag.txt
      - image_full_name.txt
    expire_in: 1 hour

把这部分代码作为第二部分添加在刚才第一部分代码的下方,保存并提交

git add .
git commit -m "提交说明"
git push -u origin master

这样你就能在刚才那个gitlab 流水线页面看到你的第二步的作业了
在这里插入图片描述
在这里插入图片描述

3. 第三步:更新 ArgoCD 配置仓库(update-manifest-ui)

任务说明:

使用 alpine/k8s:1.26.11 镜像,内含常用 Kubernetes 及 Linux 工具
仅 master 分支执行
依赖 package-ui 阶段 artifacts
读取 image_tag.txt 和 image_full_name.txt 文件,获取最新镜像信息
安装 git、sed、grep 以便操作 Git 并编辑配置文件
设置 Git 用户名和邮箱,配置 Git HTTP 认证凭据(通过环境变量 CI_USERNAME、CI_PASSWORD)
克隆配置仓库 GIT_CONFIG_REPO_URL 到工作目录
校验配置文件是否存在 GIT_CONFIG_FILE_PATH
查找 yml 文件里镜像字段,智能替换镜像标签
若常规替换失败,使用 awk 做更强力替换
对比文件是否修改,没有修改提示失败,修改了则提交并推送
配置推送之后,ArgoCD 会自动检测并启动新镜像部署

update-manifest-ui:
  stage: update-cd
  image: alpine/k8s:1.26.11
  only:
    - master
  dependencies:
    - package-ui
  script: |
    echo "======== 开始更新 ArgoCD 配置仓库 ========"

    if [ -f "image_tag.txt" ]; then
      export IMAGE_TAG=$(cat image_tag.txt)
    else
      echo "⚠️ 文件 image_tag.txt 不存在,使用当前时间作为版本号"
      export IMAGE_TAG=$(date +"%Y%m%d%H%M")
    fi
    
    if [ -f "image_full_name.txt" ]; then
      export IMAGE_FULL_NAME=$(cat image_full_name.txt)
    else
      echo "⚠️ 文件 image_full_name.txt 不存在,使用构建的镜像名"
      export IMAGE_FULL_NAME="${HARBOR_URL}/${HARBOR_PROJECT}/${RUDYI_UI_IMAGE_NAME}:${IMAGE_TAG}"
    fi
    
    echo "使用镜像: ${IMAGE_FULL_NAME}"

    apk add --no-cache git sed grep

    git config --global user.name "填写你自己的git账号"
    git config --global user.email "填写你自己git账号的邮箱"

    echo "🔐 使用 HTTP 认证"
    git config --global credential.helper store
    echo "http://${CI_USERNAME}:${CI_PASSWORD}@192.168.150.11" > ~/.git-credentials

    echo "🔄 正在克隆配置仓库..."
    git clone "${GIT_CONFIG_REPO_URL}" /tmp/k8s-manifests || { echo "❌ Git 克隆失败"; exit 1; }
    cd /tmp/k8s-manifests

    if [ ! -f "${GIT_CONFIG_FILE_PATH}" ]; then
      echo "❌ 配置文件 ${GIT_CONFIG_FILE_PATH} 不存在"
      exit 1
    fi

    echo "🔎 检查配置文件内容"
    echo "-----------------------------------"
    cat "${GIT_CONFIG_FILE_PATH}"
    echo "-----------------------------------"

    if grep -q "image: {image}" "${GIT_CONFIG_FILE_PATH}"; then
      echo "🔍 找到占位符格式: 'image: {image}'"
      sed -i "s|image: {image}|image: ${IMAGE_FULL_NAME}|g" "${GIT_CONFIG_FILE_PATH}"
    elif grep -q "image: ${HARBOR_URL}/${HARBOR_PROJECT}/${RUDYI_UI_IMAGE_NAME}:" "${GIT_CONFIG_FILE_PATH}"; then
      echo "🔍 找到具体镜像格式,尝试替换版本"
      sed -i "s|image: ${HARBOR_URL}/${HARBOR_PROJECT}/${RUDYI_UI_IMAGE_NAME}:[^ ]*|image: ${IMAGE_FULL_NAME}|g" "${GIT_CONFIG_FILE_PATH}"
    else
      echo "⚠️ 未找到匹配的镜像格式,尝试正则表达式匹配镜像标签"
      sed -i "s|image: ${HARBOR_URL}/${HARBOR_PROJECT}/${RUDYI_UI_IMAGE_NAME}:[^ #]*|image: ${IMAGE_FULL_NAME}|g" "${GIT_CONFIG_FILE_PATH}"
    fi

    echo "🔎 替换后的文件内容"
    echo "-----------------------------------"
    cat "${GIT_CONFIG_FILE_PATH}"
    echo "-----------------------------------"

    if git diff --quiet; then
      echo "⚠️ 没有检测到文件更改,尝试更强力的替换"
      
      awk -v new_image="${IMAGE_FULL_NAME}" '
      {
        if ($0 ~ /image:/) {
          if ($0 ~ /ruoyi-ui/) {
            comment = ""
            if ($0 ~ /#/) {
              split($0, parts, "#")
              comment = "#" parts[2]
            }
            print "        image: " new_image "             " comment
          } else {
            print $0
          }
        } else {
          print $0
        }
      }' ${GIT_CONFIG_FILE_PATH} > ${GIT_CONFIG_FILE_PATH}.tmp

      mv ${GIT_CONFIG_FILE_PATH}.tmp ${GIT_CONFIG_FILE_PATH}

      echo "🔎 强力替换后的文件内容"
      echo "-----------------------------------"
      cat "${GIT_CONFIG_FILE_PATH}"
      echo "-----------------------------------"
    fi

    if git diff --quiet; then
      echo "❌ 所有替换方法都失败,无法更新文件"
      exit 1
    else
      echo "✅ 成功更新镜像标签"
    fi

    echo "🔄 提交并推送更新..."
    git add "${GIT_CONFIG_FILE_PATH}"
    git commit -m "[CI] Update ruoyi-ui image to ${IMAGE_TAG}"
    git push || { echo "❌ Git 提交推送失败"; exit 1; }

    echo "✅ 配置仓库更新完成!ArgoCD 将会自动部署。"

保存退出后再次提交git

git add .
git commit -m "提交说明"
git push -u origin master

到gitlab的流水线上你能看到完整的ci部分流水线了
在这里插入图片描述
到此为止,恭喜你,利用gitlab-runner完成了ci部分的内容

CD:argocd cd部分

ArgoCD 部署

ArgoCD 是 Kubernetes 原生的持续交付工具,通过声明式 GitOps 模式实现应用自动同步和版本回滚。下面介绍如何在 Kubernetes 集群中部署 ArgoCD,并进行基础配置。

1. ArgoCD 安装

目前最简便的安装方式是通过官方提供的 YAML 清单文件一键部署:

# 没有kubectl的先安装kubectl
kubectl create namespace argocd
kubectl apply -n argocd -f https://github.com/argoproj/argo-cd/tree/release-2.8/manifests/install.yaml
# 或者
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.8.8/manifests/install.yaml

发布 Argo CD 服务

默认情况下, Argo CD 服务不对外暴露服务,可以通过 LoadBalancer 或者 NodePort 类型的 Service、Ingress、Kubectl 端口转发等方式将 Argo CD 服务发布到 Kubernetes 集群外部。
这里使用以下命令通过 NodePort 服务的方式暴露 Argo CD 到集群外部:

# 修改 Service 类型
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "NodePort"}}'
# 获取随机生成的 NodePort 端口
kubectl get svc -n argocd

在这里插入图片描述
访问Web UI
kubectl get secret argocd-initial-admin-secret -o jsonpath=“{.data.password}” -n argocd | base64 -d

在这里插入图片描述到这里为止,恭喜你,成功部署了argocd

argocd发版

让我们来到argocd的页面,创建一个新的发版任务
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

填写一下需要的参数
保存即可

argocd的部分就这些
我这边为了方便测试,我的cd部分就只有k8s deploy的一份文件
在这里插入图片描述
在这里插入图片描述

最后测试一下

随便修改一个页面上的内容并提交到git
这里千万注意,是提交到业务代码,也就是ci部分,因为cd部分的代码变更是由ci来自动更新的
在这里插入图片描述
进去随便改点内容然后提交git

git add .
git commit -m "提交说明"
git push -u origin master

如果顺利,你将看到完整走完的ci流水线任务
等待ci部分完成以后,再回到argocd页面,看你刚才创建的任务,就会发现任务被自动触发运行了
在这里插入图片描述
最后,查看一下你的前端页面,如果正常更新,说明这个流程走下来是没问题的
在这里插入图片描述

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐