昔我往矣

Vagrant和Vagrantfile入门

2023年02月22日

Vagrant 是由hashicorp公司(该公司还开源了Consul、Terraform等工具)开源的用于构建和管理虚拟机环境的工具,对于快速构建开发环境十分有用。Vagrant使用Vagrantfile进行虚拟机编排,和docker-compose有些类似,只不过Vagrant管理的对象是虚拟机,Vagrant支持VirtualBox、 VMware Fusion 和 Hyper-V 等许多虚拟化产品。本文基于VirtualBox做演示

对于开发人员来说,Vagrant可以快速构建剥离多余依赖的一致性环境,通过分享Vagrantfile快速创建相同的开发环境。Vagrantfile是Vagrant的核心。

实践环境

  • 系统:Ubuntu 22.04
  • Vagrant版本:Vagrant 2.3.4
  • VirtualBox版本:7.0.6

各操作系统上安装Vagrant的方法见官方链接 [Install | Vagrant | HashiCorp Developer]()

为什么要用Vagrant?

  • 对于运维工程师来说,学习新软件的第一步肯定是要尝试自行搭建一套环境,尤其是Kubernetes、Ceph等大型软件,一般需要多台机器构建分布式集群。使用Vagrantfile可以快速定制多虚拟机组成的服务器基础环境。
  • 对研发和测试工程师来说,通过干净的虚拟机环境测试,有也助于复现、排除和定位环境相关的软件问题。

Vagrant基础

Vagrant box

box的概念可以对应docker镜像,也即虚拟机镜像,box是本机全局共享的。Vagrant提供了类似docker hub的在线镜像平台:https://app.vagrantup.com ,可以选择自己需要的基础镜像。

执行vagrant box 命令可以查看和box相关的操作,常用的有如下几个子命令:

  • add:在线下载box
  • list:列出当前机器上的box
  • remove:删除box

常用的Vagrant 指令

常用的Vagrant指令如下:

vagrant init 是初始化Vagrantfile的命令,可以添加各类参数,最常见的是指定需要使用的基础镜像

vagrant up 根据当前目录下的Vagrantfile创建和启动虚拟机

vagrant destroy 销毁Vagrantfile创建的虚拟机

vagrant ssh/scp ssh相关的子命令,可以使用ssh协议登录虚拟机,scp子命令则可以在虚拟机和物理机之间拷贝文件

vagrant status/global-status:status查看当前环境下的虚拟机状态,global-status 则可以查看本机上所有的虚拟机状态。

vagrant halt 关闭虚拟机

vagrant reload重启,基于更新后Vagrantfile更新虚拟机

vagrant port 查看虚拟机和物理机端口映射

以下是基础的vagrant 指令操作

$ mkdir /tmp/vagrant
$ cd /tmp/vagrant
$ vagrant init generic/centos7 -m   # -m 的含义是最简化的Vagrantfile
$ cat Vagrantfile              # 查看 Vagrantfile中的内容
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "generic/centos7"
end

$ vagrant up --provider=virtualbox  # 默认的provider一般是virtualbox,也可以使用--provider指定

$ vagtrant status 
Current machine states:
default                   running (virtualbox)  # 新创建的虚拟机名叫default

$ vagrant port  
    22 (guest) => 2222 (host)  本机的2222端口映射到了虚拟机的22端口

$ vagrant ssh  # 登录到默认虚拟机
[vagrant@centos7 ~]$ exit

$ vagrant halt default  # 虚拟机关机
==> default: Attempting graceful shutdown of VM...
==> default: [vagrant-hostsupdater] Removing hosts on suspend

$ vagrant status 
Current machine states:
default                   poweroff (virtualbox)   # 虚拟机状态变为poweroff

The VM is powered off. To restart the VM, simply run `vagrant up`

$ vagrant destroy     # 销毁虚拟机
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Destroying VM and associated drives...

$ vagrant status 
Current machine states:

default                   not created (virtualbox)  # 状态变为 not created

以上创建的虚拟机,在virtualbox的GUI界面上也能看得见,也可以使用virtualbox对其进行管理。

到这里为止基本上就完成了Vagrant的入门,也没什么了不起的,对吧?和直接操作virtualbox也没啥差别嘛!别着急,Vagrant的精华在Vagrantfile,精彩的在后面。

定制Vagrantfile

在上一部分中,我们仅仅使用了Vagrant生成的默认Vagrantfile,Vagrantfile使用的是ruby的语法,非常灵活和强大,我也不会ruby,只能老老实实依葫芦画瓢按照需求来写。

provisioning 功能可以在vagrant up 过程中执行安装软件、运行指定命令等,用来更好的按需定制化系统。Provisioning | Vagrant | HashiCorp Developer

Vagrant 有插件机制,支持的插件列表见链接 Available-Vagrant-Plugins ,Vagrant可以对plugin执行installuninstalllistupdate等操作。

接下来安装 vagrant-hosts 插件,用于管理虚拟机的/etc/hosts文件

$ vagrant plugin install vagrant-hosts
$ vagrant plugin list
vagrant-hosts (2.9.0, global)
vagrant-scp (0.5.9, global)

以下是一个比较完整的定制化Vagrantfile,涉及了网络、磁盘、内存、CPU、系统初始化等方面的定制,基于这个Vagrantfile,可以在不会ruby语法的情况下,将Vagrantfile修改成自己需要的样子。

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

$init_script = <<-'SCRIPT'
     setenforce 0
     sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
     unlink /etc/localtime
     ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
SCRIPT

$ext_mount = <<-'SCRIPT'
  parted -s /dev/sdb mklabel gpt mkpart primary ext4 0% 100%
  mkfs.ext4  /dev/sdb1
  mkdir /data
  mount -t ext4 /dev/sdb1 /data
  echo "/dev/sdb1 /data ext4 defaults 0 0" >> /etc/fstab
SCRIPT

Vagrant.configure("2") do | config |
    config.vm.box = "generic/centos7"

    config.vm.provision "shell", inline: "systemctl stop firewalld.service"
    config.vm.provision "shell", inline: $init_script
    config.vm.provision "shell", inline: $ext_mount
    config.vm.provision "file", source: "~/.vimrc", destination: "~/.vimrc"

    config.vm.provision "hosts" do | host |
      host.add_host '192.168.56.11', ['node-1']
      host.add_host '192.168.56.12', ['node-2']
      host.add_host '192.168.56.13', ['node-3']
    end

    (1..3).each do |i|
      config.vm.define "node-#{i}" do | node |
        node.vm.hostname = "node-#{i}"
        node.vm.disk :disk, name: "data", size: "10GB"

        node.vm.provider "virtualbox" do | vb |
          vb.customize ["modifyvm", :id, "--memory", "1024"]
          vb.customize ["modifyvm", :id, "--cpus", "2"]
        end

      end
    end

    config.vm.define "node-1" do | node |
        node.vm.network "private_network", ip: "192.168.56.11"
    end

    config.vm.define "node-2" do | node |
        node.vm.network "private_network", ip: "192.168.56.12"
    end

    config.vm.define "node-3" do | node |
        node.vm.network "private_network", ip: "192.168.56.13"
    end
end

启动上面定义的Vagrantfile

$ VAGRANT_EXPERIMENTAL=disks vagrant up

这里的vagrant up命令前加了环境变量 VAGRANT_EXPERIMENTAL=disks,原因是本Vagrantfile中使用的disks相关功能还在实验阶段,如果不加环境变量,相关功能将无法正常使用。如果更新了Vagrantfile,在使用vagrant reload的时候也需要加上这个环境变量。

接下来解释下这个Vagrantfile每一部分所做的事情。

  1. 首先定义了2个变量init_scriptext_mount,分别自定义了服务器创建时需要执行的初始化操作。这种多行字符串变量的定义方法叫 Heredoc,Bash中也有类似的操作。
  2. do/end 相当于 {},也可以用{}替换 ,用于定义一段代码块。
  3. test do |x| ... end,在这种语句结构中,两个竖线中的 x 是局部变量,用于接收前面语句抛出来的值。
  4. config.vm.provision 几行,使用了shellfile这两个内置的Provisioner。在这个过程中可以定义许多主机初始化操作。
  5. config.vm.provision "hosts" 一行中,使用了前面安装的vagrant-hosts插件,为集群内所有机器都添加了/etc/hosts静态解析。
  6. (1..3).each do |i| ... end 这里是个循环,分别将1、2、3赋值给i,然后定义了node-1node-2node-3这三台主机。并定制了主机的内存、CPU资源和额外data硬盘等。
  7. node.vm.network "private_network" 这3处代码则分别定义了3个节点的额外网卡和ip。
  8. 剩余的语句基本上就是赋值和函数执行一类的,没什么好说的,依葫芦画瓢即可,遇到特殊需求再google搜索。

启动Vagrant之后,继续验证主机是否符合预期。

$ vagrant status 
Current machine states:

node-1                    running (virtualbox)
node-2                    running (virtualbox)
node-3                    running (virtualbox)

$ vagrant ssh node-3
Last login: Tue Feb 21 23:37:03 2023 from 10.0.2.2

[vagrant@node-3 ~]$ df -hT /dev/sdb1 
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb1      ext4  9.8G   37M  9.2G   1% /data

[vagrant@node-3 ~]$ ip a s eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:6d:39:57 brd ff:ff:ff:ff:ff:ff
    inet 192.168.56.13/24 brd 192.168.56.255 scope global noprefixroute eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe6d:3957/64 scope link 
       valid_lft forever preferred_lft forever

[vagrant@node-3 ~]$ nproc 
2

[vagrant@node-3 ~]$ free -m
              total        used        free      shared  buff/cache   available
Mem:            990         130         683          12         176         708
Swap:          2047           0        2047

[vagrant@node-3 ~]$ tail -3 /etc/hosts
192.168.56.11 node-1
192.168.56.12 node-2
192.168.56.13 node-3

[vagrant@node-3 ~]$ systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)

通过以上验证,可以看到主机的初始化完全符合我们的定义。

为什么要使用Vagrant?

我个人总结Vagrant有如下几点优势:

  • Vagrant十分强大,也有很丰富的插件可以选择:Available Vagrant Plugins · hashicorp/vagrant Wiki · GitHub
  • 十分灵活的配置方法,且易于分享,简化系统初始化和配置过程。
  • 虚拟机可以弥补某些测试场景下Docker的不足,比如需要特定的操作系统版本和内核。

当前暂无评论 »

添加新评论 »