Vagrant 简介与常用操作及配置

前方许多有关于 Kafka, Docker, Python 和 Kubernates 的文章都是在 Vagrant 虚拟机中做的 Demo,经常用到的一些 Vagrant 命令是时候有必要写篇日志记录下来。Vagrant 是 HashiCorp 家族中的一员,HashiCorp 旗下著名的工具还有  Terraform, ConsulVault, Boundary, Packer, NomadWaypoint

说起 Vagrant,不得不提起与之仿佛类似的 Docker,其实它们相差还是比较大的,只因它们给人的外在感觉都是命令行控制 Linux。Vagrant 实质是一个虚拟机的外挂,让我们更方便的用 Vagrant 命令与虚拟机交互,而不用在宿主机与虚拟机间来回切换,管理多个虚拟机就更得心应手了; 而 Docker 是一个容器,容器的本质是宿主机上的一个进程,只是用命名空间与该进程的文件系统,进程,网络等进行了隔离,使得该容器进程看似一个虚拟  OS。

Vagrant 是开发环境的部署工具, 而 Docker 是运行环境的部署工具; Vagrant 操作的是一个标准的 Linux 或  Windows 操作系统,而 Docker 的镜像考虑到发布服务的个头,通常是一个裁剪的系统,去除了服务器非必要的命令。既然 Vagrant 对应的是虚拟机,那么在 Vagrant 中的操作,安装的软件在 Vagrant 退出后都会保留下来,而 Docker 中操作的都是当前容器(copy-on-write),并不影响所对应的镜像, 除非用 docker commit 固化为新的镜像.

明白了 Vagrant 只是一个虚拟机的皮,那他在不同的硬件平台或操作系统下需要与不同的 Provider, 如 VirtualBox, Hyper-V, VMware 等配合工作,还能用 Vagrant 来操作 Docker。

有了 Vagrant, 从此不再需要下载不同操作系统的 ISO 安装镜像文件,耗时的逐步安装操作系统,也不用手工的下载别人安装好并导出的虚拟机文件,一切有点类似 Docker 一样从远程公共仓库中选择系统即可。

因此在 Mac OS X 下要能使用 Vagrant 的话,我们先安装免费的 Virtualbox, 然后是 Vagrant,可用 brew 进行安装
$ brew install --cask virtualbox
$ brew install vagrant
安装好后就能使用 Vagrant 命令了,当前版本是
$ VirtualBoxVM --help
Oracle VM VirtualBox VM Runner v6.1.20 $ vagrant --version
Vagrant 2.2.15
这时候用 vagrant 的 up, halt 等子命令就可以看作是在和 VirtualBoxVM, VBoxManage 等命令通信。下面来看一下 vagrant 命令 如何创建, 启动, ssh 及关闭一个虚拟机。

Docker 有 Dockerfile, 与 Vagrant 相应的是 Vagrantfile,在同一个宿主机上我们要创立多个虚拟机的话,通常为每一个 Vagrantfile  创建一个独立的目录。比如我们想创建一个 Ubuntu 20.04 LTS 的虚拟机,首先创建一个目录 ubuntu-20.04, 然后在其下创建文件 Vagrantfile

执行 vagrant 命令时定位 Vagrantfile 文件的顺序是从当前一直上升到根目录下, 如 ./Vagrantfile, ../Vagrantfile, ../../Vagrantfile, ... 一直到  /Vagrantfile。可通过修改环境变量 VAGRANT_CWD 改变起始搜索位置。一个 Vagrantfile 文件中还能定义多个虚拟机,下面有说明。

再搜索到官方的 ubuntu 20.04 的 box(不再叫做镜像了) https://app.vagrantup.com/boxes/search?utf8=%E2%9C%93&sort=downloads&provider=virtualbox&q=ubuntu+20.04, 进到 https://app.vagrantup.com/ubuntu/boxes/focal64 页面拷贝

1Vagrant.configure("2") do |config|
2  config.vm.box = "ubuntu/focal64"
3end

为 Vagrantfile 的内容,演示前面操作的命令系列如下

~$ mkdir ubuntu-20.04 && cd ubuntu-20.04
ubuntu-20.04$ cat << EOF > Vagrantfile
Vagrant.configure("2") do |config|
    config.vm.box = "ubuntu/focal64"
end
EOF
也可用下面的 vagrant init 命令
ubuntu-20.04$ vagrant init ubuntu/focal64
达成与前面的 cat 命令的基本等价的结果,只不过 vagrant init 生成的 Vagrantfile 文件中有详细的注释说明。Vagrantfile 是一个采用了 ruby 语法的文件, 因为 Vagrant 是用 ruby 写成的。

接下来介绍的 vagrant 虚拟机的操作指令默认都是针对当前目录下的 Vagrantfile 文件所指引的虚拟机。如果想在任意目录下针对某个虚拟机进行操作,需要指定虚拟机的名称或 ID, 在 Vagrantfile 中不指定名称的话所有虚拟机的名称都是 default, ID 是不一样的。如何获得虚拟机的 Name 或  ID 可用 vagrant global-status 命令,后面会讲到。

现在开始用 up, ssh, halt 等命令来启动虚拟机,ssh 连接到虚拟,以及关闭虚拟机

下面从命令行提示符可识别出当前是在宿主机还是虚拟机中的 shell, 类似的 $ 或 ubuntu-20.04$ 是在宿主机上,vagrant@ubuntu-focal:~$ 是在虚拟机中。vagrant ssh 的用户名是 vagrant

vagrant up 启动一个虚拟机

 1ubuntu-20.04$ vagrant up
 2Bringing machine 'default' up with 'virtualbox' provider...
 3==> default: Importing base box 'ubuntu/focal64'...
 4==> default: Matching MAC address for NAT networking...
 5==> default: Checking if box 'ubuntu/focal64' version '20210415.0.0' is up to date...
 6==> default: A newer version of the box 'ubuntu/focal64' for provider 'virtualbox' is
 7==> default: available! You currently have version '20210415.0.0'. The latest is version
 8==> default: '20210429.0.0'. Run `vagrant box update` to update.
 9==> default: Setting the name of the VM: ubuntu-2004_default_1619755398798_4656
10==> default: Clearing any previously set network interfaces...
11==> default: Preparing network interfaces based on configuration...
12    default: Adapter 1: nat
13==> default: Forwarding ports...
14    default: 22 (guest) => 2222 (host) (adapter 1)
15==> default: Running 'pre-boot' VM customizations...
16==> default: Booting VM...
17==> default: Waiting for machine to boot. This may take a few minutes...
18    default: SSH address: 127.0.0.1:2222
19    default: SSH username: vagrant
20    default: SSH auth method: private key
21    default: Warning: Connection reset. Retrying...
22    default: Warning: Remote connection disconnect. Retrying...
23    default:
24    default: Vagrant insecure key detected. Vagrant will automatically replace
25    default: this with a newly generated keypair for better security.
26    default:
27    default: Inserting generated public key within guest...
28    default: Removing insecure key from the guest if it's present...
29    default: Key inserted! Disconnecting and reconnecting using new SSH key...
30==> default: Machine booted and ready!
31==> default: Checking for guest additions in VM...
32==> default: Mounting shared folders...
33    default: /vagrant => /Users/yanbin/vagrant/ubuntu-20.04

启动后直接对应到 VirtualBox 中一个虚拟机

如果创建一个新的目录,放个  Vagrantfile 文件再用 vagrant up 启动又会在 VirtualBox 中看到对应的新虚拟机。同时注意 Vagrant 所创建的虚拟机内存只有 1024 MB, 它的 Settings 中可看到其他的配置,如网络是 NAT 等,这些都可以通过 Vagrantfile 来配置,尽量不要在 VirtualBox 中直接修改虚拟机的设置。

虚拟机与宿主机间文件共享

注意到 vagrant  启动过程中显示了挂载共享目录,如上面的
1==> default: Mounting shared folders...
2    default: /vagrant => /Users/yanbin/vagrant/ubuntu-20.04

也就是在虚拟机上进到 /vagrant 目录,内容就是宿主机上 /Users/yanbin/vagrant/ubuntu-20.04 目录,也就是 Vagrantfile 文件所在的目录。在虚拟机中挂载的是可读写的文件系统,通过这个连接就能在虚拟机与宿主机之间共享文件了。

vagrant ssh 登陆虚拟机

 1ubuntu-20.04$ vagrant ssh
 2Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-72-generic x86_64)
 3
 4.......
 5
 6  System information as of Fri Apr 30 04:19:44 UTC 2021
 7
 8  System load:  0.02              Processes:               113
 9  Usage of /:   3.2% of 38.71GB   Users logged in:         1
10  Memory usage: 20%               IPv4 address for enp0s3: 10.0.2.15
11  Swap usage:   0%
12
13.......
14
15Last login: Fri Apr 30 04:06:24 2021 from 10.0.2.2
16vagrant@ubuntu-focal:~$ free
17              total        used        free      shared  buff/cache   available
18Mem:        1004584      143788      500384         940      360412      706680
19Swap:             0           0           0
20vagrant@ubuntu-focal:~$ hostname
21ubuntu-focal

此时进入了那个 VirtualBox 虚拟机 ubuntu-2004_default_1619...中,现在可以做任何 ssh 可做的事情。

vagrant halt: 关闭当前虚拟机,在 VirtualBox 中该虚拟机的状态变为 Powered Off

vagrant status: 查看当前虚拟机的状态, running, poweroff, not created(从未 up 过),saved(执行了 vagrant suspend 后的状态)

vagrant destroy 销毁虚拟机

ubuntu-20.04$ vagrant destroy
default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives..
vagrant destroy 操作会把对应的虚拟机从 VirutalBox 中删除掉。

vagrant 的命令可以用 vagrant --help 列出来, 下面列一些其他 vagrant 常用的操作

vagrant global-status -- 列出所有虚拟机的状态

1$ vagrant global-status
2id       name    provider   state    directory
3--------------------------------------------------------------------------
4cef6e15  default virtualbox poweroff /Users/yanbin/vagrant/ubuntu-18.04
5b9988a1  default virtualbox running  /Users/yanbin/vagrant/fedora30
65888413  default virtualbox running  /Users/yanbin/vagrant/ubuntu-20.04

有了以上的内容,前面的 vagrant 虚拟机相关命令就可以带上 id 或 name 作针对性操作, 当 name 不是唯一时只会作用到第一可用的虚拟上

vagrant up cef6e15 会启动 ubuntu-18.04 的虚拟机

vagrant ssh b9988a1 会 ssh 连接到 fedora30 虚拟机

有了 id 或 name, 每次 vagrant  操作就不必跑到 Vagrantfile 所在的目录去执行命令。

vagrant cloud search -- 搜索 box

除了打开 https://app.vagrantup.com/boxes/search 网页搜索外,我们也可以用命令来搜索

 1ubuntu-20.04$ vagrant cloud search fedora
 2| NAME                 | VERSION | DOWNLOADS | PROVIDERS                                              |
 3+----------------------+---------+-----------+--------------------------------------------------------+
 4| generic/fedora28     | 3.2.18  |   267,894 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
 5| generic/fedora27     | 3.2.18  |   171,412 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
 6| generic/fedora32     | 3.2.18  |   116,801 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
 7| generic/fedora33     | 3.2.18  |   114,666 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
 8| generic/fedora26     | 3.2.18  |   111,408 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
 9| generic/fedora25     | 3.2.18  |   102,329 | virtualbox, vmware_desktop, parallels, libvirt, hyperv |
10| generic/fedora29     | 3.2.18  |    88,640 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
11| generic/fedora30     | 3.2.18  |    83,148 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
12| roboxes/fedora28     | 3.2.18  |    55,764 | vmware_desktop, virtualbox, parallels, libvirt, hyperv |
13| lavabit/magma-fedora | 3.2.18  |    53,727 | vmware_desktop, virtualbox, libvirt, hyperv            |
14+----------------------+---------+-----------+--------------------------------------------------------+

会列出关键字对应的所有 box, 及版本和 providers, 本人用这个命令搜索比网页中还来得慢,倒不如在网页中找到相应的 box 再加到 Vagrantfile 中或 vagrant init, 或 vagrant box add。

vagrant box add -- 添加 box 到本地仓库

$ vagrant box add generic/fedora30 --provider virtualbox
如果没有指定 provider 会列出可用的 provider 进行选择。vagrant box add 还能从一个 url 或文件路径中添加一个 box. 在 Mac OS X 下 vagrant box add 的 box 存储在 ~/.vagrant.d/boxes 目录中
1$ ls -l ~/.vagrant.d/boxes
2total 0
3drwxr-xr-x  4 yanbin  staff  128 Apr 29 23:45 generic-VAGRANTSLASH-fedora30
4drwxr-xr-x  4 yanbin  staff  128 Apr 30 00:29 hashicorp-VAGRANTSLASH-vagrant-share
5drwxr-xr-x  4 yanbin  staff  128 Apr 29 23:53 ubuntu-VAGRANTSLASH-bionic64
6drwxr-xr-x  4 yanbin  staff  128 Apr 29 19:15 ubuntu-VAGRANTSLASH-focal64

vagrant box list -- 列出本地的 box

$ vagrant box list
generic/fedora30 (virtualbox, 3.2.18)
ubuntu/bionic64 (virtualbox, 20210415.0.0)
ubuntu/focal64 (virtualbox, 20210415.0.0)

vagrant box remove -- 移除相应的 box

$ vagrant box remove ubuntu/bionic64
Removing box 'ubuntu/bionic64' (v20210415.0.0) with provider 'virtualbox'...
当然它会从 ~/.vagrant.d/boxes 目录中清除掉。

其他的 vagrant box 命令还有

vagrant box outdated: 检查所有的 box 是否有更新

vagrant box update: 更新 box

vagrant 的 share 命令

vagrant 不能直接使用 vagrant share 命令,需要先安装 vagrant-share 插件和 ngrok 组件
$ vagrant plugin install vagrant-share
$ brew install ngrok
vagrant share --ssh, vagrant share --http 80 等命令使用起来没觉得有多大的意义。

以下是关于 Vagrantfile 配置的一些知识,包括机器名, 内存, CPU, 网络, 及端口的配置, 还能初始化虚拟机时预装软件。

查看我们用像 vagrant init ubuntu/focal64 命令生成的 Vagrantfile 文件,可以看到非常详尽的 Vagranfile 配置说明。官方说明文档在这里 https://www.vagrantup.com/docs/vagrantfile, 本人一直很喜欢 HashiCorp 的在线文档,其中数 Terraform 的在线文档查阅的最多。

配置虚拟机的 hostname

默认的话, 虚拟机的 hostname 是不太确定的,来自 box 中预设的机器名。当我们配置多个 Vagrant 虚拟机进行集群的时候,就有必要为每个虚拟机指定一个有意义的机器名,这时需要配置 Vagrantfile 如下

1Vagrant.configure("2") do |config|
2  config.vm.hostname = "k8s-master"
3  config.vm.box = "ubuntu/focal64"
4end 

然后用命令 vagrant reload 重新启动虚拟机(相当于 vagrant halt; vagrant up),再 vagrant ssh 登陆后看到 hostname 变成了 k8s-master
ubuntu-20.04$ vagrant ssh
vagrant@k8s-master:~$ hostname
k8s-master 

配置虚拟机的标识名

此处所说的虚拟机标识名和上面虚拟机的 hostname 是不同的概念,而是指在 vagrant global-status 中看到的 name
1$ vagrant global-status
2id       name    provider   state   directory
3-----------------------------------------------------------------------
4f284d36  default virtualbox running /Users/yanbin/vagrant/ubuntu-20.04
5def48b8  default virtualbox running /Users/yanbin/vagrant/fedora30

记得前面的与虚拟机操作相关的命令都可以带个参数 [id|name], 就是上面的 id 或 name,但因为 name 默认总是 default,所以只能用 id 来标识虚拟机,如
$ vagrant ssh f284d36
$ vagrant halt d3f48b8
如果我们给虚拟机一个 name, 比如 ubuntu-server-1, 那么操作时可以是
$ vagrant up ubuntu-server-1
要配置虚拟机的 name, Vagrantfile 要调用 config.vm.define 函数,并传入一个 name
1Vagrant.configure("2") do |config|
2  config.vm.define "ubuntu-server-1"
3  config.vm.box = "ubuntu/focal64"
4end

再重启虚拟机,用 vagrant global-status 看到的就是
1$ vagrant global-status
2id       name            provider   state   directory
3------------------------------------------------------------------------------
4def48b8  default         virtualbox running /Users/yanbin/vagrant/fedora30
56dc44cb  ubuntu-server-1 virtualbox running /Users/yanbin/vagrant/ubuntu-20.04

这里就能使用 name 来操作了。不过本人认为能用 id 操作就足够了,从 vagrant global-status 中的 directory 可知操作的是哪个虚拟机,不管是用 id 还是 name, 总需频繁的用 vagrant global-status 来查看所有虚拟机的 id 和 name。所以这里的 name 的实际意义并不大。

注:在更新的虚拟机 name 后,需要用 vagrant global-status --prune 清理掉垃圾条目。

配置虚拟机的名称

怎么又来了一个名称,我们这里之所以说虚拟机名称是因为......,先配置再看运行后的效果
1Vagrant.configure("2") do |config|
2  config.vm.box = "ubuntu/focal64"
3
4  config.vm.provider "virtualbox" do |vb|
5    vb.name = "ubuntu-server-x"
6  end
7end

vagrant destroy 掉再 vagrant up 重新启动,这时候配置的虚拟机名称对应到 VirtualBox 中的

ubuntu-server-x, 而不是 xxx_default 后面跟一串带纳秒的时间戳。当我们选择使用 vagrant 后大概很少会去查看 VirtualBox 中对应虚拟机长什么样了。

配置虚拟机内存和 CPU

对虚拟机内存的配置显然是很重要的,试想我们能拿默认的 1G 内存做什么开发工作?前面我们已经来到了 config.vm.provider 块的配置,对内存和 CPU 的配置也在当中进行,比如我们配置 2G 内存和 3 个 CPU
1Vagrant.configure("2") do |config|
2  config.vb.box = "ubuntu/focal64"
3
4  config.vm.provider "virtualbox" do |vb|
5    vb.memory = 2048
6    vb.cpus = 3
7  end
8end

vagrant reload 后,vagrant ssh 进到虚拟机,查看内存和 CPU
1vagrant@ubuntu-focal:~$ cat /proc/meminfo | grep MemTotal
2MemTotal:        2035020 kB
3vagrant@ubuntu-focal:~$ cat /proc/cpuinfo | grep processor
4processor   : 0
5processor   : 1
6processor   : 2

内存从默认的 1G 变成了 2G, 并且 CPU 有了 3 个。

网络的配置

Vagrant 虚拟机默认的网络配置是 NAT, 它允许虚拟机访问外部网络,并与宿主机或本地其他虚拟机之间通信,但外部无法直接访问虚拟机。我们可以配置从 DHCP 上获得 IP(它将与宿主机处于同一个网络,相当于 VirutalBox 中的 bridge 方式), 或用静态 IP 地址,这样更方便与外部机器交互。
1Vagrant.configure("2") do |config|
2  config.vm.box = "ubuntu/"
3  config.vm.network "public_network"
4# config.vm.network "private_network", type: "dhcp"  # 与上等价
5# config.vm.network "private_network", ip: "172.28.1.100" # 配置静态私有 IP
6  config.vm.network "forwarded_port", guest: 80, host: 8080
7end

再执行 vagrant reload 时就会询问桥接到哪个网络接口(只会询问一次),选择本机连接外部网络的接口就行,或者配置 public_network 时指定外部网络接口,像
1config.vm.network "public_network", bridge: "enp0s31f6"

如此 vagrant upvagrant reload 就不会提示选择外部网络接口

vagrant ssh 连接后就能看到
 1vagrant ssh
 2Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-72-generic x86_64)
 3
 4 * Documentation:  https://help.ubuntu.com
 5 * Management:     https://landscape.canonical.com
 6 * Support:        https://ubuntu.com/advantage
 7
 8  System information as of Fri Apr 30 14:06:34 UTC 2021
 9
10  System load:  0.21              Processes:               131
11  Usage of /:   3.2% of 38.71GB   Users logged in:         0
12  Memory usage: 9%                IPv4 address for enp0s3: 10.0.2.15
13  Swap usage:   0%                IPv4 address for enp0s8: 192.168.86.47

其中 192.168.86.47 是从我的 wifi 路由器上分配的 IP 地址, 而不仅仅是 NAT 上分配的 10.0.2.15 这个地址。
config.vm.network "private_network", ip: "172.28.1.100"
以上静态 IP 地址的方式会促使 VirtualBox 的 OS 上创建一个虚拟网络 172.28.1.1/24,比如 vboxnet1, 或  vboxnet2(如果  vboxnet1 已使用)。所以在虚拟机中分配的静态 IP 总是可以宿主机和该宿主机上的其他虚拟机访问。

如果在 Vagrantfile 中配置了太多的静态 IP 段会在宿主机操作系统中产生太多的 vboxnet1, vboxnet2, vboxnet3... 那样的网络设备。用 ifconfig 可以看到它们
 1$ ifconfig
 2....
 3vboxnet0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
 4    ether 0a:00:27:00:00:00
 5    inet 127.0.0.1 netmask 0xffffff00 broadcast 127.0.0.255
 6vboxnet1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
 7    ether 0a:00:27:00:00:01
 8    inet 10.0.2.1 netmask 0xffffff00 broadcast 10.0.2.255
 9vboxnet2: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500
10    ether 0a:00:27:00:00:02
11    inet 172.28.1.1 netmask 0xffffff00 broadcast 172.28.1.255

如果因为在 Vagrantfile 中配置静态 IP 产生太多的 vboxnetX 网络接口的话,需用 VBoxMange 进行清理
$ VBoxManage hostonlyif remove vboxnet1
$ VBoxManage hostonlyif remove vboxnet2
给 config.vm.network 加上 auto_config: false 参数将不再自动配置 vboxnetX,需手工创建,可避免产生一大堆垃圾,完整的配置如下
config.vm.network "private_network", ip: "172.28.1.100", auto_config: false
比如可用 VBoxManger 来创建一个网络
$ VBoxManage natnetwork add --netname myvboxnet1 --network "172.28.1.100/24" --enable --dhcp on
在 VirtualBox 中会是这样

端口转发配置

特别是配置为默认 NAT 的网络或静态 IP 时,外部机器(非宿主机或其上的其他虚拟机)无法直接访问,这时候就要用到端口转发的功能,由宿主机上一个端口指向到虚拟机内部的服务端口。
1Vagrant.configure("2") do |config|
2  config.vm.box = "ubuntu/"
3  config.vm.network "forwarded_port", guest: 80, host: 8080
4end

然后 vagrant reload, 启动的时候我们可以看到输出
1==> default: Forwarding ports...
2    default: 80 (guest) => 8080 (host) (adapter 1)
3    default: 22 (guest) => 2200 (host) (adapter 1)

或用 vagrant port 列出当前的端口映射
$ vagrant port
22 (guest) => 2200 (host)
80 (guest) => 8080 (host)
宿主机上看到由 VBoxHead1 启动的 8080 监听端口
1$ netstat -na|grep 8080
2tcp4       0      0  *.8080                 *.*                    LISTEN
3$ lsof -i :8080
4COMMAND     PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
5VBoxHeadl 33617 yanbin   20u  IPv4 0xd679f7ee9be9755d      0t0  TCP *:http-alt (LISTEN)

测试它之前,需要 vagrant ssh 进到虚拟机安装一个 apache 启动虚拟机中的 80 服务,然后访问宿主机的 8080 端口

config.vm.network "forward_port", guest: <port>, host: <port> 可以在 Vagrantfile 中出现多次用以配置多个端口映射,所以它是一个 ruby 函数调用,而赋值语名。

预安装软件(provision)

如果我们需要在虚拟机安装应用的话,在第一次 vagrant up (创建虚拟机后) 用 vagrant ssh 进行安装,而后在 vagrant destroy 了再 vagrant up 时又得相同的操作。欲避免重复安装相同软件的操作,可以使用别人已安装相应软件的 box,或者在手动安装软件后用 vagrant package --base name --output /path/to/name.box 命令保存为新的 box, --base name 中的 name 可用 VBoxManage list vms 列出来。

vagrant package 不指定 --output 的话会保存在当前目录中,文件名为 package.box, 以后想要用本地保存的 box 就可用  vagrant box add /path/to/name.box 来添加。

除以上两种办法外,就是这里要说的  provision, 先看配置
1Vagrant.configure("2") do |config|
2  config.vm.box = "ubuntu/focal64"
3
4  config.vm.provision "shell", inline: <<-SHELL
5    apt update
6    apt install -y apache2
7  SHELL
8end

这里配置的 config.vm.provision 只在第一次创建虚拟机的时候执行,以后 vagrant up 不会再执行它,所以用 vagrant destroy 后也不怕,下回用 vagrant up 时又会重新创建新的虚拟机并安装上 Apache2。当然在虚拟机已创建好后,再修改 config.vm.provision 中的内容后只单纯用 vagrant up 是不会发生作用的。

除非用  vagrant provision 对已启动的虚拟机强制执行 config.vm.provision 中的内容。或 vagrant [up|reload] --provision 启动/重载时强制执行 provision.

provision 除了可用 inline 执行脚本的方式,还能执行外部脚本文件,或调用 Ansible, Chef, puppet 等,详情请见 https://www.vagrantup.com/docs/provisioning

一个  Vagrantfile 中配置多个虚拟机

每创建一个虚拟机都新建一个 Vagrantfile 文件,还要把它们安置到不同的目录下也有点儿麻烦,特别是一组相关的虚拟机,有些配置还是共享的情况下。单文件多个虚拟机帮我们解决了这一问题,如

 1Vagrant.configure("2") do |config|
 2  config.vm.provision "shell", inline: "echo Hello"
 3  config.vm.box = "ubuntu/focal64"
 4
 5  config.vm.define "web" do |web|
 6    web.vm.box = "generic/fedora33"
 7    web.vm.hostname = "web"
 8    web.vm.provision :shell, inline: "dnf install httpd -y; systemctl start httpd.service"
 9  end
10
11  config.vm.define "db" do |db|
12    db.vm.provision :shell, inline: "apt install -y mysql-server"
13    db.vm.provider :virtualbox do |vb|
14        vb.memory = 4096
15    end
16  end
17end

然后 vagrant up 就会启动这两个虚拟机,分别显示 webdb 的启动过程, 查看状态
1$ vagrant global-status
2id       name    provider   state    directory
3----------------------------------------------------------
4b638892  web     virtualbox running  /Users/yanbin/vagrant
5d5a0845  db      virtualbox running  /Users/yanbin/vagrant

要单独控制的话加上 id 或 name, 如 vagrant up web, vagrant ssh db 等, 共同的配置用 config.vm.xxx.

不使用默认的 Vagrantfile 配置文件

前面无论是配置一个还是多个虚拟机都是在 Vagrantfile 文件中进行的,如果需要更多的 Vagrant 虚拟机的话,不得不创建多个目录来存放 Vagrantfile 文件,那些目录显得有些多余。那能不能用不同的文件名来定义 Vagrant 虚拟机呢,既然提到了,答案就是肯定的, 比如 Vagrantfile-Redis, Vagrantfile-Jenkins。这样我们就能把所有的 Vagrant 配置文件放在同一个工作目录中。

那么接下来就要告诉 Vagrant 命令使用非默认的 Vagrantfile 文件,要用到环境变量 VAGRANT_VAGRANTFILE
VAGRANT_VAGRANTFILE=Vagrantfile-redis vagrant up
或用 export VAGRANT_VAGRANTFILE=Vagrantfile-redis 给当前窗口设置好环境变量,再 vagrant up

假设 Vagrantfile-redis 文件内容为
1Vagrant.configure("2") do |config|
2  config.vm.provision "shell", inline: "echo initialized by Vagrantfile-redis"
3  config.vm.box = "ubuntu/xenial64"
4end

vagrant up 第一次启动就能看到 provision shell 的输出
test $ VAGRANT_VAGRANTFILE=Vagrantfile-redis vagrant up
......
==> default: Mounting shared folders...
default: /vagrant => /Users/yanbin/test
==> default: Running provisioner: shell...
default: Running: inline script
default: initialized by Vagrantfile-redis
vagrant global-status  看下
1test $ vagrant global-status
2id       name    provider   state   directory
3------------------------------------------------------
452e37b8  default virtualbox running /Users/yanbin/test

vagrant global-status 中只能看到 Vagrantfile-redis 所在的目录,而不知道配置文件的名称。这样做有一个好处就是同一个目录中的不同 Vagrantfile-xxx 配置的虚拟机能够共享同一个 /vagrant 目录。

ZSH 的 Vagrant 插件

Zsh 有对 Vagrant 的插件,需要在 ~/.zshrc 中加到  plugins 列表中
plugins=(git docker vagrant)
它对 vagrant 的常用命令定义了别名,如
  1. vup: vagrant up
  2. vgs: vagrant global-status
  3. vssh: vagrant ssh
  4. ......

更多命令别名请查看 https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/vagrant

所遇见的问题

  1. 没有挂载共享目录 /vagrant
    在配置文件中加了 config.vm.synced_folder ".", "/vagrant", 启动时报 "Vagrant was unable to mount VirtualBox shared folders. This is usually
    because the filesystem "vboxsf" is not available...."  错误
    解决办法,对  VirtualBox 6.1.xx 安装插件 "vagrant plugin install vagrant-vbguest",解决。或有说降级到 VirtualBox 5.1.18。
  2. 多个虚拟机只获得一个相同的 IP 地址 "10.0.2.15", 造成彼此之间不能通信,配置中加上 config.vm.network "private_network", type: "dhcp" 解决,必要时检查 VirtualBox 的网络。

链接:
  1. 超详细的 Vagrant 上手指南
  2. How to change Vagrant 'default' machine name?
永久链接 https://yanbin.blog/vagrant-intro-config-commands/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。