1. 概述

Jenkins 是一个功能强大的自动化流程管理平台,主要由 Java 编写。它通过定义每个步骤的脚本实现高效的流水线管理。虽然 Jenkins 的部署过程通常较为简单,但在实际环境中仍可能遇到一些与环境相关的问题。

在本文中,我们将手动安装 Jenkins,并探讨可能导致服务无法启动的常见问题。首先,我们会介绍基于包管理器的基本安装流程;接着分析 Jenkins 的核心依赖,特别是 Java 相关的问题;随后讨论由于依赖配置不当导致的启动失败及其解决办法;最后展示一种更简洁、更稳定的方式来部署 Jenkins。

本文中的示例代码已在 Debian 12(Bookworm)和 GNU Bash 5.2.15 环境下测试通过,适用于大多数 POSIX 兼容系统。


2. 安装 Jenkins 包

尽管 Jenkins 有多种运行方式,通过包管理器进行经典安装仍然是最常见的部署方法之一

首先,确保我们添加了 Jenkins 的官方仓库:

$ wget --output-document=/usr/share/keyrings/jenkins-keyring.asc https://pkg.jenkins.io/debian/jenkins.io-2023.key

上面的命令使用 wget 下载 Jenkins 的仓库签名密钥,并保存到 /usr/share/keyrings/jenkins-keyring.asc。如需获取最新密钥,可访问 https://pkg.jenkins.io/debian/

接下来,创建仓库配置文件:

$ echo 'deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian binary/' >> /etc/apt/sources.list.d/jenkins.list

更新包列表并安装 Jenkins:

$ apt-get update && apt-get install jenkins

或者,我们也可以从 https://get.jenkins.io/war-stable/ 手动下载 Jenkins 的通用 WAR 包进行部署。

⚠️ 注意:安装过程中 Jenkins 会尝试自动启动服务,这可能导致启动失败。


3. Jenkins 服务启动失败的常见原因

Jenkins 服务启动失败可能由多种原因引起,例如:

✅ Jenkins 未正确设置 shell 环境变量
✅ 安装脚本未正确执行
✅ 配置文件损坏或缺失
✅ 可执行文件损坏或缺失

虽然这些情况不会直接导致服务无法启动,但它们可能是更严重问题的表征。

Jenkins 启动失败的最终原因通常集中在其启动脚本上,路径为 /usr/bin/jenkins/usr/bin/jenkins.sh

我们可以通过 grep 查看脚本中可能导致失败的判断逻辑:

$ grep 'die ' /usr/bin/jenkins -C 1
                if $required && [ -z "${val}" ]; then
                        die "check_env: ${var} must be non-empty"
                fi
--
        if [ -n "${JENKINS_HOME}" ]; then
                [ -d "${JENKINS_HOME}" ] || die "${JENKINS_HOME} is not a directory"
        fi
        [ -f "${JENKINS_WAR}" ] || die "${JENKINS_WAR} is not a file"

        infer_java_cmd || die 'failed to find a valid Java installation'

从上面的脚本片段可以看出,以下几种情况会导致启动失败:

  • 必要的环境变量为空(如 JENKINS_WAR
  • JENKINS_HOME 路径不存在或不是一个目录
  • WAR 文件不存在
  • Java 安装异常

⚠️ 即使删除了 $JENKINS_HOME 下的 config.xml 文件,Jenkins 仍能正常启动,因此 Java 通常是导致启动失败的罪魁祸首。


4. Jenkins 的 Java 依赖

Jenkins 最重要的外部依赖是 Java 运行环境(JRE)或开发工具包(JDK)。如果在安装 Jenkins 时未安装 Java,即使安装成功,服务也会因缺少依赖而无法启动。

安装 Jenkins 时,可能出现如下错误:

$ apt-get install jenkins
[...]
Created symlink /etc/systemd/system/multi-user.target.wants/jenkins.service → /lib/systemd/system/jenkins.service.
Job for jenkins.service failed because the control process exited with error code.
See "systemctl status jenkins.service" and "journalctl -xeu jenkins.service" for details.
[...]

进一步查看日志:

$ journalctl -xeu jenkins.service
Feb 02 10:00:00 xost jenkins[6660]: jenkins: failed to find a valid Java installation

问题明确:缺少有效的 Java 安装。

解决方法:安装 OpenJDK:

$ apt-get install openjdk-17-jre

安装完成后,验证 Java 是否安装成功:

$ java -version
openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+9-Debian-1deb12u1)
OpenJDK 64-Bit Server VM (build 17.0.9+9-Debian-1deb12u1, mixed mode, sharing)

再次启动 Jenkins 服务:

$ systemctl start jenkins.service
$ systemctl status jenkins.service
● jenkins.service - Jenkins Continuous Integration Server
     Loaded: loaded (/lib/systemd/system/jenkins.service; enabled; preset: enabled)
     Active: active (running) since Thu 2024-02-02 16:00:01 UTC; 966ms ago
   Main PID: 6111 (java)

⚠️ 注意:有时即使安装了 Java,服务仍可能无法启动,还需进一步排查。


5. Jenkins 的 Java 配置问题

5.1. Java 是否在 $PATH

即使安装了 Java,如果 java 命令不在环境变量 $PATH 中,Jenkins 也无法识别:

$ java
bash: /usr/bin/java: No such file or directory

常见原因包括:

  • 安装路径未加入 $PATH
  • 安装方式未创建符号链接
  • 安装后未重启 shell 会话

解决方法:

$ update-alternatives --config java

选择正确的 Java 安装路径。

5.2. 支持的 Java 版本

Jenkins 不同版本对 Java 的兼容性要求不同:

支持的 Java 版本 长期支持版本(LTS) 周发布版本(Weekly)
Java 11, 17, 21 2.426.1(2023年11月) 2.419(2023年8月)
Java 11, 17 2.361.1(2022年9月) 2.357(2022年6月)
Java 8, 11, 17 2.346.1(2022年6月) 2.340(2022年3月)
Java 8, 11 2.164.1(2019年3月) 2.164(2019年2月)

⚠️ Jenkins 控制器节点必须使用支持的 Java 版本。

5.3. 设置 Jenkins 使用的 Java 版本

系统中可能安装了多个 Java 版本,Jenkins 默认使用 java 命令,但可能不是预期版本。

解决方案包括:

✅ 修改 systemd 服务文件:

Environment="JAVA_HOME=/custom/java/path"

✅ 修改 Jenkins 启动脚本 /usr/bin/jenkins 中的 infer_java_cmd() 函数:

infer_java_cmd() {
  if [ -n "${JENKINS_JAVA_CMD}" ] && [ -x "${JENKINS_JAVA_CMD}" ]; then
    return 0
  fi

  if [ -n "${JAVA_HOME}" ] && [ -x "${JAVA_HOME}/bin/java" ]; then
    JENKINS_JAVA_CMD="${JAVA_HOME}/bin/java"
    return 0
  fi

  JENKINS_JAVA_CMD="$(command -v java)" || return "$?"
}

✅ 使用 update-alternatives 设置默认 Java:

$ update-alternatives --config java

5.4. 安装旧版本或替代 Java

某些情况下,我们需要安装非官方源提供的 Java 版本:

⚠️ 注意:安装旧版本时可能会遇到依赖冲突,需手动解决。


6. 使用容器避免部署问题

最简单、最稳定的 Jenkins 部署方式是使用容器化部署,例如 Docker:

$ docker run --name jenkins --publish 8080:8080 --detach jenkins/jenkins:lts

该命令会:

  1. 启动一个基于 jenkins/jenkins:lts 镜像的容器
  2. 将容器的 8080 端口映射到宿主机
  3. 以后台方式运行容器

⚠️ 注意:默认情况下,容器停止后数据会丢失。可通过挂载卷保留数据:

$ docker run --name jenkins \
  --publish 8080:8080 \
  --volume /var/data/jenkins_home:/var/jenkins_home \
  --detach \
  jenkins/jenkins:lts

7. 总结

本文讨论了 Jenkins 在部署和启动过程中可能遇到的常见问题,尤其是 Java 依赖相关的错误。

尽管 Jenkins 本身是一个相对独立的单体应用,但其运行依赖于外部组件(如 Java)。只有在 Java 环境配置正确、版本兼容的前提下,Jenkins 才能正常启动。

推荐做法:

✅ 使用容器化部署(如 Docker)来简化配置
✅ 确保 Java 安装并配置正确
✅ 使用 update-alternatives 管理多版本 Java
✅ 避免直接修改 Jenkins 启动脚本或服务文件(除非调试)


原始标题:Jenkins Deployment and Service Start Issues