基于Docker的Jenkins安裝部署與流水線腳本

一. Docker

1.1 docker安裝

安裝相關(guān)依賴
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

國內(nèi)源
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安裝docker
sudo yum -y install docker-ce

服務(wù)自啟動(dòng)
systemctl enable docker

設(shè)置阿里云鏡像
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://a05qb3lx.mirror.aliyuncs.com"]
}
EOF

啟動(dòng)服務(wù)
sudo systemctl daemon-reload
sudo systemctl restart docker

1.2 docker常用命令

查看所有docker容器
docker ps -a

啟動(dòng)新容器
docker run -d --name (指定容器名字) -p (端口):(端口) -v (數(shù)據(jù)路徑):(數(shù)據(jù)路徑) (鏡像名):(版本)

進(jìn)入容器內(nèi)部
docker exec -it (容器名字) /bin/bash

查看容器的日志
docker logs (容器名字)


二. Jenkins

2.1 jenkins安裝

拉取鏡像
docker pull jesusperales/jenkins-docker-run-inside

啟動(dòng)鏡像
docker run --name jenkins -d -p 8080:8080 -p 50000:50000 -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) --add-host updates.jenkins-ci.org:(nginx所在節(jié)點(diǎn)的ip地址) jesusperales/jenkins-docker-run-inside

訪問服務(wù)
http://ip:8080

生產(chǎn)項(xiàng)目中還需要將配置文件(-v /root/jenkins/conf/config.xml:/var/jenkins_home/config.xml),項(xiàng)目文件等掛載到宿主機(jī)上恶迈。

2.2 參數(shù)介紹

①-p 8080:8080 jenkins通訊端口署咽。

②-p 50000:50000 基于JNLP的Jenkins代理通過TCP端口50000與Jenkins主站進(jìn)行通信,即可以通過瀏覽器直接執(zhí)行java應(yīng)用程序遂赠。

③-v /var/run/docker.sock:/var/run/docker.sock 用于docker客戶端與守護(hù)進(jìn)程通訊

④-v $(which docker):$(which docker) docker指令腳本

⑤--add-host updates.jenkins-ci.org:192.168.32.128 添加本地DNS域名解析

2.3 配置

修改權(quán)限
因?yàn)槟J(rèn)是jenkins用戶登錄,需要添加docker權(quán)限
docker exec -it jenkins /bin/bash
sudo groupadd docker
sudo usermod -aG docker jenkins
sudo cat /etc/group
sudo chmod a+rw /var/run/docker.sock

配置maven倉庫為阿里云倉庫

將容器中的配置文件settings.xml復(fù)制到宿主機(jī)
docker cp jenkins:/var/jenkins_home/tools/hudson.tasks.Maven_MavenInstallation/mvn3.5.0/conf/settings.xml ~/

將<mirror></mirrors>標(biāo)簽內(nèi)容該為如下配置

<mirror>
     <id>alimaven</id>
     <mirrorOf>central</mirrorOf>
     <name>aliyun maven</name>
     <url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
    </mirror>
</mirrors>

將在宿主機(jī)修改完成后的配置文件settings.xml覆蓋回容器中
docker cp jenkins:/var/jenkins_home/tools/hudson.tasks.Maven_MavenInstallation/mvn3.5.0/conf ~/settings.xml

2.4 存在的問題

jenkins需要安裝大量的第三方插件晌杰,但是所有的數(shù)據(jù)源都是指向國外倉庫跷睦,導(dǎo)致國內(nèi)下載插件緩慢甚至大量失敗,因此需要配置國內(nèi)的鏡像源肋演。


1592498234178.png

方式一:修改鏡像源的url地址(因?yàn)閖enkins會通過數(shù)字簽名驗(yàn)證鏡像源是否有效抑诸,所以這個(gè)方法不可靠):

①$ cd {你的Jenkins工作目錄}/updates #進(jìn)入更新配置位置
②$ vim default.json
在vim中替換官方鏡像源為清華鏡像源
1)http://updates.jenkins-ci.org/download/ 替換為 https://mirrors.tuna.tsinghua.edu.cn/jenkins
2)/http://www.google.com/ 替換為 https://www.baidu.com
或通過sed命令替換
$ sed -i 's/http://updates.jenkins-ci.org/download/https://mirrors.tuna.tsinghua.edu.cn/jenkins/g' default.json && sed -i 's/http://www.google.com/https://www.baidu.com/g' default.json

方式二:將請求引向nginx烂琴,通過nginx進(jìn)行代理,重定向到清華鏡像源蜕乡,具體見第三章奸绷。


三. Nginx

3.1 nginx安裝

拉取鏡像
docker pull jesusperales/jenkins-docker-run-inside

啟動(dòng)鏡像
docker run -d -p 80:80 -p 81:81 -p 82:82 -v /root/nginx/html:/usr/share/nginx/html -v /root/nginx/nginx.conf:/etc/nginx/nginx.conf -v /root/nginx/conf.d:/etc/nginx/conf.d -v /root/nginx/log:/var/log/nginx --name nginx nginx

3.2 配置文件

將請求引向nginx,重定向到清華鏡像源
https://blog.csdn.net/scc95599/article/details/104656973
①添加本地DNS域名解析updates.jenkins-ci.org
echo '127.0.0.1 updates.jenkins-ci.org' >> /etc/hosts

②創(chuàng)建文件
vim ~/root/nginx/conf.d/jenkins_redirect.conf
添加如下配置內(nèi)容

server {
    listen       80;
    server_name updates.jenkins-ci.org;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location /download/plugins {
  proxy_next_upstream http_502 http_504 error timeout invalid_header;
  proxy_set_header Host mirrors.tuna.tsinghua.edu.cn;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Accept-Encoding "";
  proxy_set_header Accept-Language "zh-CN";
  rewrite /download/plugins/(.*) /jenkins/plugins/$1 break;
  proxy_pass https://mirrors.tuna.tsinghua.edu.cn;
    }

    location /pub/jenkins/plugins {
  proxy_next_upstream http_502 http_504 error timeout invalid_header;
        proxy_set_header Host mirrors.tuna.tsinghua.edu.cn;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Accept-Encoding "";
        proxy_set_header Accept-Language "zh-CN";
        rewrite /pub/jenkins/plugins/(.*) /jenkins/plugins/$1 break;
        proxy_pass https://mirrors.tuna.tsinghua.edu.cn;
    }

#    location / {
#        root   /usr/share/nginx/html;
#        index  index.html index.htm;
#    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    access_log  /var/log/nginx/mirrors.access.log;
    error_log   /var/log/nginx/mirrors.error.log;

}


四. Pipeline腳本

4.1 自動(dòng)化部署腳本

方式一:通過賬號密碼遠(yuǎn)程登錄

pipeline {
  agent any
  //系統(tǒng)參數(shù)配置
  options{
    buildDiscarder(logRotator(numToKeepStr:'2'))  //持久化工件和控制臺輸出层玲,規(guī)定pipeline運(yùn)行的最大個(gè)數(shù)
    disableConcurrentBuilds() //設(shè)置pipeline不能并行運(yùn)行号醉,放置同時(shí)訪問共享資源。
    skipDefaultCheckout() //跳過默認(rèn)設(shè)置的代碼check out
    skipStagesAfterUnstable() //一旦構(gòu)建狀態(tài)變成unstable不穩(wěn)定狀態(tài)称簿,跳過該階段
    timeout(time:1,unit:'HOURS')  //設(shè)置該pipeline運(yùn)行的超時(shí)時(shí)間扣癣,超時(shí)的pipeline會自動(dòng)被終止
    timestamps()  //為控制臺輸出增加時(shí)間戳
  }
  //變量定義
  environment {   
    CREDENTIALSID = 'smartcook'
    GIT_URL = 'http://gitlab.iotmars.com/backend/smartcook/smartcook.git'
    BRANCH = 'dev'
    ALIYUN_NAMESPACE = 'wecook'
    ALIYUN_REPOSITORY = 'menu-center-dev'
    IMAGE_VERSION = '0.0.1-SNAPSHOT'
    DOCKER_CONTAINER_NAME = 'smartcook'
    REMOTE_SERVER_IP = '192.168.32.128'
    REMOTE_SERVER_NAME = 'localhost.localdomain'
    REMOTE_SERVER_CREDENTIALSID = 'server_128'
    REMOTE_REPOSITORY_CREDENTIALSID = 'hxr_aliyun'
    SERVER_PORT = '8002'
    EMAIL = '792965772@qq.com'
  }
  //定義工具
  triggers {
    GenericTrigger (
      genericVariables: [
        [key: 'ref',value: '$.ref']
      ],
      causeString: 'Triggered on $ref',
      token: 'Smartcook_Menu-Center',
            
      printContributedVariables: true,
      printPostContent: true,
            
      silentResponse: false,
            
      regexpFilterText: '$ref',
      regexpFilterExpression: "refs/heads/dev"
    )
  }

  stages {
    //1.拉取源碼
    stage('Git Checkout'){
      steps {
        retry(3){
          git (
            branch:"${BRANCH}" ,
            credentialsId:"${CREDENTIALSID}" ,
            url: "${GIT_URL}" ,
            changelog: true 
          )
        }
      }
    }
    //2.編譯成jar包
    stage('Maven Build') {
      steps { 
        retry(3){
          sh "mvn -Dmaven.test.failure.ignore=true clean package"
        }
      }
    }
    //3.構(gòu)建鏡像并上傳到阿里云鏡像倉庫
    stage('Build and Push Image'){
      steps{
        withCredentials([usernamePassword(credentialsId: 'hxr_aliyun', passwordVariable: 'password', usernameVariable: 'username')]) {
          script{
            out=sh(script:"ls ./Dockerfile",returnStatus:true)
            println out
            if( out == 2 ){
              println "創(chuàng)建默認(rèn)Dockerfile"
              sh '''
                cat  <<  EOF  >  Dockerfile
                FROM openjdk:8-jdk-alpine
                VOLUME /tmp
                ADD ./target/*.jar app.jar
                EXPOSE ${SERVER_PORT}
                ENTRYPOINT ["java","-Xmx200m","-Xms200m","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EOF
              '''
            }
          retry(3){
            sh """
              docker build -t ${DOCKER_CONTAINER_NAME} .
              sudo docker login --username=${username} --password=${password} registry.cn-hangzhou.aliyuncs.com
              sudo docker tag ${DOCKER_CONTAINER_NAME} registry.cn-hangzhou.aliyuncs.com/${ALIYUN_NAMESPACE}/${ALIYUN_REPOSITORY}:${IMAGE_VERSION}
              sudo docker push registry.cn-hangzhou.aliyuncs.com/${ALIYUN_NAMESPACE}/${ALIYUN_REPOSITORY}:${IMAGE_VERSION}
            """
            }
          }
        }
      }
    }
    //4.拉取鏡像并啟動(dòng)
    stage('Pull Image and Run'){
      steps{
        retry(3){
          script{
            withCredentials([usernamePassword(credentialsId: REMOTE_SERVER_CREDENTIALSID, passwordVariable: 'password', usernameVariable: 'username')]) {
              def remote = [:]
              remote.name = REMOTE_SERVER_NAME
              remote.host = REMOTE_SERVER_IP
              remote.user = username
              remote.password = password
              remote.allowAnyHosts = true
              withCredentials([usernamePassword(credentialsId: REMOTE_REPOSITORY_CREDENTIALSID, passwordVariable: 'password', usernameVariable: 'username')]) {
                //從阿里云鏡像倉庫中拉取鏡像并啟動(dòng)
                sshCommand remote: remote, command: "sudo docker login --username=\"${username}\" --password=\"${password}\" registry.cn-hangzhou.aliyuncs.com"
                sshCommand remote: remote, command: "sudo docker pull registry.cn-hangzhou.aliyuncs.com/\"${ALIYUN_NAMESPACE}\"/\"${ALIYUN_REPOSITORY}\":\"${IMAGE_VERSION}\""
                sshCommand remote: remote, command: "docker stop \"${DOCKER_CONTAINER_NAME}\" || true"
                sshCommand remote: remote, command: "docker rm  \"${DOCKER_CONTAINER_NAME}\" || true"
                sshCommand remote: remote, command: "docker run -it -d -p \"${SERVER_PORT}\":\"${SERVER_PORT}\" --name \"${DOCKER_CONTAINER_NAME}\" registry.cn-hangzhou.aliyuncs.com/\"${ALIYUN_NAMESPACE}\"/\"${ALIYUN_REPOSITORY}\":\"${IMAGE_VERSION}\""
              }
            }
          }
        }
      }
    }
  }
  post {
    always {
      echo 'This will always run'
      script{
        currentBuild.description = "\n always"
      }
      deleteDir() /* clean up our workspace */
      //archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
      //junit 'build/reports/**/*.xml'
      //TODO 添加郵箱服務(wù)
    }
    success {
      println("success!!!!!!!")
      script{
        currentBuild.description = "\n success"
      }
      //mail  to: "${EMAIL}", 
      //      subject: "Success Pipeline: ${currentBuild.fullDisplayName}",
      //      body: "Success with ${env.BUILD_URL}" /*該構(gòu)建的url地址*/
    }
    failure {
      echo 'This will run only if failed'
      script{
        currentBuild.description = "\n failure"
      }
      //mail  to: "${EMAIL}", 
      //      subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
      //      body: "Something is wrong with ${env.BUILD_URL}" /*該構(gòu)建的url地址*/
    }
  }
}


方式二:通過設(shè)置私鑰憑證的方式遠(yuǎn)程登錄

如果不會寫流水線語法,有兩種解決方式:

pipeline {
  agent any
  options{
    buildDiscarder(logRotator(numToKeepStr:'2'))  //持久化工件和控制臺輸出授药,規(guī)定pipeline運(yùn)行的最大個(gè)數(shù)
    disableConcurrentBuilds() //設(shè)置pipeline不能并行運(yùn)行士嚎,放置同時(shí)訪問共享資源。
    skipDefaultCheckout() //跳過默認(rèn)設(shè)置的代碼check out
    skipStagesAfterUnstable() //一旦構(gòu)建狀態(tài)變成unstable不穩(wěn)定狀態(tài)悔叽,跳過該階段
    timeout(time:1,unit:'HOURS')  //設(shè)置該pipeline運(yùn)行的超時(shí)時(shí)間莱衩,超時(shí)的pipeline會自動(dòng)被終止
    timestamps()  //為控制臺輸出增加時(shí)間戳
  }
  environment {
    CREDENTIALSID = 'CJsGitlab'
    GIT_URL = 'http://gitlab.iotmars.com/backend/duerosbots.git'
    BRANCH = 'master'
    ALIYUN_NAMESPACE = 'wecook'
    ALIYUN_REPOSITORY = 'menu-center-dev'
    IMAGE_VERSION = '0.0.1-SNAPSHOT'
    DOCKER_CONTAINER_NAME = 'duerosbots'
    REMOTE_SERVER_IP = '121.41.68.248'
    REMOTE_SERVER_NAME = 'iotmars.ecs.area.h03'
    REMOTE_SERVER_CREDENTIALSID = 'server_aliyun_248'
    REMOTE_REPOSITORY_CREDENTIALSID = 'hxr_aliyun'
    SERVER_PORT = '8090'
    EMAIL = '792965772@qq.com'
  }
  //定義工具
  tools {
    maven "mvn3.5.0"
  }
  //定義遠(yuǎn)程觸發(fā)器
  triggers {
    GenericTrigger (
      genericVariables: [
        [key: 'ref',value: '$.ref']
      ],
      causeString: 'Triggered on $ref',
      token: 'Smartcook_Menu-Center',
            
      printContributedVariables: true,
      printPostContent: true,
            
      silentResponse: false,
            
      regexpFilterText: '$ref',
      regexpFilterExpression: "refs/heads/dev"
    )
  }

  stages {
    stage('Git Checkout'){
      steps {
        retry(3){
        //1.拉取源碼
          git (
            branch:"${BRANCH}" ,
            credentialsId:"${CREDENTIALSID}" ,
            url: "${GIT_URL}" ,
            changelog: true 
          )
        }
      }
    }
    stage('Maven Build') {
      steps { 
        //2.編譯成jar包
        retry(3){
          sh "mvn -Dmaven.test.failure.ignore=true clean package"
        }
      }
    }
    stage('Build and Push Image'){
      steps{
        //3.構(gòu)建鏡像
        withCredentials([usernamePassword(credentialsId: REMOTE_REPOSITORY_CREDENTIALSID, passwordVariable: 'password', usernameVariable: 'username')]) {
          script{
            //判斷路徑下是否有dockerfile文件,沒有則創(chuàng)建默認(rèn)的dockerfile
            out=sh(script:"ls ./Dockerfile",returnStatus:true)
            println out
            if( out == 2 ){
              println "創(chuàng)建默認(rèn)Dockerfile"
              sh '''
                cat  <<  EOF  >  Dockerfile
                FROM openjdk:8-jdk-alpine
                VOLUME /tmp
                ADD ./target/*.jar app.jar
                EXPOSE ${SERVER_PORT}
                ENTRYPOINT ["java","-Xmx200m","-Xms200m","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EOF
              '''
            }
          retry(3){
            sh """
              docker build -t ${DOCKER_CONTAINER_NAME} .
              sudo docker login --username=${username} --password=${password} registry.cn-hangzhou.aliyuncs.com
              sudo docker tag ${DOCKER_CONTAINER_NAME} registry.cn-hangzhou.aliyuncs.com/${ALIYUN_NAMESPACE}/${ALIYUN_REPOSITORY}:${IMAGE_VERSION}
              sudo docker push registry.cn-hangzhou.aliyuncs.com/${ALIYUN_NAMESPACE}/${ALIYUN_REPOSITORY}:${IMAGE_VERSION}
            """
            }
          }
        }
      }
    }
    
    stage('Pull Image and Run'){
      steps{
        retry(3){
          script{
            //通過私鑰登錄到遠(yuǎn)程服務(wù)器
            withCredentials([sshUserPrivateKey(credentialsId: REMOTE_SERVER_CREDENTIALSID, keyFileVariable: 'keyFile', passphraseVariable: 'passphrase', usernameVariable: 'username')]) {
              def remote = [:]
              remote.name = REMOTE_SERVER_NAME
              remote.host = REMOTE_SERVER_IP
              remote.user = username
              remote.identityFile = keyFile
              remote.port = 22
              remote.allowAnyHosts = true
              withCredentials([usernamePassword(credentialsId: REMOTE_REPOSITORY_CREDENTIALSID, passwordVariable: 'password', usernameVariable: 'username')]) {
                //4.拉取鏡像并啟動(dòng)
                sshCommand remote: remote, command: "sudo docker login --username=\"${username}\" --password=\"${password}\" registry.cn-hangzhou.aliyuncs.com"
                sshCommand remote: remote, command: "sudo docker pull registry.cn-hangzhou.aliyuncs.com/\"${ALIYUN_NAMESPACE}\"/\"${ALIYUN_REPOSITORY}\":\"${IMAGE_VERSION}\""
                sshCommand remote: remote, command: "docker stop \"${DOCKER_CONTAINER_NAME}\" || true"
                sshCommand remote: remote, command: "docker rm  \"${DOCKER_CONTAINER_NAME}\" || true"
                sshCommand remote: remote, command: "docker run -it -d -p \"${SERVER_PORT}\":\"${SERVER_PORT}\" --name \"${DOCKER_CONTAINER_NAME}\" registry.cn-hangzhou.aliyuncs.com/\"${ALIYUN_NAMESPACE}\"/\"${ALIYUN_REPOSITORY}\":\"${IMAGE_VERSION}\""
              }
            }
          }
        }
      }
    }
  }
  post {
    always {
      echo 'This will always run'
      script{
        currentBuild.description = "\n always"
      }
      deleteDir() /* clean up our workspace */
      //archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
      //junit 'build/reports/**/*.xml'
      //TODO 添加郵箱服務(wù)
    }
    success {
      println("success!!!!!!!")
      script{
        currentBuild.description = "\n success"
      }
      //mail  to: "${EMAIL}", 
      //      subject: "Success Pipeline: ${currentBuild.fullDisplayName}",
      //      body: "Success with ${env.BUILD_URL}" /*該構(gòu)建的url地址*/
    }
    failure {
      echo 'This will run only if failed'
      script{
        currentBuild.description = "\n failure"
      }
      //mail  to: "${EMAIL}", 
      //      subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
      //      body: "Something is wrong with ${env.BUILD_URL}" /*該構(gòu)建的url地址*/
    }
  }
}   


方式三:通過私鑰文件遠(yuǎn)程登錄(將pem文件放到j(luò)enkins容器中)

pipeline {
  agent any
  options{
    buildDiscarder(logRotator(numToKeepStr:'2'))  //持久化工件和控制臺輸出娇澎,規(guī)定pipeline運(yùn)行的最大個(gè)數(shù)
    disableConcurrentBuilds() //設(shè)置pipeline不能并行運(yùn)行笨蚁,放置同時(shí)訪問共享資源。
    skipDefaultCheckout() //跳過默認(rèn)設(shè)置的代碼check out
    skipStagesAfterUnstable() //一旦構(gòu)建狀態(tài)變成unstable不穩(wěn)定狀態(tài)趟庄,跳過該階段
    timeout(time:1,unit:'HOURS')  //設(shè)置該pipeline運(yùn)行的超時(shí)時(shí)間括细,超時(shí)的pipeline會自動(dòng)被終止
    timestamps()  //為控制臺輸出增加時(shí)間戳
  }
  environment {
    CREDENTIALSID = 'CJsGitlab'
    GIT_URL = 'http://gitlab.iotmars.com/backend/duerosbots.git'
    BRANCH = 'master'
    ALIYUN_NAMESPACE = 'wecook'
    ALIYUN_REPOSITORY = 'menu-center-dev'
    IMAGE_VERSION = '0.0.1-SNAPSHOT'
    DOCKER_CONTAINER_NAME = 'duerosbots'
    REMOTE_SERVER_IP = '121.41.68.248'
    REMOTE_SERVER_NAME = 'iotmars.ecs.area.h03'
    REMOTE_SERVER_CREDENTIALSID = 'server_128'
    REMOTE_SERVER_USERNAME = 'root'
    REMOTE_SERVER_IDENTITYFILE = '/home/jenkins/.ssh/M20200509_162337marssenger.pem'
    REMOTE_REPOSITORY_CREDENTIALSID = 'hxr_aliyun'
    SERVER_PORT = '8090'
    EMAIL = '792965772@qq.com'
  }
  //定義工具
  tools {
    maven "mvn3.5.0"
  }
  //定義遠(yuǎn)程觸發(fā)器
  triggers {
    GenericTrigger (
      genericVariables: [
        [key: 'ref',value: '$.ref']
      ],
      causeString: 'Triggered on $ref',
      token: 'Smartcook_Menu-Center',
            
      printContributedVariables: true,
      printPostContent: true,
            
      silentResponse: false,
            
      regexpFilterText: '$ref',
      regexpFilterExpression: "refs/heads/dev"
    )
  }

  stages {
    stage('Git Checkout'){
      steps {
        retry(3){
        //1.拉取源碼
          git (
            branch:"${BRANCH}" ,
            credentialsId:"${CREDENTIALSID}" ,
            url: "${GIT_URL}" ,
            changelog: true 
          )
        }
      }
    }
    stage('Maven Build') {
      steps { 
        //2.編譯成jar包
        retry(3){
          sh "mvn -Dmaven.test.failure.ignore=true clean package"
        }
      }
    }
    stage('Build and Push Image'){
      steps{
        //3.構(gòu)建鏡像
        withCredentials([usernamePassword(credentialsId: 'hxr_aliyun', passwordVariable: 'password', usernameVariable: 'username')]) {
          script{
            out=sh(script:"ls ./Dockerfile",returnStatus:true)
            println out
            if( out == 2 ){
              println "創(chuàng)建默認(rèn)Dockerfile"
              sh '''
                cat  <<  EOF  >  Dockerfile
                FROM openjdk:8-jdk-alpine
                VOLUME /tmp
                ADD ./target/*.jar app.jar
                EXPOSE ${SERVER_PORT}
                ENTRYPOINT ["java","-Xmx200m","-Xms200m","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EOF
              '''
            }
          retry(3){
            sh """
              docker build -t ${DOCKER_CONTAINER_NAME} .
              sudo docker login --username=${username} --password=${password} registry.cn-hangzhou.aliyuncs.com
              sudo docker tag ${DOCKER_CONTAINER_NAME} registry.cn-hangzhou.aliyuncs.com/${ALIYUN_NAMESPACE}/${ALIYUN_REPOSITORY}:${IMAGE_VERSION}
              sudo docker push registry.cn-hangzhou.aliyuncs.com/${ALIYUN_NAMESPACE}/${ALIYUN_REPOSITORY}:${IMAGE_VERSION}
            """
            }
          }
        }
      }
    }
    
    stage('Pull Image and Run'){
      steps{
        retry(3){
          script{
            def remote = [:]
            remote.name = REMOTE_SERVER_NAME
            remote.host = REMOTE_SERVER_IP
            remote.user = REMOTE_SERVER_USERNAME
            remote.port = 22
            remote.identityFile = '/home/jenkins/.ssh/M20200509_162337marssenger.pem'
            remote.allowAnyHosts = true
            withCredentials([usernamePassword(credentialsId: REMOTE_REPOSITORY_CREDENTIALSID, passwordVariable: 'password', usernameVariable: 'username')]) {
              //4.拉取鏡像并啟動(dòng)
              sshCommand remote: remote, command: "sudo docker login --username=\"${username}\" --password=\"${password}\" registry.cn-hangzhou.aliyuncs.com"
              sshCommand remote: remote, command: "sudo docker pull registry.cn-hangzhou.aliyuncs.com/\"${ALIYUN_NAMESPACE}\"/\"${ALIYUN_REPOSITORY}\":\"${IMAGE_VERSION}\""
              sshCommand remote: remote, command: "docker stop \"${DOCKER_CONTAINER_NAME}\" || true"
              sshCommand remote: remote, command: "docker rm  \"${DOCKER_CONTAINER_NAME}\" || true"
              sshCommand remote: remote, command: "docker run -it -d -p \"${SERVER_PORT}\":\"${SERVER_PORT}\" --name \"${DOCKER_CONTAINER_NAME}\" registry.cn-hangzhou.aliyuncs.com/\"${ALIYUN_NAMESPACE}\"/\"${ALIYUN_REPOSITORY}\":\"${IMAGE_VERSION}\""
            }
          }
        }
      }
    }
  }
  post {
    always {
      echo 'This will always run'
      script{
        currentBuild.description = "\n always"
      }
      deleteDir() /* clean up our workspace */
      //archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
      //junit 'build/reports/**/*.xml'
      //TODO 添加郵箱服務(wù)
    }
    success {
      println("success!!!!!!!")
      script{
        currentBuild.description = "\n success"
      }
      //mail  to: "${EMAIL}", 
      //      subject: "Success Pipeline: ${currentBuild.fullDisplayName}",
      //      body: "Success with ${env.BUILD_URL}" /*該構(gòu)建的url地址*/
    }
    failure {
      echo 'This will run only if failed'
      script{
        currentBuild.description = "\n failure"
      }
      //mail  to: "${EMAIL}", 
      //      subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
      //      body: "Something is wrong with ${env.BUILD_URL}" /*該構(gòu)建的url地址*/
    }
  }
}


4.2 pipeline腳本設(shè)置

4.2.1 腳本路徑為配置文件在倉庫中的路徑

image.png

4.2.2 配置鉤子程序

需要安裝插件Generic Webhook Trigger

在gitlab的項(xiàng)目settings=>Integrations中設(shè)置jenkins項(xiàng)目的url:
http://192.168.32.128:8080/generic-webhook-trigger/invoke?token=Smartcook_Menu-Center

token需要和腳本中的TRIGGER_TOKEN對應(yīng)

 triggers {
    GenericTrigger (
      genericVariables: [
        [key: 'ref',value: '$.ref']
      ],
      causeString: 'Triggered on $ref',
      token: 'Smartcook_Menu-Center',
            
      printContributedVariables: true,
      printPostContent: true,
            
      silentResponse: false,
            
      regexpFilterText: '$ref',
      regexpFilterExpression: "refs/heads/dev"
    )
  }

可以指定前置任務(wù)完成后觸發(fā)
triggers { upstream(upstreamProjects: 'Smartcook_Register-Center', threshold: hudson.model.Result.SUCCESS) }


4.3 郵箱服務(wù)設(shè)置

4.3.1 系統(tǒng)配置

①下載插件Email Extension Plugin
②在系統(tǒng)配置中設(shè)置系統(tǒng)管理員郵箱

image.png

③在系統(tǒng)配置Extended E-mail Notification中進(jìn)行設(shè)置
User Name必須與系統(tǒng)管理員郵箱一致,Password是獲取的郵箱第三方登錄授權(quán)碼戚啥。
image.png

4.3.2 郵件腳本(一般添加在pipeline腳本的always{}中)

emailext body: '''<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次構(gòu)建日志</title>
</head>

<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
    offset="0">
    <table width="95%" cellpadding="0" cellspacing="0"
        style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
        <tr>
            <td>(本郵件是程序自動(dòng)下發(fā)的奋单,請勿回復(fù)!)</td>
        </tr>
        <tr>
            <td><h2>
                    <font color="#0000FF">構(gòu)建結(jié)果 - ${BUILD_STATUS}</font>
                </h2></td>
        </tr>
        <tr>
            <td><br />
            <b><font color="#0B610B">構(gòu)建信息</font></b>
            <hr size="2" width="100%" align="center" /></td>
        </tr>
        <tr>
            <td>
                <ul>
                    <li>項(xiàng)目名稱&nbsp;:&nbsp;${PROJECT_NAME}</li>
                    <li>構(gòu)建編號&nbsp;:&nbsp;第${BUILD_NUMBER}次構(gòu)建</li>
                    <li>SVN&nbsp;版本:&nbsp;${SVN_REVISION}</li>
                    <li>觸發(fā)原因:&nbsp;${CAUSE}</li>
                    <li>構(gòu)建日志:&nbsp;<a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
                    <li>構(gòu)建&nbsp;&nbsp;Url&nbsp;:&nbsp;<a href="${BUILD_URL}">${BUILD_URL}</a></li>
                    <li>工作目錄&nbsp;:&nbsp;<a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
                    <li>項(xiàng)目&nbsp;&nbsp;Url&nbsp;:&nbsp;<a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
                </ul>
            </td>
        </tr>
        <tr>
            <td><b><font color="#0B610B">Changes Since Last
                        Successful Build:</font></b>
            <hr size="2" width="100%" align="center" /></td>
        </tr>
        <tr>
            <td>
                <ul>
                    <li>歷史變更記錄 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li>
                </ul> ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n:<br />%c<br />",showPaths=true,changesFormat="<pre>[%a]<br />%m</pre>",pathFormat="&nbsp;&nbsp;&nbsp;&nbsp;%p"}
            </td>
        </tr>
        <tr>
            <td><b>Failed Test Results</b>
            <hr size="2" width="100%" align="center" /></td>
        </tr>
        <tr>
            <td><pre
                    style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">$FAILED_TESTS</pre>
                <br /></td>
        </tr>
        <tr>
            <td><b><font color="#0B610B">構(gòu)建日志 (最后 100行):</font></b>
            <hr size="2" width="100%" align="center" /></td>
        </tr>
        <!-- <tr>
            <td>Test Logs (if test has ran): <a
                href="${PROJECT_URL}ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip">${PROJECT_URL}/ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip</a>
                <br />
            <br />
            </td>
        </tr> -->
        <tr>
            <td><textarea cols="80" rows="30" readonly="readonly"
                    style="font-family: Courier New">${BUILD_LOG, maxLines=100}</textarea>
            </td>
        </tr>
    </table>
</body>
</html>''', subject: '${BUILD_STATUS} - ${PROJECT_NAME} - Build # ${BUILD_NUMBER} !', to: "${EMAIL}"

全局郵件變量解釋
${FILE,path="PATH"} 包括指定文件(路徑)的含量相對于工作空間根目錄
path文件路徑猫十,好比你用jenkins+git览濒,他執(zhí)行Pipeline的時(shí)候,找文件的路徑就是從拉下來的代碼開始
${BUILD_NUMBER} 當(dāng)前構(gòu)建的編號
${JOB_DESCRIPTION} 項(xiàng)目描述
${SVN_REVISION} svn版本號拖云。還支持Subversion插件出口的SVN_REVISION_n版本
${CAUSE} 顯示誰贷笛、通過什么渠道觸發(fā)這次構(gòu)建
${CHANGES } -顯示上一次構(gòu)建之后的變化
${BUILD_ID}顯示當(dāng)前構(gòu)建生成的ID
${PROJECT_NAME} 顯示項(xiàng)目的全名
${PROJECT_DISPLAY_NAME} 顯示項(xiàng)目的顯示名稱
${JENKINS_URL} 顯示Jenkins服務(wù)器的url地址
${BUILD_LOG_MULTILINE_REGEX}按正則表達(dá)式匹配并顯示構(gòu)建日志。
${BUILD_LOG} 最終構(gòu)建日志宙项。
${PROJECT_URL} 顯示項(xiàng)目的URL地址昨忆。
${BUILD_STATUS} -顯示當(dāng)前構(gòu)建的狀態(tài)(失敗、成功等等)
${BUILD_URL} -顯示當(dāng)前構(gòu)建的URL地址杉允。
${CHANGES_SINCE_LAST_SUCCESS} -顯示上一次成功構(gòu)建之后的變化邑贴。
${CHANGES_SINCE_LAST_UNSTABLE} -顯示顯示上一次不穩(wěn)固或者成功的構(gòu)建之后的變化。
${FAILED_TESTS} -如果有失敗的測試叔磷,顯示這些失敗的單元測試信息拢驾。
${JENKINS_URL} -顯示Jenkins服務(wù)器的地址。(你能在“系統(tǒng)配置”頁改變它)改基。
${PROJECT_URL} -顯示項(xiàng)目的URL繁疤。
${SVN_REVISION} -顯示SVN的版本號。
${TEST_COUNTS} -顯示測試的數(shù)量秕狰。


五稠腊、使用LDAP進(jìn)行登陸認(rèn)證

進(jìn)入Manage Jenkins -> Configure Global Security ,在訪問控制中選擇LDAP鸣哀,配置如下架忌。用戶的權(quán)限控制可以通過Manage and Assign Roles來實(shí)現(xiàn)。

image.png

需要注意的是我衬,一旦使用LDAP叹放,那么原來的管理員賬戶會失效,需要再指定一個(gè)LDAP中的用戶給其管理員權(quán)限挠羔,否則LDAP中的全部用戶都沒有任何權(quán)限井仰。如果不幸的事發(fā)生,那么有兩個(gè)方法來獲取管理員權(quán)限:
①LDAP中創(chuàng)建一個(gè)與原管理員賬號同名的用戶破加,那么該用戶就是超級管理員俱恶。
②修改jenkins的配置文件了,該配置文件在docker中的位置是 /var/jenkins_home/config_cp.xml范舀,修改內(nèi)容如下:

<securityRealm class="hudson.security.HudsonPrivateSecurityRealm">    
   <disableSignup>false</disableSignup>
   <enableCaptcha>false</enableCaptcha>
</securityRealm>

修改完成后使用原來的管理員賬戶進(jìn)行登陸合是,重新保存一下LDAP配置,并給一個(gè)用戶超級管理員角色即可尿背。

以上都完成后端仰,可以使用Test LDAP Settings按鈕進(jìn)行用戶登陸測試,user為uid田藐,password為對應(yīng)的用戶密碼荔烧。

image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者汽久。
  • 序言:七十年代末鹤竭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子景醇,更是在濱河造成了極大的恐慌臀稚,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件三痰,死亡現(xiàn)場離奇詭異吧寺,居然都是意外死亡窜管,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門稚机,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幕帆,“玉大人,你說我怎么就攤上這事赖条∈” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵纬乍,是天一觀的道長碱茁。 經(jīng)常有香客問我,道長仿贬,這世上最難降的妖魔是什么纽竣? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮诅蝶,結(jié)果婚禮上退个,老公的妹妹穿的比我還像新娘。我一直安慰自己调炬,他們只是感情好语盈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缰泡,像睡著了一般刀荒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棘钞,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天缠借,我揣著相機(jī)與錄音,去河邊找鬼宜猜。 笑死泼返,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姨拥。 我是一名探鬼主播绅喉,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼叫乌!你這毒婦竟也來了柴罐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤憨奸,失蹤者是張志新(化名)和其女友劉穎革屠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡似芝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年那婉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片国觉。...
    茶點(diǎn)故事閱讀 39,926評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吧恃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出麻诀,到底是詐尸還是另有隱情,我是刑警寧澤傲醉,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布蝇闭,位于F島的核電站,受9級特大地震影響硬毕,放射性物質(zhì)發(fā)生泄漏呻引。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一吐咳、第九天 我趴在偏房一處隱蔽的房頂上張望逻悠。 院中可真熱鬧,春花似錦韭脊、人聲如沸童谒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饥伊。三九已至,卻和暖如春蔫饰,著一層夾襖步出監(jiān)牢的瞬間琅豆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工篓吁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茫因,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓杖剪,卻偏偏與公主長得像冻押,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子摘盆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容