1. 项目概述与核心价值最近在折腾一个基于Brainy Pi的嵌入式项目每次手动编译、烧录、测试一套流程下来不仅耗时费力还容易因为环境不一致导致各种“灵异”问题。相信很多做嵌入式开发或者物联网IoT项目的朋友都深有体会代码仓库一更新团队里每个人的本地环境都得同步稍有不慎构建就失败了。为了解决这个痛点我决定把Jenkins这套成熟的持续集成/持续部署CI/CD流水线搬到Brainy Pi上打造一个专属于嵌入式开发的低成本、高可用的自动化构建服务器。持续集成CI的核心思想其实很简单就是让开发人员频繁地将代码集成到主干。每次集成都通过自动化的构建包括编译、测试、打包等来验证从而尽早发现集成错误。而Jenkins作为这个领域的“老大哥”是一个用Java编写的开源自动化服务器它通过插件生态几乎能对接任何开发工具链实现从代码提交到产品上线的全流程自动化。对于资源受限但又需要稳定自动化环境的场景比如我们手头的Brainy PiJenkins提供了一个绝佳的解决方案。它不仅能自动拉取代码、运行单元测试、生成构建报告还能在特定条件下触发固件烧录或设备重启极大解放了开发者的双手保证了软件质量的一致性。将Jenkins部署在Brainy Pi这类ARM架构的单板计算机上尤其适合个人开发者、小型团队或教育场景。你无需租用云服务器利用手边闲置的开发板接上电源和网络就能获得一个7x24小时在线的自动化助手。无论是C/C的交叉编译还是Python脚本的自动化测试Jenkins都能帮你打理得井井有条。接下来我就把在Brainy Pi上从零部署Jenkins并配置一个基础CI流水线的全过程、踩过的坑以及一些优化心得毫无保留地分享出来。2. 环境准备与系统优化在开始安装Jenkins之前我们必须为Brainy Pi打好基础。一个稳定、干净且资源分配合理的系统环境是后续所有步骤能顺利进行的保障。很多人容易忽视这一步直接开干结果遇到各种依赖缺失、权限不足或磁盘空间爆满的问题。2.1 系统更新与基础加固拿到一块全新的Brainy Pi或者即使是一块已经用过一段时间的板子第一件事永远是更新软件源和升级现有包。这能确保我们获取到最新的安全补丁和软件版本避免因版本过旧导致的兼容性问题。sudo apt update sudo apt upgrade -y这里有个细节upgrade命令加上-y参数是为了在升级过程中自动确认避免脚本执行时卡在交互确认环节。对于自动化部署脚本这个参数至关重要。更新完成后我强烈建议进行一次重启让内核更新生效sudo reboot等待板子重启并重新登录后我们还需要安装一些基础工具它们在后续的排查和运维中会非常有用sudo apt install -y curl wget vim git htop net-toolscurl/wget: 用于从网络下载文件安装Jenkins时会用到。vim: 一个高效的文本编辑器用于修改配置文件。git: 版本控制工具是CI/CD流水线的源头。htop: 比传统top命令更直观的进程查看器方便监控Brainy Pi的资源CPU、内存使用情况。net-tools: 包含ifconfig等老牌网络工具虽然推荐用ip命令但有些场景下它更直观。2.2 存储与交换空间考量Brainy Pi通常使用SD卡或eMMC作为存储。Jenkins在运行中会产生大量的构建日志、工作空间文件以及插件缓存。默认的根分区可能很快就会被占满。首先检查磁盘空间df -h重点关注/根目录的使用率。如果剩余空间小于2GB你需要考虑清理旧文件或者为Jenkins的工作目录 (/var/lib/jenkins) 挂载一个更大的外部存储如USB硬盘。对于轻度使用确保有5GB以上的空闲空间是比较稳妥的。其次评估内存与交换空间Brainy Pi的内存有限通常为1GB或2GB。Java应用Jenkins本身有一定内存开销同时运行编译任务时如gcc会更吃内存。为了避免因内存不足导致构建失败甚至系统崩溃设置交换空间Swap是必要的。交换空间相当于一块硬盘上的虚拟内存当物理内存不足时系统会将部分数据暂时移到交换空间。检查当前交换空间状态sudo swapon --show如果没有任何输出说明没有启用交换空间。我们可以创建一个交换文件# 创建一个1GB大小的交换文件根据你的SD卡容量调整通常1-2GB足够 sudo fallocate -l 1G /swapfile # 设置正确的权限 sudo chmod 600 /swapfile # 将其格式化为交换分区 sudo mkswap /swapfile # 启用交换文件 sudo swapon /swapfile为了让系统开机自动挂载交换文件需要将其添加到/etc/fstab文件末尾echo /swapfile none swap sw 0 0 | sudo tee -a /etc/fstab注意交换文件在SD卡上频繁读写可能会影响SD卡寿命。但对于保障构建任务的稳定性来说利大于弊。如果条件允许将交换文件放在USB硬盘或SSD上是更好的选择。3. Java运行环境安装与验证Jenkins是基于Java开发的因此我们必须先安装Java运行时环境JRE。虽然Jenkins支持多个Java版本但为了最佳的兼容性和稳定性我们选择长期支持LTS的版本。OpenJDK 11是一个经过广泛验证的选择。3.1 安装OpenJDK 11 JRE执行以下命令进行安装sudo apt install -y openjdk-11-jre-headless这里我选择了openjdk-11-jre-headless而不是openjdk-11-jre。headless版本去除了图形界面相关的库如AWT, Swing在服务器环境下更为轻量能节省宝贵的磁盘空间和内存这对于资源紧张的Brainy Pi来说是个好习惯。3.2 验证安装与环境配置安装完成后必须验证Java是否安装成功以及版本是否正确java -version期望的输出应该类似于openjdk version 11.0.22 2024-01-16 OpenJDK Runtime Environment (build 11.0.227-post-Raspbian-1deb12u1) OpenJDK 64-Bit Server VM (build 11.0.227-post-Raspbian-1deb12u1, mixed mode, sharing)看到版本号是11并且没有报错就说明安装成功了。一个关键的实操心得有时候系统里可能预装了多个Java版本。你可以通过以下命令查看所有已安装的Java版本并设置默认版本sudo update-alternatives --config java如果列表中有多个Java选择与openjdk-11相关的那个序号即可。确保Jenkins运行时使用的是我们指定的版本能避免很多难以排查的兼容性问题。4. Jenkins服务端部署详解有了Java环境我们就可以开始安装Jenkins本体了。我们将采用官方推荐的APT仓库安装方式这比手动下载.war包运行更便于后续的升级和管理。4.1 添加Jenkins官方软件仓库首先需要将Jenkins的官方GPG密钥添加到系统的可信密钥链中。这个密钥用于验证从该仓库下载的软件包的完整性。sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \ https://pkg.jenkins.io/debian/jenkins.io-2023.key这里我使用了wget并指定了输出路径和文件名。原教程中的curl管道操作也可以但这样更直接。接着添加Jenkins的APT源到系统源列表。我们创建一个新的源文件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这条命令做了两件事1) 指定了使用刚才导入的密钥进行签名验证2) 将Jenkins的Debian二进制仓库地址写入到/etc/apt/sources.list.d/jenkins.list文件中。将第三方源放在独立的文件里是一个好习惯方便管理。4.2 安装与启动Jenkins添加仓库后更新本地软件包索引然后安装Jenkinssudo apt update sudo apt install -y jenkins安装过程会自动完成以下几件事创建系统用户和用户组jenkins。将Jenkins安装到/usr/share/jenkins。创建服务配置文件并设置开机自启。将Jenkins服务注册到systemd。安装完成后Jenkins服务默认是启动的。我们可以检查其状态sudo systemctl status jenkins你应该看到状态为active (running)。如果没有自动启动使用以下命令启动并设置开机自启sudo systemctl start jenkins sudo systemctl enable jenkins4.3 防火墙与端口访问配置Jenkins默认监听8080端口。我们需要确保这个端口可以被访问。 如果你的Brainy Pi在本地网络且没有启用防火墙如ufw那么可能无需额外配置。但为了安全起见或者如果你的Pi有公网IP强烈不推荐直接暴露配置防火墙是必须的。检查防火墙状态sudo ufw status如果显示inactive则防火墙未启用。如果显示active则需要为Jenkins放行8080端口sudo ufw allow 8080 sudo ufw reload更重要的网络配置获取Brainy Pi的IP地址。在浏览器中访问Jenkins你需要知道Brainy Pi在你网络中的IP地址。hostname -I这条命令会列出所有网络接口的IP地址。通常eth0有线或wlan0无线对应的地址就是你需要用的。记下这个IP例如192.168.1.100。5. 初始Web界面配置指南现在服务已经在Brainy Pi上跑起来了。我们通过浏览器来完成最后的初始化设置。5.1 解锁Jenkins在你的电脑浏览器中访问http://BrainyPi的IP地址:8080。例如http://192.168.1.100:8080。 首次访问你会看到一个“解锁Jenkins”的页面要求输入初始管理员密码。这个密码存储在Brainy Pi上的一个文件中。回到终端执行sudo cat /var/lib/jenkins/secrets/initialAdminPassword屏幕上会打印出一长串随机字符如a1b2c3d4e5f67890abcdef1234567890这就是初始密码。复制它粘贴到浏览器的密码框中点击“继续”。5.2 插件安装策略选择接下来Jenkins会提示你安装插件。它提供了两个选项安装推荐的插件这是给新手的快速入门选择会安装一套最常用的插件如Git、Pipeline、邮件通知等。选择插件来安装你可以手动勾选需要的插件适合有特定需求的用户。我的建议是直接点击“安装推荐的插件”。对于在Brainy Pi上的初次部署这套插件组合已经足够开始一个基础的CI流程。手动选择可能会遗漏某些依赖导致后续配置麻烦。插件安装过程可能需要5-15分钟取决于你的网络速度和Brainy Pi的性能。请耐心等待。5.3 创建管理员账户插件安装完成后会进入“创建第一个管理员用户”页面。强烈建议你在这里创建一个新的管理员账户而不是继续使用初始的随机密码。填写用户名、密码、全名和邮箱地址。这个账户将拥有Jenkins的最高权限请务必使用强密码并妥善保管。点击“保存并完成”。5.4 配置实例URL最后一步是配置Jenkins的URL。通常系统会自动检测并填入http://BrainyPi的IP:8080。确认这个地址正确它将是所有通知邮件中的链接地址然后点击“保存并完成”。点击“开始使用Jenkins”你就会进入Jenkins的仪表盘。至此Jenkins服务本身已经部署并初始化完毕。6. 基础CI流水线创建实战一个空的Jenkins只是开始它的价值在于自动化任务Job。这里我将演示创建一个最经典的Freestyle项目用于自动构建一个简单的C语言项目。6.1 准备示例代码仓库为了演示我们可以在Brainy Pi上本地初始化一个Git仓库模拟一个远程代码库。在实际项目中这里应该替换成你的GitHub、GitLab或Gitee仓库地址。在Brainy Pi上创建一个示例项目mkdir -p ~/my_embedded_project cd ~/my_embedded_project git init cat hello_world.c EOF #include stdio.h int main() { printf(Hello, CI/CD from Brainy Pi!\n); printf(Build successful at: %s %s\n, __DATE__, __TIME__); return 0; } EOF cat Makefile EOF CCgcc CFLAGS-Wall TARGEThello_world all: $(TARGET) $(TARGET): hello_world.c $(CC) $(CFLAGS) -o $(TARGET) hello_world.c clean: rm -f $(TARGET) EOF git add . git commit -m Initial commit: Simple hello world program现在~/my_embedded_project就是一个包含简单C代码和Makefile的本地Git仓库。6.2 在Jenkins中创建构建任务回到Jenkins网页点击左侧菜单的“新建任务”。输入一个任务名称例如My-Embedded-Build。选择“构建一个自由风格的软件项目”然后点击“确定”。源码管理配置在任务配置页面找到“源码管理”部分。因为我们用的是本地仓库所以这里先不配置Git实际项目中你需要在这里填入仓库URL和凭证。我们用一个更直接的方式让Jenkins直接在Brainy Pi的某个目录下工作。构建触发器配置“构建触发器”决定了何时启动构建。对于测试我们可以先不设置定时或轮询SCM。稍后我们会手动触发。构建环境配置可以暂时跳过。构建步骤配置这是核心部分。点击“增加构建步骤”选择“执行shell”。在命令框中输入我们的构建脚本#!/bin/bash echo 开始构建任务 echo 当前工作目录: $(pwd) echo Brainy Pi 主机名: $(hostname) # 假设我们的项目代码已经放在 /home/pi/my_embedded_project # Jenkins的工作空间会是一个独立目录为了演示我们复制项目过来 # 在实际中你应该使用‘源码管理’部分来自动拉取代码 PROJECT_SRC/home/pi/my_embedded_project cp -r $PROJECT_SRC/* . # 执行清理和编译 make clean make # 检查构建产物 if [ -f hello_world ]; then echo 构建成功 ./hello_world echo 构建产物信息: file hello_world else echo 构建失败 exit 1 # 返回非零状态码Jenkins会将此构建标记为失败 fi echo 构建任务结束 这个脚本做了几件事打印信息、复制源代码模拟拉取、执行make编译、运行生成的可执行文件并检查结果。后构建操作配置我们可以配置构建后行为例如归档构建产物。在“后构建操作”部分点击“增加后构建操作”选择“归档构件”。在“要归档的文件”中填入hello_world。这样每次成功构建后生成的二进制文件都会被保存起来方便下载。点击页面底部的“保存”。6.3 手动触发与结果分析回到任务主页面点击左侧的“立即构建”。你会看到任务开始排队并执行。点击构建历史记录中的编号如 #1再点击“控制台输出”就可以实时查看构建日志。如果一切顺利你会在日志末尾看到“Hello, CI/CD from Brainy Pi!”的输出以及构建成功的信息。在任务页面你也能看到“上次成功构建”的标记和归档的hello_world文件。第一次构建的常见问题make: command not found说明系统没有安装构建工具链。对于C/C项目需要安装build-essential。在Brainy Pi上执行sudo apt install -y build-essential。权限拒绝Jenkins默认以jenkins用户运行可能无法访问/home/pi目录下的文件。解决方案有两种1) 将项目代码放在/var/lib/jenkins/workspace或/tmp等公共目录2) 调整目录权限需谨慎有安全风险。在测试时最简单的方法是将示例项目创建在/tmp下。控制台输出乱码确保Jenkins的系统编码和SSH终端的编码一致通常为UTF-8。可以在Jenkins的“系统管理” - “系统信息”中查看file.encoding属性。7. 高级配置与性能调优当基础构建跑通后我们需要让这个在Brainy Pi上的Jenkins更稳定、更高效、更安全。7.1 Jenkins系统配置优化修改Jenkins监听端口可选如果你不喜欢8080端口或者该端口已被占用可以修改。编辑Jenkins的配置文件sudo vim /etc/default/jenkins找到HTTP_PORT8080这一行修改为你想要的端口例如HTTP_PORT9090。保存后重启服务sudo systemctl restart jenkins。别忘了在防火墙中放行新端口。调整JVM运行参数Jenkins运行在Java虚拟机中其内存设置直接影响性能。编辑同一个文件/etc/default/jenkins找到JAVA_ARGS或JENKINS_ARGS。对于1GB内存的Brainy Pi可以尝试设置为JAVA_ARGS-Xms256m -Xmx512m -Djava.awt.headlesstrue-Xms256mJVM堆内存初始大小为256MB。-Xmx512mJVM堆内存最大为512MB。这是关键设置过大可能导致系统因内存不足而崩溃设置过小则Jenkins本身或大型构建任务可能失败。-Djava.awt.headlesstrue确保在无图形界面环境下正常运行。 修改后重启Jenkins生效。你需要监控构建时的内存使用通过htop来精细调整这个值。7.2 插件管理与必要插件安装Jenkins的强大离不开插件。进入“系统管理” - “插件管理”。Available plugins可选插件在这里搜索并安装你需要的插件。对于嵌入式CI/CD我推荐Pipeline定义流水线即代码Jenkinsfile是现代Jenkins的核心。Git Parameter允许在构建时选择Git分支或标签。Email Extension Template强大的邮件通知插件可以定制化构建结果邮件。Workspace Cleanup构建前后清理工作空间节省磁盘空间。Build Timeout设置构建超时防止卡死的任务一直占用资源。Update Center更新中心定期在这里更新已安装的插件以获取新功能和安全补丁。安装插件的心得在Brainy Pi上由于硬件性能限制不要一次性安装大量插件。按需安装并注意插件间的依赖。安装或更新后最好重启一下Jenkins服务 (sudo systemctl restart jenkins)让插件完全生效。7.3 用户权限与项目安全对于多人协作或公开访问的Jenkins权限管理至关重要。 进入“系统管理” - “安全全局配置”。启用安全确保“启用安全”被勾选。安全域Security Realm选择“Jenkins专有用户数据库”并允许用户注册。或者如果你有LDAP等外部系统可以集成。授权策略Authorization初学者可以从“登录用户可以做任何事”开始。但对于正式环境强烈建议使用“项目矩阵授权策略”或“Role-Based Strategy”插件为不同用户或用户组分配精细的权限如开发人员只能触发构建管理员可以配置任务。8. 嵌入式CI/CD流水线设计思路将Jenkins用于嵌入式开发与普通的Web后端开发有一些特殊考量。8.1 典型嵌入式构建流水线阶段一个完整的嵌入式CI流水线可能包含以下阶段可以在Jenkins Pipeline中定义pipeline { agent any // 可以指定在带有特定标签的agent上运行例如‘arm’ stages { stage(代码检出) { steps { git branch: main, url: https://your-git-repo.com/your-firmware.git } } stage(交叉编译) { steps { sh # 加载交叉编译工具链环境变量 source /opt/toolchain/activate.sh # 执行项目本身的构建脚本如 make all make clean make -j4 # 使用4个并行任务加速编译需根据Brainy Pi核心数调整 } } stage(静态代码分析) { steps { // 使用Cppcheck, PC-lint等工具进行代码检查 sh cppcheck --enableall --inconclusive . 2 cppcheck_report.txt || true // 将报告发布到Jenkins界面 publishCppcheck pattern: cppcheck_report.txt } } stage(单元测试) { steps { // 运行Unity, CppUTest等框架编写的单元测试 sh make test junit test_results/*.xml // 收集JUnit格式的测试报告 } } stage(生成固件) { steps { sh make package // 例如生成.bin或.hex文件 archiveArtifacts artifacts: output/*.bin, output/*.hex, fingerprint: true } } stage(硬件在环测试可选) { steps { // 此阶段可能需要连接实际的开发板 // 通过脚本将固件烧录到连接的Brainy Pi或其他设备并运行自动化测试 sh python3 flash_and_test.py ${JOB_BASE_NAME}-${BUILD_NUMBER}.bin } } } post { always { // 无论成功失败都清理工作空间 cleanWs() } success { emailext ( subject: 构建成功: ${JOB_NAME} - ${BUILD_NUMBER}, body: 固件已生成请查看附件或构建日志。, to: teamexample.com, attachmentsPattern: output/*.bin ) } failure { emailext ( subject: 构建失败: ${JOB_NAME} - ${BUILD_NUMBER}, body: 请及时查看构建日志以排查问题。, to: teamexample.com ) } } }8.2 资源管理与调度策略Brainy Pi性能有限如何管理并发构建是关键。执行器数量进入“系统管理” - “系统配置”找到“# of executors”。它决定了可以同时运行多少个构建任务。对于单核或双核的Brainy Pi建议设置为1。设置为大于核心数会导致任务严重排队系统负载激增反而降低整体效率。使用标签进行分组如果你有多个设备例如一个Brainy Pi用于编译另一个用于测试可以为它们打上不同的标签如builder,tester。在Pipeline的agent部分指定标签让特定任务在特定设备上运行。避免在Master节点运行重型任务最佳实践是让Jenkins MasterBrainy Pi本身只负责调度和界面展示将具体的构建任务分配给专门的Agent可以是另一台性能更强的机器甚至是Docker容器。但对于个人或小团队在Master上直接运行也是可行的只需控制好并发。8.3 与版本控制系统的集成实际项目中代码存储在Git服务器上。你需要在Jenkins中配置Git插件并添加你的仓库地址。配置凭据Credentials如果是私有仓库需要添加SSH密钥或用户名/密码。在Job的“源码管理”部分正确选择仓库和凭据。配置“构建触发器”例如“轮询SCM”Poll SCM让Jenkins定期检查仓库是否有新提交或者更优的方案是使用GitHub/GitLab的Webhook在代码推送时立即通知Jenkins触发构建。9. 故障排查与日常维护指南即使配置无误在长期运行中也可能遇到问题。这里记录一些常见问题的排查思路。9.1 Jenkins服务无法启动或访问问题现象sudo systemctl status jenkins显示服务失败。排查步骤查看详细日志sudo journalctl -u jenkins -f或sudo tail -f /var/log/jenkins/jenkins.log。日志通常会明确指出错误原因如端口冲突、Java版本不兼容、权限错误等。检查端口占用sudo netstat -tlnp | grep :8080。如果8080端口被其他程序占用修改Jenkins端口或停止那个程序。检查Java再次确认java -version输出的是预期版本。检查磁盘空间df -h。如果根分区满了Jenkins可能无法写入日志或临时文件。9.2 构建任务失败常见原因编译错误查看构建的控制台输出错误信息通常很明确。可能是缺少头文件、库文件或编译工具链未正确设置环境变量。确保构建脚本中正确source了交叉编译工具链的环境配置。权限不足构建过程中尝试写入某些系统目录失败。确保Jenkins用户jenkins对工作空间目录和必要的工具路径有读写权限。避免在构建脚本中使用sudo这是不安全且容易出错的做法。内存不足OOM构建过程被系统杀死。查看系统日志/var/log/syslog或dmesg输出寻找Out of memory或Killed process相关信息。解决方案是调整Jenkins JVM参数减小-Xmx增加交换空间或者优化构建过程如减少并行编译任务数make -j2。9.3 日常维护建议定期备份Jenkins的所有配置、任务和构建历史都存储在/var/lib/jenkins目录。定期备份这个目录至关重要。可以使用tar打包或者使用 Jenkins 自有的“ThinBackup”插件。清理旧数据构建日志和归档的构件会占用大量空间。配置“丢弃旧的构建”策略在Job配置页面的“General”部分勾选“丢弃旧的构建”设置保留构建的天数或最大个数。监控系统资源使用htop或glances定期检查Brainy Pi的CPU、内存和磁盘I/O使用情况。如果持续高负载需要考虑优化构建脚本或升级硬件。插件更新定期检查并更新插件但不要在重要项目进行前立即更新。最好先在测试环境验证新插件版本的兼容性。更新后建议重启Jenkins。将Jenkins部署在Brainy Pi上让我深刻体会到“轻量级”自动化带来的便利。它可能无法像大型服务器那样承担极其繁重的并发编译但对于个人项目、原型验证和小团队迭代来说完全绰绰有余。最关键的是整个过程让你对CI/CD的每一个环节都有了亲手掌控的感觉从系统配置、服务部署到流水线编写这种理解是单纯使用云服务无法替代的。如果你手头也有一块吃灰的开发板不妨用它来搭建你的第一个自动化构建环境迈出提升开发效率的关键一步。