3

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

 1 year ago
source link: https://blog.51cto.com/u_15909716/5996675
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

DevOps实战系列【第八章】:详解Jenkins集成Docker私服Nexus3_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报权限问题

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

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

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

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

  • 以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
DevOps实战系列【第八章】:详解Jenkins集成Docker私服Nexus3_nexus_04

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

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

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

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

再次构建,就没问题了。

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

​解决目标:​​既然是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=$1
jobName=$2
imageVersion=$3
remoteImageName=$repositoryUrl/$jobName:$imageVersion #远程私服仓库的完整镜像名称
echo "remoteImageName is "$remoteImageName

### 检查是否存在同名镜像运行的容器,如果存在则需要先删除,然后再删除镜像,然后再拉取新镜像,最后以新镜像启动容器

#获取已启动的容器ID
containerId=$(docker ps -a | grep -w "$remoteImageName" | awk '{print $1}')
if [ "$containerId" ! = "" ]; then
docker stop $containerId && docker rm $containerId
fi

#如果镜像存在同样先删除
imageId=$(docker images | grep -w "$repositoryUrl/$jobName" | grep -w "$imageVersion" | awk '{print $3}')
if [ "$imageId" ! = "" ]; then
docker rmi -f $imageId
fi

#从私服拉取镜像:如果不允许匿名,则需要先登录,因为我已经设置允许匿名,所以可以不登录直接拉取
docker login -u admin -p 123456 $repositoryUrl
docker 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帮我们远程拷贝到宿主机的固定目录,然后执行

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

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

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

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

DevOps实战系列【第八章】:详解Jenkins集成Docker私服Nexus3_nexus_08
DevOps实战系列【第八章】:详解Jenkins集成Docker私服Nexus3_docker_09

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK