基于Docker+Jenkins+Git的CI/CD实战

与上一篇随笔:基于 Jenkins+Docker+Git 的CI流程初探 有所不同,该内容更偏向于实际业务的基础需求。

有几点需要注意:

  • 该实战中没有涉及到镜像仓库,所以略去了镜像推送阶段,可以参考基于 Jenkins+Docker+Git 的CI流程初探。
  • 与上一篇对比,该实战是基于外网服务器进行的,所以加入了Jenkins自动触发拉取代码以及发送构建报告功能。
  • 实战中的Jenkins也是基于docker运行,对于Jenkins数据持久化是通过VOLUME实现的。
  • 不再进行自建git代码仓库,选择使用Gitee管理代码。
  • 实验环境均已提前准备完毕。

1、Jenkins启动

docker run \
 -u root \
 -d \
 -p 8080:8080 \
 -p 50000:50000 \
 -v jenkins-data:/var/jenkins_home \
 -v /etc/localtime:/etc/localtime:ro \
 -v /var/run/docker.sock:/var/run/docker.sock \
 --restart=always \
 jenkinsci/blueocean

启动后设置用户与密码

2、新建item

名称:java-devops-demo

创建流水线

docker部署jenkins怎么查看登录密码 jenkins docker 权限_docker

docker部署jenkins怎么查看登录密码 jenkins docker 权限_Jenkins_02

选择保存

Jenkins流水线工作流程:

  先定义一个流水线项目,指定项目的git位置

  流水线启动

  a.先去git位置自动拉取代码

  b.解析拉取代码里面的Jenkinsfile文件

  c.按照Jenkinsfile指定的流水线开始加工项目

Jenkins重要的点

  1) jenkins的家目录 /var/jenkins_home 已经被我们docker外部挂载了/var/lib/docker/volumes/jenkins-data/_data

  2)WORKSPACE(工作空间)=/var/jenkins_home/workspace/java-devops-demo每一个流水线项目,占用一个文件夹位置

  3)BUILD_NUMBER=5;当前第几次构建

  4)WORKSPACE_TMP(临时目录)=/var/jenkins_home/workspace/java-devops-demo@tmp 

3、定义Jenkinsfile与Dockerfile具体内容

Jenkinsfile部分是逐步测试,按阶段写成。

pipeline{
    //全部的CI/CD流程都需要在这里定义
    //任何一个代理可用就可以执行
    agent any

    //定义一些环境信息
    environment {
      WS = "${WORKSPACE}"
    }

    //定义流水线的加工流程
    stages{

        stage('环境检查'){
            steps {
               sh 'printenv'
               echo "正在检测基本信息"
               sh 'java -version'
               sh 'git --version'
               sh 'docker version'
               sh 'pwd && ls -alh'
            }
        }

        //1、编译 "abc"
        stage('maven编译'){
            agent {
                docker {
                    image 'maven:3-alpine'
                    args '-v /var/jenkins_home/appconfig/maven/.m2:/root/.m2'
                    //docker run -v /var/jenkins_home/appconfig/maven/.m2:/root/.m2
                }
            }
            //要做的所有事情
            //jenkins不配置任何环境的情况下, 仅适用docker兼容所有场景
            steps{
                echo "编译..."
                sh 'pwd && ls -alh'
                sh 'cd ${WS} && mvn clean package -s "/var/jenkins_home/appconfig/maven/settings.xml" -Dmaven.test.skip=true'
            }
        }
//2、打包
        stage('生成镜像'){
            steps{
                sh 'pwd && ls -alh'
                sh 'docker version'
                sh 'docker build -t java-devops-demo .'
            }
        }

        //3、部署
        stage('部署'){
            steps{
                echo "部署..."
                sh 'docker rm -f java-devops-demo-dev'
                sh 'docker run -d -p 80:8080 --name java-devops-demo-dev java-devops-demo'
            }
        }

        //4、推送报告
        stage("发送报告"){
            steps {
                //短信通知,购买api接口即可
//                 sh 'curl -i -k -X POST 'https://gyytz.market.alicloudapi.com/sms/smsSend?mobile=mobile¶m=**code**%3A12345%2C**minute**%3A5&smsSignId=2e65b1bb3d054466b82f0c9d125465e2&templateId=908e94ccf08b4476ba6c876d13f084ad'  -H 'Authorization:APPCODE dddddddd''
                //REST API 所有都行
//                 sh 'curl '
                echo '准备发送报告'
                emailext body: '''<!DOCTYPE html>
                <html>
                <head>
                <meta charset="UTF-8">
                <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</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">
                <h3>本邮件由系统自动发出,请勿回复!</h3>
                        <tr>
                           <br/>
                            各位同事,大家好,以下为${PROJECT_NAME }项目构建信息</br>
                            <td><font color="#CC0000">构建结果 - ${BUILD_STATUS}</font></td>
                        </tr>
                        <tr>
                            <td><br />
                            <b><font color="#0B610B">构建信息</font></b>
                            <hr size="2" width="100%" align="center" /></td>
                        </tr>
                        <tr>
                            <td>
                                <ul>
                                    <li>项目名称 : ${PROJECT_NAME}</li>
                                    <li>构建编号 : 第${BUILD_NUMBER}次构建</li>
                                    <li>触发原因: ${CAUSE}</li>
                                    <li>构建状态: ${BUILD_STATUS}</li>
                                    <li>构建日志: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li>
                                    <li>构建  Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li>
                                    <li>工作目录 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li>
                                    <li>项目  Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
                                </ul>


                <h4><font color="#0B610B">最近提交</font></h4>
                <ul>
                <hr size="2" width="100%" />
                ${CHANGES_SINCE_LAST_SUCCESS, reverse=true, format="%c", changesFormat="<li>%d [%a] %m</li>"}
                </ul>
                详细提交: <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a><br/>

                            </td>
                        </tr>
                    </table>
                </body>
                </html>''', subject: '${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志', to: 'xxxxx@163.com'
            }
        }
    }
        //后置处理过程
        post {
          failure {
            echo "这个阶段 完蛋了.... $currentBuild.result"
          }
          success {
            echo "这个阶段 成了.... $currentBuild.result"
          }
        }
}

Dockerfile:

#这个也得有
FROM openjdk:8-jre-alpine
LABEL maintainer="xxxxxxx@qq.com"
#复制打好的jar包
COPY target/*.jar /app.jar
RUN  apk add -U tzdata; \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime; \
echo 'Asia/Shanghai' >/etc/timezone; \
touch /app.jar;

ENV JAVA_OPTS=""
ENV PARAMS=""

EXPOSE 8080

ENTRYPOINT [ "sh", "-c", "java -Djava.security.egd=file:/dev/./urandom $JAVA_OPTS -jar /app.jar $PARAMS" ]

Jenkinsfile与Dockerfile均位于java-devops-jemo目录下,由Jenkins从git仓库拉取。

4、远程构建触发

期望效果: 远程的github代码提交了,jenkins流水线自动触发构建。 

实现条件:

  • 保证jenkins所在主机能被远程访问。
  • jenkins中远程触发需要权限,我们应该使用用户进行授权。
  • 配置gitee,webhook进行触发。

实现过程:

1)进入流水线配置页,填入身份验证令牌

docker部署jenkins怎么查看登录密码 jenkins docker 权限_docker_03

远程构建即使配置了gitee/github 的webhook,默认会403。我们应该使用用户进行授权 

  a.创建一个用户 (主界面>管理Jenkins>管理user>新建用户)

docker部署jenkins怎么查看登录密码 jenkins docker 权限_Jenkins_04

  b.新建用户后一定要重新登陆激活一次,进入用户列表>点击当前用户名>设置

  c.生成一个apitoken (生成后立即复制,只出现一次)

3)码云端配置WebHooks,进入码云对应代码仓库-配置-WebHooks-添加WebHook

docker部署jenkins怎么查看登录密码 jenkins docker 权限_docker_05

URL配置格式:http://dk:用户dk的apitoken@主机的公网ip:8080/job/java-devops-demo/build?token=身份验证令牌

添加成功后可测试与Jenkins主机的连通性。

至此,当本地修改代码,git push提交到gitee/github后,Jenkins就能够自动构建,构建成功即可查看前端页面的变化。

5、配置maven环境

(使用自定义agent的方式引入maven环境,利用多阶段构建不同场景下的复杂环境)

1)安装docker pipeline插件

2)自定义agent(在stages内部)

3)配置maven加速(配置国内阿里云)

把Maven的配置文件放在jenkins-data里面的某个位置。默认所有的可变配置项都推荐放在jenkins-home的位置,增强移植性。

4)缓存必要jar包,下次构建无需下载

agent {
    docker {
        image 'maven:3-alpine' //用完就会杀掉
        args '-v /var/jenkins_home/appconfig/maven/.m2:/root/.m2'
        // 将jar包映射到宿主机上/var/jenkins_home/appconfig/maven/.m2目录中
        // 也可以将jar包以数据卷方式挂载到宿主机
    }
}

注:jenkins不配置任何环境的情况下, 仅适用docker兼容所有场景。

  • 临时容器导致的问题(每个stage都会回到默认workspace,临时容器产生的打包数据不能被利用)
  • 第一次检出代码,默认在 /var/jenkins_home/workspace/【java-devops-demo】
  • 使用docker临时agent的时候,每一个临时容器运行又分配临时目录 /var/jenkins_home/workspace/java-devops-demo@2;默认就是workspace/java-devops-demo 的内容
  • 在临时容器里面 运行的mvn package命令,会在 /var/jenkins_home/workspace/java-devops-demo@2 进行工作 
  • package到了 /var/jenkins_home/workspace/java-devops-demo@2 位置
  • 进入下一步(stage)进行打包镜像,又会回到 /var/jenkins_home/workspace/【java-devops-demo】(默认workspace)这个位置
  •  这个位置没有运行过 mvn clean package ,所以没有target。 默认的 工作目录 没有 target

     解决方法:在临时容器内部切换到Jenkins的默认工作目录,再进行maven打包。

6、邮件推送

使用邮件扩展插件:Email Extension Plugin-2.71 (对于每个stage执行的任务成功与否可以通过后置执行post来进行感知)

系统管理>系统配置>配置管理员邮箱(系统管理员邮件地址)、SMTP服务相关及其他

docker部署jenkins怎么查看登录密码 jenkins docker 权限_Jenkins_06

docker部署jenkins怎么查看登录密码 jenkins docker 权限_Jenkins_07

 

docker部署jenkins怎么查看登录密码 jenkins docker 权限_java_08

 

下图Use SMTP Authentication部分在高版本插件中已不再支持

docker部署jenkins怎么查看登录密码 jenkins docker 权限_Jenkins_09

填写完毕,可以通过发送测试邮件进行测试。

docker部署jenkins怎么查看登录密码 jenkins docker 权限_docker_10

上图的邮件用户的授权码需要配置邮件发送的认证权限信息

  1. 登录自己邮箱,开启POP3/SMTP邮件服务
  2. 获取到自己的授权码(tlqhksolsmeodjad)
  3. 配置并测试好邮件发送即可

邮件模板内容见Jenkinsfile中报告推送阶段。

至此,开发提交代码后,将自动触发构建过程,构建结束后发送此次构建邮件报告如下:

docker部署jenkins怎么查看登录密码 jenkins docker 权限_docker_11