news 2026/6/22 0:44:49

Ubuntu 18.04 安装 Jekyll 的系统级兼容性问题与解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu 18.04 安装 Jekyll 的系统级兼容性问题与解决方案

1. 为什么 Ubuntu 18.04 上跑 Jekyll 不是“装个 gem 就完事”——一个被低估的系统级兼容性问题

Jekyll 是静态网站生成器里最沉稳的老派选手,它不靠实时热更新炫技,也不靠插件生态堆砌功能,而是用 Ruby 的简洁语法和 Liquid 模板的清晰逻辑,把 Markdown 文档稳稳地编译成纯 HTML 文件。这本该是极简主义者的天堂。但当你在 Ubuntu 18.04 上执行gem install jekyll,看到终端里滚动出一长串ERROR: Failed to build gem native extension,或者更糟——卡死在make步骤、报错unable to make protected void java.util.resourcebundle.setparent(注意:这个 Java 错误其实是 Ruby 编译时链接了错误的系统库导致的混淆提示),你就立刻明白:这不是 Ruby 版本太低的问题,而是整个构建链路在 Ubuntu 18.04 这个特定发行版上出现了系统级的“齿轮咬合错位”。

Ubuntu 18.04 发布于 2018 年 4 月,其默认的 Ruby 版本是 2.5.1,而当前主流 Jekyll(v4.3+)虽标称支持 Ruby 2.5+,但实际依赖的webrickhttp_parser.rb等底层 gem 在编译 C 扩展时,对系统工具链有隐性要求。最关键的三个组件是:make(构建调度器)、build-essential(编译器套件集合)、以及ruby-dev(Ruby 头文件与静态库)。很多人只装了build-essential,却漏掉ruby-dev,结果make能运行,但gcc在编译nokogiriffi时找不到ruby.h,直接报错退出。更隐蔽的是,Ubuntu 18.04 的make默认版本是 4.1,而某些较新 gem 的Makefile中使用了 4.2+ 的语法(比如$(file >...)函数),导致make解析失败,却把错误日志淹没在大量 C 编译输出中,最终表现为“无法安装 homebrew portable ruby”——这其实是社区用户在 macOS 上遇到的同类问题,被错误地复制粘贴到 Ubuntu 场景下,形成了一个典型的“跨平台误诊”陷阱。

我第一次在一台老旧的 Dell OptiPlex 7050 上部署 Jekyll 博客时,就栽在这个坑里。当时以为是网络问题,反复gem sources --add https://ruby.taobao.org/切换镜像源;又以为是权限问题,加了sudo;最后甚至怀疑硬盘坏了,因为make进程会卡住数分钟,iostat显示磁盘 I/O 飙高。直到我用strace -e trace=execve,openat,write make -j1跟踪命令调用,才发现在gcc尝试打开/usr/include/ruby-2.5.0/ruby.h时返回ENOENT。那一刻我才意识到:不是 Ruby 太老,是开发头文件根本没装。这个教训让我后来所有基于 Ubuntu 的 Ruby 开发环境初始化脚本里,第一行永远是apt install -y build-essential ruby-dev,而不是gem install

所以,这篇文章不讲“如何用 Jekyll 写博客”,而是聚焦一个具体、真实、高频的工程现场:在 Ubuntu 18.04 这个已进入 ESM(扩展安全维护)阶段的 LTS 系统上,如何让 Jekyll 的开发环境从零启动、稳定运行、且能长期维护。它解决的不是“能不能跑”,而是“为什么跑得磕磕绊绊”、“哪些错误日志是真线索、哪些是干扰项”、“离线环境下怎么救急”这些一线开发者真正卡住的点。如果你正对着终端里一串红色报错发呆,或者刚重装了系统想快速复现旧项目,那接下来的内容就是为你写的。

2. 构建链路四层解剖:从 apt 包管理器到 Jekyll 的 Ruby 字节码

要让 Jekyll 在 Ubuntu 18.04 上跑起来,你面对的不是一个单一的“安装命令”,而是一条横跨四个技术层级的构建流水线。每一层都可能成为故障点,而错误日志往往只暴露最后一层的表象。我们必须像拆解一台机械手表一样,逐层拨开外壳,看清每个齿轮的咬合关系。

2.1 第一层:APT 包管理器 —— 系统级依赖的基石

Ubuntu 使用 APT(Advanced Package Tool)作为底层包管理器。它不像 macOS 的 Homebrew 那样允许用户自由选择安装路径,而是严格遵循 FHS(Filesystem Hierarchy Standard)规范,将二进制文件、头文件、库文件分门别类存放在/usr/bin/usr/include/usr/lib等目录下。这意味着,build-essential这个元包(metapackage)安装的不是“编译器本身”,而是它所依赖的一组 deb 包清单:gccg++makedpkg-dev等。而ruby-dev同样是一个元包,它确保/usr/include/ruby-2.5.0/目录存在,并包含ruby.hst.h等关键头文件。

提示:build-essentialruby-dev必须成对安装。单独安装build-essential只能让你运行make命令,但make在编译 Ruby gem 的 C 扩展时,会调用gcc,而gcc又需要ruby.h来知道 Ruby 的内存结构和 API 接口。缺少ruby-devgcc就像一个没有地图的司机,知道要去哪(编译),但找不到路(头文件路径)。

验证方法很简单:

# 检查 build-essential 是否完整 dpkg -l | grep build-essential # 应输出:ii build-essential 12.4ubuntu1 amd64 Informational list of build-essential packages # 检查 ruby-dev 是否安装 dpkg -l | grep ruby-dev # 应输出:ii ruby-dev 1:2.5.1 amd64 Header files for compiling extension modules for the Ruby language # 检查关键头文件是否存在 ls /usr/include/ruby-2.5.0/ruby.h # 应输出:/usr/include/ruby-2.5.0/ruby.h

如果ruby-dev未安装,执行sudo apt update && sudo apt install -y build-essential ruby-dev。注意,apt update不可省略,因为 Ubuntu 18.04 的源列表可能已过期,apt install会因索引陈旧而找不到包。

2.2 第二层:Ruby 运行时与 Bundler —— 语言环境的容器

Ubuntu 18.04 自带的 Ruby 2.5.1 是一个“系统 Ruby”,它被apt严格管理,任何通过gem install安装的全局 gem 都会写入/var/lib/gems/2.5.0/目录。这带来两个隐患:一是权限问题,普通用户执行gem install jekyll可能因/var/lib/gems/目录权限不足而失败;二是污染风险,不同项目可能依赖不同版本的 Jekyll,全局安装会导致冲突。

因此,强烈建议跳过gem install jekyll,直接使用 Bundler。Bundler 是 Ruby 的依赖管理器,它的核心思想是“每个项目一个独立的 Gemfile”,所有依赖都安装在项目目录下的vendor/bundle子目录中,完全隔离,互不干扰。这不仅是最佳实践,更是 Ubuntu 18.04 上规避权限问题的最稳妥方案。

安装 Bundler 的命令是:

sudo gem install bundler

虽然用了sudo,但 Bundler 本身是个轻量级工具,安装后不会修改系统 Ruby 的行为。之后,你在任何项目目录下执行bundle init,就会生成一个空的Gemfile;再执行bundle add jekyll,Bundler 就会自动解析依赖树,下载并安装 Jekyll 及其所有子依赖(如kramdownliquidwebrick)到本地vendor/bundle中。

注意:bundle add jekyll会自动写入Gemfile并执行bundle install。如果你的网络受限,可以先bundle init,手动编辑Gemfile,加入gem "jekyll", "~> 4.3",再运行bundle install。这样你可以精确控制版本,避免因自动升级引入不兼容变更。

2.3 第三层:Make 与 GCC —— C 扩展编译的引擎

Jekyll 本身是纯 Ruby 代码,但它的许多核心依赖(尤其是nokogiri,用于 HTML 解析;ffi,用于调用系统库)都包含用 C 编写的原生扩展(native extensions)。这些.c文件不能被 Ruby 直接执行,必须先由gcc编译成.so(共享对象)文件,再由 Ruby 动态加载。

这个过程由make驱动。当你执行bundle install时,Bundler 会为每个需要编译的 gem 调用gem build,后者内部会执行makemake的工作就是读取 gem 自带的Makefile,按顺序调用gccarranlib等工具,完成编译、链接、打包。

Ubuntu 18.04 的make版本是 4.1,它不支持Makefile中的$(file >...)语法(该语法在 GNU Make 4.2+ 中引入,用于将文本写入文件)。而某些较新的nokogiri版本(如 1.14+)的Makefile恰好使用了此语法。结果就是make报错Makefile:xxx: *** missing separator. Stop.,但错误信息极其模糊,让人误以为是缩进问题(tab vs space),实则是版本不兼容。

解决方案有两个:

  1. 降级 nokogiri:在Gemfile中指定旧版本,例如gem "nokogiri", "~> 1.13.10"。1.13.x 系列完全兼容 Make 4.1。
  2. 升级 make(不推荐):从源码编译安装 GNU Make 4.3。但这会破坏 APT 的包管理一致性,可能导致其他系统工具(如内核模块编译)出错,属于“为了解决一个问题,制造十个新问题”的典型反模式。

我选择方案一。在Gemfile中锁定nokogiri版本,是成本最低、风险最小的解法。它不改变系统环境,只影响当前项目,且经过大量生产环境验证。

2.4 第四层:Jekyll 服务与 Webrick —— 最终交付的 HTTP 服务器

当所有 gem 安装完毕,执行bundle exec jekyll serve,Jekyll 就会启动一个内置的 Web 服务器,默认使用webrick(Ruby 标准库的一部分)。webrick是一个纯 Ruby 实现的 HTTP 服务器,轻量、无依赖,非常适合开发预览。

但在 Ubuntu 18.04 上,webrick有一个鲜为人知的兼容性细节:它默认绑定到127.0.0.1:4000,但某些企业网络策略或老旧的防火墙规则,会阻止127.0.0.1的 loopback 流量(尽管这极不常见)。更常见的问题是,webrick在处理大量并发请求(比如你同时打开 20 个浏览器标签页刷新页面)时,会因 Ruby 的 GIL(全局解释器锁)而出现轻微卡顿,表现为页面加载延迟 1-2 秒。

这不是 bug,而是webrick的设计使然。它的定位就是“够用就好”,而非生产级服务器。因此,在开发阶段,我们接受它的轻量与简单;但若你追求极致的响应速度,可以无缝切换到pumapuma是一个高性能、多线程的 Ruby Web 服务器,它能充分利用多核 CPU,将页面刷新时间从秒级降到毫秒级。

切换方法只需两步:

  1. Gemfile中添加gem "puma"
  2. 启动时加上--server参数:bundle exec jekyll serve --server puma

puma会自动接管 HTTP 请求,而无需修改任何 Jekyll 配置。这体现了 Bundler + Gemfile 方案的另一个巨大优势:基础设施即代码(Infrastructure as Code)。服务器选型不再是系统配置,而是项目代码的一部分,可版本化、可复现、可协作。

3. 从零开始的完整实操流程:一条命令都不容错过的精准步骤

纸上谈兵不如亲手一试。下面是我每天在新服务器上初始化 Jekyll 开发环境的标准操作流。它经过上百次重复验证,每一步都有明确目的,每一个参数都有其不可替代的理由。请务必逐字输入,不要跳过任何&&-y

3.1 环境初始化:清理、更新、安装系统级依赖

打开终端,执行以下命令。这是一个原子化的单行命令,确保所有前置条件一次性满足:

sudo apt update && sudo apt full-upgrade -y && sudo apt install -y build-essential ruby-dev zlib1g-dev libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 && sudo gem install --no-document bundler

让我们拆解这个长命令的每一部分:

  • sudo apt update:刷新本地软件包索引。Ubuntu 18.04 的源列表可能已过期,不执行此步,后续apt install可能报Unable to locate package
  • sudo apt full-upgrade -y:执行一次完整的系统升级。full-upgradeupgrade更激进,它会智能处理包依赖冲突,移除旧包、安装新包,确保系统内核、glibc 等基础组件处于最新可用状态。-y参数自动确认所有提示,避免交互中断。
  • sudo apt install -y build-essential ruby-dev ...:安装构建链路所需的所有系统库。除了前面强调的build-essentialruby-dev,还额外安装了:
    • zlib1g-devzlib压缩库的开发文件,jekyll-sitemap等插件需要它来生成压缩的sitemap.xml.gz
    • libssl-dev:OpenSSL 开发文件,jekyll-feed插件在生成 RSS 订阅时需要 SSL 加密支持。
    • libreadline-dev:提供命令行历史记录和编辑功能,jekyll console命令依赖它。
    • libyaml-dev:YAML 解析库,Jekyll 的_config.yml文件解析离不开它。
    • libsqlite3-devsqlite3:SQLite 数据库,虽然 Jekyll 本身不用数据库,但jekyll-search等第三方插件会用到。

注意:sudo gem install --no-document bundler中的--no-document是关键。它跳过 RDoc 和 RI 文档的生成,能将 Bundler 安装时间从 30 秒缩短到 3 秒。对于开发环境,文档完全可以在需要时在线查阅,没必要在本地存储。

执行完毕后,验证 Ruby 和 Bundler 是否就位:

ruby -v # 应输出:ruby 2.5.1p57 (2018-03-29 revision 63023) [x86_64-linux-gnu] bundler -v # 应输出:Bundler version 2.3.25 (或类似)

3.2 项目创建:用 Bundler 初始化,而非 jekyll new

很多教程教大家用jekyll new myblog创建项目。这在 Ubuntu 18.04 上是危险的,因为jekyll new会尝试全局安装jekylljekyll-watch,并直接写入/var/lib/gems/,极易触发权限错误。

正确做法是:先创建空目录,再用 Bundler 初始化

mkdir ~/my-jekyll-site && cd ~/my-jekyll-site bundle init

bundle init会在当前目录生成一个最简Gemfile,内容只有一行# frozen_string_literal: true。接着,我们手动编辑Gemfile,加入 Jekyll 及其关键依赖:

echo 'gem "jekyll", "~> 4.3"' >> Gemfile echo 'gem "nokogiri", "~> 1.13.10"' >> Gemfile echo 'gem "webrick", "~> 1.7"' >> Gemfile

这里指定了三个 gem 的精确版本范围:

  • jekyll "~> 4.3"表示安装 4.3.x 系列的最新版(如 4.3.3),但不升级到 4.4.x,避免大版本不兼容。
  • nokogiri "~> 1.13.10"是针对 Make 4.1 的兼容性锁定,1.13.10 是 1.13.x 系列的最后一个稳定版。
  • webrick "~> 1.7"是因为 Ubuntu 18.04 的 Ruby 2.5.1 自带webrick1.4.x,但新版 Jekyll 要求webrick>= 1.6.0,显式声明可避免 Bundler 选择过旧版本。

保存Gemfile后,执行:

bundle install --path vendor/bundle

--path vendor/bundle参数至关重要。它告诉 Bundler:所有 gem 都安装到当前目录下的vendor/bundle子目录中,而不是全局的/var/lib/gems/。这实现了完美的环境隔离。

3.3 静态站点骨架生成:用 bundle exec 替代全局 jekyll

现在,我们有了 Bundler 管理的依赖,但还没有任何网页文件。传统jekyll new会自动生成_posts/_layouts/等目录,我们可以用 Bundler 的方式“借力”完成:

bundle exec jekyll _new . --force

bundle exec是 Bundler 的核心命令,它会启动一个子 shell,在其中将vendor/bundle的 bin 目录加入PATH,确保所有jekyll命令都使用我们刚刚安装的、版本受控的 gem。jekyll _new .是 Jekyll 的内部命令,_new表示调用new子命令,.表示在当前目录创建,--force强制覆盖(因为当前目录已存在Gemfile)。

执行后,你会看到熟悉的 Jekyll 骨架:

. ├── _config.yml ├── _posts/ ├── _site/ ├── about.md ├── index.md └── Gemfile

此时,项目已经具备了运行一切的基础。但别急着serve,我们先做一次彻底的依赖检查:

bundle check

如果输出The Gemfile's dependencies are satisfied,说明一切就绪。如果报错,比如The following gems are missing,那就说明Gemfile中声明的某个 gem 没有成功安装,需要根据错误信息回溯到bundle install步骤排查。

3.4 启动开发服务器:从 webrick 到 puma 的平滑过渡

最后一步,启动服务器:

bundle exec jekyll serve --host 0.0.0.0 --port 4000 --livereload

参数详解:

  • --host 0.0.0.0:让服务器监听所有网络接口,而不仅是127.0.0.1。这样,你可以在同一局域网内的手机或平板上,通过http://<你的UbuntuIP>:4000访问预览,方便多端测试。
  • --port 4000:显式指定端口,避免与其他服务冲突。
  • --livereload:启用热重载。当你修改.md.html文件并保存时,浏览器会自动刷新,无需手动按 F5。

如果你追求更快的响应,只需在Gemfile中添加gem "puma",然后重启服务:

echo 'gem "puma"' >> Gemfile bundle install --path vendor/bundle bundle exec jekyll serve --host 0.0.0.0 --port 4000 --livereload --server puma

你会立刻感受到区别:页面刷新几乎无延迟,即使在编辑一个包含 50 篇文章的_posts/目录时,jekyll serve的重建时间也稳定在 1.2 秒左右,远优于webrick的 2.5 秒。

4. 离线环境救急指南:当没有网络时,如何让 Jekyll 继续工作

在真实的工程场景中,“离线”不是假设,而是常态。可能是你在飞机上修改博客,可能是客户内网完全断网,也可能是公司 IT 策略禁止访问外部 RubyGems 源。Ubuntu 18.04 的build-essentialruby-dev可以通过离线 deb 包安装,但gem的世界是另一套规则。幸运的是,Bundler 为离线部署提供了原生支持,其核心思想是:把所有依赖“打包带走”

4.1 在有网机器上制作离线 gem 包仓库

找一台网络通畅、同样运行 Ubuntu 18.04 的机器(可以是你的开发机),进入你的 Jekyll 项目目录,执行:

bundle package --all --all-platforms

这个命令会做三件事:

  1. 扫描Gemfile.lock,找出项目所需的所有 gem 及其确切版本(包括传递依赖)。
  2. 从 RubyGems.org 下载这些 gem 的.gem文件(二进制包),存放到项目根目录下的vendor/cache/文件夹中。
  3. 修改Gemfile,在顶部添加一行source "https://rubygems.org",并确保bundle package生成的缓存路径被 Bundler 识别。

执行完毕后,vendor/cache/目录下会有几十个.gem文件,例如jekyll-4.3.3.gemnokogiri-1.13.10.gemliquid-4.0.4.gem等。整个vendor/cache/文件夹就是你的离线 gem 仓库。

4.2 将离线仓库迁移到目标机器

将整个项目文件夹(包括vendor/cache/)通过 U 盘、SCP 或任何离线方式,复制到目标 Ubuntu 18.04 机器上。

在目标机器上,进入项目目录,首先确保系统级依赖已安装(build-essentialruby-dev等),然后执行:

bundle config set --local path 'vendor/bundle' bundle config set --local without 'development test' bundle install --local
  • bundle config set --local path 'vendor/bundle':设置 Bundler 本地配置,让所有 gem 安装到vendor/bundle,与在线流程一致。
  • bundle config set --local without 'development test':跳过developmenttest组的 gem(如rspecrubocop),减小体积,加快安装。
  • bundle install --local:这是离线安装的核心命令。它会强制 Bundler 只从vendor/cache/目录读取.gem文件,完全不联网。

bundle install --local的输出会显示Using <gemname> <version>,而不是Installing <gemname> <version>,这表明它确实在使用本地缓存,而非下载。

4.3 离线环境下的常见陷阱与绕过技巧

离线安装并非万无一失,有两个经典陷阱必须提前规避:

陷阱一:nokogiri的预编译二进制包缺失nokogiri为了加速安装,会为常见平台(Linux x86_64)提供预编译的.so文件。但bundle package下载的是源码包(.gem),离线安装时仍需make编译。如果目标机器的makegcc版本与源码包期望的不一致,就会失败。

绕过技巧:在有网机器上,用--platform参数强制下载预编译包

# 在有网机器上,执行 bundle package --all --all-platforms --platform ruby # 然后,手动下载 nokogiri 的预编译包 wget https://github.com/sparklemotion/nokogiri/releases/download/v1.13.10/nokogiri-1.13.10-x86_64-linux.gem -O vendor/cache/nokogiri-1.13.10-x86_64-linux.gem

nokogiri-1.13.10-x86_64-linux.gem是专为 Linux x86_64 编译好的包,它不包含.c源码,只有.so文件,离线安装时直接解压即可,完全跳过make步骤。

陷阱二:ffigem 的系统库依赖ffi(Foreign Function Interface)用于 Ruby 调用 C 函数,它依赖系统的libffi.so库。Ubuntu 18.04 默认安装了libffi6,但ffigem 可能期望libffi7。离线安装时,ffi会报错libffi.so.7: cannot open shared object file

绕过技巧:在有网机器上,用--without参数排除 ffi,改用纯 Ruby 替代

# 在有网机器上,Gemfile 中添加 gem "ffi", require: false # 然后,在项目根目录创建一个空的 lib/ffi.rb 文件 touch lib/ffi.rb # 这样,bundle install 会跳过 ffi,而 Jekyll 的核心功能并不强依赖 ffi

这两个技巧,是我为一家金融客户的内网知识库系统定制 Jekyll 部署方案时总结出来的。他们要求所有生产环境服务器绝对离线,连ping外网都不允许。通过预编译包 + 空桩文件的组合,我们成功让 Jekyll 在零网络连接的 Ubuntu 18.04 上稳定运行了三年,从未因依赖问题宕机。

5. 故障排查全景图:从报错日志到根因定位的完整思维链

在 Ubuntu 18.04 上搭建 Jekyll,最大的挑战不是“怎么做”,而是“为什么失败”。终端里那一长串红色文字,90% 都是干扰项,真正的线索往往藏在第 3 行或第 17 行。下面这张排查全景图,是我过去五年处理上百个同类问题后提炼出的决策树。它不教你背命令,而是训练你像调试器一样思考。

5.1 第一层过滤:区分“系统错误”与“Ruby 错误”

当你看到报错,第一反应不是 Google,而是看错误信息的前缀:

  • 如果以E:W:dpkg:apt:开头,例如E: Unable to locate package build-essential,这是APT 层错误,问题出在系统包管理器。解决方案只有两个:sudo apt update或检查/etc/apt/sources.list是否指向了正确的源(Ubuntu 18.04 应为bionic)。
  • 如果以ERROR:Failed to buildcannot load such file开头,例如ERROR: Failed to build gem native extension,这是Ruby 层错误,问题出在 gem 编译或加载环节。这时才轮到 Bundler、makeruby-dev等概念登场。

提示:一个快速判断法是执行which makewhich gcc。如果它们返回/usr/bin/make/usr/bin/gcc,说明系统工具链正常,错误大概率在 Ruby 层;如果返回空,说明build-essential根本没装,这就是 APT 层问题。

5.2 第二层定位:make错误的三种典型模式

make报错是 Ruby gem 编译失败的最常见表象,但它背后有三种截然不同的根因:

错误模式典型日志片段根因分析解决方案
头文件缺失ruby.h: No such file or directoryruby-dev未安装,gcc找不到 Ruby 的 API 定义sudo apt install ruby-dev
Make 版本不兼容Makefile:123: *** missing separator. Stop.$(file >...)语法错误make4.1 不支持 4.2+ 的新语法,nokogiri等 gem 的Makefile使用了新特性Gemfile中锁定nokogiri "~> 1.13.10"
链接库找不到cannot find -lsslcannot find -lzlibssl-devzlib1g-dev未安装,gcc链接时找不到动态库sudo apt install libssl-dev zlib1g-dev

记住这个表格。下次bundle install卡住,直接 Ctrl+C 中断,然后看最后一屏的报错,90% 的情况都能对号入座。

5.3 第三层深挖:用strace捕捉系统调用真相

当错误日志语焉不详,比如make: *** [Makefile:45: all] Error 2,没有任何具体线索时,就需要祭出终极武器:strace

strace是 Linux 的系统调用追踪器,它能告诉你一个程序在执行时,到底打开了哪些文件、调用了哪些系统函数、收到了什么错误码。

nokogiri编译失败为例,先进入它的临时构建目录(通常在/tmp/下,名字类似nokogiri-20231015-12345-abcde),然后执行:

strace -e trace=openat,open,write,close make 2>&1 | grep -E "(ruby.h|ssl|zlib)"

这条命令的意思是:追踪make进程的openatopenwriteclose四个系统调用,将所有输出重定向到标准错误(2>&1),再用grep过滤出包含ruby.hsslzlib的行。

如果输出中出现:

openat(AT_FDCWD, "/usr/include/ruby-2.5.0/ruby.h", O_RDONLY) = -1 ENOENT (No such file or directory)

那么ruby-dev缺失就是铁证。

如果输出是:

openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libssl.so", O_RDONLY) = -1 ENOENT (No such file or directory)

那就是libssl-dev的问题。

strace的强大之处在于,它不依赖错误日志的“人话描述”,而是直接呈现操作系统层面的“事实”。它是我处理那些“玄学报错”的最后防线,也是我向客户证明“问题不在我们的代码,而在系统配置”的最有力证据。

5.4 第四层验证:用bundle show确认依赖路径

bundle install显示成功,但bundle exec jekyll serve仍报cannot load such file -- jekyll时,问题往往出在 Bundler 的路径解析上。

执行:

bundle show jekyll

它会输出类似/home/user/my-jekyll-site/vendor/bundle/ruby/2.5.0/gems/jekyll-4.3.3的路径。如果这个路径不存在,或者路径中的2.5.0与你的 Ruby 版本不符(比如你升级了 Ruby,但vendor/bundle还是旧的),那就说明 Bundler 没有正确安装 gem。

此时,最干净的解决方案是:

rm -rf vendor/bundle bundle install --path vendor/bundle

删除整个vendor/bundle目录,相当于给 Bundler 一个“全新开始”的机会。这比试图修复损坏的缓存要快得多,也更可靠。

6. 长期维护心得:让 Ubuntu 18.04 的 Jekyll 环境活过五年

Ubuntu 18.04 的官方支持已于 2023 年 4 月结束,但它的 ESM(Extended Security Maintenance)服务将持续到 2028 年。这意味着,只要支付订阅费用,你依然能获得关键安全补丁。对于一个静态博客生成器来说,这已经足够。但“足够”不等于“无忧”。在长达五年的维护周期里,我总结了三条血泪经验,它们无关技术细节,而是关于如何与一个“半退休”的系统和平共处。

6.1 经验一:永远不要sudo gem update --system

gem update --system会升级 RubyGems 本身。Ubuntu 18.04 的 Ruby 2.5.1 与最新的 RubyGems(如 3.4+)存在兼容性问题,升级后gem install可能直接崩溃,报错undefined method 'spec' for nil:NilClass。这不是 bug,而是 RubyGems 的 API 在大版本迭代中发生了不兼容变更。

我的做法是:将 RubyGems 版本锁定在 3.0.3.1,这是最后一个完美兼容 Ruby 2.5.x 的稳定版

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 0:43:53

Linux环境变量与shell变量实战指南:PATH、export与故障排查

1. 项目概述&#xff1a;为什么搞懂环境变量和 shell 变量是 Linux 生存的第一课在 Linux 系统里&#xff0c;你敲下ls能列出文件&#xff0c;输入python3就能启动解释器&#xff0c;执行git commit就能提交代码——这些看似理所当然的操作&#xff0c;背后全靠一组看不见、摸不…

作者头像 李华
网站建设 2026/6/22 0:42:55

NSK超大导程精密滚珠丝杠UPFC1540技术解析

型号 UPFC 1540-2 属于 sources 中 NSK 的标准端盖式&#xff08;大导程高速&#xff09;滚珠丝杠系列。 | 编码 | 属性 | 数据 | 内容 | |------|------|--------|------| | A | 联 | 133 | 许 | | B | 系 | 2798 | 经 | | C | 我 | 2959 | 理 …

作者头像 李华
网站建设 2026/6/22 0:37:19

第10章:上下文与会话记忆——多轮对话如何不跑偏

1. 项目背景 业务场景 某公司的AI产品经理小美正在验收内部开发的"AI面试助手"。这个助手的场景是:HR输入候选人简历,助手进行模拟面试,一轮接一轮地问问题,并在面试结束后生成综合评估。 验收时发现一个严重问题——面试到第5轮时,助手突然问:"请问你…

作者头像 李华
网站建设 2026/6/22 0:35:06

Linux rwlock读写锁arch_read_lock与ticket锁对比

Linux rwlock读写锁arch_read_lock与ticket锁对比Linux内核中的rwlock提供了读写分离的同步语义:读者之间不互斥,写者独占.其实现同样经过多层抽象,rwlock_t -> arch_rwlock_t.底层最关键的对比在于:传统rwlock使用ticket锁来保证公平性,而arch_read_lock/core读者实现则面临…

作者头像 李华
网站建设 2026/6/22 0:32:59

AI视频时序取证:Flow of Truth框架解析与实战

1. 项目概述&#xff1a;当AI学会“剪辑”时间&#xff0c;我们如何鉴别真伪&#xff1f;最近圈子里聊得最多的&#xff0c;除了各种大模型的迭代&#xff0c;就是AIGC&#xff08;人工智能生成内容&#xff09;在视频领域的狂飙突进了。从静态的“文生图”到动态的“图生视频”…

作者头像 李华