最近关于 rvm/rbenv 的问题多了起来,所以我想分享我的观点,不要用 rvm 和 rbenv。
rvm/rbenv 都是多版本 Ruby 管理工具,但是工作原理不同:
这两个工具本质都是 PATH 上做手脚,一个在执行前,一个在执行中。那么问题来了,使用这两个工具意味着使用者要清楚了解自己目前的 PATH 是怎么设置的,不然就会各种提示找不到路径。而且更麻烦的是:
这么多可配置的地方叠加起来,很容易让人搞不清楚当前执行的 Ruby 是什么版本——用 ssh 登录上去执行的 Ruby,可能跟你用 cap deploy 执行的 Ruby 不一样,又跟 app server 使用的不一样。
对于新手,如果你不需要维护特定版本的 Ruby 项目,那么只需要装一个比较新的 Ruby 版本就行了,不需要管理多版本。这在不同系统下有不同选择。
使用系统源里的版本,例如 Ubuntu 18.04 源里自带了 2.5 版本:
apt-get install ruby ruby-dev
我在生产环境也只安装源里的 ruby,一个服务器只有一个唯一的 Ruby 路径,杜绝不同环境的 PATH 不一致,并且可以利用源的安全更新。
先安装 Homebrew https://brew.sh/ ,再用 brew 安装:
brew install ruby
我没有 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。
以前发过的一个技巧在这里汇总一下。
如果使用 docker,可以在 bash 配置里面加一个方法:
sandbox() {
docker run -it -v $(pwd):/app -w /app $1 bash
}
然后可以方便的打开不同版本的 ruby 沙箱环境:
$ sandbox ruby:2.3
临时测试版本差异的时候会用到。
Docker 是不错的方法。 Docker 化是一个长期的过程,一些小项目比如内部管理/统计/配置系统,再就是自动化测试。 Docker 整个体系还不是非常成熟,用过就知道有很多坑。 开发和生产都 Docker 化,现在是很理想的情况。 现实来说整个团队需要有一个学习的过程。
volumn 慢用 http://docker-sync.io/ 解决。
解决了 volumn 慢的问题,开发环境我觉得没啥坑了。生产环境比较折腾,除非用 Heroku。
还没折腾这个 现在用 cached 这个
volumes:
- ./mall:/app:cached
主要是有时候 mysql count 的时候超级慢
本地开发还好 上生产感觉就很繁琐了
我最近写了一个项目练手 https://github.com/tuliang/in-the-eyes
用 Capistrano 部署,Docker Compose 管理,现在跑着 Rails,PostgreSQL 和 Nginx,准备加 Memcached 和 Redis
流程大概是下图
现在整个服务还是很简单的,我想如果后期加入各种系统,比如后台管理,数据统计,环境配置,自动化测试/压测之类的就挺复杂了。
现实是在公司内部,各种系统是不同团队维护的。如果要整体 docker 化,现在 docker 以及配套的系统并不能即装即用,那么他们都需要去学习,这个学习成本还是比较高的,并且遇到了坑一般的团队可能不太好解决。
像 @Rei 提到的,如果有一天能达到 Heroku 这种程度那就好了。
docker compose 只适合用来搭开发环境,生产环境需要 k8s 之类的编排工具,然后对于需要持久化的服务要有特殊处理,最好用云服务。
docker 隐藏了太多细节,capistrano 也是。
登录/交互 shell 确实头疼
我总是觉得对环境细节不够了解,迟早是个风险(
我还是喜欢用脚本探测环境。。生成 systemd 单元,固定启动路径
https://ruby-china.org/topics/37470
我不会用 docker。因为开发机,我要足够了解环境。生产机,我们没有那么大的规模。
服务器上的那几步可以预先打好 Docker Image,AWS 之类的云平台他们有私有的 Docker Registry 服务
我把大部分配置都放到了环境变量里,不过还有有一些行为是硬编码不可配置的,例如 rails db 下的命令,开发环境执行的同时会对 test db 也执行一遍,所以我做了折衷,Rails 默认行为不去改它,新增的配置遵循 12factor。只改配置,不区分 staging 和 production 环境。
我开发环境用 docker 的目的是为了让开发环境和生产环境等价。曾经试过开发环境用了新版本的 redis 和新命令,上线之后才发现不支持而崩溃。
其实 macOS 新版本升级后出问题最多还是那个 oh-my-zsh 吧?所以它这个命名似乎也说明了一切(oh!my zsh)。所以这个东西虽然有很多亮点,我还是不装的。
一直感觉 rvm 还好,不能因噎废食啊。看重 rvm 的优点就是能帮你把系统缺少的库自动装上,管理 Ruby 多版本的同时,也能管理多个 gem set。不能说扔就扔。
rbenv 尝试过几次,感觉不若 rvm,弃之久矣。至于 Docker 得看入坑深浅了。
生产环境目前我用的是CentOS+rbenv,感觉 CentOS 比 Ubuntu 更少折腾点,用 rbenv 的话,只装一个 ruby 其实也不可能搞错版本。
Rails 项目中 ruby 版本和 gemset 大多可以自动切换,是不是痛点得据情况而定,一般在项目切换不频繁时,ruby 版本不会是痛点。
Docker 的本质是对运行环境进行隔离,主要价值在于可保证各个运行环境的一致性。对于环境不一致导致的问题,其实也可以考虑打造一个 和生产环境高度一致的测试环境,在测试环境中暴露问题。一般由于环境不一致导致的问题,其实不会太频繁。如果导致方案整体不可用的情况, 则可能方案的选择上欠考虑或者开发环境和生产环境差别过大。这其实也可以解决环境不一致带来的潜在问题,当然得根据情况而定。
在开发环境中用 docker,除了环境一致性的优点外,其他的差不多可以视为缺点,带来反向价值。特别是对于像我等菜鸟,在开发时,经常拼错单词,
或者变量名取的不好要改,或者其他纰漏。这些小错在代码自动 reload 时,可以高效的解决。docker 嘛,毕竟多了一层,差不多活生生把动态语言开发搞成了静态语言开发,改一下还要构建一下。是否值得选择,也得看自身情况。
至于用了 docker 是否区分 Rails 的三个模式,我实在想不出两者有什么关联。不用 docker 也不妨碍只用一种模式吧,只要你想。用了 docker,有多个模式也不会有问题。关联何在?
以上是我的个人观点。
开发环境用 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
开发环境的 docker 只打包系统依赖,例如 libpg-dev,nodejs 这些,项目文件是通过 volumes mount 进去的。Rails 的 auto reload 和 webpacker 的 live reload 都能正常用。