热门:DevOps实战系列【第八章】:详解Jenkins集成Docker私服Nexus3

2023-01-08 19:12:51 来源:51CTO博客

个人亲自录制全套DevOps系列实战教程:​​手把手教你玩转DevOps全栈技术​​


【资料图】

Jenkins集成Docker镜像仓库

docker私服已经搭建完毕,下边我们期望jenkins做的事是:

①通过git拉取代码②通过maven构建生成jar包③构建含有jar包的镜像​​④推送到docker仓库​​​​⑤通知宿主从仓库拉取镜像并启动容器​

​有什么好处?​​避免将jar包拷贝到宿主机,而是直接将jar包打入镜像上传到私服。

​为什么不是jenkins直接拉取并启动容器?​​从角色上看jenkins并不是docker服务,生产中多数是部署docker集群,所以拉取镜像并部署容器更应该由docker自身操作。

​非要用jenkins拉取和部署可以吗?​​当然可以,但jenkins容器中一直只映射单个docker宿主机的docker.sock,如果是docker集群就不好解决了,比较麻烦。

Jenkins容器编排文件修改

​注意:​​之前我们讲的都是jenkins构建完jar包后,传输到宿主机,由宿主机通过docker命令完成构建和启动容器,

此处我们期望jenkins能完成这些事,有几种方法:

在jenkins中安装docker服务或安装docker cli并连接到宿主直接将宿主机的docker内核映射给jenkins容器,让jenkins能操作宿主机的docker【推荐】
# 很简单只需要将jenkins的docker-compose.yml修改一下即可version: "3"services:  jenkins:    build:       context: .      dockerfile: Dockerfile    image: "lij/jenkins:2.346.3-2-lts-centos7"    restart: always    container_name: "jenkins"    hostname: "jenkins"    environment:      - JAVA_OPTS="-Dhudson.model.DownloadService.noSignatureCheck=true"    ports:      - "9078:8080"      - "50000:50000"    networks:      - "exist-net-bloom"    volumes:      - "/docker/jenkins/home:/var/jenkins_home"      - "/docker/jenkins/mvnrepo:/mvnrepo"         - "/etc/timezone:/etc/timezone:ro"      - "/etc/localtime:/etc/localtime:ro"      # 新增加内容      - "/var/run/docker.sock:/var/run/docker.sock"      - "/usr/bin/docker:/usr/bin/docker"      - "/etc/docker/daemon.json:/etc/docker/daemon.json"networks:  exist-net-bloom:    external:      name: devops

​解释:​​/var/run/docker.sock 是docker服务器后台进程执行docker客户端命令的服务,

不论是docker cli还是对外开放的api最终都是与/var/run/docker.sock进行交互,所以把他映射到jenkins内部,jenkins就可以内部操作宿主机了。

另外还需映射docker命令(即客户端命令)和已经配置好的配置文件。

​问题:​​​更新配置后执行docker info报权限问题

分析:这应该是执行docker.sock的权限问题,我们进入宿主机查看docker.sock的权限

​解决:​​这里有几种方式

以root用户登录jenkins,我实际我们用的是jenkins用户,这样容器导致用户权限过大,不推荐
# 修改方法只需在docker-compose.yml中增加以root登录即可user: root
将宿主机的docker.sock文件的权限改为可以让jenkins的账号ID=1000的用户使用即可​​【推荐】​​这里我们粗暴些,直接777权限
# 第一种:授权777权限chmod 777 /var/run/docker.sock# 第二种:授权other组可读写chmod o+rw /var/run/docker.sock

修改jenkins的job配置

当maven构建后,直接在jenkins工作目录构建镜像并推送镜像到私服

[此处我们以Nexus作为私服演示,对于大型项目harbor优势较大,成本相对也较高,比如内置数据库PostgreSQL,而我们一般会使用公共数据库,所以至少要有一个PostgreSQL实例,等等,而Nexus就就相对简单些,也能和Maven仓库复用,成本较低]

​注意:​​jenkins中运行shell脚本的目录是jenkins_home/workspace/jobName目录

# 注意:因为我们要构建镜像而不需要直接启动容器,所以只需要Dockerfile文件,而暂时不需要使用docker-compose.yml# 1.构建镜像的命令,我们使用docker build,通过-t指定镜像标签,最后指定Dockerfile的目录,我们使用当前目录即可docker build -t 10.10.1.199:9082/busybox:v-${branch} .# 注意:jenkins中运行shell脚本当前目录是[workspace/jobName]目录set -e \&& mv target/*.jar docker/ \  # 拷贝jar包到docker目录&& cd docker \&& docker build -t demo:v-$branch . \  # 构建镜像,使用分支名作为版本号&& docker tag demo:v-$branch 10.10.1.199:9082/demo:v-$branch \  #给镜像打标签,准备推送私服&& docker login -u admin -p jie123456 10.10.1.199:9082 \ #登录私服hosted仓库&& docker push 10.10.1.199:9082/demo:v-$branch \#推送镜像#避免构建同一个镜像产生悬空镜像,假设已经执行了当前jenkins job,那么再次执行时,本地一定是拉取了镜像,所以直接构建会产生悬空镜像&& docker image prune -f

​​​问题:​​​点击构建会报错,如下:

​​​原因:​​​就是镜像名称定义的不规范,因为我们使用的git分支名,而分支默认都是orgin/开头,而放到镜像中斜杠就不合适了,所以我们在git参数构建时,将[orgin/]过滤掉,配置如下:使用正则​​orgin/(.*)​​​即可

再次构建,就没问题了。

宿主机拉取镜像并部署容器

​解决目标:​​既然是jenkins通知宿主机,那么就可能是多个jenkins的job都有这种通知操作,

所以我们不能写死拉取镜像的信息,而是通过jenkins的通知将这些参数传递过来,比如拉取镜像的地址、镜像标识等,并且要处理镜像重复等问题。

​思路:​​怎么让jenkins给宿主传参数呢?说的比较抽象,其实我们之前已经做过了,就是ssh。

jenkins的job做完构建镜像并推送后,就可以执行ssh连接宿主,然后执行脚本,而jenkins的job中可以设置变量,在执行宿主机脚本是可以直接引用过来,而这次脚本比较多,我们就不再jenkins中罗列了,而是直接写入到shell脚本,让jenkins直接调用。

​脚本内容:​​在宿主机目录创建shell脚本, vi /share/jenkins/demo/script/publish.sh

​注意:​​脚本权限问题,我是root创建,并且jenkins中ssh也是使用的root所以没问题,大家根据自己情况去酌情处理。

# 定义变量repositoryUrl=$1jobName=$2imageVersion=$3remoteImageName=$repositoryUrl/$jobName:$imageVersion #远程私服仓库的完整镜像名称echo "remoteImageName is "$remoteImageName### 检查是否存在同名镜像运行的容器,如果存在则需要先删除,然后再删除镜像,然后再拉取新镜像,最后以新镜像启动容器#获取已启动的容器IDcontainerId=$(docker ps -a | grep -w "$remoteImageName" | awk "{print $1}")if [ "$containerId" ! = "" ]; thendocker stop $containerId && docker rm $containerIdfi#如果镜像存在同样先删除imageId=$(docker images | grep -w "$repositoryUrl/$jobName" | grep -w "$imageVersion" | awk "{print $3}")if [ "$imageId" ! = "" ]; thendocker rmi -f $imageIdfi#从私服拉取镜像:如果不允许匿名,则需要先登录,因为我已经设置允许匿名,所以可以不登录直接拉取docker login -u admin -p 123456 $repositoryUrldocker pull $remoteImageName#启动容器:我们之前启动都是拿docker-compose.yml,但此时因为在宿主机,并没有这么一个docker-compose.yml文件,#所以我们可以直接使用docker run运行容器docker run -d \-p 9099:8080 \-v /etc/timezone:/etc/timezone:ro \-v /etc/localtime:/etc/localtime:ro \--network devops \--privileged=true \--name $jobName $remoteImageName

​解释:​​其实我们jenkins容器已经拿到宿主机的docker执行权了,那这个文件不传到宿主机执行,在本地容器执行也是可以的,但由于角色不同,该脚本由docker服务器执行更合适,并且如果是docker集群那么容器执行会很麻烦。

​优化:​​​简单起见,或者说为了隔离、可扩展,我们把这个脚本文件放到springboot中,让jenkins帮我们远程拷贝到宿主机的固定目录,然后执行

参数配置:我们脚本里引用了那么几个参数,哪里来的?

jenkins我们知道可以参数化构建,比如我们用的git参数,当然他也可以指定普通参数,如下,我们在job中增加这些参数:

[当然也可以直接写到脚本执行的后边,我这里就只配一个示意一下,其他参数直接传递]

标签: 因为我们 当前目录

上一篇:世界讯息:如何利用filebeat把不同服务器上的log4j日志传输到同一台ELK服务器
下一篇:【热闻】MySQL 常用脚本