ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Jenkins离线部署实战:构建可审计可升级的生产级CI/CD环境

Jenkins离线部署实战:构建可审计可升级的生产级CI/CD环境 1. 为什么离线部署 Jenkins 不是“备选方案”而是生产环境的刚性需求在金融、能源、政务、军工等强监管或高安全要求的行业里我见过太多次这样的场景运维同事盯着 Jenkins 控制台构建任务卡在“Downloading plugin: workflow-aggregator”上一动不动日志里反复刷着Connection refused或UnknownHostException开发同学提交完代码等着自动化测试结果却被告知“Jenkins 插件仓库连不上CI 流水线今天停摆”更棘手的是某次紧急上线前夜团队发现 Jenkins 升级失败——因为新版本依赖的script-security插件无法从官网拉取而线上环境又严禁临时开通外网。这些不是偶发故障而是在线部署模式在封闭网络中必然暴露的结构性缺陷。Jenkins 离线部署从来就不是给“没网的树莓派”准备的玩具方案。它是一套完整的生产级交付契约它承诺在无外部网络依赖的前提下完成 Jenkins 主体、所有必需插件、运行时依赖Java、Git、Docker CLI 等、甚至构建产物缓存的完整闭环。关键词“Jenkins”和“离线部署”之所以长期稳居搜索热榜并非因为教程多恰恰是因为真正能跑通、能维护、能升级的离线方案太少。市面上大量所谓“离线安装包”只打包了 Jenkins WAR 文件却对插件生态视而不见——这就像给你一台没有预装任何驱动的笔记本告诉你“系统已安装好”然后让你自己去网上找显卡、声卡、网卡驱动。而真实生产环境里一个 Java Web 应用的 CI/CD 流水线往往需要gitlab-plugin、docker-workflow、pipeline-utility-steps、ansicolor、blueocean等 15 个核心插件协同工作缺一不可。我亲身参与过三个大型离线 Jenkins 部署项目某省级电力调度系统的自动化测试平台、某国有银行核心交易系统的灰度发布流水线、某航天院所卫星软件的静态代码分析网关。它们的共同点是网络策略由安全团队统一管控所有出向 HTTP/HTTPS 请求默认拒绝所有软件包必须经过内部镜像源签名与漏洞扫描任何未经审批的远程调用都可能触发 SOC 平台告警。在这种环境下“在线安装插件”不是快捷方式而是合规红线。因此本文不讲“如何下载一个离线包”而是带你从零开始构建一套可审计、可复现、可升级的离线 Jenkins 生态——它包含精确到 SHA256 的插件清单、基于实际构建链路反推的最小依赖集、规避update-center.json动态解析的静态元数据注入方法以及最关键的当 Jenkins 版本升级时如何让整套离线插件体系无缝迁移。这不是一次性的手工操作而是一套工程化交付流程。2. 离线部署的本质不是“断网安装”而是“构建一个自洽的软件宇宙”很多人把 Jenkins 离线部署理解为“把官网下载的 war 包拷过去再手动传几个插件 hpi 文件”。这种做法在单机演示时或许可行但一旦进入真实生产环境就会立刻暴露出三个致命盲区插件依赖树未收敛、运行时环境未隔离、升级路径未定义。离线部署真正的技术内核在于将 Jenkins 从一个依赖外部生态的“客户端”重构为一个拥有完整自洽运行边界的“独立服务体”。这需要我们穿透 Jenkins 的三层依赖结构第一层是JVM 运行时边界。Jenkins 是 Java 应用但它对 Java 版本有严格要求。例如 Jenkins 2.387 要求 Java 11而某些国产操作系统默认 JDK 8。更隐蔽的是不同插件对 JVM 参数敏感docker-workflow插件在内存不足时会静默失败jacoco插件在-XX:UseG1GC下存在 GC 暂停时间异常。离线环境中无法动态调整必须在部署前就通过java -version、java -XshowSettings:vm -version精确验证目标节点的 JVM 兼容性并固化启动脚本中的-Xms2g -Xmx4g -XX:MaxMetaspaceSize512m等参数。我曾在一个离线金融项目中因未校验openjdk-11-jre-headless与openjdk-11-jdk的差异导致 Jenkins 启动后无法加载jdk-tool插件整个 JDK 工具链失效。第二层是插件依赖图谱的静态化。这是离线部署最易被忽视的核心。Jenkins 插件不是扁平列表而是深度嵌套的有向无环图DAG。以gitlab-plugin为例它直接依赖workflow-step-api、scm-api、mailer而workflow-step-api又依赖structs、script-securityscript-security再依赖cloudbees-folder……这个链条可能长达 7 层。在线安装时Jenkins Update Center 会自动解析并下载全图离线环境下你必须手动构建这张图。关键在于不能只下载“你认为需要的插件”而要基于实际流水线脚本Jenkinsfile反向推导。比如你的 Jenkinsfile 中有stage(Build) { steps { sh mvn clean package } }那么maven-plugin就是强依赖如果有docker.withRegistry(https://my-registry) { ... }则docker-workflow和其依赖的docker-commons、docker-api必须全部纳入。我推荐使用jenkins-plugin-manager工具的--available-updates模式在联网机器上生成完整依赖树再用--war参数指定 Jenkins WAR 路径确保版本兼容性。命令如下# 在联网机器执行生成离线插件清单 java -jar jenkins-plugin-manager.jar \ --war /path/to/jenkins.war \ --plugin-file plugins.txt \ --available-updates \ --output-directory /tmp/offline-plugins/其中plugins.txt内容为gitlab-plugin:1.5.24 docker-workflow:1.29 pipeline-utility-steps:2.14.0 ansicolor:1.0.4该命令会递归解析所有依赖并下载.hpi文件到/tmp/offline-plugins/同时生成plugin-dependencies.txt记录完整依赖关系。第三层是构建工具链的本地化锚定。Jenkins 本身不编译代码它调用外部工具Maven、Gradle、Node.js、Docker。离线环境中这些工具不能依赖curl https://repo.maven.apache.org/...下载依赖。解决方案是将 Maven 的settings.xml配置为指向内部 Nexus 仓库将 Node.js 的npm config set registry指向内部 Artifactory并将 Docker 构建上下文中的FROM指令全部替换为内部镜像仓库地址如FROM harbor.internal.com/base/openjdk:11-jre-slim。这些配置不是写在 Jenkins UI 里而是作为init.groovy.d/脚本注入 Jenkins 启动过程确保每次重启后自动生效。例如/var/lib/jenkins/init.groovy.d/maven-config.groovyimport jenkins.model.* import hudson.tools.* // 设置全局 Maven 配置 def maven Jenkins.instance.getDescriptor(hudson.tasks.Maven) maven.installations [ new hudson.tasks.Maven.DescriptorImpl().newInstance( name: M3.8.6, home: /opt/maven/apache-maven-3.8.6, properties: [ new hudson.tools.InstallSourceProperty( new hudson.tools.CommandInstaller( wget -qO- https://internal-repo.com/maven/apache-maven-3.8.6-bin.tar.gz | tar -xzf - -C /opt/maven/ ) ) ] ) ] Jenkins.instance.save()这段代码在 Jenkins 启动时自动注册一个名为 “M3.8.6” 的 Maven 安装项并将其home指向预装的本地路径彻底切断对外部 Maven 下载链接的依赖。提示离线部署的成败80% 取决于对这三层边界的清晰界定。不要试图“先装 Jenkins再慢慢补插件”而应以“最终流水线能否跑通”为唯一验收标准反向推导每一层的最小完备集。我在某银行项目中曾因忽略kubernetes-cli插件对kubectl二进制文件的硬依赖导致 Kubernetes 部署阶段始终报错kubectl not found排查耗时两天——根源就是未将构建工具链纳入离线范围。3. 从零构建离线包一份可审计、可复现、可升级的交付物清单构建一个真正可用的 Jenkins 离线包绝非简单地压缩 WAR 文件和插件目录。它是一份具备完整元数据、可被安全团队审计、可被运维团队一键复现、可被架构师规划升级路径的交付物。我将其拆解为六个核心组件每个组件都需提供 SHA256 校验值、来源说明和用途注释。这份清单已在三个金融级项目中落地验证平均部署耗时从 4 小时缩短至 22 分钟。3.1 Jenkins 主体 WAR 文件版本锁定与安全基线选择 Jenkins WAR 文件不是看“最新版”而是看“LTSLong-Term Support稳定版”与企业 Java 生态的匹配度。截至 2024 年Jenkins 2.414.3 LTS 是当前最稳妥的选择它支持 Java 11/17修复了 2023 年曝出的SECURITY-2721CSRF 漏洞和SECURITY-2745XSS 漏洞。下载地址必须来自官方可信源https://get.jenkins.io/war/2.414.3/jenkins.war。切勿使用第三方镜像站因其可能缓存旧版或篡改哈希值。下载后立即计算 SHA256sha256sum jenkins.war # 输出a1b2c3d4e5f6... jenkins.war将此哈希值写入manifest.json{ jenkins-war: { version: 2.414.3, url: https://get.jenkins.io/war/2.414.3/jenkins.war, sha256: a1b2c3d4e5f6..., purpose: Jenkins 主程序LTS 稳定版满足金融行业安全基线要求 } }3.2 插件集合基于流水线脚本的最小依赖集插件不是越多越好而是“刚好够用”。我们采用“Jenkinsfile 驱动法”提取所有生产流水线中的plugins {}块、tool {}块、withDockerRegistry {}等关键指令生成初始插件列表。例如某 Java 项目 Jenkinsfile 中有pipeline { agent any tools { maven M3.8.6; jdk JDK11 } stages { stage(Build) { steps { sh mvn clean package } } stage(Deploy) { steps { script { docker.withRegistry(https://harbor.internal.com) { docker.build(app:${BUILD_ID}).push() } } } } } }对应插件需求为maven-plugin、jdk-tool、docker-workflow、docker-commons、docker-api、workflow-cpsPipeline 引擎、credentials-binding凭据绑定。使用jenkins-plugin-manager生成完整依赖树后得到 37 个.hpi文件。关键操作是为每个.hpi文件单独计算 SHA256并记录其MANIFEST.MF中的Plugin-Version和Plugin-Dependencies字段。例如docker-workflow.hpi的元数据Plugin-Version: 1.29 Plugin-Dependencies: docker-commons:1.19;resolution:required,workflow-step-api:436.v301c009a628a;resolution:required,... SHA256: b2c3d4e5f6a1...将所有插件元数据汇总到plugins-manifest.csv格式为filename,version,sha256,dependencies,source_url docker-workflow.hpi,1.29,b2c3d4e5f6a1...,docker-commons:1.19;workflow-step-api:436.v301c009a628a,https://updates.jenkins-ci.org/download/plugins/docker-workflow/1.29/docker-workflow.hpi此 CSV 文件是安全审计的核心依据证明每个插件均来自官方源且版本可控。3.3 运行时依赖JDK、Git、Docker CLI 的离线安装包Jenkins 本身不包含运行时必须显式提供。我们坚持“最小化原则”只提供流水线实际调用的二进制。例如若流水线只用git clone和git checkout则无需安装完整 Git GUI只需git-core包若只用docker build和docker push则无需docker-compose。各组件清单如下组件版本离线包名SHA256来源说明OpenJDK 1111.0.227openjdk-11-jre-headless_11.0.227-1~deb11u1_amd64.debc3d4e5f6a1b2...Debian 官方仓库apt download openjdk-11-jre-headlessGit2.30.2git_2.30.2-1~deb11u1_amd64.debd4e5f6a1b2c3...Debian 官方仓库apt download gitDocker CLI24.0.7docker-ce-cli_24.0.7-1~debian.11_amd64.debe5f6a1b2c3d4...Docker 官方 APT 仓库apt download docker-ce-cli注意所有 deb/rpm 包必须使用apt download或yumdownloader从目标操作系统同源仓库获取确保 glibc 版本、架构amd64/arm64完全一致。我曾在一个 ARM64 离线集群中误用 amd64 的 Docker CLI 包导致docker version命令报错cannot execute binary file: Exec format error耗费半天排查。3.4 构建工具链Maven、Node.js、Python 的本地化镜像构建工具的依赖管理是离线部署的“暗礁”。Maven 默认从repo.maven.apache.org下载依赖Node.js 从registry.npmjs.org下载包Python 从pypi.org下载库。解决方案是将这些远程仓库的索引与元数据预先同步到内部 Nexus/Artifactory并在 Jenkins 中强制指向它们。具体操作分三步Nexus 同步在 Nexus 3 中创建maven-central、npm-registry、pypi三种 proxy 仓库设置定时任务同步上游。Jenkins 配置注入通过init.groovy.d/脚本在 Jenkins 启动时自动配置全局工具。例如maven-settings.groovyimport jenkins.model.* import hudson.tools.* def settingsXml ?xml version1.0 encodingUTF-8? settings xmlnshttp://maven.apache.org/SETTINGS/1.0.0 mirrors mirror idnexus-internal/id mirrorOf*/mirrorOf urlhttps://nexus.internal.com/repository/maven-public//url /mirror /mirrors /settings def settingsFile new File(/var/lib/jenkins/.m2/settings.xml) settingsFile.parentFile.mkdirs() settingsFile.text settingsXml工具二进制预装将 Maven 3.8.6、Node.js 18.18.2、Python 3.9.18 的 tar.gz 包下载到离线包中并在init.groovy.d/中注册为全局工具。这样流水线中tool M3.8.6就能直接调用本地二进制无需联网下载。3.5 凭据与配置模板避免 UI 手动输入的合规风险在离线环境中通过 Jenkins UI 手动添加 GitLab Token、Docker Registry 密码、Kubernetes Config 是高风险操作它无法审计、无法版本控制、无法批量分发。正确做法是将所有凭据以加密形式预置到JENKINS_HOME/credentials.xml并将关键配置如 GitLab Connection、Docker Registry定义为JENKINS_HOME/init.groovy.d/脚本。例如gitlab-connection.groovyimport jenkins.model.* import org.jenkinsci.plugins.gitlab.GitLabConfiguration def gitlab GitLabConfiguration.get() gitlab.setServerUrl(https://gitlab.internal.com) gitlab.setCredentialsId(gitlab-token) // 此 ID 对应 credentials.xml 中的凭据 gitlab.setIgnoreCertificateErrors(true) gitlab.save()凭据文件credentials.xml使用 Jenkins 内置加密hudson.util.Secret其内容为com.cloudbees.plugins.credentials.SystemCredentialsProvider plugincredentials2.7.1 domainCredentialsMap classhudson.util.CopyOnWriteMap$Hash entry com.cloudbees.plugins.credentials.domains.Domain specifications/ /com.cloudbees.plugins.credentials.domains.Domain list com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl scopeGLOBAL/scope idgitlab-token/id descriptionGitLab API Token for CI/description usernamejenkins-ci/username password{AQAAABAAAAAQA...}/password !-- AES 加密后的密文 -- /com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl /list /entry /domainCredentialsMap /com.cloudbees.plugins.credentials.SystemCredentialsProvider此文件在离线部署时直接覆盖JENKINS_HOME/credentials.xml确保凭据一致性。3.6 自动化部署脚本从解压到可用的一键流程最后将所有组件封装为一个deploy.sh脚本实现“解压即用”。该脚本需完成校验所有 SHA256、安装运行时依赖deb/rpm、解压 Jenkins WAR、复制插件、注入配置脚本、启动 Jenkins 服务。核心逻辑如下#!/bin/bash # deploy.sh - Jenkins 离线部署主脚本 set -e # 任一命令失败即退出 # 1. 校验离线包完整性 echo 校验离线包 SHA256 sha256sum -c manifest.sha256 || { echo 校验失败; exit 1; } # 2. 安装运行时依赖 echo 安装运行时依赖 dpkg -i openjdk-11-jre-headless_*.deb git_*.deb docker-ce-cli_*.deb # 3. 创建 Jenkins 目录结构 mkdir -p /var/lib/jenkins/{plugins,init.groovy.d,tools} # 4. 部署 Jenkins WAR 和插件 cp jenkins.war /usr/share/jenkins/jenkins.war cp plugins/*.hpi /var/lib/jenkins/plugins/ # 5. 注入配置脚本 cp init.groovy.d/*.groovy /var/lib/jenkins/init.groovy.d/ cp credentials.xml /var/lib/jenkins/credentials.xml # 6. 启动 Jenkins systemctl daemon-reload systemctl enable jenkins systemctl start jenkins echo Jenkins 离线部署完成访问 http://$(hostname -I | awk {print $1}):8080 此脚本在目标节点执行bash deploy.sh22 分钟内即可完成从零到可用的全过程。所有操作均有日志输出失败时自动终止并提示原因符合金融级运维的可观测性要求。4. 离线环境下的 Jenkins 升级如何让“断网”不等于“停滞”在离线环境中Jenkins 升级常被视为“不可能任务”新版本 WAR 文件怎么传新插件怎么下载旧插件兼容性如何验证许多团队因此长期停留在老旧版本承受着已知安全漏洞的风险。实际上离线升级完全可以做到零停机、零配置变更、零流水线中断其核心在于将升级过程解耦为三个独立、可验证的阶段WAR 升级、插件升级、配置验证。4.1 WAR 升级滚动替换与健康检查的双保险Jenkins WAR 升级本身很简单停止服务、替换 WAR、重启。但离线环境的特殊性在于你无法依赖 Jenkins UI 的“升级中心”进行平滑过渡。我们的方案是在升级前预先在离线包中准备好新旧两个 WAR 文件并设计一个带健康检查的滚动切换脚本。步骤如下准备双 WAR 目录在/usr/share/jenkins/下创建jenkins-old.war当前版本和jenkins-new.war目标版本。编写健康检查脚本health-check.sh用于验证 Jenkins 是否真正就绪不仅是进程存活而是 API 可用#!/bin/bash # health-check.sh - 检查 Jenkins API 是否就绪 for i in {1..60}; do if curl -sf http://localhost:8080/api/json?treemode /dev/null; then echo Jenkins API 就绪 exit 0 fi sleep 5 done echo Jenkins 启动超时 exit 1执行滚动升级# 1. 停止当前 Jenkins systemctl stop jenkins # 2. 备份旧 WAR可选 cp /usr/share/jenkins/jenkins.war /usr/share/jenkins/jenkins.war.bak # 3. 替换为新 WAR cp /usr/share/jenkins/jenkins-new.war /usr/share/jenkins/jenkins.war # 4. 启动并等待健康检查 systemctl start jenkins ./health-check.sh # 5. 验证 UI 可访问可选 curl -sf http://localhost:8080/login | grep -q Sign in echo UI 就绪此流程确保升级后 Jenkins 能正常响应 API 和 UI 请求避免“进程起来但功能异常”的假成功。4.2 插件升级依赖图谱的增量更新与冲突检测插件升级是离线升级中最复杂的环节。盲目替换所有.hpi文件会导致依赖冲突如新workflow-cps要求script-security1200而旧版只有 1100。我们的方法是基于 Jenkins 新版本的plugin-dependencies.txt与当前离线包中的插件清单做差分仅升级必要插件并用jenkins-plugin-manager验证依赖一致性。操作流程生成新旧插件清单对比# 在联网机器用新 Jenkins WAR 生成依赖 java -jar jenkins-plugin-manager.jar \ --war jenkins-2.414.3.war \ --plugin-file plugins.txt \ --available-updates \ --output-directory /tmp/new-plugins/ # 提取新插件版本号 find /tmp/new-plugins/ -name *.hpi | xargs -I{} sh -c echo $(basename {}) $(unzip -p {} META-INF/MANIFEST.MF | grep Plugin-Version | cut -d -f2) new-versions.txt # 与当前离线包中的 old-versions.txt 对比 diff old-versions.txt new-versions.txt | grep ^ | cut -d -f2,3 upgrade-list.txt下载增量插件upgrade-list.txt内容为docker-workflow.hpi 1.30 script-security.hpi 1210.v73f461150115离线验证依赖在目标节点用jenkins-plugin-manager检查增量插件是否与现有环境兼容# 检查 script-security 1210 是否与当前 workflow-cps 兼容 java -jar jenkins-plugin-manager.jar \ --war /usr/share/jenkins/jenkins.war \ --plugin script-security:1210.v73f461150115 \ --dry-run \ --verbose--dry-run参数会模拟安装过程输出所有将被下载的依赖插件及版本。若输出中出现ERROR: Plugin script-security (1210.v73f461150115) requires workflow-cps (2803.vb_9828891a_35e) but current version is 2799.vb_9828891a_35e则说明需同步升级workflow-cps。此验证必须在离线环境中完成确保升级包 100% 可用。4.3 配置验证用真实流水线脚本做回归测试升级完成后最可靠的验证不是看 Jenkins UI 是否显示“升级成功”而是用一条真实的、覆盖核心能力的流水线脚本执行一次端到端构建。我们设计了一个smoke-test-pipeline.groovy它包含所有关键能力点pipeline { agent any environment { TEST_ENV offline-smoke-test } stages { stage(Checkout) { steps { checkout scm } } stage(Build) { steps { sh echo Build step executed } } stage(Test) { steps { script { // 验证凭据是否可用 withCredentials([string(credentialsId: gitlab-token, variable: GITLAB_TOKEN)]) { sh echo GitLab token accessible: ${GITLAB_TOKEN:0:4}... } } } } stage(Deploy) { steps { script { // 验证 Docker CLI 是否可用 sh docker version | head -n 3 } } } } post { always { echo Smoke test completed } } }将此脚本保存为JENKINS_HOME/jobs/smoke-test/config.xml然后通过 Jenkins CLI 触发构建java -jar jenkins-cli.jar -s http://localhost:8080/ -auth admin:password build smoke-test若构建成功且所有sh步骤输出符合预期则证明升级后 Jenkins 的核心能力SCM、凭据、Docker全部正常。此回归测试脚本应随离线包一同交付成为每次升级的强制验证环节。经验之谈在某省级政务云项目中我们曾因跳过插件依赖验证直接升级gitlab-plugin到 1.5.25导致其依赖的scm-api3.12 与旧版workflow-api1213 冲突整个 GitLab 集成失效。后续我们强制加入--dry-run验证并将smoke-test纳入 CI/CD 流水线升级成功率提升至 100%。5. 离线部署的终极护城河构建一个自我演进的本地更新中心真正的离线 Jenkins 生态不应是一个“静态快照”而应是一个可自我演进、可按需扩展、可与外部世界安全同步的微型更新中心。这需要我们在离线环境中重建 Jenkins Update Center 的核心能力插件元数据索引、版本依赖解析、安全签名验证。我们不追求 100% 复刻官方中心而是构建一个轻量、可靠、可审计的本地替代方案。5.1 本地 update-center.json 的生成与签名Jenkins 启动时会从https://updates.jenkins-ci.org/update-center.json下载一个巨大的 JSON 文件其中包含所有插件的元数据名称、版本、URL、SHA256、依赖。离线环境下我们必须提供一个等效的本地文件。生成流程如下在联网机器下载并解析官方 update-center.json# 下载官方元数据 curl -s https://updates.jenkins-ci.org/update-center.json | sed 1d;$d update-center.json # 提取我们关心的插件基于 plugins-manifest.csv awk -F, {print $1} plugins-manifest.csv | while read plugin; do # 从 update-center.json 中提取该插件的最新版本元数据 jq -r --arg p $plugin .plugins[$p] | select(. ! null) | \($p) \(.version) \(.url) \(.sha256) update-center.json done local-update-center.txt构建精简版 update-center.json将local-update-center.txt转换为标准 JSON 格式{ updateCenterVersion: 1, plugins: { gitlab-plugin: { name: gitlab-plugin, version: 1.5.24, url: https://updates.jenkins-ci.org/download/plugins/gitlab-plugin/1.5.24/gitlab-plugin.hpi, sha256: a1b2c3d4..., dependencies: [{name:scm-api,version:2.6.4},{name:workflow-step-api,version:2.22}], requiredCore: 2.222.1 }, docker-workflow: { name: docker-workflow, version: 1.29, url: https://updates.jenkins-ci.org/download/plugins/docker-workflow/1.29/docker-workflow.hpi, sha256: b2c3d4e5..., dependencies: [{name:docker-commons,version:1.19}], requiredCore: 2.222.1 } } }对本地 update-center.json 进行 GPG 签名确保其不被篡改。使用公司内部 GPG 密钥gpg --detach-sign --armor local-update-center.json # 生成 local-update-center.json.asc将local-update-center.json和local-update-center.json.asc一同放入离线包。5.2 Jenkins 配置本地更新中心绕过 HTTPS 重定向陷阱Jenkins 默认从https://updates.jenkins-ci.org/update-center.json加载且会尝试重定向到https://www.google.com/...这是 Jenkins 的一个历史遗留行为。在离线环境中我们必须强制 Jenkins 使用本地文件。方法是修改JENKINS_HOME/hudson.model.UpdateCenter.xml注入本地 URL并禁用 SSL 验证因本地文件无 HTTPS。init.groovy.d/update-center.groovyimport jenkins.model.* import hudson.model.* def uc Jenkins.instance.getUpdateCenter() uc.updateCenterUrl file:///var/lib/jenkins/update-center.json uc.pluginRepositoryUrl file:///var/lib/jenkins/plugins/ // 禁用 SSL 验证针对 file:// 协议 System.setProperty(hudson.model.UpdateCenter.neverVerify, true) // 强制刷新元数据 uc.updateAllSites() Jenkins.instance.save()同时将local-update-center.json复制到/var/lib/jenkins/update-center.json。这样当管理员在 Jenkins UI 点击 “Manage Plugins” - “Available” 时Jenkins 会从本地文件读取插件列表而非尝试连接外部网络。5.3 安全同步机制建立离线环境与外部世界的“气隙通道”一个静态的本地更新中心仍会过时。我们需要一种安全、可控、可审计的同步机制让离线环境能定期获取外部更新。我们称之为“气隙通道”Air-Gap Channel其核心是所有同步操作必须通过物理介质U 盘/光盘进行且每一步都有数字签名与哈希校验。流程如下同步计划制定安全团队每月初发布《Jenkins 更新公告》列出本月推荐升级的插件基于 CVE 修复、功能增强并提供对应的update-bundle.zip含新update-center.json、新插件.hpi、GPG 签名。离线环境接收运维人员将update-bundle.zip拷贝到离线节点执行校验# 校验 ZIP 完整性 sha256sum -c update-bundle.sha256 # 校验 GPG 签名 gpg --verify update-bundle.zip.asc update-bundle.zip # 解压并校验内部文件
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进