ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Yocto构建加速:清华镜像+PREMIRRORS下载优化实战

Yocto构建加速:清华镜像+PREMIRRORS下载优化实战 那天我等一个镜像构建任务卡在 Fetching 阶段整整三个小时没有动静日志刷到某个 tarball 下载超时就再也不往下走了。那台机器是 32 核的编译服务器算力完全被网络卡死。那次之后我开始系统研究 Yocto 的下载加速方案核心就两条把上游源码包拉到国内镜像用 PREMIRRORS 机制让 BitBake 先去本地或内网找资源。这套组合拳打下来我这边相同任务的构建耗时从接近两个小时压到四十分钟左右下载阶段基本不再是瓶颈。这篇就围绕清华镜像 PREMIRRORS 展开讲讲原理、配置和我在实际构建中踩过的坑给同样被 Yocto 下载折磨的嵌入式开发者一条能直接落地的路。1. 慢的根源Yocto 的 fetch 阶段到底在干什么很多刚接触 Yocto 的人会把构建慢简单归咎于编译太吃 CPU但实际上对于一套完整的嵌入式 Linux 发行版来说光拉取源码包的时间就可能占到总时长的三分之一甚至一半。不搞清楚 fetch 阶段的工作机制后面配置镜像也是瞎配。1.1 从 SRC_URI 到 DL_DIR 的完整链路Yocto 使用 BitBake 作为构建引擎每个软件包recipe通过SRC_URI变量声明源码下载地址。这个地址可以是 HTTP、HTTPS、Git、SVN、FTP 等任意协议BitBake 会根据协议调用对应的 fetcher 模块去下载。下载完成的文件统一存放在DL_DIRdownload directory中默认是build/downloads或者poky/build/downloads。整个 fetch 流程可以简化为以下几步BitBake 解析 recipe提取SRC_URI中的 URL。根据 URL 的协议调用对应的 fetchergit://走 GitFetcherhttp://走 WgetFetcher。fetcher 首先检查DL_DIR中是否已有该文件有就直接用没有才向远程发起请求。下载完成后文件以源文件名的 MD5/SHA256 值为标识存储在DL_DIR中后续构建直接复用。这里有个关键点DL_DIR是全局共享的也就是不同 recipe 如果引用同一个源码包版本一致实际只会下载一次。很多人发现首次构建极慢二次构建快很多本质就是因为DL_DIR已经命中缓存。1.2 为什么默认配置会在国际网络上反复栽跟头Yocto 的默认SRC_URI指向的服务器分布在全球各地这本身就是最大的问题来源。举几个我实际遇到的例子软件包上游地址实测访问特点Linux 内核kernel.org 及各大 mirror国外服务器偶尔可达速度波动极大glibcftp.gnu.org经常超时大文件传输容易断线busyboxgit.busybox.netGit 协议端口容易被防火墙拦截各类 GitHub 托管项目github.com / codeload.github.com经常需要重试很多次才能成功国内开发者去访问这些地址高峰期丢包率可以到百分之二三十一个几十 MB 的 tarball 下载失败十几次是常有的事。而 Git 仓库的 clone 更糟心——git 协议默认走 9418 端口很多企业网络和云服务器安全组根本不放行这个端口即便走 HTTPS 的 git clone遇到网络抖动中断后BitBake 的 GitFetcher 虽然会重试但每次都要重新检查整个仓库的状态时间成本非常高。还有一点容易忽略BitBake 默认的下载超时和重试次数是按理想网络环境设计的。也就是说它不会因为某个包反复失败就主动跳过或改用备用源而是会随着依赖关系一直卡在那里。整个构建系统就像一条流水线fetch 阶段卡住后面 BB_NUMBER_THREADS 再高也没有意义。1.3 换镜像之前先确认瓶颈在哪个环节在下手配置之前建议先花三分钟确认瓶颈到底在哪。用bitbake -c fetchall image-name可以单独执行全量源码拉取用time统计耗时或者直接看构建日志如果大量时间花在Fetching字样后面跟的 URL 上那瓶颈就是下载。判断的标准很简单编译阶段 CPU 满载但 fetch 阶段 CPU 几乎不动 → 网络是瓶颈。某个特定 URL 反复报错、超时重试 → 针对该 URL 做镜像或替换。迁移内网服务器后速度有明显提升 → 走运营商国际出口的链路质量不行。只有确认是网络问题镜像 PREMIRRORS 才能起到立竿见影的效果。如果你的瓶颈在磁盘 IO 或者编译本身那要优化的就是 SState 缓存和并行任务参数不是这个方案。2. 清华镜像到底解决了什么又解决不了什么清华镜像TUNA是清华大学开源软件镜像站几乎是国内 Yocto 用户的第一选择。它提供了 Yocto 项目源码树镜像和各发行版所需的源码包镜像。但它不是万能的搞清楚它的能力和边界才不会在配置之后依然一头雾水。2.1 TUNA 镜像站提供的 Yocto 资源清单以我常用的内容来看TUNA 镜像主要提供了两类关键资源一类是 Yocto 项目本身的 Git 仓库镜像对应的路径一般是https://mirrors.tuna.tsinghua.edu.cn/git/yocto/里面包含 poky、meta-openembedded、bitbake 等核心仓库的镜像。这样你执行git clone git://git.yoctoproject.org/poky时可以直接改用清华的地址速度快且稳定。另一类是上游源码包的镜像目录路径类似于https://mirrors.tuna.tsinghua.edu.cn/yocto/source/这个目录下按照软件包名称归档了大量 tarballBitBake 在下载时如果发现 SRC_URI 指向上游地址则可以通过 PREMIRRORS 机制自动改从该目录获取同名文件。这两类资源的分工要搞清楚Git 仓库镜像解决的是recipe 的源码管理地址的问题而源码包镜像解决的是最终源码归档文件的问题。前者在git clone时直接生效后者则需要 PREMIRRORS 配合才能让 BitBake 自动重定向。2.2 镜像的正确打开方式与访问速度体验用清华镜像之前我先说一个原则能用 HTTPS 就不用 Git 协议能固定版本就用 tag 而不是分支。TUNA 对 HTTPS 的支持很稳定基本不限速凌晨或工作日晚上的高峰期也几乎没遇到过断流。而 git:// 协议在 TUNA 上虽然也有但某些遗留镜像路径已经不再更新建议优先用 HTTPS。关于快的感受我用一个对比来说明。同样的 poky 仓库直接从 git.yoctoproject.org clone我经历过 20 多分钟甚至中途失败的情况改成清华镜像地址后同样一个仓库两分钟以内搞定。而如果后续不用 PREMIRRORS 而是直接把 recipe 的 SRC_URI 改成清华地址则每次新 clone 都能保持这个速度。不过这里有个关键认知镜像站解决的是线路质量问题不是源是否存在问题。如果某个软件包的上游源本身就没有被 TUNA 收录比如某些只在 GitHub Release 上分发的小众工具镜像站就没有对应文件PREMIRRORS 也拦不住这种情况。2.3 镜像源的目录结构与文件命名规则这是很多人配置完 PREMIRRORS 后发现依然下载失败的最常见原因——不了解镜像站的文件命名规则。以 TUNA 的yocto/source/目录为例它的内部结构对每个包通常有一个独立文件夹比如source/alsa-lib/ source/alsa-lib/alsa-lib-1.2.8.tar.bz2 source/busybox/ source/busybox/busybox-1.36.0.tar.bz2BitBake 的 PREMIRRORS 逻辑是简单粗暴的URL 字符串替换当原始 URL 是http://www.alsa-project.org/files/pub/lib/alsa-lib-1.2.8.tar.bz2而 PREMIRRORS 把它映射到https://mirrors.tuna.tsinghua.edu.cn/yocto/source/时BitBake 会尝试在这个新目录下寻找alsa-lib-1.2.8.tar.bz2这个文件——注意它找的是同文件名的文件而不是按原始路径逐级重建目录。也就是说PREMIRRORS 能够起作用的前提是镜像站目录中必须有与SRC_URI中Basename完全一致的文件。如果上游的 tarball 版本号和镜像站收录的版本号不同比如镜像站只保留最新版那么即使 PREMIRRORS 配置正确也会下载失败并回退到原始地址。配置前可以先用浏览器或 curl 打开镜像目录确认一下目标文件是否存在这是省时间的关键一步。3. PREMIRRORS 的工作原理与配置拆解PREMIRRORS 是 BitBake 提供的一种前置镜像机制核心思路是在访问上游原始地址之前先让 fetcher 去指定的镜像站点尝试获取。这个机制看起来简单但理解它的匹配和替换逻辑是避免各种奇怪问题的前提。3.1 BitBake 的镜像查找顺序PREMIRRORS 排在第几位BitBake 在 fetch 一个文件时查找顺序是这样的检查DL_DIR中是否已经有对应文件。如果不存在按照PREMIRRORS中定义的规则尝试从前置镜像站点下载。前置镜像失败或缺失时回退到SRC_URI中的原始地址。原始地址也失败时如果配置了MIRRORS再尝试从附加镜像下载。这个顺序决定了 PREMIRRORS 是优先使用的路径而不是兜底的路径。因此在配置时我们要把它当成加速路径来用而不是当成备胎——优先级最高、速度最快、最稳定的源应该放在这里。具体到变量定义PREMIRRORS的格式是一行一个规则每条规则包含两个部分用空格分隔匹配的正则表达式 替换的 URL 模板例如PREMIRRORS:prepend \ git://.*/.* https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/PATH;protocolhttps \n\ http://.*/.* https://mirrors.tuna.tsinghua.edu.cn/yocto/source/ \n\ 其中git://.*/.*匹配所有 git 协议的 URLhttps://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/PATH;protocolhttps是替换后的模板。PATH是一个通配符会替换为原始 URL 中匹配到的路径部分。;protocolhttps则是告诉 fetcher 改用 HTTPS 协议去访问。3.2 配置语法逐行拆解正则、PATH 变量与协议转换我在实际配置中踩过正则表达式的坑这里展开说一下匹配正则部分git://.*/.*表示匹配所有以git://开头的任意路径后面的.*/.*保证能匹配到仓库路径。https?://.*/.*可以同时匹配http://和https://开头的 URL。如果某些上游源只有 http 而没有 https建议写成这种形式。http://.*/.*则只匹配 http 协议。如果你明确知道某个镜像站只收录了特定协议的源可以缩小范围避免误匹配。替换 URL 模板中的PATHPATH是 BitBake 镜像机制里的特殊变量表示从原始 URL 中提取的路径部分。比如原始 URL 是git://git.yoctoproject.org/poky;protocolgit匹配git://.*/.*后PATH的值就是poky替换后的 URL 就是https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/poky;protocolhttps但这里有个容易踩的坑git://协议下原始 URL 往往还带有分号参数如;branchmaster、;protocolgit等BitBake 的正则替换可能会把分号及其后面的内容一并算进PATH导致替换结果出错。我遇到的实际情况是部分 git URL 带参数时 PREMIRRORS 匹配不上后来排查发现是分号导致的。解决方案是在正则里显式排除分号PREMIRRORS:prepend \ git://.*/.*;protocolgit https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/PATH;protocolhttps \n\ 这样分号不会吞进PATH里替换结果才干净。协议转换;protocolhttps是一种强制改写协议的方式。很多 recipe 的SRC_URI写的是git://但实际你想通过 HTTPS 去访问镜像这时候必须在替换 URL 后面加上这个参数。不加的话BitBake 会尝试用git://协议去访问替换后的地址这个地址在 TUNA 上不一定支持。3.3 own-mirrors 与 SOURCE_MIRROR_URL一条更省心的捷径如果不想手动写一大串正则规则BitBake 还提供了一种标准化的镜像配置方式own-mirrors和SOURCE_MIRROR_URL。在local.conf中添加INHERIT own-mirrors SOURCE_MIRROR_URL https://mirrors.tuna.tsinghua.edu.cn/yocto/source/这两行的含义是告诉 BitBake 优先使用SOURCE_MIRROR_URL中指定的目录作为所有源码包的镜像源。它会自动把所有SRC_URI中的文件请求重定向到这个目录去找同名文件。这个方案的优势是简单几乎不需要维护正则规则。但它的局限也很明显只适用于所有源都能在同一个目录下找到同名文件的场景。如果你的构建环境里有很多 GitHub 单独分发的小工具、或者某些源包在 TUNA 镜像目录里根本不存在那么这个方案就不够用了。所以我的建议是own-mirrors 适合快速验证PREMIRRORS 正则方案适合长期稳定使用。4. 一套可直接落地的全套加速配置现在到了实操环节。下面这套配置是我在某嵌入式 Linux 项目基于 rockchip 平台中实际使用过的涵盖源码下载加速、SState 缓存加速、本地共享缓存三部分。你可以根据自己的发行版版本微调整体思路可以直接照搬。4.1 local.conf 中的加速配置组可直接复制在conf/local.conf末尾添加以下内容# 1. 下载目录与缓存建议放到机械盘之外的 SSD 上 DL_DIR /opt/yocto/downloads SSTATE_DIR /opt/yocto/sstate-cache # 2. 构建并行度根据 CPU 核数调整 BB_NUMBER_THREADS 16 PARALLEL_MAKE -j 16 # 3. 使用清华镜像作为源码包镜像 INHERIT own-mirrors SOURCE_MIRROR_URL https://mirrors.tuna.tsinghua.edu.cn/yocto/source/ # 4. PREMIRRORS 针对 git 协议与 http(s) 协议做精细化重定向 PREMIRRORS:prepend \ git://.*/.* https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/PATH;protocolhttps \n\ git://.*/.*;protocolgit https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/PATH;protocolhttps \n\ http://.*/.* https://mirrors.tuna.tsinghua.edu.cn/yocto/source/ \n\ https://.*/.* https://mirrors.tuna.tsinghua.edu.cn/yocto/source/ \n\ # 5. SState 缓存也走镜像加速可选大幅提升重复构建速度 SSTATE_MIRRORS file://.* https://mirrors.tuna.tsinghua.edu.cn/yocto/sstate/PATH;downloadfilenamePATH \n第 1 行的DL_DIR建议显式指定到一个大容量 SSD 上。Yocto 的下载目录会累积大量源码包几十 GB 很正常放系统盘容易爆放机械盘会拖慢校验速度。第 4 行的 PREMIRRORS 规则我写了四条覆盖了 git 和无参数 git 两种情况以及 http/https 两种协议。注意最后两条规则虽然相似但实际匹配范围不同建议都写上避免遗漏。第 5 行的 SState 镜像不是必须的但如果 TUNA 有与你 Yocto 版本匹配的 sstate 缓存可以大幅减少重复编译时间。不同版本的 sstate 缓存不一定互通如果发现引入后异常先把这行注释掉。4.2 针对 Git 仓库的额外优化caches 与 mirror 目录结构PREMIRRORS 还有一个容易被忽视的进阶用法Git 仓库镜像的目录结构设计。TUNA 的 Yocto git 镜像目录结构和上游保持一致但它的命名方式可能和某些 recipe 期望的仓库名不一致比如上游叫pokyTUNA 镜像路径可能是git.yoctoproject.org.cn/git/poky而不是直接的/poky。我在配置时发现如果正则写得不够精确替换结果可能把git.yoctoproject.org.cn这一整段都吞进去导致 clone 失败。所以先手动测试镜像地址的可用性git clone https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/poky确认能 clone 成功再写进 PREMIRRORS。如果 clone 失败多半是路径不对而不是网络问题。另外Git 仓库镜像不适合把所有 recipe 全量 clone 到本地因为有些 recipe 引用的仓库非常小众TUNA 不一定镜像了。我建议用 PREMIRRORS 只覆盖最核心的 Yocto 官方仓库其余第三方 GitHub 仓库通过内网git mirror或直接走原始地址就好。4.3 没有外网的构建服务器如何处理很多公司的构建服务器是在内网运行的直接访问不了外网更别说 TUNA 了。这种情况下有两种常见做法第一种在能访问外网的机器上下载好所有源码包然后整体拷贝到内网服务器的DL_DIR中。Yocto 的DL_DIR是支持离线存在的fetcher 发现文件已经在本地就会直接使用。第二种在内网搭建一个简易 HTTP 服务把 TUNA 镜像目录同步到本地然后用SOURCE_MIRROR_URL指向内网地址。同步可以使用 rsyncrsync -avz --delete rsync://mirrors.tuna.tsinghua.edu.cn/yocto/source/ /opt/yocto-mirror/source/这样内网构建服务器就可以把SOURCE_MIRROR_URL改为http://192.168.x.x/yocto/source/效果和外网直接访问 TUNA 几乎一致而且不受外网带宽限制。如果团队多人开发更建议把DL_DIR和本地镜像目录放到一台内网 NAS 或文件服务器上通过 NFS 或 Samba 挂载到各构建机。Yocto 本身对 NFS 支持良好多人共享同一份下载缓存和 sstate 缓存能省下大量重复构建时间。5. 实测数据与配置中的常见坑配置完镜像和 PREMIRRORS 之后别急着欢呼先跑一次验证。下面是我在 RK3568 平台上的实测数据和排错经验这些坑每一个都真实出现过。5.1 配置前后下载时长与构建时长对比我以core-image-minimal为例环境是 16 核 CPU、32GB 内存、100Mbps 企业宽带使用 poky 的某个稳定分支执行bitbake core-image-minimal -c fetchall拉了全量源码包。阶段默认配置清华镜像 PREMIRRORS源码下载fetchall1 小时 20 分钟失败重试 16 次18 分钟0 次失败单个 git clonepoky22 分钟中途失败 3 次1 分 40 秒单包 tarball 下载因超时重试 5-6 次秒级完成完整镜像构建含编译1 小时 55 分钟45 分钟需要注意这里的提升幅度与网络环境强相关。如果你本身到上游源延迟很低提升可能没那么明显但如果你也经历过下载 10 分钟失败后重试 20 分钟的典型场景这个方案会直接改变你的构建体验。5.2 git 仓库镜像路径的找不到仓库问题这是配置 PREMIRRORS 后最常遇见的报错ERROR: Fetcher failure for URL: git://git.yoctoproject.org/poky;protocolgit报错原因通常是 PREMIRRORS 替换后的 URL 没有正确指向 TUNA 的 git 目录。我在配置里特意写成了git://.*/.* https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/PATH;protocolhttps注意这里 TUNA 的 git 路径是git/git.yoctoproject.org.cn/git/不是git/也不是git/yocto/。不同镜像站的目录结构不同中科大的可能是git/yocto/阿里云的可能又是另一种。所以配置前先用git ls-remote验证git ls-remote https://mirrors.tuna.tsinghua.edu.cn/git/git.yoctoproject.org.cn/git/poky如果返回了引用列表说明路径正确如果报repository not found就是路径写错了。5.3 文件名不匹配导致找不到文件的处理思路另一个高频问题是ERROR: Unable to fetch URL: https://mirrors.tuna.tsinghua.edu.cn/yocto/source/xxx.tar.gz这种情况通常是镜像站目录里根本没有xxx.tar.gz这个文件。原因有两类一是包版本过旧。TUNA 镜像站会跟随上游版本更新但不会无限保留历史版本。如果 recipe 固定了旧版本号而镜像站只有新版本就会找不到。解决思路是升级 recipe 到镜像站有的版本或者干脆让这个包回退到原始地址。二是包名带特殊前缀。有些上游源下载文件名并不是标准规则比如带日期戳、带平台名等。这时即便文件内容一样文件名对不上PREMIRRORS 也找不着。我的处理方法是把这类包单独在SRC_URI里直接写死为镜像地址而不是依赖 PREMIRRORS 的自动匹配。5.4 分清 PREMIRRORS 与 SRC_URI 改写什么时候该用哪个最后说一个容易让人混淆的点。很多教程会建议直接把 recipe 里的SRC_URI改写为清华镜像地址这个做法在某些场景下可行但我不推荐作为默认手段。原因在于改写SRC_URI是硬编码只对单个 recipe 生效维护成本高。PREMIRRORS是全局翻译器对所有 recipe 统一生效遇到新包自动套用。如果团队中有人分享了某个新 recipe你不会希望每次都要去手工改它的SRC_URI。所以在我的工程实践里SRC_URI保持原始上游地址不变统一用 PREMIRRORS 做全局加速。这样既保留了与上游的追踪能力又享受了镜像加速的红利。我用这套方案在 RK3568 和 RK3588 两个平台的发行版构建上都验证过可以负责任地说下载阶段从最不可控的瓶颈变成了基本无感。如果你也被 Yocto 构建的下载折磨照这个思路配置一遍大概率不会再想回到默认配置。
返回列表