赞
踩
大致流程说明:
1)开发人员每天把代码提交到Gitlab代码仓库
2)Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到
Harbor私有仓库。
3)Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。
4)最后,用户可以访问到容器
checkout([$class: ‘GitSCM’, branches: [[name: ‘*/master’]], extensions: [], userRemoteConfigs: [[credentialsId: ‘fa0ff0a7-3b63-46b5-aaed-a7b357c23abe’, url: ‘http://192.168.0.121:82/root/online2022_admin.git’]]])
把上面生成的语法copy到我们编辑的脚本式文件里面
正常拉取代码了
1)在项目根目录建立Jenkinsfile文件,把内容复制到该文件中
安装Extended Choice Parameter插件,支持多选框
//编译并安装公共工程
sh “mvn -f common clean install”
添加可以多选的项目打包编译
checkboxes选中值得格式我们采用逗号分隔每个项目,每个项目里面项目名和端口号以@分隔
例如: 项目名称1@8001,项目名称2@8002
infrastructure/api_gateway@8222,service/service_acl@8009,service/service_cms@8004,service/service_edu@8001,service/service_msm@8005,service/service_order@8007,service/service_oss@8002,service/service_statistics@8008,service/service_ucenter@8160,service/service_vod@8003
网关,权限,轮播图管理,后台系统主模块,验证码模块,订单模块,阿里云存储文件模块,统计模块,前台系统模块,阿里云音视频模块
stage('编译构建') { //编译并安装公共工程 echo '编译并安装公共工程' sh "mvn -f common clean install" for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; echo "${currentProject}" //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] echo "${currentProjectName}" echo "${currentProjectPort}" sh "mvn -f ${currentProjectName} clean package" } }
上面得脚本中引用变量,必须在双引号里面,否则会有问题
注意该项目打包后jar大小是否是正常的,如果太小的话,证明jar里面是没有BOOT-INFO这个包的(自己可以用压缩工具打开看一下),这个jar是不能启动的。这种情况一般是spring-boot-maven-plugins不生效,
1)第一种情况:你的父项目没有继承spring-boot-parent,需要在每个微服务项目里面添加spring-boot-maven-plugin插件,并且指定每个微服务项目的入口文件(公共模块不需要引入spring-boot-maven-plugin插件)
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!--因为我们得guli_parent项目没有以springboot作为复工程,所以需要指定子项目打可执行jar包程序入口--> <mainClass>com.atguigu.gateway.ApiGatewayApplication</mainClass> </configuration> <!--最主要的是要添加 repackage goal,用来重新打jar或者war包。--> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
2)第二种情况:你的父项目继承spring-boot-parent,那么只需要在父项目pom文件添加
spring-boot-maven-plugin插件,公共模块就排除spring-boot-maven-plugin插件就可以了
1)第一种办法(我们不采用)
在项目中生成Dockerfile 文件
进入该项目目录
docker build
–build-arg JAR_FILE=xx.jar
-f ${Dockerfile} -t ${projectImage} .
–build-arg是构建参数 JAR_FILE是我们Dockefile文件里面需要的参数
-f 指定Dockerfile位置(-f ${Dockerfile} 可以不写,默认会找该目录下的Dockerfile文件)
-t 后面的 projectImage表示镜像名称
最后.号是指镜像构建时打包上传到Docker引擎中的文件的目录,一定要加上去
2)第二种办法(采用),在项目中生成Dockerfile 文件,我们采用maven 有关dockerfile-maven-plugin插件,执行mvn dockerfile:build命令来生成镜像
Dockerfile文件内容:
# 拉取jdk8作为基础镜像
FROM openjdk:8-jdk-alpine
# 输入参数,改参数为打包后的jar包,例如 api_gateway/target/api_gateway-1.0-SNAPSHOT.jar
ARG JAR_FILE
# 添加jar到镜像并命名为app.jar, copy命令是把 api_gateway-1.0-SNAPSHOT.jar直接添加到镜像里面不解压,ADD是添加入镜像并且解压
COPY ${JAR_FILE} app.jar
# 镜像启动后暴露的端口
EXPOSE 8222
# jar运行命令,参数使用逗号隔开
ENTRYPOINT ["java","-jar","/app.jar"]
项目的pom文件添加dockerfile-maven-plugin插件
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<repository>${project.artifactId}</repository>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
在原有的Jenkinsfile文件添加
sh "mvn -f ${currentProjectName} dockerfile:build"
jenkins构建结果
我们手动运行一下
docker run -di --name=api_gateway -p 8222:8222 api_gateway:latest
查看一下启动日志 (我这个项目是需要注册中心nacos,自己在nacos安装一下2.x版本nacos启动就可以了)
docker logs -f api_gateway
以上已经完成 选中的微服务项目编译打包,并且制作成docker镜像了,如果不需要私服镜像仓库的话,就部署和jenkins同一台机器上面的话,自己在Jenkinsfile文件加一下docker 运行镜像的shell命令就可以了,每次编译前都要停止对应项目的容器,删除容器和镜像。
如有需要搭建镜像私服Harbor来存储docker制作执行,并且新的服务器可以从Harbor上拉取镜像并且运行成容器的,请往下面看
1)先安装docker-compose
[root@localhost bin]# sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-uname -s
-uname -m
-o /usr/local/bin/docker-compose
2)给docker-compose添加执行权限
[root@localhost bin]# sudo chmod +x /usr/local/bin/docker-compose
3)查看docker-compose是否安装成功
docker-compose -version
4)下载Harbor的压缩包(版本为:v1.9.2)
https://github.com/goharbor/harbor/releases
5)上传压缩包到linux,并解压
tar -xzf harbor-offline-installer-v1.9.2.tgz
mkdir /opt/harbor
mv harbor/* /opt/harbor
cd /opt/harbor
6)修改Harbor的配置
vi harbor.yml
修改hostname和port
hostname: 192.168.0.121
port: 85
7)安装Harbor
./prepare
./install.sh
8)启动Harbor
docker-compose up -d 启动
docker-compose stop 停止
docker-compose restart 重新启动
10)访问Harbor
http://192.168.0.121:85
默认账户密码:admin/Harbor12345
先手动测试一波命令(测试)
1)给镜像打标签
docker tag api_gateway:latest 192.168.0.121:85/online2022_admin/api_gateway:v1
api_gateway:latest 是jenkins所在机器docker制作镜像名称和版本
192.168.0.121是harbors所在机器的ip
85是harbor应用程序的端口
online2022_admin 是在harbors上面创建的项目,待会镜像是传到该项目上
2)推送镜像到harbor私有仓库的online2022_admin项目里面
docker push 192.168.0.121:85/online2022_admin/api_gateway:v1
正常情况是推送失败的
以上的错误是jenkins上面机器的docker 不信任harbor造成的。
在 jenkins机器上,Docker把Harbor加入信任列表中
vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://zydiol88.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.0.121:85"]
}
docker push 192.168.0.121:85/online2022_admin/api_gateway:v1
#重启本地docker
systemctl restart docker
再次推送,会提示权限不足
在jenkins机器上docker登录harbor所在的机器
docker login -u 用户名 -p 密码 192.168.0.121:85
docker login -u lijun -p Lijun123456 192.168.0.121:85
docker push 192.168.0.121:85/online2022_admin/api_gateway:v1
推送成功了
把sh命令改造放进Jenkinsfile文件里面
//定义镜像名称(因为原本微服务项目前面还有一个父项目的,所以 必须获取到项目的真正名称 ,例如 infrastructure/api_gateway 变成 api_gateway)
//gitlab的凭证 def git_auth = "fa0ff0a7-3b63-46b5-aaed-a7b357c23abe" //构建版本的名称 def tag = "latest" //Harbor私服地址 def harbor_url = "192.168.0.121:85" //Harbor的项目名称 def harbor_project_name = "online2022_admin" // harbor用户名和密码 def username = "lijun" def password = "Lijun123456" def realProjectName = currentProjectName.split("/") def realName = realProjectName[realProjectName.length-1] def imageName = "${realName}:${tag}" //登录 sh "docker login -u ${username} -p ${password} ${harbor_url}" //给镜像打标签 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" //上传镜像 sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" //删除本地镜像 (根据镜像名称删除镜像和打标签的镜像,因为原本的镜像和打包后镜像是同一 个镜像id,无法通过镜像id删除镜像) sh "docker rmi -f ${imageName}" sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}"
Jenkinsfile完整内容:
//gitlab的凭证 def git_auth = "fa0ff0a7-3b63-46b5-aaed-a7b357c23abe" //构建版本的名称 def tag = "latest" //Harbor私服地址 def harbor_url = "192.168.0.121:85" //Harbor的项目名称 def harbor_project_name = "online2022_admin" // harbor用户名和密码 def username = "lijun" def password = "Lijun123456" node { //把选择的项目信息转为数组 def selectedProjects = "${project_name}".split(',') echo "${selectedProjects}" stage('拉取代码') { checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: 'http://192.168.0.121:82/root/online2022_admin.git']]]) } stage('编译构建') { //编译并安装公共工程 sh "mvn -f common clean install" for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] // 编译打包选中微服务项目 sh "mvn -f ${currentProjectName} clean package" // 生成镜像 sh "mvn -f ${currentProjectName} dockerfile:build" //定义镜像名称(因为原本微服务项目前面还有一个父项目的,所以 必须获取到项目的真正名称 ,例如 infrastructure/api_gateway 变成 api_gateway) def realProjectName = currentProjectName.split("/") def realName = realProjectName[realProjectName.length-1] def imageName = "${realName}:${tag}" //登录 sh "docker login -u ${username} -p ${password} ${harbor_url}" //给镜像打标签 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" //上传镜像 sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" //删除本地镜像 (根据镜像名称删除镜像和打标签的镜像,因为原本的镜像和打包后镜像是同一个镜像id,无法通过镜像id删除镜像) sh "docker rmi -f ${imageName}" sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" } // infrastructure/api_gateway@8222,service/service_acl@8009,service/service_cms@8004,service/service_edu@8001,service/service_msm@8005,service/service_order@8007,service/service_oss@8002,service/service_statistics@8008,service/service_ucenter@8160,service/service_vod@8003 // 网关,权限,轮播图管理,后台系统主模块,验证码模块,订单模块,阿里云存储文件模块,统计模块,前台系统模块,阿里云音视频模块 } }
效果图
如果怕harbor账号密码泄露,在jenkins凭证管理添加全局配置
添加后会自动生成id,这个id就可以直接关联harbor账号和密码了
用jenkins的流水线语法生成器生成脚本片段
withCredentials([usernamePassword(credentialsId: '262b0b77-61b6-493c-9bb4-bf0659365903', passwordVariable: 'password', usernameVariable: 'username')]) {
// some block
}
改造后的Jenkinsfile内容
//gitlab的凭证 def git_auth = "fa0ff0a7-3b63-46b5-aaed-a7b357c23abe" //构建版本的名称 def tag = "latest" //Harbor私服地址 def harbor_url = "192.168.0.121:85" //Harbor的项目名称 def harbor_project_name = "online2022_admin" // harbor用户凭证(全局凭证管理那里配置) def harbor_auth = "262b0b77-61b6-493c-9bb4-bf0659365903" node { //把选择的项目信息转为数组 def selectedProjects = "${project_name}".split(',') echo "${selectedProjects}" stage('拉取代码') { checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: 'http://192.168.0.121:82/root/online2022_admin.git']]]) } stage('编译构建') { //编译并安装公共工程 sh "mvn -f common clean install" for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] // 编译打包选中微服务项目 sh "mvn -f ${currentProjectName} clean package" // 生成镜像 sh "mvn -f ${currentProjectName} dockerfile:build" //定义镜像名称(因为原本微服务项目前面还有一个父项目的,所以 必须获取到项目的真正名称 ,例如 infrastructure/api_gateway 变成 api_gateway) def realProjectName = currentProjectName.split("/") def realName = realProjectName[realProjectName.length-1] def imageName = "${realName}:${tag}" //给镜像打标签 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" withCredentials([usernamePassword(credentialsId: '262b0b77-61b6-493c-9bb4-bf0659365903', passwordVariable: 'password', usernameVariable: 'username')]) { //登录 sh "docker login -u ${username} -p ${password} ${harbor_url}" //上传镜像 sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" } //删除本地镜像 (根据镜像名称删除镜像和打标签的镜像,因为原本的镜像和打包后镜像是同一个镜像id,无法通过镜像id删除镜像) sh "docker rmi -f ${imageName}" sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" } // infrastructure/api_gateway@8222,service/service_acl@8009,service/service_cms@8004,service/service_edu@8001,service/service_msm@8005,service/service_order@8007,service/service_oss@8002,service/service_statistics@8008,service/service_ucenter@8160,service/service_vod@8003 // 网关,权限,轮播图管理,后台系统主模块,验证码模块,订单模块,阿里云存储文件模块,统计模块,前台系统模块,阿里云音视频模块 } }
1)jenkins(192.168.0.120) 所在的机器 上生成密钥:
[root@mycentos .ssh]# ssh-keygen
不断回车就好了
2)拷贝公钥到远程服务器(192.168.0.130)
ssh-copy-id 192.168.0.130
3)尝试在jenkin(192.168.0.120)机器上ssh登录部署服务器(192.168.0.130)
如果连接失败jenkins尝试连接失败,请开启sshd服务
sudo service sshd start //开启sshd服务
ssh root@192.168.0.130
4)jenkins 192.168.0.120(系统配置)->添加远程服务器
设置私钥位置
/root/.ssh/id_rsa
5)如果jenkins用ssh连接部署服务器失败的话,估计是jenkins不支持所在机器生成的私钥(机器版本太高了)
先移除known_hosts文件
rm -rf /root/.ssh/known_hosts
重新生成密钥
ssh-keygen -m PEM -t rsa -b 4096
重新把公钥给部署服务器
ssh root@192.168.0.130
sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh ${harbor_url} ${harbor_project_name} ${realName} ${tag} ${currentProjectPort}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
完整jenkins流水想脚本
//gitlab的凭证 def git_auth = "fa0ff0a7-3b63-46b5-aaed-a7b357c23abe" //构建版本的名称 def tag = "latest" //Harbor私服地址 def harbor_url = "192.168.0.121:85" //Harbor的项目名称 def harbor_project_name = "online2022_admin" // harbor用户凭证(全局凭证管理那里配置) def harbor_auth = "262b0b77-61b6-493c-9bb4-bf0659365903" node { //把选择的项目信息转为数组 def selectedProjects = "${project_name}".split(',') echo "${selectedProjects}" stage('拉取代码') { checkout([$class: 'GitSCM', branches: [[name: '*/${branch}']], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: 'http://192.168.0.121:82/root/online2022_admin.git']]]) } stage('编译构建') { //编译并安装公共工程 sh "mvn -f common clean install" for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] // 编译打包选中微服务项目 sh "mvn -f ${currentProjectName} clean package" // 生成镜像 sh "mvn -f ${currentProjectName} dockerfile:build" //定义镜像名称(因为原本微服务项目前面还有一个父项目的,所以 必须获取到项目的真正名称 ,例如 infrastructure/api_gateway 变成 api_gateway) def realProjectName = currentProjectName.split("/") def realName = realProjectName[realProjectName.length-1] def imageName = "${realName}:${tag}" //给镜像打标签 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" withCredentials([usernamePassword(credentialsId: '262b0b77-61b6-493c-9bb4-bf0659365903', passwordVariable: 'password', usernameVariable: 'username')]) { //登录 sh "docker login -u ${username} -p ${password} ${harbor_url}" //上传镜像 sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" } //删除本地镜像 (根据镜像名称删除镜像和打标签的镜像,因为原本的镜像和打包后镜像是同一个镜像id,无法通过镜像id删除镜像) sh "docker rmi -f ${imageName}" sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" //=====以下为远程调用进行项目部署======== sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh ${harbor_url} ${harbor_project_name} ${realName} ${tag} ${currentProjectPort}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) } // =================================这个是jenkins部署多参数用到的值,可以直接复制黏贴过去来改 // infrastructure/api_gateway@8222,service/service_acl@8009,service/service_cms@8004,service/service_edu@8001,service/service_msm@8005,service/service_order@8007,service/service_oss@8002,service/service_statistics@8008,service/service_ucenter@8160,service/service_vod@8003 // 网关,权限,轮播图管理,后台系统主模块,验证码模块,订单模块,阿里云存储文件模块,统计模块,前台系统模块,阿里云音视频模块 } }
8)部署服务器(192.168.0.130) 添加harbot信任列表
{
"registry-mirrors": ["https://zydiol88.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.0.121:85"]
}
cd /opt/harbor
docker-compose stop
docker-compose up -d
10)部署服务器(192.168.0.130)的deploy.sh内容
#! /bin/sh #接收外部参数 harbor_url=$1 harbor_project_name=$2 project_name=$3 tag=$4 port=$5 imageName=$harbor_url/$harbor_project_name/$project_name:$tag echo "$imageName" #查询容器是否存在,存在则删除 # docker ps -a 查询所有的容器(无论启动还是停止) # grep -w 根据一个单词来全匹配查询 # awk '{print $1}' 对于获取来的每行数据,按空格分隔,取第1列的值,$2表示拿第二列的值 containerId=`docker ps -a | grep -w ${project_name}:${tag} | awk '{print $1}'` if [ "$containerId" != "" ] ; then #停掉容器 docker stop $containerId #删除容器 docker rm $containerId echo "成功删除容器" fi #查询镜像是否存在,存在则删除 imageId=`docker images | grep -w $project_name | awk '{print $3}'` if [ "$imageId" != "" ] ; then #删除镜像 docker rmi -f $imageId echo "成功删除镜像" fi # 登录Harbor私服(自己手动登录harbor的话记得加上harbor的端口号,例如docker login -u lijun -p Lijun123456 192.168.0.121:85) docker login -u lijun -p Lijun123456 $harbor_url # 下载镜像 docker pull $imageName # 启动容器 docker run -di -p $port:$port $imageName echo "容器启动成功"
9)效果图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。