Vagrant: Best method for Multiple Identical VMs - vagrant

I would like to provision multiple VMs with Vagrant. I would like to be able to increase the number of VMs quickly and easily.
Based on my current understanding of Vagrant. The following method detailed here https://www.edureka.co/blog/10-steps-to-create-multiple-vms-using-vagrant/ seems to be the best way:
# This defines the version of vagrant
Vagrant.configure(2) do |config|
# Specifying the box we wish to use
config.vm.box = "chef/centos-6.5"
# Adding Bridged Network Adapter
config.vm.network "public_network"
# Iterating the loop for three times
(1..3).each do |i|
# Defining VM properties
config.vm.define "edureka_vm#{i}" do |node|
# Specifying the provider as VirtualBox and naming the VM's
config.vm.provider "virtualbox" do |node|
# The VM will be named as edureka_vm{i}
node.name = "edureka_vm#{i}"
end
end
end
end
So if I want to increase from 3 to 4, I just change the loop range.
Is there another method? When I was reading the docs I was hoping there would be some version of the vagrant up command that would spin up a new unique instance.

Environment Variables of Vagrant is your best bet. Define something like this in your Vagrantfile.
myVMs = ENV['NODES'] || 1
Where NODES is an environment variable. You can use this in the following way.
NODES=4 vagrant up
This will update the myVMs variable in your Vagrantfile and spawn 4 VMs. You do not need to open your Vagrantfile and update it. But note, this will be for this session alone.If you want to save the value of the NODES, then you might have to add it to your ~/.profile in Linux or environment variables in windows.
Hope this helps.

Related

Create Vagrant VM with an alias

I want to create a couple of Vagrant VM's. Most of them could be Ubuntu 16.04. But I want the VM's to be named as webserver01, webserver02 and webserver03. Basically I want the Vagrantfile for each VM to be inside the directory name I provided above.
Command vagrant init ubuntu/xenial64 might create a new VM but how do I make sure I create three webservers{1..3} as above and run vagrant up command from inside that directory?
I want all the VM's to be in a specific directory so I just open CMD inside that directory and run vagrant up from inside them.
you can spawn as many VMs with the help of loops.
https://www.vagrantup.com/docs/vagrantfile/tips.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
(1..3).each do |i|
config.vm.define "node-#{i}" do |node|
node.vm.provision "shell",
inline: "echo hello from node #{i}"
end
end
end
you can access any machine from the cli e.g. from the given example in the link you can control node-1 like
vagrant up node-1 && vagrant ssh node-1

How do I separate stages of multi-machine cluster provisioning?

Let's say I have 4 Vagrant boxes.
3 are variations of the same configuration, say, Consul, and one has a completely different configuration, say a database.
I need to run a provisioning step with slight variations on the three similarly configured Consul VMs.
Then, I need to run a provisioning step against 1 of those Vault VMs.
Only after this provisioning step run on 1 of the VMs can I successfully run the next provisioning step, with slight variations, on the three similarly configured Vault VMs.
I've read through the docs on Vagrant with multiple machines, looping over VM definitions, and provisioning parameters. The preserve_order: true parameter didn't work for me, at least not the way I was using it.
Here's what I've been trying so far, anonymized to protect the guilty:
~ $cat Hark_TisAVagrant
Vagrant.configure("2") do |config|
## 1.
## Do This on 3 VMs
(1..3).each do |i|
config.vm.define "node-#{i}" do |node|
node.vm.provision "shell",
inline: "echo FIRST from node #{i}"
node.vm.provider "docker" do |d|
d.image = "consul"
end
end
end
## 2.
## Afterwards, do this on one VM from that set
node3.vm.provision "then-this",
type: "shell",
preserve_order: true,
inline: "echo SECOND from node3!"
## 3.
## Finally, on all 3, do this
(1..3).each do |i|
config.vm.define "node-#{i}" do |node|
node.vm.provision "shell",
inline: "echo FINAL from node #{i}"
end
end
## 4.
## On the side, no order needed
config.vm.define "db" do |db|
db.vm.provision "shell", inline: "echo an irrelevant dbtard meme-ing in the peanut gallery"
db.vm.provider "docker" do |d|
d.image = "postgres"
end
end
end
I'll spare you the massive Vagrant logs, but I'm getting the equivalent of the following:
FIRST (1-Consul)
SECOND (Fails due to lack of 3-Consul Quorum)
FINAL
FIRST
FINAL
FIRST
FINAL
DB
What I'm looking for is more like this:
FIRST
FIRST
FIRST (3-Consul Quorum achieved)
SECOND (API Call)
FINAL
FINAL
FINAL
DB
I'm also going to research doing this with a .yaml file, and on Vagrantfile hierarchies.
This answer seems to indicate that what I'm looking for isn't possible using a mere Vagrantfile, but it's from 2 years ago: https://stackoverflow.com/a/25065243/2146138
And this answer looked promising at first, but seems to focus mostly on Ansible:
Vagrant multi-machine provisioning
The main problem motivating this question, for me, at least, is that all 3 machines need to be provisioned for a Consul Cluster "Quorum".
After we get a 3-Consul "Quorum" a single call needs to be made against the Consul Cluster's API to ACL it.
After that call, provisioning of other services can continue properly, and they can register in the newly initialized and ACL'd Consul Cluster.
Regardless of my particular motivating problem, though, I am curious about the capabilities of the Vagrant DSL's features for multi-machine control flow for cases like this, because I suspect this will come up again.

Bring up vagrant boxes simultaneously

Vagrant brings up one box at a time. Is there a way to make vagrant bring up all boxes at the same time?
The Vagrantfile configuration which I'm using for several boxes has the following format:
Vagrant.configure("2") do |config|
config.vm.define :master do |master|
# Master config...
end
config.vm.define :slave1 do |slave|
# Slave config...
end
end
Now, when running > vagrant up Vagrant will start by setting up master and once that is done it will set up slave1.
Depending on your provider, you can pass pass the --parallel flag to vagrant up.
vagrant up --parallel

As of March 17th, Mitchell Hashimoto noted that Virtualbox is not a provider that supports parallelism. Reference

Do multiple machines declared in a Vagrantfile have to be based on the same base box?

From the documentation you can address the need to have multiple environments running to reproduce your production (like one db machine, one web machine etc.):
Vagrant.configure("2") do |config|
config.vm.define "web" do |web|
web.vm.box = "apache"
end
config.vm.define "db" do |db|
db.vm.box = "mysql"
end
end
This is well explained but I also understand they will be all based on the same box: e.g. ubuntu-1404 for instance.
What happens if you need the db box to be on oracle linux, the web box on ubuntu for instance?
Appart from creating two directories with two Vagrantfile I don't see any other option.
Has anyone ever done that and how?
No, the boxes can definitely be different. The example you showed (from the Multi-Machine doc page is a big confusing because it's not clear what "apache" and "mysql" are referring to.
You can set *.vm.box to actual box names. For example, finding a couple of boxes from the Vagrant cloud might change your example Vagrantfile to:
Vagrant.configure("2") do |config|
config.vm.define "web" do |web|
web.vm.box = "hashicorp/precise64"
end
config.vm.define "db" do |db|
db.vm.box = "box-cutter/oel65"
end
end
No, you are free to overwrite every setting - which you have already done. Didn't you tested it?

Using Vagrant to manage development and production environments?

How are people handling simple automation (with puppet) for dev / prod environments with vagrant (ideally from the same vagrantfile)?
Use case I'm trying to solve
I would love to spin up the the production machine with vagrant if it isn't created.
I would love to reload nginx or apache confs on production with vagrant if they were tweaked in the puppet files for my dev environment.
The Problem
When you call vagrant up with a provider like AWS or Digital Ocean, it becomes the active provider and you can't switch. You get this error:
An active machine was found with a different provider. Vagrant
currently allows each machine to be brought up with only a single
provider at a time. A future version will remove this limitation.
Until then, please destroy the existing machine to up with a new
provider.
It seems the answer it to destroy, but I just need to switch. I don't want to destroy.
I would love to be able to say
vagrant up prod
or
vagrant reload prod
and then a simple vagrant up would fall back to the default machine.
This syntax is similar to how multiple machines work, but I don't want to spin up a dev and production environment when I just call vagrant up (which is the default behavior).
Should I be looking at packer as part of the workflow? I watched the whole talk at puppetconf 2013 on Mitchell's talk on Multi-Provider http://puppetlabs.com/presentations/multi-provider-vagrant-aws-vmware-and-more
I'm still not seeing a solution for my problem.
UPDATE 9/27/13
In case anybody else is fighting this idea, this article cleared up a lot of questions I had.
http://pretengineer.com/post/packer-vagrant-infra
As for workaround, you should define config.vm.define (as suggested here), in order to support multiple providers.
Please find the following configuration posted by #kzap as example:
Vagrant.configure("2") do |config|
# Store the current version of Vagrant for use in conditionals when dealing
# with possible backward compatible issues.
vagrant_version = Vagrant::VERSION.sub(/^v/, '')
# Configuration options for the VirtualBox provider.
def configure_vbox_provider(config, name, ip, memory = 2048, cpus = 1)
config.vm.provider :virtualbox do |v, override|
# override box url
override.vm.box = "ubuntu/trusty64"
# configure host-only network
override.vm.hostname = "#{name}.dev"
override.vm.network :private_network, id: "vvv_primary", ip: ip
v.customize ["modifyvm", :id,
"--memory", memory,
"--cpus", cpus,
"--name", name,
"--natdnshostresolver1", "on",
"--natdnsproxy1", "on"
]
end
end
default_provider = "virtualbox"
supported_providers = %w(virtualbox rackspace aws managed)
active_provider = ENV['VAGRANT_ACTIVE_PROVIDER'] # it'd be better to get this from the CLI --provider option
supported_providers.each do |provider|
next unless (active_provider.nil? && provider == default_provider) || active_provider == provider
#
# VM per provider
#
config.vm.define :"sample-#{provider}" do | sample_web_config |
case provider
when "virtualbox"
configure_vbox_provider(sample_web_config, "examine-web", "192.168.50.1")
when "aws"
configure_aws_provider(sample_web_config)
when "managed"
configure_managed_provider(sample_web_config, "1.2.3.4")
when "rackspace"
configure_rackspace_provider(sample_web_config)
end
end
end
Or the following example posted at gist by #maxlinc:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "dummy"
config.vm.provider :rackspace do |rs|
rs.username = ENV['RAX_USERNAME']
rs.api_key = ENV['RAX_API_KEY']
rs.rackspace_region = :ord
end
supported_providers = %w(virtualbox rackspace)
active_provider = ENV['VAGRANT_ACTIVE_PROVIDER'] # it'd be better to get this from the CLI --provider option
supported_providers.each do |provider|
next unless active_provider.nil? || active_provider == provider
config.vm.define "exact_name_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = '1 GB Performance'
rs.image = 'Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)'
end
end
config.vm.define "regex_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = /1\s+GB\s+Performance/
rs.image = /Ubuntu.*Trusty Tahr.*(PVHVM)/
end
end
config.vm.define "id_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = 'performance1-1'
rs.image = 'bb02b1a3-bc77-4d17-ab5b-421d89850fca'
end
end
config.vm.define "unlisted_#{provider}" do |box|
box.vm.provider :rackspace do |rs|
rs.flavor = 'performance1-1'
rs.image = '547a46bd-d913-4bf7-ac35-2f24f25f1b7a'
end
end
end
end
Not an ideal solution, but what about using git branches? My thinking is that it could be conceptually similar to using heroku, where you might have a master, staging, and production versions (since they're usually different remotes).
In this case you start off the prod branch with the small edit to the Vagrantfile to name the VM a little differently. Then you should be able to merge all changes from dev with the prod branch as they occur. So your workflow would look like:
$ git checkout prod
$ vagrant up
$ git checkout master
... make changes to puppet ...
$ git checkout prod
$ git merge master
$ vagrant reload
$ git checkout master
You could script and alias these so you end up with
$ start_production
$ reload_production
Here is a simple way of dynamically changing the 'default' machine name depending on the specified --provider from the command line, so they won't conflict between the different providers:
require 'getoptlong'
opts = GetoptLong.new(
[ '--provider', GetoptLong::OPTIONAL_ARGUMENT ],
[ '--vm-name', GetoptLong::OPTIONAL_ARGUMENT ]
)
provider=ENV['PROVIDER'] || 'virtualbox'
vm_name=ENV['VM_NAME'] || 'default'
opts.each do |opt, arg|
case opt
when '--provider'
provider=arg
when '--vm-name'
vm_name=arg
end
end
Vagrant.configure(2) do |config|
# HERE you are dynamically changing the machine name to prevent conflict.
config.vm.define "mt-#{provider}-#{vm_name}"
# Below sections are just examples, not relevant.
config.vm.provider "virtualbox" do |vm|
vm.name = "test.local"
vm.network "private_network", ip: "192.168.22.22"
vm.customize ['modifyvm', :id, '--natdnshostresolver1', 'on']
config.vm.box = "ubuntu/wily64"
end
config.vm.provider :aws do |aws, override|
aws.aws_profile = "testing"
aws.instance_type = "m3.medium"
aws.ami = "ami-7747d01e"
config.vm.box = "testing"
end
end
Example usage:
VM_NAME=dev PROVIDER=virtualbox vagrant up --provider=virtualbox
VM_NAME=uat PROVIDER=aws vagrant up --provider=aws
VM_NAME=test PROVIDER=aws vagrant up --provider=aws
VM_NAME=prod PROVIDER=aws vagrant up --provider=aws
VM_NAME=uat PROVIDER=aws vagrant destroy -f
VM_NAME=test PROVIDER=aws vagrant status
See also: Multiple provisioners in a single vagrant file?
what I came up with to work with this scenario is to manage 2 distincts .vagrant folder.
Note: most of the other answers deal with setting up multi-provider assuming you will run dev and prod on different provider, in most cases this might be true but you can definitely have cases where you have same provider for dev and prod. Lets say you're using aws and you want to use dev and prod as ec2 instance it will be the same provider.
Say you want to manage dev and prod instances, potentially using different providers (but could also very well be on the same provider) so you'll do:
set up dev instance with normal vagrant up --provider <dev_provider>.
This will create a dev VM that you can manage
back up the .vagrant folder created in your project directory and rename it like .vagrant.dev
set up prod instance with your provider of choice vagrant up --provider <prod_provider>. This now creates your prod VM
back up the newly .vagrant folder created in your project directory and rename it like .vagrant.prod
now, depending if you want to work on dev or prod, you'll rename the .vagrant.dev or .vagrant.prod directory as .vagrant and vagrant will operate the right VM.
I did not come up with a script as mainly the most of the time I work with dev and very few times I need to switch to the other provider. but I dont think it will be too hard to read the parameter from CLI and make the renaming more dynamic.

Resources