写在最前

终于来到jenkins篇章了它是即强大又难以掌握啊,那么跟随我拿下它让它来做非常有意思的工作。

1. 前置要求

  1. 构建高效DNS环境:Docker 部署 dnsmasq 实战

  2. Jenkins:jenkins-slave-maven3.6

2. 部署流程

2.1 docker 部署

按流程部署,再 docker logs jenkins 查看密码即可,然后选择推荐插件安装

# 删除容器和数据目录
docker rm -f jenkins && rm -rf /data/jenkins

# 创建数据目录和更改访问权限
mkdir -p /data/jenkins && chown -R 1000 /data/jenkins

# 部署容器
docker run --name jenkins -d \
  -p 8080:8080 \
  -p 50000:50000 \
  --restart=always \
  -v /data/jenkins:/var/jenkins_home \
  jenkins/jenkins:2.478-jdk17

2.2 域名配置

2.2.1 1panel 域名配置

  1. 主域名:jenkins.dev.tanqidi.com

  2. 代理地址:172.31.0.100:8080

image-sltq.png

2.2.2 dnsmasq 域名解析

address=/jenkins.dev.tanqidi.com/172.31.0.100

2.3 安装插件

  1. Locale,部署上jenkins会发现只有部分汉化这个插件可以让你控制Jenkins的语言,安装此插件能最大限度完成汉化。

  2. CloudBees Docker Build and Publish,这个插件可以构建基于Dockerfile的项目,以及将构建的镜像推送到指定镜像仓库中。

  3. CloudBees Docker Custom Build Environment,流水线调用docker必备插件,用于构建环境参数。

  4. Parameterized Trigger,该插件允许你在构建完成时触发新的构建任务,并通过各种方式为新构建指定参数。

image-lxzy.png

image-iuin.png

image-pbgi.png

2.4 配置汉化

安装完 Local 插件后即可配置汉化,因为jenkins更新的太快了汉化组只能兼容大部分并非完全汉化。

image-macr.png

记住!!!选择第一个英语,不要选择中文!!!

image-xuzu.png

在jenkins入口访问/restart即可出现重启按钮,重启后jenkins即可实现汉化

image-okso.png

image-zmek.png

2.5 密码修改

image-wosz.png

image-eyui.png

image-wspo.png

3. 操作流程

3.1 创建秘钥

在流水线自动化执行过程中,访问Git仓库和Harbor容器仓库等资源时,需要使用凭证进行身份验证。为了确保流程顺畅,我们需要提前准备并配置好这些访问密钥。

图片-gugu.png

图片-mkdk.png

图片-ydce.png

图片-xjbc.png

3.2 基础操作

3.2.1 创建流水线

在主页点击 + 创建一个名称为 devops-java-sample 项目

https://gitee.com/tanqidi/devops-java-sample,你可以到这里Forked一份代码到你的gitee仓库中。

图片-hzot.png

图片-dxrh.png

图片-phzc.png

3.2.2 限制项目的运行节点

这里你要先看 前置要求2 构建Maven3.6的slave节点才会出现此选项,如果你没有任何slave节点这里是不会出现的,我就选择build-maven3.6作为构建环境

图片-mbvg.png

3.2.3 git源码配置

图片-vgdo.png

3.2.4 添加构建步骤 shell

Build Steps 添加一个步骤为 shell,粘贴以下命令最终你可以看到我成功通过mvn构建源码得到 devops-sample-0.0.1-SNAPSHOT.jar

# 查看当前路径
pwd
# 列表文件
ls -l
# 使用build-maven3.6当前环境的mvn构建java源码
mvn clean package -Dmaven.test.failure.ignore=true -DskipTests=true -U
# 构建完成后当前目录会多一个target目录,里面就是最终具体的可运行jar包了
ls -l target

# 这个项目看起来有点特殊他不叫Dockerfile而是叫Dockerfile-on-prem,所以我们这里需要使用cp命令将它复制一份名为Dockerfile,即可为 Docker Build and Publish 步骤识别
cp Dockerfile-on-prem Dockerfile
# 列表文件
ls -l

图片-uijn.png

图片-uotc.png

图片-vogg.png

3.2.5 添加构建步骤 Docker Build and Publish

这里用到了 CloudBees Docker Build and Publish 这个插件

Build Steps 添加一个步骤为 Docker Build and Publish

因为这个插件的 " Registry credentials " 是不支持私有仓库的,所以我们需要在构建环境中选择凭证让他注入环境变量中来用让shell来使用,按此操作进行完再构建,可以看到我顺利构建出镜像并且推送到aliyun了。

图片-upda.png

#!/bin/bash
echo "登录私有镜像仓库"
echo ${HARBOR_ACCESS_PWD} | docker login registry.cn-hangzhou.aliyuncs.com --username ${HARBOR_ACCESS_USER} --password-stdin

图片-fdgl.png

图片-axgz.png

图片-oijl.png

3.2.6 调用部署任务

请先看高级操作 添加构建后操作 Trigger parameterized build on other projects,具体这些变量名什么含义你去 Pipeline Script 看看

deploy_node_name=build-maven3.6
application_name=devops-java-sample
container_name=devops-java-sample
image_name=tanqidi-temp/devops-java-sample
tag_name=dev_latest

图片-phjp.png

在构建完成后,devops-java-sample成功调用了deploy-app,并传递了相关参数,脚本顺利执行,应用成功部署至容器。然而,你可能会疑惑,应用部署到了哪里?让我来澄清一下:我的build-maven3.6节点运行在IP地址为172.31.0.123的宿主机上。这意味着该宿主机的Docker服务被挂载进了容器,脚本中调用的Docker正是位于172.31.0.123的Docker实例。只需访问172.31.0.123:8080,你就能看到应用成功运行的界面。

图片-lzjy.png

图片-loca.png

图片-rcbz.png

3.3 高级操作

我们需要为流水线解耦,简单来说就是A流水线操作完了传递期望的参数给到B流水线让他来运行后续的东西,这样的好处就是剥离开一些公共的部分,这样流水线一多就会非常容易操作,如果不拆分那么所有操作都在一个流水线里面,流水线一多就不好修改了。

3.3.1 其他工程构建后触发

这里用到了 Parameterized Trigger 这个插件

现在 devops-java-sample 已经完成了编译jar与构建镜像了,就差最后一步的部署容器运行起来了,这个部分我们就可以拆分开命名为 deploy-app,接受一些必须的参数然后让它来拉取镜像部署到主机上,这样后续所有的流水线都可以在最后的步骤调用这个任务来完成。

  1. 创建流水线

  2. 参数化构建过程,全部都是字符参数:application_name,deploy_node_name,container_name,image_name,tag_name

  3. Pipeline Script,注意!我的阿里云harbor凭证是aliyun-harbor-secret,要将你的粘贴替换上去,如果不存在会报错。

node("${deploy_node_name}") {
    try {
        timestamps {
            stage("Deploy ${application_name} to ${deploy_node_name}") {
                withCredentials([usernamePassword(credentialsId: 'aliyun-harbor-secret', passwordVariable: 'HARBOR_ACCESS_PWD', usernameVariable: 'HARBOR_ACCESS_USER')]) {
                    sh """
                    echo ${HARBOR_ACCESS_PWD} | docker login registry.cn-hangzhou.aliyuncs.com --username '${HARBOR_ACCESS_USER}' --password-stdin
                    docker pull registry.cn-hangzhou.aliyuncs.com/${image_name}:${tag_name}
                    docker rm -f -v ${container_name} || echo "${container_name} container not exist"
                    sleep 5s
                    docker run -d --name ${container_name} --dns=172.31.0.53 --net host -v /data/${container_name}:/data -v /log/${container_name}:/log registry.cn-hangzhou.aliyuncs.com/${image_name}:${tag_name}
                    """
                }
            }
        }
    } catch (err) {
        // Handle exception
		echo "Error during deployment: ${err}"
    }
}

图片-dmzm.png

图片-nqyf.png

图片-xcoy.png

图片-wxrr.png

4. 异常解决

4.1 permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

4.1.1 临时

这是因为挂载进去的 /var/run/docker.sock 的权限,和容器内的执行者权限不一样所以出现了无法调用docker.sock的问题,最快速的就是在宿主机上对docker.sock做权限变更,但这只是临时的,下次重启docker这个sock文件会被重建权限就又丢失了。

chmod 666 /var/run/docker.sock

4.1.2 永久

本质上,就是要确保确保Docker运行的组和Jenkins slave运行的组一致。具体步骤如下:

  1. 我在docker-install.sh中优化了一下,先创建一个GID为999docker组,这样在生成docker.service文件时,docker.sock会自动拥有该组的权限。

  2. 在Jenkins slave的Dockerfile中,同样创建一个名为docker、GID为999的组。

  3. 将Jenkins用户(即容器中的运行用户)添加到这个docker组中。

通过这种方式,Jenkins slave在调用docker.sock时,其GID为999,与宿主机一致,从而获得必要的权限。这样可以确保容器与主机之间的Docker访问权限协调一致。

# 创建docker组并指定GID为999
RUN groupadd -g 999 docker && \
    usermod -aG docker jenkins

4.2 Docker Build and Publish "Registry credentials注册表凭据" 不适用于私有 docker 注册表

https://issues.jenkins.io/browse/JENKINS-39952

图片-ycpv.png

5. 操作总结

你如此这般耐心地逐字逐句地阅读本篇章,实属不易,而我也期待你能完整的跟着本篇章来操作一遍,仔细梳理devops-java-sampledeploy-app之间的调用关系,以及如何使用build-maven3.6这个slave节点进行操作,最后我要恭喜你,你成功的掌握到了jenkins的部署与构建流程!