Featured image of post Linux运维 02:一台服务器再搭一套完全隔离的 Jenkins(华为云 EulerOS 实战)

Linux运维 02:一台服务器再搭一套完全隔离的 Jenkins(华为云 EulerOS 实战)

在已有一套 Jenkins 的服务器上用 Docker 再搭一套完全隔离的 Jenkins:独立端口、home 与插件体系,Java 17 选型、初始化、升级到工具配置全过程,附 4 个踩坑与解法。

场景:服务器上已经跑着一套给老项目用的 Jenkins(8080 端口),新项目(myapp)需要一套完全独立的 Jenkins——独立端口、独立 home 目录、独立插件体系,互不影响。 系统:Huawei Cloud EulerOS 2.0 (x86_64),部署方式:Docker + docker-compose。 本文记录从环境调研到初始化、升级、工具配置的全过程,以及踩到的 4 个坑。 (文中密码、密钥均已脱敏,请替换成自己的。)


一、先看清楚家底:环境调研

1.1 系统源里有什么 Java?——一个坏消息

新项目后端是 Spring Boot 3.x,要求 Java 17+。按惯例先查系统源:

1
2
3
4
5
cat /etc/os-release
# NAME="Huawei Cloud EulerOS"
# VERSION="2.0 (x86_64)"

yum list available 2>/dev/null | grep -iE 'openjdk'

结果只有:

1
2
java-1.8.0-openjdk    1:1.8.0.432.b06-0.hce2
java-11-openjdk       1:11.0.30.7-0.hce2

结论:HCE 2.0 的 yum/dnf 源里没有 java-21-openjdk,连 java-17 也没有,只有 1.8 和 11。直接 yum install java-21-openjdk 必然报"没有任何匹配"。

dnf 和 yum 在 HCE 上是同一个源,换命令没用。

这件事直接影响了后面的技术选型:所有 Java 都不装在系统上——Jenkins 本体用官方镜像(自带 JDK),构建用 JDK 来自 Adoptium 官方 tar.gz 解压包,应用运行用 Docker 镜像。系统层面零 Java 安装。

1.2 旧 Jenkins 是怎么部署的?

服务器上已有一套给 bid 项目用的 Jenkins,先摸清它的部署方式(照抄成熟经验,避开它的资源):

1
2
3
4
docker ps
# jenkins   0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp

cat /home/compose/docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    privileged: true
    user: root
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
      - /root/.m2/repository/:/root/.m2/repository/
      - ./jdk17/:/usr/local/jdk17/
    extra_hosts:
      - "git.example.com:10.10.10.15"
    restart: unless-stopped

关键信息:

项目旧实例情况对新实例的影响
端口8080(Web)+ 50000(Agent)新实例必须换端口
编排目录/home/compose/新实例用独立目录
版本2.541.1 (LTS),镜像 jenkins/jenkins:lts可复用本地镜像,不用重新拉
运行 JDK镜像自带 Temurin 21.0.9不用管
构建 JDK宿主机 /home/compose/jdk17(Temurin 17.0.18+8,Adoptium tar.gz 解压)可以只读挂载复用
docker.sock + privileged任务里能直接操作宿主机 Docker新实例不挂,隔离
extra_hosts内网 Git git.example.com → 10.10.10.15,DNS 不解析新实例必须照配,否则拉不了代码
已有任务4 个 bid 项目任务,生产在用一个字都不能动

另外确认宿主机资源充足(内存 15G 剩 13G,磁盘剩 76G),跑第二个 Jenkins 没压力。

二、方案设计:怎么算"完全隔离"

维度旧实例新实例(jenkins2)
编排目录/home/compose//home/compose-jenkins2/
容器名jenkinsjenkins2
Web 端口80808082
Agent 端口5000050001
home 卷./jenkins_home(6.4G,197 个插件)全新空目录
运行用户root(privileged)镜像默认 jenkins(uid 1000)
docker.sock挂载不挂(任务不能直接动宿主机 Docker)
.m2 仓库挂宿主机不挂,独立
jdk17读写挂载只读挂载复用(:ro)

选 8082/50001 之前先查端口占用:ss -tln,确认 8080/50000/8081 都已被占,8082/50001 空闲。

三、创建 jenkins2

3.1 第一版 compose:先跑起来

1
2
3
mkdir -p /home/compose-jenkins2/jenkins_home
chown -R 1000:1000 /home/compose-jenkins2/jenkins_home   # 容器内 jenkins 用户 uid=1000
cd /home/compose-jenkins2

docker-compose.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
services:
  jenkins2:
    image: jenkins/jenkins:lts
    container_name: jenkins2
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8082:8080"
      - "50001:50000"
    volumes:
      - ./jenkins_home:/var/jenkins_home
    restart: unless-stopped
1
2
docker-compose up -d
docker logs -f jenkins2

注意 chown 1000:1000 不能省:容器以 jenkins(uid 1000)运行,home 目录属主是 root 会起不来。

3.2 浏览器初始化

  1. 拿初始密码:docker exec jenkins2 cat /var/jenkins_home/secrets/initialAdminPassword
  2. 浏览器打开 http://<服务器IP>:8082,粘贴解锁
  3. 选 Install suggested plugins(推荐套装,含 Pipeline / Git / Credentials 等基础插件)
  4. 创建管理员账号,完成

两个提醒:

  • 云服务器记得在安全组放行 8082。
  • 如果插件下载慢,可以把更新中心换成清华镜像:https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json(Manage Jenkins → Plugins → Advanced settings → Update Site)。

3.3 坑 1:git.example.com 域名解析不了

旧实例 compose 里有 extra_hosts,第一版没抄这一条——内网 GitLab 域名 git.example.com 在 DNS 里不存在,不配的话流水线 clone 代码必然失败。补上:

1
2
    extra_hosts:
      - "git.example.com:10.10.10.15"
1
2
3
4
5
6
docker-compose up -d   # 重建
# 验证:
docker exec jenkins2 grep git.example.com /etc/hosts
# 10.10.10.15 git.example.com
docker exec jenkins2 curl -sk -o /dev/null -w '%{http_code}' https://git.example.com/
# 302(可达)

四、坑 2:安全警告横幅 → 顺便升级到最新 LTS

初始化完成后,Manage Jenkins 页面顶部挂着红色横幅:当前 2.541.1 core 存在多个已知安全漏洞。

因为新实例还没建任何任务,升级零成本,直接升:

1
2
3
4
5
cd /home/compose-jenkins2
docker-compose pull      # 拉最新 lts
docker-compose up -d     # 重建
curl -s -D- http://localhost:8082/login | grep X-Jenkins
# X-Jenkins: 2.568.3

升级到 2.568.3(当时最新 LTS),红色漏洞横幅消失。

其余几条横幅的处理:

  • “Pipeline: Groovy Libraries 有 CSRF 问题但无修复版本”——没修复版本,只能忽略;
  • built-in node 安全提示、CSP 提示——确认自己会用 built-in node 构建后,点 Dismiss 即可。

旧实例(8080)不要顺手升:它跑着 bid 项目的生产构建,升级要另约时间窗口、先备份 jenkins_home。隔离的意义就在这里——两边版本可以不一样,互不影响。

五、装插件:只装两个

推荐套装已经覆盖了 Pipeline、Git、Credentials,额外只需要:

Manage Jenkins → Plugins → Available plugins,搜索勾选:

  1. NodeJS Plugin——前端构建用。Jenkins 本体只是调度壳子,JDK/Maven 的工具管理是核心自带的,Node 不是;没有它,流水线脚本里 tools { nodejs 'node22' } 不认识,pnpm install/build 直接 command not found。
  2. SSH Agent Plugin——部署用。流水线 Deploy 阶段用 sshagent(['凭据ID']) { ... } 把私钥临时加载进 ssh-agent,实现免密 scp/ssh。

点 Install,勾选"安装完成后重启 Jenkins",等半分钟自动重启(不影响 8080 的旧实例)。

后续小插曲:SSH Agent 方案后来因凭据问题被弃用(改用容器内密钥文件,见本系列第二篇),但插件装着无害。

六、全局工具配置:jdk17 / maven3.9 / node22

Manage Jenkins → Tools,配三个工具。名字必须和流水线脚本里 tools {} 引用的一字不差。

6.1 JDK——坑 3:没有 adoptium 安装器

点 JDK 新增,打算让 Jenkins 自动装 Temurin 17,结果发现 Add Installer 下拉里没有 “Install from adoptium.net”(只有 Extract zip / Batch / Shell 三项,这是部分版本/网络环境下的常见情况)。

解决办法:不自动装,复用宿主机现成的 JDK 17——当年旧实例手工解压的 Adoptium Temurin 17 就在 /home/compose/jdk17。给 jenkins2 加只读挂载(compose 第三版,也是最终版):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
services:
  jenkins2:
    image: jenkins/jenkins:lts
    container_name: jenkins2
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "8082:8080"
      - "50001:50000"
    volumes:
      - ./jenkins_home:/var/jenkins_home
      - /home/compose/jdk17:/usr/local/jdk17:ro   # 只读复用宿主机的 JDK17
    extra_hosts:
      - "git.example.com:10.10.10.15"
    restart: unless-stopped
1
2
3
docker-compose up -d
docker exec jenkins2 /usr/local/jdk17/bin/java -version
# openjdk version "17.0.18" Temurin-17.0.18+8

Tools 页面配置:

  • JDK:Name 填 jdk17;取消勾选 Install automatically;JAVA_HOME 填 /usr/local/jdk17。

只读挂载(:ro)是个好习惯:新实例万一配错也伤不到旧实例在用的 JDK。

6.2 Maven

  • Maven:Name 填 maven3.9;勾选 Install automatically → Install from Apache → 选 3.9.x 最新(实际装到 3.9.16)。

默认走 Apache 中央仓库慢,给容器内的 Maven 配阿里云镜像(宿主机写好,jenkins_home 是绑定挂载,容器内直接生效):

/home/compose-jenkins2/jenkins_home/.m2/settings.xml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<settings>
  <mirrors>
    <mirror>
      <id>aliyun</id>
      <mirrorOf>central</mirrorOf>
      <name>aliyun</name>
      <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
  </mirrors>
</settings>

6.3 NodeJS

  • NodeJS:Name 填 node22;勾选 Install automatically → Install from nodejs.org → Node.js 22.x LTS。

如果 nodejs.org 下载不动,可以换 npmmirror 的二进制地址: https://registry.npmmirror.com/-/binary/node/v22.20.0/node-v22.20.0-linux-x64.tar.gz

七、坑 4:保存配置时报 HTTP 403

在 Tools 页面填好 JDK 的 JAVA_HOME,点 Save,弹错:

1
2
An internal error occurred during form field validation (HTTP 403).
Please reload the page and if the problem persists, ask the administrator for help.

这不是配置填错了。 原因是:当天 jenkins2 容器被反复重建过好几次、还升过一次级(2.541.1 → 2.568.3),而浏览器里还是升级前打开的旧会话页面——后台的表单校验接口需要有效的 CSRF crumb,过期会话提交就被 403 拒绝。

解决:退出登录 → 重新登录 → 重新进 Tools 填一遍 → Save。 保存后最好回进页面确认值真的存进去了。如果重新登录后仍然 403,那才需要查容器日志(docker logs jenkins2)深挖。

八、建 Git 凭据

流水线要从内网 GitLab 拉代码,先建凭据:

Manage Jenkins → Credentials → System → Global credentials (unrestricted) → Add Credentials:

  • Kind:Username with password
  • Username / Password:git.example.com 的账号密码(账号要有 myapp-safe 组的读权限)
  • ID 手动填 myapp-git(不填会自动生成 UUID,脚本里没法引用)
  • Create

九、收尾状态

1
2
3
docker ps
# jenkins    0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp   ← 旧实例,原封不动
# jenkins2   0.0.0.0:8082->8080/tcp, 0.0.0.0:50001->50000/tcp   ← 新实例
项目值
访问地址http://10.10.10.44:8082
版本2.568.3 (LTS)
编排文件/home/compose-jenkins2/docker-compose.yml
工具jdk17(JAVA_HOME=/usr/local/jdk17)、maven3.9(3.9.16)、node22(22.x)
插件(额外)NodeJS Plugin、SSH Agent Plugin
凭据myapp-git
镜像仓库Maven 阿里云、Node npmmirror(备用)

十、经验总结

  1. 先查系统源再定方案:HCE 2.0 源里最高只有 OpenJDK 11,想"一条 yum 装 JDK 17/21"会直接卡死;全程容器化 + 官方 tar.gz 是绕开这个问题的正解。
  2. 隔离 = 端口 + 目录 + 权限三件事:换端口、独立编排目录、不挂 docker.sock、不用 root 跑,两个 Jenkins 就是真正的两套系统。
  3. 成熟实例的配置是宝藏:旧实例的 extra_hosts(内网 Git 域名)、宿主机 jdk17,都是可以只读复用的现成资产——但复用一律 :ro。
  4. 新建实例趁没任务赶紧升级,安全横幅不要拖;跑生产的实例升级另约窗口。
  5. Jenkins 的 403 表单错误八成是会话过期,重新登录比查日志快。
  6. 工具 Name 和脚本 tools{} 引用必须一字不差(jdk17 / maven3.9 / node22),凭据 ID 要手动指定,不要用自动生成的 UUID。

下一篇:《从零到上线:用这套 Jenkins 部署前后端项目(MySQL/Redis 容器化)踩坑全记录》——包括 3306 之争、ssh-agent 凭据三连败、403 Forbidden、logo 消失之谜等 8 个实战坑。

使用 Hugo 构建
主题 Stack 由 Jimmy 设计