1. 先搞明白Yocto 构建慢的根源与 BitBake 抓取顺序1.1 一次完整构建要拉多少源码先给你一个直观的量级感受。一个最普通的core-image-minimal也会涉及 Linux 内核、busybox、glibc、gcc 交叉编译器这些大头源码总量大概在 2~5GB一旦换成带桌面或 Qt、GPU 中间件的 BSP 镜像300 到 800 个 recipe 加起来源码和 git 仓库总共拉到 10GB 以上是非常正常的。真正让人头疼的不只在于总数据量大更在于来源站点杂、文件数量多。同一套构建里可能既要从git.yoctoproject.org拉内核和 poky 自带组件又要从 GitHub 拉第三方库还要从上游的 SourceForge、GNU 官方服务器拉各种 tarball。这些站点的线路质量、限速策略千差万别任何一个卡住整个构建就停在 fetch 阶段一动不动。我自己统计过一次全量拉取干净的 DL_DIR 下构建带 Qt 的镜像源码阶段总下载量约 11GB包含 400 多个独立文件另有 20 多个完整 git 仓库历史。直连海外站点的情况下这个阶段跑三四个小时不稀奇中途时不时冒出连接超时、服务器断流。所以很多 Yocto 老手都有同一个共识构建慢很多时候根本不是 CPU 不够而是下载卡住了。1.2 BitBake 的抓取顺序PREMIRRORS 站在最前面先说明一个核心事实BitBake 抓源码的顺序是固定的PREMIRRORS是优先级最高的一环。一条 recipe 里的 SRC_URI 被解析出来后抓取流程大致是下面这样recipe 的 SRC_URI 地址 │ ▼ PREMIRRORS 规则匹配 ──命中──→ 从镜像地址下载 │ │ 成功 │ 未命中 / 镜像下载失败 ▼ ▼ 落到 DL_DIR 原始 SRC_URI 地址 ──成功──→ 完成校验、进入编译 │ │ 原始地址也失败 ▼ MIRRORS 规则匹配 ──→ 备用镜像 / 报错也就是说PREMIRRORS会在访问原始地址之前先把下载地址劫持到你配置的镜像上只有镜像缺失或下载失败后才会回退到原始地址。而MIRRORS是反过来原始地址失败之后才轮到的后手。这两个变量长得像作用时机完全不同很多人一开始会搞混。除此之外还有一个绕不开的老朋友DL_DIR。它是 BitBake 的下载缓存目录默认在build/downloads/。真正发起网络请求之前BitBake 会先看 DL_DIR 里有没有同名文件有就直接用不重复下载。所以概括起来就是PREMIRRORS解决第一次下载从哪拉DL_DIR 解决拉过之后还要不要再拉两者配合才是完整的加速思路。1.3 清华镜像解决的是哪一环TUNA 是清华大学维护的开源软件镜像站很多开发者都用过它的 pip、apt、Homebrew 源。它同样同步了 Yocto Project 的源码镜像目录地址是https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/这个目录的存在意义是把 BitBake 要抓的各类源码 tar 包和 git 裸仓库在镜像站里按约定好的布局放一份。配置 PREMIRRORS 之后原本要去海外站点碰运气的下载就变成了从国内服务器下载镜像站本身就是多线接入教育网、电信、联通访问都比较稳定带宽通常也能跑满。这里想特别强调一点镜像站不会改文件内容。BitBake 下载完成后仍然要做 SHA256 校验校验值来自 recipe 里的SRC_URI[sha256sum]这层约束保证了从镜像拿到的源码和上游完全一致。换句话说镜像加速是在不牺牲校验安全性的前提下把拉得动变成拉得快。2. PREMIRRORS 配置实战local.conf 接入清华镜像2.1 配置前先确认镜像目录可达动手改配置之前建议先花一分钟把镜像目录确认一遍。直接在浏览器里打开https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/或者在服务器上执行curl -I https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/只要返回 200 OK 就行。这个目录下面按抓取器的约定存放源码普通文件按原文件名放好git 仓库则通常在git2/子目录下以主机名.路径的格式做成裸仓库。你不需要完全搞懂这套布局但提前确认它存在且能访问能省下后续大量的排查时间。如果构建机在公司内网有 TLS 中间设备curl 一条 https 过去可能会报证书错误。遇到这种情况不用纠结把后面的镜像地址统一写成http://开头同样能用TUNA 对 http 的支持也很好速度影响可以忽略。2.2 local.conf 最小配置模板可以直接抄最标准的做法是把 PREMIRRORS 写进build/conf/local.conf在文件末尾追加以下内容# 使用清华大学 TUNA 镜像作为 BitBake 源码预镜像 PREMIRRORS:prepend \ git://.*/.* https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/ \n \ https?://.*/.* https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/ \n \ ftp://.*/.* https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/ \n \ 这三条规则的含义逐条拆开看git://.*/.*匹配所有 git 协议开头的 SRC_URI。BitBake 的 Git Fetcher 命中镜像后会到镜像基地址下的git2/目录里按固定规则找对应裸仓库。这一条覆盖的是内核、gcc 这类以 git 形式拉取的组件也是全量构建里下载体量最大的部分。https?://.*/.*这里的https?是正则写法同时匹配http://和https://。绝大多数普通源码包下载地址都属于这一类覆盖面最广。ftp://.*/.*部分老 recipe 还在用 ftp 协议拉文件一并导过去。必须强调的是每一条规则正则和它后面的镜像地址之间以及两条规则之间都要用\n分隔。这是 PREMIRRORS 的固定语法BitBake 靠换行符把一整段字符串拆分成一组成对规则。漏了一个\n正则会被拼接到一起轻则规则失效重则把下载导到完全错误的地址上。提示如果你在特别老的环境上构建比如 Yocto 2.x 时代的 thud、warrior冒号写法可能不识别需要换成老语法PREMIRRORS_prepend。Yocto 3.0dunfell开始推荐新写法后续版本逐渐移除了旧语法支持新工程一律用冒号版本就对了。2.3 官方还有一条捷径own-mirrors其实 OpenEmbedded 自带了一个专门处理镜像的类叫own-mirrors用它配清华源会更省事。在 local.conf 里写INHERIT own-mirrors SOURCE_MIRROR_URL https://mirrors.tuna.tsinghua.edu.cn/yocto/sources/这个类会自动把SOURCE_MIRROR_URL展开成一组 PREMIRRORS 规则。优点是代码简洁、不容易写错缺点是你对协议匹配的精细控制变少了。如果只是个人构建提速用 own-mirrors 完全没问题但如果是给团队维护构建配置希望以后能按需调整某个协议的镜像地址手写 PREMIRRORS 反而更直观维护成本更低。我自己的习惯是测试时用 own-mirrors 快速验证链路是否通正式沉淀到团队配置时改回手写 PREMIRRORS并把规则写进一个单独的 include 文件里方便统一管理和加注释。2.4 配置放在哪一层、怎么管理local.conf 只对当前构建目录生效适合个人验证。如果团队所有人都想用这套加速不必让大家各自复制粘贴更好的做法是写进自己的 distro 配置或 layer 里比如在meta-xxx/conf/distro/下的 .conf 文件里统一设置。这样只要 layer 被 include所有构建都会自动带上镜像配置。但这里有一个需要权衡的点镜像配置一旦进了 distro 层对外部协作者就是强制的。如果你发布的开源 layer 里写死了清华源别人在海外反而会被绕到国内服务器速度未必更快。所以开源项目更合适的做法是把 PREMIRRORS 作为推荐配置写进local.conf.sample并在 README 里说明用法由使用者自行决定打开还是关闭。团队内部项目则没这么多顾虑直接固化到公共配置里最省心。3. 配置验证与加速效果实测下载日志与 DL_DIR 检查3.1 三招验证配置是否生效配置写完之后别急着闷头构建。先花几分钟做三个验证确认下载真的走了清华镜像否则你可能配了个寂寞。第一招看变量是否真的被解析进 BitBake。执行bitbake -e core-image-minimal | grep -E ^PREMIRRORS如果输出里能看到mirrors.tuna.tsinghua.edu.cn说明变量已经进入解析结果。看不到的话先检查语法和文件位置八成是写错了地方或者:prepend拼写不对。第二招用调试模式抓一次真实的抓取过程。随便挑一个简单 recipe比如:bitbake -DD -c fetch busybox输出里会有大量 DEBUG 信息直接在终端里搜索mirror、Trying、PREMIRROR这些关键字能看到 BitBake 在尝试预镜像时实际拼出来的 URL。确认里面的主机名是mirrors.tuna.tsinghua.edu.cn而不是git.yoctoproject.org或github.com。第三招用断网构建反推覆盖度。在 local.conf 里临时加上BB_NO_NETWORK 1然后执行bitbake --runallfetch core-image-minimalBB_NO_NETWORK的意思是禁止一切网络访问这种情况下所有 fetch 都能成功说明全部源码都从镜像拿到了配置覆盖是完整的。如果有 recipe 报网络错误就说明它的某种协议没被 PREMIRRORS 覆盖到按报错信息去补规则即可。注意验证完记得把BB_NO_NETWORK 1这行删掉或者设为 0否则后续构建一遇到 DL_DIR 里没有的文件就会直接失败。3.2 实测效果下载阶段从碰运气到跑满带宽这套方案的实际效果我在几个项目里都验证过。最直观的一次是给一块 ARM 平台的板子做 BSP 镜像干净的 DL_DIR构建带 Qt 的完整镜像。配置清华镜像之前fetch 阶段断断续续跑了接近三个小时中间因为 GitHub 连接被掐断还重新拉了好几次大仓库配置之后同样的源码量下载阶段 26 分钟左右全部完成速度基本稳定在 20 到 40MB/s。需要提醒的是镜像加速带来的总时长收益取决于下载时间在整个构建时间里的占比。首次全量构建、CI 每次用干净缓存目录、网络条件本身又差的场景收益非常明显整体构建时间能压缩 30% 到 50%说下载速度翻倍都是保守的。但如果 DL_DIR 常年不清理源码早就缓存好了构建瓶颈在 CPU 编译上那镜像对总时长的帮助就有限。想让这种场景也翻倍得靠后面要讲的 sstate 缓存。3.3 让加速成果沉淀DL_DIR 的复用镜像解决第一次下载DL_DIR 解决后面的每一次。默认的 DL_DIR 在build/downloads/可以通过DL_DIR变量把它挪到独立位置比如一块 SSD 或一个共享目录。我强烈建议把 DL_DIR 放在 SSD 上因为git2/底下那些裸仓库文件数量非常多构建过程中要反复读取机械盘很容易成为新的 IO 瓶颈。如果你想多台机器共用一份源码缓存可以在每台机器上把 DL_DIR 指向同一个 NFS 共享目录。BitBake 自身有 .lock 文件机制做并发保护多台机器同时构建同一个目录通常不会互相踩坏文件。不过 NFS 上的锁偶尔会有延迟问题构建机数量多的时候更稳妥的做法是搭一个内网镜像服务器而不是直接共享 NFS这一点在下一节展开。4. 进阶提速离线构建、内网镜像与 sstate 缓存4.1 彻底离线化BB_NO_NETWORK 与 BB_FETCH_PREMIRRORONLY很多团队做产线或 CI 构建时不允许机器直接访问外网。BitBake 提供了几个跟网络控制相关的开关和 PREMIRRORS 组合起来非常实用。BB_NO_NETWORK 1是彻底禁止网络抓取任何 recipe 只要 DL_DIR 里没有对应文件就直接报错。这个开关适合离线产线也适合用来确认DL_DIR 加镜像是否已经完整覆盖所有源码前面验证环节已经用过一次。BB_FETCH_PREMIRRORONLY 1是只允许使用 PREMIRRORS不允许访问原始地址。它和BB_NO_NETWORK的区别在于并没有禁止下载行为本身只是把下载来源死死限制在镜像上。当你确定清华镜像覆盖完整、不想让任何请求绕回 GitHub 时可以打开这个开关。它还有一个很实用的排查用途当 recipe 下载失败时开着这个开关报错会立刻告诉你镜像上没有这个文件而不是让它默默回退到原始地址慢吞吞重试。如果你的 Yocto 版本比较新还可以用BB_ALLOWED_NETWORKS做白名单只允许访问少数几个指定主机其他一律拒绝。这套组合拳下来整个构建的网络行为就完全可控了。4.2 自建内网文件镜像让团队复制你的速度清华镜像虽好但团队人多、构建频繁时每次都去外网拉总归不优雅。一个很常规的做法是利用 BitBake 自带的镜像导出能力在公司内网再架一层自己的镜像。在构建配置里打开BB_GENERATE_MIRROR_TARBALLS 1BitBake 在抓取 git 仓库的同时会额外生成源码归档方便把归档整理成镜像。等 DL_DIR 积累得差不多了用 rsync 把整个目录同步到内网服务器上再给其他机器配置PREMIRRORS:prepend \ git://.*/.* file:///opt/yocto-downloads/ \n \ https?://.*/.* file:///opt/yocto-downloads/ \n \ ftp://.*/.* file:///opt/yocto-downloads/ \n \ 这条规则的思路和清华镜像完全一样只是把镜像地址换成了内网路径。要注意的是file://镜像要求目录结构和 DL_DIR 保持一致也就是直接把 DL_DIR 的内容复制过去即可。第一次同步后后续每天跑一次 rsync 增量同步团队所有人就都能享受秒级命中的源码缓存了。4.3 sstate 缓存才是总时长的终极大招前面反复强调镜像最多解决下载阶段。如果想让整体构建时间也翻倍真正的大杀器是 sstate 缓存。sstate 是 BitBake 编译产物的缓存包含每个 recipe 已经编译好的输出命中之后直接跳过编译提速效果比源码镜像高一个数量级。sstate 缓存的配置方式类似SSTATE_MIRRORS file://.* http://your-cache-server/sstate-cache/PATH \n其中PATH是占位符BitBake 会替换成实际的架构和机器路径。对个人或小团队来说最省事的方案是把共享 sstate 目录放在 NFS 或一台内网服务器上配置SSTATE_DIR指向同一个位置。编译过的组件第二次构建时直接命中缓存整个镜像的构建时间能从小时级降到十几分钟。公共的 sstate 镜像不如源码镜像那么普及因为它是针对具体架构、版本、机器配置生成的通用性差很多。所以我的建议是源码镜像用清华sstate 缓存自己攒。攒 sstate 最直接的方式就是在 CI 上把常用镜像构建一遍然后把 sstate-cache 目录共享出来后续的构建自然越跑越快。4.4 其他镜像源与选型参考除了清华也值得看看其他可用的备选源。http://sources.openembedded.org/OpenEmbedded 官方源码镜像内容最全同步也最及时但服务器在海外国内访问速度一般适合作为清华源的回退补充。中科大 USTC 镜像也提供 yocto 相关目录地址格式类似使用前最好先 curl 确认目录结构。这类高校镜像的带宽和稳定性通常都很好。公司或实验室自建的 file:// 镜像最可控适合内网环境。各大云厂商的公开镜像一般不专门同步 yocto sources别指望通过 apt/pip 那一套镜像地址来加速 BitBake 抓取协议和数据格式完全不同。选型的大原则有两条一是用之前先确认镜像目录真实存在并且包含你要的源码版本二是不要把单一镜像当信仰配置里预留回退路径PREMIRRORS 和 MIRRORS 各指一个源网络波动时韧性会好很多。5. 常见问题排查与避坑技巧PREMIRRORS 配置疑难速查5.1 配了 PREMIRRORS 但还是连 GitHub这种情况我见过非常多先别怀疑镜像没用大部分是配置本身的问题。按下面的顺序逐个排查确认拼写是PREMIRRORS不是PREMIRROR也不是PREMIRRORS_PREPEND。确认语法新工程用PREMIRRORS:prepend老工程用PREMIRRORS_prepend混用会失效。确认位置local.conf 是否真的在生效的 BUILD_DIR 下如果构建时用了不同的TEMPLATECONF或 init 脚本配置文件可能不是你改的那份。确认解析结果跑bitbake -e target | grep ^PREMIRRORS看变量内容有没有进解析结果。确认 recipe 用的抓取器种类git、http、ftp 之外的协议不会被这三条规则覆盖如果碰到 gitsm、npm、crate、cpan 这类特殊抓取器需要单独处理。比如带 submodule 的gitsm://本质上还是 git 抓取器的变种但规则匹配的细节略有不同出现报错时日志要仔细看。还有一个容易忽略的小细节file://开头的本地文件地址不需要走镜像也不应该走镜像。这类 recipe 的 SRC_URI 本来就是本地路径镜像规则天然匹配不到不用刻意处理。5.2 镜像缺文件时的表现与对策镜像站是尽力而为的同步偶尔会有冷门文件没同步到或者某次上游更新后镜像还没来得及跟上。这种情况 BitBake 的表现是PREMIRRORS 命中了镜像地址但镜像返回 404于是回退到原始地址继续拉。如果你网络本来就不好这就会表现为个别源码下载特别慢。遇到这种情况先别急着怪镜像。可以临时打开BB_FETCH_PREMIRRORONLY 1再跑一次 fetch强制它不去访问原始地址这样失败的 recipe 会立刻把缺哪个文件暴露出来。然后去镜像目录里看一眼确认是没同步还是文件路径本身和你预期的不一样。如果是同步滞后等一等或者换备用源解决如果是路径问题调整你的 PREMIRRORS 规则。5.3 证书与协议相关的经典报错使用清华源时常见的报错有两类都很好认。一类是证书相关报错信息里会有SSL certificate problem、certificate verify failed这样的关键字。多发生在老版本 Yocto 搭配老系统 CA 证书的环境里解决办法很直接把 PREMIRRORS 里的https://换成http://。TUNA 支持 http 访问速度差异可以忽略。另一类是 git 低速被掐断。回退到原始 git 地址拉大仓库时如果链路持续低速git 自带的http.lowSpeedLimit/http.lowSpeedTime机制会把连接当作卡死直接断开。这时候可以调大超时容忍度git config --global http.lowSpeedLimit 1000 git config --global http.lowSpeedTime 600意思是 600 秒内平均速度低于 1000 字节/秒才判定为卡死基本能避免大仓库被误杀。5.4 常见问题速查表把平时被问得最多的问题整理成一张表遇到时可以直接对照定位现象大概率原因处理方式变量看起来配了但日志仍连 GitHubPREMIRRORS 语法或位置错误bitbake -e确认变量检查:prepend和新老语法报Fetcher failure ... exit code 4镜像和原始地址都失败从日志抄出最终 URL用 curl 手动验证哪个环节断了报checksum failed镜像文件同步异常或源文件更新核对 recipe 哈希删除 DL_DIR 中对应文件后重试个别 recipe 下载特别慢文件不在镜像上回退到了原始地址开BB_FETCH_PREMIRRORONLY1强制暴露缺口报证书错误老环境 CA 证书不识别 httpsPREMIRRORS 中改用 http://报git rev-list --all ... failedgit 仓库不在镜像 git2/ 目录检查仓库是否同步必要时让该 recipe 走原始地址排查时还有一个小技巧BitBake 的 verbose 日志会记录每一步抓取行为路径通常在tmp/log/cooker/machine/底下。搜索PREMIRROR或mirror关键字能看到它是先试镜像、再回退原始地址的完整过程很多时候比自己瞎猜要高效得多。最后分享一个我自己的习惯。每到一个新环境、新机器做 Yocto 构建我第一件事就是把 PREMIRRORS 和 DL_DIR 配置好然后不急着构建先跑一次bitbake --runallfetch 目标镜像把源码全部预取一遍盯着日志确认下载源都是清华域名之后才真正开始构建。这套提前量能避开大多数构建到一半卡在下载的糟心事。另外我会定期用 rsync 把 DL_DIR 备份一份因为源码缓存一旦累积起来重来的成本远比备份高。镜像加速不是一锤子买卖偶尔要关注一下同步情况但一旦跑顺了那种一条宽带拉满、构建一路绿灯的感觉真的很值。