vagrant 使用

326 阅读9分钟

vagrant介绍

官网:www.vagrantup.com/ 通过编写一个Vagrantfile来控制虚拟机的启动、虚拟机网络环境的配置、虚拟机与主机间的文件共享,以及启动后自动执行一些配置脚本,比如自动执行一个shell script来安装一些必备的开发工具,如Mysql。这意味着,当你需要在多台机器间同步开发进度时,只需要同步Vagrantfile,就可以保证各台机器拥有一致的开发环境。

  1. vagrant能干什么?
  • 建立和删除虚拟机
  • 配置虚拟机的运行参数
  • 管理虚拟机的运行状态
  • 自动化运行和安装开发环境
  • 打包和分发虚拟机运行环境
  1. Vagrant的依赖程序

既然是配置虚拟机,那么自然需要虚拟机程序和被虚拟的操作系统镜像(Image)。前者常用的选择有VirtualBox和VMWare,后者则包括Ubuntu、FreeBSD等等。Vagrant称前者为provider,称后者为box。原则上,我们可以自由搭配providerbox,但由于VirtualBox开源且免费, Vagrant将其作为默认的provider。所以,一般会先安装VirtualBox,再安装Vagrant。

Vagrant的box

假设我们使用VirtualBox作为provider,安装好Vagrant后,新建一个空文件夹,将其路径视作项目的根路径。然后运行终端并进入这个路径,键入vagrant init,此文件夹中会多出一个Vagrantfile。用文本编辑器打开后,会发现该文件由ruby语言编写,且大部分行都被注释掉。一般未注释的内容如下:

Vagrant.configure(“2”) do |config|
config.vm.box = “base”
End
  1. do…end结构在ruby中称为块(block),可以简单理解为Vagrant.configure(“2”)执行到某一步时程序被挂起,并将一个需要配置的对象交给块,由块内的语句进行具体配置,当块内的语句依次执行完后,配置好的对象又被交回给Vagrant.configure(“2”),之前被挂起的程序则继续执行直到完毕。
  2. .configure(“2”)中的2是指配置文件的语法规则的版本。虽然是基于ruby,Vagrantfile仍然有一套自己的语法细则。

config.vm.box直译过来即“配置虚拟机盒子”,因此这一步指定将要使用哪个虚拟机镜像(box)。

Vagrant如何管理boxes

传统使用虚拟机的方式为下载某个虚拟机的*.iso安装镜像到本地,然后使用VirtualBox加载镜像并安装操作系统到虚拟机中,期间可能涉及到网卡、USB等虚拟硬件的配置。之后就可以正常的使用这个虚拟的系统了。这一过程基本类似于使用安装盘在一台真实的电脑上安装系统。

 

Vagrant 使用的镜像并不是待安装的系统镜像,而是从虚拟机中导出的、对已经安装配置好的操作系统的快照,以 .box 作为扩展名。这类似于Windows中完整的系统备份所产生的镜像文件。.box文件不过是个压缩文件包,里面除了基础数据的镜像外,还包括一些元数据文件,用来指导Vagrant将系统镜像正确的加载到对应的provider中。因此,每个.box都有自己对应的provider。我们当然可以从最原始的安装镜像开始,一步步制作自己的.box镜像。但这一过程比较麻烦,详情可参见官方文档CREATING A BASE BOX。幸运的是,Vagrant官网提供了许多制作好的box文件。参见atlas.hashicorp.com/boxes/searc…。选择时注意box与provider的对应关系。

添加box文件到Vagrant的box管理系统

基本语法:vagrant box add {title} {url}。其中,title是box文件在Vagrant中的名字,必须唯一。url是box的物理地址,可以是一个网址,也可以是本地的一个文件路径。当url是网址时,vagrant会调用内置的下载模块从服务器下载镜像到本地,Vagrant官网中提供的box都只能通过指定网址在线安装。无论是哪种方式,原始的box文件都会被放在~.vagrant.d路径下。指令vagrant box list可以查看所有已添加的box。

添加完box后,便可以将Vagrantfile中的config.vm.box赋值为该box的title。另外,你还可以更灵活的指定box。比如,vagrant init的完整指令是vagrant init [options] [name [url]]。再比如,Vagranfile中也可以给config.vm.box_url赋值。

回忆之前自动生成的Vagrantfile,config.vm.box被默认赋值为base。这意味着当Vagrant调用名为base的box且发现本地没有时,它会自动远程安装一个box并命名为base。

vagrant up

写好Vagrantfile后,在项目的根目录下运行vagrant up,Vagrant便会依据Vagrantfile中的指令来搭建虚拟机环境。

第一步当然是载入box文件。之前说过,添加到Vagrant系统中的box会被统一保存在文件夹~.vagrant.d。而这一步,则是根据指定的box文件中的元数据,使用相关的provider来载入系统镜像。这意味着系统镜像的数据又被复制一遍,并交给provider管理。也就是说,如果我们使用同一个box创建N个开发环境,那么同一份基础数据会被复制N遍。这种处理方式固然造成数据的冗余,但优点也很突出,即保证了每个开发环境的独立性。另外,vagrant up只有在第一次执行时才会复制box镜像。而且这之后,如果打开provider程序(这里指VirtualBox),你会看到虚拟机中多了一个以项目所在文件夹的名字为前缀命名的系统镜像。

第一步之后,Vagrant还会进一步完成文件夹共享、网络通信等项目的配置。注意到,除了第一次的up会完整的执行所有指令外,为了节省启动成本,除非显示指定,此后的up中Vagrant会自动跳过那些不需要反复执行的配置指令。

up的逆操作是destroy。顾名思义,在项目的根目录下运行vagrant destroy,Vagrant会将box从provider中卸载并删除。但destroy并不会清除共享文件夹中的内容。所谓共享文件夹,即虚拟机与宿主机间共享的一个文件夹。任何一方均可对该文件夹操作,其更改对另一方实时可见。

Provision

Provision可以说是自动装机的核心步骤,Vagrant则提供了一个方便的接口。

这一步相当于自动执行“安装各类软件”、“调整系统设置”等步骤。其实,这一步的操作结果可以被打包到box中,从而完全避免Provision。比如,我们既可以在Provision中安装Python,也可以直接将一个已经安装好Python的系统打包为box,然后使用该box创建虚拟机。两种方式各有优劣,显然前者更灵活、更轻量。毕竟,不是所有打包到box中的功能都为用户所需。使用Provision可以最大限度的“按需定制”。

在这一步,Vagrant会可能需要调用第三方的Provision System,比如Chef、Puppet。而最基本的Provision可以通过shell script来指定,反映在Vagrantfile中,便是如下的代码:

Vagrant.configure("2") do |config|
  ...
  config.vm.provision "shell", inline: "echo Hello, World"
  config.vm.provision "shell", path: "script.sh"
  ...
end

其中,inline表示script直接写在Vagrantfile中,而path则表示script被写在指定的文件中。

常用命令

  1. vagrant init centos/7 :初始化虚拟机
  2. vagrant up:启动虚拟机
  3. vagrant status:查看当前虚拟机状态
  4. vagrant halt :关机
  5. vagrant destory : 删除当前虚拟机
  6. vagrant suspend:暂停虚拟机,会保存虚拟机当前运行状态(只会耗费硬盘空间)
  7. vagrant resume:恢复虚拟机
  8. vagrant reload:重启虚拟机

vagrant 共享目录

vagrant在启动的时候指定了宿主机和虚拟机之间的共享目录

 Rsyncing folder: /Users/cuiqiaoqiao/vagrant/ => /vagrant

你也可以添加其他的共享目录:

  config.vm.synced_folder "../data", "/vagrant_data",
    create: true, owner: "root",group: "root"

../data:宿主机目录

/vagrant_data:虚拟机目录

create: true:如果没有则新建

owner:"root":虚拟机所属者

group:"root":虚拟机所属组

重启虚拟机出现:

Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attempted was:

mount -t vboxsf -o uid=0,gid=0 vagrant_data /vagrant_data

解决方案:

vagrant plugin install vagrant-vbguest
vagrant reload
# 启动信息如下:
==> default: Mounting shared folders...
    default: /vagrant_data => /Users/cuiqiaoqiao/data

vagrant网络配置

私有网络配置

修改配置文件vagrantfile

config.vm.network "private_network", ip: "192.168.33.10"

共有网络配置

修改配置文件vagrantfile

config.vm.network "public_network"

打包box

# 使用命令进行打包,此时会在当前目录生成package.box
1. vagrant package
# 添加打包好的box
2. vagrant box add cqq/centos-7-empty package.box
# 删除package.box
3. rm -rf package.box
# 查看box列表
4. vagrant box list
# 重新创建虚拟机
5. cd ../
	mkdir vagrant1
  vagrant init cqq/centos-7-empty
  vagrant up

vagrant多主机的定义

  1. 在vagrantfile中定义多主机
 config.vm.box = "cqq/centos-7-empty"
 config.vm.define "dev" do |dev| 
 end
 config.vm.define "prod" do |prod|
 end
  1. 使用命令进行查看
➜  [/Users/cuiqiaoqiao/vagrant-project/work-pro] vagrant status
Current machine states:

dev                       not created (virtualbox)
prod                      not created (virtualbox)
  1. 启动定义的主机
# 启动所有定义的主机
vagrant up
# 启动自定义的某个主机
vagrant up dev
  1. 连接到自定义的主机上
vagrant ssh dev
  1. 配置主机网络
# 配置了私有网络  
config.vm.define "dev" do |dev|
   dev.vm.network "private_network", ip: "192.168.33.11"
end
config.vm.define "prod" do |prod|
   prod.vm.network "private_network", ip: "192.168.33.22"
end
  1. 重启 vagrant rolad 默认会重启所有配置文件中配置的主机。
  2. 使用ping命令来测试是网络情况
  3. 在vagrantfile中定义主机名
  config.vm.define "dev" do |dev|
    dev.vm.network "private_network", ip: "192.168.33.11"
    dev.vm.hostname = "cqq-dev"
  end

  config.vm.define "prod" do |prod|
    prod.vm.network "private_network", ip: "192.168.33.22"
    prod.vm.hostname = "cqq-prod"
  end
  1. vagrant多主机定义同步文件夹
  # 在配置目录中创建dev和prod文件夹,并修改vagrantfile文件
 config.vm.define "dev" do |dev|
    dev.vm.network "private_network", ip: "192.168.33.11"
    dev.vm.hostname = "cqq-dev"
    dev.vm.synced_folder "dev","/vagrant-data"
  end

  config.vm.define "prod" do |prod|
    prod.vm.network "private_network", ip: "192.168.33.22"
    prod.vm.hostname = "cqq-prod"
    prod.vm.synced_folder "prod","/vagrant-data"
  end
[dev] GuestAdditions 6.1.10 running --- OK.
==> dev: Checking for guest additions in VM...
==> dev: Setting hostname...
==> dev: Configuring and enabling network interfaces...
==> dev: Mounting shared folders...
    dev: /vagrant => /Users/cuiqiaoqiao/vagrant-project/work-pro
    dev: /vagrant-data => /Users/cuiqiaoqiao/vagrant-project/work-pro/dev
==> dev: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> dev: flag to force provisioning. Provisioners marked to run always will still run.
[prod] GuestAdditions 6.1.10 running --- OK.
==> prod: Checking for guest additions in VM...
==> prod: Setting hostname...
==> prod: Configuring and enabling network interfaces...
==> prod: Mounting shared folders...
    prod: /vagrant => /Users/cuiqiaoqiao/vagrant-project/work-pro
    prod: /vagrant-data => /Users/cuiqiaoqiao/vagrant-project/work-pro/prod
==> prod: Machine already provisioned. Run `vagrant provision` or use the `--provision`

配合示例:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.require_version ">= 1.6.0"

boxes = [
    {
        :name => "docker-host",
        :eth1 => "192.168.205.10",
        :mem => "1024",
        :cpu => "1"
    }
]

Vagrant.configure(2) do |config|

  config.vm.box = "centos/7"
  boxes.each do |opts|
    config.vm.define opts[:name] do |config|
      config.vm.hostname = opts[:name]
      config.vm.provider "vmware_fusion" do |v|
        v.vmx["memsize"] = opts[:mem]
        v.vmx["numvcpus"] = opts[:cpu]
      end
      config.vm.provider "virtualbox" do |v|
        v.customize ["modifyvm", :id, "--memory", opts[:mem]]
        v.customize ["modifyvm", :id, "--cpus", opts[:cpu]]
      end
      config.vm.network :private_network, ip: opts[:eth1]
    end
  end
  config.vm.synced_folder "./labs", "/home/vagrant/labs"
  config.vm.provision "shell", privileged: true, path: "./setup.sh"
end