如何让Ruby忽略用户安装的gem?企业部署方案解析
启动Ruby时,我发现它会扫描用户主目录查找用户安装的gem:
strace -e file ruby30 -e nil |& fgrep $HOME
扫描结果很长,你可以自行查看,对我而言大约有300次系统调用。
我在企业内部部署Ruby脚本时,希望这些脚本不要访问用户主目录。脚本所需的所有gem均已通过RPM包与解释器一同安装。
问题背景
我需要阻止Ruby访问用户主目录,主要有以下几点原因:
确保行为可复现/正确性
用户可能安装了某个gem的新版本,而新版本的行为可能与我们部署的版本不同。因此我希望脚本仅使用通过RPM安装的gem,这对需要确保可复现性的构建场景至关重要。
维护便利性
部分用户使用旧版本Ruby安装了gem,而我们部署的脚本使用较新的解释器。每当这类用户启动脚本时,每次都会收到如下警告:
Ignoring mysql2-0.4.8 because its extensions are not built. Try: gem pristine mysql2 --version 0.4.8
性能优化
用户主目录挂载在NFS网络共享上,通过NFS扫描主目录会大幅拖慢Ruby的启动速度。
已尝试的方案
--disable=gems参数
- 优点:
ruby --disable=gems可阻止Ruby访问用户gem(经strace验证:完全不访问用户主目录)。 - 优点:在我的环境中,Ruby启动速度提升约10倍。
- 优点:只需修改shebang行:
#!/usr/bin/ruby30 --disable=gems - 缺点:无法加载部分gem(如
nokogiri),推测与gem是否为内置有关。 - 缺点:Ruby >=3.1版本中
--disable=gems已被弃用。
设置$GEM_PATH与$GEM_SPEC_CACHE环境变量
- 优点:
env GEM_PATH= GEM_SPEC_CACHE= ruby可阻止Ruby访问用户gem(经strace验证:完全不访问用户主目录)。 - 优点:启动速度慢于
ruby --disable=gems,但仍远快于默认行为。 - 优点:Ruby仍可加载所有通过RPM安装的gem。
- 待确认:需创建并部署包装脚本:
所有部署的脚本都需使用该包装器。#!/bin/sh GEM_PATH= GEM_SPEC_CACHE= exec /usr/bin/ruby30 "$@"
设置Gem.paths
在脚本开头插入以下代码片段:
#!/usr/bin/ruby Gem.paths = { "GEM_HOME" => Gem.paths.home, "GEM_PATH" => Gem.path.reject { |path| path.start_with? "/home/" }.join(":"), "SPEC_CACHE_DIR" => "" } [...]
- 优点:可阻止Ruby加载用户gem。
- 缺点:Ruby会在脚本启动前访问并检查所有用户gem。
- 缺点:代码不够简洁美观。
与Python对比
是否有更优的解决方案?类似Python的python -s参数那样的方式?
内容的提问来源于stack exchange,提问作者hagello
相关产品推荐
相关产品推荐

