☰
Rundeck RPM 包 Docker 安装测试指南:packaging/test/docker/rpminstall 全流程解析
2026/10/6 2:25:51 网站建设 项目流程
  • 运维
  • 任务调度
  • 后端

【免费下载链接】rundeck

Enable Self-Service Operations: Give specific users access to your existing tools, services, and scripts

项目地址:https://gitcode.com/gh_mirrors/ru/rundeck
点击查看免费下载

Rundeck 是一个面向自助运维(Self-Service Operations)的开源任务调度与自动化平台,允许把既有脚本、服务和工具安全地授权给特定用户执行。在发布 RPM/Deb 安装包之前,项目需要一套可靠的手段来验证"从零安装"是否成功。本文围绕仓库中 packaging/test/docker/rpminstall/Readme.md 展开,讲解如何用 Docker 对本地构建的 Rundeck RPM 包进行安装测试,并深入剖析配套的 Dockerfile、入口脚本与冒烟测试逻辑。读完本文,你将掌握手动与自动化两条测试路径,理解容器内服务启动、健康等待、加密配置校验等关键机制,并能对照源码定位每一步的实际行为。

这套测试方案解决什么问题

Rundeck 的发行包(RPM / Deb / WAR)由独立的packaging打包工程构建,产出物最终会被用户直接安装在生产服务器上。如果安装脚本、初始化逻辑或默认配置在真实环境里出错,代价很高。因此项目在 packaging/test/docker/rpminstall 提供了一套Docker 化的 RPM 安装测试方案:

  • 在容器内模拟一台干净的 RHEL 系主机(基于 Red Hat UBI8);
  • 将本地刚构建出来的rundeck-*.rpm复制进镜像并执行rpm -ivh;
  • 安装完成后自动启动rundeckd服务,并等待日志中出现成功标志;
  • 执行一组冒烟测试,验证服务状态、重复启动保护与密钥加密配置。

README 开宗明义:"You can use this to test install of a locally build .rpms for rundeck."——它的定位就是本地构建产物的安装验证,而非单元测试或功能测试。

配套的 Debian 版本位于 packaging/test/docker/debinstall,两者结构完全对称(仅包管理器不同:rpm -ivh对dpkg -i),本文以 RPM 侧为主线讲解。

前置条件:先构建出 RPM 产物

测试的前提是本地已构建出 RPM 包。从打包工程的 CI 脚本 scripts/circleci/packaging-functions.sh 可以看到标准构建方式:

packaging_create_packages() { local RELEASE_NUM="1" mkdir -p "${PACKAGING_DIR}/packaging/artifacts/" copy_rundeck_war "${PACKAGING_DIR}/packaging/artifacts/" cd "${PACKAGING_DIR}/packaging" LIB_DIR="${PACKAGING_DIR}/lib" ./gradlew ${GRADLE_BASE_OPTS} -PlibsDir=${LIB_DIR} -PpackageRelease=$RELEASE_NUM clean packageArtifacts }

即进入 packaging 目录,用 Gradle 执行packageArtifacts,RPM 产物会落入packaging/build/distributions/(该路径在 packaging/test/test-docker-install-common.sh 中被显式引用:PATTERN="${PACKAGING_DIR}/packaging/build/distributions/*.${PACKAGE_TYPE}")。测试脚本正是从这个目录收集.rpm文件。

方案一:按 README 手动构建镜像并安装(挂载源码卷)

packaging/test/docker/rpminstall/Readme.md 给出了最直接的手动流程,全文核心命令如下:

# 1) 从 rundeck 源码仓库根目录执行,构建镜像 docker build docker/rpminstall # 2) 复制镜像 id,例如 53c9cb76cbe1 # (构建输出末尾的 Successfully built <id> 即是) # 3) 以交互方式运行容器,并把当前源码目录挂载进容器 docker run -it -v $PWD:/home/rundeck/rundeck $IMG bash

其中关键点:

  • -v $PWD:/home/rundeck/rundeck:README 明确说明要从源码仓库根目录使用该挂载,将源码卷挂到容器内/home/rundeck/rundeck。容器的工作目录WORKDIR与VOLUME都指向$HOME/rundeck(即/home/rundeck/rundeck),挂载后容器内即可访问源码与构建产物。
  • 交互式 bash:docker run ... $IMG bash让你进入容器后手工执行rpm -ivh、service rundeckd start等操作,适合调试。

需要注意,README 中docker build docker/rpminstall指向的是根目录下 docker/rpminstall 这套早期版本镜像(基于 CentOS 6、Java 1.8,入口脚本 docker/rpminstall/entry.sh 直接到/home/rundeck/packaging/packaging/rpmdist/RPMS/noarch/下查找rundeck*.rpm)。而 packaging/test/docker/rpminstall 目录内是一套更新的工程化流程(基于 Red Hat UBI8、Java 17,通过脚本自动复制 RPM 并构建),两者目录名与 README 内容一致,但镜像基础与安装路径不同。日常建议优先使用下面介绍的自动化脚本。

方案二:自动化脚本构建与运行(推荐)

仓库提供了两个驱动脚本:

  • packaging/test/test-docker-install-rpm.sh:RPM 入口,设置DIR="${PACKAGING_DIR}/test/docker/rpminstall"、COMMON="redhatubi8"、PACKAGE_TYPE="rpm",然后加载公共逻辑并依次调用build与run;
  • packaging/test/test-docker-install-common.sh:公共实现,负责基础镜像构建、产物复制、镜像构建与容器运行。

第一步:build —— 构建基础工具镜像

build()的逻辑:

build(){ local TAG="rdpro-$COMMON" docker build --no-cache -t "$TAG-util" \ -f "${PACKAGING_DIR}/test/docker/installcommon/${COMMON}.Dockerfile" \ "${PACKAGING_DIR}/test/docker/installcommon" }

即先构建名为rdpro-redhatubi8-util的基础镜像,其定义在 packaging/test/docker/installcommon/redhatubi8.Dockerfile:

FROM redhat/ubi8 RUN yum -y update # Grails 7: Java 17 required RUN yum -y install java-17-openjdk java-17-openjdk-devel initscripts openssh openssl COPY scripts/rd-util.sh /rd-util.sh COPY scripts/rpm-tests.sh /init-tests.sh

值得注意:基础镜像预装了Java 17,注释明确写着 "Grails 7: Java 17 required"——这正对应 rundeckapp 基于 Grails 7 的运行时要求;同时把公共工具 packaging/test/docker/installcommon/scripts/rd-util.sh 与测试脚本rpm-tests.sh拷贝为/rd-util.sh与/init-tests.sh,供后续所有安装镜像复用。

第二步:run —— 复制 RPM 并构建最终测试镜像

run(){ local TAG="rdpro-$COMMON" DEBS=$(list_debs) # packaging/build/distributions/*.rpm for DEB in $DEBS; do cp -pv "$DEB" "$DIR/rundeck.rpm" # 复制为 rpminstall 目录下的 rundeck.rpm docker build --no-cache "$DIR" -t "$TAG":latest docker run -it -e "PACKAGE=/rundeck/rundeck.rpm" "$TAG":latest -test done }

该循环对packaging/build/distributions/下的每个 RPM 执行三件事:

  1. 将 RPM 复制为 packaging/test/docker/rpminstall 目录下的rundeck.rpm,作为构建上下文的一部分;
  2. docker build构建最终镜像,镜像名rdpro-redhatubi8:latest;
  3. 运行容器,通过-e "PACKAGE=/rundeck/rundeck.rpm"注入安装包路径环境变量,并以-test参数触发安装后的冒烟测试。

整个测试链路会被 CI 定期触发:见 scripts/circleci/packaging-functions.sh 的packaging_test_packages(),它依次调用test-docker-install-deb.sh与test-docker-install-rpm.sh。

最终镜像剖析:Dockerfile 与入口脚本

Dockerfile 关键指令

packaging/test/docker/rpminstall/Dockerfile:

FROM rdpro-redhatubi8-util:latest RUN useradd rundeck ENV USERNAME=rundeck \ USER=rundeck \ HOME=/home/rundeck \ LOGNAME=$USERNAME \ TERM=xterm-256color ENV JAVA_HOME=/etc/alternatives/java_sdk COPY --chown=rundeck:root rundeck.rpm /rundeck/rundeck.rpm COPY entry.sh /entry.sh RUN chmod +x /entry.sh VOLUME $HOME/rundeck WORKDIR $HOME/rundeck EXPOSE 4440 ENTRYPOINT ["/entry.sh"]

要点:

  • 预置 RPM:COPY rundeck.rpm /rundeck/rundeck.rpm,安装包在构建阶段即被打进镜像(因此run()必须先复制产物);
  • 专用用户:useradd rundeck,与安装脚本中创建同名服务用户保持一致;
  • 端口与入口:EXPOSE 4440(Rundeck Web 默认端口),入口固定为/entry.sh,镜像启动即进入安装流程。

entry.sh:安装 → 授权 → 展示配置 → 启动

packaging/test/docker/rpminstall/entry.sh:

#!/bin/bash . /rd-util.sh set -e FLAV=${EDITION:-cluster} # 版本风味,默认 cluster DIR="${HOME}/${PACKAGING_DIR}/packaging/build/distributions" install_rundeck(){ echo "Install Rundeck from file: " $DIR/$PACKAGE rpm -ivh "$PACKAGE" # 实际安装的是环境变量 PACKAGE 指向的绝对路径 } main(){ install_rundeck cp_license echo_config entry_start "$@" } main "$@"

执行顺序与作用:

  1. rpm -ivh "$PACKAGE"——安装 RPM。容器运行时通过-e "PACKAGE=/rundeck/rundeck.rpm"指定;EDITION环境变量(默认cluster)用于选择版本风味;
  2. cp_license——若设置了LICENSE_FILE/LICENSE环境变量,则把许可证复制为/etc/rundeck/rundeckpro-license.key(详见 rd-util.sh);
  3. echo_config——打印/etc/rundeck/rundeck-config.properties内容,方便确认安装生成的默认配置;
  4. entry_start "$@"——启动服务并按参数执行后续动作。

rd-util.sh:服务启动与健康等待

rd-util.sh 的entry_start是整个流程的"交通枢纽":

entry_start(){ if [ "$1" != "-skipstart" ]; then service rundeckd start wait_for_start /var/log/rundeck/service.log else shift fi if [ "$1" = "-test" ] ; then shift . /init-tests.sh test_all fi exec "$@" }
  • 默认行为:启动rundeckd并调用wait_for_start等待就绪;
  • -skipstart:跳过启动(适用于已运行的服务或纯安装验证);
  • -test:加载/init-tests.sh(即rpm-tests.sh)并执行test_all冒烟测试;
  • 末尾exec "$@":把剩余参数作为命令执行——README 中docker run ... $IMG bash之所以能进入交互 shell,正是因为这行把bash交棒给了容器主进程。

wait_for_start的实现很有参考价值:它以30 次 × 10 秒 ≈ 5 分钟为上限轮询/var/log/rundeck/service.log,直到出现Grails application running这一成功标志;期间若service rundeckd status失败则立即报错退出。这与 packaging/lib/rpm/etc/rc.d/init.d/rundeckd 启动脚本把日志写入/var/log/rundeck/service.log的行为一一对应。

容器内冒烟测试:rpm-tests.sh

安装并启动之后,-test参数触发 packaging/test/docker/installcommon/scripts/rpm-tests.sh 中三组断言:

测试函数断言内容失败处理
test_statusservice rundeckd status输出包含is running打印FAILED并exit 2
test_start_twice对已运行的rundeckd再次start输出Already started(防重复启动)同上
test_encryption_password配置中不存在明文占位default.encryption.password,且rundeck.storage.converter.N.config.password为 32 位十六进制同上

第三项测试值得展开。打包模板 packaging/lib/common/etc/rundeck/rundeck-config.properties 中默认写着:

rundeck.storage.converter.1.type=aes-gcm-encryption rundeck.storage.converter.1.path=keys rundeck.storage.converter.1.config.password=default.encryption.password

而 RPM 安装脚本 packaging/lib/rpm/scripts/postinst.sh 在安装后会把占位密码替换为随机值:

STORAGE_PASS=$(openssl rand -hex 16) sed -i -E 's/^rundeck\.storage\.converter\.([0-9]+)\.config\.password=default\.encryption\.password$/rundeck.storage.converter.\1.config.password='"$STORAGE_PASS"'/' "$RDECK_CONFIG"

openssl rand -hex 16生成 32 位十六进制随机串,与测试里的^[0-9a-f]{32}$正则完全吻合。这条测试本质上在验证:安装过程确实完成了密钥存储加密口令的随机化,未把不安全的默认占位值留在生产配置里(对应 aes-gcm 加密插件 plugins/aes-gcm-encryption-plugin 的存储加密链路)。

安装脚本在背后做了什么

理解容器内安装测试的断言,还需要知道 RPM 安装脚本自身的动作。以 packaging/lib/rpm/scripts/postinst.sh 为例:

  • 生成 SSH 密钥:若~rundeck/.ssh/id_rsa不存在则生成(Rundeck 节点通信的基础设施);
  • 写入 server UUID:若framework.properties中尚无合法的 UUID(8-4-4-4-12格式),追加随机生成的rundeck.server.uuid;
  • 随机化加密口令:如上文所示替换两处default.encryption.password;
  • 支持目录重定位:通过RPM_INSTALL_PREFIX0~4可自定义 home、etc、bin、log、init 目录,非默认路径时会批量改写profile、配置与 init 脚本;
  • 注册服务:当 init 目录为默认的/etc/rc.d/init.d时执行/sbin/chkconfig --add rundeckd。

而 packaging/lib/rpm/scripts/preinst.sh 负责前置校验:解析java -version得到主版本号,要求 Java 17、21 或 25,否则exit 1;随后创建rundeck用户/组及 home 目录。这与基础镜像预装 Java 17 的选择相互印证,也解释了为什么这套新方案把旧版 CentOS 6 + Java 8 镜像替换为 UBI8 + Java 17。

常见问题与调试技巧

结合源码实现,排查时可重点关注以下几点:

  • 找不到 RPM 报错:旧版入口脚本 docker/rpminstall/entry.sh 会检查$HOME/packaging/packaging/rpmdist/RPMS/noarch/rundeck*.rpm,若文件不存在exit 2。请确认先执行了./gradlew packageArtifacts产出包,再运行测试脚本;
  • 启动超时:wait_for_start最多等约 5 分钟。若超时,它会打印service.log全文帮助定位(如rundeckd status失败也会立即报错并输出日志);
  • 进入容器手工验证:docker run -it ... $IMG bash可进入 shell 手动执行rpm -qa | grep rundeck、service rundeckd status、cat /etc/rundeck/rundeck-config.properties等检查;
  • 跳过启动/测试:追加-skipstart仅做安装验证,追加-test执行三组冒烟断言,二者可组合控制容器行为;
  • 指定版本风味:通过-e EDITION=cluster(默认)等环境变量控制FLAV。

总结

从 packaging/test/docker/rpminstall/Readme.md 的三步命令出发,Rundeck 项目把"本地 RPM 安装验证"沉淀为一套完整的 Docker 测试体系:Gradle 构建产物 →test-docker-install-rpm.sh自动复制并构建镜像 →entry.sh执行rpm -ivh→rd-util.sh启动并等待Grails application running→rpm-tests.sh验证服务状态、幂等启动与加密口令随机化。这条链路同时覆盖了安装、初始化脚本(preinst.sh / postinst.sh)与运行时健康三方面的回归验证,是理解 Rundeck 发行包质量保障机制的理想切入点,也完全可以作为你自己项目"发行包容器化测试"的参考模板。Debian 一侧仅需把rpm -ivh换成dpkg -i,流程与本文完全对称,可对照 packaging/test/docker/debinstall 继续阅读。

  • 运维
  • 任务调度
  • 后端

【免费下载链接】rundeck

Enable Self-Service Operations: Give specific users access to your existing tools, services, and scripts

项目地址:https://gitcode.com/gh_mirrors/ru/rundeck
点击查看免费下载
上一篇:openEuler数据库容器化指南:MySQL、PostgreSQL与Redis部署教程
下一篇:OpenEuler Conch快速入门:10分钟搭建AI Agent安全隔离环境

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询