Skip to main content

Vagrant 小技巧

·2514 字·6 分钟
工具 & 生产效率 vagrant
Table of Contents

断断续续的用着 Vagrant,本地开发依旧有着不可比拟的优势,POC,demo,hacking 等等

基本命令 #

命令作用
vagrant box add <box 名> <box 文件>如果缺省 box 文件,将从 vagrant 的网站查询并下载
vagrant box list显示本地 box 列表
vagrant box remove删除相应的 box
vagrant up启动当前虚拟机环境
vagrant ssh 登录虚拟机,也可以指定 hostname
vagrant ssh-config查看 ssh 登录信息
vagrant status 获取当前虚拟机的状态,也可以查看指定 hostname
vagrant global-status显示所有虚拟机环境状态
vagrant suspend暂停当前虚拟机环境
vagrant resume恢复当前虚拟机环境
vagrant reload修改了 Vagrantfile 后,使之生效(相当于先 halt,再 up)
vagrant halt关闭当前虚拟机环境
vagrant destroy删除当前虚拟机环境,释放硬盘空间
vagrant pacakge当前的运行的虚拟机环境进行打包

plugins #

vagrant-cachier
这玩意在 VM 下载包的时候(agt,yum,etc.)可以缓存,如三台 centOS 的 VM 只需下载 centOS 一次。安装:

$ vagrant plugin install vagrant-cachier

Vagantfile 里加入这段:

	if Vagrant.has_plugin?("vagrant-cachier")
		config.cache.scope = :box
	end

vagrant-hostsupdater
VM 和主机里的 hosts 文件都会得到更新,这样 VM 和主机里的都可以直接通过 hostname 通讯。安装:

$ vagrant plugin install vagrant-hostmanager

Vagantfile 里加入这段:

	if Vagrant.has_plugin?("vagrant-hostmanager")
		config.hostmanager.enabled = true
		config.hostmanager.manage_host = true
		config.hostmanager.ignore_private_ip = false
		config.hostmanager.include_offline = true
	end

swap #

可以通过增加 swap 的方式增加虚机的内存,以免耗尽主机的内存,下面这段代码就是做这件事的:

#!/bin/sh

#create the swap space 1GB
echo "Creating 1GB swap space in /swapfile..."
fallocate -l 1G /swapfile
ls -lh /swapfile

#secure the swapfile
echo "Securing the swapfile..."
chown root:root /swapfile
chmod 0600 /swapfile
ls -lh /swapfile

#turn the swapfile on
echo "Turning the swapfile on..."
mkswap /swapfile
swapon /swapfile


echo "Verifying..."
swapon -s
grep -i --color swap /proc/meminfo

echo "Adding swap entry to /etc/fstab"
echo "swapfile none swap sw 0 0" >> /etc/fstab

echo "Result: "
cat /etc/fstab

echo " ****** We are done !!! ********"

vagrant file 模板 #

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

ENV["LC_ALL"] = "en_US.UTF-8"

# ---- 虚机配置 ----
N = 1

nodes = [
  {
    :node => "node0",
    :box => "centos/7",
    :cpu => 1,
    :mem => 1024
  }
]

# ---- 各种控制变量 ----
box = "centos/7"
custom_cpu_mem = "yes"
enable_custom_boxes = "yes"
enable_port_forwards = "yes"
linked_clones = "no"
server_cpus = 1
server_memory = 512
additional_nics = "yes"       # yes | no
additional_nics_dhcp = "yes"
additional_nics_num = 1
subnet = "192.168.202."
subnet_ip_start = 200
ansible_groups = {
  "test-nodes" => [
    "node[0:#{N-1}"
  ]
}
port_forwards = [
  {
    :node => "node0",
    :guest => 80,
    :host => 8080
  },
  {
    :node => "node0",
    :guest => 8000,
    :host => 8000
  }
]
provision_nodes = "yes"
synced_folder == "yes"

Vagrant.configure(2) do |config|
  (1..N).each do |node_id|
    nid = (node_id - 1)
	config.vm.define "node#{nid}" do |node|
	  ### 指定 vagrant box
      if enable_custom_boxes == "yes"
        box_set = "no"  #Initially set to no so it can be set to true if found in custom box defined
        nodes.each do |cust_box|
          if cust_box[:node] == "node#{nid}"
            node.vm.box = cust_box[:box]
            box_set = "yes"
          end
        end
        if box_set == "no"
          node.vm.box = box
        end
      end
	  if enable_custom_boxes == "no"
        node.vm.box = box
      end
	  node.vm.provider "virtualbox" do |vb|
        if linked_clones == "yes"
          vb.linked_clone = true
		end
		### 指定 cpu 数
        if custom_cpu_mem == "no"
          vb.customize ["modifyvm", :id, "--cpus", server_cpus]
          vb.customize ["modifyvm", :id, "--memory", server_memory]
		end
		### 指定内存
        if custom_cpu_mem == "yes"
          nodes.each do |cust_node|
            if cust_node[:node] == "node#{nid}"
              vb.customize ["modifyvm", :id, "--cpus", cust_node[:cpu]]
              vb.customize ["modifyvm", :id, "--memory", cust_node[:mem]]
            end
          end
		end
		### 指定 desktop
        if desktop == "yes"
          vb.gui = true
          vb.customize ["modifyvm", :id, "--graphicscontroller", "vboxvga"]
          vb.customize ["modifyvm", :id, "--accelerate3d", "on"]
          vb.customize ["modifyvm", :id, "--ioapic", "on"]
          vb.customize ["modifyvm", :id, "--vram", "128"]
          vb.customize ["modifyvm", :id, "--hwvirtex", "on"]
        end
      end
      node.vm.hostname = "node#{nid}"
      ### 指定网卡
      if additional_nics == "yes"
        if additional_nics_dhcp == "no"
          (1..additional_nics_num).each do |nic_num|
            nnum = Random.rand(0..50)
            node.vm.network :private_network, ip: subnet+"#{subnet_ip_start + nid + nnum}"
          end
        end
        if additional_nics_dhcp == "yes"
          (1..additional_nics_num).each do |nic_num|
            node.vm.network :private_network, type: "dhcp"
          end
        end
      end
	  ### 指定端口
	  if enable_port_forwards == "yes"
        port_forwards.each do |pf|
          if pf[:node] == "node#{nid}"
            node.vm.network "forwarded_port", guest: pf[:guest], host: pf[:host] + nid
          end
        end
	  end
	  ### 指定 ansible 初始化
      if provision_nodes == "yes"
        if node_id == N
          node.vm.provision "ansible" do |ansible|  #runs bootstrap Ansible playbook
            ansible.limit = "all"
            ansible.playbook = "bootstrap.yml"
          end
          node.vm.provision "ansible" do |ansible|  #runs Ansible playbook for installing roles/executing tasks
            ansible.limit = "all"
            ansible.playbook = "playbook.yml"
            ansible.groups = ansible_groups
          end
        end
      end
    end
  end
  if provision_nodes == "yes"
    config.vm.provision :shell, path: "bootstrap.sh", keep_color: "true"  #runs initial shell script
  end
  # 读写共享目录
  if synced_folder == "yes"
    config.vm.synced_folder ".", "/vagrant", mount_options: ["dmode=774,fmode=775"]
  end
end

网络 #

这是最让人困惑的部分,vagrant 本身就是一个脚本工具,需要具体的 vm (Virtualbox,Vmware Workstation 等)配合来实现。下面以 Virtualbox 为例,先了解 Virtualbox 网络方式后,再了解 vagrant 配置。

Virtualbox 网络模式 #

对每个虚拟机,可以配置其 Adapter 的网络模式(Not Attached 和 Generic Driver 这里不讨论):

Virtualbox network mode

各模式的特点总结如下:

网络模式VM ↔ VMVM → HostVM ← HostVM → LAN/InternetVM ← LAN/Internet
NAT(default) Port Forward Port Forward
NAT Network  Port Forward Port Forward
Bridged     
Internal Network 
Host-only   

NAT(default) #

vb-nat

这是默认模式或者通过 GUI/命令行指定,会自动产生一个默认的虚拟 NAT router(图中的 10.0.2.2),通常是虚拟机的 DHCP。

  • 特点:

    命令行例子:VBoxManage modifyvm VM_name --nic1 nat
    
    -   虚拟机可以直接访问 Internet
    -   每台虚拟机完全隔离
    
  • 缺点:

    -   虚拟机之间无法通信
    -   宿主机和虚拟机也无法通信,除非把虚拟机的端口暴露出来(port forwarding),但一个端口只能对应一个虚拟机
    

NAT Network #

vb-nat-network

和 NAT 类似,这种模式下,还会按照要求产生一个虚拟的 NAT network(图中的 10.0.2.0/24),默认的 Gateway(10.0.2.1)也会自动产生。

  • 特点:

    命令行例子:VBoxManage natnetwork add --netname natnet1 --network "192.168.22.0/24" --enable
    
    -   虚拟机可以直接访问 Internet
    -   虚拟机之间可以通信
    
  • 缺点:

    -   宿主机和虚拟机也无法通信,除非把虚拟机的端口暴露出来(port forwarding),但一个端口只能对应一个虚拟机
    

Bridged Adapter #

vb-bridge

这是默认模式,会产生一个默认的 router/gateway(例如图中的 10.0.0.2)

  • 特点:

    -   完全暴露虚拟机
    -   虚拟机和宿主机处在统一网络
    
  • 缺点:

    -   需要让网络卡工作在 Promiscuous 模式下(一个网络卡绑定不同的 MAC 地址,接收所有的 packet),不少 wireless 网络卡不支持这一模式
    -   通常公司网络都采用 DHCP,所以有可能无法指定虚拟机的 IP,或者违反公司的网络安全策略
    

Internal Network #

vb-internal

注意,上图显示的是个混合模式,VM1 配置成 NAT 模式,VM2 & VM3 只是 Internal Network 模式,但同时指定 VM1 为 gateway,这样就可以通过 VM1 连接 Internet。

  • 特点:

    -   方式简单
    -   虚拟机之间可以通信
    
  • 缺点:

    -   和外界完全隔离
    

Host-only #

vb-host

先配置多一个虚拟的 Host 网卡,让所有的 VM 和 Host 共享。

  • 特点:

    -   虚拟机之间可以通信
    -   虚拟机和宿主机之间可以通信
    
  • 缺点:

    -   和外界完全隔离
    

vagrant 网络模式 #

vagrant 定义/支持三种网络模式:

  • port forwarding(default):对应 Virtualbox 的 NAT
  • private network:对应 Virtualbox 的 Host-only 或者 Internal Network
  • public network:对应 Virtualbox 的 Bridged Network

注意的是,Vagrant 启动虚拟机时,会自动在虚拟机中添加一块 Virtualbox NAT 类型的网卡,然后再创建 Vagrantfile 配置文件所描述的网络。Vagrant 将宿主机的 TCP/2222 端口转发到 虚拟机的 TCP/22 端口,这样就可以使用 vagrant ssh 命令快速地连接虚拟机。同时,虚拟机使用这块自动创建的 NAT 网卡访问外部网络。

常用的配置情况:

外网(bridged) #

# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
config.vm.network "public_network"

此时,vm 在宿主机所在的 LAN 中等价于一台物理机器,最好在 DHCP 里通过 mac 绑定为 VM 保留一个固定的 dhcp 地址,这样 VM 无论何时启动都会获取到相同的 IP 地址,这时 VM 暴露出来,开发和调试将会很顺利很简单。

内网 + Internet(混合模式) #

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

标识符“private_network“总是被映射为 Virtualbox 的 Host-only 模型,同时 vagrant 自动创建 NAT 网卡,混合网络非常适合本地开发和测试,VM 可以通过 NAT 和 Internet 相通,然后多个 VM 之间也能相互通信。

内网(internal) #

当指定了两个以上的 private_network 的话,vagrant 不再自动创建 NAT 网卡,这时具有两个或以上的 host-only 网络。

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

也可以强制指定:

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

内外网 #

同时具有 bridged 和 host-only 网络:

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