Jenkins(七) Jenkins+Docker+SpringCloud微服务持续集成(上)

大致流程说明:
1.开发人员每天把代码提交到Gitlab代码仓库
2.Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库。
3.Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。
4.最后,用户可以访问到容器

这里不讲述Docker的安装及基础命令的使用,学dockers可以看 Docker 概念、命令、DockerFile等看这篇就够了 这篇文章

Harbor 镜像仓库安装及使用

Harbor(港口,港湾)是一个用于存储和分发Docker镜像的企业级Registry服务器。除了Harbor这个私有镜像仓库之外,还有Docker官方提供的Registry。相对Registry,Harbor具有很多优势:

  1. 提供分层传输机制,优化网络传输 Docker镜像是是分层的,而如果每次传输都使用全量文件(所以
    用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定
    传输的对象。
  2. 提供WEB界面,优化用户体验 只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界
    面可以支持登陆、搜索功能,包括区分公有、私有镜像。
  3. 支持水平扩展集群 当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力作分
    解。
  4. 良好的安全机制 企业中的开发团队有很多不同的职位,对于不同的职位人员,分配不同的权限,
    具有更好的安全性。

Harbor 安装

  1. 除了docker以外,还要安装dockers-compose,Docker Compose 命令及使用 你可以看这篇博客
  2. 下载Harbor的压缩包,地址,需要科式上网,下载完成开始解压
tar -xzf harbor-offline-installer-v1.9.2.tgz
mv harbor /opt/software/
# 修改配置文件内容
vim /opt/software/harbor/harbor.yml 
# 修改hostname
hostname: 你自己机器的IP
# 修改端口
port: 85
  1. 安装Harbor
cd /opt/software/harbor
./prepare
./install.sh
  1. 启动Harbor
docker-compose up -d 启动
docker-compose stop 停止
docker-compose restart 重新启动
  1. 访问Harbor http://IP:85,默认账户密码:admin/Harbor12345

Harbor 使用

1.创建项目
Harbor的项目分为公开和私有的:

  • 公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。
  • 私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。

我们可以为微服务项目创建一个新的项目:

2.用户创建

3.为用户分配项目

推送到镜像仓库

# 打标签,命令 docker tag nginx:1.17.1 Harbor的IP:端口/Harbor项目的名称/镜像名字
docker tag nginx:1.17.1 192.168.81.102:85/test/nginx:1.17.1
# 查看
docker images
# 会多一条镜像数据
192.168.81.102:85/test/nginx         1.17.1                          98ebf73aba75        2 years ago         109MB

把Harbor地址加入到Docker信任列表

# 编辑docker文件
vim /etc/docker/daemon.json
# 加入这句话
"insecure-registries":["192.168.81.102:85"],
# 重启配置和docker
systemctl restart docker
# Harbor  的 test 项目是私有的,需要登录
docker login -u admin -p Harbor12345 192.168.81.102:85
# 推送到Harbor仓库里
docker push 192.168.81.102:85/test/nginx

其他服务下载镜像也需要把地址添加到docker的配置文件中,并且也需要使用 docker login 进行登录。复制以下的命令,到其他服务器下执行即可。

微服务构建到docker镜像

1.把微服务提交到 SVN
2.在jenkins上创建一个 Pipelin 类型的 item(项目)
3.配置Pipelin item的构建时参数

这里讲一点,项目中很多的 jar 是有依赖关系的,如果单独打某个服务可能会包找不到依赖,所以建议第一次对根目录进行打包,会把依赖全部加载到maven库中,后期单个服务打包即可。

4.在每个服务的pom文件中添加 plugin

<!-- 帮助读取项目中的dockerfile文件,帮我们构建docker镜像 -->
<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>dockerfile-maven-plugin</artifactId>
    <version>1.3.6</version>
    <configuration>
        <repository>${project.artifactId}</repository>
        <!-- 定义dockerfile 文件的参数 -->
        <buildArgs>
            <!-- 定义一个 JAVA_FILE 参数,指为我们项目的名称 -->
            <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
        </buildArgs>
    </configuration>
</plugin>

5.在每个服务的根目录下编写 Dockerfile 文件

FROM openjdk:8-jdk-alpine
# 这个会读取在pom中声明的变量
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
# 对外端口
EXPOSE 10086
ENTRYPOINT ["java","-jar","/app.jar"]

6.设置构建后镜像推至镜像仓库

7.在项目的根目录中编写 Jenkinsfile

node {
    // 版本
    def tag = "1.0"
    // 镜像仓库的地址
    def harbor_url = "192.168.81.102:85"
    // 镜像仓库的项目,这里建议项目名称和jenkins的item项目名称、以及harbor的项目名称保持一致,否则用一下脚本会出问题
    def harbor_project = "demo"

    // 拉取代码
    stage('pull code') {
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: '80dfe5c5-1684-47b1-a410-6f53ceb3c543', url: 'http://192.168.81.15:3000/biguncle/test.git']]])
    }
    // 编译并推送镜像仓库
    stage('build project') {
        if  ("${project_name}" ==  'demo' ) {
            echo '打包根目录'
            sh 'mvn clean package dockerfile:build'
        } else {
            echo  "打包子目录 ${project_name}"
            sh "mvn -f ${project_name} clean package dockerfile:build"
        }
        echo "把jar上传镜像仓库"
        def oldImageName = "${project_name}:latest"
        def newImageName = "${harbor_url}/${harbor_project}/${project_name}:${tag}"
        // 改名称 做规范
        sh "docker tag ${oldImageName} ${newImageName}"
        // 删除之前的 镜像
        sh "docker rmi ${oldImageName}"
        // 推送到 dockers仓库
        withCredentials([usernamePassword(credentialsId: '8a3d7ab1-4cd6-482c-86c9-a12aa6404d98', passwordVariable: 'harbor_password', usernameVariable: 'harbor_account')]) {
            // 登录
            sh "docker login -u ${harbor_account} -p ${harbor_password} ${harbor_url}"
            // 上传
            sh "docker push ${newImageName}"
            echo "镜像推送成功"
        }
    }
    // 发送邮件
    stage('send email') {
        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, sansserif">
            <tr>
                <td>(本邮件是程序自动下发的,请勿回复!)</td>
            </tr>
            <tr>
                <td><h2>
                    <font color="#0000FF">构建结果 - ${BUILD_STATUS}</font>
                </h2></td>
            </tr>
            <tr>
                <td><br/>
                    <b><font color="#0B610B">构建信息</font></b>
                    <hr size="2" width="100%" align="center"/>
                </td>
            </tr>
            <tr>
                <td>
                    <ul>
                        <li>项目名称&nbsp;:&nbsp;${PROJECT_NAME}</li>
                        <li>构建编号&nbsp;:&nbsp;第${BUILD_NUMBER}次构建</li>
                        <li>触发原因:&nbsp;${CAUSE}</li>
                        <li>构建日志:&nbsp;
                            <a href="${BUILD_URL}console">${BUILD_URL}console</a>
                        </li>
                        <li>构建&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>项目&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">构建日志 (最后 100行):</font></b>
                    <hr size="2" width="100%" align="center"/>
                </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>''', mimeType: 'text/html', subject: '43243214321', to: '875730567@qq.com'
    }
}

jenkins自动化服务拉取镜像并启动

这部操作需要安装一个 Publish Over SSH 插件,安装好后在Manager Jenkins -> Configure System 进行配置

更改 Jenkinsfile 脚本,添加远程机器拉取镜像并启动容器,具体语法怎么使用,可以到 流水线语法中查看

node {
    // 版本
    def tag = "1.0"
    // 镜像仓库的地址
    def harbor_url = "192.168.81.102:85"
    // 镜像仓库的项目,这里建议项目名称和jenkins的item项目名称、以及harbor的项目名称保持一致,否则用一下脚本会出问题
    def harbor_project = "demo"

    // 拉取代码
    stage('pull code') {
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: '80dfe5c5-1684-47b1-a410-6f53ceb3c543', url: 'http://192.168.81.15:3000/biguncle/test.git']]])
    }
    // 编译并推送镜像仓库
    stage('build project') {
        if  ("${project_name}" ==  'demo' ) {
            echo '打包根目录'
            sh 'mvn clean package dockerfile:build'
        } else {
            echo  "打包子目录 ${project_name}"
            sh "mvn -f ${project_name} clean package dockerfile:build"
        }
        echo "把jar上传镜像仓库"
        def oldImageName = "${project_name}:latest"
        def newImageName = "${harbor_url}/${harbor_project}/${project_name}:${tag}"
        // 改名称 做规范
        sh "docker tag ${oldImageName} ${newImageName}"
        // 删除之前的 镜像
        sh "docker rmi ${oldImageName}"
        // 推送到 dockers仓库
        withCredentials([usernamePassword(credentialsId: '8a3d7ab1-4cd6-482c-86c9-a12aa6404d98', passwordVariable: 'harbor_password', usernameVariable: 'harbor_account')]) {
            // 登录
            sh "docker login -u ${harbor_account} -p ${harbor_password} ${harbor_url}"
            // 上传
            sh "docker push ${newImageName}"
            echo "镜像推送成功"
        }

        // 远程调用脚本,port 最好也添加 jenkins项目配置里的参数配置,作为参数传进来
        echo "执行远程命令 /home/server/deploy.sh ${harbor_url} ${harbor_project} ${project_name} ${tag} ${port}"
        sshPublisher(publishers: [sshPublisherDesc(configName: 'test_103', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/home/server/deploy.sh ${harbor_url} ${harbor_project} ${project_name} ${tag} ${port}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

    }
    // 发送邮件
    stage('send email') {
        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, sansserif">
            <tr>
                <td>(本邮件是程序自动下发的,请勿回复!)</td>
            </tr>
            <tr>
                <td><h2>
                    <font color="#0000FF">构建结果 - ${BUILD_STATUS}</font>
                </h2></td>
            </tr>
            <tr>
                <td><br/>
                    <b><font color="#0B610B">构建信息</font></b>
                    <hr size="2" width="100%" align="center"/>
                </td>
            </tr>
            <tr>
                <td>
                    <ul>
                        <li>项目名称&nbsp;:&nbsp;${PROJECT_NAME}</li>
                        <li>构建编号&nbsp;:&nbsp;第${BUILD_NUMBER}次构建</li>
                        <li>触发原因:&nbsp;${CAUSE}</li>
                        <li>构建日志:&nbsp;
                            <a href="${BUILD_URL}console">${BUILD_URL}console</a>
                        </li>
                        <li>构建&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>项目&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">构建日志 (最后 100行):</font></b>
                    <hr size="2" width="100%" align="center"/>
                </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>''', mimeType: 'text/html', subject: '43243214321', to: '875730567@qq.com'
    }
}

重新构建,其他服务器就会自动拉取了。

其他服务器一定要有 jenkins 服务器的公钥,构建过程 SSH 不会输出任何信息只会告诉你 EXEC 执行了多久,需要自己去测一下。

vue 前端使用Jenkins部署

1.首先需要安装 NodeJS 插件
2.到Manager Jenkins->Global Tool Configuration->NodeJS

3.创建一个流水线的前端项目,根据脚本把配置补全

node {
    stage('拉取代码') {
        checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: '80dfe5c5-1684-47b1-a410-6f53ceb3c543', url: 'http://192.168.81.15:3000/biguncle/test_vue.git']]])
    }
    stage('打包,部署网站') {
        //使用NodeJS的npm进行打包,这个和 以上的 name 保持一致
        nodejs('nodejs12'){
            sh '''
                npm install
                npm run build
            '''
        }
        //=====以下为远程调用进行项目部署========
        sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server',transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '',execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes:false, patternSeparator: '[, ]+', remoteDirectory: '/usr/share/nginx/html',remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')],usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
    }
}

从脚本中可以看出,我们需要一个 branch 参数,还要配置 前端项目的 远程 server 地址

这里没有使用execCommand的命令,而是通过 sourceFiles、removePrefix、remoteDirectory,sourceFiles 代表我们copy哪个文件,remoteDirectory 远程目录,特就是copy到nginx所在目录
57-62

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容