为什么这个 Vagrant / Ansible Ruby 循环执行了两次并且不考虑变量更改?

Why is this Vagrant / Ansible Ruby loop executed twice and variable changes not respected?

我是 Vagrant / Ansible / Ruby 的新手,我不知道为什么内部循环似乎被执行了两次以及为什么外部循环不考虑对变量所做的更改内循环。

我想要一个动态清单文件,我想将一些配置值放入由 Vagrant 加载的配置文件中,它将为我创建它。我想定义一些具有相似结构的组,这些组将定义每个组中有多少主机由 Vagrant 创建。

---
# vagrant.yml  
general:
  base_ip: "192.168.2"
  vm_box: "ubuntu/trusty64"

virtualbox:
  vb_gui: true
  vb_memory: 2048
  vb_cpus: 1

groupA:
  num_nodes: 1
  instance_name_prefix: "groupA"
  base_ip_offset: 50

groupB:
  num_nodes: 0
  instance_name_prefix: "groupB"
  base_ip_offset: 60

我想遍历每个组并使用 Vagrant 在该组中创建 num_nodes 个实例。这是我的 Vagrantfile:

# -*- mode: ruby -*-
# vi: set ft=ruby :

require 'yaml'
settings = YAML.load_file 'vagrant.yml'

Vagrant.configure(2) do |config|
  config.vm.box = settings['general']['vm_box']

  ansible_groups = [
    "groupA",
    "groupB"
  ]

  # this counter should just represent which machine we are configuring out of the total
  # population of all machines across all groups
  # i.e. it should be monotonically increasing from 0..totalNumberOfNodes 
  machine_counter = 0

  ansible_groups.each do |group|
    num_nodes = settings[group]['num_nodes'].to_i
    (1..num_nodes).each do |i|
      config.vm.define vm_name = "%s-%02d" % [settings[group]['instance_name_prefix'], i]
      config.vm.hostname = vm_name

      config.vm.provider :virtualbox do |vb|
        vb.gui = settings['virtualbox']['vb_gui']
        vb.memory = settings['virtualbox']['vb_memory']
        vb.cpus = settings['virtualbox']['vb_cpus']
      end

      ip = "#{settings['general']['base_ip']}.#{settings[group]['base_ip_offset'].to_i + i}"
      config.vm.network :private_network, ip: ip

      config.vm.network :forwarded_port, guest: 2001, host: 10000 + 2001 + machine_counter
      machine_counter = machine_counter + 1

      config.vm.provision :ansible do |ansible|
        ansible.playbook = "playbook.yml"
#        ansible.verbose = "vvvv"
      end
    end
  end
end

此时groupB为空(num_nodes为0),一切正常。这是 vagrant up --no-provision:

的输出
$ vagrant up --no-provision
Bringing machine 'groupA-01' up with 'virtualbox' provider...
==> groupA-01: Importing base box 'ubuntu/trusty64'...
==> groupA-01: Matching MAC address for NAT networking...
==> groupA-01: Checking if box 'ubuntu/trusty64' is up to date...
==> groupA-01: Setting the name of the VM: demo_groupA-01_1442511426046_79038
==> groupA-01: Clearing any previously set forwarded ports...
==> groupA-01: Clearing any previously set network interfaces...
==> groupA-01: Preparing network interfaces based on configuration...
    groupA-01: Adapter 1: nat
    groupA-01: Adapter 2: hostonly
==> groupA-01: Forwarding ports...
    groupA-01: 2001 => 12001 (adapter 1)
    groupA-01: 22 => 2222 (adapter 1)
==> groupA-01: Running 'pre-boot' VM customizations...
==> groupA-01: Booting VM...
==> groupA-01: Waiting for machine to boot. This may take a few minutes...
    groupA-01: SSH address: 127.0.0.1:2222
    groupA-01: SSH username: vagrant
    groupA-01: SSH auth method: private key
    groupA-01: Warning: Connection timeout. Retrying...
    groupA-01: 
    groupA-01: Vagrant insecure key detected. Vagrant will automatically replace
    groupA-01: this with a newly generated keypair for better security.
    groupA-01: 
    groupA-01: Inserting generated public key within guest...
    groupA-01: Removing insecure key from the guest if it's present...
    groupA-01: Key inserted! Disconnecting and reconnecting using new SSH key...
==> groupA-01: Machine booted and ready!
==> groupA-01: Checking for guest additions in VM...
==> groupA-01: Setting hostname...
==> groupA-01: Configuring and enabling network interfaces...
==> groupA-01: Mounting shared folders...
    groupA-01: /vagrant => /Users/me/demo
==> groupA-01: Machine not provisioned because `--no-provision` is specified.

现在,当我将另一台机器添加到配置时(groupA 中的 num_nodes 为 2),我将端口转发两次,并且端口转发到第二台机器的相同值,导致失败:

$ vagrant up --no-provision
Bringing machine 'groupA-01' up with 'virtualbox' provider...
Bringing machine 'groupA-02' up with 'virtualbox' provider...
==> groupA-01: Importing base box 'ubuntu/trusty64'...
==> groupA-01: Matching MAC address for NAT networking...
==> groupA-01: Checking if box 'ubuntu/trusty64' is up to date...
==> groupA-01: Setting the name of the VM: demo_ groupA-01_1442512392590_86418
==> groupA-01: Clearing any previously set forwarded ports...
==> groupA-01: Clearing any previously set network interfaces...
==> groupA-01: Preparing network interfaces based on configuration...
    groupA-01: Adapter 1: nat
    groupA-01: Adapter 2: hostonly
    groupA-01: Adapter 3: hostonly
==> groupA-01: Forwarding ports...
    groupA-01: 2001 => 12001 (adapter 1)
    groupA-01: 2001 => 12002 (adapter 1)
    groupA-01: 22 => 2222 (adapter 1)
==> groupA-01: Running 'pre-boot' VM customizations...
==> groupA-01: Booting VM...
==> groupA-01: Waiting for machine to boot. This may take a few minutes...
    groupA-01: SSH address: 127.0.0.1:2222
    groupA-01: SSH username: vagrant
    groupA-01: SSH auth method: private key
    groupA-01: Warning: Connection timeout. Retrying...
    groupA-01: 
    groupA-01: Vagrant insecure key detected. Vagrant will automatically replace
    groupA-01: this with a newly generated keypair for better security.
    groupA-01: 
    groupA-01: Inserting generated public key within guest...
    groupA-01: Removing insecure key from the guest if it's present...
    groupA-01: Key inserted! Disconnecting and reconnecting using new SSH key...
==> groupA-01: Machine booted and ready!
==> groupA-01: Checking for guest additions in VM...
==> groupA-01: Setting hostname...
==> groupA-01: Configuring and enabling network interfaces...
==> groupA-01: Mounting shared folders...
    groupA-01: /vagrant => /Users/john/travelnet/kafka-samza2
==> groupA-01: Machine not provisioned because `--no-provision` is specified.
==> groupA-02: Importing base box 'ubuntu/trusty64'...
==> groupA-02: Matching MAC address for NAT networking...
==> groupA-02: Checking if box 'ubuntu/trusty64' is up to date...
==> groupA-02: Setting the name of the VM: demo_ groupA-02_1442512426303_57538
==> groupA-02: Clearing any previously set forwarded ports...
Vagrant cannot forward the specified ports on this VM, since they
would collide with some other application that is already listening
on these ports. The forwarded port to 12001 is already in use
on the host machine.

To fix this, modify your current projects Vagrantfile to use another
port. Example, where '1234' would be replaced by a unique host port:

  config.vm.network :forwarded_port, guest: 2001, host: 1234

Sometimes, Vagrant will attempt to auto-correct this for you. In this
case, Vagrant was unable to. This is usually because the guest machine
is in a state which doesn't allow modifying port forwarding.

端口转发部分被执行了两次(如果我不使用 --no-provision,配置部分也是如此)。

我不明白为什么它被执行了两次,也不明白为什么第二个虚拟机试图转发到 12001(我认为应该是到 12002,这应该没问题,因为第一个虚拟机应该只能转发到 12001)。

此处有关于使用 Vagrant 循环进行延迟加载的警告:https://docs.vagrantup.com/v2/vagrantfile/tips.html,但我是 Ruby 的新手,我看不到 how/why 适用于此处。

我要实现的循环是:

For each group within groups:
    For each node in 1..num_nodes within group:
        Configure node

请问这是怎么回事?

我的猜测是 ruby 没有任何问题。

问题是您只使用一台机器。如果你想在 Vagrant 中使用多台机器,你需要遵循:https://docs.vagrantup.com/v2/multi-machine/

你想做的未经测试的方法(注意 this_vm 而不是配置):

(1..num_nodes).each do |i|
  vm_name = "%s-%02d" % [settings[group]['instance_name_prefix'], i]
  config.vm.define vm_name do |this_vm|
    this_vm.vm.hostname = vm_name

    this_vm.vm.provider :virtualbox do |vb|
      vb.gui = settings['virtualbox']['vb_gui']
      vb.memory = settings['virtualbox']['vb_memory']
      vb.cpus = settings['virtualbox']['vb_cpus']
    end
  end