【问题标题】:Override Vagrant configuration settings locally (per-dev)在本地覆盖 Vagrant 配置设置(每个开发人员)
【发布时间】:2023-03-28 20:20:01
【问题描述】:

我希望这个问题得到一般性的回答,但为了说明这一点,这里有一个用例:

我将 Vagrant 用于一个简单的 LMAP 项目。我使用独立的 Puppet 进行配置。现在,可能有一些开发人员坐在代理后面,他们需要对 VM 进行一些额外的配置。我在 Puppet 方面有一些工作:我可以将代理 IP(如果有)作为事实传递给 Vagrantfile 中的 puppet,如果已设置,Puppet 会做出相应的反应。

我遇到的唯一问题是:开发人员如何在不更改 Vagrantfile(受版本控制且必须保持 dev-environment-neutral)的情况下为其开发环境指定/覆盖此设置?

如果人们可以在一个名为例如的文件中覆盖一些 Vagrant 设置,那就太棒了。 Vagrantfile.local,我将通过 .gitignore 排除。

由于 Vagrantfile 只是 Ruby,我尝试了以下方法:

# Also load per-dev custom vagrant config
custom_vagrantfile = 'Vagrantfile.local'
load custom_vagrantfile if File.exist?(custom_vagrantfile)

文件包含基本上可以工作,但看起来在包含的文件中,我不再处于同一个 Vagrant 上下文中......

Vagrant::Config.run do |config|
  config.vm.provision :puppet do |puppet|
    puppet.facter = { "proxy" => "proxy.host:80" }
  end
end

... 还“重置”了我在主 Vagrantfile 中创建的所有其他 puppet 配置值,这让我觉得我在这里走错了方向。我应该注意,我是 Ruby 的菜鸟;)

谁能给我一个提示,甚至是一个可行的解决方案,说明如何在这里完成每个开发者的定制?

【问题讨论】:

    标签: ruby vagrant


    【解决方案1】:

    Vagrantfile 只是 Ruby,所以 YAML 是另一种选择。

    例如,在Vagrantfile我这样做:

    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    require 'yaml'
    
    settings = YAML.load_file 'vagrant.yml'
    db_ip_address = settings['db']['ip_address']
    api_ip_address = settings['api']['ip_address']
    
    Vagrant.configure("2") do |config|
      config.vm.box = "ffuenf/ubuntu-13.10-server-amd64"
      config.vm.box_url = "https://vagrantcloud.com/ffuenf/ubuntu-13.10-server-amd64/version/4/provider/virtualbox.box"
    
      config.vm.define "db" do |db|
        db.vm.synced_folder settings['db']['artifacts_dir']['host'], settings['db']['artifacts_dir']['guest']
        db.vm.network "private_network", ip: db_ip_address
        ... other stuff ...
      end
    
      config.vm.define "api" do |api|
        api.vm.synced_folder settings['api']['artifacts_dir']['host'], settings['api']['artifacts_dir']['guest']
        api.vm.network "private_network", ip: api_ip_address
        api.vm.network "forwarded_port", guest: settings['api']['forwarded_port']['guest'], host: settings['api']['forwarded_port']['host']
      end
    end
    

    然后我有一个vagrant.yml 文件(我只是编了一个名字;你可以使用任何你喜欢的名字)用于开发者特定的配置:

    db:
      ip_address: 192.168.4.14
      artifacts_dir:
        host: /Users/willie/myapp/db-scripts
        guest: /opt/myapp/db
    
    api:
      ip_address: 192.168.4.15
      forwarded_port:
        host: 9080
        guest: 8080
      artifacts_dir:
        host: /Users/willie/myapp/artifacts
        guest: /opt/myapp/api
    

    【讨论】:

    • 这似乎比所有其他选项更具可扩展性,我想知道为什么这不是答案。
    • 好吧,我在所有其他答案之后几年添加了这个。 :-)
    • 这应该是一个流浪成语。这将进入我的所有项目。
    • 我从来没有做过任何 Ruby,所以请原谅这个问题,但是有没有一种简单的方法可以将 'settings' 哈希合并到 'config' 对象中,这样如果 yml 文件反映了 vagrant config 对象,所有所需的属性会被自动覆盖吗?
    • 请为它创建一个问题。
    【解决方案2】:

    我建议使用环境变量来动态更改Vagrantfile 的行为,而无需编辑文件本身。

    举一个真实世界的例子,下面是默认情况下使用 Ubuntu 基础盒但使用环境变量定义替代 Linux 发行版的方法:

    if ENV['OPERATINGSYSTEM']
      if ENV['OPERATINGSYSTEM'].downcase == 'redhat'
        os_name = 'centos'
        config.vm.box     = 'centos'
        config.vm.box_url = 'https://dl.dropbox.com/u/7225008/Vagrant/CentOS-6.3-x86_64-minimal.box'
      else
        raise(Exception, "undefined operatingsystem: #{ENV['OPERATINGSYSTEM']}")
      end
    else
      os_name = 'precise64'
      config.vm.box     = 'precise64'
      config.vm.box_url = 'http://files.vagrantup.com/precise64.box'
    end
    

    这个例子来自https://github.com/puppetlabs/puppetlabs-openstack_dev_env

    【讨论】:

    • 我一直在使用这种方法并且喜欢它......除了我现在要管理多个不同的 AWS 机器,所有这些机器都使用来自同一台机器的不同 Vagrant 脚本进行配置......这是一个每次都很难设置正确的 ENV 设置。您是否考虑过 ENV 变量以外的其他概念?也许在管理所有设置的更高目录中读取未签入的配置文件?
    【解决方案3】:

    如果您准备定义应用于所有 vagrant 框的设置,值得注意的是,“Vagrant 实际上会加载一系列 Vagrantfile,并在运行时合并设置。” (参考https://docs.vagrantup.com/v2/vagrantfile/)

    所以我在~/.vagrant.d/Vagrantfile 中定义了以下内容,以增加我的 Vagrant 盒子的 RAM 量:

    Vagrant.configure(2) do |config|
        config.vm.provider "virtualbox" do |vb|
          vb.memory = 2048
        end
    end
    

    【讨论】:

    • 如果像答案所说的那样,您可以将设置应用于所有 vagrant 框,那么这比使用环境变量要容易得多。
    • 正如 Seth 和回答者所提到的,如果您在 .vagrant.d/Vagrantfile 中添加了配置程序或其他任何内容,这可能会产生意想不到的后果,因为它们将应用于任何由 Vagrant 在任何地方启动的 VM你的机器。使用另一个答案中提到的 yaml 解析要安全得多。
    【解决方案4】:

    这是一个想法。它可能是“丑陋的”和“错误的”,但至少它有效:)

    # file2.rb, this is your per-dev configuration file
    puts "included external file which uses outer var: #{foo}"
    
    # file1.rb, this would be your Vagrantfile
    puts 'first'
    foo = 'bar'
    
    external = File.read 'file2.rb'
    eval external
    puts 'second'
    

    让我们运行它

    $ ruby file1.rb
    first
    included external file which uses outer var: bar
    second
    

    根据您的示例,file2.rb 将仅包含 config 的用法而不定义它(config 将从外部上下文中提供)

      config.vm.provision :puppet do |puppet|
        puppet.facter = { "proxy" => "proxy.host:80" }
      end
    

    您的 Vagrant 文件可能如下所示:

    Vagrant::Config.run do |config|
      external = File.read 'Vagrantfile.local'
      eval external
    
      # proceed with general settings here
      config.vm.provision :puppet do |puppet|
        puppet.facter = { "proxy" => "proxy.host:80" }
      end
    end
    

    更新(另一种“数据驱动”方法)

    # Vagranfile.local
    config_values[:puppet][:facter][:proxy] = 'proxy.host:80'
    
    # Vargantfile
    Vagrant::Config.run do |config|
      config_values = {
        puppet: {
          facter: {
            proxy: nil
          },
          manifests_file: 'my_manifest.pp'
    
        }
      }
      external = File.read 'Vagrantfile.local'
      eval external # this should overwrite proxy config
    
      # proceed with general settings here
      config.vm.provision :puppet do |puppet|
        if config_values[:puppet][:facter][:proxy]
          puppet.facter = { "proxy" => config_values[:puppet][:facter][:proxy] } 
        end
    
        puppet.manifests_file = config_values[:puppet][:manifests_file]
      end
    end
    

    【讨论】:

    • 问题是它看起来像多个config.vm.provision :puppet... 块相互覆盖(完全)。所以不可能在主 Vagrantfile 中配置 Vagrant 的 Puppet 块的一部分,而在自定义的文件中配置另一部分。我尝试在所述块中使用 eval 。当然,这可行,但这只会允许开发人员设置 puppet 参数,这是非常具体、丑陋的(如你所说),并且如果他们想要配置的不仅仅是 puppet 块,那么它们会变得重复......
    • 您可以使用相同的架构,但让外部文件提供您稍后将在块中使用的值。我更新了答案。这会使你的主 Vagrantfile 更加混乱,但至少它应该是可配置的。
    • 有可能,也许,用一些动态技巧(send 等)来增加它。
    • 或者为了同样的目的使用环境变量呢?
    • 是的,我们的一位开发人员也有这个想法。如果没有办法让第二个文件以本机方式与主 Vagrantfile“交互”,我认为使用专用环境变量虽然不太灵活,但可能是一个不错的解决方案。你能举一个(最好是简短的)表达some.config.val = use value of env variable 'MYVAR' if set, else use the default value '123'if (env_var_set('MYVAR')) { do something with env_var('MYVAR') }的简单例子吗?也许在另一个回复中,所以我可以验证:)
    【解决方案5】:

    我相信这正是创建 Nugrant plugin 来解决的用例。它允许您的每个开发人员在 YAML 中拥有一个 .vagrantuser(这是一个 .gitignore-ed 文件)来指定自定义配置值,然后在 Vagrantfile 中轻松引用这些值。

    在您的情况下,代理开发人员的.vagrantuser 文件如下所示:

    proxy: 'proxy.host:80'
    

    你的Vagrantfile 看起来像这样(伪代码,我不太了解 ruby​​):

    Vagrant::Config.run do |config|
      config.vm.provision :puppet do |puppet|
        if config.user.has_key?('proxy')
          puppet.facter = { "proxy" => config.user.proxy }
        end
      end
    end
    

    您应该捆绑一个示例/参考 vagrantuser(即vagrantuser.example)文件,供您的开发人员复制并适应他们的环境。

    【讨论】:

    • 谢谢 - 我不知道这个插件
    【解决方案6】:

    扩展@Willie Wheeler 的回答。我的设置是:

    Root
    |-- defaults.yml
    |-- env.yml
    |-- Vagrantfile
    

    流浪文件

    # Load local env config
    require 'yaml'
    dir = File.dirname(File.expand_path(__FILE__))
    
    # defaults
    settings = YAML::load_file("#{dir}/defaults.yml")
    
    if File.exist?("#{dir}/env.yml")
        env_settings = YAML::load_file("#{dir}/env.yml")
        settings.merge!(env_settings)
    end
    ...
    # Customize the amount of memory on the VM:
        vb.memory = settings["vb"]["memory"]
    

    defaults.yml

    vb:
      memory: 1024
    

    env.yml

    vb:
      memory: 204
    

    这会将您拥有的任何默认值与您的 per-dev 配置合并。开发人员也很清楚他们可以实际更改哪些值

    【讨论】:

      【解决方案7】:

      考虑使用vagrant-proxyconf 插件。它允许为全球所有 Vagrant 虚拟机设置代理。

      另一种解决方案是在配置期间运行外部 shell 脚本。我在Vagrantfile 开头使用单独的config.vm.provision 部分来做到这一点:

      # reset: true below is needed to reset the connection to the VM so that new
      # environment variables set in /etc/environment will be picked up in next
      # provisioning steps
      config.vm.provision "shell", reset: true, inline: <<-SHELL
      
        if [ -f /vagrant/Vagrantfile-settings.sh ]
        then
          /vagrant/Vagrantfile-settings.sh
        fi
      SHELL
      

      然后只需在Vagrantfile 旁边放一个Vagrantfile-settings.sh 文件,将其添加到.gitignore(或其他)并在其中放入任何脚本,例如为交互式终端、所有守护进程和docker 容器设置代理:

      # Proxy for interactive terminals
      echo "http_proxy=http://PROXY_ADDRESS:PROXY_PORT" >> /etc/environment
      echo "https_proxy=http://PROXY_ADDRESS:PROXY_PORT" >> /etc/environment
      echo "no_proxy=127.0.0.1,localhost" >> /etc/environment
      # Proxy for daemons (e.g. Docker deamon - used to pull images, apt - run from default daily cron job)
      mkdir /etc/systemd/system.conf.d
      echo [Manager] > /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "DefaultEnvironment=\"http_proxy=PROXY_ADDRESS:PROXY_PORT\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "DefaultEnvironment=\"https_proxy=PROXY_ADDRESS:PROXY_PORT\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "DefaultEnvironment=\"no_proxy=127.0.0.1,localhost\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "# Docker requires upper-case http proxy environment variables..." >> /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "DefaultEnvironment=\"HTTP_PROXY=http://PROXY_ADDRESS:PROXY_PORT2\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "DefaultEnvironment=\"HTTPS_PROXY=http://PROXY_ADDRESS:PROXY_PORT\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
      echo "DefaultEnvironment=\"NO_PROXY=127.0.0.1,localhost\"" >> /etc/systemd/system.conf.d/01-http-proxy.conf
      # Proxy for docker containers started with `docker run`
      mkdir /home/vagrant/.docker
      cat <<EOF > /home/vagrant/.docker/config.json
      {
        "proxies": {
          "default": {
            "httpProxy": "http:/PROXY_ADDRESS:PROXY_PORT",
            "httpsProxy": "http://PROXY_ADDRESS:PROXY_PORT",
            "noProxy": "127.0.0.1,localhost"
          }
        }
      }
      EOF
      chown -R vagrant:vagrant /home/vagrant/.docker
      

      【讨论】:

        【解决方案8】:

        您可以从 YAML 文件加载设置。这在Drupal VM 中进行了演示,如下所示:

        # Use config.yml for basic VM configuration.
        require 'yaml'
        dir = File.dirname(File.expand_path(__FILE__))
        if !File.exist?("#{dir}/config.yml")
          raise 'Configuration file not found! Please copy example.config.yml to config.yml and try again.'
        end
        vconfig = YAML::load_file("#{dir}/config.yml")
        

        那么你就可以像这样创建config.yml

        vagrant_box: geerlingguy/ubuntu1404
        vagrant_user: vagrant
        vagrant_ip: 192.168.88.88
        

        Vagrantfile 中,您可以将变量用作:

        config.vm.box = vconfig['vagrant_box']
        config.vm.network "private_network", ip: vconfig['vagrant_ip']
        

        【讨论】:

          猜你喜欢
          • 2021-12-16
          • 1970-01-01
          • 1970-01-01
          • 2011-02-27
          • 1970-01-01
          • 1970-01-01
          • 2011-09-24
          • 2015-07-16
          • 2022-10-14
          相关资源
          最近更新 更多