4、持续集成环境:Jenkins安装与配置、嵌入式交叉编译环境集成、构建节点管理

好,咱们进入第四章。持续集成环境,说白了就是给嵌入式开发配个自动化流水线。我这些年折腾过不少CI工具,Jenkins虽然不是最轻量的,但胜在生态成熟、插件多,嵌入式场景下尤其好用。今天咱们就把它从零搭起来,顺便把交叉编译环境也揉进去。

4.1 Jenkins安装与基础配置

安装Jenkins其实不复杂,但我建议你别图省事用apt直接装。为什么?因为嵌入式项目经常需要特定版本的JDK,apt源里的版本往往太老。我个人习惯用官方war包,配合systemd管理,这样版本控制最灵活。

核心要点:Jenkins依赖Java 11或17,嵌入式交叉编译工具链通常需要Java 8,这两者不冲突,但要注意环境变量隔离。

安装步骤我简化一下:

# 下载Jenkins war包
wget https://get.jenkins.io/war-stable/2.440.3/jenkins.war

# 创建专用用户
sudo useradd -m -s /bin/bash jenkins

# 启动(首次会生成初始密码)
java -jar jenkins.war --httpPort=8080

嗯,这里要注意:首次启动后,浏览器访问 http://你的IP:8080,会要求输入初始密码。密码在 /home/jenkins/.jenkins/secrets/initialAdminPassword 里。我遇到过有人找半天找不到,其实路径里有个点开头的隐藏文件夹,别漏了。

4.2 嵌入式交叉编译环境集成

这才是重头戏。嵌入式开发最头疼的就是交叉编译——你在x86的服务器上,要编译出ARM或RISC-V架构的二进制。Jenkins默认跑在宿主机上,你得告诉它:嘿,编译的时候用这个工具链。

我建议的做法是:把交叉编译工具链做成Docker镜像。这样每个构建节点都能复用,版本也统一。你想想看,团队里有人用Ubuntu 20.04,有人用22.04,工具链路径都不一样,多乱啊。

我的经验:曾经有个项目,因为工具链版本不一致,导致A同事编译的固件在B同事的板子上跑不起来。排查了两天才发现是glibc版本差异。后来统一用Docker,再没出过这问题。

Dockerfile示例:

FROM ubuntu:22.04

# 安装ARM交叉编译工具链
RUN apt-get update && apt-get install -y \
    gcc-arm-linux-gnueabihf \
    g++-arm-linux-gnueabihf \
    make \
    cmake

# 设置环境变量
ENV CROSS_COMPILE=arm-linux-gnueabihf-
ENV CC=${CROSS_COMPILE}gcc
ENV CXX=${CROSS_COMPILE}g++

然后在Jenkins的Pipeline里,直接用这个镜像:

pipeline {
    agent {
        docker { image 'my-cross-compiler:1.0' }
    }
    stages {
        stage('Build') {
            steps {
                sh 'make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-'
            }
        }
    }
}

说白了,就是把交叉编译环境“容器化”。Jenkins每次构建都拉一个新容器,干净利落,不会污染宿主机。

4.3 构建节点管理

单机跑Jenkins,构建一多就卡。尤其是嵌入式项目,编译一次可能要十几分钟。这时候就需要构建节点(Agent)了。

节点管理有几个关键点:

  • 标签(Label)管理:给节点打标签,比如 arm64riscvfast-machine。Pipeline里指定标签,Jenkins会自动调度到合适的节点。
  • 节点类型:永久节点(固定服务器)和临时节点(Docker/K8s动态创建)。我建议混合使用——核心构建用永久节点,测试任务用临时节点。
  • 环境一致性:每个节点上都要有相同的交叉编译工具链。用Docker的话,这问题自动解决。

避坑指南:我曾经在节点上忘记安装某个库,结果构建到一半报错,浪费了半小时。后来我写了个脚本,每次节点注册时自动检查环境——check-toolchain.sh,确保所有依赖都在。

节点配置示例(通过SSH启动):

# 在Jenkins主节点上执行
java -jar agent.jar \
    -url http://jenkins-master:8080/ \
    -secret @secret-file \
    -name "arm-builder-01" \
    -labels "arm64 linux"

嗯,这里有个小技巧:用环境变量传递密钥,别硬编码在命令行里。我见过有人把secret写在脚本里,结果不小心提交到Git仓库,那叫一个尴尬。

4.4 实战:一个完整的嵌入式CI流水线

最后,咱们串起来看个完整的例子。假设你要编译一个基于ARM Cortex-M的固件:

pipeline {
    agent { label 'arm64' }

    environment {
        TOOLCHAIN_PATH = '/opt/gcc-arm-none-eabi'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        stage('Build') {
            steps {
                sh '''
                    export PATH=$TOOLCHAIN_PATH/bin:$PATH
                    make -j$(nproc)
                '''
            }
        }
        stage('Test') {
            steps {
                sh 'qemu-system-arm -machine lm3s6965evb -kernel build/firmware.elf'
            }
        }
        stage('Archive') {
            steps {
                archiveArtifacts artifacts: 'build/*.bin'
            }
        }
    }

    post {
        failure {
            emailext(
                subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
                body: "请检查构建日志: ${env.BUILD_URL}"
            )
        }
    }
}

你看,从拉代码、编译、测试到归档,一气呵成。失败还能自动发邮件。这就是持续集成的价值——把重复劳动自动化,让工程师专注于真正有挑战的事情。

总结一下:Jenkins安装不难,难的是把交叉编译环境管好。我的建议是:
1. 工具链用Docker封装,版本统一
2. 节点打标签,按需调度
3. Pipeline里写清楚环境变量,别依赖全局配置

做到这三点,你的嵌入式CI环境基本就稳了。下一章咱们聊聊持续部署——怎么把编译好的固件自动烧录到开发板上。那又是另一番天地了。