首先贴出启动命令
docker run -d \
--name jenkins2 \
--log-opt max-file=1 \
--log-opt max-size=200m \
-p 50000:50000/tcp \
-p 8080:8080/tcp \
--restart=always \
--user root \
-v /etc/localtime:/etc/localtime \
-v /data/jenkins/jenkins_home:/var/jenkins_home \
-v /data/jenkins:/data \
-v /usr/bin/docker:/usr/bin/docker \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /data/jenkins/jenkins_war:/usr/share/jenkins \
jenkins/jenkins:lts
启动命令详解:
1.关于jenkins官方镜像
docker搭建jenkins,获取官方镜像是第一步
查看docker hub https://hub-stage.docker.com/search?q=jenkins
镜像已经不维护
查看jenkins官网 https://www.jenkins.io/zh/doc/book/installing/#prerequisites
尝试下载发现 jenkinsci/blueocean 最新版本为一年半之前更新,运行之后jenkins也并非比较新的版本
就在我一筹莫展时看到这个篇:https://blog.csdn.net/weixin_44869002/article/details/133848647
使用docker search之后确认 jenkins/jenkins:lts 为官方镜像
到此,最新官方镜像找到了:jenkins/jenkins:lts
下载下来之后,更新时间也很新
2.启动命令参数详解
我jenkins通过docker部署有个原则,就是所有要用到的东西,能和主机共用尽量共用,能本地化尽量本地化存储,只是借用一个docker容器的壳,来保持jenkins运行环境的纯净和更新
大部分启动命令参数jenkins官网都有解释:https://www.jenkins.io/zh/doc/book/installing/#prerequisites
docker run -d \ #后台运行
--name jenkins2 \ #容器名称
--log-opt max-file=1 \ #限制 jenkins容器日志个数
--log-opt max-size=200m \ #限制 jenkins容器日志大小
-p 50000:50000/tcp \ #jenkins服务端口映射
-p 8080:8080/tcp \ #jenkins服务端口映射
--restart=always \ #容器自启动
--user root \ #使用root用户启动
-v /etc/localtime:/etc/localtime \ #和主机共用时区文件,保持容器内时区为CTS
-v /data/jenkins/jenkins_home:/var/jenkins_home \ #映射宿主机本地目录为jenkins存储目录
-v /data/jenkins:/data \ #映射宿主机本地文件夹到jenkins容器(储存工具类软件)
-v /usr/bin/docker:/usr/bin/docker \ #和主机共用docker文件(用于项目打包成docker镜像文件 docker build,docker push)
-v /var/run/docker.sock:/var/run/docker.sock \ #容器与Docker守护进程通信
-v /data/jenkins/jenkins_war:/usr/share/jenkins \ #存储docker运行war包,始终保持最新war包存储在本地
jenkins/jenkins:lts
-v /etc/localtime:/etc/localtime
-v /usr/bin/docker:/usr/bin/docker
-v /var/run/docker.sock:/var/run/docker.sock
这三个不多说,一个是保持容器内时区和主机一样,为CTS,一个是共用主机docker组件用于打包,还有一个是容器与Docker守护进程通信
docker 容器内的jenkins运行命令为
java -Duser.home=/var/jenkins_home -Djenkins.model.Jenkins.slaveAgentPort=50000 -Dhudson.lifecycle=hudson.lifecycle.ExitLifecycle -jar /usr/share/jenkins/jenkins.war
/var/jenkins_home:从启动命令可以看出,这是jenkins容器内部的部署目录,映射出来防止jenkins所有数据丢失
/usr/share/jenkins/:从启动命令可以看出,这是jenkins的war包存放目录,映射出来为了方便jenkins后续的升级,始终保持最新版本的war包不会丢失
容器内jdk版本为jdk17
目前jenkins jdk主推17及以上了,原来我用的老版本的镜像时,会提示这个问题
3.编译环境搭建
上面还有个映射目录没说,就是
-v /data/jenkins:/data
这个主要是用来存储编译所要用到的工具,包括,jdk,maven,nodejs等
这里采取编译服务器和Jenkins服务器共用的方式,如果构建任务很多,可以采用jenkins单独部署,然后调用编译服务器Jenkins_slave的方式
config dockerfiles为有些项目dockerfile是外置的,并没有储存在git上,这种场景所使用。当然这种方式不推荐,还是推荐dockerfile放在git上和代码一起存储的方式
下面着重说下编译环境和jenkins容器共用的配置方式
编译环境的配置在jenkins里有两种方式
一种是在jenkins里面配置:在系统管理,全局工具配置里配置,然后在搭建jenkins项目时选中对应的工具
第二中是不在jenkins里面配置:在搭建项目时直接在命令行指定工具的参数到环境变量里,作为系统临时变量使用
当然还是推荐用第一种方式,因为和jenkins集成度高,有利于平滑升级,也有利于后期修改参数:只用在全局工具配置一个地方修改就行
第二种方式不仅需要单独配置环境,一旦工具路径修改,要把所有相关的jenkins项目全部修改一遍,不推荐
(1)在jenkins里面配置
打开系统管理
打开全局工具配置
配置jdk,路径为容器内路径,可以新增多个,然后在jenkins项目里选择
配置maven,路径为容器内路径,可以新增多个,然后在jenkins项目里选择
配置nodejs,路径为容器内路径,可以新增多个,然后在jenkins项目里选择
最后在jenkins项目里引用即可
system为 jenkins容器自带的 jdk 17
nodejs版本可以在这里选
(2)不在jenkins里面配置
有两种方式:
可以在docker容器内和容器映射目录建立/usr/local/bin/软连接的方式
ln -s /data/nodejs/nodejs/bin/node /usr/local/bin/node ln -s /data/nodejs/nodejs/bin/npm /usr/local/bin/npm ln -s /data/nodejs/nodejs/bin/yarn /usr/local/bin/yarn ln -s /data/nodejs/pnpm/pnpm-linux-x64 /usr/local/bin/pnpm
另外我这里要配置pnpm的环境,jenkins里面暂时没找到,就手动下载并建立软连接,之后运行也是可行的
参照pnpm官网 https://pnpm.io/zh/installation 获得安装脚本
在脚本里获得包下载地址 https://github.com/pnpm/pnpm/releases/
建立软连接之后,运行pnpm -v
或者在jenkins任务里面指定临时环境变量
export PATH="/data/nodejs/nodejs-20/node-v20.12.2-linux-x64/bin:$PATH"
这里有个疑问,就是如果path里面包含其他版本的node,这样会生效吗,其实不用担心,path也有优先级的概念,排在前面参数优先级高,就保证了用这种方式肯定生效
参考文档:https://www.jianshu.com/p/00940e99252a
4.jenkins及容器的升级操作
(1)jenkins升级
很简单,直接在页面点升级,重启即可,由于做了war包存储空间映射本地,就是换jenkins镜像了,还是可以保持jenkins版本为最新版本
(2)jenkins镜像升级
直接拉取最新的官方镜像,删除原容器
docker有一点做的很好就是本地文件卷,删容器完全不用担心会影响到本地卷里的文件,重新启动镜像也会以本地路径覆盖容器内路径,这样就做到了删除和升级容器操作完全无影响
docker pull jenkins/jenkins:lts
docker rm -f jenkins2
重新执行启动命令
docker run -d \ --name jenkins2 \ --log-opt max-file=1 \ --log-opt max-size=200m \ -p 50000:50000/tcp \ -p 8080:8080/tcp \ --restart=always \ --user root \ -v /etc/localtime:/etc/localtime \ -v /data/jenkins/jenkins_home:/var/jenkins_home \ -v /data/jenkins:/data \ -v /usr/bin/docker:/usr/bin/docker \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /data/jenkins/jenkins_war:/usr/share/jenkins \ jenkins/jenkins:lts
由于关键数据都进行了本地化映射,所以容器运行起来就可以,无需额外操作
不过如果有些工具没有通过jenkins工具配置,需要进入新的jenkins容器,执行建立软连接操作
docker exec -it jenkins /bin/bash ln -s /data/nodejs/nodejs/bin/node /usr/local/bin/node ln -s /data/nodejs/nodejs/bin/npm /usr/local/bin/npm ln -s /data/nodejs/nodejs/bin/yarn /usr/local/bin/yarn ln -s /data/nodejs/pnpm/pnpm-linux-x64 /usr/local/bin/pnpm
docker run命令启动不利于后期维护,所以这里换成docker-compose.yml文件的方式
#name: jekins version: "3" services: jenkins: container_name: jenkins3 ports: - 50000:50000/tcp - 8080:8080/tcp restart: always user: root environment: JAVA_OPTS: "-server -Xms6144m -Xmx6144m" volumes: - /usr/bin/docker:/usr/bin/docker - /var/run/docker.sock:/var/run/docker.sock - /data/jenkins/jenkins_war:/usr/share/jenkins - /etc/localtime:/etc/localtime - /bin/zip:/bin/zip - /data/jenkins/jenkins_home:/var/jenkins_home - /data/jenkins:/data image: jenkins/jenkins:lts
日志大小限制,在/etc/docker/daemon.json文件里已经配置,所以这里就不加了
这里有个docker run命令转docker-compose.yml文件的工具,强烈推荐:https://www.composerize.com/
参考文档:https://blog.csdn.net/wohu1104/article/details/132159027
升级到最新版jenkins遇到的问题:
构建报错:Stderr: fatal: not in a git directory
一、问题描述:
使用jenkins构建一条流水线,拉取代码时,报错not in a git directory
已配置凭据。
二、问题原因:
这个安全问题在Git v2.35.2及更高版本中开始出现,Git现在检查文件夹的所有权,试图确保使用Git的文件夹的所有者与当前用户帐户的所有者相同。
三、问题解决
删除原workspace,重新执行,不报错了
查看新建的目录权限,发现为root:root,正是jenkins容器内启动jenkins进程的用户
于是把workspace这个目录变成root:root权限即可
chown -R root:root workspace
参考文档:https://blog.csdn.net/archereid/article/details/136376356
5.jenkins备份
jenkins备份的意义不言而喻,仅次于git的备份
检查jenkins目录,发现一个问题,就是占用空间最大的文件是jenkins_home里的workspace,这是用来进行编译打包的目录,这个文件夹是可以随意删除的,重新执行job就会自动生成
所以备份jenkins正好可以排除workspace目录,其他的文件占用空间还算合理,我这里就全量备份了,先看一下排除workspace后的空间
du -sh /data/jenkins --exclude="/data/jenkins/jenkins_home/workspace"
本来想先压缩再拷贝到其他目录
tar -zcvf jenkins.tar.gz /data/jenkins --exclude="/data/jenkins/jenkins_home/workspace"
问题来了,由于jenkins文件很多,压缩一下2小时,后面还要传输,考虑到再解压2小时,这对容灾恢复是不能容忍的
再看jenkins文件的压缩率也不高,就显得压缩没太大意义了
一番权衡之后,我觉得最佳的备份方式还是用rsync工具不压缩直接传输文件(忽略掉workspace目录),由于在内网传输速度很快,再加上rsync的特性:支持增量备份,后面备份只传输增量修改的内容,没修过的不传输,节省备份空间和时间,堪称完美!
下面是我的备份脚本
#!/bin/bash datetime=`date +%Y%m%d-%H-%M-%S` logfile=$1 echo "$datetime Rsync backup start " > $logfile rsync -e "ssh -p22" -avpgolr --delete --exclude="jenkins_home/workspace" /data/jenkins bigtree@10.30.30.6:/data/backup_data/bigtree/10.30.30.8/ >> $logfile 2>&1 ret=`tail -n 1 $logfile |grep "total size"|wc -l` if [ "$ret" = 1 ] ; then echo "$datetime Rsync backup finish " >> $logfile else echo "$datetime Rsync backup failure ,pls sendmail" >> $logfile fi
这里有一点要说明
1.使用rsync,首先要在源文件机器上做备份机器的对等性认证,实现免登录
2.rsync的排除文件参数 --exclude必须要使用相对路径!这点很重要,相对路径是相对于备份路径 /data/jenkins 的相对路径
rsync --exclude 使用参考文档:https://www.cnblogs.com/xzongblogs/p/14326948.html
新建corntab定时任务,每天晚上12点备份
备份脚本在同步目录里(/data/jenkins),保证了jenkins备份脚本和日志也同样有备份
0 0 * * * /bin/bash -x /data/jenkins/backup/script/jenkins_rsync.sh /data/jenkins/backup/jenkins_rsync.log > /data/jenkins/backup/jenkins_cron.log 2>&1
-----------------------------------------------------------------------------实践分割线--------------------------------------------------------------------------------------------------------
刚通过docker搭了个jenkins用于生产环境发版,踩了一些坑,特意做个总结
业务背景:
1.生产环境和测试环境网络隔离
2.生产环境访问不了外网
3.生产环境开了一个网络白名单,只能连接测试环境的一台主机ssh端口,用于拉取发版文件
根据以上特点,综合考虑安全因素,这里采用在测试环境jenkins打包,生产环境jenkins部署的方式
搭建遇到的问题:
1.离线导入jenkins镜像
由于生产机器连不了外网,所以这里只能在测试环境docker主机上拉取jenkins镜像,打包上传到生产环境机器,再导入镜像
-----测试环境
#拉取jenkins镜像 docker pull jenkins/jenkins:lts #查看镜像 docker images #打包 docker save -o jenkins_save.tar 镜像id
------生产环境
#导入镜像 docker load -i jenkins_save.tar #查看镜像 docker images
#给镜像打tag
docker tag 镜像id jenkins/jenkins:lts
至此jenkins镜像导入生产环境
2.docker run命令转docker-compose
之前测试环境的jenkins是通过docker run命令启动的,不利于后期维护,这次直接转成docker-compose的方式
但是docker run命令涉及的参数比较多,所以我在想有没有一个工具可以转换的,还真找到了!
工具地址:https://www.composerize.com/
参考文档:https://blog.csdn.net/wohu1104/article/details/132159027
经过一番修改,docker-compose.yml文件如下
version: "3" services: jenkins: container_name: jenkins ports: - 50000:50000/tcp - 1000:8080/tcp restart: always user: root volumes: - /usr/bin/docker:/usr/bin/docker - /var/run/docker.sock:/var/run/docker.sock - /etc/localtime:/etc/localtime - /data/servers:/data/servers - /home/webs:/home/webs - /root/.ssh:/root/.ssh - /usr/bin/rsync:/usr/bin/rsync - /usr/lib64/libpopt.so.0:/usr/local/lib/libpopt.so.0 - ./jenkins_home:/var/jenkins_home - ./:/data/tools image: jenkins/jenkins:lts
3.离线导入jenkins插件
这个一开始我乐观了,以为在页面上导入即可
从官网下载插件:https://updates.jenkins-ci.org/download/plugins/,再从页面导入
后来发现不是这么回事,jenkins插件是互相调用的,有时候装一个插件要连续下10个插件,才能解决依赖关系
我知道jenkins有离线下载工具,但是下载的插件清单我也不清楚,而且还要搭建运行工具的环境,比较繁琐
就在我一筹莫展的时候看到这个:https://www.jianshu.com/p/f94405ea7727
方式二:
先在有外网环境的机器上安装jenkins
,在线安装需要的插件后,将.jenkins/plugins
目录覆盖,重启即可完成插件安装。
对啊,我在外网搭建一个一样的jenkins,安装完需要的插件,再把目录拷贝至生产环境覆盖,不就行了!
docker搭建服务本就简单
于是开始操作
jenkins启动完,解锁之后
直接选择默认方式安装插件,这样会把所有常用的插件都装上,包括语言汉化等等
接着要选择jenkins远程部署必须的插件,这里我蒙圈了,不过还好理出了头绪
远程安装最常用的方式就是通过ssh,于是我搜索ssh插件
这里ssh插件是 build步骤的 Execute shell script on remote host using ssh,官方已经不推荐使用了,有安全隐患,而且6年没更新了,直接pass
ssh server插件实际上是把jenkins作为ssh服务器,执行jenkins cli命令,暂时用不到
ssh agent插件意思是说允许你通过ssh私钥的方式调用ssh 节点,具体怎么用还没搞明白
https://plugins.jenkins.io/ssh-agent/
最符合jenkins通过ssh方式远程部署服务的插件是 Publish Over SSH
安装上即可
最后打包外网机器上的plugins目录上传至生产对应目录覆盖,插件安装完成。
4.拉取远程主机文件
这个问题困扰了我好久,jenkins没有现成可用的工具可以满足拉取远程服务器文件的功能
只能自己写脚本了,一开始想通过sftp命令,略显复杂,最终还是决定用 rsync命令拉取文件
rsync命令的配置又踩了不少坑。。
(1)docker共用主机对等性认证文件
首先在宿主机上配置到远程文件服务器的对等性认证
在测试环境服务器上新建package账号,赋权本地路径
#新建账号
useradd -m -s /bin/bash package
#设置密码
passwd package
#配置路径权限
chown -R package:package /data/jenkins/package
注意:这个路径同时为 测试机器上 jenkins容器映射路径,方便打包完之后,拷贝过来
在生产机器上执行
#生成密钥文件,输入完一直回车
ssh-keygen
#拷贝公钥文件到服务器主机上
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 端口 package@10.30.30.6
#测试是否配置成功,可以免密登陆,即登陆成功
ssh package@10.30.30.6 -p 端口
到这里还没完,生产宿主机配置了对等性验证,还需要映射到jenkins容器中供容器使用
- /root/.ssh:/root/.ssh
配置映射到docker-compose.yml,启动并进入jenkins容器,再执行ssh命令测试,可以免密登陆了!
(2)共用主机rsync组件
jenkins容器内,并没有安装rsync命令,而生产环境连不了外网,所以最好的方式是让jenkins容器共用主机rsync组件
在生产宿主机上安装rsync,因为生产环境有单独的yum源
#在生产宿主机上安装rsync yum install rsync -y
#测试rsync是否正常,这里测试连的是那台测试环境机器,指定了ssh端口,可以正常拉取文件即可
rsync -e "ssh -p端口" -av --delete package@ip:/data/jenkins/package/xxx/ /data/servers/xxx/jars
映射rsync到jenkins容器
- /usr/bin/rsync:/usr/bin/rsync
把文件映射到jenkins容器内,重启容器后,本以为完事了,结果又报错了
#进入jenkins容器 docker exec -it jenkins /bin/bash #执行rsync命令 rsync
报错:rsync: error while loading shared libraries: libpopt.so.0: cannot open shared object file: No such file or directory
很明显缺少libpopt.so.0依赖,但是又不能连外网,安装不了啊
于是我就想既然rsync可以映射到jenkins容器内,libpopt.so.0应该也可以
于是开始查找宿主机上的libpopt.so.0文件
find / |grep libpopt.so.0
还真找到了
参考文档:https://blog.csdn.net/keeprunper/article/details/131896833
于是映射到jenkins容器
- /usr/lib64/libpopt.so.0:/usr/lib64/libpopt.so.0
本以为解决的时候,进入容器运行rsync命令还是报错
就这我一筹莫展的时候看到这篇文章:https://blog.csdn.net/qq_15821487/article/details/122985321
在/etc/ld.so.conf.d/目录下加入的任何以.conf为后缀的文件都能被识别到
我在容器内看了下,/usr/loacal/lib天生就有,我把libpopt.so.0放到这里是不是就可以
于是开始重新映射
- /usr/lib64/libpopt.so.0:/usr/local/lib/libpopt.so.0
然后再重新进入容器,运行 ldconfig 命令,以更新配置文件;
#进入容器 docker exec -it jenkins /bin/bash #运行ldconfig ldconfig #测试rsync rsync
这个时候,rsync可用了,我可真是天才~
至此 jenkins容器内部rsync命令配置完成,jenkins通过ssh拉取远程主机的功能得以实现!
同样的,容器要用到zip命令也可以通过同样文件映射的方式共用宿主机的zip
- /bin/zip:/bin/zip
5.共用主机相关的文件
就是在jenkins宿主机上有些服务需要jenkins部署,jenkins容器需要读取到他们的目录
- /data/servers:/data/servers
- /home/webs:/home/webs
6.zip压缩多个文件,解压时不包含目录层级
这个问题也比较棘手,就是前端打包出的文件是dist,压缩时不想带上dist目录
一开始想到的时用
zip -rj
-j : 只储存文件的名称,不含目录。
但是会有问题,就是dist目录里还是有文件夹的,打包的时候需要带上,用 -j内部文件夹也没了
看来需要其他办法,好在找到了
假设我们有个目录叫 dev,dev中有很多文件,我们想要将dev中的文件打包,名字可能叫dev.zip,但当我们解压的时候,不想要解压生成一个dev目录,想要直接解压在当前目录,这样如何压缩呢
# 进入dev目录 $ cd dev $ zip -r ../dev.zip * # 这时候一个dev.zip文件就已经被打包在dev目录上一级目录中了
以上!
参考文档:https://blog.csdn.net/w_monster/article/details/120314597