jenkins pipeline语法、自动生成、部署案例

发布时间 2023-10-23 11:37:08作者: 蕝戀

Jenkins Pipeline是一套插件,支持在Jenkins中实现持续集成和持续交付;

pipeline的编写都要写入到一个名为Jenkinsfile的文件中。

流水线脚本管理 Jenkinsfile

流水线脚本有两种写入管理方法:

1、在 Jenkins - Gui 界面里写。

2、存放在 gitlab,与 项目代码同一级目录。命名为 Jenkinsfile ( j 必须大写 ) 文件,编写具体pipeline代码。

好处:****在代码项目中和的 Jenkinsfile ,方便管理,方便备份,不会因为 jenkins宕机丢失整个流水线配置,而且又具备了代码仓的功能,可查看历史记录、复查等等。

如果方法2将Jenkins放在代码层后的使用方法:在 jenkins - GUI 界面 通过 ****pipeline SCM ****连接到 gitlab仓库,获取 Jenkinsfile 文件

如下图:

pipeline语法

pipeline语法有两种

  • Declarative Pipeline(声明式),推荐使用。
  • Scripted Pipeline(脚本式),旧版,很少用了。

声明式pipeline:推荐使用。

Jenkinsfile (Declarative Pipeline)
pipeline {
    agent any
    stages { # stages表示这是一组阶段stage,由多个阶段stage组成,按顺序从上到下执行每个stage
        stage('Example') { # 具体阶段,由多个steps组成, 这里的Example时指定这个stage的名称
            steps { # 定义步骤,用于指定该阶段具体要执行什么步骤,如:执行命令等等
                echo 'Hello World'
            }
        }
    }
}

脚本式pipeline:

Jenkinsfile (Scripted Pipeline)
node {
    stage('Example') {
         echo 'Hello World'
    }
}

语法自动生成

片段生成器:可以自动生产拉取、打包、部署的代码。直接粘贴到 pipeline即可使用。

Declarative Directive Generator:声明式指令生成器。

Declarative Online Documentation:声明式指令官方文档

片段生成器

拉取代码:check out from version control

pipeline指令生成器

在实际工作中,会维护多个项目,如果每个服务都创建一个item,势必给运维维护成本增加很大,因此需要编写一个通用Pipeline脚本,将这些项目部署差异化部分使用Jenkins参数化,人工交互确认发布的分支、副本数、命名空间等。

生成通用化参数-拉取指定分支得代码

parameters {
  gitParameter branch: '', branchFilter: '.*', defaultValue: 'origin/master', description: '选取拉取代码的分支', name: 'userChoiceBranch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
}

生成单选项-自定义单选项-

parameters {
  choice choices: ['dev', 'pord', 'test', 'default'], description: '发布到哪个namespace', name: 'deployNamespace'
}

完整部署项目到K8S的pipeline脚本示例:

/// 使用def 定义变量

// harbor镜像仓库地址
def registry = "10.0.1.140"

// harbor项目名称
def project = "test"

// harbor项目中的镜像名
def app_name = "java-demo"

// 具体镜像名,镜像的tag版本号,引用的时jenkins自带的BUILD_NUMBERB变量,每build一次这个变量都会自增+1.
def image_name = "${registry}/${project}/${app_name}:${BUILD_NUMBER}"

// 代码仓库地址
def git_address = "http://10.0.1.141:88/root/jenkinstest.git"

//harbor仓库的账号和密码登录凭据
def harbor_auth = "a6e4e47a-dbdd-45af-8735-eb8d158c104d"

// git的账号和密码凭据ID
def git_auth = "8370aa1b-ec04-4caa-b753-851f7fe169a5"

// kubeconfig文件的id值,用于执行kubectl命名时引用。
def k8s_kubeconfig = "4b6cb8e6-e5e4-42a1-8d4b-3514e51a5d3e"

// pod部署时向harbor拉取镜像的secret凭据名称
def secret_name = "myharbor"

pipeline {
    agent {
        kubernetes {
            label "jenkins-slave"
            yaml """
apiVersion: v1
kind: Pod
metadata:
  name: jenkins-slave
spec:
  containers:
  - name: jnlp
    image: "10.0.1.140/library/jenkins-slave-jdk:1.8.1"
    imagePullPolicy: Always
    volumeMounts: # 将宿主机的docker命令这些程序挂载进来pod容器中使用。因为你要打包镜像要用到docker命令
      - name: docker-cmd
        mountPath: /usr/bin/docker
      - name: docker-sock
        mountPath: /var/run/docker.sock
      - name: maven-cache
        mountPath: /root/.m2 # 挂载maven仓库的缓存,这样以后再次执行编译就需要全部重新下载项目要用到的依赖jar包等。因为slave pod每次执行完就会被delete的。
  volumes:
    - name: docker-cmd
      hostPath:
        path: /usr/bin/docker
    - name: docker-sock
      hostPath:
        path: /var/run/docker.sock
    - name: maven-cache
      hostPath:
        path: /tmp/m2
"""
        }
    }
    
    **// 定义一些参数选择器,这些会在运行任务前,让执行者去进行选择**
    parameters {
        // 分支选择器
        gitParameter branch: '', branchFilter: '.*', defaultValue: 'master', description: '选择拉取gitlab项目代码的分支', name: 'chooesebranch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
        // 选择部署deployment的副本数
        choice choices: ['1', '2', '3', '4', '5'], description: '请选择部署到集群的副本数', name: 'replicas'
        // 选择部署到哪个namespace中
        choice choices: ['default', 'dev', 'prod', 'test'], description: '请选择部署到集群的哪个namespace', name: 'namespace'
    }
    
    // 定义每个阶段执行什么东西
    stages {
        stage('从gitlab拉取代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "${chooesebranch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
            }
        }
        stage('编译项目代码') {
            steps {
               sh """
                mvn -B -DskipTests clean package
               """
            }
        }
        stage('打包镜像并推送到harbor仓库') {
            steps {
                // 使用withCredentials从jenkins凭据中读取harbor的用户名和密码,并分别保存到指定的变量中,在执行命令时会指定的地方自动输入
                withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'harborpw', usernameVariable: 'harborusername')]) {
                    // some block
                   sh """
                      # 解压编译后的war包到当前目录
                      unzip target/*.war -d target/ROOT  
                      # 生成一个Dockerfile,用于后面的打包镜像
                      # 这里其实推荐将Dockerfile也放入到代码项目中,这样可以直接用就行,不需要echo。。。
                      echo '
                        FROM lizhenliang/tomcat
                        LABEL maitainer lizhenliang
                        ADD target/ROOT /usr/local/tomcat/webapps/ROOT
                      ' > Dockerfile
                      # 打包docker镜像
                      docker build -t ${image_name} .
                      # 登录harbor镜像仓库,用户名和密码调用jenkins的凭据管理器中存储的相关凭据
                      docker login -u "${harborusername}" -p "${harborpw}" ${registry}
                      # 推送镜像到镜像仓库
                      docker push ${image_name}
                   """
                }
            }
        }
        stage('部署到k8s中') {
            steps {
                configFileProvider([configFile(fileId: "${k8s_kubeconfig}", targetLocation: "my.kubeconfig")]) {
                  // some block
                  sh """
                              sed -i 's#J_IMAGE_NAME#${image_name}#' deploy.yml
                              sed -i 's#J_SECRET_NAME#${secret_name}#' deploy.yml
                              sed -i 's#J_REPLICASNUM#${replicas}#' deploy.yml
                              echo "----------------------------------"
                              cat deploy.yml
                              echo "----------------------------------"
                    kubectl apply -f deploy.yml -n ${namespace} --kubeconfig=my.kubeconfig
                  """
                }
            }
        }
    }
}
/// 使用def 定义变量

// harbor镜像仓库地址
def registry = "192.168.2.6"

// harbor项目名称
def project = "library"

// harbor项目中的镜像名
def app_name = "k8s-flask-web"

// 具体镜像名,镜像的tag版本号,引用的时jenkins自带的BUILD_NUMBERB变量,每build一次这个变量都会自增+1.
def image_name = "${registry}/${project}/${app_name}:${BUILD_NUMBER}"

// 代码仓库地址
def git_address = "https://jihulab.com/SWQJueLian/k8s-flask.git"

//harbor仓库的账号和密码登录凭据
def harbor_auth = "f74d85d9-2fe3-41c3-b8df-6b56ce88545c"

// git的账号和密码凭据ID
def git_auth = "61af6ddc-6d2c-4888-b758-8afd5d529929"

// kubeconfig文件的id值,用于执行kubectl命名时引用。
def k8s_kubeconfig = "7ea988e4-f4e0-44ec-b99d-9b7852461bf3"

// pod部署时向harbor拉取镜像的secret凭据名称
def secret_name = "myharbor"

pipeline {
    agent {
        kubernetes {
            label "jenkins-slave"
            yaml """
apiVersion: v1
kind: Pod
metadata:
  name: jenkins-slave-py3
spec:
  containers:
  - name: jnlp
    image: "192.168.2.6/library/jenkins-slave@sha256:e9f63cf7551b8970645fa61aaa0c9819152f45bb02e5dc4ba8114acb3f5b0564"
    volumeMounts: # 将宿主机的docker命令这些程序挂载进来pod容器中使用。因为你要打包镜像要用到docker命令
      - name: localtime # 时区用宿主机的
        mountPath: /etc/localtime
      - name: nerdctl
        mountPath: /usr/bin/nerdctl # nerdctl肯定是要的...锤子个麻烦,还不如docker...
      - name: buildctl
        mountPath: /usr/bin/buildctl # containerd打包镜像
      - name: containerd-sock
        mountPath: /run/containerd/containerd.sock
      - name: buildkitd-sock # buildkit的socket也要挂载
        mountPath: /run/buildkit/buildkitd.sock
  volumes:
    - name: localtime
      hostPath:
        path: /etc/localtime
    - name: buildctl
      hostPath:
        path: /usr/local/bin/buildctl
    - name: kubectl # kubectl 看情况挂载,也可以准备自己的,提前打到jenkins-slave的镜像中。
      hostPath:
        path: /usr/bin/kubectl
    - name: nerdctl
      hostPath:
        path: /usr/local/bin/nerdctl
    - name: containerd-sock
      hostPath:
        path: /run/containerd/containerd.sock
    - name: buildkitd-sock
      hostPath:
        path: /var/run/buildkit/buildkitd.sock
"""
        }
    }
    
    // 定义一些参数选择器,这些会在运行任务前,让执行者去进行选择
    parameters {
        // 分支选择器
        gitParameter branch: '', branchFilter: '.*', defaultValue: 'master', description: '选择拉取gitlab项目代码的分支', name: 'chooesebranch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
        // 选择部署deployment的副本数
        choice choices: ['1', '2', '3', '4', '5'], description: '请选择部署到集群的副本数', name: 'replicas'
        // 选择部署到哪个namespace中
        choice choices: ['default', 'dev', 'prod', 'test'], description: '请选择部署到集群的哪个namespace', name: 'namespace'
    }
    
    stages {
        stage('从gitlab拉取代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "${chooesebranch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
            }
        }
        stage('编译项目代码') {
            steps {
               sh """
                mvn -B -DskipTests clean package
               """
            }
        }
        stage('打包镜像并推送到harbor仓库') {
            steps {
                // 使用withCredentials从jenkins凭据中读取harbor的用户名和密码,并分别保存到指定的变量中,在执行命令时会指定的地方自动输入
                withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'harborpw', usernameVariable: 'harborusername')]) {
                    // some block
                   sh """
                      mkdir -pv imagepackage/target/ROOT
                      # 解压编译后的war包到当前目录
                      unzip target/*.war -d imagepackage/target/ROOT
                      cp Dockerfile imagepackage/
                      ls imagepackage/
                      # 生成一个Dockerfile,用于后面的打包镜像
                      # 这里其实推荐将Dockerfile也放入到代码项目中,这样可以直接用就行,不需要echo。。。
                      #echo '
                      #  FROM lizhenliang/tomcat
                      #  LABEL maitainer lizhenliang
                      #  ADD target/ROOT /usr/local/tomcat/webapps/ROOT
                      #' > Dockerfile
                      # 打包docker镜像
                      cd imagepackage
                      docker build -t ${image_name} .
                      # 登录harbor镜像仓库,用户名和密码调用jenkins的凭据管理器中存储的相关凭据
                      docker login -u "${harborusername}" -p "${harborpw}" ${registry}
                      # 推送镜像
                      docker push ${image_name}
                      ls -l
                      cd ../
                   """
                }
            }
        }
        stage('部署到k8s中') {
            steps {
        configFileProvider([configFile(fileId: "${k8s_kubeconfig}", targetLocation: "my.kubeconfig")]) {
          // some block
          sh """
                      sed -i 's#J_IMAGE_NAME#${image_name}#' deploy.yml
                      sed -i 's#J_SECRET_NAME#${secret_name}#' deploy.yml
                      sed -i 's#J_REPLICASNUM#${replicas}#' deploy.yml
                      sed -i 's#J_NS#${namespace}#g' deploy.yml
                      echo "----------------------------------"
                      # cat deploy.yml
                      echo "----------------------------------"
            kubectl apply -f deploy.yml --kubeconfig=my.kubeconfig
          """
        }
            }
        }
    }
}