使用 Vagrant 打造本地开发环境

1. vagrant介绍

1.1 vagrant能做什么

做Web开发(java/php/python/ruby...)少不了要在本地搭建好开发环境,虽然说目前各种脚本/语言都有对应的Windows版,甚至是一键安装包,但很多时候和Windows环境的兼容性(如配置文件、编译的模块)并不是那么好,麻烦的问题是实际部署的环境通常是Linux,常常还要面临着开发和部署环境不一致,上线前还要大量的调试。而如果让每个开发人员都自己去搭建本地环境,安装虚拟机、下载ISO镜像、选择规格安装创建vm、安装OS、配置,会耗费非常多的时间,如果是团队开发应该要尽量保持每个人的运行环境一致。此时vagrant正式你所需要的。不适用正式环境部署。

vagrant实际上一套虚拟机管理工具,基于Ruby开发,底层支持VirtualBox、VMware甚至AWS、docker等作为虚拟化系统。我们可以通过 Vagrant 封装一个 Linux 的开发环境,分发给团队成员。成员可以在自己喜欢的桌面系统(Mac/Windows/Linux)上开发程序,代码却能统一在封装好的环境里运行,“代码在我机子上运行没有问题”这种说辞将成为历史。

通过上面的介绍如果你还在困惑有virtualbox或vmware为什么还要加入vagrant,纠结于要不要使用,可以参考这个问答 使用vagrant的意义在哪,另外docker作为后起之秀也可以做vagrant能完成的事情,stackoverflow有关于两位作者讨论各自应用场景的精彩"互掐",传送门→ (中文)。

1.2 几个概念

2. 安装vagrant

选择适合你的平台(Windows、Mac、Linux),下载对应格式的安装包。如Mac下 vagrant_1.7.1.dmg、VirtualBox-4.3.20-96997-OSX.dmg 。

3. 使用vagrant打造一个本地开发环境

本文将会演示从 nrel CentOS6.5 开始,安装必要的开发包、python、插件、Puppet,然后打包成一个box分发给团队的全过程。你也可以在别人box的基础上进一步通过Vagrantfile定制自己的环境。

3.1 初始化

3.1.1 vagrant box add {box-name}

$ vagrant box add ct65_00 Downloads/centos65.box 
==> box: Adding box 'ct65_00' (v0) for provider: 
    box: Downloading: file:///Users/sean/Downloads/centos65.box
==> box: Successfully added box 'ct65_00' (v0) for 'virtualbox'!

$ ll ~/.vagrant.d/boxes/ct65_00
$ vagrant box list

# vagrant box list
ct65_00   (virtualbox, 0)
centos64-i386  (virtualbox, 0)

这一条命令就是根据给出的box(镜像)文件地址,解压一份到用户目录~/.vagrant.d/boxes/{box-name}/0/virtualbox/下,所以你尽量应该以同一用户来管理进行vagrant所有操作。

_F_K GFW
在GFW保护之下,这简单获取box文件反而一开始就难到我们了。官方提供的在线安装在墙外是极为方便的,vagrant box add minimal/centos6便自动从vagrantcloud.com(现更名为https://atlas.hashicorp.com/search/boxes)下载,直接进入第二步。

还有一种方法是,先vagrant init minimal/centos6,然后直接启动vagrant up --provider virtualbox。当然这些都与下载boxes到本地效果是一样的,下载方法就是在vagrantcloud.com上点开你所需要的box版本,然后再URL里加入/providers/virtualbox.box便得到文件地址,如 https://atlas.hashicorp.com/hashicorp/boxes/precise64 对应的文件为 https://atlas.hashicorp.com/hashicorp/boxes/precise64/providers/virtua... 。

在墙内直接在线安装启动box,会报错:

The box 'ubuntu/trusty64' could not be found or
could not be accessed in the remote catalog. If this is a private
box on HashiCorp's Atlas, please verify you're logged in via
`vagrant login`. Also, please double-check the name. The expanded
URL and error message are shown below:

URL: ["https://atlas.hashicorp.com/ubuntu/trusty64"]
Error:

一个办法是ubuntu来 http://uec-images.ubuntu.com/vagrant/ 下载,centos来 http://nrel.github.io/vagrant-boxes/ 下载。我也从墙外下了几个典型的box放到了自己的百度云上共享了:http://pan.baidu.com/s/1sjHQBa1 。

2015-04-01更新:无意间发现现在不用梯子也可以访问了,Happy April Fool's Day!

3.1.2 vagrant init

$ mkdir ~/vagrant && cd ~/vagrant  //这个目录的目的就是统一管理你的Vagrantfile
$ vagrant init ct65_00
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

$ vi Vagrantfile
...
Vagrant.configure(2) do |config|
  config.vm.box = "ct65_00"
  onfig.vm.network "forwarded_port", guest: 80, host: 8080
#  config.vm.synced_folder "../data", "/vagrant_data"
  config.vm.provider "virtualbox" do |vb|
    vb.memory = "384"
    vb.cpus = 1
  end
  config.vm.hostname = "vg-ct65_00.tp-link.net"
...

init只是在当前目录生成一个Vagrantfile文件和.vagrant/目录,可以对它进行修改,比如定义 vm guest machine 的hostname、memory、cpu等,具体有关语法介绍见后文。

用户后面up虚拟机,这个 box-name 与上面add的相同,如果是 base 则可以省略。

3.2 启动虚拟机

# vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ct65_00'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: v-box_default_1427284884787_97348
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Connection timeout. Retrying...
    ...
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => /root/vagrant/v-box/ct65_00

up过程是默认会根据当前目录下的Vagrantfile来启动vm,如果当前目录没有Vagrantfile,则去上层目录寻找,依次类推。第一次vagrant up ct65_00时会从~/.vagrant.d/boxes中导入相应的box文件到~/VirtualBox VMs/,可以通过vboxmanage showvminfo {VM-ID}看到该虚拟机的配置(Mac上为VBoxManage)。如果你想让虚拟机存储在指定位置,如我的Mac SSD硬盘空间贵,可以运行VirtualBox,手动设置存储/storage的路径。

默认 localhost:2222 转发到 guest:22 以供ssh连接;用户名/密码:vagrant/vagrant;默认共享目录就是host上Vagrantfile所在目录;如果电脑配置比较低导致启动时间比较长,或者VirtualBox启动出错,可能会提示上面的 Connection timeout 。

另外提示一下,某次我在Linux上测试,由于Linux host本身也是vSphere虚拟机,通过vagrant启动virtualbox另一个虚拟机(即嵌套),一直Retrying,后来根据上面 stackoverflow 打开了VBox GUI,发现是CPU架构的问题,一直堵塞,所以就不建议虚拟机上再装虚拟机了:

VT-x/AMD-V hardware acceleration is not available on your system. Your 64-bit guest will fail to detect a 64-bit CPU and will not be able to boot.

3.3 连接虚拟机,初始化环境

vagrant ssh

$ vagrant ssh
Last login: Tue Mar 31 02:15:38 2015 from 10.0.2.2
Welcome to your Vagrant-built virtual machine.

一般建立box时约定的用户名/密码:vagrant/vagrant,root密码也是 vagrant,默认的网络连接方式是Host-Only。

定制你的环境

如安装jdk,创建用户,解压tomcat,修改server.xml,添加yum源等。这里一步到位,唯一要说明的是tomcat conf/server.xml 的
<Context path="" docBase="/vagrant_data" reloadable="true" >...应用目录设置为共享目录。

3.4 打包成box

3.4.1 安装必要软件

打包是为了分发出去,做扩展用

# yum install -y lrzsz telnet vim puppet puppetmaster

如果你是从0开始建立一个box,当然还需要创建vagrant用户以及public key,具体可以参考如何制作一个vagrant的base box

$ 3.4.2 安装Virtualbox Guest Additions

每个人电脑上安装的Virtualbox版本很可能不一样,vagrant up可能会有提示版本不兼容(同一大版本号还好,可省略这一步),导致host到guest共享目录模块失败,最终无法启动虚拟机。

安装方法可以有 vagrant-vbguest(注意这是vagrant插件,不是virtualbox插件),使用超级详细,只需执行vagrant plugin install vagrant-vbguest,默认从本地找 VBoxGuestAdditions.iso (各平台路径一般都可以找到),如果没找到则去http://download.virtualbox.org/virtualbox/%{version}/VBoxGuestAdditions_%{version}.iso 下载,直接启动vm便可安装或更新virtualbox guest additions ,甚至可以通过vagrant vbguest命令给正在运行的vm安装,缺点是 plugin install 得连网。下面是手动在vm内部安装:

一般最小化的box不带有CDROM,需要通过VirtualBox图形化界面添加一个DVD/CD存储设备,然后在启动VM后 Devices -> Insert Guest Additions CD 。(相信你可以可以找到办法直接挂载 .iso 文件到vm里面,免去添加多余设备)

for linux : /usr/share/virtualbox/VBoxGuestAdditions.iso
for Mac : /Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso
for Windows : %PROGRAMFILES%/Oracle/VirtualBox/VBoxGuestAdditions.iso

$ sudo yum install linux-headers-$(uname -r) build-essential dkms 
$ sudo mount /dev/cdrom /media/cdrom
$ sudo sh /media/cdrom/VBoxLinuxAdditions.run --nox11

官方参考:http://docs.vagrantup.com/v2/virtualbox/boxes.html

3.4.3 vagrant package

打包导出:

 vagrant package --output sean-vg-ct65_ts.box
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...
==> default: Compressing package to: /Users/sean/vagrant/sean-vg-ct65_ts.box

当前目录下若存在同名package.box则会export失败。打包的来源并不是.vagrant.d而是VirtualBox虚拟机本身,可以通过--base vm-name来指定所导出的虚拟机名称,--vagrantfile file-pathname可以将Vagrantfile直接封进box中。以后就可以把这个 .box 文件分发给开发人员使用了。

4. 其他

4.1 命令

vagrant suspend将虚拟机置于休眠状态。这时候主机会保存虚拟机的当前状态。再用vagrant up启动虚拟机时能够返回之前工作的状态。这种方式优点是休眠和启动速度都很快,只有几秒钟。缺点是需要额外的磁盘空间来存储当前状态。

vagrant halt则是关机。如果想再次启动还是使用vagrant up命令,不过需要多花些时间。

vagrant destroy则会将虚拟机从磁盘中删除。如果想重新创建还是使用vagrant up命令。

vagrant reload从Vagrantfile重新启动虚拟机。

vagrant global-status输出所有虚拟机当前运行状态,关机、已启动等。

另外1.2以上版本的Vagrant还引用了插件机制。可以通过vagrant plugin来添加各种各样的plugin,这给Vagrant的应用带来了更大的灵活性和针对性。比如可以添加vagrant-windows的插件来增加对windows系统的支持,通过添加vagrant-aws插件来实现给AWS创建虚拟机的功能。你也可以编写自己的插件。由于Vagrant是ruby写的一个gem,其插件的编写也是使用的Ruby语言。

关于 Vagrantfile说明以及网络、多机器管理的配置,见 Varantfile说明

4.2 问题集

关机后启动虚拟机,相应的会提示:

Failed to mount folders in Linux guest. This is usually because the "vboxsf" file 
system is not available. Please verify that the guest additions are properly 
installed in the guest and can work properly. The command attempted was:


mount -t vboxsf -o uid=id -u vagrant,gid=getent group vagrant | cut -d: -f3 vagrant /vagrant
mount -t vboxsf -o uid=id -u vagrant,gid=id -g vagrant vagrant /vagrant

The error output from the last command was:

/sbin/mount.vboxsf: mounting failed with the error: No such device

原因就是旧版本卸载成功但新版本guest additions却因为yum install kernel-devel找不到软件包(No package available)失败。这种情况很少见,kernel-devel 一般都可以装上去解决。如果像我这样提示死活没这个软件包的,换个box吧!

上面那串数字字母,是虚拟磁盘的UUID,可以先通过ps -ef|grep Virtual(Mac, virtualbox in Linux )查到虚拟机UUID,再通过VBoxManage showvminfo {VM UUID}|grep vdi看到这个 disk UUID。参考 Add some way to increase disk space from Vagrantfile
扩容完成后把Vagrantfile中对应的部分去掉,以免每次启动都进行这步操作。