0. 简介
Jenkins 是一个开源的持续集成和持续交付 (CI/CD) 工具。它通过自动化构建测试和部署流程,显著提升软件交付效率。其核心优势在于强大的插件生态可扩展性以及对分布式构建的支持。
1. Jenkins 安装与初始化
1.1 安装方式
方法 A:Debian/Ubuntu 官方源安装(推荐)
# 添加密钥
curl -fsSL https://pkg.jenkins.io/debian/jenkins.io-2023.key | sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
# 添加存储库
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] https://pkg.jenkins.io/debian binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
# 安装
sudo apt-get update
sudo apt-get install fontconfig openjdk-11-jre jenkins
方法 B:WAR 包手动安装(灵活便捷)
- 下载:
wget https://mirrors.tuna.tsinghua.edu.cn/jenkins/war/latest/jenkins.war - 运行:
java -jar jenkins.war --enable-future-java --httpPort=8081
1.2 基础配置
- 修改端口:编辑
/etc/default/jenkins或/usr/lib/systemd/system/jenkins.service中的HTTP_PORT。 - 初始密码:首次访问时从日志或
/var/lib/jenkins/secrets/initialAdminPassword获取。 - 镜像加速:在
管理 Jenkins -> 插件管理 -> 高级中,将升级站点改为:http://mirror.esuni.jp/jenkins/updates/update-center.json。
2. 权限与环境调优
2.1 切换为 Root 用户运行
默认的 jenkins 用户权限有限。若需以 root 运行:
- 编辑
/etc/default/jenkins,修改JENKINS_USER=rootJENKINS_GROUP=root。 - 编辑
/usr/lib/systemd/system/jenkins.service,修改User=rootGroup=root。 - 执行:
systemctl daemon-reload && systemctl restart jenkins。
2.2 内存限制与环境变量
- 内存优化:在配置文件
JAVA_ARGS中添加-Xmx2048m。 - 全局变量:
节点管理 -> 配置从节点 -> 环境变量。例如PATH加入 Python 或 Allure 的路径。 - 并发设置:在系统配置中修改
# of executors,提升任务并行处理能力。
3. 自动化任务构建技巧
3.1 源码管理与凭据
- Git 凭据:使用 "Username with password" 存储 Git 账号。
- 子模块 (Submodule):若子模块拉取失败,建议将子仓库设为 Public 或在服务器配置 SSH 公钥。
3.2 定时触发 (Cron 语法)
在“构建触发器”中输入,格式为:分钟 小时 日 月 周。
H */4 * * *:每 4 小时一次。H 18 * * 1-5:周一至周五 18 点。
3.3 解决 Python 导入错误
如果在 Jenkins 环境下运行 Python 报 ImportError,在脚本头部手动添加路径:
import sys
sys.path.append('/usr/local/lib/python3.8/dist-packages')
4. 核心功能:动态修改构建标题 (无需插件)
场景: 将默认的 #203 标题实时改为 #203-resume-192.168.31.182。
在 Execute shell 任务最后添加如下脚本:
#!/bin/bash
# 1. 业务逻辑
cd /home/yys/mobile && pipenv run python3 control_device.py ${ACTION} ${TARGET_IP}
# 2. 修改标题逻辑 (仅需账号密码,方便迁移)
USER="yys"
PASS="123456"
NEW_NAME="#${BUILD_NUMBER}-${ACTION}-${TARGET_IP}"
# 获取 Crumb 并保持 Session Cookie
CRUMB_DATA=$(curl -s -u "$USER:$PASS" -c cookies.txt "${JENKINS_URL}crumbIssuer/api/xml?xpath=concat(//crumbRequestField\":\"//crumb)")
# 调用 API 修改 (增加 -s 隐藏进度条,> /dev/null 隐藏返回结果)
curl -s -u "$USER:$PASS" -b cookies.txt -H "$CRUMB_DATA" -X POST "${BUILD_URL}configSubmit" \
--data-urlencode "json={\"displayName\":\"$NEW_NAME\"}" > /dev/null
rm -f cookies.txt
echo "✅ 构建标题已更新: $NEW_NAME"
5. Jenkins API 自动化脚本 (Python 实现)
5.1 批量读取任务配置
import requests
import xml.etree.ElementTree as ET
def get_all_jobs(url user pwd):
s = requests.Session()
s.auth = (user pwd)
jobs = s.get(f"{url}/api/json").json()['jobs']
result = []
for j in jobs:
config = s.get(f"{url}/job/{j['name']}/config.xml").text
result.append({'name': j['name'] 'config': config})
return result
5.2 检测任务状态并自动运行
def run_job_if_idle(url user pwd job_name):
s = requests.Session()
s.auth = (user pwd)
info = s.get(f"{url}/job/{job_name}/api/json?tree=builds[building]").json()
is_building = any(b['building'] for b in info['builds'])
if not is_building:
s.post(f"{url}/job/{job_name}/build")
print(f"任务 {job_name} 已启动")
6. 进阶维护与故障排查
6.1 中文乱码解决 (Docker 环境)
在 docker-compose.yml 中添加环境变量,确保 UTF-8 编码:
environment:
- LANG=C.UTF-8
- JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF8
6.2 自动备份
创建脚本定期打包 /var/lib/jenkins 目录:
tar -czvf /backup/jenkins_$(date +%Y%m%d).tar.gz /var/lib/jenkins
6.3 安卓 ADB 授权
Jenkins root 用户和普通用户下的 .android/adbkey 需保持一致。初次连接时需在手机端点击“允许调试”,否则 Jenkins 脚本调用 adb 会报权限错误。
结语: 本手册涵盖了 Jenkins 从 0 到 1 的全过程。建议在迁移项目时,优先检查权限设置(Section 2.1)和编码配置(Section 6.1)。
6.4. 最佳实践:基于 Python 的 Job 备份与迁移
相比于传统的物理打包 /var/lib/jenkins,使用 Python 脚本备份 Job 更加轻量且易于管理。
6.4.1 备份 Job(提取配置为字典列表)
使用 get_jenkins_jobs 将所有任务的描述参数定时器Shell 脚本提取出来。
import requests
import xml.etree.ElementTree as ET
def get_jenkins_jobs(jenkins_url username password):
s = requests.Session()
s.auth = (username password)
jobs_data = s.get(f"{jenkins_url}/api/json").json()
result = []
for job in jobs_data['jobs']:
config_xml = s.get(f"{jenkins_url}/job/{job['name']}/config.xml").text
root = ET.fromstring(config_xml)
# 提取核心配置
job_description = root.find("description").text if root.find("description") is not None else ""
build_periodically = [bp.text.strip() for bp in root.findall(".//hudson.triggers.TimerTrigger/spec")]
execute_shell = [es.text.strip() for es in root.findall(".//hudson.tasks.Shell/command")]
# 提取参数
params = []
for p in root.findall(".//hudson.model.ParametersDefinitionProperty/parameterDefinitions/*"):
params.append({
'name': p.find("name").text if p.find("name") is not None else ""
'description': p.find("description").text if p.find("description") is not None else ""
'type': p.tag.split('.')[-1]
'default_value': p.find("defaultValue").text if p.find("defaultValue") is not None else ""
})
result.append({
'job_name': job['name']
'job_description': job_description
'build_periodically': build_periodically
'execute_shell': execute_shell
'parameters': params
})
return result
6.4.2 还原/创建 Job(利用备份数据迁移)
使用 create_jenkins_jobs 在新环境中批量重建任务。通过 <![CDATA[]]> 完美解决 Shell 中 && 等特殊字符导致的创建失败问题。
from requests.auth import HTTPBasicAuth
def create_jenkins_jobs(jenkins_url user passwd jobs_list):
print("⚠️ 注意:description 和 execute_shell 中的中文字符不宜过多,以免触发 500 错误。")
for job in jobs_list:
# 1. 组装参数 XML
param_xml = "<properties/>"
if job.get("parameters"):
inner_xml = "".join([f"""
<hudson.model.StringParameterDefinition>
<name>{p['name']}</name>
<description>{p['description']}</description>
<defaultValue>{p['default_value']}</defaultValue>
<trim>false</trim>
</hudson.model.StringParameterDefinition>""" for p in job["parameters"] if p['type'] == "StringParameterDefinition"])
param_xml = f"<properties><hudson.model.ParametersDefinitionProperty><parameterDefinitions>{inner_xml}</parameterDefinitions></hudson.model.ParametersDefinitionProperty></properties>"
# 2. 组装 Shell XML (使用 CDATA 保护特殊符号)
shell_cmd = "\n".join(job.get("execute_shell" []))
builder_xml = f"<builders><hudson.tasks.Shell><command><![CDATA[{shell_cmd}]]></command></hudson.tasks.Shell></builders>" if shell_cmd else "<builders/>"
# 3. 组装定时器 XML
trigger_cmd = "\n".join(job.get("build_periodically" []))
trigger_xml = f"<triggers><hudson.triggers.TimerTrigger><spec>{trigger_cmd}</spec></hudson.triggers.TimerTrigger></triggers>" if trigger_cmd else "<triggers/>"
config_xml = f"""<project><actions/><description>{job.get('job_description' '')}</description>
<keepDependencies>false</keepDependencies>{param_xml}
<scm class="hudson.scm.NullSCM"/><canRoam>true</canRoam><disabled>false</disabled>
{trigger_xml}<concurrentBuild>false</concurrentBuild>{builder_xml}<publishers/><buildWrappers/>
</project>"""
# 4. 发送请求
s = requests.Session()
s.auth = HTTPBasicAuth(user passwd)
crumb = s.get(f"{jenkins_url}/crumbIssuer/api/json").json()
headers = {'Content-Type': 'application/xml' crumb['crumbRequestField']: crumb['crumb']}
r = s.post(f"{jenkins_url}/createItem?name={job['job_name']}" data=config_xml.encode('utf-8') headers=headers)
print(f"{job['job_name']} 创建状态: {r.status_code}")
6.4.3. 维护心得与避坑指南
- 迁移时的“4个汉字”限制:
在通过 API 创建 Job 时,如果description或shell中的中文过多(尤其是没有指定 XML header 为 UTF-8 时),Jenkins 可能会解析报错。- 对策:尽量保持描述精简,或在生成的
config_xml字符串最前面加上<?xml version=Ƈ.1' encoding='UTF-8'?>。
- 对策:尽量保持描述精简,或在生成的
- 特殊符号问题:
Shell 脚本中的&<>是 XML 的敏感字符。在 Python 拼接时,必须像上面代码中那样使用<![CDATA[ 脚本内容 ]]>包裹,否则 Job 配置会损坏。 - 定时任务检测:
可以使用run_jenkins_job_if_not_running脚本(见笔记第 20 节)定期检测核心任务,确保“全任务”等监控脚本始终在运行。
评论区(0 条)
发表评论⏳ 加载编辑器…