备份与恢复 Jenkins 配置

Beginner

简介

Jenkins 将大部分控制器和任务配置存储在 JENKINS_HOME 下的文件中。一个实用的备份方案必须能够捕获定义任务和控制器设置的文件,而恢复方案则必须证明 Jenkins 能够成功加载这些恢复的文件。

在本实验中,你将创建一个示例任务,备份其 config.xml,删除该任务,从 tar 归档中进行恢复,重载 Jenkins,并验证任务是否恢复正常工作。

创建值得备份的 Jenkins 状态

在此步骤中,你将创建一个名为 backup-restore-demo 的自由风格(Freestyle)任务。这将为你提供一个真实的 Jenkins 配置文件,以便进行备份和恢复练习。

编写任务配置:

cat > /home/labex/project/backup-restore-demo-config.xml <<'XML'
<?xml version='1.1' encoding='UTF-8'?>
<project>
  <actions/>
  <description>Job used to practice Jenkins backup and restore.</description>
  <keepDependencies>false</keepDependencies>
  <properties/>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <hudson.tasks.Shell>
      <command>echo "Restored Jenkins job is running"</command>
      <configuredLocalRules/>
    </hudson.tasks.Shell>
  </builders>
  <publishers/>
  <buildWrappers/>
</project>
XML

将任务复制到 Jenkins 并重载:

docker exec -u root jenkins mkdir -p /var/jenkins_home/jobs/backup-restore-demo
docker cp /home/labex/project/backup-restore-demo-config.xml jenkins:/var/jenkins_home/jobs/backup-restore-demo/config.xml
docker exec -u root jenkins chown -R jenkins:jenkins /var/jenkins_home/jobs/backup-restore-demo
/home/labex/project/reload-jenkins.sh

确认 Jenkins 可以加载该任务:

curl -fsS http://localhost:8080/job/backup-restore-demo/api/json | grep -o '"name":"backup-restore-demo"'

备份任务配置

在此步骤中,你将把任务配置从 Jenkins 容器中复制出来并进行归档。备份保留了相对路径 jobs/backup-restore-demo/config.xml,这使得恢复路径非常清晰。

创建备份工作目录并复制任务配置:

mkdir -p /home/labex/project/jenkins-backup-work/jobs/backup-restore-demo
docker cp jenkins:/var/jenkins_home/jobs/backup-restore-demo/config.xml /home/labex/project/jenkins-backup-work/jobs/backup-restore-demo/config.xml

创建压缩的 tar 归档:

cd /home/labex/project/jenkins-backup-work
tar -czf /home/labex/project/backup-restore-demo.tar.gz jobs/backup-restore-demo/config.xml

列出归档内容:

tar -tzf /home/labex/project/backup-restore-demo.tar.gz

输出应包含:

jobs/backup-restore-demo/config.xml

删除任务以模拟数据丢失

在此步骤中,你将从 Jenkins 中删除该任务。这模拟了意外删除的情况,并为你提供了一个明确的恢复目标。

删除任务目录并重载 Jenkins:

docker exec -u root jenkins rm -rf /var/jenkins_home/jobs/backup-restore-demo
/home/labex/project/reload-jenkins.sh

确认任务已无法访问。下面的命令特意将 404 响应视为此检查的成功结果:

if curl -fsS http://localhost:8080/job/backup-restore-demo/api/json >/dev/null 2>&1; then
  echo "Job still exists"
  exit 1
else
  echo "Job is absent and ready to restore"
fi

从备份中恢复任务

在此步骤中,你将解压备份归档,将恢复的任务目录复制回 Jenkins,并重载控制器。

将归档解压到恢复目录中:

rm -rf /home/labex/project/restore-work
mkdir -p /home/labex/project/restore-work
tar -xzf /home/labex/project/backup-restore-demo.tar.gz -C /home/labex/project/restore-work

将恢复的任务目录复制到 Jenkins 并重载:

docker cp /home/labex/project/restore-work/jobs/backup-restore-demo jenkins:/var/jenkins_home/jobs/backup-restore-demo
docker exec -u root jenkins chown -R jenkins:jenkins /var/jenkins_home/jobs/backup-restore-demo
/home/labex/project/reload-jenkins.sh

确认 Jenkins 可以加载恢复后的任务:

curl -fsS http://localhost:8080/job/backup-restore-demo/api/json | grep -o '"name":"backup-restore-demo"'

运行恢复后的任务

在此步骤中,你将运行恢复后的任务。只有当 Jenkins 能够加载并执行恢复的配置时,恢复才有意义。

触发构建。Jenkins 首先会返回一个队列项,因此请存储响应头并读取 Location 头:

CRUMB=$(curl -fsS -c /tmp/jenkins-backup-build.cookies 'http://localhost:8080/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
curl -fsS -D /tmp/backup-restore-build.headers -o /dev/null -b /tmp/jenkins-backup-build.cookies -H "$CRUMB" -X POST http://localhost:8080/job/backup-restore-demo/build
QUEUE_URL=$(awk 'tolower($1)=="location:" {print $2}' /tmp/backup-restore-build.headers | tr -d '\r')
echo "Jenkins queued the restored job at ${QUEUE_URL}"

等待队列项变为实际的构建。Jenkins 在分配最终构建编号之前可能会有几秒钟的静默期:

until BUILD_NUMBER=$(python3 - "$QUEUE_URL" <<'PY'
import json
import sys
import urllib.request

with urllib.request.urlopen(sys.argv[1] + 'api/json') as response:
    data = json.load(response)

executable = data.get('executable')
if not executable:
    raise SystemExit(1)
print(executable['number'])
PY
); do
  echo "Waiting for Jenkins to assign a build number..."
  sleep 3
done

echo "The restored job is running as build #${BUILD_NUMBER}"

等待构建完成:

until curl -fsS "http://localhost:8080/job/backup-restore-demo/${BUILD_NUMBER}/api/json" >/tmp/backup-restore-build.json 2>/dev/null && grep -q '"building":false' /tmp/backup-restore-build.json; do
  echo "Waiting for restored job build #${BUILD_NUMBER}..."
  sleep 3
done

检查控制台输出:

curl -fsS "http://localhost:8080/job/backup-restore-demo/${BUILD_NUMBER}/consoleText" | grep -E 'Restored Jenkins job is running|Finished: SUCCESS'

从桌面界面打开 Firefox,访问终端显示的构建编号对应的控制台 URL(例如 http://localhost:8080/job/backup-restore-demo/2/console)。控制台页面应显示恢复后的任务已成功运行。

Jenkins 恢复任务的控制台输出

总结

你创建了值得备份的 Jenkins 配置,归档了任务的 config.xml,删除了该任务,从备份归档中进行了恢复,重载了 Jenkins,并通过运行一次成功的构建验证了恢复后的任务。