【问题标题】:Is it possible to run a script on a virtual machine after Vagrant finishes provisioning all of them?在 Vagrant 完成配置所有脚本后,是否可以在虚拟机上运行脚本?
【发布时间】:2014-08-01 20:56:36
【问题描述】:

我正在使用 Vagrant v1.5.1 创建一个虚拟机 (VM) 集群。配置完所有虚拟机后,是否可以在其中一台机器上运行单个脚本?我要运行的脚本将设置从一个 VM 到所有其他 VM 的无密码 SSH。

例如,我在 Vagrant (CentOS 6.5) 中配置的节点如下。

  • 节点1
  • 节点2
  • 节点3
  • 节点4

我的Vagrantfile 如下所示。

(1..4).each do |i|
 config.vm.define "node-#{i}" do |node|
  node.vm.box = "centos65"
  ...omitted..
 end
end

完成所有这些之后,我需要在 node1 上运行一个脚本来启用到 node2、node3 和 node4 的无密码 SSH。

我知道您可以在配置每个 VM 时运行脚本,但在这种情况下,我想在配置所有 VM 后运行一个脚本,因为我需要所有 VM 都启动并运行才能运行最后一个脚本。

这在 Vagrant 中可行吗?

我意识到我也可以向后迭代。

r = 4..1
(r.first).downto(r.last).each do |i|
 config.vm.define "node-#{i}" do |node|
  node.vm.box = "centos65"
  ...omitted..
  if i == 1
   node.vm.provision "shell" do |s|
    s.path = "/path/to/script.sh"
   end
  end
 end
end

这会很好用,但实际上,我还需要设置从 node2 到 node1、node3 和 node4 的无密码 SSH。在上述方法中,这仅适用于 node1,但不适用于 node2(因为不会配置 node1)。

如果有一个 Vagrant 插件允许在我的集群中的所有节点之间使用密码 SSH,那就更好了。

【问题讨论】:

    标签: ruby vagrant vagrantfile centos6.5


    【解决方案1】:

    问题是一年前的问题,反正我找到了,因为我有同样的问题,所以这里是我用来解决问题的解决方法,有人可能会觉得它有用。

    我们需要“vagrant triggers”才能使其工作。 vagrant 触发器的问题是它们会为您正在创建的每台机器触发,但我们想确定所有机器都启动的时刻。我们可以通过检查每个 UP 事件来做到这一点,如果该事件对应于正在创建的最后一台机器:

    Vagrant.configure("2") do |config|
    
      (1..$machine_count).each do |i| 
    
      config.vm.define vm_name = "w%d" % i do |worker|
    
       worker.vm.hostname = vm_name
       workerIP = IP
       worker.vm.network :private_network, ip: workerIP
    
       worker.trigger.after :up do
         if(i == $machine_count) then
           info "last machine is up"
           run_remote  "bash /vagrant/YOUR_SCRIPT.sh"
         end   
       end
    
      end
     end
    end
    

    这适用于不支持在 Vagrant(VBox、VMWare)上并行执行的提供程序。

    【讨论】:

    • 有没有办法在“最后一台机器启动”时触发脚本在另一台机器上运行?假设我提供了一个头节点 + 3 个工作节点。一旦所有 3 个工作人员都启动了,我想在我的头节点上执行一些操作。
    • 问题询问如何在节点 1 全部启动后运行命令。这不会那样做。
    【解决方案2】:

    Vagrant 中没有“在配置所有 VM 后运行”的钩子,因此您需要自己实现它。我能想到的几个选项:

    1:在所有虚拟机运行后运行 SSH 设置脚本。

    例如,如果脚本名为 ssh_setup.sh 并存在于共享文件夹中:

    $ for i in {1..4}; do vagrant ssh node$i -c 'sudo /vagrant/ssh_setup.sh'; done
    

    2:对所有主机使用相同的 SSH 密钥并在配置期间设置

    如果所有节点共享相同的无密码 SSH 密钥,您可以将所需的文件(如 authorized_keysid_rsa 等)复制到 ~.ssh

    【讨论】:

      【解决方案3】:

      添加更新的答案。

      vagrant-triggers 插件于 2018 年 5 月从 merged 变为 Vagrant 2.1.0

      我们可以简单地使用 trigger 类中的only_on option 选项。

      假设我们有以下配置:

      servers=[
        {:hostname => "net1",:ip => "192.168.11.11"},
        {:hostname => "net2",:ip => "192.168.22.11"},
        {:hostname => "net3",:ip => "192.168.33.11"}
      ]
      

      我们现在可以在最后一台机器启动后轻松执行触发器:

      # Take the hostname of the last machine in the array
      last_vm = servers[(servers.length) -1][:hostname]
      
      Vagrant.configure(2) do |config|
          servers.each do |machine|
              config.vm.define machine[:hostname] do |node|
      
                  # ----- Common configuration ----- #
                  node.vm.box = "debian/jessie64"
                  node.vm.hostname = machine[:hostname]
                  node.vm.network "private_network", ip: machine[:ip]
      
                  # ----- Adding trigger - only after last VM is UP ------ #
                  node.trigger.after :up do |trigger|
                      trigger.only_on = last_vm  # <---- Just use it here!
                      trigger.info = "Running only after last machine is up!"
                  end
              end
          end
      end
      

      我们可以检查输出并看到触发器只有在“net3”启动后才真正触发:

      ==> net3: Setting hostname...
      ==> net3: Configuring and enabling network interfaces...
      ==> net3: Installing rsync to the VM...
      ==> net3: Rsyncing folder: /home/rotem/workspaces/playground/vagrant/learning-network-modes/testing/ => /vagrant
      ==> net3: Running action triggers after up ...
      ==> net3: Running trigger...
      ==> net3: Running only after last machine is up!
      

      【讨论】:

      • 这不会只在最后一台机器上运行吗?如果你想在它们都启动后在另一台机器上运行命令怎么办?
      • 1.是的,因为我只提到了“last_vm”。 2.您可以将其更改为任何其他VM/s。
      • 但是它只会在第一个虚拟机启动时运行,而不是在它们都启动后运行。
      • 检查看看(:
      【解决方案4】:

      这对我很有效:我使用了每个 VM 配置脚本,在最后一个脚本中,我通过 ssh 在第一个 VM 上调用了配置后脚本。

      Vagrantfile:

      require 'fileutils'
      
      Vagrant.require_version ">= 1.6.0"
      
      $max_nodes = 2
      $vm_name = "vm_prefix"
      
      #...<skipped some lines that are not relevant to the case >...
      Vagrant.configure("2") do |config|
        config.ssh.forward_agent = true
        config.ssh.insert_key    = false
        #ubuntu 16.04
        config.vm.box = "ubuntu/xenial64"
      
        (1..$max_nodes).each do |i|
          config.vm.define vm_name = "%s-%02d" % [$vm_name, i] do |config|
            config.vm.hostname = vm_name
            config.vm.network "private_network", ip: "10.10.0.%02d" % [i+20], :name => 'vboxnet2'
            config.vm.network :forwarded_port, guest: 22, host: "1%02d22" % [i+20], id: "ssh"
            config.vm.synced_folder "./shared", "/host-shared"
            config.vm.provider :virtualbox do |vb|
              vb.name = vm_name
              vb.gui = false
              vb.memory = 4096
              vb.cpus = 2
              vb.customize ["modifyvm", :id, "--cpuexecutioncap", "100"]
              vb.linked_clone = true
            end
      
            # Important part:
            config.vm.provision "shell", path: "common_provision.sh"
            config.vm.provision "shell", path: "per_vm_provision#{i}.sh"
      
          end
        end
      end
      

      在磁盘上: (确保 post_provision.sh 至少具有所有者执行权限:rwxr..r..)

      vm$ ls /vagrant/
      ...<skipped some lines that are not relevant to the case >...
      config.sh
      common_provision.sh
      per_vm_provision1.sh
      per_vm_provision2.sh
      per_vm_provision3.sh
      ...
      per_vm_provisionN.sh
      post_provision.sh
      Vagrantfile
      ...<skipped some lines that are not relevant to the case >...
      

      config.sh:

        num_vm="2" # should equal the $max_nodes in Vagrantfile
        name_vm="vm_prefix" # should equal the $vm_name in Vagrantfile
        username="user1"
        userpass="abc123"
          ...<skipped some lines that are not relevant to the case >...
      

      common_provision.sh:

        source /vagrant/config.sh
        ...<skipped some lines that are not relevant to the case >...
      
        sed -r -i 's/\%sudo.*$/%sudo       ALL=(ALL:ALL) NOPASSWD:ALL/' /etc/sudoers
        sed -r -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
        service ssh reload
      
        # add user ${username}
        useradd --create-home --home-dir /home/${username}  --shell /bin/bash  ${username}
        usermod -aG admin ${username} 
        usermod -aG sudo ${username}
        /bin/bash -c "echo -e \"${userpass}\n${userpass}\" | passwd ${username}"
      
        # provision additional ssh keys
        # copy ssh keys from disk
         cp /vagrant/ssh/* /home/vagrant/.ssh
         cat /vagrant/ssh/id_rsa.pub >> /home/vagrant/.ssh/authorized_keys
         mkdir /home/${username}/.ssh
         cp /vagrant/ssh/* /home/${username}/.ssh
         cat /vagrant/ssh/id_rsa.pub >> /home/${username}/.ssh/authorized_keys
      
      # not required, just for convenience
       cat >> /etc/hosts <<EOF
      10.10.0.21    ${name_vm}-01
      10.10.0.22    ${name_vm}-02
      10.10.0.23    ${name_vm}-03
      ...
      10.10.0.2N    ${name_vm}-0N
      EOF
        ...<skipped some lines that are not relevant to the case >...
      

      per_vm_provision2.sh:

      #!/bin/bash
      
        # import variables from config
        source /vagrant/config.sh
      
        ...<skipped some lines that are not relevant to the case >...
      
       # check if this is the last provisioned vm 
       if [ "x${num_vm}" = "x2" ] ; then
         ssh vagrant@10.10.0.21 -o StrictHostKeyChecking=no -- '/vagrant/post_provision.sh'
       fi
      

      per_vm_provisionN.sh:

      #!/bin/bash
      
        # import variables from config
        source /vagrant/config.sh
      
        ...<skipped some lines that are not relevant to the case >...
      
       # check if this is the last provisioned vm. N represents the highest number 
       if [ "x${num_vm}" = "xN" ] ; then
         ssh vagrant@10.10.0.21 -o StrictHostKeyChecking=no -- '/vagrant/post_provision.sh'
       fi
      

      我希望,我没有跳过任何重要的内容,但我认为这个想法总体上是明确的。

      注意: 用于虚拟机间访问的 ssh 密钥默认由 Vagrant 提供。如果需要,您可以使用common_provision.sh添加自己的 ssh 密钥

      【讨论】:

      • 我猜有更好的 ruby​​ 专业知识的人可以将所有这些编译到一个 Vagrantfile 中
      猜你喜欢
      • 1970-01-01
      • 2017-11-09
      • 1970-01-01
      • 2021-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      • 2023-03-16
      相关资源
      最近更新 更多