Vagrant 使用中碰到的一个奇葩问题

碰到一个很无厘头的问题,跟大家分享一下。

今天公司突然断电,电脑重启之后 vagrant up 无法启动,报错:

C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/io.rb:32:in `encode': incomplete "\xB0" on GBK (Encoding::InvalidByteSequenceError)
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/io.rb:32:in `read_until_block'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/subprocess.rb:163:in `block in execute'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/subprocess.rb:161:in `each'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/subprocess.rb:161:in `execute'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/subprocess.rb:22:in `execute'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/base.rb:429:in `block in raw'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/busy.rb:19:in `busy'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/base.rb:428:in `raw'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/base.rb:367:in `block in execute'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/retryable.rb:17:in `retryable'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/base.rb:362:in `execute'
        from C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/version_5_1.rb:551:in `read_state'
 ...

看到这个错误有点懵逼了,从来没见过,而且网上根本搜不到类似的信息:incomplete "\xB0" on GBK (Encoding::InvalidByteSequenceError)\xB0 这个编码貌似是一个温度符号「°」的编码,而且从内容上看应该是哪里设置出问题了,这个字符 ruby 无法处理。

刚好前两天自己手动装了一个 virtual box 的桌面版 Ubuntu,想学学 c,还以为是安装的新系统出问题了,于是把那个系统所有的镜像配置文件都删掉,重新启动,故障依旧。

于是老老实实看错误信息,一个一个查。

首先看看 C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/lib/vagrant/util/io.rb 这个文件的 32 行:

data << io.readpartial(READ_CHUNK_SIZE).encode("UTF-8", Encoding.default_external)

这个是案发第一现场,io.readpartial 中的 IO 类是 ruby 中处理所有基本输入输出的一个类,readpartial 这个方法被设计来用来获取一个输入的「流」,并且要求提供最大的长度,在 io.rb 中这个值被设为 4096

到这步的基本处理过程应该是 vagrant 获取某个数据,然后再转码成 "UTF-8",之后再继续处理。所以我的目标是找出获取什么数据的时候出错的。

继续往上查看方法的调用过程(也就是消息的传递过程), C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/base.rb:367 这个位置是一个 def execute(*command, &block) 方法,从注释可以知道,这个方法是解析一个命令并且返回输出:

Execute the given subcommand for VBoxManage and return the output.

再往上追溯,来到 C:/HashiCorp/Vagrant/embedded/gems/gems/vagrant-1.9.1/plugins/providers/virtualbox/driver/version_5_1.rb:551read_state 方法,这个方法通过 VBoxManage.exe 来获取虚拟机的状态:

def read_state
  output = execute("showvminfo", @uuid, "--machinereadable", retryable: true)
  if output =~ /^name="<inaccessible>"$/
    return :inaccessible
  elsif output =~ /^VMState="(.+?)"$/
    return $1.to_sym
  end

  nil
end

到这里才发现是运行这个命令的时候出错了,于是手动在命令行中运行这个命令:

~\Oracle\VirtualBox\VBoxManage.exe showvminfo 1a0185d1-3d4d-4b05-ab79-0a90808882a9 --machinereadable

发现输出没有任何问题,根本没有什么特殊的字符串:

name="homestead-7"
groups="/"
ostype="Ubuntu (64-bit)"
UUID="1a0185d1-3d4d-4b05-ab79-0a90808882a9"

...

SnapshotUUID-1-1-1="53726207-4a4d-48ea-8db9-8d17eb8f7362"
SnapshotName-1-1-1-1="安装vim"
SnapshotUUID-1-1-1-1="a17c7d39-f032-4696-b06c-5ad85c36a15b"
CurrentSnapshotName="安装vim"
CurrentSnapshotUUID="a17c7d39-f032-4696-b06c-5ad85c36a15b"
CurrentSnapshotNode="SnapshotName-1-1-1-1"

中间省略了不少,有兴趣的可以自己试试看,VBoxManage.exe 在 virtual box 的安装目录下。

这个时候我又蒙了,根本没有「°」这个符号。

突然想到了之前的 io.readpartial 方法,这个方法的参数是一个数字,相当于到了获取到这么多数据的时候会做一个截断。会不会把某些中文刚好给截断了?从上面的信息可以看到,最近刚好做了一个镜像备份,备份的名称为 「安装vim」。

我去查了下,「安装」的 GBK 编码是 B0B2 D7B0,这就是错误信息中 \xB0 的来源了,而且是「装」引起的,因为我把名称改为 「安vim」,可以正常启动。

坑爹,于是把备份的名称改成英文,正常启动。

这可能也跟 ruby 的 encode 方法有关:
io.readpartial(READ_CHUNK_SIZE).encode("UTF-8", Encoding.default_external),在这里 vagrant 使用了两个参数,而中文可能被当成 GBK 编码来处理了。

这一路上的坑真的是太多了,一不小心就会掉进去,小伙伴如果碰到这种情况,可以优先考虑看看有没有可能是这个原因。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7
leo

人生苦短,我用mac

7年前 评论

@leo 公司主要 .net 开发 :laughing:

7年前 评论
Summer

所有有些习惯是非常好的,例如:不要使用中文名称命名、文件夹使用英文命名并且不包含特殊字符。 :smile:

7年前 评论

@Summer 我一般都会比较注意,万万没想到那个也是一个坑 :sweat_smile:

7年前 评论

妈蛋中枪了,直接用vb拍了个快照就不能启动了,vb默认快照名称是 “备份 + 数字”

6年前 评论

兄弟!~ 你牛掰。我也遇到了同样的问题,快郁闷死了。真的把备份的镜像的名称里面【安装】的【装】去掉,重启虚拟机,就真的好了。感谢作者的分享,真的解决了问题。
我是电脑突然蓝屏了,再打开就遇到了和作者一样的错误。错误信息一模一样。
唯一不一样的是,我能力不足,惭愧。

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!