微服务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服务器部署及应用
文章目录
- 一、先决条件
-
- [1.1 服务器先决条件](#1.1 服务器先决条件)
- [1.2 项目配置](#1.2 项目配置)
-
- [1.2.1 Dockerfile](#1.2.1 Dockerfile)
- [1.2.2 pom配置](#1.2.2 pom配置)
- [1.2.3 部署脚本](#1.2.3 部署脚本)
- 二、Jenkins构建部署
-
- [2.1 创建项目](#2.1 创建项目)
- [2.2 配置项目基本信息](#2.2 配置项目基本信息)
- [2.3 定义 Pipeline script](#2.3 定义 Pipeline script)
- [2.4 构建部署项目](#2.4 构建部署项目)
- 三、其他方式构建部署后端服务
后端微服务项目是基于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
bash
# 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:
bash
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打包依赖:
xml
<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 项目配置。完整的后端部署脚本如下:
bash
#!/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
bash
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服务器验证服务是否部署成功
bash
[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云原生平台的测试和生产环境,仅需要添加相应流水线流程即可。
bash
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配置
xml
<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文件,劣势是无法灵活管理构建步骤。