jenkins 动态 slave

上一篇文章中已经安装好 jenkins,此次咱们来实现动态 slave。node

当咱们使用动态 slave 时,每启动一个 job 都会建立一个 pod,其中这个 pod 的主容器天然就是 jenkins slave。有主容器天然就有其余容器,什么状况下会出现其余容器呢?git

好比我得启动一个 git 容器拉代码,拉下来代码以后须要使用 maven 进行构建,这时就须要启动 maven 容器了。等你构建完了以后还得打成镜像吧,还得上传吧,因此还得须要 docker 容器。web

因此这样一来一个 pod 中就有四个容器了,有意思的是,当你拉代码以后,以后的容器都会将代码目录挂载进去,而且默认就在这个目录下面工做。这样一来就十分方便了,多个容器能够如同一个容器般的操做。docker

可是你没法挂载目录进镜像中,也就是说你打好的 war 包带不出来,你只能打成镜像或者上传到 ftp 等。api

说了这么多,相信你对其已经有了大体的了解,那让咱们直接开整。tomcat

建立云

登陆 jenkins 以后,首先须要安装 Kubernetes 这个插件,安装完成以后,咱们点击 Manage Jenkins -> Configure System -> Cloud -> add -> Kubernetes。markdown

开始新建一个云:并发

  • Name:kubernetes,这个名字随意;
  • Kubernetes URLhttps://kubernetes.default
  • Disable https certificate check:勾选;
  • Credentials:Add -> Jenkins
    • Domain:Global credentials;
    • Kind:Kubernetes Service Account;
    • Scope:System;
  • Jenkins URL:若是你启动的容器可以解析你的 jenkins 域名,那么就写域名(能够是 http),不然就写 service。我这里使用 http://jenkins,感受直接写 service 更好;
  • Jenkins tunnel:和上面同样的道理,我这里使用 jenkins:50000
  • Connection Timeout:0;
  • Read Timeout:0;
  • Concurrency Limit:10,指定并发,应该是同时建立的 slave 的数量。

只须要配置这些,其余默认就好。dom

建立 job

接着建立第一个 job,一个名为 test 的 pipeline 任务。咱们直接来到 Pipeline 这里,你这能够直接将 pipeline 脚本贴在 Script 中,也可使用 Pipeline script from SCM,将 jenkinsfile 放在 git/svn 上。socket

我这里选择将 jenkinsfile 放在 git 上,这样的好处是每次你对 jenkinsfile 的修改都会记录下来。

  • Definition:Pipeline script from SCM;
  • SCM:Git;
  • Repositories:
    • Repository URL:我以为可使用 http 地址,这样能够输入用户名和密码;
    • Credentials:建立一个 Username with password 类型的 credentials,而后输入你 gitlab 的用户名和密码便可;
    • Branches to build:应该都是 master 吧;
  • Script Path:脚本的名称,你 jenkinsfile 名字是什么这里就写什么。

jenkinsfile

如下是 jenkinsfile 的内容:

def label = "test-${UUID.randomUUID().toString()}"

podTemplate(label: label, yaml: """ apiVersion: v1 kind: Pod metadata: labels: jenkins: slave spec: imagePullSecrets: - name: nexus-pull containers: - name: jnlp image: registry.ntpstat.com:2222/jenkins/jnlp-slave:3.10-1-alpine args: - \$(JENKINS_SECRET) - \$(JENKINS_NAME) """
) {
    node(label) {
        stage('test') {
            echo "test"
        }
    }
}
复制代码

说明:

  • label 就是 slave 的名字,使用一堆随机字符避免重复;
  • podTemplate:关键字,用来定义 pod 中全部须要的镜像;
  • yaml:经过 yaml 关键字能够直接使用 kubernetes 语法;
  • node:经过它来选择 pod,表示下面的 stage 都在 pod 中执行。

因为是从私有镜像仓库中拉镜像,因此这里我定义了一个 imagePullSecrets,相信看了我前面文章的同窗都懂。动态 slave 状况下,slave 节点必需要启动,哪怕你以为它啥也没干,因此这里定义了 slave 这一个容器。

必须传递给 slave 容器两个参数,由于 slave 容器在启动以后必须链接 master,而这两个参数是由 kubernetes 插件自动注入的。事实上,插件会传递多个参数,只不过其余的咱们用不上而已。

由于只是测试,因此这里就简单的输出 test,看看是否可以直接输出。

保存以后回到主界面以后,直接点击构建 test 任务,过会就可以看到在左侧出现一个新的 slave,一开始是离线状态。

等会它就消失了,由于构建结束。咱们能够直接点击进入任务中查看。

能够看出编译成功。

使用 maven

测试成功以后,咱们直接实战。我会首先使用 git 镜像拉取项目代码,而后使用 maven 构建。因为 maven 镜像配置文件是默认的,因此我会使用 configMap 建立一个我线上实现的配置文件,挂载到 Maven 镜像中覆盖其配置文件。

maven 编译过程当中会从 nexus 下载依赖包,虽然配置文件中指定了 nexus 的地址,可是若是每次构建下载的依赖包都放在容器中,那么这次构建完毕以后这些下载的依赖包就会被清掉,没法二次利用。因此咱们将 nfs 的一个目录直接映射到 maven 镜像的 /root/.m2 目录下。

因为此时用来进行构建实战的项目有些复杂,它会生成五个包,所以这里会有循环的操做。循环生成五个镜像,并发布到私有仓库。

在以后的发布中,我会启动 docker 镜像,可是会将本地的 docker 域套接字映射进去,这样 docker 镜像仓库的操做和宿主机就没什么区别了。

def label = "maven-${UUID.randomUUID().toString()}"
def warDir = "warDir"
def dirPrefix = "hehe_"
def targetDir = "/target"

podTemplate(label: label, yaml: """ apiVersion: v1 kind: Pod metadata: labels: jenkins: slave spec: imagePullSecrets: - name: nexus-pull containers: - name: jnlp image: registry.ntpstat.com:2222/jenkins/jnlp-slave:3.10-1-alpine args: - \$(JENKINS_SECRET) - \$(JENKINS_NAME) - name: git image: registry.ntpstat.com:2222/alpine/git command: - cat tty: true - name: maven image: registry.ntpstat.com:2222/maven:3-alpine command: - cat tty: true volumeMounts: - name: maven-xml mountPath: /usr/share/maven/conf/settings.xml readOnly: true subPath: settings.xml - name: maven-data mountPath: /root/.m2 - name: docker image: registry.ntpstat.com/library/docker:18.06 command: - cat tty: true volumeMounts: - name: docker-socket mountPath: /var/run/docker.sock readOnly: true volumes: - name: maven-xml configMap: name: maven-conf - name: maven-data nfs: server: registry.ntpstat.com path: /opt/kubernetes/maven-data - name: docker-socket hostPath: path: /var/run/docker.sock """
) {
  wars = ['fuckGod-sync', 'fuckGod-job', 'fuckGod-mq', 'fuckGod-main', 'fuckGod-web']
  node(label) {
    stage('拉代码') {
      checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '19f20e6e-910e-42cd-b395-e4f82c76fd89', url: 'http://git.ntpstat.com/hehe/hehe.git']]])
      container('git') {
        sh "cp Dockerfile /"
        version = sh(script: "git tag | tail -1", returnStdout: true).trim().split('-')[1]
        sh "git checkout version-${version}"
        sh "cp /Dockerfile ."
      }
    }
    stage('maven 编译') {
      container('maven') {
        sh 'mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package -Dmaven.test.failure.ignore=true'
        wars.each { item ->
          dir(dirPrefix + item + targetDir) {
            sh "mkdir ${warDir}"
            sh "unzip *.war -d ${warDir}"
          }
        }
      }
    }
    stage('打镜像') {
      container('docker') {
        withDockerRegistry(credentialsId: 'nexus-pull', url: 'https://registry.ntpstat.com') {
          wars.each { item ->
            dir(dirPrefix + item + targetDir) {
              imageName = "registry.ntpstat.com/tomcat/${item}:${version}"
              sh "cp ../../Dockerfile ."
              sh "docker build -t ${imageName} ."
              sh "docker push ${imageName}"
            }
          }
        }
      }
    }
  }
}
复制代码

从 gitlab/svn 上面拉代码的语法可使用 Pipeline Syntax,选择 checkout 自动生成。

为了方便使用,我将 dockerfile 放在了项目的根目录。

咱们这里的 git 在上线前会打个 tag,所以我在拉代码以后会切换到这个 tag 进行编译。

其余的也没啥好说的了,相信你看看就能懂。