ArgoCD+Jenkins实现GitOps

ArgoCD+Jenkins实现GitOps

一、安装ArgoCD+Jenkins

二、Jenkins实现CI

  • 试验环境:
    • 阿里云镜像仓库
    • jihu的jitlab
    • k8s 1.28.0
    • Jenkins

2.1、实验做的demo展示

​ 这是一个go编写的简单代码演示,然后写了一个chart包去部署这个go代码,在helm目录(这个目录在argocd中会使用到)

package main

import (
  "net/http"

  "github.com/gin-gonic/gin"
  "github.com/sirupsen/logrus"
)
import "fmt"

func main() {
  r := gin.Default()

  r.GET("/", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H {
      "msg": "Welcome to the Jenkins CI Demo!",
    })
  })

  r.GET("/health", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H {
      "health": true,
    })
  })

  if err := r.Run(":8080"); err != nil {
    logrus.WithError(err).Fatal("Couldn't listen")
  }
}

​ 然后这个项目main.go 同级目录有个Jenkinsfile文件,这就是我们可以在Jenkins通过SCM配置git地址,然后指定路径读取到Jenkinsfile,yamlFile "ci/jnlp.yaml" // 指定YAML配置文件路径,这个是ci目录下

  • Jenkinsfile
    • stage('change ImageTag') 这个步骤,主要是通过envbust 把变量转换成yaml,首先values.yaml 跟values.tpl文件内容是一样的,就是values.tpl是变量,values.yaml是固定值,values.tpl 接收变量,渲染到values.yaml ,然后通过helm 部署的时候还是读取values.yaml的值
// 初始化变量,用于存储提交ID和时间戳  
def COMMITID = ""  
def TIMESTAMP = ""  
pipeline {  
    agent {  
        // 使用Kubernetes代理,生成唯一的标签  
        kubernetes {  
            label "jnlp-slave-${UUID.randomUUID().toString().substring(0, 8)}"  
            yamlFile "ci/jnlp.yaml" // 指定YAML配置文件路径,这个是ci目录下  
        }  
    }  
    environment {  
        // 定义环境变量  
        DOCKER_REGISTRY = "registry.cn-hangzhou.aliyuncs.com" // Docker注册表地址  
        REGISTRY_NAMEPSACE = "zhenhuan" // 注册表命名空间  
        IMAGE = "${DOCKER_REGISTRY}/${REGISTRY_NAMEPSACE}" // 完整的镜像地址  
        OPS_REPO = "jihulab.com/devopsx/ci-demo.git" // GitOps仓库地址  
    }  

    options {  
        // 构建丢弃策略,保持构建15天,最多保留30个构建  
        buildDiscarder logRotator(artifactDaysToKeepStr: '15', artifactNumToKeepStr: '', daysToKeepStr: '15', numToKeepStr: '30')  
        // 启用时间戳  
        timestamps()  
        // 设置超时时间为100分钟  
        timeout(time: 100, unit: 'MINUTES')  
    }  

    stages {  
        stage('commit') {  
            steps {  
                script {  
                    // 获取当前Git提交的短ID  
                    COMMITID = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()  
                    // 获取当前时间戳  
                    TIMESTAMP = sh(script: "date +%Y%m%d%H%M-%S", returnStdout: true).trim()  
                    // 设置镜像标签,格式为:BUILD_ID-时间戳-提交ID  
                    env.ImageTag = "${BUILD_ID}-${TIMESTAMP}-${COMMITID}"  
                    // 从JOB_NAME中提取应用名称并转换为小写  
                    env.AppName = env.JOB_NAME.split('/').last().toLowerCase()  
                }  
            }  
        }  
        stage('build image') {  
            steps {  
                container('docker') { // 在Docker容器中执行步骤  
                    withCredentials([[$class: 'UsernamePasswordMultiBinding',  
                        credentialsId: 'docker-auth', // 使用Docker认证凭证  
                        usernameVariable: 'DOCKER_USER',  
                        passwordVariable: 'DOCKER_PASSWORD']]) {  
                        script {  
                            sh """  
                            echo "开启多架构编译"  
                            // 创建并使用Docker Buildx构建器  
                            docker buildx create --name mybuilder --use --driver docker-container --driver-opt image=registry.cn-hangzhou.aliyuncs.com/zhenhuan/buildkit:buildx-stable-1  

                            echo "登陆仓库"  
                            // 登录到Docker注册表  
                            docker login ${DOCKER_REGISTRY} -u ${DOCKER_USER} -p ${DOCKER_PASSWORD}  

                            echo "构建/推送镜像"  
                            // 使用Buildx构建镜像并推送到注册表,支持多架构  
                            docker buildx build --progress=plain --no-cache --platform=linux/amd64,linux/arm64 -f Dockerfile -t ${IMAGE}/${AppName}:${ImageTag} . --push  
                            """  
                        }  
                    }  
                }  
            }  
        }  
        stage('change ImageTag') {  
            steps {  
                container('tools') { // 在工具容器中执行步骤  
                    script {  
                        sh """  
                        // 使用envsubst替换模板中的环境变量,生成Helm值文件  
                        envsubst < ./values.tpl > helm/values.yaml  
                        cat helm/values.yaml // 输出生成的Helm值文件  
                        // 使用Helm模板渲染  
                        helm template --debug helm/ -f helm/values.yaml  
                        """  
                    }  
                }  
            }  
        }  
        stage('push yaml') {  
            steps {  
                withCredentials([[$class: 'UsernamePasswordMultiBinding',  
                    credentialsId: 'github-ci', // 使用GitHub CI凭证  
                    usernameVariable: 'GIT_USER',  
                    passwordVariable: 'GIT_PASSWORD']]) {  
                    script {  
                        sh """  
                        // 配置Git用户信息  
                        git config --global user.email "ci"  
                        git config --global user.name "ci@ci.com"  
                        // 切换到主分支  
                        git checkout main  
                        // 添加所有更改  
                        git add .  
                        // 提交更改,提交信息包含应用名称和镜像标签  
                        git commit -m "${AppName}-${ImageTag} " || true  
                        // 推送更改到GitOps仓库  
                        git push https://${GIT_USER}:${GIT_PASSWORD}@${OPS_REPO}  
                        """  
                    }  
                }  
            }  
        }  
    }  
}  
  • ci/jnlp.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-slave
spec:
  volumes:
    - name: docker-socket
      emptyDir: {}
    - name: workspace-volume
      emptyDir: {}      
  serviceAccount: jenkins      
  #nodeName: k8s-node05
  containers:
    - name: jnlp
      image: registry.cn-hangzhou.aliyuncs.com/zhenhuan/inbound-agent:latest
    - name: tools  
      image: registry.cn-hangzhou.aliyuncs.com/s-ops/tools:latest
      command:
        - cat
      tty: true         
    - name: docker
      image: registry.cn-hangzhou.aliyuncs.com/zhenhuan/docker:latest
      env:
        - name: DOCKER_CLI_EXPERIMENTAL
          value: "enabled"  
      command:
      - sleep
      args:
      - 99d
      readinessProbe:
        exec:
          command: ["ls", "-S", "/var/run/docker.sock"]      
        initialDelaySeconds: 10  
      volumeMounts:
      - name: docker-socket
        mountPath: /var/run       
    - name: docker-daemon
      image: registry.cn-hangzhou.aliyuncs.com/zhenhuan/docker:19.03.1-dind
      securityContext:
        privileged: true
      volumeMounts:
      - name: docker-socket
        mountPath: /var/run
      - name: workspace-volume
        mountPath: /home/jenkins/agent
        readOnly: false 
  • ci/tools 镜像的dockerfile
# 基于 Alpine 最新版本构建tools容器镜像
FROM docker.cloudimages.asia/alpine:latest

# 安装kubectl helm yq envsubst
RUN sed -i 's|dl-cdn.alpinelinux.org|mirrors.ustc.edu.cn|g' /etc/apk/repositories && \
    apk update && apk upgrade && \
    apk add --no-cache \
        ca-certificates \
        tzdata \
        helm \
        kubectl \
        yq \
        bash \
        vim \
        gettext && \
    update-ca-certificates && \
    rm -rf /var/cache/apk/*
ENV TZ=Asia/Shanghai
  • 部署这个go程序的dockerfile
# 指定基础的go编译镜像
FROM registry.cn-hangzhou.aliyuncs.com/zhenhuan/golang:alpine3.17 as build

# 指定go的环境变量

RUN go env -w GOPROXY=https://goproxy.cn,direct   # https://mirrors.aliyun.com/goproxy/


# 指定工作空间目录,会自动cd到这个目录
WORKDIR /opt

# 把项目的其他所有文件拷贝到容器中
COPY . .

# 编译成可执行二进制文件
RUN go env -w GO111MODULE=on
RUN go mod tidy
RUN go build -o app main.go

FROM registry.cn-hangzhou.aliyuncs.com/zhenhuan/alpine:latest as serviceDeploy

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories &&   apk update &&   apk upgrade &&   apk add ca-certificates && update-ca-certificates &&   apk add --update tzdata &&   rm -rf /var/cache/apk/*

ENV TZ=Asia/Shanghai

COPY --from=build /opt/app /
CMD ["/app"]

三、Argocd 实现CD

​ 参考:https://cloud.tencent.com/developer/article/2398649

​ ArgoCD的APP配置文件

apiVersion: argoproj.io/v1alpha1   # 指定API版本,表明这是Argo CD应用程序的定义  
kind: Application                  # 定义的资源类型为“Application”  
metadata:                          # 资源的元数据  
  name: cd-demo                    # 应用程序的名称为 cd-demo  
  namespace: argocd                # 资源所在的命名空间为 argocd  
spec:                              # 资源的具体规格  
  project: default                 # 指定该应用程序属于 'default' 项目  
  source:                          # 应用程序的源代码定义  
    path: helm                     # 指定应用程序的路径,这里使用 Helm Charts  
    repoURL: https://jihulab.com/devopsx/ci-demo.git  # 指向包含应用代码和 Helm Charts 的 Git 仓库的 URL  
    targetRevision: main           # 指定要使用的代码分支,通常为主分支 main  
  destination:                     # 目标 Kubernetes 集群和命名空间配置  
    server: 'https://kubernetes.default.svc'  # 指向默认的 Kubernetes API 服务器  
    namespace: kube-ops            # 应用程序部署到的 Kubernetes 命名空间为 kube-ops  
  syncPolicy:                      # 同步策略设置  
    automated:                     # 启用自动同步  
      prune: true                  # 在同步过程中自动删除不再需要的资源  
      selfHeal: true               # 启用自我恢复功能,若检测到偏差会自动纠正  

​ 查看部署结果:

# 查看pod 日志(稍微改动了下go代码,无碍)
[root@k8s-master01 devops]# kubectl logs -f -n kube-ops  ci-demo-5768d9c6c6-bmtn7
你好吗? 我的朋友!

后续代码有变更,修改代码后,在Jenkins构建镜像,后面Argocd就会帮我们去部署

四、部署Helm到 Argo CD的方式

参考:https://cloud.tencent.com/developer/article/2398649

部署 Helm 仓库中 Charts

​ 要使用 Argo CD 部署 Helm Charts,可以创建一个 Application 资源,并指定 Helm Chart 的路径、存储库 URL 和目标修订版本。例如,要部署一个名为 nginx 的 Helm Chart,可以创建以下 Application 资源:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: mysql
  namespace: argocd
spec:
  project: default
  source:
    chart: mysql
    repoURL: https://charts.onwalk.net
    targetRevision: 9.21.2
    helm:
      releaseName: mysql
  destination:
    server: "https://kubernetes.default.svc"
    namespace: itsm-dev-db
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

部署 Git仓库中的 Charts-Helmfile

​ 要部署 Helmfiles,可以在 Application 资源中指定 Helmfile 的路径、存储库 URL 和目标修订版本。例如,要部署一个名为 my-helmfiles 的 Helmfile,可以创建以下 Application 资源:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: itsm
  namespace: argocd
spec:
  project: default
  source:
    path: helmfiles/itsm
    repoURL: https://github.com/svc-design/gitops.git
    targetRevision: main
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: itsm-dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

部署 Git仓库中的 Charts

​ 要部署来自 Git 仓库的 Helm Charts,可以在 Application 资源中指定 Helm Chart 的路径、存储库 URL 和目标修订版本。例如,要部署一个名为 my-chart 的 Helm Chart,可以创建以下 Application 资源:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: redis
  namespace: argocd
spec:
  project: default
  source:
    path: helm/redis
    repoURL: https://github.com/svc-design/gitops.git
    targetRevision: main
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: itsm-dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
posted @ 2025-03-20 18:10  taotaozh  阅读(233)  评论(0)    收藏  举报