简介
Vagrant [1]是一个开源的虚拟化开发环境管理工具,采用Ruby开发。它自身并不提供任何虚拟化能力,而是通过封装VirtualBox、QEMU、KVM等Hypervisor提供一套更加方便使用的接口。Vagrant的角色类似于剥离了底层containerd及runC的Docker客户端及守护进程,它创造了“box”的概念,每一个box都是一个打包好的Vagrant环境,类似于容器镜像,一经发布,任何人都可以拿到这个box并在自己的机器上运行。
在已安装配置好Vagrant的机器上,启动一个配置好网络的虚拟机最少只需要两行命令:
vagrant init hashicorp/bionic64
vagrant up
与传统虚拟机创建工具相比,Vagrant显然方便得多。
简洁、优雅、开箱即用、一键部署,这些也是Metarget[2]的追求和理念。接下来,Metarget将需要创建虚拟机并实现云原生靶机和靶场的自动化部署,我们打算借助Vagrant实现对虚拟机的管理,不必再带着复杂的参数调用QEMU等工具。
本文记录了Vagrant的基本用法。官方也提供了一些入门教程[3],推荐阅读。
某种程度上来说,Vagrant与Docker是在不同架构层次解决相同的问题。抛开我们在Metarget项目上的需求不谈,Vagrant本身也是一款能够提高研究、研发人员工作效率的利器,值得我们收入兵器谱,应用在后续工作中。
安装部署
最新的安装方法可参考Vagrant官方说明[4],本文记录的是2021年5月24日可用的安装步骤。
部署环境为一台内核版本为4.15.0-142-generic
的Ubuntu 18.04虚拟机(需开启硬件虚拟化支持)。
我们计划使用QEMU-KVM作为虚拟机管理器,整个安装过程包含三个步骤(未特殊说明则默认均以root权限执行),下面依次进行说明。
注意,QEMU、KVM等组件在不同版本的Ubuntu系统上的安装方式存在细节差异,具体还请参考官方文档。
TL; DR
在开启硬件虚拟化的Ubuntu 18.04系统上,执行以下命令(后面各步骤的总结)来安装部署Vagrant:
apt-get install qemu
apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
adduser `id -un` libvirt
adduser `id -un` kvm
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
apt-get update && apt-get install vagrant
apt-get build-dep vagrant ruby-libvirt
apt-get install qemu libvirt-bin ebtables dnsmasq-base
apt-get install libxslt-dev libxml2-dev libvirt-dev zlib1g-dev ruby-dev
vagrant plugin install vagrant-libvirt
1. 安装QEMU-KVM
参考QEMU[5]和KVM[6]的文档,步骤如下:
# qemu
apt-get install qemu
# kvm
apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
adduser `id -un` libvirt
adduser `id -un` kvm
# 安装完成后执行以下命令,无报错则安装成功
virsh list --all
# 确认libvirtd服务处于运行状态
systemctl status libvirtd
2. 安装Vagrant
参考Vagrant的官方说明[7],步骤如下:
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
apt-get update && apt-get install vagrant
3. 安装vagrant-libvirt插件
参考vagrant-libvirt的仓库说明[8],步骤如下:
# 安装依赖项
apt-get build-dep vagrant ruby-libvirt
apt-get install qemu libvirt-bin ebtables dnsmasq-base
apt-get install libxslt-dev libxml2-dev libvirt-dev zlib1g-dev ruby-dev
# 安装vagrant-libvirt插件
vagrant plugin install vagrant-libvirt
基本用法
本节,我们以创建Ubuntu 18.04虚拟机为例展示如何使用Vagrant管理虚拟机。大家可以将这个过程与“使用Docker创建Ubuntu 18.04容器”的过程对比,它们有一些相似的设计理念。
1. 添加Box
Vagrant创造了“box”概念,每一个box都是一个打包好的Vagrant环境,类似于容器镜像,一经发布,任何人都可以拿到这个box并在自己的机器上运行。就像创建容器镜像一样,我们当然可以创建自己的box,但是初学阶段还没有这个必要。我们直接拿别人创建好的box来创建虚拟机。
考虑到网络情况,我们最好先将要使用的box下载下来。
需要注意的是,不同box支持的底层Hypervisor(即下图中的providers)可能不同,要选择那些支持自己实际环境Hypervisor的box来使用。
首先,从Vagrant Cloud [9](类似于Docker Hub)上下载“generic/ubuntu1804”[10] box到安装好Vagrant的宿主机,假设该文件的名称是ubuntu1804.box
。我们在Vagrant中添加该box,并将其命名为ubuntu-bionic
:
vagrant box add --provider=libvirt --name ubuntu-bionic ./ubuntu1804.box
根据官方文档[11],添加后的box存放在$HOME/.vagrant.d/boxes/
目录下(仅供了解,日常使用不需要涉及)。
2. 编写Vagrantfile
Vagrant使用基于Ruby的Vagrantfile文件(是不是在Docker中见过类似的东西?)来描述一个虚拟机。
首先创建一个目录,如vagrant_getting_started
,然后在这个目录中创建一个Vagrantfile文件,内容如下:
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu-bionic"
config.vm.provider :libvirt do |libvirt|
libvirt.cpus = 2
libvirt.memory = 1024
end
end
内容非常好理解,大意是基于之前添加的ubuntu-bionic
box创建一个拥有2核CPU、1G内存的虚拟机。
事实上,Vagrantfile中可以加入许多复杂的设定,来满足具体场景的需求。对此,本篇教程不再过多展开,感兴趣的朋友可以参考官方文档[12]。
3. 启动和使用虚拟机
万事俱备,在前述vagrant_getting_started
目录下执行vagrant up
启动虚拟机。如果一切正常,过程将类似下面这样:
root@metarget-test:~/vagrant_getting_started# vagrant up
Bringing machine 'default' up with 'libvirt' provider...
==> default: Creating image (snapshot of base box volume).
==> default: Creating domain with the following settings...
==> default: -- Name: vagrant_getting_started_default
==> default: -- Domain type: kvm
==> default: -- Cpus: 2
...
==> default: -- Memory: 1024M
...
==> default: -- Base box: ubuntu-bionic
==> default: -- Storage pool: default
==> default: -- Image: /var/lib/libvirt/images/vagrant_getting_started_default.img (128G)
...
==> default: Creating shared folders metadata...
==> default: Starting domain.
==> default: Waiting for domain to get an IP address...
==> default: Waiting for SSH to become available...
default:
default: Vagrant insecure key detected. Vagrant will automatically replace
default: this with a newly generated keypair for better security.
default:
default: Inserting generated public key within guest...
default: Removing insecure key from the guest if it's present...
default: Key inserted! Disconnecting and reconnecting using new SSH key...
接着,我们可以在前述目录下执行vagrant status
查看虚拟机状态:
root@metarget-test:~/vagrant_getting_started# vagrant status
Current machine states:
default running (libvirt)
The Libvirt domain is running. To stop this machine, you can run
`vagrant halt`. To destroy the machine, you can run `vagrant destroy`.
可以看到,虚拟机已经正常运行。我们可以执行vagrant ssh
进入该虚拟机:
root@metarget-test:~/vagrant_getting_started# vagrant ssh
Last login: Mon May 24 07:56:51 2021 from 192.168.121.1
vagrant@ubuntu1804:~$ whoami
vagrant
vagrant@ubuntu1804:~$ sudo su
root@ubuntu1804:/home/vagrant# whoami
root
至此,就可以像通过SSH使用传统服务器和虚拟机一样开始工作了。
其他的一些常用操作如下:
vagrant halt
:关机vagrant destroy
:关机并销毁vagrant suspend
:暂停虚拟机vagrant resume
:恢复运行vagrant reload
:重启虚拟机并重载Vagrantfile配置
常见问题
宿主机与虚拟机之间如何共享文件?
根据官方文档[13],默认情况下虚拟机内的/vagrant
目录与宿主机上的Vagrantfile所在目录同步。然而,在笔者前述测试过程中,Vagrant并未自动帮我们配置共享文件夹。经查官方手册[14],我们可以在Vagrantfile中手动配置共享目录:
Vagrant.configure("2") do |config|
config.vm.synced_folder ".", "/vagrant"
# ...
end
如何访问虚拟机内的服务?
根据官方文档[15],Vagrant提供了三种高层次的网络配置方式:端口转发、私有网络、公有网络。
1. 端口转发
顾名思义,在宿主机与虚拟机端口之间做转发。配置十分简单:
Vagrant.configure("2") do |config|
config.vm.network "forwarded_port", guest: 2003, host: 12003
end
更多配置参数可参考官方文档[16]。
2. 私有网络
顾名思义,为虚拟机配置一个私有网络IP。方式有两种:DHCP和静态IP。
DHCP
Vagrant.configure("2") do |config|
config.vm.network "private_network", type: "dhcp"
end
在DHCP的情况下,我们事先无法知道该虚拟机的IP,只能在虚拟机创建后通过vagrant ssh
连接上去执行ifconfig
之类的命令查询获得IP。
静态IP
Vagrant.configure("2") do |config|
config.vm.network "private_network", ip: "192.168.50.4"
end
更多配置参数可参考官方文档[17]。
3. 公有网络
参考官方文档[18],这里不再介绍。
如何对虚拟机执行自动化操作?
根据官方文档[19],Vagrant允许我们在虚拟机创建后自动化执行一些操作。例如,我们可以配置命令虚拟机在启动后自动执行一个Bash脚本:
Vagrant.configure("2") do |config|
# ...
config.vm.provision :shell, path: "bootstrap.sh"
end
更多功能可参考官方文档[20]。
参考文献
- https://github.com/hashicorp/vagrant
- https://github.com/brant-ruan/metarget
- https://learn.hashicorp.com/tutorials/vagrant/getting-started-index?in=vagrant/getting-started
- https://www.vagrantup.com/downloads
- https://www.qemu.org/download/
- https://help.ubuntu.com/community/KVM/Installation
- https://www.vagrantup.com/downloads
- https://github.com/vagrant-libvirt/vagrant-libvirt#installation
- https://vagrantcloud.com/boxes/search
- https://app.vagrantup.com/generic/boxes/ubuntu1804
- https://docs-v1.vagrantup.com/v1/docs/boxes.html
- https://www.vagrantup.com/docs/vagrantfile
- https://learn.hashicorp.com/tutorials/vagrant/getting-started-synced-folders?in=vagrant/getting-started
- https://www.vagrantup.com/docs/synced-folders/basic_usage
- https://www.vagrantup.com/docs/networking
- https://www.vagrantup.com/docs/networking/forwarded_ports
- https://www.vagrantup.com/docs/networking/private_network
- https://www.vagrantup.com/docs/networking/public_network
- https://learn.hashicorp.com/tutorials/vagrant/getting-started-provisioning?in=vagrant/getting-started
- https://www.vagrantup.com/docs/provisioning