【Jenkins简单自动化部署案例:基于Docker和Harbor的自动化部署流程记录】

摘要

本文记录了作者使用Jenkins时搭建的一个简单自动化部署案例,涵盖Jenkins的Docker化安装、Harbor私有仓库配置、Ansible远程部署等核心步骤。通过一个SpringBoot项目 (RuoYi) 的完整流程演示,从代码提交到镜像构建、推送、滚动更新,逐步实现CI/CD的基础能力。文章重点解决可能遇到的权限问题、证书配置、流水线调试等痛点,并提供可复现的操作命令和排错思路,适合刚接触DevOps工具链的朋友参考。

安装Jenkins

Jenkins-Docker官网

1.创建网络

使用以下命令在 Docker 中创建一个桥接网络:

docker network create jenkins

2.Dockerfile创建Jenkins Docker 镜像

通过执行以下两个步骤来自定义官方的 Jenkins Docker 镜像:

创建一个包含以下内容的 Dockerfile:

FROM jenkins/jenkins:2.504.1-jdk21
USER root
RUN apt-get update && apt-get install -y lsb-release ca-certificates curl && \install -m 0755 -d /etc/apt/keyrings && \curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && \chmod a+r /etc/apt/keyrings/docker.asc && \echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \https://download.docker.com/linux/debian $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" \| tee /etc/apt/sources.list.d/docker.list > /dev/null && \apt-get update && apt-get install -y docker-ce-cli && \apt-get clean && rm -rf /var/lib/apt/lists/*
USER jenkins
RUN jenkins-plugin-cli --plugins "blueocean docker-workflow json-path-api"

根据此 Dockerfile 构建一个新的 Docker 镜像,“myjenkins-blueocean:2.504.1-1”:

docker build -t myjenkins-blueocean:2.504.1-1 .

如果您尚未下载官方的 Jenkins Docker 镜像,上述过程会自动为您下载。

使用到对应工具需添加安装步骤, 例: 安装Ansible

FROM myjenkins-blueocean:2.504.1-1
USER root
RUN apt-get update && apt-get install -y ansible \&& git config --global --add safe.directory '*'
# 配置 Git 安全目录(root用户)
#RUN git config --global --add safe.directory '*'USER jenkins
docker build -t myjenkins-ansible .

4.启动容器

使用以下 docker run 命令在 Docker 中以容器形式运行您自己的 myjenkins 镜像:

docker run --name jenkins-blueocean --restart=on-failure --detach \
--user root \
--publish 8080:8080 --publish 50000:50000 \
--volume jenkins-data:/var/jenkins_home \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /etc/docker/certs.d:/etc/docker/certs.d:ro \
--volume /etc/ansible/hosts:/etc/ansible/hosts:ro \
--volume ~/.ssh/id_rsa:/root/.ssh/id_rsa \
--volume ~/.ssh/known_hosts:/root/.ssh/known_hosts:ro \
--add-host=harbor.host3:192.168.0.223 \
myjenkins-ansible# 停止并删除旧容器
docker stop jenkins-blueocean
docker rm jenkins-blueocean
  • Ansible Inventory --volume /etc/ansible/hosts:/etc/ansible/hosts:ro
  • 免密登录
    • --volume ~/.ssh/id_rsa:/root/.ssh/id_rsa
    • --volume ~/.ssh/known_hosts:/root/.ssh/known_hosts:ro
  • harbor主机host: --add-host=harbor.host3:192.168.0.223

访问测试

http://192.168.0.220:8080

提示输入密码可查看容器日志获取:docker logs jenkins-blueocean

一:基础环境配置

环境架构概览

节点名称角色及配置关键组件链接功能说明
host0‌ (内存>2G)控制节点(Ansible + Jenkins)Ubuntu 安装 Docker CE + Jenkins负责 CI/CD 流程调度与任务分发
host3‌ (内存>1G)私有镜像仓库(Harbor v2.13.0)Harbor v2.13.0 HTTPS 安装指南存储 Docker 镜像与项目管理
host1/host2‌ (内存>2G)目标服务器(Docker 运行时)-运行业务容器集群

快速创建虚拟环境: 【Vagrant+VirtualBox创建自动化虚拟环境】

节点详细配置

‌1. 控制节点 (host0)

  • ‌核心组件

    • Jenkins‌:用于自动化构建、测试与部署
    • Ansible‌:实现批量服务器配置管理
  • ‌安装步骤

    参考教程:Ubuntu 部署 Docker CE + Jenkins

‌2. 镜像仓库节点 (host3)

  • ‌核心配置‌

    • HTTPS 证书配置
    • 创建项目 ruoyi 用于镜像管理
    • 参考教程:Harbor HTTPS 安装与配置

在这里插入图片描述

‌3. 目标服务器 (host1/host2)

  • ‌基础环境‌
    • Docker 运行时
    • 开放必要端口(80、3306 等)

4.所有服务器

  • 域名映射
echo "192.168.0.223 harbor.host3" | sudo tee -a /etc/hosts
  • 确保 Harbor 的证书文件(如 harbor.host3.cert)已存于目标主机
#jenkins挂载目录
docker volume inspect jenkins-docker-certs#确保 Harbor 的证书文件(如 harbor.host3.cert)已存于宿主机的 /etc/docker/certs.d/harbor.host3/ca.crt,,
sudo scp root@harbor.host3:/opt/harbor/certs/ca.crt /etc/docker/certs.d/harbor.host3/#构建时报错需添加为.pem
sudo scp root@harbor.host3:/opt/harbor/certs/ca.crt /var/lib/docker/volumes/jenkins-docker-certs/_data/ca.pem

1.1 配置Docker信任Harbor仓库(所有主机)

# 在host0/host1/host2上执行
sudo tee /etc/docker/daemon.json <<-'EOF'
{"registry-mirrors": ["https://docker.1ms.run","https://docker.mybacc.com","https://dytt.online","https://lispy.org","https://docker.xiaogenban1993.com","https://docker.yomansunter.com","https://aicarbon.xyz","https://666860.xyz","https://docker.zhai.cm","https://a.ussh.net","https://hub.littlediary.cn","https://hub.rat.dev","https://docker.m.daocloud.io"],"insecure-registries": ["https://harbor.host3"]
}
EOFsudo systemctl daemon-reload
sudo systemctl restart docker

1.2 Jenkins添加凭证(host0 Jenkins网页)

  1. 访问 http://host0:8080

  2. Dashboard -> 系统管理 -> 插件管理-> 选择 Available plugins

    • 安装插件 Ansible plugin, Git Plugin , GitHub API Plugin
      在这里插入图片描述
  3. 左侧菜单点击 Manage Jenkins -> Credentials -> System -> Global credentials

    在这里插入图片描述

  4. 点击 Add Credentials

    • harbor凭证

      • Kind: Username with password

      • ID: harbor-creds

      • Username: [你的Harbor用户名]

      • Password: [你的Harbor密码]

    • git凭证

      • Kind: Username with password
      • Username: 你的 Gitee 用户名
      • Password: 你的 Gitee 密码或 Token
      • ID: gitee-creds
      • ashboard–>系统管理–>全局工具配置
        • 勾选自动安装 -->保存

1.3 准备Git仓库

部署的项目来自github-RuoYi: yangzongzhuan/RuoYi

可克隆我的Gitee来获取:

git clone https://gitee.com/xbd_zc/rytest.git

将以下文件推送到Git仓库:

tree├── deploy.yml
├── docker-compose.yml
├── Jenkinsfile
├── mysql
│   ├── conf
│   │   └── my.cnf
│   ├── Dockerfile
│   ├── init
│   │   ├── 01-init.sql
│   │   ├── 02-quartz.sql
│   │   └── 03-ry_20250416.sql
│   └── runmysql.sh
└── tomcat9├── conf│   ├── context.xml│   └── server.xml├── Dockerfile├── runtomcat.sh├── tomcat│   └── apache-tomcat-9.0.82.tar.gz└── war└── ruoyi-admin-docker.war
  • Dockerfile(tomcat/mysql)
  • docker-compose.yml
  • Jenkinsfile
  • ansible-playbook(deploy.yml)
#初始化项目
git init 
#码云复制的路径,将本地仓库和码 云上的仓库关联起来
git remote add origin https://gitee.com/xx/xx.git
#将远程仓库pull到本地仓库
git pull origin master
#将所有的文件都添加进去,也可以选择使用git add + 文件名 提交具体的某个文件。
git add . 
#‘内容描述’ 提交时的描述
git commit -m "first create" 
#推送到远程仓库(也就是码云上)
git push origin master #配置ssh协议认证
#1生成SSH密钥
ssh-keygen -t ed25519 -C "your_email@qq.com"
按提示生成密钥对(默认路径为 ~/.ssh/id_ed25519 和 ~/.ssh/id_ed25519.pub)34。
cat ~/.ssh/id_ed25519.pub
#2添加公钥到Gitee账户‌
将公钥文件(id_ed25519.pub)内容复制到Gitee的 ‌SSH公钥管理‌ 页面并保存6。#3修改远程仓库协议为SSH‌
检查当前远程地址是否为HTTPS:
git remote -v
若为HTTPS格式,修改为SSH协议地址:
git remote set-url origin git@gitee.com:用户名/仓库名.git
此后push时将自动使用SSH密钥认证,无需输入账号密码

二:流水线配置(host0)

任务步骤

  1. 开发人员提交代码到Git仓库
  2. Jenkins触发构建任务
  3. 构建成功后自动推送镜像到Harbor
  4. 触发Ansible Playbook进行滚动部署

2.1 创建Pipeline任务

  1. Dashboard -> 新建任务 -> 输入名称 image-builder -> 选择 流水线

  2. Pipeline 配置:

    • 流水线

      • Definition 定义: 选择 : Pipeline script from SCM

      • SCM: Git

        • Repository URL: [你的Git仓库地址]

        • Credentials: [选择Git仓库的凭证]

          • 没有可添加
    • Script Path 脚本路径: Jenkinsfile

在这里插入图片描述


在这里插入图片描述

2.2 配置Ansible Inventory

# host0上操作
sudo tee /etc/ansible/hosts <<-'EOF'
[webservers]
host1 ansible_host=192.168.0.221 ansible_user=root
host2 ansible_host=192.168.0.222 ansible_user=root
EOF

2.3 配置SSH免密登录

# host0上操作
ssh-keygen -t rsa
ssh-copy-id root@host1
ssh-copy-id root@host2

2.4 编写Ansible Playbook

harbor拉取镜像后启动容器

# deploy.yml
- hosts: webserverstasks:- name: Login Harborshell: docker login -u {{ harbor_user }} -p {{ harbor_password }} harbor.host3- name: Pull imagesshell: |docker pull harbor.host3/ruoyi/ry-tomcat:{{ build_number }}docker pull harbor.host3/ruoyi/ry-mysql:{{ build_number }}- name: Stop old containersshell: |docker stop ry-tomcat || truedocker stop ry-mysql || truedocker rm ry-tomcat || truedocker rm ry-mysql || true- name: Create app directory with permissionsfile:path: /opt/appstate: directoryowner: rootgroup: rootmode: '0755'- name: Copy docker-compose.ymlcopy:src: "docker-compose.yml"dest: /opt/app/docker-compose.ymlbackup: yes- name: Ensure docker-compose.yml existsstat:path: /opt/app/docker-compose.ymlregister: compose_file- name: Fail if docker-compose.yml does not existfail:msg: "docker-compose.yml not found at /path/to/docker-compose.yml"when: not compose_file.stat.exists- name: Start new containersshell: |export TAG={{ build_number }}docker compose -f /opt/app/docker-compose.yml  downdocker compose -f /opt/app/docker-compose.yml  up -d

2.5 Jenkinsfile内容

pipeline {agent anyenvironment {HARBOR_URL = "harbor.host3"PROJECT_NAME = "ruoyi"// 添加 IMAGE_TAG 定义(使用 BUILD_NUMBER 或自定义值)IMAGE_TAG = "${BUILD_NUMBER}"}stages {// 阶段 1:构建镜像stage('Build Images') {steps {script {docker.withRegistry("https://${HARBOR_URL}", 'harbor-creds') {// 构建 Tomcat 镜像def tomcatImage = docker.build("${HARBOR_URL}/${PROJECT_NAME}/ry-tomcat:${IMAGE_TAG}",'./tomcat9')tomcatImage.push()// 构建 MySQL 镜像def mysqlImage = docker.build("${HARBOR_URL}/${PROJECT_NAME}/ry-mysql:${IMAGE_TAG}",'./mysql')mysqlImage.push()} // 闭合 docker.withRegistry} // 闭合 script} // 闭合 steps} // 闭合 stage('Build Images')// 阶段 2:部署stage('Deploy') {steps {ansiblePlaybook(playbook: 'deploy.yml',         // Ansible Playbook 路径inventory: '/etc/ansible/hosts', // Ansible Inventory 文件extras: '-e ansible_ssh_private_key_file=/root/.ssh/id_rsa',  // 指定私钥路径extraVars: [harbor_user: 'admin',harbor_password: 'Harbor12345',build_number: "${BUILD_NUMBER}"],installation: 'Auto-Install-Ansible'  // 引用全局工具名称)} // 闭合 steps} // 闭合 stage('Deploy')} // 闭合 stages
} // 闭合 pipeline

2.6 执行流水线imag-builder

Dashboard --> image-builder --> 立即构建

在这里插入图片描述

  • 可在构建的任务重查看日志Console Output

在这里插入图片描述

最后成功Console Output应输出一下内容

Started by user xbdzc
Obtained Jenkinsfile from git https://gitee.com/xbd_zc/rytest.git
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /var/jenkins_home/workspace/image-builder
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Declarative: Checkout SCM)
[Pipeline] checkout
The recommended git tool is: git
using credential gitee-creds> git rev-parse --resolve-git-dir /var/jenkins_home/workspace/image-builder/.git # timeout=10
Fetching changes from the remote Git repository> git config remote.origin.url https://gitee.com/xbd_zc/rytest.git # timeout=10
Fetching upstream changes from https://gitee.com/xbd_zc/rytest.git> git --version # timeout=10> git --version # 'git version 2.39.5'
using GIT_ASKPASS to set credentials xbdzc-passwd> git fetch --tags --force --progress -- https://gitee.com/xbd_zc/rytest.git +refs/heads/*:refs/remotes/origin/* # timeout=10> git rev-parse refs/remotes/origin/master^{commit} # timeout=10
Checking out Revision b3d95a777b4fa27956fa864ce008527296196630 (refs/remotes/origin/master)> git config core.sparsecheckout # timeout=10> git checkout -f b3d95a777b4fa27956fa864ce008527296196630 # timeout=10
Commit message: "update compose"> git rev-list --no-walk cd6d562fb8aa4472ecfa32177959752d75f371d8 # timeout=10
[Pipeline] }
[Pipeline] // stage
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withEnv
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Build Images)
[Pipeline] script
[Pipeline] {
[Pipeline] withEnv
[Pipeline] {
[Pipeline] withDockerRegistry
Using the existing docker config file.
Removing blacklisted property: auths
$ docker login -u admin -p ******** https://harbor.host3
WARNING! Using --password via the CLI is insecure. Use --password-stdin.WARNING! Your credentials are stored unencrypted in '/var/jenkins_home/workspace/image-builder@tmp/3425a5d5-afa6-4967-ac2a-891cbc224086/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/Login Succeeded
[Pipeline] {
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker build -t harbor.host3/ruoyi/ry-tomcat:39 ./tomcat9
#0 building with "default" instance using docker driver#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 773B done
#1 DONE 0.0s#2 [internal] load metadata for docker.io/library/eclipse-temurin:8-jre-jammy
#2 DONE 3.6s#3 [internal] load .dockerignore
#3 transferring context: 2B done
#3 DONE 0.0s#4 [1/6] FROM docker.io/library/eclipse-temurin:8-jre-jammy@sha256:d45584c02ee4da09862673207d57121049b89eb99d2a3415573efc0530a87e61
#4 DONE 0.0s#5 [internal] load build context
#5 transferring context: 89.77MB 0.3s done
#5 DONE 0.3s#6 [3/6] RUN mkdir -p "/opt/tomcat" &&     tar xzvf /tmp/tomcat.tar.gz -C /opt/tomcat --strip-components=1 &&     rm -rf /tmp/tomcat.tar.gz /opt/tomcat/webapps/*
#6 CACHED#7 [4/6] COPY conf/server.xml /opt/tomcat/conf/
#7 CACHED#8 [5/6] COPY conf/context.xml /opt/tomcat/conf/
#8 CACHED#9 [2/6] COPY tomcat/apache-tomcat-9.0.82.tar.gz /tmp/tomcat.tar.gz
#9 CACHED#10 [6/6] COPY war/ruoyi-admin-docker.war /opt/tomcat/webapps/ROOT.war
#10 CACHED#11 exporting to image
#11 exporting layers done
#11 writing image sha256:e1157c41978f859e8f9ca9c631b635c2b694a156a9f127f9efb94904e5dccc9f done
#11 naming to harbor.host3/ruoyi/ry-tomcat:39 done
#11 DONE 0.0s
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker tag harbor.host3/ruoyi/ry-tomcat:39 harbor.host3/ruoyi/ry-tomcat:39
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker push harbor.host3/ruoyi/ry-tomcat:39
The push refers to repository [harbor.host3/ruoyi/ry-tomcat]
be3aaabe879b: Preparing
3180fdaa6749: Preparing
b7e66be23a97: Preparing
7700871d37e6: Preparing
abd6fe352750: Preparing
77f2daad0e52: Preparing
b31e18c35f3d: Preparing
735b0864b65b: Preparing
06dd982b4145: Preparing
346f14bf17b9: Preparing
77f2daad0e52: Waiting
b31e18c35f3d: Waiting
735b0864b65b: Waiting
06dd982b4145: Waiting
346f14bf17b9: Waiting
b7e66be23a97: Layer already exists
abd6fe352750: Layer already exists
3180fdaa6749: Layer already exists
7700871d37e6: Layer already exists
b31e18c35f3d: Layer already exists
be3aaabe879b: Layer already exists
77f2daad0e52: Layer already exists
735b0864b65b: Layer already exists
06dd982b4145: Layer already exists
346f14bf17b9: Layer already exists
39: digest: sha256:be702d8f76de44a3da0a1a8f9b3763531f1c82f5ac1b8b0c32c52cec6c5f424c size: 2419
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker build -t harbor.host3/ruoyi/ry-mysql:39 ./mysql
#0 building with "default" instance using docker driver#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile:
#1 transferring dockerfile: 777B done
#1 DONE 0.2s#2 [internal] load metadata for docker.io/library/mysql:5.7
#2 DONE 2.7s#3 [internal] load .dockerignore
#3 transferring context: 2B done
#3 DONE 0.1s#4 [1/5] FROM docker.io/library/mysql:5.7@sha256:4bc6bc963e6d8443453676cae56536f4b8156d78bae03c0145cbe47c2aad73bb
#4 DONE 0.0s#5 [internal] load build context
#5 transferring context: 74.34kB done
#5 DONE 0.1s#6 [2/5] RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone
#6 CACHED#7 [3/5] COPY conf/my.cnf /etc/mysql/conf.d/
#7 CACHED#8 [4/5] RUN chmod 644 /etc/mysql/conf.d/my.cnf
#8 CACHED#9 [5/5] COPY init/*.sql /docker-entrypoint-initdb.d/
#9 CACHED#10 exporting to image
#10 exporting layers done
#10 writing image sha256:e41ca592aefc8dd1e70ed6926f764d560bbf812e811af16f0a8d854a2fd7010e done
#10 naming to harbor.host3/ruoyi/ry-mysql:39 done
#10 DONE 0.0s
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker tag harbor.host3/ruoyi/ry-mysql:39 harbor.host3/ruoyi/ry-mysql:39
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] isUnix
[Pipeline] withEnv
[Pipeline] {
[Pipeline] sh
+ docker push harbor.host3/ruoyi/ry-mysql:39
The push refers to repository [harbor.host3/ruoyi/ry-mysql]
f1f76fda4403: Preparing
8664e10de2b4: Preparing
300d02752ddc: Preparing
bf6dd8c03784: Preparing
441e16cac4fe: Preparing
73cb62467b8f: Preparing
337ec6bae222: Preparing
532b66f4569d: Preparing
0d9e9a9ce9e4: Preparing
4555572a6bb2: Preparing
8527ccd6bd85: Preparing
d76a5f910f6b: Preparing
8b2952eb02aa: Preparing
7ff7abf4911b: Preparing
cff044e18624: Preparing
73cb62467b8f: Waiting
337ec6bae222: Waiting
532b66f4569d: Waiting
0d9e9a9ce9e4: Waiting
4555572a6bb2: Waiting
8527ccd6bd85: Waiting
d76a5f910f6b: Waiting
8b2952eb02aa: Waiting
7ff7abf4911b: Waiting
cff044e18624: Waiting
441e16cac4fe: Layer already exists
8664e10de2b4: Layer already exists
300d02752ddc: Layer already exists
bf6dd8c03784: Layer already exists
f1f76fda4403: Layer already exists
73cb62467b8f: Layer already exists
0d9e9a9ce9e4: Layer already exists
337ec6bae222: Layer already exists
532b66f4569d: Layer already exists
4555572a6bb2: Layer already exists
7ff7abf4911b: Layer already exists
8527ccd6bd85: Layer already exists
8b2952eb02aa: Layer already exists
d76a5f910f6b: Layer already exists
cff044e18624: Layer already exists
39: digest: sha256:bdd554f007c7cf0af8a9d320e9b711e4f7a2cc32e1ed49e3ed5a7360881b69bc size: 3448
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // withDockerRegistry
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Deploy)
[Pipeline] ansiblePlaybook
[image-builder] $ ansible-playbook deploy.yml -i /etc/ansible/hosts -e ******** -e ******** -e ******** -e ansible_ssh_private_key_file=/root/.ssh/id_rsaPLAY [webservers] **************************************************************TASK [Gathering Facts] *********************************************************
ok: [host1]
ok: [host2]TASK [Login Harbor] ************************************************************
changed: [host1]
changed: [host2]TASK [Pull images] *************************************************************
changed: [host1]
changed: [host2]TASK [Stop old containers] *****************************************************
changed: [host2]
changed: [host1]TASK [Create app directory with permissions] ***********************************
ok: [host2]
ok: [host1]TASK [Copy docker-compose.yml] *************************************************
changed: [host1]
changed: [host2]TASK [Ensure docker-compose.yml exists] ****************************************
ok: [host2]
ok: [host1]TASK [Fail if docker-compose.yml does not exist] *******************************
skipping: [host1]
skipping: [host2]TASK [Start new containers] ****************************************************
changed: [host1]
changed: [host2]PLAY RECAP *********************************************************************
host1                      : ok=8    changed=5    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
host2                      : ok=8    changed=5    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   [Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // withEnv
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
Finished: SUCCESS

访问测试

host1登录若依系统

host2-登录若依系统


三 常见问题

Jenkins容器内工作目录/var/jenkins_home/workspace/image-builder

排查问题可进入容器测试遇到的问题

docker exec -it jenkins-blueocean bash
cd /var/jenkins_home/workspace/image-builder #此目录下应有从git拉取的文件

git连接和权限问题

报错 fatal: not in a git directory 表明 Jenkins 任务执行时 未在 Git 仓库目录中操作,可能由以下原因导致:

  1. Jenkins 任务未正确克隆仓库:Git 仓库未成功拉取到工作目录。
  2. 权限问题:容器内用户对工作目录无读写权限。
  3. Docker 组配置残留影响:之前的 --group-add 参数可能干扰了容器用户组。
# 在宿主机host0执行,确保 Jenkins 数据卷权限正确
docker run --rm -v jenkins-data:/var/jenkins_home alpine \chown -R 1000:1000 /var/jenkins_home
#进入容器执行git命令
docker exec -it jenkins-blueocean bash
cd /var/jenkins_home/workspace/image-builder
git config --global --add safe.directory '*'  # 忽略目录安全警告
git clone https://gitee.com/xbd_zc/rytest.git
# 预期输出:成功克隆仓库
  1. Git 安全机制升级:Git 2.35.2+ 引入了 safe.directory 检查,防止在非信任目录执行操作(避免恶意脚本攻击)。

  2. 用户权限变化

    • 旧启动参数中 Jenkins 可能以 jenkins 用户运行(UID=1000),工作目录所有权为 1000:1000
    • 新参数以 root 用户运行,工作目录所有权可能变为 0:0(root),Git 会认为目录不安全。
  3. 安全目录匹配规则

    • Git 默认只信任与当前用户匹配的目录所有权。
    • 以 root 用户操作非 root 所有的目录时需显式声明安全。
    # 全局信任所有目录
    git config --global --add safe.directory '*'# 或仅信任 Jenkins 工作目录
    git config --global --add safe.directory /var/jenkins_home/workspace/image-builder
    

harbor证书问题 ERROR: Could not find credentials matching harbor-creds

在 Jenkins 中添加 Harbor 凭证

Dashboard–>系统管理–>凭据–>系统–>全局凭据 (unrestricted) -->New credentials

  1. 点击 Add Credentials

  2. 按以下参数填写:

    字段
    Scope范围Global全局
    Username用户名你的 Harbor 用户名(如 admin
    Password密码你的 Harbor 密码 (如Harbor12345)
    IDharbor-creds(必须与 Jenkinsfile 中一致)

手动测试(host0)

docker exec -it jenkins-blueocean bash
docker login harbor.host3 -u admin -p Harbor12345
  • 成功输出Login Succeeded

  • 失败处理可能报错: x509: certificate signed by unknown authority

    • 检查 Harbor 地址是否正确(harbor.host3 是否可解析)。

    • 检查 Docker 是否信任 Harbor 仓库(参考阶段一中的 insecure-registries 配置)。

      #在 host0/host1/host2 上配置 Docker 信任 Harbor 的 CA 证书:
      sudo scp root@harbor.host3:/opt/harbor/certs/ca.crt /etc/docker/certs.d/harbor.host3/
      sudo systemctl restart docker
      # 进入容器检查证书路径,应看到 ca.crt 文件
      docker exec jenkins-blueocean ls -l /etc/docker/certs.d/harbor.host3/
      

Jenkins容器无法操作Docker的权限报错

报错 permission denied while trying to connect to the Docker daemon socket 表示 Jenkins 容器内的用户无权访问宿主机的 Docker Socket。根本原因是:

宿主机上的 /var/run/docker.sock 文件权限不足。

#查看Jenkins启动容器命令# 停止并删除旧容器
docker stop jenkins-blueocean
docker rm jenkins-blueoceandocker run --name jenkins-blueocean --restart=on-failure --detach \
--user root \
--publish 8080:8080 --publish 50000:50000 \
--volume jenkins-data:/var/jenkins_home \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /etc/docker/certs.d:/etc/docker/certs.d:ro \
--volume /etc/ansible/hosts:/etc/ansible/hosts:ro \
--volume ~/.ssh/id_rsa:/root/.ssh/id_rsa \
--volume ~/.ssh/known_hosts:/root/.ssh/known_hosts:ro \
--add-host=harbor.host3:192.168.0.223 \
myjenkins-ansible
  • 使用root测试 : --user root
  • 挂载docker.sock文件

Jenkins Ansible和Ansible插件问题

Jenkins Pipeline 中的命令是在 容器内 执行的,因此必须在容器内安装 Ansible。

未安装会报错**No such file or directory**

#启动容器时动态安装
docker exec -it jenkins-blueocean bash
apt-get update && apt-get install -y ansible
exit
缺点:容器重启后需重新安装。固化到镜像(永久生效)
修改 Dockerfile:dockerfile
FROM myjenkins-blueocean:2.504.1-1
USER root
# 配置 Git 安全目录
RUN git config --global --add safe.directory '*'
# 可选:安装其他工具(如 Ansible)
RUN apt-get update && apt-get install -y ansible
USER jenkins  # 切换回原用户(如果基础镜像是非 root)

一、Jenkins Ansible 插件自动安装机制解析

  1. 插件能力
    Ansible 插件本身 不直接安装 Ansible,但允许通过 “Installers” 配置安装逻辑(如执行 Shell 脚本或使用包管理器)。
  2. 核心步骤
    • 在 Jenkins 全局工具中定义 Ansible 安装方式。
    • 在 Pipeline 中指定使用该工具,触发自动安装。

二、配置 Ansible 自动安装

步骤1:进入全局工具配置

  1. 访问 http://host0:8080/configureTools/
  2. 找到 Ansible 部分,点击 新增 Ansible

步骤2:配置自动安装器

# 示例配置(根据你的环境调整)
Name: Auto-Install-Ansible  # 自定义名称
Installers:- 选择 "Run shell command"- Command:# 适用于 Debian/Ubuntu 容器apt-get update && apt-get install -y ansible# 或使用 pip 安装# pip3 install ansible-core

步骤3:验证安装路径

  • Path to ansible-playbook executable: /usr/bin/ansible-playbook(默认 APT 安装路径)

三、修改 Jenkinsfile 触发自动安装

更新部署阶段代码

stage('Deploy') {steps {ansiblePlaybook(playbook: 'deploy.yml',inventory: '/etc/ansible/hosts',extraVars: [harbor_user: 'admin',harbor_password: 'Harbor12345',build_number: "${BUILD_NUMBER}"],installation: 'Auto-Install-Ansible'  // 引用全局工具名称)}
}

Ansible Inventory 管理和 容器内 SSH 连接问题

一、自动管理 Ansible Inventory 文件

方案1:挂载宿主机 Inventory 到容器

# 宿主机 Inventory 路径:/home/user/ansible/hosts
docker run ... \--volume /home/user/ansible/hosts:/etc/ansible/hosts:ro \myjenkins-blueocean:latest

验证

docker exec jenkins-blueocean cat /etc/ansible/hosts

方案2:动态生成 Inventory 文件

在 Jenkins Pipeline 中动态创建:

stage('Prepare Inventory') {steps {sh '''echo "[webservers]" > inventoryecho "host1 ansible_host=192.168.0.101" >> inventoryecho "host2 ansible_host=192.168.0.102" >> inventory'''}
}
stage('Deploy') {steps {ansiblePlaybook(playbook: 'deploy.yml',inventory: 'inventory',  // 使用动态生成的文件// ...)}
}

二、解决 SSH Host Key 验证失败

1. 禁用 Host Key 检查(仅限测试环境)

ansible.cfg 或 Playbook 中配置:

# ansible.cfg
[defaults]
host_key_checking = False

或在 Jenkinsfile 中传递参数

ansiblePlaybook(extras: '-e ansible_ssh_common_args="-o StrictHostKeyChecking=no"'
)

2. 挂载宿主机的 known_hosts 文件

docker run ... \--volume ~/.ssh/known_hosts:/root/.ssh/known_hosts:ro \myjenkins-blueocean:latest

3. 预配置 known_hosts

在容器构建阶段添加目标主机指纹:

FROM myjenkins-blueocean:2.504.1-1
RUN mkdir -p /root/.ssh && \ssh-keyscan host1 host2 >> /root/.ssh/known_hosts

三、完整操作流程

步骤1:准备 Inventory 和 SSH 配置

  1. 宿主机 Inventory 文件 /home/user/ansible/hosts

    [webservers]
    host1 ansible_host=192.168.0.101 ansible_user=root
    host2 ansible_host=192.168.0.102 ansible_user=root
    
  2. 挂载 Inventory 和 SSH 密钥

    docker run --name jenkins-ansible \--volume /home/user/ansible/hosts:/etc/ansible/hosts:ro \--volume ~/.ssh/id_rsa:/root/.ssh/id_rsa \--volume ~/.ssh/known_hosts:/root/.ssh/known_hosts:ro \myjenkins-blueocean:latest
    

步骤2:调整 Jenkinsfile

stage('Deploy') {steps {ansiblePlaybook(playbook: 'deploy.yml',inventory: '/etc/ansible/hosts',extras: '-e ansible_ssh_private_key_file=/root/.ssh/id_rsa',  // 指定私钥路径installation: 'Auto-Install-Ansible')}
}

步骤3:验证 SSH 连接

# 在容器内手动测试
docker exec -it jenkins-ansible bash
ansible webservers -m ping
# 预期输出:pong

四、关键错误排查

错误:Permission denied (publickey)

  • 原因:SSH 私钥权限过宽或路径错误。

  • 修复

    # 容器内执行
    chmod 600 /root/.ssh/id_rsa
    

错误:No such file or directory

  • 原因:Inventory 文件路径错误。

  • 修复

    // 确认 inventory 参数路径
    inventory: '/etc/ansible/hosts'
    

五、自动化优化建议

  1. 使用 Ansible Vault 管理敏感变量

    ansible-vault encrypt secrets.yml
    
    ansiblePlaybook(extras: '--ask-vault-pass -e @secrets.yml'
    )
    
  2. 动态 Inventory 脚本

    stage('Generate Inventory') {steps {writeFile file: 'inventory', text: """[webservers]${env.HOST1_IP}${env.HOST2_IP}"""}
    }
    

可使用 ansible xxx -vvv 调试输出。, 进入容器执行测试

#进入容器
docker exec -it jenkins-blueocean bash
cd /var/jenkins_home/workspace/image-builderansible-playbook deploy.yml -i /etc/ansible/hosts -e harbor_user='admin' -e harbor_password='Harbor12345' -e build_number="36" -e ansible_ssh_private_key_file=/root/.ssh/id_rsa -vvv

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/905175.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Git】GitHub上传图片遇到的问题

一开始我直接在网页上拖拽上传&#xff0c;会说“网页无法正常运作”。 采用git push上去&#xff1a; git clone https://github.com/your-username/your-repo-name.git cd your-repo-name git add . git commit -m "Add large images" git push origin main报错&…

【落羽的落羽 C++】stack和queue、deque、priority_queue、仿函数

文章目录 一、stack和queue1. 概述2. 使用3. 模拟实现 二、deque三、priority_queue1. 概述和使用2. 模拟实现 四、仿函数 一、stack和queue 1. 概述 我们之前学习的vector和list&#xff0c;以及下面要认识的deque&#xff0c;都属于STL的容器&#xff08;containers&#x…

用生活例子通俗理解 Python OOP 四大特性

让我们用最生活化的方式&#xff0c;结合Python代码&#xff0c;来理解面向对象编程的四大特性。 1. 封装&#xff1a;像使用自动售货机 生活比喻&#xff1a; 你只需要投币、按按钮&#xff0c;就能拿到饮料 不需要知道机器内部如何计算找零、如何运送饮料 如果直接打开机…

软件安全(三)实现后门程序

如下是一个经典的后门程序 #define _WINSOCK_DEPRECATED_NO_WARNINGS 1 #include<WinSock2.h> #include<windows.h> #include<iostream> #pragma comment(lib, "ws2_32.lib")int main() {//初始化网络环境WSADATA wsaData;int result WSAStartup…

深入理解高性能网络通信:从内核源码到云原生实践

深入理解高性能网络通信&#xff1a;从内核源码到云原生实践 前言 随着互联网业务规模的高速增长&#xff0c;服务端网络通信能力成为系统性能的核心瓶颈。如何支撑百万级连接、在极限场景下实现低延迟高吞吐&#xff1f;本篇博客将围绕Linux通信机制内核剖析、性能调优实战、…

从实战看软件测试与质量管理:方法、过程与质量的全景解读

作为一名高级软件测试工程师&#xff0c;在过往多个大型系统项目的测试工作中&#xff0c;我深刻体会到&#xff1a;软件测试不仅是产品质量的“守门员”&#xff0c;更是项目成功的“加速器”。今天这篇文章&#xff0c;我将站在实战角度&#xff0c;结合具体案例&#xff0c;…

Megatron系列——流水线并行

内容总结自&#xff1a;bilibili zomi 视频大模型流水线并行 注&#xff1a;这里PipeDream 1F1B对应时PP&#xff0c;Interleaved 1F1B对应的是VPP 1、朴素流水线并行 备注&#xff1a; &#xff08;1&#xff09;红色三个圈都为空泡时间&#xff0c;GPU没有做任何计算 &am…

在Web应用中集成Google AI NLP服务的完整指南:从Dialogflow配置到高并发优化

在当今数字化客服领域,自然语言处理(NLP)技术已成为提升用户体验的关键。Google AI提供了一系列强大的NLP服务,特别是Dialogflow,能够帮助开发者构建智能对话系统。本文将详细介绍如何在Web应用中集成这些服务,解决从模型训练到高并发处理的全套技术挑战。 一、Dialogflow…

Wi-Fi网络角色及功能详解

在 Wi-Fi 网络中&#xff0c;不同的角色和组件协同工作以实现无线通信。以下是 Wi-Fi 中的主要角色及其功能&#xff1a; 1. 基础设施模式&#xff08;Infrastructure Mode&#xff09; 这是最常见的 Wi-Fi 网络架构&#xff0c;包含以下核心角色&#xff1a; 接入点&#xff…

密码学--希尔密码

一、实验目的 1、通过实现简单的古典密码算法&#xff0c;理解密码学的相关概念 2、理解明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。 二、实验内容 1、题目内容描述 ①定义分组字符长度 ②随机生成加密密钥&#xff0c;并验证密钥的可行性 …

[C++] 一个线程打印奇数一个线程打印偶数

要求开辟两个线程打印从0-100的数&#xff0c;一个线程打印奇数一个线程打印偶数&#xff0c;要求必须按照1,2,3,4,5,6…100这种按照顺序打印 使用std::shared_mutex的版本 #ifndef PrintNumber2_H_ #define PrintNumber2_H_#include <shared_mutex>class PrintNumber2…

MySQL全量、增量备份与恢复

目录 数据备份 一、数据备份类型 二、常见备份方法 扩展&#xff1a;GTID与XtraBackup ‌一、GTID&#xff08;全局事务标识符&#xff09;‌ ‌1. 定义与核心作用‌ ‌2. GTID在备份恢复中的意义‌ ‌3. GTID配置与启用‌ ‌二、XtraBackup的意义与核心价值‌ ‌1. 定…

木马查杀篇—Opcode提取

【前言】 介绍Opcode的提取方法&#xff0c;并探讨多种机器学习算法在Webshell检测中的应用&#xff0c;理解如何在实际项目中应用Opcode进行高效的Webshell检测。 Ⅰ 基本概念 Opcode&#xff1a;计算机指令的一部分&#xff0c;也叫字节码&#xff0c;一个php文件可以抽取出…

DeepSeek-R1-Distill-Qwen-1.5B代表什么含义?

DeepSeek‑R1‑Distill‑Qwen‑1.5B 完整释义与合规须知 一句话先行 这是 DeepSeek‑AI 把自家 R1 大模型 的知识&#xff0c;通过蒸馏压缩进一套 Qwen‑1.5B 架构 的轻量学生网络&#xff0c;并以宽松开源许可证发布的模型权重。 1 | 名字逐段拆解 片段意义备注DeepSee…

Megatron系列——张量并行

本文整理自bilibili Zomi视频 1、行切分和列切分 注意&#xff1a; &#xff08;1&#xff09;A按列切分时&#xff0c;X无需切分&#xff0c;split复制广播到A1和A2对应设备即可。最后Y1和Y2需要拼接下&#xff0c;即All Gather &#xff08;2&#xff09;A按行切分时&#…

java agent技术

从JDK1.5之后引入了java angent技术 Java Agent 是一种强大的技术&#xff0c;它允许开发者在 JVM 启动时或运行期间动态地修改类的字节码&#xff0c;从而实现诸如性能监控、日志记录、AOP&#xff08;面向切面编程&#xff09;等功能 java agent依赖于Instrumentation API&…

LLaMA Factory 深度调参

注意&#xff0c;本文涵盖从基础调参到前沿研究的完整知识体系&#xff0c;建议结合具体业务场景灵活应用。一篇“参考文献”而非“可运行的代码”。https://github.com/zysNLP/quickllm 初始指令&#xff1a; llamafactory-cli train \--stage sft \--do_train True \--mode…

Linux驱动:驱动编译流程了解

要求 1、开发板中的linux的zImage必须是自己编译的 2、内核源码树,其实就是一个经过了配置编译之后的内核源码。 3、nfs挂载的rootfs,主机ubuntu中必须搭建一个nfs服务器。 内核源码树 解压 tar -jxvf x210kernel.tar.bz2 编译 make x210ii_qt_defconfigmakeCan’t use ‘…

Redis集群模式、持久化、过期策略、淘汰策略、缓存穿透雪崩击穿问题

Redis四种模式 单节点模式 架构​​&#xff1a;单个Redis实例运行在单台服务器。 ​​优点​​&#xff1a; ​​简单​​&#xff1a;部署和配置容易&#xff0c;适合开发和测试。 ​​低延迟​​&#xff1a;无网络通信开销。 ​​缺点​​&#xff1a; ​​单点故障​​&…

1.2 函数

函数的本质是描述变量间的依赖关系&#xff1a;​​一个变量&#xff08;自变量&#xff09;的变化会唯一确定另一个变量&#xff08;因变量&#xff09;的值​​。 ​​基本构成​​&#xff1a;通过符号&#xff08;如YF(X)&#xff09;表达规则&#xff0c;X输入 → F处理 …