在 agent 之间传递照片

JenkinsBeginner
立即练习

介绍

Fieldlight Studio 现在需要一个完整的照片包:三张小尺寸照片、一张照片总览图、照片说明,以及下载后仍可使用的画廊。一个 Jenkins agent 能处理照片,另一个则装有提供的打包工具。正确选择两个 agent,并不意味着它们的工作区会共享文件。

你将观察这个缺少文件的问题,在一次 Pipeline 运行内添加真实的临时交接,并保留生成的 ZIP 供独立使用。本实验有自己的原图、已连接的 agent 和尚未构建的任务,不使用前一个实验的文件或构建。

前置要求是掌握选择处理照片的 agent中讲解的 agent 标签、阶段分配、宽度选项、原生构建产物、指纹设置和工作区清理,以及 Jenkins 基本页面操作。不需要 Java 编程或 agent 安装知识。两个 agent 是同一台训练 VM 上文件系统独立的容器,共享其内核。Fieldlight 及其 AI 生成的学习照片不涉及真实客户或活动。预计用时由作者估算,未经初学者实测。

观察独立的工作区

在这一步中,你将跟随工作从图像 agent 转到打包 agent,观察为什么处理成功并不意味着能得到可用的交付成果。请从 Photo Desk 开始。

检查三张 Garden Stories 原图:薰衣草、花园小径和浇水壶。打开每张图片,并使用浏览器的 Back 操作返回。照片台显示 No completed collection yet。这些原图必须保持不变。目标交付要求预览恰好为 320 pixels wide(320 像素宽),并保留其宽高比。

切换到 Jenkins,打开 build-photo-pack。选择 Configure → Pipeline。阅读已准备好的 Script。顶层 agent none 让每个阶段自行分配具备相应能力的 agent。Prepare photos 请求 photo-imaging 并运行 /opt/fieldlight/prepare-photos --width 320;Pack collection 请求 photo-packing 并运行 /opt/fieldlight/pack-photos。

每个阶段以 deleteDir() 开始,并各自拥有 post { always { deleteDir() } } 清理。工作区是任务所分配 agent 上的目录。虽然两个阶段使用相同的任务名称,但它们的目录位于不同容器的文件系统上,并非共享目录的两个名称。只读原图位于任务工作区之外,在图像 agent 的 /opt/fieldlight/originals 下。

图像程序相对于当前工作区写入六个文件:

prepared/
  images/
    garden-01.png
    garden-02.png
    garden-03.png
  contact-sheet.png
  captions.csv
  preparation.json

三张预览均为 320 × 213 像素。照片总览图将它们集中展示;CSV 将各文件名与说明和尺寸关联;JSON 标识照片集、请求宽度和原图哈希。提供的打包工具从自己的工作区读取这些实际文件。它不会从另一个 agent 或 Photo Desk 补取缺失文件。

不编辑脚本,返回任务页面,选择一次 Build Now。在 Build History 中打开新的带编号构建,然后打开 Console Output。等待预期的 FAILURE。首先,image-studio 报告 3 previews at 320 pixels wide。它完成清理后,pack-studio 运行打包命令,报告 Missing prepared/preparation.json in this agent's workspace。最后的清理也会运行。

这是文件位置问题,不是原图损坏或缺少程序。处理阶段完成了,但没有交接。手动复制文件、共享挂载、移除清理或将两个阶段都放到图像 agent 上,只会绕开本课目标,无法交付预期的双 agent 工作流。保留两个阶段的分配和两个清理部分。

如果构建仍在运行或排队,请等待最终状态后再检查。如果意外编辑导致了其他错误,请按下面的配置恢复原始脚本,保存并运行一次,观察这个预期的缺少文件失败:

pipeline {
    agent none
    stages {
        stage('Prepare photos') {
            agent { label 'photo-imaging' }
            steps {
                deleteDir()
                sh '/opt/fieldlight/prepare-photos --width 320'
            }
            post {
                always { deleteDir() }
            }
        }
        stage('Pack collection') {
            agent { label 'photo-packing' }
            steps {
                deleteDir()
                sh '/opt/fieldlight/pack-photos'
                archiveArtifacts artifacts: 'photo-pack.zip', fingerprint: true
            }
            post {
                always { deleteDir() }
            }
        }
    }
}

保留失败的运行及其控制台。切换到 Photo Desk 并刷新:它必须仍显示没有已完成的照片集。三张完整原图仍然可用。下一步只会传递处理后的小尺寸输出,然后创建保留归档。

交接照片并保留照片包

在这一步中,你将在同一次 Pipeline 运行内传递六个处理后的文件,归档完成的 ZIP,并独立检查它。请从 Jenkins 开始,返回 build-photo-pack,选择 Configure → Pipeline。

stash 保存一小组选定文件,供同一次 Pipeline 运行中的后续操作使用。为它指定名称和 includes 匹配模式。这里的 prepared/** 包含 prepared 文件夹内的文件和嵌套的 images/ 文件。相对路径会被保留,因此恢复 stash 会在接收工作区中创建 prepared/。仅 stash 这些小尺寸衍生文件,不要包含原图、Java 运行时或工具目录。

unstash 将该名称对应的文件集恢复到当前 agent 的工作区。名称必须与之前的 stash 匹配。将它放在接收阶段初始 deleteDir() 之后、打包命令之前。将 stash 放在处理之后、图像阶段清理之前。否则,文件可能尚不存在、已被移除,或刚恢复就被移除。

stash 用于临时传递,而不是可下载归档:Jenkins 通常会在本次运行结束后丢弃它。archiveArtifacts 则将最终 ZIP 与构建一起保留。同时保留这两个操作,既能实现临时交接,也能让交付成果日后仍然可用。

点击 Script 内部,在 Windows/Linux 上使用 Ctrl+A,或在 macOS 上使用 Command+A 全选,然后替换为这个完整版本:

pipeline {
    agent none
    stages {
        stage('Prepare photos') {
            agent { label 'photo-imaging' }
            steps {
                deleteDir()
                sh '/opt/fieldlight/prepare-photos --width 320'
                stash name: 'prepared-photos', includes: 'prepared/**'
            }
            post {
                always { deleteDir() }
            }
        }
        stage('Pack collection') {
            agent { label 'photo-packing' }
            steps {
                deleteDir()
                unstash 'prepared-photos'
                sh '/opt/fieldlight/pack-photos'
                archiveArtifacts artifacts: 'photo-pack.zip', fingerprint: true
            }
            post {
                always { deleteDir() }
            }
        }
    }
}

新增的两行是具名的 stash 和对应的 unstash。提供的图像处理和打包命令、正确的 agent、原图边界和清理都保持原样。archiveArtifacts 设置保留 photo-pack.zip 并记录其原生文件指纹;指纹追踪文件身份,不代表客户批准或数字签名。

选择 Save,然后选择 Build Now。等待新的运行以 SUCCESS 结束。打开其 Console Output 并阅读顺序:image-studio 处理 320 像素照片,Jenkins 报告 **Stashed 6 file(s)**,然后图像工作区被清空。接着,pack-studio 恢复 stash,打包工具报告 Packed 9 collection files into photo-pack.zip,Jenkins 在清空打包工作区前归档 ZIP 并记录指纹。

如果控制台显示 No such saved stash,请比较两个 stash 名称及其顺序,恢复完整脚本,保存并再次运行。如果仍报告缺少处理后的文件,请检查 includes 匹配模式是否为 prepared/**,且 unstash 位于接收端初始清理之后。每次新构建都会开始新的传递;较早构建的 stash 不能替代它。如果出现 Groovy 语法错误,请恢复完整脚本,保留花括号、引号和逗号。保留所有失败和成功的运行;普通错误不需要重置 VM。

在成功构建的页面上,选择 photo-pack.zip 构建产物并保存。在你自己的电脑上,将整个 ZIP 解压到一个新文件夹。不要只在归档查看器内打开 HTML 文件:旁边的文件也必须解压。文件夹应包含:

index.html
styles.css
delivery.txt
captions.csv
preparation.json
contact-sheet.png
images/
  garden-01.png
  garden-02.png
  garden-03.png

在浏览器中打开解压后的 index.html。Fieldlight Studio 画廊应显示三张正确的花园照片及其说明。打开每张照片,使用浏览器的 Back 返回。选择 Open contact sheet 查看真实的三张照片总览图,再使用浏览器的 Back 返回画廊。在解压后的文件夹中,用文本编辑器打开 captions.csv、preparation.json 和 delivery.txt;画廊链接指向同样的文件。CSV 中的文件名必须指向实际的 images/ 文件,每张预览必须为 320 × 213 像素,JSON 必须标识 garden 和宽度 320。交付说明应标识本次实际的 Jenkins 任务、构建和打包 agent。

完成的 Fieldlight Studio 照片画廊

完整画廊包含三张花园照片、照片总览图链接,以及指向实际照片说明、图像详情和交付文件的链接。打开解压后的副本时,请将旁边的文件保持在一起。

保持完整的解压文件夹内容在一起。不使用 Photo Desk,在新的浏览器隐私窗口中打开它:所有图片、样式和文件链接仍应通过本地文件正常工作。画廊不使用远程字体或在线备用图片。本地图片无法显示,意味着实际交付文件缺失或位置错误;请将完整的原始 ZIP 解压到一个新文件夹,而不是从源网站复制缺失文件。

切换到 Photo Desk,刷新并选择 Open prepared collection。这个视图读取你实际原生归档中的文件。检查照片和附件,然后用浏览器的 Back 操作返回,选择 Download complete ZIP。其字节应与下载的 Jenkins 构建产物一致。返回照片台,确认三张完整原图均保持不变。

最后切换到 Jenkins。重新打开成功构建,确认清理之后其构建产物仍可下载。保留最初缺少文件的运行、全部历史、原图、工具、节点连接和能力标签。如果继续编辑脚本,请先保存并完成一次新的成功构建,再检查是否完成。现在你拥有真实的双 agent 交接和完整保留的交付成果。

总结

你观察了独立的 Jenkins 工作区,然后在一次运行内用具名 stash 和对应的 unstash 只传递所需的照片输出。打包 agent 创建了完整的本地画廊,原生构建产物保留功能让它在两个阶段工作区清空后仍然可用。独立挑战将更换照片集和宽度,同时复用已经教授的机制。