实现GitOps流程:JenkinsCI+ArgoCD

Argo CD 是一个为 Kubernetes 而生的,遵循声明式 GitOps 理念的持续部署工具。Argo CD 可在 Git 存储库更改时自动同步和部署应用程序
Argo CD 遵循 GitOps 模式,使用 Git 仓库作为定义所需应用程序状态的真实来源,Argo CD 支持多种 Kubernetes 清单
Argo CD 可在指定的目标环境中自动部署所需的应用程序状态,应用程序部署可以在 Git 提交时跟踪对分支、标签的更新,或固定到清单的指定版本

Argo CD Version Kubernetes versions
2.13 v1.30,v1.29,v1.28,v1.27
2.12 v1.29,v1.28,v1.27,v1.26
2.11 v1.29,v1.28,v1.27,v1.26,v1.25

试验环境:

一、安装Jenkins+ArgoCD

Argo CD (opens new window)是针对 Kubernetes 的声明式 GitOps 持续交付工具


Jenkisn部分

[root@k8s-master01 argocd]# vim Jenkins.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: argocd
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: jenkins-local
  labels:
    app: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 5Gi
  storageClassName: local-storage
  local:
    path: /data/k8s/jenkins
  persistentVolumeReclaimPolicy: Retain
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - k8s-node02   # 这里随便找一台跑就可以
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pvc
  namespace: argocd
spec:
  storageClassName: local-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: argocd
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: jenkins
rules:
  - apiGroups: ["extensions", "apps"]
    resources: ["deployments", "ingresses"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
  - apiGroups: [""]
    resources: ["services"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
  - apiGroups: [""]
    resources: ["pods/exec"]
    verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
  - apiGroups: [""]
    resources: ["pods/log", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: jenkins
  namespace: argocd
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: jenkins
subjects:
  - kind: ServiceAccount
    name: jenkins
    namespace: argocd
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  namespace: argocd
spec:
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      serviceAccount: jenkins
      initContainers:
        - name: fix-permissions
          image: registry.cn-guangzhou.aliyuncs.com/cumybride/busybox:1.35.0
          command: ["sh", "-c", "chown -R 1000:1000 /var/jenkins_home"]
          securityContext:
            privileged: true
          volumeMounts:
            - name: jenkinshome
              mountPath: /var/jenkins_home
      containers:
        - name: jenkins
          image: registry.cn-guangzhou.aliyuncs.com/cumybride/jenkins:lts-jdk11
          imagePullPolicy: IfNotPresent
          env:
            - name: JAVA_OPTS
              value: -Dhudson.model.DownloadService.noSignatureCheck=true -Duser.timezone=Asia/Shanghai
          ports:
            - containerPort: 8080
              name: web
              protocol: TCP
            - containerPort: 50000
              name: agent
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 5
            failureThreshold: 12
          volumeMounts:
            - name: jenkinshome
              mountPath: /var/jenkins_home
      volumes:
        - name: jenkinshome
          persistentVolumeClaim:
            claimName: jenkins-pvc
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins
  namespace: argocd
spec:
  ingressClassName: nginx
  rules:
    - host: jenkins.tbchip.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: jenkins
                port:
                  name: web
[root@k8s-master01 argocd]# vim jenkins-nodeport.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins
  namespace: argocd
  labels:
    app: jenkins
spec:
  selector:
    app: jenkins
  ports:
    - name: web
      port: 8080
      protocol: TCP
      nodePort: 30989
    - name: agent
      port: 50000
      targetPort: agent
  type: NodePort
[root@k8s-master01 argocd]# kubectl apply -f jenkins.yaml
[root@k8s-master01 argocd]# kubectl apply -f jenkins-nodeport.yaml
获取密码
[root@k8s-master01 argocd]# kubectl logs -f jenkins-cd9fb8959-ccp95 -n argocd
3ddfc6fd163e48ebaac58bf8911fa9e2

输入管理员密码 —— 先勾选“无”(目的是取消所有勾选) —— 再勾选 “Languages” —— 安装

创建管理员用户—— admin/Zlsd@2025

修改时区——“admin” —— “设置” —— "时区:Shanghai"

安装插件

① 访问地址: 192.168.0.66:30989/manage/pluginManager/available

② 安装插件:

Git           
Lock
Pipeline     
timestamp
ThinBackup
Kubernetes    
Pipeline View 
Active Choices
Build Name and Description Setter
配置动态 slave 节点

① 访问地址: http://192.168.0.66:30989/manage/cloud/new

② Cloud name: Kubernetes || Type: Kubernetes —— create 创建动态节点

配置连接 Kubernetes API Server 的地址:

名称: Kubernetes

Kubernetes地址:https://kubernetes.default.svc.cluster.local

Kubernetes命名空间:argocd

”连接测试“(在页面右边): Connected to Kubernetes v1.28.0 —— 出现k8s版本即连接正常

配置Jenkins地址

Jenkins 地址 :http://jenkins.argocd.svc.cluster.local:8080 (这里看情况用https或http)

Jenkins 通道 :jenkins.argocd.svc.cluster.local:50000

Add Pod Labels —— 键:jenkins 值:jenkins

最后Save即可

配置凭据

① 访问地址:http://192.168.0.66:30989/credentials/store/system/domain/_/

② 添加凭据:

  • ID:registry-auth

    用户名:zwbnhic

    密码:xxxxxx

    描述:阿里云ACR容器镜像仓库认证信息

  • ID:gitlab-auth

用户名:root

密码:xxxxxx

描述:gitlab代码仓库认证信息

  • ID:argo-auth

    用户名:admin

    密码:xxxxxx

    描述:argocd认证信息

ArgoCD部分

wget -O argocd.yaml https://raw.githubusercontent.com/argoproj/argo-cd/v2.8.4/manifests/install.yaml
sed -i 's|quay.io/argoproj/argocd:v2.8.4|registry.cn-guangzhou.aliyuncs.com/cumybride/argocd:v2.8.4|g' argocd.yaml
sed -i 's|ghcr.io/dexidp/dex:v2.37.0|registry.cn-guangzhou.aliyuncs.com/cumybride/dex:v2.37.0|g' argocd.yaml
sed -i 's|redis:7.0.11-alpine|registry.cn-guangzhou.aliyuncs.com/cumybride/redis:7.0.11-alpine|g' argocd.yaml
sed -i 's/imagePullPolicy: Always/imagePullPolicy: IfNotPresent/g' argocd.yaml

允许不安全(禁用TLS)

[root@k8s-master01 argocd]# vim argocd.yaml
... ...
19839       containers:
19840       - args:
19841         - /usr/local/bin/argocd-server
19842         - --insecure     # 添加到 argocd-server命令

部署NodePort用于访问

[root@k8s-master01 argocd]# vim  argocd-nodeport.yaml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: server
    app.kubernetes.io/name: argocd-server
    app.kubernetes.io/part-of: argocd
  name: argocd-server-nodeport
  namespace: argocd
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    nodePort: 30980
  - name: serviceport     # 主要通过这个端口来进行访问
    port: 8080
    protocol: TCP
    nodePort: 30988
  - name: https
    port: 443
    protocol: TCP
    nodePort: 30943
  selector:
    app.kubernetes.io/name: argocd-server
  type: NodePort

本地安装CLI工具

curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v2.8.4/argocd-linux-amd64
chmod  + /usr/local/bin/argocd
部署
[root@k8s-master01 argocd]# kubectl create ns argocd
[root@k8s-master01 argocd]# kubectl apply -f argocd.yaml -n argocd && kubectl apply -f argocd-nodeport.yaml

访问验证:192.168.0.66:30988

获取密码
[root@k8s-master01 argocd]# kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echo
Fe-LAta6EA2BWwcQ     # admin账号的密码
Argocd配置认证
Argocd —— “Settings” —— “Repositories” —— “CONNECT REPO” —— 选择连接方法:[ VIA HTTPS ]

Type:git

Project:cumy

Repository URL:http://192.168.7.71:8028/root/cumy.git

Username (optional):root

Password (optional):xxxxxx

(这里密码不要使用特殊字符)

命令行登录

argocd login 192.168.0.66:30988
admin
******

实现的思路是这样的:

  • Jenkins利用ci/jnlp.yaml拉起Pod来完成Gitops全流程所需运行环境
  • Jenkins实现CI,拉代码打包成镜像,然后更新代码仓库helm目录的values.yaml文件(这通过envsubst+ values.tpl实现)
  • ArgoCD实现CD,创建APP,通过helm方式部署,监听仓库helm目录

二、Jenkins实现CI

2.1、实验做的demo展示(以下文件均存放/上传于gitlab代码仓库中: http://192.168.7.71:8028/root/cumy.git )

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

  • main.go
package main

import (
  "net/http"

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

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

  r.GET("/", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H {
      "msg": "Welcome to the Argocd GitOps.Here is new World",
    })
  })

  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")
  }
}

  • go.mod
module app

go 1.20

require (
	github.com/gin-gonic/gin v1.10.1
	github.com/sirupsen/logrus v1.9.3
)

require (
	github.com/bytedance/sonic v1.11.6 // indirect
	github.com/bytedance/sonic/loader v0.1.1 // indirect
	github.com/cloudwego/base64x v0.1.4 // indirect
	github.com/cloudwego/iasm v0.2.0 // indirect
	github.com/gabriel-vasile/mimetype v1.4.3 // indirect
	github.com/gin-contrib/sse v0.1.0 // indirect
	github.com/go-playground/locales v0.14.1 // indirect
	github.com/go-playground/universal-translator v0.18.1 // indirect
	github.com/go-playground/validator/v10 v10.20.0 // indirect
	github.com/goccy/go-json v0.10.2 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/klauspost/cpuid/v2 v2.2.7 // indirect
	github.com/leodido/go-urn v1.4.0 // indirect
	github.com/mattn/go-isatty v0.0.20 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	github.com/ugorji/go/codec v1.2.12 // indirect
	golang.org/x/arch v0.8.0 // indirect
	golang.org/x/crypto v0.23.0 // indirect
	golang.org/x/net v0.25.0 // indirect
	golang.org/x/sys v0.20.0 // indirect
	golang.org/x/text v0.15.0 // indirect
	google.golang.org/protobuf v1.34.1 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)

然后这个项目main.go 同级目录有个Jenkinsfile文件,这就是我们可以在Jenkins通过SCM配置git地址,然后指定路径读取Jenkinsfile,下面都是后续可以用于每个分支的固定文件

  • Jenkinsfile
// 初始化变量,用于存储提交ID和时间戳  
def COMMITID = ""  
def TIMESTAMP = ""  
pipeline {  
    agent {  
        // 使用Kubernetes代理,生成唯一的标签  
        kubernetes {  
            label "jnlp-slave-${UUID.randomUUID().toString().substring(0, 8)}"  
            yamlFile "ci/jnlp.yaml" // 指定YAML配置文件路径
        }  
    }  
    environment {  
        // 定义环境变量  
        DOCKER_REGISTRY = "registry.cn-guangzhou.aliyuncs.com" // Docker注册表地址
        REGISTRY_NAMESPACE = "cumybride" // 注册表命名空间  
        IMAGE = "${DOCKER_REGISTRY}/${REGISTRY_NAMESPACE}" // 完整的镜像地址
    }  

    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仓库URL
                    OPS_REPO = scm.getUserRemoteConfigs()[0].getUrl().replaceAll(/^https?:\/\//, "")
                    // 获取当前分支信息
                    env.BRANCH = env.GIT_BRANCH?.replace('origin/', '')?.trim()
                    // 获取当前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: 'registry-auth', // 使用jenkins凭据 registry-auth,用于拉/推镜像
                        usernameVariable: 'DOCKER_USER',  
                        passwordVariable: 'DOCKER_PASSWORD']]) {  
                        script {  
                            sh """  
                            echo "开启多架构编译,创建并使用Docker Buildx构建器,使用host主机网络"  
                            docker buildx create --name mybuilder --use --driver docker-container --driver-opt network=host --driver-opt  image=registry.cn-guangzhou.aliyuncs.com/cumybride/buildkit:buildx-stable-1  

                            echo "登陆镜像仓库"  
                            docker login ${DOCKER_REGISTRY} -u ${DOCKER_USER} -p ${DOCKER_PASSWORD}  

                            echo "构建/推送镜像,使用Buildx构建镜像并推送到注册表,支持多架构  "  
                            docker buildx build --progress=plain  --platform=linux/amd64 -f Dockerfile -t ${IMAGE}/${AppName}:${ImageTag} . --push  
                            """  
                        }  
                    }  
                }  
            }  
        }  
        stage('render ImageTag') {  
            // values.tpl 接收变量,通过envbust把变量tpl转换成值yaml,渲染到values.yaml,再通过helm读取values.yaml的值部署
            steps {  
                container('tools') { // 在工具容器中执行步骤
                    script {  
                        sh """  
                        echo "使用envsubst将values.tpl模板文件渲染helm可用的文件values.yaml;示例:echo 'Hello, $USER!' | envsubst -→ Hello,root!"
                        echo "注意,envsubst是纯shell命令,只替换shell环境变量,无法从Jenkins Pipeline Groovy的env自动继承变量,因此这里用export声明给shell"
                        export APP_NAME=${env.AppName}
                        export IMAGE_TAG=${env.ImageTag}
                        export REGISTRY_NAMESPACE=${env.REGISTRY_NAMESPACE}
                        export COMMITID=${env.COMMITID}
                        export DOCKER_REGISTRY=${env.DOCKER_REGISTRY}
                        export REGISTRY_NAMESPACE=${env.REGISTRY_NAMESPACE}
                        
                        envsubst < ./values.tpl > helm/values.yaml  
                        cat helm/values.yaml
                        echo "使用Helm模板渲染"
                        helm template --debug helm/ -f helm/values.yaml  
                        """  
                    }  
                }  
            }  
        }  
        stage('push yaml') {  
            steps {  
                withCredentials([[$class: 'UsernamePasswordMultiBinding',
                    credentialsId: 'gitlab-auth', // 使用gitlab-auth凭证
                    usernameVariable: 'GIT_USER',  
                    passwordVariable: 'GIT_PASSWORD']]) {  
                    script {  
                        sh """  
                        echo "切换到对应分支"
                        git checkout ${BRANCH}
                        echo "配置Git用户信息" 
                        git config --global user.email "ci"
                        git config --global user.name "ci@ci.com"
                        echo "添加所有更改"
                        git add .  
                        echo "提交更改,提交信息包含应用名称和镜像标签"
                        git commit -m "${AppName}-${ImageTag} " || true
                        echo "推送更改到GitOps仓库,按照实际情况配置https/http"
                        git push http://${GIT_USER}:${GIT_PASSWORD}@${OPS_REPO}
                        """
                    }
                }
            }
        }
        stage('argocd-sync') {
            steps {
               //含有argocd工具的tools容器
               container('tools-argocd'){
                    withCredentials([
                        usernamePassword(
                            credentialsId: 'argo-auth',
                            usernameVariable: 'ARGO_USER',
                            passwordVariable: 'ARGO_PASSWORD'
                        ),
                        usernamePassword(
                            credentialsId: 'gitlab-auth',
                            usernameVariable: 'GIT_USER',
                            passwordVariable: 'GIT_PASSWORD'
                        )
                    ]) {
                    script {
                        def loginArgoCmd = "echo y | argocd login --insecure 192.168.0.66:30988 --username ${ARGO_USER} --password ${ARGO_PASSWORD} --grpc-web"
                        try {
                            sh loginArgoCmd
        echo "✅ 成功登录到 ArgoCD"
                        } catch (Exception e) {
                            echo "❌ ArgoCD 登录失败: ${e.message}"
                            error("❌ ArgoCD 登录失败")
                        }
                        def loginGitCmd = "argocd repo add http://${OPS_REPO} --username ${GIT_USER} --password ${GIT_PASSWORD} --insecure-skip-server-verification --grpc-web --upsert"
                        try {
                            sh loginGitCmd
                            echo "✅ 成功登录到 REPO仓库"
                        } catch (Exception e) {
                            echo "❌ REPO仓库 登录失败: ${e.message}"
                            error("❌ REPO仓库 登录失败")
                        } 

                    try {
                        // 1. 确保 helm 目录存在
                        def helmExists = sh(script: "test -d helm && echo exists", returnStatus: true)
                        if (helmExists != 0) {
                            error("❌ helm 目录不存在,请检查代码仓库")
                        }

                        // 2. 检查应用是否存在
                        def appExists = sh(
                            script: "argocd app list --grpc-web | grep -w '${AppName}'",
                            returnStatus: true
                        )

                        // 3. 应用存在则更新,不存在则创建
                        if (appExists == 0) {
                            echo "🔄 ArgoCD 应用 ${AppName} 已存在,执行更新..."
                            sh """
                                argocd app set ${AppName} \
                                    --repo http://${OPS_REPO} \
                                    --path helm \
                                    --revision ${BRANCH} \
                                    --sync-policy automated \
                                    --self-heal \
                                    --grpc-web
                            """
                        } else {
                            echo "🆕 创建 ArgoCD 应用: ${AppName}"
                            sh """
                                argocd app create ${AppName} \
                                    --repo http://${OPS_REPO} \
                                    --path helm \
                                    --dest-namespace default \
                                    --revision ${BRANCH} \
                                    --dest-server https://kubernetes.default.svc \
                                    --sync-policy automated \
                                    --auto-prune \
                                    --sync-option PruneLast=true \
                                    --sync-option CreateNamespace=true \
                                    --grpc-web
                            """
                        }
                        echo "🔄 等待应用同步完成 ${AppName}..."
                        sh "argocd app wait ${AppName} --health --grpc-web"
                        echo "✅ ArgoCD 操作完成"
                    } catch (Exception e) {
                        error "❌ ArgoCD 操作失败: ${e.message}"
                        }
                    }
                    }
               }
            }
        }
    }  
}
  • ci/jnlp.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-slave
spec:
  volumes:
    - name: docker-socket
      emptyDir: {}
    - name: workspace-volume
      emptyDir: {}      
  serviceAccount: jenkins
  dnsPolicy: ClusterFirst
  #nodeName: k8s-node07
  containers:
    - name: jnlp
      image: registry.cn-guangzhou.aliyuncs.com/cumybride/inbound-agent:latest #原镜像:jenkins/inbound-agent:latest,换了个tag而已
    - name: tools
      image: registry.cn-guangzhou.aliyuncs.com/cumybride/tools:latest # 工具包镜像,用于CD
      command:
        - cat
      tty: true         
    - name: tools-argocd
      image: registry.cn-guangzhou.aliyuncs.com/cumybride/tools-argocd:latest  # 带argocd命令的tools镜像
      command: ["cat"]
      tty: true
    - name: docker
      image: registry.cn-guangzhou.aliyuncs.com/cumybride/docker:latest # 原镜像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-guangzhou.aliyuncs.com/cumybride/docker:19.03.1-dind # 原镜像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 --platform=linux/amd64 registry.cn-guangzhou.aliyuncs.com/cumybride/alpine:latest

# 安装kubectl helm yq envsubst 
RUN sed -i 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|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
  • ci/tools-argocd镜像的Dockerfile(包含argocd-cli工具的tools镜像)
FROM registry.cn-guangzhou.aliyuncs.com/cumybride/tools:latest
#RUN curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v2.7.4/argocd-linux-amd64  && chmod +x /usr/local/bin/argocd
ADD argocd-linux-amd64 /usr/local/bin/argocd
RUN chmod +x /usr/local/bin/argocd
  • Dockerfile (运行这个go程序的镜像)
# 指定基础的go编译镜像
FROM registry.cn-guangzhou.aliyuncs.com/cumybride/golang:alpine3.17 as build

# 指定go的环境变量
RUN go env -w GOPROXY= https://goproxy.cn ,direct   # https://mirrors.aliyun.com/goproxy/
RUN go env -w  \
    GOPROXY= https://goproxy.cn,https://mirrors.aliyun.com/goproxy/ ,direct \
    GOSUMDB=off \
    GO111MODULE=on
# 指定工作空间目录,会自动cd到这个目录
WORKDIR /opt

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

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

#FROM registry.cn-guangzhou.aliyuncs.com/cumybride/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/*
# 这里如果网络不稳定,apk update偶发报错,可以考虑直接使用预装依赖的镜像;即找网络稳定的机器用上面俩FROM+RUN来build一个 alpine-ca:latest
FROM registry.cn-guangzhou.aliyuncs.com/cumybride/alpine-ca:latest as serviceDeploy

ENV TZ=Asia/Shanghai

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

2.2 Jenkins环境配置

配置动态 slave 节点

① 访问地址: http://192.168.0.66:30989/manage/cloud/new

② Cloud name: Kubernetes || Type: Kubernetes —— create 创建动态节点

配置连接 Kubernetes API Server 的地址:

名称: Kubernetes

Kubernetes地址:https://kubernetes.default.svc.cluster.local

Kubernetes命名空间:argocd

”连接测试“(在页面右边): Connected to Kubernetes v1.28.0 —— 出现k8s版本即连接正常

配置Jenkins地址

Jenkins 地址 :http://jenkins.argocd.svc.cluster.local:8080 (这里看情况用https或http)

Jenkins 通道 :jenkins.argocd.svc.cluster.local:50000

Add Pod Labels —— 键:jenkins 值:jenkins

最后Save即可

配置凭据

① 访问地址:http://192.168.0.66:30989/credentials/store/system/domain/_/

② 添加凭据:

  • ID:registry-auth

    用户名:zwbnhic

    密码:xxxxxx

    描述:阿里云ACR容器镜像仓库认证信息

  • ID:gitlab-auth

用户名:root

密码:xxxxxx

描述:gitlab代码仓库认证信息

  • ID:argo-auth

    用户名:admin

    密码:xxxxxx

    描述:argocd认证信息

2.3 Jenkins配置pipeline实现CI

Jenkins——"新建item"——”流水线:cumy“ # 这里跟代码仓库的项目名同名

流水线——”Configuration“

定义:Pipeline script from SCM

SCM:Git

Repositories URL:http://192.168.7.71:8028/root/cumy.git

Credentials:root/*** (gitlab代码仓库认证信息),前面创建的 gitlab-auth

Branches to build: gitops/dev-1.0

脚本路径:Jenkinsfile

三、Argocd 实现CD

3.1 实验做的demo展示(以下文件均存放/上传于gitlab代码仓库中: http://192.168.7.71:8028/root/cumy.git )

利用helm命令渲染helm/templates目录下的deployment.yaml,这里的变量全部由values.yaml渲染而成

  • helm/templates/deployment.yaml (这里按实际需求写,我这里简单写一个deployment)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.app.name }}-deployment
  labels:
    app: {{ .Values.app.name }}
  #namespace: argocd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: {{ .Values.app.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.app.name }}
    spec:
      #nodeName: k8s-node07
      containers:
      - name: {{ .Values.app.name }}
        image: "{{ .Values.app.image.repository }}:{{ .Values.app.image.tag }}"
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 2
          periodSeconds: 5
  • helm/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.app.name }}-svc
  #namespace: argocd
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.app.name }}
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

然后这个项目还有用于Helm的values.tpl和helm/Chart.yaml,下面都是后续可以用于每个分支的固定文件

  • values.tpl (利用envsubst命令生成/更新代码仓库helm目录下的values.yaml文件,这里的变量全部由tools容器自动替换)
app:
  name: ${APP_NAME}
  image:
    repository: ${DOCKER_REGISTRY}/${REGISTRY_NAMESPACE}/${APP_NAME}
    tag: ${IMAGE_TAG}
    commitId: ${COMMITID}
  • helm/Chart.yaml (用于Helm部署应用)
apiVersion: v2
name: cumy
description: A Helm chart for Jenkins CI Demo App
type: application
version: 0.1.0
appVersion: "1.0"

3.2 Argocd创建APP实现CD

注意:Jenkinfile中已经定义”利用tools-argocd容器完成更新/同步APP的动作实现CD“,这里只是针对WEB端和手动创建的说明

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

ArgoCD的APP配置文件参考: ”NEW APP“ → “EDIT AS YAML”

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: http://192.168.7.71:8028/root/cumy.git  # 指向包含应用代码和 Helm Charts 的 Git 仓库的 URL  
    targetRevision: gitops/dev-1.0           # 指定要使用的代码分支  
  destination:                     # 目标 Kubernetes 集群和命名空间配置  
    server: 'https://kubernetes.default.svc'  # 指向默认的 Kubernetes API 服务器  
    namespace: argocd            # 应用程序部署到的 Kubernetes 命名空间为 argocd  
  syncPolicy:                      # 同步策略设置  
    automated:                     # 启用自动同步  
      prune: true                  # 在同步过程中自动删除不再需要的资源
      selfHeal: true               # 启用自我恢复功能,若检测到偏差会自动纠正  
或者直接在UI上配置Argocd实现CD
Argocd —— “Applications” —— “NEW APP”

【GENERAL】

Application Name:cumy

Project Name:default (如需自定义项目名称,需先创建)

SYNC POLICY(默认):Manual

【SOURCE】

Repository URL(Git):http://192.168.7.71:8028/root/cumy.git

Revision(Branches):gitops/dev-1.0

Path:helm

(这里的路径是 仓库中 Kubernetes 清单文件的子目录路径 , 包含应用的 YAML 文件,如 :Deployment、Service)

【DESTINATION】

Cluster URL:https://kubernetes.default.svc

Namespace:argocd

实际使用

切换分支仅需要在jenkins-Web那边修改分支即可,

新建项目就把对应文件Fork过去就可以了,然后改改部署yaml就可以点构建了

ps:除了部署的yaml:deployment.yaml + service.yaml 和 代码:main.go + go.mod

posted @ 2025-06-07 02:03  cumybride  阅读(254)  评论(0)    收藏  举报