欢迎光临
我们一直在努力

使用 Docker 部署 Jenkins 并实现自动化部署 —— 从零到一的 CI/CD 实践指南

🐳 使用 Docker 部署 Jenkins 并实现自动化部署 —— 从零到一的 CI/CD 实践指南

从容器化部署到 Git 触发自动化构建镜像,一篇带你把 Jenkins CI/CD 流水线跑起来(附流程图)


📌 文章导览

持续集成与持续部署(CI/CD)已成为现代软件开发不可或缺的环节,而 Jenkins 作为全球最流行的开源自动化服务器,被广泛应用于各类自动化任务中。结合 Docker 的轻量化和可移植性,我们可以快速搭建一个高效、灵活且易于扩展的自动化部署环境。

本文涵盖:

✅ Docker 环境准备与镜像选择
✅ Jenkins 容器部署(单容器 + 权限配置)
✅ 初始化与插件安装
✅ 核心重点:让 Jenkins 容器内调用 Docker(两种方案对比)
✅ 凭证管理(Git + 容器仓库)
✅ Jenkins Pipeline 声明式流水线编写(构建 → 推送 → 部署)
✅ 完整流程图 + 架构图(Mermaid 格式)
✅ 生产环境优化建议与常见问题排查

🧱 一、前置条件

项目要求
操作系统 Linux(Ubuntu 20.04+ / CentOS 7+ / Debian)
Docker 版本 ≥ 20.10(含 docker compose)
硬件配置 建议 2 核 4G 以上(Jenkins 运行需一定资源)
代码仓库 GitHub / GitLab / Gitee(任意 Git 仓库即可)
Docker Registry Docker Hub / 私有 Harbor / 阿里云容器镜像服务

⚠️ 本文基于 Docker 官方 LTS 镜像 jenkins/jenkins:lts,此版本兼容性最佳,适合生产部署。

🗺 二、端到端自动化部署流程总览

#mermaid-svg-mgpZro56RZDKxMCK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mgpZro56RZDKxMCK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mgpZro56RZDKxMCK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mgpZro56RZDKxMCK .error-icon{fill:#552222;}#mermaid-svg-mgpZro56RZDKxMCK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mgpZro56RZDKxMCK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mgpZro56RZDKxMCK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mgpZro56RZDKxMCK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mgpZro56RZDKxMCK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mgpZro56RZDKxMCK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mgpZro56RZDKxMCK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mgpZro56RZDKxMCK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mgpZro56RZDKxMCK .marker.cross{stroke:#333333;}#mermaid-svg-mgpZro56RZDKxMCK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mgpZro56RZDKxMCK p{margin:0;}#mermaid-svg-mgpZro56RZDKxMCK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-mgpZro56RZDKxMCK .cluster-label text{fill:#333;}#mermaid-svg-mgpZro56RZDKxMCK .cluster-label span{color:#333;}#mermaid-svg-mgpZro56RZDKxMCK .cluster-label span p{background-color:transparent;}#mermaid-svg-mgpZro56RZDKxMCK .label text,#mermaid-svg-mgpZro56RZDKxMCK span{fill:#333;color:#333;}#mermaid-svg-mgpZro56RZDKxMCK .node rect,#mermaid-svg-mgpZro56RZDKxMCK .node circle,#mermaid-svg-mgpZro56RZDKxMCK .node ellipse,#mermaid-svg-mgpZro56RZDKxMCK .node polygon,#mermaid-svg-mgpZro56RZDKxMCK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mgpZro56RZDKxMCK .rough-node .label text,#mermaid-svg-mgpZro56RZDKxMCK .node .label text,#mermaid-svg-mgpZro56RZDKxMCK .image-shape .label,#mermaid-svg-mgpZro56RZDKxMCK .icon-shape .label{text-anchor:middle;}#mermaid-svg-mgpZro56RZDKxMCK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mgpZro56RZDKxMCK .rough-node .label,#mermaid-svg-mgpZro56RZDKxMCK .node .label,#mermaid-svg-mgpZro56RZDKxMCK .image-shape .label,#mermaid-svg-mgpZro56RZDKxMCK .icon-shape .label{text-align:center;}#mermaid-svg-mgpZro56RZDKxMCK .node.clickable{cursor:pointer;}#mermaid-svg-mgpZro56RZDKxMCK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mgpZro56RZDKxMCK .arrowheadPath{fill:#333333;}#mermaid-svg-mgpZro56RZDKxMCK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mgpZro56RZDKxMCK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mgpZro56RZDKxMCK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mgpZro56RZDKxMCK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mgpZro56RZDKxMCK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mgpZro56RZDKxMCK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mgpZro56RZDKxMCK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mgpZro56RZDKxMCK .cluster text{fill:#333;}#mermaid-svg-mgpZro56RZDKxMCK .cluster span{color:#333;}#mermaid-svg-mgpZro56RZDKxMCK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mgpZro56RZDKxMCK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mgpZro56RZDKxMCK rect.text{fill:none;stroke-width:0;}#mermaid-svg-mgpZro56RZDKxMCK .icon-shape,#mermaid-svg-mgpZro56RZDKxMCK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mgpZro56RZDKxMCK .icon-shape p,#mermaid-svg-mgpZro56RZDKxMCK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mgpZro56RZDKxMCK .icon-shape .label rect,#mermaid-svg-mgpZro56RZDKxMCK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mgpZro56RZDKxMCK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mgpZro56RZDKxMCK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mgpZro56RZDKxMCK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

开发者提交代码到 Git 仓库

Git Webhook 触发 Jenkins

Jenkins 拉取最新代码

Pipeline:代码编译构建

Pipeline:Docker 镜像打包

Pipeline:登录容器镜像仓库

Push 镜像到仓库

部署到目标服务器/环境

完成自动化部署

🔄 核心闭环:代码提交 → Webhook 触发 → Jenkins 构建 → 镜像推送 → 自动部署,全程无需人工干预。

🐳 三、Docker 环境下部署 Jenkins

3.1 拉取 Jenkins 官方镜像

我们选择 Jenkins 长期支持(LTS)版本,它不仅稳定,而且是目前生产环境的主力选择:

docker pull jenkins/jenkins:lts

若拉取较慢,可配置国内镜像加速源或使用阿里云加速器。

3.2 创建数据持久化目录

mkdir -p /data/jenkins_home
chown -R 1000:1000 /data/jenkins_home

💡 设置权限为 1000:1000 是因为 Jenkins 容器内的默认运行用户 jenkins 的 UID 正是 1000,确保容器能够正常读写该目录,避免使用 777(生产环境权限应最小化)。

3.3 启动 Jenkins 容器(基础版)

docker run -d \\
–name jenkins \\
–restart always \\
-p 8080:8080 \\
-p 50000:50000 \\
-v /data/jenkins_home:/var/jenkins_home \\
-e TZ="Asia/Shanghai" \\
jenkins/jenkins:lts

关键参数说明:

参数作用
-p 8080:8080 Web 管理界面访问端口
-p 50000:50000 Jenkins Master-Slave 通信端口
-v Jenkins 数据持久化
-e TZ="Asia/Shanghai" 设置容器时区
–restart always 保障容器异常退出后自动拉起

此时一个基础的 Jenkins 容器已经运行起来。但是请注意:这个容器内无法执行 Docker 命令——流水线中的 docker build 等指令会直接报错。接下来我们要解决这个核心问题。

🔑 四、核心配置:让 Jenkins 能调用 Docker

✅ 这是本教程的核心重点。Jenkins 要构建 Docker 镜像,就必须具备 Docker 能力。

4.1 两种技术路线对比

对比维度方案 A:挂载 Docker Socket方案 B:Docker-in-Docker (DinD)
原理 Jenkins 容器直接调用宿主机 Docker 在 Jenkins 旁启动一个独立的 Docker 守护进程容器
容器大小 轻量 稍重
隔离性 较低(同宿主共享 Docker 引擎) 高(独立的 Docker 进程)
适用阶段 测试/预发布环境 生产环境(隔离要求高时)
安全风险 容器获得宿主机 root-like 权限 –privileged 运行但存在独立隔离
官方示例 较为常见 官方推荐 DinD 保持环境纯净

4.2 方案 A:挂载 Socket(推荐入门)

docker run -d \\
–name jenkins \\
–restart always \\
-p 8080:8080 \\
-p 50000:50000 \\
-v /data/jenkins_home:/var/jenkins_home \\
-v /var/run/docker.sock:/var/run/docker.sock \\
-v /usr/bin/docker:/usr/bin/docker \\
-e TZ="Asia/Shanghai" \\
jenkins/jenkins:lts

核心选项:

  • -v /var/run/docker.sock:/var/run/docker.sock:让 Jenkins 容器与宿主机 共享 Docker 守护进程
  • -v /usr/bin/docker:/usr/bin/docker:让容器内的 docker CLI 可执行
  • 在容器内验证是否成功:docker exec jenkins docker ps

⚠️ 安全提示:该方案相当于将宿主机的 Docker 完全“借”给 Jenkins 容器,务必确保 Jenkins 流水线中不能执行恶意命令。

4.3 方案 B:Docker-in-Docker(DinD,更隔离)

适用场景:K8s 环境、高安全性 CI/CD 集群、多租户场景

步骤一:创建专用 Docker 网络

docker network create jenkins

步骤二:启动 DinD 守护进程容器

docker run –name jenkins-docker \\
–rm –detach \\
–privileged \\
–network jenkins \\
–network-alias docker \\
-e DOCKER_TLS_CERTDIR=/certs \\
-v jenkins-docker-certs:/certs/client \\
-p 2376:2376 \\
docker:dind \\
–storage-driver overlay2

步骤三:启动 Jenkins 并连接到 DinD

docker run –name jenkins-blueocean \\
–restart=on-failure \\
–detach \\
–network jenkins \\
-e DOCKER_HOST=tcp://docker:2376 \\
-e DOCKER_CERT_PATH=/certs/client \\
-e DOCKER_TLS_VERIFY=1 \\
-p 8080:8080 \\
-v jenkins-data:/var/jenkins_home \\
-v jenkins-docker-certs:/certs/client:ro \\
jenkins/jenkins:lts

关键解释:

  • –network-alias docker:为 DinD 容器设置了固定的网络别名,Jenkins 可通过 tcp://docker:2376 与之通信
  • DOCKER_TLS_VERIFY=1:启用 TLS 加密验证
  • :ro:只读挂载证书,进一步增强安全性

🌐 五、Jenkins 初始化与插件配置

5.1 获取初始解锁密码

docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

执行后会打印一长串字符串,复制备用。

5.2 Web 初始化流程

  • 浏览器访问 http://<服务器IP>:8080
  • 粘贴解锁密码进入安装向导
  • 选择 “安装推荐插件”(后续可按需增删)
  • 创建管理员用户并完成基础设置
  • 5.3 必装插件清单

    安装完成之后,建议手动补充以下插件(路径:Dashboard → Manage Jenkins → Plugins → Available plugins):

    插件名称用途
    Git Plugin 从 Git 仓库拉取代码
    Pipeline Jenkins 核心流水线支持
    Blue Ocean(可选) 可视化流水线界面,提升排错效率
    Docker Pipeline 提供 Jenkinsfile 中的 docker.build(), docker.withRegistry() 语法
    Credentials Binding 安全管理凭证信息(避免在脚本中硬编码)
    GitHub Integration / Gitee 实现 Git 触发 Webhook

    💡 安装 Docker Pipeline 后,在流水线中即可调用丰富的 Docker 操作语法。

    🔐 六、凭证管理(核心安全配置)

    6.1 凭证类型与使用场景

    凭证类型典型使用场景
    Username with password 登录 Docker Hub / 私有镜像仓库
    SSH Username with private key Jenkins 免密拉取 Git 仓库(推荐 GitHub 部署密钥)
    Secret text API Token(如 GitHub Personal Access Token)
    Certificate PKCS#12 证书文件

    在 Jenkins 中配置凭证的位置:Dashboard → 系统管理 → 凭证 → 系统 → 全局凭据 → 添加凭据。

    6.2 添加 Git 拉取凭证(SSH 方式)

    💡 这是确保 Jenkins 能拉取私有仓库代码的必备操作。

    在 Jenkins 服务器上生成 SSH 密钥对

    ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

    生成的文件:

    • ~/.ssh/id_rsa —— 私钥(内容粘贴到 Jenkins)
    • ~/.ssh/id_rsa.pub —— 公钥(添加到 GitHub/GitLab 账户的 SSH Keys)
    将公钥添加到代码仓库
    • GitHub:Settings → SSH and GPG keys → New SSH key
    • GitLab:Preferences → SSH Keys
    在 Jenkins 中添加私钥

    添加凭证时选择 SSH Username with private key:

    • Username:填写代码仓库账户(如 git 或 GitHub 用户名)
    • Private Key → Enter directly → 粘贴 id_rsa 私钥的完整内容(包含 —–BEGIN OPENSSH PRIVATE KEY—– 头尾)
    • 输入可选的 Passphrase(若生成密钥时设置了密码短语)

    6.3 添加容器镜像仓库凭证

    选择 Username with password 类型:

    注册表类型UsernamePassword凭证 ID 示例
    Docker Hub Docker Hub 用户名 Docker Hub 密码/Access Token dockerhub-creds
    阿里云容器镜像 阿里云账号名 镜像仓库访问密码 aliyun-acr-creds

    📌 关键提示:为凭证填写一个有意义的 ID(如 dockerhub-credentials、git-ssh-key),因为在 Jenkinsfile 中要引用这个 ID,而不是直接硬编码用户名密码。

    Pipeline 中的凭证引用示例

    withCredentials([usernamePassword(
    credentialsId: 'dockerhub-credentials',
    usernameVariable: 'DOCKER_USER',
    passwordVariable: 'DOCKER_PASS'
    )]) {
    sh 'echo $DOCKER_PASS | docker login -u $DOCKER_USER –password-stdin'
    }

    📄 七、Jenkinsfile 自动化部署流水线

    7.1 流程图速览

    #mermaid-svg-VRyHLRPSuXZT7BgT{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VRyHLRPSuXZT7BgT .error-icon{fill:#552222;}#mermaid-svg-VRyHLRPSuXZT7BgT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VRyHLRPSuXZT7BgT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VRyHLRPSuXZT7BgT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VRyHLRPSuXZT7BgT .marker.cross{stroke:#333333;}#mermaid-svg-VRyHLRPSuXZT7BgT svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VRyHLRPSuXZT7BgT p{margin:0;}#mermaid-svg-VRyHLRPSuXZT7BgT .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT .cluster-label text{fill:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT .cluster-label span{color:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT .cluster-label span p{background-color:transparent;}#mermaid-svg-VRyHLRPSuXZT7BgT .label text,#mermaid-svg-VRyHLRPSuXZT7BgT span{fill:#333;color:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT .node rect,#mermaid-svg-VRyHLRPSuXZT7BgT .node circle,#mermaid-svg-VRyHLRPSuXZT7BgT .node ellipse,#mermaid-svg-VRyHLRPSuXZT7BgT .node polygon,#mermaid-svg-VRyHLRPSuXZT7BgT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VRyHLRPSuXZT7BgT .rough-node .label text,#mermaid-svg-VRyHLRPSuXZT7BgT .node .label text,#mermaid-svg-VRyHLRPSuXZT7BgT .image-shape .label,#mermaid-svg-VRyHLRPSuXZT7BgT .icon-shape .label{text-anchor:middle;}#mermaid-svg-VRyHLRPSuXZT7BgT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VRyHLRPSuXZT7BgT .rough-node .label,#mermaid-svg-VRyHLRPSuXZT7BgT .node .label,#mermaid-svg-VRyHLRPSuXZT7BgT .image-shape .label,#mermaid-svg-VRyHLRPSuXZT7BgT .icon-shape .label{text-align:center;}#mermaid-svg-VRyHLRPSuXZT7BgT .node.clickable{cursor:pointer;}#mermaid-svg-VRyHLRPSuXZT7BgT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VRyHLRPSuXZT7BgT .arrowheadPath{fill:#333333;}#mermaid-svg-VRyHLRPSuXZT7BgT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VRyHLRPSuXZT7BgT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VRyHLRPSuXZT7BgT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VRyHLRPSuXZT7BgT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VRyHLRPSuXZT7BgT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VRyHLRPSuXZT7BgT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VRyHLRPSuXZT7BgT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VRyHLRPSuXZT7BgT .cluster text{fill:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT .cluster span{color:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VRyHLRPSuXZT7BgT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VRyHLRPSuXZT7BgT rect.text{fill:none;stroke-width:0;}#mermaid-svg-VRyHLRPSuXZT7BgT .icon-shape,#mermaid-svg-VRyHLRPSuXZT7BgT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VRyHLRPSuXZT7BgT .icon-shape p,#mermaid-svg-VRyHLRPSuXZT7BgT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VRyHLRPSuXZT7BgT .icon-shape .label rect,#mermaid-svg-VRyHLRPSuXZT7BgT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VRyHLRPSuXZT7BgT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VRyHLRPSuXZT7BgT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VRyHLRPSuXZT7BgT :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    检出代码

    Maven/NPM 构建

    Build Docker 镜像

    Login Registry

    Push 镜像

    部署镜像docker run 或 remote deploy

    通知/清理

    7.2 完整声明式 Pipeline 模板

    在代码仓库的根目录下创建一个 Jenkinsfile(以 Spring Boot Java 应用为例,实际可灵活适配):

    pipeline {
    agent any

    environment {
    // ==================== 请根据实际修改此处 ====================
    DOCKER_REGISTRY = 'your-registry.cn-hangzhou.cr.aliyuncs.com' // 镜像仓库地址
    IMAGE_NAME = 'your-namespace/your-app' // 镜像名称
    // ==========================================================
    }

    stages {
    // Stage 1: 从 Git 仓库拉取最新代码
    stage('Checkout') {
    steps {
    checkout scm
    script {
    // 生成唯一的镜像标签: 构建号-提交哈希前7位(保证可追溯)
    SHORT_COMMIT = sh(script: 'git rev-parse –short HEAD', returnStdout: true).trim()
    TAG = "${env.BUILD_NUMBER}–${SHORT_COMMIT}"
    echo "镜像标签: ${TAG}"
    }
    }
    }

    // Stage 2: 【构建】根据项目类型调整
    stage('Build') {
    steps {
    // Java/Maven 项目示例
    sh 'mvn clean package -DskipTests'
    // Node.js 示例: sh 'npm install && npm run build'
    }
    }

    // Stage 3: 【打包】基于项目 Dockerfile 构建镜像(利用宿主机 Docker)
    stage('Build Docker Image') {
    steps {
    script {
    // 方案 A(挂载 socket)语法:
    docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}")

    // 方案 B(DinD)语法:
    // docker.withServer('tcp://docker:2376', 'docker-certs') {
    // docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}")
    // }
    }
    }
    }

    // Stage 4: 【推送】登录私有镜像仓库并推送镜像
    stage('Push to Registry') {
    steps {
    script {
    // 使用之前配置的 Docker Hub / 私有仓库凭证
    docker.withRegistry("https://${DOCKER_REGISTRY}", 'dockerhub-credentials') {
    docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}").push()
    // 可选: 为 latest 标签也推送一次
    docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}").push('latest')
    }
    }
    echo "✅ 镜像推送完成: ${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}"
    }
    }

    // Stage 5: 【部署】在目标服务器上拉取并运行新版本容器
    stage('Deploy to Server') {
    steps {
    script {
    // 方式一: 如果部署在本地宿主机(直接 docker run 更新)
    sh """
    docker pull
    ${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}
    docker stop my-app || true
    docker rm my-app || true
    docker run -d –name my-app -p 80:8080
    ${DOCKER_REGISTRY}/${IMAGE_NAME}:${TAG}
    """

    // 方式二: 如果部署在远程服务器(通过 SSH 执行命令)
    // 需要安装 SSH Pipeline Steps 插件并配置远程主机凭证
    // sshagent(['remote-server-ssh-key']) {
    // sh "scp … && ssh user@remote-server 'docker pull … && docker run …'"
    // }
    }
    }
    }
    }

    post {
    // 每个阶段结束后自动清理工作空间残留
    always {
    cleanWs()
    }
    success {
    echo "🎉 流水线执行成功!镜像已推送并部署。"
    }
    failure {
    echo "❌ 流水线执行失败,请检查日志。"
    }
    }
    }

    7.3 关键代码解读

    代码块作用
    checkout scm 自动拉取触发此次构建的 Git 仓库对应分支的最新代码
    SHORT_COMMIT + BUILD_NUMBER 组合镜像标签,保证唯一性且易于回溯提交记录
    docker.build(…) Docker Pipeline 插件提供的原生镜像构建命令,底层调用 docker build
    docker.withRegistry 安全登录镜像仓库并推送,全程无需暴露明文密码
    post -> always/cleanWs Jenkins Pipeline 的“最终执行块”,保证每次构建完毕清理残留文件,避免磁盘占满

    🚀 八、多分支流水线配置(推荐生产环境)

    8.1 多分支流水线的价值

    多分支 Pipeline 是 Jenkins 提供的一种高级功能,它可以自动扫描 Git 仓库中所有分支,并为每个分支独立创建和触发对应的流水线。这意味着:

    • 开发分支(develop)自动构建并部署到测试环境
    • 主干分支(main / master)自动构建并部署到预发环境
    • 发布分支(release/*)自动构建并部署到生产环境
    • 无需为每个分支单独配置 Jenkins Job

    8.2 配置步骤

  • 在 Jenkins 首页点击 新建任务
  • 输入项目名称,选择 Multibranch Pipeline(多分支流水线)
  • 在 Branch Sources 中添加 Git 仓库 URL 并选择之前配置的 SSH 凭证
  • 在 Build Configuration 中指定 Jenkinsfile 的路径(默认为仓库根目录的 Jenkinsfile)
  • 点击 保存,Jenkins 将自动扫描全部分支并启动构建
  • #mermaid-svg-ApIw8Fo8p64wmlhX{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ApIw8Fo8p64wmlhX .error-icon{fill:#552222;}#mermaid-svg-ApIw8Fo8p64wmlhX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ApIw8Fo8p64wmlhX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ApIw8Fo8p64wmlhX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ApIw8Fo8p64wmlhX .marker.cross{stroke:#333333;}#mermaid-svg-ApIw8Fo8p64wmlhX svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ApIw8Fo8p64wmlhX p{margin:0;}#mermaid-svg-ApIw8Fo8p64wmlhX .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX .cluster-label text{fill:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX .cluster-label span{color:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX .cluster-label span p{background-color:transparent;}#mermaid-svg-ApIw8Fo8p64wmlhX .label text,#mermaid-svg-ApIw8Fo8p64wmlhX span{fill:#333;color:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX .node rect,#mermaid-svg-ApIw8Fo8p64wmlhX .node circle,#mermaid-svg-ApIw8Fo8p64wmlhX .node ellipse,#mermaid-svg-ApIw8Fo8p64wmlhX .node polygon,#mermaid-svg-ApIw8Fo8p64wmlhX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ApIw8Fo8p64wmlhX .rough-node .label text,#mermaid-svg-ApIw8Fo8p64wmlhX .node .label text,#mermaid-svg-ApIw8Fo8p64wmlhX .image-shape .label,#mermaid-svg-ApIw8Fo8p64wmlhX .icon-shape .label{text-anchor:middle;}#mermaid-svg-ApIw8Fo8p64wmlhX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ApIw8Fo8p64wmlhX .rough-node .label,#mermaid-svg-ApIw8Fo8p64wmlhX .node .label,#mermaid-svg-ApIw8Fo8p64wmlhX .image-shape .label,#mermaid-svg-ApIw8Fo8p64wmlhX .icon-shape .label{text-align:center;}#mermaid-svg-ApIw8Fo8p64wmlhX .node.clickable{cursor:pointer;}#mermaid-svg-ApIw8Fo8p64wmlhX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ApIw8Fo8p64wmlhX .arrowheadPath{fill:#333333;}#mermaid-svg-ApIw8Fo8p64wmlhX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ApIw8Fo8p64wmlhX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ApIw8Fo8p64wmlhX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ApIw8Fo8p64wmlhX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ApIw8Fo8p64wmlhX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ApIw8Fo8p64wmlhX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ApIw8Fo8p64wmlhX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ApIw8Fo8p64wmlhX .cluster text{fill:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX .cluster span{color:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ApIw8Fo8p64wmlhX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ApIw8Fo8p64wmlhX rect.text{fill:none;stroke-width:0;}#mermaid-svg-ApIw8Fo8p64wmlhX .icon-shape,#mermaid-svg-ApIw8Fo8p64wmlhX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ApIw8Fo8p64wmlhX .icon-shape p,#mermaid-svg-ApIw8Fo8p64wmlhX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ApIw8Fo8p64wmlhX .icon-shape .label rect,#mermaid-svg-ApIw8Fo8p64wmlhX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ApIw8Fo8p64wmlhX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ApIw8Fo8p64wmlhX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ApIw8Fo8p64wmlhX :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Git 仓库

    独立构建

    独立构建

    独立构建

    main 分支

    develop 分支

    feature/xxx 分支

    Jenkins 多分支扫描

    main 流水线 → 生产部署

    develop 流水线 → 测试环境部署

    feature 分支 → 快速验证

    🏗 九、整体架构图(UML 部署图)

    渲染错误: Mermaid 渲染失败: Lexical error on line 8. Unrecognized text.
    …e配置 + 插件 + 构建历史] end
    ———————–^

    📐 此图展示了 Jenkins Master 与宿主机 Docker Engine、代码仓库、镜像仓库、以及部署目标之间的关联关系。

    🔧 十、常见问题排查与优化建议

    10.1 容器内无法执行 docker 命令

    现象:构建阶段报 docker: command not found 或 Cannot connect to the Docker daemon。

    解决方案:

  • 确认启动容器时已挂载 /var/run/docker.sock 和 /usr/bin/docker
  • 进入容器验证:docker exec jenkins docker ps
  • 检查宿主机 Docker 是否正常运行:systemctl status docker
  • 10.2 Webhook Git 推送不触发构建

    现象:代码已 push 到仓库,但 Jenkins 未自动触发构建。

    解决方案:

  • 确认 Jenkins 能够被外网/Git仓库访问(检查安全组或防火墙是否放行 8080 端口)
  • GitHub 仓库的 Settings → Webhooks 中添加 Payload URL:
    http://<jenkins-公网IP>:8080/github-webhook/
  • GitLab 的 Webhook URL 为:http://<jenkins-公网IP>:8080/project/<项目名>
  • Jenkins 全局安全配置中勾选 “GitHub 自动触发” 相关选项
  • 10.3 Docker socket 权限不足

    现象:Got permission denied while trying to connect to the Docker daemon socket

    解决方案:

    # 查看 /var/run/docker.sock 的属主
    ls -l /var/run/docker.sock
    # 将 Jenkins 容器用户加入 docker 组
    sudo usermod -aG docker $USER
    newgrp docker
    # 或修改 socket 权限(不推荐生产环境)
    sudo chmod 666 /var/run/docker.sock

    10.4 Jenkins 频繁崩溃或内存不足

    现象:容器 502 无法访问,日志显示 OutOfMemoryError。

    解决方案:

    • 启动容器时增加资源限制:–memory="4g" –memory-swap="4g"
    • 在 docker run 参数中加入 –shm-size=256m
    • 定期清理构建历史:系统管理 → 构建记录管理器 → 配置规则

    10.5 生产环境安全强化清单

    类别推荐实践
    网络隔离 Jenkins 置于专用 Docker 网络,不与其他服务混用
    HTTPS 前置 Nginx/Traefik 反向代理并启用 Let’s Encrypt 自动签发证书
    权限最小化 避免容器以 root 运行:–user 1000:1000
    凭证轮换 镜像仓库 Token 每 90 天轮换一次,Git SSH Key 与个人用户严格区分
    数据备份 每日凌晨自动备份 /data/jenkins_home:tar -zcvf jenkins_backup_$(date +%Y%m%d).tar.gz /data/jenkins_home
    日志轮转 配置 Jenkins 系统日志保留 30 天,避免磁盘写满

    📝 十一、总结

    本文系统地介绍了使用 Docker 部署 Jenkins 并实现自动化部署的完整路径:

    章节核心收获
    三、部署 Jenkins 掌握 docker run 和 docker-compose 两种容器化部署方式
    四、Docker 集成 理解 挂载 socket 与 Docker-in-Docker 两种方案的原理与适用场景,并根据安全要求做出正确选择
    六、凭证管理 学会安全存储 Git 和镜像仓库的登录凭据,避免密钥泄露
    七、Jenkinsfile 能够独立编写声明式 Pipeline,完成从代码拉取到镜像推送再到部署的自动化闭环
    八、多分支 掌握多分支流水线的配置,让不同分支的 CI/CD 策略独立运行

    完成本教程后,你可以将这套 CI/CD 流水线快速集成到团队的 Spring Boot、Node.js、Python 等项目之中,实现“一次提交,自动构建,一键部署”的 DevOps 全流程。

    📎 十二、快速命令参考集

    # 1. 启动 Jenkins(挂载 socket 方案)
    docker run -d –name jenkins –restart always \\
    -p 8080:8080 -p 50000:50000 \\
    -v /data/jenkins_home:/var/jenkins_home \\
    -v /var/run/docker.sock:/var/run/docker.sock \\
    -e TZ=Asia/Shanghai \\
    jenkins/jenkins:lts

    # 2. 获取解锁密码
    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

    # 3. 进入容器调试
    docker exec -it jenkins bash

    # 4. 查看实时日志
    docker logs -f jenkins

    # 5. 重启 Jenkins
    docker restart jenkins

    # 6. 停止并删除容器(保留数据在 /data/jenkins_home)
    docker stop jenkins && docker rm jenkins

    # 7. 升级 Jenkins(先保留数据目录,再用新镜像启动)
    docker pull jenkins/jenkins:lts
    docker stop jenkins && docker rm jenkins
    docker run ... # 使用相同的挂载目录重新启动


    赞(0)
    未经允许不得转载:171主机测评 » 使用 Docker 部署 Jenkins 并实现自动化部署 —— 从零到一的 CI/CD 实践指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址