How to run several boxes with Vagrant? - vagrant

I need to run several boxes with Vagrant.
Is there a way to do that?
These don't relate one to another in any way, they can be thought as different environments using for test so it seems that multi-machine setup has nothing to do with this.

The best way is to use an array of hashes. You can define the array like:
servers=[
{
:hostname => "web",
:ip => "192.168.100.10",
:box => "saucy",
:ram => 1024,
:cpu => 2
},
{
:hostname => "db",
:ip => "192.168.100.11",
:box => "saucy",
:ram => 2048,
:cpu => 4
}
]
Then you just iterate each item in server array and define the configs:
Vagrant.configure(2) do |config|
servers.each do |machine|
config.vm.define machine[:hostname] do |node|
node.vm.box = machine[:box]
node.vm.hostname = machine[:hostname]
node.vm.network "private_network", ip: machine[:ip]
node.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--memory", machine[:ram]]
end
end
end
end

You can definitely run multiple Vagrant boxes concurrently, as long as their configuration does not clash with one another in some breaking way, e.g. mapping the same network ports on the host, or using same box names/IDs inside the same provider. There's no difference from having multiple boxes running on a provider manually, say multiple boxes on VirtualBox, or having them registered and started up by Vagrant. The result is the same, Vagrant just streamlines the process.
You can either use so called multi-machine environment to manage these boxes together in one project/Vagrantfile. They don't necessarily have to be somehow connected, ease of management may be the reason alone, e.g. if you need to start them up at the same time.
Or you can use separate projects/Vagrantfiles and manage the machines from their respective directories, completely separated.
In case of running multiple instances of the same project, you need multiple copies of the project directory, as Vagrant stores the box state in the .vagrant directory under the project.

You need just to copy the directory holding Vagrantfile to the new place and run vagrant up from it.
Make sure you copy the dir prior to starting up the box for the first time or Vagrant will think that these two locations refer to the same box. Or, if you already did vagrant up before copying the directory, then delete copied_directory/.vagrant after you make the copy.

You can even use different Vagrantfile(s) in same directory for different machine configuration or boxes
VAGRANT_VAGRANTFILE=Vagrantfile.ubntu_1404_64 VAGRANT_DOTFILE_PATH=.vagrant_ub140464 vagrant up
OR
VAGRANT_VAGRANTFILE=Vagrantfile.ubntu_1404_32 VAGRANT_DOTFILE_PATH=.vagrant_ub140432 vagrant up
Both the Vagrantfile can reside in same directory

I was able to have a single vagrantfile:
Vagrant.configure("2") do |winconfig|
# stuff
end
Vagrant.configure("2") do |nixconfig|
# stuff
end
Vagrant.configure("2") do |macconfig|
# stuff
end
and then I can bring them up with vagrant up --parallel. As others have mentioned, different vagrant files may be better for maintainability.

https://www.vagrantup.com/docs/vagrantfile/tips.html#loop-over-vm-definitions
(1..3).each do |i|
config.vm.define "node-#{i}" do |node|
node.vm.provision "shell",
inline: "echo hello from node #{i}"
end
end

Copying the directory holding Vagrantfile to a new place and spinning up the new machine there is the most straight forward way, if machines are not co-operating.
However, you may not want to copy/paste provisioning scripts for VCS tracking/backtracking purposes. Keep all your scripts in a folder, ex. dev, and put your Vagrantfile under numbered folders under dev, ex. dev/10. When you create a newer version, ex. dev/11 and not need the older one, you may just delete it. And refer to the common provisioning scripts using relative path:
config.vm.provision "shell", path: "../provisioner.sh"

Related

Is it possible to load a Vagrantfile config from within another Vagrantfile?

Vagrant Multi-Machine functionality seems pretty cool, however one thing that bothers me (or is isn't immediately apparent) is that there doesn't seem to be a good way to share configuration options between a "parent" Vagrantfile and "children" Vagrantfiles. Is there a way to effectively and maintainably share configuration options between the parent and it's children? An example might make this more clear.
Let's assume I've got a platform which is comprised of 3 apps/services: API, Web, and Worker.
Let's presume a directory structure of the following:
/some_platform
/api
# app code...
Vagrantfile
/web
# app code...
Vagrantfile
/worker
# app code...
Vagrantfile
Vagrantfile
Let's say /some_platform/api/Vagrantfile looks like:
Vagrant.configure("2") do |config|
config.vm.box = "debian/jessie64"
end
Presumably the web and worker Vagrantfiles look similar.
Now, using the wonders of Multi-Machine I rely on Vagrant coordinate these VMs, and /some_platform/Vagrantfile looks like:
Vagrant.configure("2") do |config|
config.vm.define "web" do |api|
api.vm.box = "debian/jessie64"
end
config.vm.define "web" do |web|
web.vm.box = "debian/jessie64"
end
config.vm.define "web" do |worker|
worker.vm.box = "debian/jessie64"
end
end
I realize this example is contrived, but it's easy to see how once you get more and more complex config declarations, it's annoying and hazardous to have that config duplicated in two places.
You might be wondering "Why does each project have it's own Vagrantfile?" Doing so provides a single source of truth for how the server that app runs on should be setup. I realize there are provisioners you can use (and I will use them), but you still have to declare a few other things outside of that and I want to keep that DRY so that I can either bring up a cluster of apps via Multi-Machine, or I can work on a single app and change it's VM/server setup.
What I'd really love is a way to merge other Vagrantfiles into a "parent" file.
Is that possible? Or am I crazy for trying? Any clever ideas on how to achieve this? I've mucked about with some yaml files and POROs to skate around this issue, but none of the hacks feel very satisfying.
Good news!
You can in fact apply DRY principles in a Vagrantfile.
First: Create a file /some_platform/DRY_vagrant/Vagrantfile.sensible to hold some sensible defaults :
Vagrant.configure("2") do |config|
# With the setting below, any vagrantfile VM without a 'config.vm.box' will
# automatically inherit "debian/jessie64"
config.vm.box = "debian/jessie64"
end
Second: Create a file /some_platform/DRY_vagrant/Vagrantfile.worker for the 'worker' virtual machine :
Vagrant.configure("2") do |config|
config.vm.define "worker" do |worker|
# This 'worker' VM will not inherit "debian/jessie64".
# Instead, this VM will explicitly use "debian/stretch64"
worker.vm.box = "debian/stretch64"
end
end
Finally: Create a file /some_platform/Vagrantfile to tie it all together :
# Load Sensible Defaults
sensible_defaults_vagrantfile = '/some_platform/DRY_vagrant/Vagrantfile.sensible'
load sensible_defaults_vagrantfile if File.exists?(sensible_defaults_vagrantfile)
# Define the 'api' VM within the main Vagrantfile
Vagrant.configure("2") do |config|
config.vm.define "api" do |api|
# This 'api' VM will automatically inherit the "debian/jessie64" which we
# configured in Vagrantfile.sensible
# Make customizations to the 'api' VM
api.vm.hostname = "vm-debian-jessie64-api"
end
end
# Load the 'worker' VM
worker_vm_vagrantfile = '/some_platform/DRY_vagrant/Vagrantfile.worker'
load worker_vm_vagrantfile if File.exists?(worker_vm_vagrantfile)
This approach can be used for almost any other vagrantfile config options. It is not limited to just the "config.vm.box" setting.
Hope this helped!
There are 2 things you can look at (probably more, but those two comes to my mind)
look at How to template Vagrantfile using Ruby? its an example how you can read the content of another file, Vagrantfile is just a ruby script so you can use all the power of ruby.
vagrant has a concept of loading and merging, see from doc so if you wanted to do something anytime you run a vagrant command, you could create a Vagrantfile under your ~/.vagrant.d/ folder and it will always run
one drawback (or at least to pay attention) : the Vagrantfile is a ruby script that is evaluated each (and every time) a vagrant command is executed (up, status, halt ....)

Scope of modules using Puppet in a Vagrant multi machine environment

I have a projekt with a setup for a multi machine environment for Vagrant. I had to fix some problems, which were initially caused by the redirect issue to https, but solving these lead into other errors, which I fixed in all projects except this one now, which uses the multi machine feature of Vagrant.
So I have this folder structure:
/Vagrantfile
/puppet/box_1/puppetfile
/puppet/box_1/manifests/site.pp
This is my code snippet, where I define my provision directories:
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "puppet/box_1/manifests"
puppet.manifest_file = "site.pp"
end
My puppetfile looks like this:
forge "https://forgeapi.puppetlabs.com"
mod 'tPl0ch/composer'
mod 'puppetlabs/apt'
mod 'puppetlabs/apache'
mod 'puppetlabs/firewall'
In my site.pp I try to include apt, but I get this error message:
Error: Evaluation Error: Error while evaluating a Function Call, Could not find class ::apt for project.local at /tmp/vagrant-puppet/manifests-f2b1fd0ac42b51938ed6ae7e6917367e/site.pp:1:1 on node project.local
When I rearange my puppet files like this:
/Vagrantfile
/puppet/puppetfile
/puppet/manifests/site.pp
like this is the common way of setting this up, it works without that problem, but as I mentioned, there are other boxes, which use different puppetfiles and site.pp files, so this folder structure makes some kinda sense. It seems, that it doesn'even matter, if I delete the config for the other boxes, and setup my Vagrantfile, as if it would be only one box, so I am just confused, how the location, of these files influence the scope of certain classes.
So my questions is here: Is there a way, to keep this folder structure and still have these modules defined in puppetfile available in my site.pp? Or is this generally some kinda bad practice to organize it this way? I was searching for some examples for this, but couldn't find any for some reason...
EDIT: It seems, on provision the puppetfile isnt even used anymore, when its not located in /puppet/ So maybe I just have to tell Vagrant how to use it?
define where librarian should find the puppet file
Vagrant.configure("2") do |config|
config.librarian_puppet.puppetfile_dir = "puppet/box1"
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "puppet/box_1/manifests"
puppet.manifest_file = "site.pp"
end

Vagrant disable Guest Additions

I would like to disable VirtualBox Guest Additions. I do not use them for folder syncing, etc. and for the box I am working on (e.g., centos/7), they fail to build anyway. Is there some way to tell vagrant not to try to install them at vagrant up?
In your Vagrantfile, add the following param
Vagrant.configure("2") do |config|
....
config.vbguest.auto_update = false
....
end
Users might not have the vagrant-vbguest plugin, and you could wrap its use in a conditional to avoid confusion.
Vagrant.configure("2") do |config|
....
if Vagrant.has_plugin?("vagrant-vbguest")
config.vbguest.auto_update = false
end
....
end

Create aliases on host during vagrant up using vagrant-triggers

Hoping someone can help me out here.
I am wanting to create aliases on my local machine (host) when running vagrant up. Through my search for vagrant run command on host, I came across the vagrant-triggers plugin. From the surface it appears to do exactly what I want (executing a script/command on the host machine during the vagrant provisioning process), however I have not been able to get it working successfully.
Below is my example code. I don't receive any errors, but the aliases are not available on the host.
Vagrant.configure("2") do |config|
# Your existing Vagrant configuration
...
# start vagrant-triggers example code
{
:up => [
'alias runscript="$(PWD)/script"',
'alias runscript2="$(PWD)/script2"'
],
[:halt, :destroy] => [
'unalias runscript',
'unalias runscript2'
]
}.each do |trigger, commands|
config.trigger.after trigger, :stdout => true do
commands.each do |command|
run command
end
end
end
...
end

Specify default provider in Vagrantfile

I'd like to specify directly in the vagrantfile which provider to use by default for each VM.
For example, given this vagrantfile:
# Vagrantfile
[...]
config.vm.define 'dev_vm' do |machine|
machine.vm.provider :libvirt do |os|
[...]
end
# machine.default_provider = :libvirt
end
config.vm.define 'production_vm' do |machine|
machine.vm.provider :openstack do |os|
[...]
end
# machine.default_provider = :openstack
end
To boot up the following to VMs, I have to issue two commands currently:
vagrant up --provider=libvirt dev_vm
vagrant up --provider=openstack production_vm
I'd like to bring up both with a single vagrant up, especially because I'm running quite a few more machines. Some configuration like the commented machine.default_provider = :openstack would be fantastic to have.
Is there a way to do so?
I don't think there is any easy way to do it. Vagrant will currently use the same provider during the whole run so it could possibly be quite big code change to support this.
Maybe wrapper scripts are the easiest solution now.
Another workaround would be to use separate Vagrantfiles for the VMs and set VAGRANT_DEFAULT_PROVIDER in each. If there is a lot of common config, you could extract it to e.g. Vagrantfile.common, which is included by the others. Something like:
# Vagrantfile 1
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'libvirt'
# assume the common config is in parent directory
load File.expand_path('../../Vagrantfile.common', __FILE__)
Vagrant.configure('2') do |config|
# ...
end

Resources