Jenkins+Gitlab自动化部署
使用场景
测试环境频繁发布让人烦不胜烦,干脆自己搭建一个自动化部署的应用,一劳永逸!
整个发布流程是,程序员提交gitlab代码后,gitlab webhook通知Jenkins触发构建Jenkins使用maven打包过后,然后Jenkins登录远程SSH服务器,执行Jar包启动
准备工作
安装步骤
一、下载Jenkins镜像文件
启动Docker后,下载Jenkins镜像文件
1
| docker pull jenkins/jenkins
|
二、创建挂载目录
创建Jenkins挂载目录并授权权限
1 2 3 4 5 6
| mkdir -p /root/docker/data/jenkins/jenkins_home
chmod 777 /root/docker/data/jenkins/jenkins_home
|
三、创建并启动Jenkins容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| docker run \ -d \ -p 8777:8080 \ -p 50000:50000 \ --name jenkins \ --restart always \ -v /root/docker/data/jenkins/jenkins_home:/var/jenkins_home \ jenkins/jenkins
docker run -d -uroot -p 8777:8080 -p 50000:50000 --name jenkins \ -v /root/docker/data/jenkins/jenkins_home:/var/jenkins_home \ -v /home:/home \ -v /etc/localtime:/etc/localtime \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker \ jenkins/jenkins
|
-d 后台运行镜像
-p 8777:8080 将镜像的8080端口映射到服务器的8777端口。
-v /root/docker/data/jenkins/jenkins_home:/home/jenkins_home
/root/docker/data/jenkins/jenkins_home
目录为容器jenkins工作目录映射至服务器的目录,我们将目录挂载到这个位置,方便后续更新镜像后继续使用原来的工作目录。/var/jenkins_home
容器jenkins的工作目录
–name jenkins 给容器起一个别名
四、查看Jenkins是否启动成功
执行命令,出现如下图状态 Up
说明启动成功
五、查看容器日志
六、配置镜像加速
1
| cd /root/docker/data/jenkins/jenkins_home/
|
修改 vim hudson.model.UpdateCenter.xml里的内容
修改前
将 url 修改为 清华大学官方镜像:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
修改后
七、访问Jenkins
输入你的服务器IP+端口, 本篇示例端口为:8777
管理员密码获取方法,编辑initialAdminPassword文件查看,把密码输入登录中的密码即可,开始使用
编辑 vim /root/docker/data/jenkins/jenkins_home/secrets/initialAdminPassword
至此,安装篇完成
Jenkins基本配置
进入网址后会有新手引导,我们依次操作
选择安装推荐的插件
>>> 创建管理员账户
>>> 实例配置选择保存并完成
这一步非常慢,需要耐心等待
时区配置
设置Jenkins时区为北京时间
Manage Jenkins
>>> Script Console
>>> 执行脚本
1
| System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Shanghai')
|
安装构建和部署所需的插件
- 安装SSH插件和Publish Over SSH插件
Publish Over SSH:因为我的项目是使用 Docker 启动 Springboot 服务;所以在 Jenkins 需要操作目标服务器,进行Dockerfile 启动
最后安装完所有插件后,重启一下Jenkins(也可以命令重启)
手动重启,直接使用URL重启即可:http://192.168.124.51:8777/restart
,点击是,进行重启
添加凭证
这里就是相当于配置一些 ssh,gitlab等 连接的凭证
Jenkins密钥连接时的,凭证密钥生成
首先进入Jenkins容器内部,用 Jenkins 用户名登录
docker exec -it -u jenkins jenkins /bin/bash
进入 /var/jenkins_home 目录
cd /var/jenkins_home/
生成密钥,一路回车,生成文件路径 /var/jenkins_home/.ssh
ssh-keygen -t rsa -C "[email protected]"
将id_rsa.pub 放入到gitee或者gitlab或者github的ssh密钥中
然后在Jenkins配置私钥
配好了需要在后台 /var/jenkins_home/workspace
目录手动拉取一下远程仓库,会自动在.ssh目录生成 known_hosts
文件
拉取完了就可以配置jinkins进行创建任务了
输入 ssh 连接的相关账户密码
配置SSH remote hosts
注:配置SSH连接Dockerfile所在服务器的相关信息,并添加凭证,最后测试连接并保存;
全局工具配置
JDK配置
需要登录 Orcale账号密码
账号 1 :[email protected]密码:1211WaN!
账号 2 :[email protected]密码:1211WaN!
账号 3 :[email protected]密码:1211WaN!
账号 4 :[email protected]密码:1211WaN!
账号 5 :[email protected]密码:1211WaN!
账号 6 :[email protected]密码:1211WaN!
Git 安装配置
还是当前页面,配置git
MAVEN安装配置
手动安装
maven官方下载地址如下:(注意:maven下载地址翻到本文最下面)
https://maven.apache.org/download.cgi
下载后上传至服务器,解压后修改conf中 setting文件后复制到docker容器中
- 解压
tar -xzvf apache-maven-3.8.6-bin.tar.gz
- 修改
apache-maven-3.8.6/conf/settings.xml
配置仓库加速
1 2 3 4 5 6 7 8 9 10
| <localRepository>/var/jenkins_home/repository</localRepository>
<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror>
|
- 复制到docker
docker cp /root/docker/data/apache-maven-3.8.6 jenkins:/maven/
- 进入容器后,创建软连接
ln -s /maven/bin/mvn /usr/local/bin/
- 执行
mvn -v
查看版本
手动安装下,需要Jenkins配置MAVEN环境
- 系统环境配置
- tool配置
Docker 安装配置
Jenkins任务创建
1. 新建maven项目任务
2. 配置git仓库
【内网推荐使用Http地址去clone项目】
3. 配置Build Triggers 构建触发器
【构建触发器中配置,会获取到URL和Token,这两个东西需要记录下来,供gitlab配置webhook使用】
记录URL
1
| http://192.168.92.130:8777/project/iotBuild
|
URL和Token都需要填写到GitLab中,去配置webhooks!!!
点击高级后,最下方可以点击生成Token
记录Token
1
| 120e649995c048afe3c981507dcad71a
|
4. 构建环境的配置根据自己需求配置
5. Pre Steps 配置前置步骤
可以在拉取代码后和maven构建前执行的步骤
一般用于解决清理空间或者手动执行一些git命令
6. Build 构建的配置
jenkins构建项目,本处是以maven插件实现的。
因此,配置
1
| clean package -Dmaven.test.skip=true -T 6
|
-T
为可选,服务器配置高可以搞上,代表多线程并行编译,速度更快
-T 6
:代表用6个线程构建
-T 6C
:代表根据CPU核数分配线程(1核分配6个线程)
7. Post Steps 即jenkins构建完成后一步配置
【本处选择,只在jenkins构建成功后,再执行这一步】
【因为最后的构建成功的maven项目的jar包是以docker启动服务为目的,所以最后的docker操作,一定是在jenkins容器以外的服务器上运行的,可能是本机宿主机,也可能是远程的服务器】
【所以本处选择,在远程的SSH执行shell脚本】
【因此,必须要求,Publish Over SSH插件以及它的相关配置】
shell命令如下:
Docker方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| #===================================================================================== #=================================定义初始化变量====================================== #=====================================================================================
#操作/项目路径(Dockerfile存放的路径),一般是项目中写的Dockerfile BASE_PATH=/root/docker/data/jenkins/jenkins_home/workspace/iot_build_docker
# jenkins构建好的源jar路径 SOURCE_PATH=/root/docker/data/jenkins/jenkins_home/workspace/iot_build_docker
#【docker 镜像】【docker容器】【Dockerfile同目录下的jar名字[用它build生成image的jar]】【jenkins的workspace下的项目名称】 #这里都以这个命名[微服务的话,每个服务都以iot-project这种格式命名] #注意统一名称!!!!! SERVER_NAME=iot-standalone
#容器id [grep -w 全量匹配容器名] [awk 获取信息行的第一列,即容器ID] [无论容器启动与否,都获取到] CID=$(docker ps -a | grep -w "$SERVER_NAME" | awk '{print $1}')
#镜像id [grep -w 全量匹配镜像名] [awk 获取信息行的第三列,即镜像ID] IID=$(docker images | grep -w "$SERVER_NAME" | awk '{print $3}')
#源jar完整地址 [jenkins构建成功后,会在自己的workspace/项目/target 下生成maven构建成功的jar包,获取jar包名的完整路径] #例如:/root/docker/data/jenkins/jenkins_home/iotBuild/iot-standalone/target/iot-standalone.jar SOURCE_JAR_PATH=$(find "$SOURCE_PATH/$SERVER_NAME/target/" -name "*$SERVER_NAME*.jar" )
DATE=`date +%Y%m%d%H%M%S`
#===================================================================================== #============================对原本已存在的jar进行备份================================ #=====================================================================================
# 备份 # function backup(){ # if [ -f "$BASE_PATH/$SERVER_NAME.jar" ]; then # echo "=========================>>>>>>>$SERVER_NAME.jar 备份..." # mv $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar # echo "=========================>>>>>>>备份老的 $SERVER_NAME.jar 完成" #
# echo "=========================>>>>>>>老的$BASE_PATH/$SERVER_NAME.jar不存在,跳过备份" # fi # }
#===================================================================================== #=========================移动最新源jar包到Dockerfile所在目录========================= #=====================================================================================
# 查找源jar文件名,进行重命名,最后将源文件移动到项目环境 function transfer(){ echo "=========================>>>>>>>源文件完整地址为 $SOURCE_JAR_PATH"
echo "=========================>>>>>>>重命名源文件" mv $SOURCE_JAR_PATH $SOURCE_PATH/$SERVER_NAME/target/$SERVER_NAME.jar
echo "=========================>>>>>>>最新构建代码 $SOURCE_PATH/$SERVER_NAME/target/$SERVER_NAME.jar 迁移至 $BASE_PATH" cp $SOURCE_PATH/$SERVER_NAME/target/$SERVER_NAME.jar $BASE_PATH
echo "=========================>>>>>>>迁移完成Success"
}
#===================================================================================== #==================================构建最新镜像======================================= #=====================================================================================
# 构建docker镜像 function build(){ #无论镜像存在与否,都停止原容器服务,并移除原容器服务 echo "=========================>>>>>>>停止$SERVER_NAME容器,CID=$CID" docker stop $CID
echo "=========================>>>>>>>移除$SERVER_NAME容器,CID=$CID" docker rm $CID
#无论如何,都去构建新的镜像 if [ -n "$IID" ]; then echo "=========================>>>>>>>存在$SERVER_NAME镜像,IID=$IID"
echo "=========================>>>>>>>移除老的$SERVER_NAME镜像,IID=$IID" docker rmi $IID
echo "=========================>>>>>>>构建新的$SERVER_NAME镜像,开始---->" cd $BASE_PATH docker build -t $SERVER_NAME . echo "=========================>>>>>>>构建新的$SERVER_NAME镜像,完成---->"
else echo "=========================>>>>>>>不存在$SERVER_NAME镜像,构建新的镜像,开始--->"
cd $BASE_PATH docker build -t $SERVER_NAME . echo "=========================>>>>>>>构建新的$SERVER_NAME镜像,结束--->" fi }
#===================================================================================== #==============================运行docker容器,启动服务=============================== #=====================================================================================
# 运行docker容器 function run(){ # backup transfer build
docker run --name $SERVER_NAME -itd --net=host -e TZ=Asia/Shanghai $SERVER_NAME } #入口 run
|
Dockerfile
1 2 3 4 5 6 7 8
| FROM openjdk:11 MAINTAINER [email protected] COPY ./iot-standalone.jar mydockerapp.jar
RUN echo 'Asia/Shanghai' >/etc/timezone
EXPOSE 8088 CMD ["java", "-jar", "mydockerapp.jar", "--spring.profiles.active=test"]
|
纯 Jar包 启动方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #操作/项目路径,选择你自己喜欢的路径,比如平时方便看控制台日志啥的 BASE_PATH=/root/app
# jenkins构建的项目路径 SOURCE_PATH=/root/docker/data/jenkins/jenkins_home/workspace/iotBuild
#Jar服务名称 SERVER_NAME=iot-standalone
#源jar完整地址 [jenkins构建成功后,会在自己的workspace/项目/target 下生成maven构建成功的jar包,获取jar包名的完整路径] #例如:/root/docker/data/jenkins/jenkins_home/workspace/iotBuild/iot-standalone/target/iot-standalone.jar SOURCE_JAR_PATH=$(find "$SOURCE_PATH/$SERVER_NAME/target/" -name "$SERVER_NAME.jar" )
DATE=`date +%Y%m%d%H%M%S`
#===================================================================================== #============================对原本已存在的jar进行备份================================ #=====================================================================================
# 备份 function backup(){ if [ ! -d "$BASE_PATH/backup/" ];then mkdir $BASE_PATH/backup else echo "$BASE_PATH/backup/ 文件夹已经存在,不需要创建" fi if [ -f "$BASE_PATH/$SERVER_NAME.jar" ]; then echo "=========================>>>>>>>$SERVER_NAME.jar 备份..." mv $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar echo "=========================>>>>>>>备份老的 $SERVER_NAME.jar 完成"
else echo "=========================>>>>>>>老的$BASE_PATH/$SERVER_NAME.jar不存在,跳过备份" fi }
#===================================================================================== #=========================移动最新源jar包到BASE_PATH所在目录========================= #=====================================================================================
# 查找源jar文件名,进行重命名,最后将源文件移动到项目环境 function transfer(){ echo "=========================>>>>>>>源文件完整地址为 $SOURCE_JAR_PATH"
echo "=========================>>>>>>>重命名源文件" mv $SOURCE_JAR_PATH $SOURCE_PATH/$SERVER_NAME/target/$SERVER_NAME.jar
echo "=========================>>>>>>>最新构建代码 $SOURCE_PATH/$SERVER_NAME/target/$SERVER_NAME.jar 迁移至 $BASE_PATH" cp $SOURCE_PATH/$SERVER_NAME/target/$SERVER_NAME.jar $BASE_PATH
echo "=========================>>>>>>>迁移完成Success"
}
#===================================================================================== #==============================运行Jar服务,启动服务=============================== #=====================================================================================
function runjar() { #无论JAR服务存在与否,都停止服务 echo "=========================>>>>>>>停止$SERVER_NAME服务" kill -9 `cat $BASE_PATH/PID`
echo "=========================>>>>>>>启动$SERVER_NAME服务" nohup java -jar $BASE_PATH/$SERVER_NAME.jar "--spring.profiles.active=test" >> $BASE_PATH/log.log 2>&1 & echo "$!" > $BASE_PATH/PID }
# 运行JAR服务 function run(){ backup transfer runjar } #入口 run
|
附上一个参考的完整配置
8.GitLab配置
打开gitlab,并进入要自动部署的项目,点击左侧设置(setting)
将从jenkins获取到的URL和Token,填写在此处
【根据自己的需求,勾选webhook的触发事件都有哪些】
最后点击添加
添加后,即可在下方看到刚刚添加的webhook!!
然后即可点击Test,选择刚刚勾选的绑定的触发事件 ,即可回到jenkins查看测试效果!!!
问题解决
git 拉取源码出现 Host key verification failed.
在jenkins里面-系统设置-系统信息里面有个user.name可以看下当前jenkins使用的用户是什么,很多朋友以为默认就是jenkins用户。我们并没有为jenkins生成过密钥对,也没有将他的公钥拷到目标服务器上.
解决方案
进入jenkins的容器,创建一个用于登录的公私钥,然后将公钥分配给远程主机即可
- 生成公私密钥,做免密登录
1 2 3 4 5
| docker exec -it jenkins /bin/bash
ssh-keygen -t rsa
|
- gitlab配置公钥
查看公钥复制到gitlab
- 收到clone一下项目测验
拉取项目测试一下
- Jenkins设置SSH私钥
查看私钥
Jenkins配置
- 在任务配置源码这里,选择 ssh git方式
再次执行就能成功拉取了
git多个子仓库拉取不到最新代码
Jenkins 默认拉取多子仓库代码语句为 git submodule update --init --recursive
问题原因:
git submodule update获取代码的时候是和子工程的git路径和这里的commit id有关联的,获取的就是对应的git路径下截止这个commit id的所有代码,之后的代码是不会获取到的。
解决方案
在 Jenkins中配置前置shell
命令语句
1
| git submodule foreach git checkout master && git submodule foreach git pull origin master
|