K8S搭建DevOps环境二

  • Jenkins Pipeline介绍
  • 说明
  • 谁在工作?
  • 流程
  • 使用Pipeline构建JAVA项目
  • 创建java项目
  • jenkins文件说明
  • agent
  • 参数化构建
  • 环境参数配置
  • pipeline 阶段步骤
  • 代码拉取
  • 代码扫描检查和测试
  • 构建和推送镜像
  • 推送lastest到仓库
  • 部署到测试环境
  • 版本镜像推送
  • 是否部署到生产环境
  • 创建jenkins部署项目
  • 参数化配置
  • 构建触发器-Gitee Webhook 配置
  • 流水线
  • 构建


Jenkins Pipeline介绍

说明

上篇文章已经初步搭建起来了DevOps环境了,并且创建了一个简单的项目进行演示了,这篇文章就真正的部署一个java的项目,来演示下一套完整的DevOps的流程是怎么样的,这里只演示一个简单的流程,项目也简单,就是一个spring boot的简单应用,流程如下:

1、创建一个spring boot项目,编写代码、jenkinsfile文件、Dockfile文件; 2、代码写好以后上传到gitee上;
3、在jenkins上创建流水线项目,通过pipeline来构建整个流程。

谁在工作?

本地devops 配置 devops环境_本地devops 配置


一个大体的流程入上图:

jenkins作为一个pod在k8s上运行,所有的任务都是由jenkins的master进行编排,流水线的编排工作也是在master上,当构建启动以后,会在对应的节点将创建一个slave的pod容器,这个pod容器作为jenkins的slave容器,可以使用宿主机的k8s客户端命令,也就是kubectl **命令,而整个构建过程是需要挂载的,比如说下载的代码放哪里?mavn的依赖包放哪里?所有需要有挂载,我这里一直使用的nfs,nfs在之前的文章中已经写了如何创建。

完整的流程如下:

  1. Git content(源码清单管理)
    a) Source code(编写代码)
    b) Jenkinsfile/Dockerfile/Deploy yaml(编写dockerfile jenkins pipeline等文件) 1 . Git repository(代码管理仓库,有三种)
    a) GitHub
    b) Gitee
    c) GitLab
  2. Docker registry-Images(镜像管理)
    a) Docker Hub
    b) Aliyun Hub c) Private
  3. K8s – deploy enviroment(pod容器管理)
    a) Testing
    b) Productions

每一个都需要相应的用户名和密码进行连接,需要在 jenkins 进行配置
大体步骤: General(基础配置)–》源码管理–》构建触发器–》构建环境–》构建–》构建

流程

本地devops 配置 devops环境_本地devops 配置_02


开发人员把做好的 devops-java-sample 项目代码通过 git 推送到 gitee,然后 Jenkins 通过 gitee webhook下来,(前提是配置好),自动从拉取 gitee 上面拉取代码然后进行 build,编译、生成镜像、然后把镜像推送到 Harbor 仓库;然后在部署的时候通过 k8s 拉取 Harbor 上面的代码进行创建容器和服务,最终发布完成,然后可以用外网访问。(ps:看着我讲这么简单,但心里有许多小鹿在心里乱撞,没关系,下面将会好好的分享给大家)当然啦,上面只是粗略的,请看下图才更加形象。

本地devops 配置 devops环境_docker_03

使用Pipeline构建JAVA项目

创建java项目

我这里java项目已经提前创建好了,反正就是在idea中很简单的创建了一个java项目,项目gitee地址为:

https://gitee.com/scjava/devops-demo.git

项目如下:

本地devops 配置 devops环境_git_04


deploy:是该项目的deployment文件,就是当镜像推送到仓库成功以后,创建部署的yaml文件目录

src:是源码文件,很简单,就一个启动类和一个controller

Dockerfile:docker 的build文件,当源码从gitee上拉取下来以后打包成jar过后,build镜像的时候的文件;

jenkinsfile:jenkins的流水线文件

比较重要的文件就这么几个,代码很简单,就写了一个Controller,内容如下:

本地devops 配置 devops环境_devops_05

jenkins文件说明

这把重点说下jenkins的流水线文件,可能初学者不是很清楚这个jenkins文件编写语法,文件内容如下:
下面将会对其进行分段说明

pipeline {

  agent {
   label 'maven'
  }
    
   parameters { 
        string(name: 'TAG_NAME',defaultValue: '1.3',description: '')
        choice choices: ['devops-java-sample', 'gateway', 'order', 'product'], name: 'APP_NAME'
    }

    environment {
        DOCKER_CREDENTIAL_ID = 'aliyun-register'
        GITEE_CREDENTIAL_ID = 'GITEE'
        KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
        REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        DOCKERHUB_NAMESPACE = 'bml-services'
        GITEE_ACCOUNT = 'scjava'
        BRANCH_NAME = 'master'
        }

    stages {
        stage ('checkout scm') {
            steps {
               // checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitee-id', url: 'https://gitee.com/liuyik8s/devops-java-sample.git']]])
               sh 'pwd'
               sh  'sleep 2'
            }
        }
 
        stage('checking'){
            steps {
                sh '''
                pwd
                echo "webhook"
                echo "webhook"
                ls -l
                sleep 2
                echo "$[app]"
                '''
            }    
      }

        stage('Unit Testing'){
          steps {
            echo "Unit Testing..."
          }
    }
    
       stage ('unit test') {
            steps {
                container ('maven') {
     //              sh 'mvn clean  -gs `pwd`/configuration/settings.xml test'
                }
            }
        }
 
        stage ('build & push') {
            steps {
                container ('maven') {
                    sh 'mvn  -Dmaven.test.skip=true -gs `pwd`/configuration/settings.xml clean package'
                    sh 'sleep 1'
                    sh 'env'
                    sh 'ls -l target'
                    sh 'docker build -f Dockerfile-online -t $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME .'
                    withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$DOCKER_CREDENTIAL_ID" ,)]) {
                        sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
                        sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME'
                    }
                }
            }
        }
    
        stage('push latest'){
           when{
             branch 'master'
           }
           steps{
                container ('maven') {
                  sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:latest '
                  sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:latest '
                }
           }
       }
      
       stage('deploy to dev') {
          when{
            branch 'master'
          }
          steps {
             container ('maven'){
       //     input(id: 'deploy-to-dev', message: 'deploy to dev?')
              sh "ls -l deploy/dev-ol"
              sh "sleep 1"
              sh "ls -l deploy/dev-ol"
              sh "cat deploy/dev-ol/*.yaml"
              sh "sleep 1"
             
             sh '''
             echo "changing parameter"
             cp deploy/dev-ol/devops-sample.yaml k8s.yaml
             
             sed -i "s#TAG_NAME#$BUILD_NUMBER#g" k8s.yaml
             sed -i "s#REGISTRY#$REGISTRY#g" k8s.yaml
             sed -i "s#DOCKERHUB_NAMESPACE#$DOCKERHUB_NAMESPACE#g" k8s.yaml
             sed -i "s#APP_NAME#$APP_NAME#g" k8s.yaml
             sed -i "s#BRANCH_NAME#$BRANCH_NAME#g" k8s.yaml

             '''
           //  sh 'kubectl delete -f k8s.yaml -n testing'
             sh "sleep 10"
             sh "cat k8s.yaml"
             sh 'kubectl apply -f k8s.yaml -n testing'
           }
          }
        }

        stage('push with tag'){
          
            steps {
              container ('maven') {
         //       input(id: 'release-image-with-tag', message: 'release image with tag?')
//                   withCredentials([usernamePassword(credentialsId: "$GITEE_CREDENTIAL_ID", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
//                     sh 'git config --global user.email "bmldyq@163.com" '
//                     sh 'git config --global user.name "bmldyq@163.com" '
//                     sh 'git tag -a $TAG_NAME -m "$TAG_NAME" '
//                     sh 'git push http://$GIT_USERNAME:$GIT_PASSWORD@gitee.com/$GITEE_ACCOUNT/$APP_NAME.git --tags --ipv4'
//                     }
            
                  sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$TAG_NAME '
                  sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$TAG_NAME '
               }
           }
        }
 
       stage('deploy to production') {
       
          steps {
           container ('maven') {
            input(id: 'deploy-to-production', message: 'deploy to production?')
           // sh 'kubectl delete -f k8s.yaml -n production'
             sh "env"
             sh "sleep 10"
             sh "cat k8s.yaml"
             sh 'kubectl apply -f k8s.yaml -n production'
          }
        }
     }

    }


}

agent

agent {
   label 'maven'
  }

这个是在jenkins的云配置上配置的那个容器的标签,我们取名是maven,忘记的可以去看下上篇文章

参数化构建

parameters { 
     string(name: 'TAG_NAME',defaultValue: '1.3',description: '')
     choice choices: ['devops-java-sample', 'gateway', 'order', 'product'], name: 'APP_NAME'
 }

这里的意思是说进行参数化构建,比如说应用名称和应用版本,这个根据自身应用需要进行配置,上面配置了两个参数,一个是文本参数,一个范围参数,就是可以构造的时候可以自由选择,并且设置了默认值

环境参数配置

environment {
    DOCKER_CREDENTIAL_ID = 'aliyun-register'
    GITEE_CREDENTIAL_ID = 'GITEE'
    KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
    REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
    DOCKERHUB_NAMESPACE = 'bml-services'
    BRANCH_NAME = 'master'
    }

DOCKER_CREDENTIAL_ID :配置的阿里云的id,这个id是jenkins上配置凭证里面自己设置的ID;
GITEE_CREDENTIAL_ID :类似,就是在jenkins上配置的gitee的ID;
KUBECONFIG_CREDENTIAL_ID: K8S配置ID REGISTRY :镜像仓库地址 DOCKERHUB_NAMESPACE
:阿里云仓库的命名空间 BRANCH_NAME:gitee的分支名称

pipeline 阶段步骤

代码拉取
stage ('checkout scm') {
    steps {
       // checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitee-id', url: 'https://gitee.com/liuyik8s/devops-java-sample.git']]])
       sh 'pwd'
       sh  'sleep 2'
    }
}

这里我注释了,因为代码拉取的我让jenkins上配置的去做,待会儿就可以看到,如果说代码拉取这些要pipeline来做,也可以在脚本上写,反正都是可以的。

代码扫描检查和测试
stage('checking'){
      steps {
          sh '''
          pwd
          echo "webhook"
          echo "webhook"
          ls -l
          sleep 2
          echo "$[app]"
          '''
      }    
}
 stage('Unit Testing'){
      steps {
        echo "Unit Testing..."
      }
}

我这里只是模拟代码扫描,扫描是否有bug,根据需要进行,我这里模拟过程,这里可以将一些单元测试攻击和代码扫描攻击集成进来,然后编写脚本进行执行

构建和推送镜像
stage ('build & push') {
    steps {
        container ('maven') {
            sh 'mvn  -Dmaven.test.skip=true -gs `pwd`/configuration/settings.xml clean package'
            sh 'sleep 1'
            sh 'env'
            sh 'ls -l target'
            sh 'docker build -f Dockerfile-online -t $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME .'
            withCredentials([usernamePassword(passwordVariable : 'DOCKER_PASSWORD' ,usernameVariable : 'DOCKER_USERNAME' ,credentialsId : "$DOCKER_CREDENTIAL_ID" ,)]) {
                sh 'echo "$DOCKER_PASSWORD" | docker login $REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME'
            }
        }
    }
}

1、mvn编译代码成jar
2、docker build成镜像;
3、docker login到镜像仓库;
4、docker本地打个标签,然后推送版本到镜像仓库;

推送lastest到仓库
stage('push latest'){
    when{
      branch 'master'
    }
    steps{
         container ('maven') {
           sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:latest '
           sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:latest '
         }
    }
}

这个意思就是说每次打版本以后作为最新的一个版本latest也推送到镜像仓库

部署到测试环境
stage('deploy to dev') {
   when{
     branch 'master'
   }
   steps {
      container ('maven'){
//     input(id: 'deploy-to-dev', message: 'deploy to dev?')
       sh "ls -l deploy/dev-ol"
       sh "sleep 1"
       sh "ls -l deploy/dev-ol"
       sh "cat deploy/dev-ol/*.yaml"
       sh "sleep 1"
      
      sh '''
      echo "changing parameter"
      cp deploy/dev-ol/devops-sample.yaml k8s.yaml
      
      sed -i "s#TAG_NAME#$BUILD_NUMBER#g" k8s.yaml
      sed -i "s#REGISTRY#$REGISTRY#g" k8s.yaml
      sed -i "s#DOCKERHUB_NAMESPACE#$DOCKERHUB_NAMESPACE#g" k8s.yaml
      sed -i "s#APP_NAME#$APP_NAME#g" k8s.yaml
      sed -i "s#BRANCH_NAME#$BRANCH_NAME#g" k8s.yaml

      '''
    //  sh 'kubectl delete -f k8s.yaml -n testing'
      sh "sleep 10"
      sh "cat k8s.yaml"
      sh 'kubectl apply -f k8s.yaml -n testing'
    }
   }
 }

将dev中的做好的yaml文件复制到根路径,然后通过 sed i “s//g”来替换里面的参数,比如镜像地址,应用版本这些
Sed 命令简要介绍
sed -i 就是直接对文本文件进行操作的
sed -i ‘s/原字符串/新字符串/’ /home/1.txt
sed -i ‘s/原字符串/新字符串/g’ /home/1.txt
需要使用双引号,如果替换值为环境变量
sed -i “s#BUILD_NUMBER#$BUILD_NUMBER#g” k8s.yaml
sed -i ‘sBUILD_NUMBER/TAG_NAME’ k8s.yaml
使用 sed 命令修改 YAML 文件中的变量值。

最后执行这个yaml文件到命名空间testing中

版本镜像推送
stage('push with tag'){
          
            steps {
              container ('maven') {
         //       input(id: 'release-image-with-tag', message: 'release image with tag?')
//                   withCredentials([usernamePassword(credentialsId: "$GITEE_CREDENTIAL_ID", passwordVariable: 'GIT_PASSWORD', usernameVariable: 'GIT_USERNAME')]) {
//                     sh 'git config --global user.email "bmldyq@163.com" '
//                     sh 'git config --global user.name "bmldyq@163.com" '
//                     sh 'git tag -a $TAG_NAME -m "$TAG_NAME" '
//                     sh 'git push http://$GIT_USERNAME:$GIT_PASSWORD@gitee.com/$GITEE_ACCOUNT/$APP_NAME.git --tags --ipv4'
//                     }
            
                  sh 'docker tag  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:SNAPSHOT-$BRANCH_NAME-$TAG_NAME $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$TAG_NAME '
                  sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$TAG_NAME '
               }
           }
        }

这里又推送了一个版本镜像到仓库,还在gitee上打了一个版本标签,只是这里我暂时注释掉了

是否部署到生产环境
stage('deploy to production') {
  
     steps {
      container ('maven') {
       input(id: 'deploy-to-production', message: 'deploy to production?')
      // sh 'kubectl delete -f k8s.yaml -n production'
        sh "env"
        sh "sleep 10"
        sh "cat k8s.yaml"
        sh 'kubectl apply -f k8s.yaml -n production'
     }
   }
}

让运维人员选择是否部署到生产,如果部署到生产则执行yaml

创建jenkins部署项目

本地devops 配置 devops环境_本地devops 配置_06


创建一个流水线的java项目,名字随便取

参数化配置

参数化也可以在这里配置,这里最好配置好,否则就是用jenkinsfile里面的默认值,这里配置了就可以根据情况需要输入不同的参数

本地devops 配置 devops环境_devops_07


本地devops 配置 devops环境_git_08


对应到jenkinsfile里面的两个参数

构建触发器-Gitee Webhook 配置

gitee的webhook是需要安装插件的,安装 Generic Webhook Trigger 插件在“系统设置– 插件管理– 可选插件”界面搜索: Generic Webhook Trigger,可以看到,点击安装,然后重启。

本地devops 配置 devops环境_java_09


安装完就重启了:

本地devops 配置 devops环境_本地devops 配置_10


安装好了,再进行java这个项目的配置就可以看到有个选项:

本地devops 配置 devops环境_git_11


本地devops 配置 devops环境_git_12


在gitee上配置这个地址过后,当开发人员提交版本代码以后就会自动触发jenkins的自动化部署,这个在ci/cd上是很有用,完全不用人工干预就做到自动化构建和部署,除了这里还需要创建用户的token,比如我的jenkins用户是bml,那么在用户管理这里:

本地devops 配置 devops环境_devops_13


我的token生成了要马上复制自己保存,因为没有地方可以查询:

bml:1111a0a4944bf9add20578b2545080b56d

本地devops 配置 devops环境_本地devops 配置_14


点击管理

本地devops 配置 devops环境_git_15

因为我这里是本地的虚拟机,所以配置了了也没有用,回头自己搭建一个gitlab来测试。

流水线

本地devops 配置 devops环境_本地devops 配置_16


这里定义的流水线是从gitee上下载,如果这里选了,在脚本里面就不用写拉取代码的步骤了,比如:

本地devops 配置 devops环境_java_17


反正看自己选择,想在哪里写就在哪里写

本地devops 配置 devops环境_devops_18

最后是脚本路径,在项目里面这个脚本是放在根目录的,这样代码拉取下来过后,在根目录就可以找到这个文件了,就可以进行构建了,还记得上篇文章我们配置了pvc agent-workspace吗?这个就是用来存储这些构建项目的文件和jar包的,采用的nfs存储,因为我这里构建过了,所以可以看下:

本地devops 配置 devops环境_docker_19

在nfs里面:

本地devops 配置 devops环境_java_20


都是有的,所以挂载的目录就是nfs的这个目录,所以在文章开头说的一个完整的构建过程就说完了,下面就开始构建:

构建

我们是参数化构建,如果直接点击左边的build with Parameters,如下

本地devops 配置 devops环境_java_21


这里就可以设置你想设置的参数,我只是演示了两个参数,真正构建如下:

本地devops 配置 devops环境_本地devops 配置_22


本地devops 配置 devops环境_devops_23

这是我构建过的记录,在构建的最左下角有构建记录,可以点击进去看下:

本地devops 配置 devops环境_docker_24


还可以看下输出,这个构建是可以通过可视化的方式展示流水线的步骤和日志的,是非常好的一种方式

本地devops 配置 devops环境_devops_25


这上面的名称就是在上面我们每个讲解的阶段名称,鼠标移到上面还可以看日志,比如看下编译代码的日志:

本地devops 配置 devops环境_本地devops 配置_26


本地devops 配置 devops环境_docker_27


点击可进入详细的日志:

本地devops 配置 devops环境_git_28


构建成功以后,在容器里的命名空间testing如下:

本地devops 配置 devops环境_git_29


在浏览器输入我们应用的地址:

本地devops 配置 devops环境_git_30


镜像仓库:

本地devops 配置 devops环境_java_31


本地devops 配置 devops环境_本地devops 配置_32


可以看到有很多的版本;这就是一个算是比较复杂的Devops搭建过程