初始化 Jenkins 控制器

Beginner

简介

Jenkins 通常以未初始化的控制器状态启动。首位管理员需要使用初始密码解锁控制器,创建永久管理员账号,并确认 Jenkins 已准备就绪。

在本实验中,你将安全地练习这一首次运行的工作流程。由于虚拟机中的主 Jenkins 服务已用于其他实验,你将会在 18080 端口启动第二个临时 Jenkins 控制器。你只需初始化该临时控制器,验证结果,然后将其移除即可。

启动一个隔离的全新 Jenkins 控制器

在此步骤中,你将启动一个新的 Jenkins 控制器,而不会更改主 Jenkins 服务。Jenkins 控制器将其配置存储在 /var/jenkins_home 下,因此本实验使用了一个名为 jenkins-init-lab-home 的独立 Docker 数据卷。

下方的 docker run 命令会在后台启动临时控制器。-d 选项表示后台运行模式,--name 为容器指定一个稳定的名称,-p 18080:8080 将 Jenkins 发布到宿主机的 18080 端口,-v 则挂载隔离的 Jenkins 主目录数据卷。

docker run -d \
  --name jenkins-init-lab \
  -p 18080:8080 \
  -v jenkins-init-lab-home:/var/jenkins_home \
  jenkins/jenkins:latest

Docker 会输出新的容器 ID。每次生成的 ID 都不相同,因此任何长十六进制值都是正常的。

接下来,等待 Jenkins 开始响应 HTTP 请求。下方的循环会不断向本地控制器请求 /login 路径,直到它返回 HTTP 状态码。200 响应表示登录页面已可用。

until [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:18080/login)" = "200" ]; do
  echo "Waiting for the temporary Jenkins controller..."
  sleep 5
done

你应该会看到几次等待提示,随后命令提示符会返回。确认容器和端口映射:

docker ps --filter name=jenkins-init-lab --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'

输出应包含容器名称和 18080 端口:

NAMES              STATUS          PORTS
jenkins-init-lab   Up ...          0.0.0.0:18080->8080/tcp, ...

获取初始管理员密码

在此步骤中,你将获取 Jenkins 为首位管理员创建的一次性密码。Jenkins 将此密码存储在控制器主目录下的 /var/jenkins_home/secrets/initialAdminPassword 文件中。

使用 docker exec 在临时 Jenkins 容器内运行 cat 命令,并将密码保存到你的项目目录中:

docker exec jenkins-init-lab cat /var/jenkins_home/secrets/initialAdminPassword > /home/labex/project/jenkins-init-password.txt

该命令使用 > 重定向将输出写入文件。这样可以在后续步骤中使用该密码,而无需依赖 shell 变量。

仅显示前几个字符,以便在不暴露完整值的情况下确认文件不为空:

cut -c1-8 /home/labex/project/jenkins-init-password.txt

你应该能看到密码的前八个字符,类似于:

1a2b3c4d

创建永久管理员账号

在此步骤中,你将使用初始密码创建一个名为 lab-admin 的正式 Jenkins 管理员账号。初始密码仅用于设置阶段。控制器初始化完成后,团队通常使用正式的管理员账号进行操作。

Jenkins 使用 crumb 令牌来保护表单提交。Crumb 与请求它的浏览器或 HTTP 会话绑定,因此下方的命令还会将 Jenkins 的 Cookie 保存到 Cookie 存储文件中。

INIT_PASSWORD=$(tr -d '\r\n' < /home/labex/project/jenkins-init-password.txt)
COOKIE_JAR=/home/labex/project/jenkins-init-cookies.txt

curl -fsS -c "${COOKIE_JAR}" -u "admin:${INIT_PASSWORD}" \
  http://localhost:18080/crumbIssuer/api/json \
  > /home/labex/project/jenkins-init-crumb.json

下一条命令向 Jenkins 发送一段简短的 Groovy 脚本。该脚本会创建一个私有安全域,添加 lab-admin 用户,授予已登录用户完全控制权限,并将设置向导标记为已完成。

CRUMB_FIELD=$(sed -n 's/.*"crumbRequestField":"\([^"]*\)".*/\1/p' /home/labex/project/jenkins-init-crumb.json)
CRUMB_VALUE=$(sed -n 's/.*"crumb":"\([^"]*\)".*/\1/p' /home/labex/project/jenkins-init-crumb.json)

cat > /home/labex/project/initialize-controller.groovy <<'GROOVY'
import jenkins.model.Jenkins
import jenkins.install.InstallState
import hudson.security.HudsonPrivateSecurityRealm
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy

def jenkins = Jenkins.get()
def realm = new HudsonPrivateSecurityRealm(false)
realm.createAccount('lab-admin', 'LabEx-Admin-123')
jenkins.setSecurityRealm(realm)

def strategy = new FullControlOnceLoggedInAuthorizationStrategy()
strategy.setAllowAnonymousRead(false)
jenkins.setAuthorizationStrategy(strategy)

jenkins.setInstallState(InstallState.INITIAL_SETUP_COMPLETED)
jenkins.save()
println('created lab-admin and completed setup')
GROOVY

curl -fsS -b "${COOKIE_JAR}" -u "admin:${INIT_PASSWORD}" \
  -H "${CRUMB_FIELD}: ${CRUMB_VALUE}" \
  --data-urlencode "script@/home/labex/project/initialize-controller.groovy" \
  http://localhost:18080/scriptText \
  > /home/labex/project/jenkins-init-result.txt

读取结果文件:

cat /home/labex/project/jenkins-init-result.txt

预期的输出信息为:

created lab-admin and completed setup

确认初始化后的控制器仪表盘

在此步骤中,你将确认临时控制器不再仅仅是一个解锁界面。一个健康且已初始化的控制器应该能够验证 lab-admin 用户并返回 Jenkins 主页面。

使用带有 -u 参数的 curl 进行身份验证。-o 选项将 HTML 写入文件,-w '%{http_code}' 仅打印 HTTP 状态码。

curl -s -u 'lab-admin:LabEx-Admin-123' \
  -o /home/labex/project/jenkins-init-dashboard.html \
  -w '%{http_code}\n' \
  http://localhost:18080/

状态码应为:

200

现在,在保存的仪表盘页面中搜索 Jenkins 的标识文本:

grep -o -E 'Dashboard|Welcome to Jenkins|Jenkins' /home/labex/project/jenkins-init-dashboard.html | head

你应该至少看到一行匹配内容。虽然具体的 HTML 结构可能会随 Jenkins 版本变化,但它应该能识别出该页面为 Jenkins。

停止并移除临时控制器

在此步骤中,你将清理临时控制器。清理工作非常重要,因为主 Jenkins 服务是其他 Jenkins 实验所使用的,而临时控制器仅用于练习首次运行初始化。

首先停止并移除临时容器:

docker rm -f jenkins-init-lab

然后移除其隔离的 Jenkins 主目录数据卷:

docker volume rm jenkins-init-lab-home

最后,验证临时控制器已被移除:

docker ps -a --filter name=jenkins-init-lab --format '{{.Names}}'

该命令不应输出任何内容。结果为空意味着该名称的容器已不存在。

总结

你启动了一个隔离的 Jenkins 控制器,获取了其一次性初始管理员密码,创建了永久管理员账号,确认了初始化后的仪表盘可访问,并清理了临时控制器。这就是一个可用的 Jenkins 控制器背后核心的首次运行初始化工作流程。