#292 Virtual Machines with Vagrant
- Download:
- source codeProject Files in Zip (41.1 KB)
- mp4Full Size H.264 Video (27.3 MB)
- m4vSmaller H.264 Video (13.6 MB)
- webmFull Size VP8 Video (13.2 MB)
- ogvFull Size Theora Video (30.4 MB)
作为一个 Rails 开发人员,你经常会发现开发环境和生产环境是有很大差别的。事实上,我们经常会在不同于生产环境的操作系统上进行应用程序的开发。Rails 应用程序有很大一部分的依赖库在不同的系统上是无法通用的。
Vagrant 可以帮助我们。它可以创建并管理一些虚拟环境,这样我们就可以在现有的操作系统上建立一个简易的 Linux,在虚拟机中运行 Rails 应用程序和应用程序的依赖库。同时我们还可以生成一个可移植的打包文件,以便和其他人员共享这个环境。
每个 Rails 开发人员都应该试用一下 Vagrant,至少它可以帮助你更好的理解怎样为 Rails 应用程序搭建生产环境。Vagrant 同样有助于用来搭建一个临时的环境,甚至在应用程序的开发阶段也是很有用的。
安装 Virtual Box 和 Vagrant
Vagrant 使用 Oracle 公司的 VirtualBox。如果你的电脑上没有 VirtualBox,那么你需要先安装它。VirtualBox 可以在 Windows、OS X 和 Linux 上使用,所以不管你用的是什么操作系统,都能找到相应的版本。安装 VirtualBox 之后就可以安装 Vagrant 了,它是个 Ruby gem,运行以下命令进行安装。
$ gem install vagrant
添加一个虚拟盒
接着需要添加一个虚拟机,Vagrant 将其称为虚拟盒。在 Vagrantbox.es 网站上有很长的一个列表,列出了可以获取到的虚拟盒,我们可以从中下载一个。我们选择使用 ubuntu lucid 32,它由 Vagrant 站点提供,基于 Ubuntu Linux 10.04。运行 vagrant box add
命令来安装虚拟盒,传给其两个参数:名称和虚拟盒的地址。
$ vagrant box add lucid32 http://files.vagrantup.com/lucid32.box [vagrant] Downloading with Vagrant::Downloaders::HTTP... [vagrant] Downloading box: http://files.vagrantup.com/lucid32.box [vagrant] Extracting box... [vagrant] Verifying box... [vagrant] Cleaning up downloaded box...
虚拟盒可能会相当地大(几百MB),所以可能会花些时间才能下载完。如果你曾经下载过一个虚拟盒,那么你可以将上面命令中的地址改为本地文件路径从本地安装。
虚拟盒下载完,然后进行安装之后,我们就可以创建虚拟环境了。Vagrant 一般情况下是为一个特定的应用程序创建虚拟盒的,现在还没有应用程序呢,所以我们需要先新建一个 Rails 应用程序,然后进入应用程序的目录。
$ rails new todo $ cd todo
我们可以为该应用程序及其依赖库创建一个虚拟机,执行 vagrant init
命令,并传递一个参数:要使用的虚拟盒。
$ vagrant init lucid32 create Vagrantfile
这个命令会新建一个 Vagrantfile
文件,其内容是该应用程序的 Vagrant 虚拟盒的设置信息。默认情况下,除了指定使用哪个虚拟盒之外,其他的设置都被注释掉了。
Vagrant::Config.run do |config| # All Vagrant configuration is done here. The most common configuration # options are documented and commented below. For a complete reference, # please see the online documentation at vagrantup.com. # Every Vagrant virtual environment requires a box to build off of. config.vm.box = "lucid32" # Rest of file omitted. end
有很多设置选项可以设定,值得我们花一点时间去了解一下。
启动 Vagrant 虚拟机
激动人心的时刻到来了,执行 vagrant up
命令将会启动我们的虚拟机。启动的过程可能会花费几分钟,启动之后我们就可以通过 SSH 连接到它了。
$ vagrant ssh
现在我们就已经进入了 Ubuntu 虚拟机。启动的过程没什么高深的东西,不过还是有些事情值得注意。其中一点就是,我们已经有一个叫做 vagrant
的用户;而且他有 sudo
权限而无需输入密码,这对本地开发很有帮助。Vagrant 还建立了一个共享文件夹 /vagrant
指向该 Rails 应用程序所在的目录。
安装应用程序的依赖库
我们的目的是让 Rails 应用程序在虚拟机上成功运行,所以我们需要安装一些应用程序的依赖库。首先,确保 Ruby 和 SQLite 已经安装了。我们的虚拟机上已经安装了 Ruby 1.8.7,那么我们就可以运行 Chef 秘籍了。
Chef 为我们提供了自动搭建服务器的方法。我们可以执行想用的 Chef 秘籍来安装 Ruby 1.9 和 SQLite。Chef 和 Vagrant 集成的很好,它本身也是值得进行深入探讨的,我们会在将来的某集中进行详细讨论。现在我们不使用 Chef 来搭建服务器,以便更好的理清各部分是如何运作的。
首先,执行 apt-get
命令,确保已经安装了的软件是最新版本的。
$ sudo apt-get update
然后安装一些所需的软件:build-essential 套件,ZLIB,Git 和 SQLite3.
$ sudo apt-get install build-essential zlib1g-dev git-core sqlite3 libsqlite3-dev
现在,还剩下 Ruby 1.9 没有安装,我们可以从源码编译安装,也可以使用 RVM,但是我们来用个比较新的东西,rbenv。使用 Vagrant 意味着我们可以在一个安全的环境中尝试一些以前没有使用过的工具,如果搞砸了,删除这个虚拟机然后重新来过就可以了。
运行 rbenv 说明文件 的 2.1 中给出的命令来安装 rbenv,首先将远端仓库克隆到本地的 ~/.rbenv
目录中。
$ cd $ git clone git://github.com/sstephenson/rbenv.git .rbenv
然后,将 rbenv 的 bin
目录加入 PATH,再将初始化命令加入 ~/.bash_profile
。
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
然后重新加载 .bash_profile
使更改生效。
$ source .bash_profile
现在有了 rbenv
命令可以用来管理 Ruby 版本了,但是还没有安装 Ruby 的方法。我们将使用 Ruby Build 来安装 Ruby。我们只需要将其 Git 仓库克隆到本地,然后进入其目录执行安装脚本即可。
$ git clone https://github.com/sstephenson/ruby-build.git $ cd ruby-build $ sudo ./install.sh
现在可以使用 rbenv 安装当前最新的 Ruby 1.9.2 了。
$ rbenv install 1.9.2-p290
这一过程可能会花费一点时间,当其执行完毕后会成功的编译好 Ruby 1.9.2。像这次这样,如果改变了可执行文件,就需要执行 rbenv rehase
命令。执行下面的命令将 Ruby 1.9.2 设为虚拟机上使用的 Ruby 的默认版本。
$ rbenv global 1.9.2-p290
当我们检查 Ruby 的版本时,已经变成了 1.9.2,这正是我们需要的。
$ ruby -v ruby 1.9.2p290 (2011-07-09 revision 32553) [i686-linux]
安装 Rails 应用程序
现在适当的 Ruby 版本已经安装好了,我们接着往下走,让 Rails 可以正常运行。如果我们进入 Rails 应用程序所在的共享目录,然后运行 bundle 安装它的依赖库,便会遇到一个问题。
vagrant@lucid32:~$ cd /vagrant/ vagrant@lucid32:/vagrant$ bundle bundle: command not found
安装 Bundler gem 来修正这个问题。
$ gem install bundler
Bundler 安装完毕后,需要执行 rbenv rehase
命令来启用 bundle
命令。现在可以执行 bundle
命令来安装应用程序所需的 gem 了。
如果现在试着启动应用程序的服务器的话,会发生一个错误,提示没有找到 Javascript 运行时。这个错误是正常的,因为 Rails 3.1 需要 Javascript 运行时,而 Ubuntu 默认是没有安装的。一个快速解决方法是在应用程序的 Gemfile 中加入一个叫做 therubyracer 的 gem,然后在虚拟机中执行 bundle 命令。这个 gem 会安装 V8 Javascript 引擎。
gem 'therubyracer'
译者注:在 Rails Style Guide 中提到 therubyracer 很耗内存。建议把 therubyracer 换成 nodejs。执行 sudo apt-get install nodejs
即可。
therubyracer 安装完之后,再来试着启动服务器。
$ bundle exec rails s
这一次启动成功了,我们的应用程序在 3000 端口顺利的运行着。但是我们不能在浏览器中查看,因为它运行在虚拟机中。我们需要告诉 Vagrant 将端口转到本地电脑上,通过修改应用程序的 Vagrantfile
来实现。我们需要将下面这一行解除注释,将其名称改为一个更加合适的,然后告诉 Vagrant 将虚拟机上的 3000 端口转到本地电脑上的相同端口。
config.vm.forward_port "rails", 3000, 3000
由于修改了配置文件,我们需要在虚拟机中执行 exit
退出 Vagrant shell,然后执行 vagrant reload
来重新加载虚拟机。这样就关闭了虚拟机然后重新加载了。加载完毕后,通过 SSH 连接到它。
$ vagrant ssh
然后在虚拟机上进入应用程序所在目录,然后启动服务器。
vagrant@lucid32:~$ cd /vagrant vagrant@lucid32:/vagrant$ bundle exec rails s
再次启动了服务器,但这次我们可以从本地电脑中查看应用程序了。
因为我们的 Rails 应用程序是和 Vagrant 共享的,所以一旦我们对应用程序做出修改,都会映射到虚拟机上。如果我们修改了默认的首页文件 /public/index.html
,然后重新加载该页面,改动就会立即显示出来。
<h2>You’re riding Ruby on Rails on Vagrant!</h2>
其他 Vagrant 命令
Vagrant 提供了很多命令可以帮助我们管理虚拟机。我们可以执行 vagrant status
来获取虚拟机的当前状态。
$ vagrant status Current VM states: default running The VM is running. To stop this VM, you can run `vagrant halt` to shut it down forcefully, or you can run `vagrant suspend` to simply suspend the virtual machine. In either case, to restart it again, simply run `vagrant up`.
通过运行 suspend
和 resume
可以临时停止虚拟机和重启虚拟机。
$ vagrant suspend [default] Saving VM state and suspending execution... $ vagrant resume [default] Resuming suspended VM... [default] Booting VM... [default] Waiting for VM to boot. This can take a few minutes. [default] VM booted and ready for use!
如果需要完全关闭虚拟机可以执行 vagrant halt
。然后需要执行 vagrant up
才能使虚拟机再次启动。
最有用的一个命令之一是 vagrant package
。这个命令会将虚拟机的当前状态打包到 package.box
,然后就可以分享并分发这个打包文件,还可以使用它快速重新搭建虚拟机。如果执行 vagrant destroy
删除了虚拟机,我们可以使用在本文前面使用的 vagrant box add
命令来重新搭建。
以上就是本集的内容。Vagrant 是个很好的解决方案,可以将 Rails 应用程序的依赖库独立到一个虚拟的环境中,然后打包分发给其他的开发人员,用来模拟生产环境或是用做一个临时服务器。结合 Chef 使用后 Vagrant 会变得更加有用,二者的结合会在未来的某集中介绍。