在新Mac M1上的Vagrant下运行虚拟机

4,190 阅读10分钟

在新Mac M1上的Vagrant下运行虚拟机

当VirtualBox不再工作时,找到一个可以工作的虚拟机替代品

如果你看到这篇文章,你可能最近已经升级到一个闪亮的新Macbook Pro或类似产品,运行新的M1处理器。

在你按下 "立即购买 "按钮之前,可能没有人告诉你的是,你用VagrantVirtualbox精心打造的所有现有的x86-64机器将不再运行!为什么?

为什么它不能运行?

你的虚拟机不再运行的主要原因是,VirtualBox在你的Mac上作为管理程序运行,依靠底层处理器来执行指令,而不是通过软件提供处理器模拟。

这一点很重要,因为旧的英特尔版本的Mac能够直接在底层硬件上运行x86-64版本的你喜欢的应用程序。新的M1是一个完全不同的芯片组(ARM),由于VirtualBox不是一个CPU模拟器,它不可能为你运行那些现有的图像。

远离VirtualBox

在2021年底深入研究了这个问题后,我觉得可行的选择相对较少。

让机器重新运行的 "最快 "选择是将我的VirtualBox .vdi文件迁移到一个开放的格式,如.qcow2,并在一个真正的模拟器(如UTM)下运行它们。

如果你需要以最小的改动快速启动你的虚拟机,这可能是你最好的选择。这也可能是Windows虚拟机的唯一真正选择,因为目前还没有可以合法购买到在ARM上运行的商业版本的Windows(尚未)。

有其他有用的文章在讨论这个特殊的解决方案,但在安装UTM后,你可能要找的命令是这个。

qemu-img convert -f vdi -O qcow2 myvirtualbox.vdi myreplacementvm.qcow2

然后你可以连接新的.qcow2磁盘并启动。

你为什么不这样做呢?

它很慢--比运行Hypervisor慢得多。这是一个了不起的工程,我真的很感谢那些建造它的人,但即使在我的有32Gb内存的Macbook Pro上,模拟的Windows运行起来也很慢。

对我来说更重要的是,Vagrant不支持UTM。

所以又回到了绘图板...

对Windows应用程序的一个附带说明

我的Windows虚拟机只是为了运行Mac上没有的Windows应用程序,当Wine在Mac OS上放弃32位模拟后停止运行时,我转而运行一个完整的虚拟机。

从那时起,时间已经过去了,Codeweavers的奇迹工作者有一个叫做Crossover的产品,在M1 Mac上运行得很好。

显然,它仍然依赖于苹果自己的Rosetta 2,但我的Windows应用程序在我的本地Mac环境中运行得很快,而且完美无缺。如果你只是需要运行一个Windows应用程序,这是你花的最好的59美元。但我想说的是...

获得对Vagrant的支持

我遇到的最大障碍是让Vagrant支持我所运行的虚拟机。我在CentOS虚拟机下运行了许多开发环境,Vagrant和VirtualBox的组合使得这些环境可以轻而易举地按需旋转,共享盒子,并以其他方式让我的Mac主机不受每个项目的影响。

转移到M1后,所有这些都停止了工作。

尽管还处于早期阶段,但我们看到其他虚拟化供应商,如VMWare和Parallels正在开发可在M1上运行的x86模拟。在撰写本文时,它们仍处于早期开发阶段,要么缺乏你所期望的VirtualBox的支持和功能,要么不被Vagrant作为供应商支持。

我对这些的主要关注,特别是在UTM下运行Windows之后,是速度。仿真在可执行文件和主机操作系统之间增加了一个复杂的层,在使用时可能会有一些速度上的牺牲。

根据你的使用情况,这可能是可以忽略不计或可以接受的,但正如你将进一步看到的,我个人在使用仿真时遇到了明显的性能问题。

介绍一下Docker

好了,我说了。

Docker是一个了不起的产品,但它与工程师之间似乎有点爱恨交加。对于这个项目来说,虽然它是一个不错的选择,特别是如果你以我下面描述的方式来配置它。

它当然不是完美的。它不像VirtualBox那样多功能(例如为你提供主机可访问的虚拟网络),它不是为在一个容器下运行整个操作系统而设计的,而且它比使用VirtualBox作为提供者更加 "麻烦"。

但我得到了它的工作。

Docker作为一个x86-64仿真器

你知道Docker可以在Mac的M1上运行一个x86-64的镜像吗?通过QEMU的魔力,你可以启动基于linux/x86-64的Docker容器,QEMU会对其进行仿真。

只要在Docker文件中这样指定就可以了。

FROM — platform=linux/x86–64 centos:latest

很好,是吗?

嗯,是的,也不是。

虽然它启动了,但随后所有的东西都被模拟了,包括你启动的子进程。

看看我们启动它时发生了什么。

警告:请求的镜像的平台(linux/amd64)与检测到的主机平台(linux/arm64/v8)不匹配,没有请求特定的平台。

而当我们登录时,看看已经启动的进程。

CentOS在M1(ARM)处理器上模拟运行x86_64进程

注意到每个子进程都在qemu-x86_64下运行?

非常聪明,但也比较耗费资源。

然而,它确实起作用了请记住,我们是在arm64处理器上运行x86进程。

在这个模型上更进一步,我安装了nginxphp-fpm 来测试我的一个开发环境。我将在文章后面解释我是如何在一个容器上运行它们的,但这次试验的重要收获是执行速度。

对于一个通常在200毫秒内启动的应用程序,每个PHP页面请求的启动和运行平均需要2秒。

在隔离了网络和文件系统活动之后,很明显,PHP脚本的执行是主要的瓶颈。

这使我得出结论,如果可能的话,应该避免仿真。

那么有什么选择呢?

我的目标是能够在我的M1 Mac上找到一个由Vagrant管理的运行CentOS(或类似系统)的VirtualBox的可行替代品。

为了提高速度,我需要运行一个本地ARM镜像。

为了方便,该容器必须 "感觉 "像一个完整的虚拟机。

为了完整起见,我需要能够在Vagrant下管理这一切。

解决方案是在Docker下运行一个兼容操作系统的ARM Docker镜像。

拯救Fedora

我从一个Docker CentOS 8.5容器开始了这部分项目。如果不是因为我需要一个高于7.2的PHP支持版本,我可能会坚持使用它。

如果你的依赖性已经到位,你可以把以下内容换成你选择的systemd 操作系统。Fedora 35支持PHP 8.0,这对我的开发项目来说是完美的。

运行Docker单一容器

Docker被设计用来运行多个容器,每个容器都运行自己的单一的、独立的服务(例如,一个用于nginx ,一个用于php-fpm 等)。然而,正如我所说的,我想让它 "感觉 "像一个虚拟机。

我的解决方案是将Fedora 35作为一个单一的容器启动。

对大多数人来说,第一个 "麻烦 "是没有systemd可用,因为你不希望同时运行多个服务。另外,需要从主机上获得安全考虑的权限(--特权模式),并且很难或几乎不可能将客人的Docker容器转移到有效运行。

有各种方法可以解决这个问题,在以前的项目中,我曾使用'监督者'来启动多个进程。

然而,为了保持'虚拟机'的氛围,这次我用最优秀的github.com/gdraheim/do…,换掉了/usr/bin/systemctl

我将让你按照链接阅读文件,但第一段说明了一切。

This script may be used to overwrite “/usr/bin/systemctl”.It will execute the systemctl commands without SystemD!

那是什么意思?好吧,这意味着你可以使用类似的命令。

systemctl run nginx

但同样重要的是,这意味着你可以把你的容器的启动过程(PID 1)换成一个systemd

在你的Docker文件中,你只需要从上面的repo中复制相关的文件,并将其作为启动命令,比如下面这样。

RUN yum -y install python3COPY src/docker-systemctl-replacement/files/docker/systemctl3.py /usr/bin/systemctlRUN chmod 755 /usr/bin/systemctl

假设你把你的源文件和repos放在一个叫做src 的目录中,这样做的目的是。

  • 安装 python3
  • systemctl 的容器副本覆盖为systemctl3.py
  • 使得替代的可执行文件

就这样了!现在在启动时,你有一个完整的systemd 容器在运行。

把它放在一起

下面的Docker文件将启动一个 "熟悉的 "M1兼容、ARM64版本的Fedora 35(或更高版本)。

重要说明

我假设从现在开始你已经安装了Docker和Vagrant(Homebrew在这方面很不错)。

brew install --cask dockerbrew install vagrant

在你开始之前,先创建一个名为src 的子目录,并把gdraheim/docker-systemctl-replacement 拉到它那里。

mkdir srccd srcgit clone https://github.com/gdraheim/docker-systemctl-replacement.gitcd ..

然后编辑你的Docker文件,看起来像这样。

FROM fedora:latestENV container docker

现在让我们快速构建它并检查是否有错误。

docker build --rm -t mynewvm .

假设它是干净的,你现在可以在Docker下运行这台机器,但让我们向前跳,通过Vagrant启动它。

将Vagrant添加到组合中

在这一点上,你应该有一个完整的Docker容器,但在未来,我们可以让Vagrant同时为你构建和启动容器。

在与你的Docker文件相同的目录下工作,运行。

vagrant init

现在编辑Vagrant文件,使其看起来像下面这样。

Vagrant.configure("2") do |config|  config.vm.define "default",primary: true do |master|    config.vm.network "private_network", ip: "192.168.21.100"    config.vm.network "forwarded_port", id: "ssh", host: 2222, guest: 22    config.vm.network "forwarded_port", id: "nginx", host: 8080, guest: 80  end  config.vm.provider "docker" do |d, override|    d.build_dir = "."    d.remains_running = true    d.has_ssh = true  endend

这个文件是一个最基本的例子,但可以。

  • 定义一个 "默认 "容器
  • 添加一个192.168.21.100的容器IP
  • 将主机端口2222映射到容器端口22,用于SSH访问
  • 将主机8080端口映射到容器80端口,用于HTTP访问(这包括一个nginx演示)
  • 告诉Vagrant使用同一目录下的Docker文件来构建docker容器。
  • 该容器应保持运行
  • Vagrant应该安装一个SSH密钥

然后运行。

vagrant up

一段时间后,你的Docker容器(或虚拟虚拟机!)应成功启动。

你可以在docker下检查它。

docker ps

当回到命令提示符时,运行。

vagrant ssh

这应该会让你登录到你新启动的机器。

快速查看一下,也可以确认nginx 已经启动并运行。

通过ps -ax查看你在systemd下运行的进程。

从你的主机操作系统(你的电脑),你也应该能够从8080端口获取默认的nginx 着陆页。

http://localhost:8080

nginx通过端口转发运行在8080端口上

这就是了!

现在你可以安装和添加额外的服务,并在systemd 下正常运行它们。

我希望你觉得这篇文章有用。如果有足够的兴趣,我将把上述文件放入一个公共的git仓库,以便于参考。

祝您好运!