微服务CI/CD实践(六)Jenkins & Docker 自动化构建部署Java微服务

微服务CI/CD实践系列:
 
微服务CI/CD实践(一)环境准备及虚拟机创建
微服务CI/CD实践(二)服务器先决准备
微服务CI/CD实践(三)gitlab部署及nexus3部署
微服务CI/CD实践(四)Jenkins部署及环境配置
微服务CI/CD实践(五)Jenkins + Dokcer 部署微服务前端VUE项目
微服务CI/CD实践(六)Jenkins + Dokcer 部署微服务后端项目
微服务CI/CD实践(七)Minio服务器部署及应用


后端微服务项目是基于JDK1.8 + SpringCloudAlibaba框架开发,我们通过Jenkins流水线作业将后端工程打包成Docker镜像的方式进行部署。构建部署流程如下:

  • 拉取代码
  • jenkins服务器maven编译代码
  • 使用dockerfile构建镜像并打包镜像
  • 上传镜像包
  • 执行sh

一、先决条件

1.1 服务器先决条件

Jenkins 和 server服务器先决条件参考微服务CI/CD实践(二)服务器先决准备微服务CI/CD实践(四)Jenkins部署及环境配置

1.2 项目配置

后端项目基于maven Dockerfile 构建镜像。

1.2.1 Dockerfile

# docker image deploy
FROM openjdk:8-jdk-alpine
COPY  /uaa-center-server/target/app.jar /app.jar

ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev","/app.jar"]

上述配置仅是一个最基本Java工程镜像构建脚本,如果需要集成其他插件需要对应修改脚本
以下示例展示了如何通过Dockerfile集成arshs 和 skywalking:

FROM openjdk:8-jdk-alpine
COPY  /uaa-center-server/target/app.jar /app.jar
# copy arthas
COPY ‐‐from=hengyunabc/arthas:latest /opt/arthas /opt/arthas
# copy skywalking
COPY /uaa-center-server/agent /usr/local/agent
# 这里可以传递skywalking‐agent 配置
ENTRYPOINT [ "sh", "‐c", "java ‐javaagent:/usr/local/agent/skywalking‐agent.jar ‐
Dskywalking.agent.service_name=yourappname ‐
Dskywalking.collector.backend_service=xx.xx.xx.xx:11800 ‐Dspring.profiles.active=dev ‐
jar /app.jar" ]

可以在ENTRYPOINT 端点指定相关agent配置,也可以在docker run执行脚本中指定。

1.2.2 pom配置

后端工程pom添加maven打包依赖:

<build>
        <finalName>app</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot-dependencies.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version> <!-- 使用与你的 Maven 版本兼容的版本 -->
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>1.18.24</version> <!-- 使用与你的 Maven 版本兼容的版本 -->
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <arg>-Aprojectlombok.classpath=${project.build.outputDirectory}</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>

后端工程Dockerfile目录结构如下:
Dockerfile 和 具体的server工程在同级目录
在这里插入图片描述

1.2.3 部署脚本

后端服务和前端服务部署脚本基本一致,也是通过传入动态参数方式执行sh脚本,sh脚本执行步骤解释参考 微服务CI/CD实践(五)Jenkins + Dokcer 部署微服务前端VUE项目 1.2 项目配置。完整的后端部署脚本如下:

#!/usr/bin/env bash

echo "param validate"
if [ $# -lt 5 ]; then
  echo "you must use like this : /usr/docker-sh/publish_project_name.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"  
  exit  
fi

container_name="$1"
image_name="$2"
version="$3"
portal_port="$4"
server_port="$5"
if [ "$6" != "" ]; then
   portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
   serve_sslr_port="$7"
fi

echo "执行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
  echo $container_name "容器存在,停止并删除"
  echo "docker stop" $container_name
  docker stop $container_name
  echo "docker rm" $container_name
  docker rm $container_name
else
  echo $container_name "容器不存在"
fi
# 删除镜像
echo "执行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
  echo $image_name '镜像存在,删除镜像'
  docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
  echo $image_name '镜像不存在'
fi

#bak image
echo "bak image" $image_name
BAK_DIR=/opt/bak/docker/$image_name/`date +%Y%m%d`
mkdir -p "$BAK_DIR"
cp "/opt/tmp/$container_name.tar" "$BAK_DIR"/"$image_name"_`date +%H%M%S`.tar

echo "docker load" $image_name
docker load --input /opt/tmp/$container_name.tar

echo "docker run" $image_name
# 这里因为服务本身还运行了netty,所以需要将netty的端口也做映射,如果没有仅需要配置server_port 
docker run -d -p $portal_port:$server_port -p $portal_ssl_port:$portal_ssl_port --name=$container_name --network=my-network -e TZ="Asia/Shanghai" --restart=always -v ./logs:/usr/local/server-log/uaa-center-server $image_name

echo "remove tmp " $image_name
rm -rf /opt/tmp/$container_name.tar

echo "Docker Portal is starting,please try to access $container_name conslone url"

二、Jenkins构建部署

2.1 创建项目

新建一个流水线任务
在这里插入图片描述

2.2 配置项目基本信息

创建完成项目,点击项目进入项目页面,点击左侧菜单》配置,进行项目基本配置
step1 项目构建历史存储策略配置
在这里插入图片描述
这里存储策略根据自己项目要求进行配置。

step2 配置参数化构建过程
Jenkins List Git Branches插件 构建选择指定git分支,点击添加参数选择List Git branchers选项进行Jenkins List Git Branches插件配置
在这里插入图片描述
Jenkins List Git Branches插件配置流程如下:

  • 配置name
  • 配置仓库并选择凭证
  • 选择Parameter Type
  • 配置Branch Filter

2.3 定义 Pipeline script

pipeline {
    agent any
    
    environment {   
        REPOSITORY="http://192.168.1.101:8929/hka/hkabackgroud/business/uaa-center.git"
		projectdir="uaa-center-pipeline"
		projectname="uaa-center-server"
		apiname="uaa-center-api"
    }
    
    stages {
        stage('获取代码') {
			steps {
				echo "start fetch code from git:${REPOSITORY} ${branch}"
				deleteDir()
				checkout([
                    $class: 'GitSCM',
                    branches: [[name: '${branch}']],
                    doGenerateSubmoduleConfigurations: false,
                    extensions: [],
                    userRemoteConfigs: [[
                        credentialsId: '2',
                        url: 'http://192.168.1.101:8929/hka/hkabackgroud/business/uaa-center.git'
                    ]]
                ])
			}
		}
		
		stage('替换') {
			steps {
				echo "start replace"
                sh " cd ${WORKSPACE}/${projectname} "
                sh " rm -f target/${projectname}.jar "
                sh "mv ${WORKSPACE}/${projectname}/src/main/resources/bootstrap.yml.example ${WORKSPACE}/${projectname}/src/main/resources/bootstrap.yml "
                sh "mv ${WORKSPACE}/${projectname}/src/main/resources/bootstrap-dev.yml.example ${WORKSPACE}/${projectname}/src/main/resources/bootstrap-dev.yml "
                			
			}
		}
		
		stage('打包') {
			steps {
				echo "start build"
			
				withMaven(maven: 'maven3.8.1') {
                    sh " mvn -f ${WORKSPACE}/${apiname}/pom.xml clean deploy -DskipDockerTag -DskipDockerPush  "
                    sh " mvn -f ${WORKSPACE}/${projectname}/pom.xml clean install -DskipDockerTag -DskipDockerPush  "
                }
			}
		}
        
        stage('Delete Old Docker Container') {
            steps {
                echo "delete docker container"
                sh '''if [[ "$(docker inspect ${projectname} 2> /dev/null | grep ${projectname})" != "" ]]; 
                then 
                  echo ${projectname} "容器存在,停止并删除"
                  echo "docker stop" ${projectname}
                  docker stop ${projectname}
                  echo "docker rm" ${projectname}
                  docker rm ${projectname}
                else 
                  echo ${projectname} "容器不存在"
                fi'''
            }
        }
        
        stage('Delete Old Docker Image') {
            steps {
                echo "delete docker image"
                sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; 
                    then 
                      echo ${projectname} \'镜像存在,删除镜像\'
                      docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
                    else 
                      echo ${projectname} \'镜像不存在,创建镜像\'
                    fi'''
            }
            
        }
        
        stage('Build Docker Image') {
             steps {
                echo "start docker build ${projectname} code"
                sh 'docker build -t ${projectname} .'
                echo "save docker images tar"
                sh 'docker save -o ${projectname}.tar ${projectname}'
             }
            
        }
        
        stage('Delete New Docker Image') {
            steps {
                echo "delete docker image"
                sh '''if [[ "$(docker images -q ${projectname} 2> /dev/null)" != "" ]]; 
                    then 
                      echo ${projectname} \'镜像存在,删除镜像\'
                      docker rmi $(docker images -q ${projectname} 2> /dev/null) --force
                    else 
                      echo ${projectname} \'镜像不存在,创建镜像\'
                    fi'''
            }
        }
        
        stage('Upload img tar') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: '103',
                            transfers: [
                                sshTransfer(
                                    cleanRemote: false,
                                    excludes: '',
                                    makeEmptyDirs: false,
                                    noDefaultExcludes: false,
                                    patternSeparator: '[, ]+',
                                    remoteDirectory: '',
                                    remoteDirectorySDF: false,
                                    removePrefix: '',
                                    sourceFiles: 'uaa-center-server.tar'
                                )
                            ],
                            usePromotionTimestamp: false,
                            useWorkspaceInPromotion: false,
                            verbose: false
                        )
                    ]
                )
            }
        }
        
        stage('Execute Command sh') {
            steps {
                sshPublisher(
                    publishers: [
                        sshPublisherDesc(
                            configName: '103',
                            transfers: [
                                sshTransfer(
                                    execCommand: '/usr/docker-sh/publish_uaa-center-server.sh uaa-center-server uaa-center-server latest 10005 10005 9000 9000',
                                    execTimeout: 300000
                                )
                            ],
                            usePromotionTimestamp: false,
                            useWorkspaceInPromotion: false,
                            verbose: false
                        )
                    ]
                )
            }
        }
        
        stage('Publish Results') {
            steps {
               echo "End Publish ${projectname}"  
            }
        }

        
    }
}

2.4 构建部署项目

回到项目页面,点击参数化构建,选择用于构建的分支点击Build执行构建任务。
在这里插入图片描述
在这里插入图片描述
当jenkins 流水线执行完成后可到对应Server服务器验证服务是否部署成功

[root@k8s-rancher-node02 ~]# docker ps -a
CONTAINER ID   IMAGE                COMMAND                   CREATED       STATUS        PORTS                                                                                      NAMES
5d65d80b5350   uaa-center-server    "java -jar -Dspring.…"   2 days ago    Up 31 hours   0.0.0.0:9000->9000/tcp, :::9000->9000/tcp, 0.0.0.0:10005->10005/tcp, :::10005->10005/tcp   uaa-center-server

三、其他方式构建部署后端服务

上述基于Jenkins流水线方式部署流程稍显复杂,优势是可以灵活配置构建步骤,特别是对于多环境多服务器的支持更加具有优势,比如通过Jenkins实现本地自动化部署并将镜像推送到AWS云原生平台的测试和生产环境,仅需要添加相应流水线流程即可。

stage('push image to aws ecr') {
			steps {
				echo "start push"
			script {
			    docker.withRegistry('https://xxxx.dkr.ecr.cn-north-1.amazonaws.com.cn', 'ecr:cn-north-1:aws_ecr') {
                    docker.image('${projectname}').push('${BUILD_NUMBER}')
			    }
			}
		    }
        }
        stage('push image to aws ecr us-west-2') {
			steps {
				echo "start push"
			script {
			    docker.withRegistry('https://xxxxx.dkr.ecr.us-west-2.amazonaws.com', 'ecr:us-west-2:aws_us_west_2_ecr') {
                    docker.image('${projectname}').push('${BUILD_NUMBER}')
			    }
			}
		    }
        }

如果仅在单环境部署,可以考虑使用Maven构建Docker镜像方式构建镜像,该方式流程简单。

使用Maven构建Docker镜像

step1 应用pom文件添加maven-docker-plugin配置

<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>1.1.0</version>
    <executions>
        <execution>
            <id>build-image</id>
            <phase>package</phase>
            <goals>
                <goal>build</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <imageName>${project.artifactId}:${project.version}</imageName>
        <dockerHost>http://registry服务器地址:2375</dockerHost>
        <baseImage>openjdk:8-jdk-alpine</baseImage>
        <entryPoint>["java", "-jar","/${project.build.finalName}.jar"]
        </entryPoint>
        <resources>
            <resource>
                <targetPath>/</targetPath>
                <directory>${project.build.directory}</directory>
                <include>${project.build.finalName}.jar</include>
            </resource>
        </resources>
    </configuration>
</plugin>

配置说明:
executions.execution.phase:此处配置了在maven打包应用时构建docker镜像;
imageName:用于指定镜像名称, p r o j e c t . a r t i f a c t I d 为镜像名称, {project.artifactId}为镜像名称, project.artifactId为镜像名称,{project.version}为仓库版本;
dockerHost:打包后上传到的docker服务器地址;
baseImage:该应用所依赖的基础镜像,此处为java;
entryPoint:docker容器启动时执行的命令;
resources.resource.targetPath:将打包后的资源文件复制到该目录;
resources.resource.directory:需要复制的文件所在目录,maven打包的应用jar包保存在target目录下面;
resources.resource.include:需要复制的文件,打包好的应用jar包。
step2 Jenkins创建项目
Jenkins—》添加Items
根据自己所有选择合适项目类型,这里我们选择Freestyle project,以便我们自定义构建流程。也可以从其他项目copy配置。

step3 配置代码源
在这里插入图片描述
说明:
Repository URL:远程代码仓库地址
Credentials:凭据,可选择已添加的全局凭据,或者在此页面自定义全局凭据
指定分支(为空时代表any):指定项目构建分支
构建触发器:可自定义构建触发,如钩子函数、定时任务等

step4 构建流程配置
这一步是因为项目的配置管理策略有关,本地配置均不提交到服务器,如何没有采用该策略可以忽略
点击增加构建步骤>选择执行shell步骤
在这里插入图片描述

在这里插入图片描述
构建步骤5 使用maven打包构建镜像并推送到server
点击增加构建步骤>选择顶层Maven目标步骤
在这里插入图片描述
在这里插入图片描述
构建步骤6 远程执行shell,部署镜像
点击增加构建步骤>选择远程执行shell步骤
在这里插入图片描述
使用maven-docker-plugin方式构建后端项目流程简单且Jenkins服务器不需要安装额外的Docker,也不需要编写Dokcerfile文件,劣势是无法灵活管理构建步骤。

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值