问题 / 解释
我确定我要添加的虚拟机镜像没有损坏,因为我在 Mac 电脑上使用同样的虚拟机镜像时它能正常工作...
通过以下步骤,我已经多次复现了这个问题:
在 macOS(任何基于 Intel 的 Mac,如 Mojave、Catalina、Mojave、Big Sur 等)中创建 Virtualbox VM。
vagrant package --base $VIRTUALBOX_VM_NAME_HERE --output test.box
将 .box
文件传输到 Ubuntu。
vagrant box add --provider virtualbox --name $VAGRANT_BOX_NAME_HERE test.box
解压错误:
The box failed to unpackage properly. Please verify that the box
file you're trying to add is not corrupted and that enough disk space
is available and then try again.
The output from attempting to unpackage (if any):
x ./include/
x ./include/README.md
x ./include/_Vagrantfile
x ./include/LICENSE
x ./metadata.json
x ./box.ovf
x ./Vagrantfile
x ./info.json
x ./box-disk001.vmdk: gzip decompression failed
bsdtar: Error exit delayed from previous errors.
答案在错误消息背后隐藏的上下文中:
bsdtar: Error exit delayed from previous errors.*
请注意,这个vagrant box add ...
是在Ubuntu上运行的,而且正在尝试使用bsd
tar
来解压缩,而不是通常的tar
命令。该.box
文件是在macOS上创建的,并使用其自己的版本的bsdtar
进行压缩。通常,在Linux上本地的.tar
文件是使用GNU版本的tar
创建的。bsdtar、GNU tar
和Linux版本libarchive
bsdtar
之间有一些重要的区别。
这意味着我们不能指望在一个平台上创建一个
.tar
文件,并且能够在另一个平台上解压缩它,除非有适当兼容的版本的
tar
可以处理该文件。该文件并非损坏,只是与 Linux 版本的
bsdtar
期望处理的格式不同。
有人可能会认为
vagrant package
旨在创建可移植的
.box
文件。然而,由于经典
bsdtar
、
libarchive
bsdtar
和GNU
tar
之间的差异,我们遇到了一个不可移植的问题。虚拟机磁盘文件通常使用稀疏文件实现。VirtualBox使用稀疏文件格式
.vmdk
来支持在Guest VM动态增长和缩小磁盘的同时,在主机操作系统上仍保持相对较小。根据您的版本,
bsdtar
处理这些稀疏文件的方式与其他版本的
tar
不同。Vagrant使用
bsdtar
,但这并不能确保生成的
.box
文件在各个系统上都是兼容和完全可移植的。MacOS附带的
bsdtar
版本与Ubuntu Linux当前可用的版本不同。
macOS Big Sur
$ sw_vers
ProductName: macOS
ProductVersion: 11.6.1
BuildVersion: 20G224
$ ls -l /usr/bin/tar
lrwxr-xr-x 1 root wheel 6 Jan 1 2020 /usr/bin/tar -> bsdtar
$ bsdtar --version
bsdtar 3.3.2 - libarchive 3.3.2 zlib/1.2.11 liblzma/5.0.5 bz2lib/1.0.6
乌班图弗科尔福萨
$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 20.04.3 LTS
Release: 20.04
Codename: focal
$ tar --version
tar (GNU tar) 1.30
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by John Gilmore and Jay Fenlason.
$ apt-cache show libarchive-tools | grep 'Description-en'
Description-en: FreeBSD implementations of 'tar' and 'cpio' and other archive tools
$ sudo apt-get install libarchive-tools
$ bsdtar --version
bsdtar 3.4.0 - libarchive 3.4.0 zlib/1.2.11 liblzma/5.2.4 bz2lib/1.0.8 liblz4/1.9.2 libzstd/1.4.4
解决方案
我发现一个解决此问题的方法是避免在 macOS 上使用由 vagrant package
创建的 .box
文件。除非您还要导入到另一台 macOS 机器,否则这个 .box
文件将无法工作,因为存在 bsdtar
问题。相反,从 $HOME/.vagrant.d/boxes/$VAGRANT_BOX_NAME_HERE
中单独复制文件。这些是裸的 VirtualBox .ovf
和 .vmdk
文件,应该与 Linux 上的 VirtualBox 兼容。一旦这些文件通过您喜欢的任何方法安全地传输到 Linux,它们就可以被加载到那里的 VirtualBox 中,或者通过 vagrant
命令直接从 .vagrant.d/boxes
使用。
您有几种选项来传输文件。我建议使用 rsync
来避免 tar
和 bsdtar
的问题。
rsync --progress -av ~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name target-machine.local:~/.vagrant.d/boxes/
rsync --progress -av your-mbp.local:~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name ~/.vagrant.d/boxes/
注意:/
斜杠在rsync
源路径和目标路径中很重要!查看这个简短的rsync
教程以获取使用rsync
的帮助
文件传输完成后,请确保检查它们是否相同。我使用shasum -a 256
为文件生成SHA256哈希值。两边的哈希值应该匹配。
cd ~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name/0/virtualbox/
shasum -a 256 box-disk001.vmdk
( cd ~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name/0/virtualbox/ && shasum -a 256 box-disk001.vmdk ) | ssh exampleuser@your-mbp.local '( cd ~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name/0/virtualbox/ && shasum -a 256 -c - )'
如果你真的想尝试使用[bsd]tar
或.box
文件...
另一个选择是使用Homebrew中的gnu-tar
将文件重新打包为兼容的.tar
存档。然后将其复制并解压缩到.vagrant.d/boxes
中。我尝试过这种方法,发现在Linux端仍会产生不同的.vmdk
磁盘文件(可以使用sha256sum
或perl的shasum -a 256
在两台机器上检查)。
另一种选择是在Ubuntu机器上使用libarchive-tools
软件包中的bsdtar
。我尝试过这种方法,并能够重现来自vagrant box add
的相同错误。
bsdtar -v -t -f /path/to/Vagrant_Box/example.box
mkdir ~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name
cd ~/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name
bsdtar -v -x -f /path/to/Vagrant_Box/example.box
x ./include/
x ./include/README.md
x ./include/_Vagrantfile
x ./include/LICENSE
x ./metadata.json
x ./box.ovf
x ./Vagrantfile
x ./info.json
x ./box-disk001.vmdk: gzip decompression failed
bsdtar: Error exit delayed from previous errors.
因此,最好避免在这些平台上使用tar
和bsdtar
,因为它们对box-disk001.vmdk
稀疏文件的处理方式不兼容。
Vagrant Box格式内容
正如我们在上面看到的那样,一个示例.box
解压缩结构可能是以下内容:
/Users/exampleuser/.vagrant.d/boxes/someuser-VAGRANTSLASH-vagrant-box-example-name/
└── 0
└── virtualbox
├── Vagrantfile
├── box-disk001.vmdk
├── box.ovf
├── include
│ ├── LICENSE
│ ├── README.md
│ └── _Vagrantfile
├── info.json
└── metadata.json
3 directories, 8 files
注意:info.json
文件和include
目录下的任何内容(例如:README.md
,_Vagrantfile
)都是可选的。这些可以通过vagrant package
选项添加:--vagrantfile
,--info
和--include
。它们不是运行虚拟机所必需的。
这些文件可以按照相同的结构放入Ubuntu机器的~/.vagrant.d/boxes/
文件夹中。确保盒子目录名称与您打算使用的盒子名称匹配(例如:目录someuser-VAGRANTSLASH-vagrant-box-example-name
= 盒子名称someuser/vagrant-box-example-name
)。
设置一个Vagrantfile
来使用盒子名称运行该盒子,然后像往常一样运行vagrant up
。
示例:
Vagrant.configure("2") do |c|
c.berkshelf.enabled = false if Vagrant.has_plugin?("vagrant-berkshelf")
c.vm.box = "someuser/vagrant-box-example-name"
c.vm.hostname = "default-vagrant-box-example-name.vagrantup.com"
c.vm.boot_timeout = 1200
c.vm.synced_folder ".", "/vagrant", disabled: true
c.vm.provider :virtualbox do |p|
p.name = "vagrant-box-example-name"
p.customize ["modifyvm", :id, "--audio", "none"]
end
end
macOS虚拟机笔记
如果涉及的.box
文件是一个macOS虚拟机,那么你可能仍然不能轻松地在Linux上运行它。原因是苹果的boot.efi
具有macOS特定的引导固件。从技术上讲,在非苹果硬件上运行macOS是不受支持的(你知道的...因为资本主义...)。我相信某个地方肯定有人已经想出了解决方法,因为创建硬件Hackintosh机器是可能的。我还没有找到一种方法来使其工作,但也许其他人知道...