使用 Vagrant 构建开发环境
使用 Vagrant 构建开发环境
摘要:本文描述了如使用 Vagrant 构建统一的开发环境。
问题
作为开发人员,我们通常面临的问题有:
- 开发环境需要手工安装配置,这包括操作系统(CentOS、Ubuntu)、PHP/HHVM、Python、Node.js、MySQL、Apache/Nginx等的版本和配置。
- 无法重现问题。
甲:你说的问题,怎么在我的机器上没事儿啊?
乙:可问题的确存在,不信你到我的机器上来看。
甲:那我的机器和你的环境哪里不同呢?
因为环境不同,比如开发环境和生产环境不同,和测试环境也不完全相同。 - 团队中没有统一的方法配置开发环境,保持相同的环境,好的可能会有一个文档,告诉大家要下载、安装的东西,缺乏文档的可能就要到处问了。
- 在同一台机器上,要为参与的不同项目配置不同的环境,因为要求的软件、版本不同,有时候这根本就不可能。
- 团队中不同的成员使用的机器、操作系统就不同,就很难保证完全相同的环境。比如很多开源项目,团队成员来自五湖四海,有人用Windows,有人用Mac,系统不一样,配置就可能不同,导致不同的结果。
解决方法 - Vagrant之道
用虚拟机构建一致的环境,并且把安装和配置的过程自动化,这就是Vagrant所做的事情。按照Vagrant的流程:
- 系统工程师只需要准备Vagrant配置以及相应的脚本,来配置虚拟机。
- 团队成员只需要从源码控制中签出代码,运行vagrant up,就可以有一个完整的开发环境了。开发人员还是可以继续在自己的机器上使用自己熟悉的编辑器、浏览器和其它工具,而代码及数据库、web服务器则是运行在Vagrant的虚拟机中。
- 如果把虚拟机搞乱了,只需要用vagrant destroy把虚拟机删除,再运行vagrant up就又可以得到事先配置好的虚拟机了。
- 用完之后,只需要使用vagrant halt或者vagrant suspend把虚拟机关机或者休眠,第二天用vagrant up就把虚拟机重新启动,恢复到昨天的状态了。
- 这种对虚拟机的管理方式,不单在一个项目的团队中可以使用,而且可以跨多个项目使用。使用相同的流程和环境,提高了生产力,再也不会有“在我的机器上明明没有这个问题”的问题了。
这就是Vagrant之道(参看[5]第4页)。
Vagrant可以做下面这些事情:
- 通过SSH登录虚拟机
- 关掉虚拟机
- 删除虚拟机,完全删除虚拟硬盘和配置信息
- 暂停和恢复虚拟机
- 把虚拟机打包,从而可以分发给其他开发人员
Vagrant涉及的概念:
- Vagrant本身并不是虚拟机,它通过其供应商(provider)来使用虚拟机,支持的虚拟机有Oracle的VirtualBox、VMWare的VMWare Fusion和VMWare Workstation和微软的Hyper-V,以及象Docker这样的容器,还有Amazone的AWS、Rackspace和DigitalOcean等公有云。缺省的是使用VirtualBox,因为这是免费的,也是最容易配置的。
- 虚拟机只是提供了一个虚拟的机器,其中的各种软件的安装和配置还是要靠provision,支持的配置工具有最基本的shell脚本、以及Puppet、Chef和Ansible等等。
- 为了便于虚拟机的分发和共享,虚拟机本身的虚拟硬盘和配置加上Vagrant的设置文件可以打包在一起,这在Vagrant中叫box,这是可被Vagrant直接使用的虚拟机镜像文件。众多厂商和用户在Atlas(以 前叫Vagrant Cloud)上提供了各种box,对应你所使用的provider。你也可以在其它box的基础上,进一步配置、发布自己的box,共享给自己的团队或者公众。
- 如果上述的功能还不能满足你对环境的要求,Vagrant还提供插件,对基本的功能进行扩展,甚至你也可以按照其规范开发自己的插件,满足特定的要求。
下面我们就用几个例子来说明Vagrant的使用。
请先下载、安装VirtualBox和Vagrant。因为Vagrant是用ruby开发的,你还要下载、安装Ruby。不论你的机器是Windows、Mac或者各种Linux发行版本,这几个软件都有相应的版本。
实例一 最简单的Vagrant环境
创建一个新目录,比如叫example。然后运行:
vagrant init hashicorp/precise64
在上述命令中指定了hashicorp/precise64为使用的box,这是Ubuntu 12.04 LTS,更多的信息可见https://atlas.hashicorp.com/hashicorp/boxes/precise64。第一次需要一个box时(即在运行vagrant up时),Vagrant会自动下载该box到你的机器上,这通常位于<homedir>\.vagrant.d\boxes(例如,在Windows 7上,这就是C:\Users\myusername\.vagrant.d\boxes),以后就不必再次下载了。
上述会生成一个基本的Vagrant配置文件Vagrantfile,最简单的情况可以只有这一个文件。基本内容如下:
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # Every Vagrant virtual environment requires a box to build off of. config.vm.box = "hashicorp/precise64" # Create a forwarded port mapping which allows access to a specific port # within the machine from a port on the host machine. In the example below, # accessing "localhost:8080" will access port 80 on the guest machine. config.vm.network "forwarded_port", guest: 80, host: 8080 end
这个文件指定了下面这些基本的配置:
- 在上述生成的配置文件中,指定了如下box:
config.vm.box = "hashicorp/precise64"
- 其次,指定虚拟机和主机之间的映射目录,通常共享的是源文件目录。这样,你就可以在主机上继续使用自己熟悉的编辑器写代码,而使用虚拟机环境来运行、调试。
上述配置文件没有指定任何映射目录,但是Vagrant缺省会把主机Vagrantfile配置文件所在的目录映射为虚拟机上的/vagrant目录。 - 还有,就是配置虚拟机的网络,否则,你从主机上无法访问虚拟机。这有三种模式:forwarded ports、host-only networking和bridged networking。
- Forwarded Ports,缺省值,在主机上选择一个端口,转发给虚拟机的一个端口,这样就不必给虚拟机一个IP,就可以从主机访问虚拟机了。
上述配置文件中,使用的是Forwarded Ports:
config.vm.network "forwarded_port", guest: 80, host: 8080
这会把虚拟机的80端口映射到主机的8080端口,在主机上通过localhost:8080就可以访问虚拟机了。同时,缺省会有另外一个映射,把虚拟机的22端口映射到主机的2222端口,这是为了允许从主机上通过SSH登录到虚拟机。
- Host-Only Networking,这会在主机上创建一个仅对主机和虚拟机可见的私有网络,这样虚拟机得到一个指定的静态IP地址,就可以从主机通过该静态IP地址来访问虚拟机了,而虚拟机也可以通过*.*.*.1的IP地址来访问主机。这种情况下,主机所在的网络上的其他机器是无法“看到”这个私有网络的,也就无法访问虚拟机。
- Bridged Networking,桥接虚拟机到主机上的一个设备(应该指网卡),这样虚拟机就如同主机所在网络上的另外一台物理机器一样,这意味着虚拟机可以访问网络资源,网络上的其他机器也可以访问虚拟机。
- Forwarded Ports,缺省值,在主机上选择一个端口,转发给虚拟机的一个端口,这样就不必给虚拟机一个IP,就可以从主机访问虚拟机了。
有了这个Vagrantfile配置文件,就可以启动虚拟机了。在命令行中,在Vagrantfile所在的目录中,运行如下命令:
vagrant up
小窍门:在Windows 7(及以上版本?)的Windows Explorer中,可以按住Shift键,再用鼠标右击某个目录,直接在跳出的上下文菜单中点击"Open command window here",就直接进入命令行并进入该目录了。
输出如下:
从上面的输出中,我们可以看到指定使用的box、端口的映射和共享目录的映射。
前面提到虚拟机22端口对主机2222端口的映射是用于SSH的,我们现在就可以用SSH登录虚拟机了。运行如下命令:
vagrant ssh
小窍门:如果你的命令行没有对ssh的支持,就无法登录。在Windows上,可以在安装git的时候,有一个选项,选择添加相应的目录到系统路径中,就支持在命令行使用SSH了。
上述命令相应的输出为:
这就登录进入了虚拟机,在$提示符下可以使用Linux命令操纵虚拟机了。
实例二 Python Sphinx文档环境
第二个例子中,我们用VirtualBox和shell脚本来构建一个最基本的虚拟环境,用于使用Sphinx来生成文档。先创建一个新目录,比如叫docs,在其中创建一个新文件Vagrantfile,完整的配置为:
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # Every Vagrant virtual environment requires a box to build off of. config.vm.box = "puphpet/ubuntu1404-x64" # Share an additional folder to the guest VM. The first argument is # the path on the host to the actual folder. The second argument is # the path on the guest to mount the folder. And the optional third # argument is a set of non-required options. # config.vm.synced_folder "../data", "/vagrant_data" config.vm.synced_folder ".", "/docs" config.vm.provider "virtualbox" do |v| v.name = "cakephp_docs" v.memory = 384 end config.vm.provision :shell, path: "provision.sh" end
最常用的Linux版本之一是Ubuntu 14.04,我们就以此为基础,在配置中指定使用puppet/ubuntu1404-x64 box:
config.vm.box = "puphpet/ubuntu1404-x64"
映射主机的当前目录(即Vagrantfile配置文件所在的目录)为虚拟机的/docs目录,这里会放置源代码控制下的文档源码*.rst文件:
config.vm.synced_folder "./", "/docs"
另外,因为不需要从主机访问虚拟机的web服务器,所以没有配置网络,即使用的是缺省的Forwarded Ports模式。
再指定使用一个shell脚本,安装、配置sphinx:
config.vm.provision :shell, path: "provision.sh"
在同一目录下,再创建shell脚本文件povision.sh,其内容为:
#!/usr/bin/env bash echo "update apt repository..." apt-get update >/dev/null 2>&1 echo "Installing python-setuptools..." sudo apt-get install -y python-setuptools >/dev/null 2>&1 echo "Installing easy_install..." sudo apt-get install -y easy_install >/dev/null 2>&1 echo "Installing sphinx..." sudo easy_install sphinx==1.2 >/dev/null 2>&1 echo "Installing sphinxcontrib-phpdomain..." sudo easy_install sphinxcontrib-phpdomain >/dev/null 2>&1
因为Python是Linux自带的,不需要再安装了。该脚本使用apt-get安装两个软件包python-setuptools和easy_install,然后再用easy_install安装sphinx 1.2和sphinxcontrib-phpdomain。
这样就配置好了,在命令行中,在Vagrantfile所在的目录中运行
vagrant up vagrant ssh
之后,就通过SSH登录到虚拟机,可以在其中用sphinx从源代码构建html文档了。
实例三 LAMP开发环境
在第三个例子中,我们使用PuPHPet为PHP配置一个LAMP开发环境。PuPHPet是一个网页的向导式界面,让你更容易地生成使用Puppet的Vagrant配置文件。
用浏览器打开https://puphpet.com。
第一步,选择部署目标,缺省为本地,使用VirtualBox。我们选择操作系统为Ubuntu 14.04 64位,如下图所示:
然后,设置虚拟机IP地址、内存、端口,以及目录映射,注意这里的目录是用来作为网站应用根目录的,如下图所示:
第二步,选择要在虚拟机上安装的软件包。我们选择Ubuntu的htop和vim,如下图所示:
第三步,防火墙规则,不需要做任何设置,略过:
第四步,选择web服务器,我们选择使用Apache,要安装rewrite模块,如下图所示:
再设置域名、网站根目录,允许设置多个VHOST,如下图所示:
注意上面的Document Root路径和在第一步中设置的目录映射有关。
第五步,选择语言PHP,以及相关的设置和需要的模块,如下图所示:
还可以选择需要的PHP库,如下图所示:
第六步,选择数据库MySQL,并且设置管理员密码,如下图所示:
然后创建数据库,允许创建多个数据库,如下图所示:
其中,/var/www/database/backup/2014-10-19_cakephp_blog.sql是在虚拟机中要恢复的数据库备份,这个路径也和你在第一步中设置的目录映射有关。
第七步,选择其它工具,包括邮件工具Mailcatcher、队列工具Beanstalkd及RabbitMQ、搜索工具Elastic Search及Solr,如下图所示:
我们不使用任何额外工具,所以略过这一步。
第八步,也就是最后一步,完成,如下图所示:
点击那个大大的蓝色按钮“GO AHEAD THEN, MAKE IT!”,就可以下载一个将近3MB的puphpet.zip。打开这个.zip文件,可以看到按照我们前面第一至八步设置的Vagrantfile配置以及相应的Puppet模块,如下图所示:
把它解压到一个目录中,这些文件可以和PHP项目的源代码一起放入源码控制中,供项目团队的其他成员使用。
在命令行中,在Vagrantfile所在的目录中运行vagrant up,就可以得到一个运行的LAMP虚拟机了。
在主机的hosts文件中,按照第四步的设置加入下面这行:
192.168.56.102 www.cakephp3.dev # CakePHP 3
就可以在主机的浏览器上通过www.cakephp3.dev访问虚拟机上的网站了。
Vagrant的常用命令
vagrant up # 启动、配置虚拟机
vagrant suspend # 休眠虚拟机
vagrant halt # 关闭虚拟机
vagrant destroy # 删除虚拟机
vagrant status # 当前目录下Vagrantfile配置对应的Vagrant虚拟机的状态,以及在此状态下可以使用的命令
vagrant global-status # 本机上所有Vagrant虚拟机的状态
更多的命令可以参考[3]中的Command-Line Interface。
进一步的思考
- 打包、分发box,这是Vagrant通过box以及Atlas提供的功能。
- 使用Docker的provider,可以节省资源,极大地缩短虚拟机的启动时间,也利于生产环境的部署。
- 是否可以使用Vagrant来配置Windows虚拟机?应该使用什么provision?是否可以使用PowerShell?
- 是否可以使用Vagrant来配置Mac虚拟机?可能不行,至少在Atlas上没有找到Mac的box。而且,恐怕也没必要。
参考资料
- Vagrant官网
https://www.vagrantup.com/ - Vagrant Getting Started
https://docs.vagrantup.com/v2/getting-started/ - Vagrant Documentation
https://docs.vagrantup.com/v2/ - Atlas (之前叫Vagrant Cloud)
https://atlas.hashicorp.com - Vagrant: up and running, ISBN 978-1-449-33583-0
http://shop.oreilly.com/product/0636920026358.do - Hello Vagrant - 黄博文
http://www.cnblogs.com/huang0925/p/3349841.html - Docker与Vagrant之间的特点比较
http://www.cnblogs.com/vikings-blog/p/3973265.html