jenkin安装下载
大致流程说明:
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)
sudo service sshd start //开启sshd服务
ssh [email protected]
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 [email protected]
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 "容器启动成功"