Vagrant多层加载Vagrantfile实现配置继承报栈溢出如何解决
问题场景
搭建多目录Vagrant集群时计划实现配置逐层继承,目标目录结构如下:
/base Vagrantfile /testvm Vagrantfile /nginx-test Vagrantfile
设计的继承逻辑:
- testvm层Vagrantfile加载base层Vagrantfile配置
- nginx-test层Vagrantfile加载testvm层Vagrantfile配置
各层配置职责: - base层:指定基础box为
bento/ubuntu-18.04,设置虚拟机默认内存1GB,配置所有节点通用的初始化规则 - testvm层:继承base配置后,覆写box为
bento/ubuntu-22.04,设置独立主机名,追加curl安装的初始化步骤 - nginx-test层:继承testvm配置后,设置自身主机名,追加nginx安装的初始化步骤
故障表现
在base、testvm目录下执行vagrant up可正常启动对应虚拟机,但进入nginx-test目录后,所有vagrant命令(包括vagrant status)都会执行失败,抛出栈溢出错误:
$ vagrant up Vagrant failed to initialize at a very early stage: There was an error loading a Vagrantfile. The file being loaded and the error message are shown below. This is usually caused by a syntax error. Path: /Users/donseiler/vagrant_test/testvm/nginx-test/Vagrantfile Line number: 5 Message: SystemStackError: stack level too deep
各层级初始配置
/base/Vagrantfile
VAGRANTFILE_API_VERSION = "2" NAME = "basevm" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # 默认使用bento/ubuntu-18.04基础镜像 config.vm.box = "bento/ubuntu-18.04" config.vm.hostname = NAME # VirtualBox默认配置 config.vm.provider :virtualbox do |vb| vb.name = NAME vb.memory = "1024" vb.gui = false end config.vm.provision "shell", inline: "DEBIAN_FRONTEND=noninteractive apt-get update" end
/base/testvm/Vagrantfile
# 加载上层目录Vagrantfile load "../Vagrantfile" # 定义主机名 NAME = "testvm" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # 覆写基础镜像为Ubuntu 22.04 config.vm.box = "bento/ubuntu-22.04" config.vm.provision "shell", inline: <<-SHELL export DEBIAN_FRONTEND=noninteractive apt-get -y install curl SHELL end
/base/testvm/nginx-test/Vagrantfile
# 加载上层目录Vagrantfile load "../Vagrantfile" # 定义主机名 NAME = "nginx-test" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.provision "shell", inline: <<-SHELL export DEBIAN_FRONTEND=noninteractive apt-get -y install nginx SHELL end
根因分析
多轮测试验证:使用绝对路径加载Vagrantfile、仅在当前执行目录的Vagrantfile中使用相对路径、或所有Vagrantfile存放在同一目录时,多层加载均可正常运行。
最终定位到核心问题:
Vagrantfile中Ruby的
load方法解析相对路径时,基准路径是执行vagrant命令时的当前工作目录(pwd),而非当前被加载的Vagrantfile自身所在目录。
当在nginx-test目录下执行命令时,nginx-test/Vagrantfile里的load "../Vagrantfile"会正确加载testvm层的Vagrantfile,但testvm层Vagrantfile中写的load "../Vagrantfile",会以nginx-test目录为基准往上一级解析,最终又加载回testvm目录下的Vagrantfile,形成无限自加载的死循环,最终触发栈溢出。
修复方案
直接写死绝对路径虽然能解决问题,但灵活性太差,只要把路径解析的基准改成当前Vagrantfile自身所在目录,就能既保留相对路径的灵活性,又彻底解决循环加载问题。
把所有层级Vagrantfile里的load "../Vagrantfile"替换为以下代码即可:
parent_vagrantfile = File.expand_path("../Vagrantfile", __FILE__) load parent_vagrantfile
其中__FILE__是Ruby内置变量,永远指向当前正在执行的Vagrantfile的绝对路径,File.expand_path会基于该文件所在目录解析相对路径,和执行vagrant命令时的工作目录完全无关,不会出现路径解析错位的问题。
内容的提问来源于stack exchange,提问作者Don Seiler

