分享 为什么不应该使用 rvm/rbenv,以及替代方案

Rei · 2018年10月27日 · 最后由 ericguo 回复于 2018年11月01日 · 8300 次阅读

最近关于 rvm/rbenv 的问题多了起来,所以我想分享我的观点,不要用 rvm 和 rbenv。

rvm/rbenv 有什么问题?

rvm/rbenv 都是多版本 Ruby 管理工具,但是工作原理不同:

  • rvm:在执行 rvm use 命令时修改 PATH,将所需版本的路径加入到 PATH 当中。
  • rbenv:开发者自行将 rbenv 的固定 bin 目录加入 PATH。rbenv bin 目录存放一些包装脚本(Shims),在执行的时候查找对应版本的可执行文件。

这两个工具本质都是 PATH 上做手脚,一个在执行前,一个在执行中。那么问题来了,使用这两个工具意味着使用者要清楚了解自己目前的 PATH 是怎么设置的,不然就会各种提示找不到路径。而且更麻烦的是:

  • bash shell 分为 login/non-login,interactive/non-interactive 几种可以组合的状态,不同状态读取的配置文件优先级不同。我相信很多人都搞不清楚完整的优先级顺序
  • cron 的执行环境和 bash 不同,PATH 要单独设定。
  • 一些部署工具允许单独指定自己的 Ruby PATH,例如 capistrano,passenger。

这么多可配置的地方叠加起来,很容易让人搞不清楚当前执行的 Ruby 是什么版本——用 ssh 登录上去执行的 Ruby,可能跟你用 cap deploy 执行的 Ruby 不一样,又跟 app server 使用的不一样。

那么解决方案是?

对于新手,如果你不需要维护特定版本的 Ruby 项目,那么只需要装一个比较新的 Ruby 版本就行了,不需要管理多版本。这在不同系统下有不同选择。

Linux

使用系统源里的版本,例如 Ubuntu 18.04 源里自带了 2.5 版本:

apt-get install ruby ruby-dev

我在生产环境也只安装源里的 ruby,一个服务器只有一个唯一的 Ruby 路径,杜绝不同环境的 PATH 不一致,并且可以利用源的安全更新。

MacOS

先安装 Homebrew https://brew.sh/ ,再用 brew 安装:

brew install ruby

Windows

我没有 Windows 上开发的经验,知道有一个 RubyInstaller https://rubyinstaller.org/

另外 Ubuntu on Windows 也可以一试 https://tutorials.ubuntu.com/tutorial/tutorial-ubuntu-on-windows

如果需要管理多版本

如果你有几个遗留项目只能跑在特定版本的 Ruby,那么使用 Docker https://docs.docker.com/compose/rails/

Docker 不只是把 Ruby,还把其它所有依赖都固化下来,例如 MySQL,Redis 等等。

我在开发环境也用了很久 Docker,因为它可以把项目依赖封装起来,不污染本地环境。至于要不要在生产环境用 Docker,我觉得目前对于小项目还比较折腾(https://chloerei.com/2018/07/08/container-may-be-the-future-but-not-the-present/),希望有一天可以用上。

总结

用一个工具前要想想为什么,需不需要,工具带来的好处是否超过带来的坏处。Keep it simple。

Rei macOS 安装 Ruby 报错 提及了此话题。 10月27日 15:26

用 Docker 固化项目环境是不错的替代方案。

各种方案使用下来,目前来说,chruby + ruby-install 应该算是比较好的选择。

确实初学者不需要搞得这么复杂, 之前就是用 homebrew 安装的,看来还是换回去比较好

以前发过的一个技巧在这里汇总一下。

如果使用 docker,可以在 bash 配置里面加一个方法:

sandbox() {
  docker run -it -v $(pwd):/app -w /app $1 bash
}

然后可以方便的打开不同版本的 ruby 沙箱环境:

$ sandbox ruby:2.3

临时测试版本差异的时候会用到。

Docker 是不错的方法。 Docker 化是一个长期的过程,一些小项目比如内部管理/统计/配置系统,再就是自动化测试。 Docker 整个体系还不是非常成熟,用过就知道有很多坑。 开发和生产都 Docker 化,现在是很理想的情况。 现实来说整个团队需要有一个学习的过程。

tuliang 回复

有啥坑,mac 下我用 docker 开发环境就是有点 volumns 比较慢

lidashuang 回复

volumn 慢用 http://docker-sync.io/ 解决。

解决了 volumn 慢的问题,开发环境我觉得没啥坑了。生产环境比较折腾,除非用 Heroku。

Rei 回复

还没折腾这个 现在用 cached 这个

volumes:
      - ./mall:/app:cached

主要是有时候 mysql count 的时候超级慢

Rei 回复

docker 方案是我现在用过的最舒服的开发方式了

lidashuang 回复

cached 是主机到容器单向同步,会不会导致每次重启 node_modules 都要重装?而且据文档介绍同步有延迟。

rbenv 改个 path 就好。docker 太慢了。

部署机的路径问题基本都是 capistrano 这种工具的问题。

@Rei 开发环境的数据库 redis 之类的你也是直接丢在同一个容器里的么

IChou 回复

用 docker compose。

我说呢 @chunlea 最近也开始用 docker 做开发环境了

lidashuang 回复

本地开发还好 上生产感觉就很繁琐了

我最近写了一个项目练手 https://github.com/tuliang/in-the-eyes

用 Capistrano 部署,Docker Compose 管理,现在跑着 Rails,PostgreSQL 和 Nginx,准备加 Memcached 和 Redis

流程大概是下图

现在整个服务还是很简单的,我想如果后期加入各种系统,比如后台管理,数据统计,环境配置,自动化测试/压测之类的就挺复杂了。

现实是在公司内部,各种系统是不同团队维护的。如果要整体 docker 化,现在 docker 以及配套的系统并不能即装即用,那么他们都需要去学习,这个学习成本还是比较高的,并且遇到了坑一般的团队可能不太好解决。

@Rei 提到的,如果有一天能达到 Heroku 这种程度那就好了。

tuliang 回复

docker compose 只适合用来搭开发环境,生产环境需要 k8s 之类的编排工具,然后对于需要持久化的服务要有特殊处理,最好用云服务。

k8s 太复杂了 我现在还是晕的

我觉得持有化服务 现在分布式架构都比较成熟了 不一定要上 docker 除非是混部之类的

tuliang 回复

上面的意思是持久化服务不要放 k8s 里面,因为要做得对很难。

docker 隐藏了太多细节,capistrano 也是。

登录/交互 shell 确实头疼

我总是觉得对环境细节不够了解,迟早是个风险(

我还是喜欢用脚本探测环境。。生成 systemd 单元,固定启动路径

https://ruby-china.org/topics/37470

我不会用 docker。因为开发机,我要足够了解环境。生产机,我们没有那么大的规模。

每次 macOS 一发布新版本,这类问题就来了

tuliang 回复

服务器上的那几步可以预先打好 Docker Image,AWS 之类的云平台他们有私有的 Docker Registry 服务

赞最后一句,用了容器以后下一步就可以思考到底要不要 Rails 三个模式了 https://ruby-china.org/topics/32538

huacnlee 回复

国内好像还没有比较靠谱的私有 Docker Registry 服务

tuliang 回复

阿里云啊

wiki 如何安装 ruby 的介绍里写的是使用 rvm...

nouse 回复

我把大部分配置都放到了环境变量里,不过还有有一些行为是硬编码不可配置的,例如 rails db 下的命令,开发环境执行的同时会对 test db 也执行一遍,所以我做了折衷,Rails 默认行为不去改它,新增的配置遵循 12factor。只改配置,不区分 staging 和 production 环境。

a-wing 回复

我开发环境用 docker 的目的是为了让开发环境和生产环境等价。曾经试过开发环境用了新版本的 redis 和新命令,上线之后才发现不支持而崩溃。

其实 macOS 新版本升级后出问题最多还是那个 oh-my-zsh 吧?所以它这个命名似乎也说明了一切(oh!my zsh)。所以这个东西虽然有很多亮点,我还是不装的。

一直感觉 rvm 还好,不能因噎废食啊。看重 rvm 的优点就是能帮你把系统缺少的库自动装上,管理 Ruby 多版本的同时,也能管理多个 gem set。不能说扔就扔。

rbenv 尝试过几次,感觉不若 rvm,弃之久矣。至于 Docker 得看入坑深浅了。

生产环境目前我用的是CentOS+rbenv,感觉 CentOS 比 Ubuntu 更少折腾点,用 rbenv 的话,只装一个 ruby 其实也不可能搞错版本。

ericguo 回复

ubuntu 少折腾吧

Rails 项目中 ruby 版本和 gemset 大多可以自动切换,是不是痛点得据情况而定,一般在项目切换不频繁时,ruby 版本不会是痛点。

Docker 的本质是对运行环境进行隔离,主要价值在于可保证各个运行环境的一致性。对于环境不一致导致的问题,其实也可以考虑打造一个 和生产环境高度一致的测试环境,在测试环境中暴露问题。一般由于环境不一致导致的问题,其实不会太频繁。如果导致方案整体不可用的情况, 则可能方案的选择上欠考虑或者开发环境和生产环境差别过大。这其实也可以解决环境不一致带来的潜在问题,当然得根据情况而定。

在开发环境中用 docker,除了环境一致性的优点外,其他的差不多可以视为缺点,带来反向价值。特别是对于像我等菜鸟,在开发时,经常拼错单词, 或者变量名取的不好要改,或者其他纰漏。这些小错在代码自动 reload 时,可以高效的解决。docker 嘛,毕竟多了一层,差不多活生生把动态语言开发搞成了静态语言开发,改一下还要构建一下。是否值得选择,也得看自身情况。

至于用了 docker 是否区分 Rails 的三个模式,我实在想不出两者有什么关联。不用 docker 也不妨碍只用一种模式吧,只要你想。用了 docker,有多个模式也不会有问题。关联何在?

以上是我的个人观点。

early 回复

开发环境用 docker 也可以自动 reload, 不用重新构建,绑定下 volumes

docker-compose.yml

version: '2'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    restart: always
    volumes:
      - .:/app
early 回复

开发环境的 docker 只打包系统依赖,例如 libpg-dev,nodejs 这些,项目文件是通过 volumes mount 进去的。Rails 的 auto reload 和 webpacker 的 live reload 都能正常用。

@ThxFly @Rei 感谢,我的说法有点问题。

lidashuang 回复

CentOS 装好了以后折腾少,但安装可能比 Ubuntu 稍微烦一点。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号