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
Related
I'm trying to set up an inheritance structure in my forest of Vagrantfiles. Basically I wanted something like this:
/base
Vagrantfile
/testvm
Vagrantfile
/nginx-test
Vagrantfile
The testvm/Vagrantfile would load the base/Vagrantfile and the nginx-test/Vagrantfile would load the testvm/Vagrantfile. In my test case, the base Vagrantfile specifies one box type (eg bento/ubuntu-18.04) and sets 1GB of memory for the VM and some other stuff. The testvm Vagrantfile loads that and overrides the box to bento/ubuntu-22.04 and sets a different hostname. The nginx-test Vagrantfile loads that last file and sets its own hostname plus adds another provisioning block to install nginx.
I've tried this but I get this SystemStackError: stack level too deep error:
$ vagrant up
Vagrant failed to initialize at a very early stage:
There was an error loading a Vagrantfile. The file being loaded
and the error message are shown below. This is usually caused by
a syntax error.
Path: /Users/donseiler/vagrant_test/testvm/nginx-test/Vagrantfile
Line number: 5
Message: SystemStackError: stack level too deep
I've been searching but haven't found anyone attempting multiple layers of loading yet. I can run vagrant up just fine from the base and testvm directories to bring those machines up. I can't run any vagrant commands from the nginx-test directory (not even vagrant status, etc)
Here are the file contents:
/base/Vagrantfile:
VAGRANTFILE_API_VERSION = "2"
NAME = "basevm"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Default to bento bionic box
config.vm.box = "bento/ubuntu-18.04"
config.vm.hostname = NAME
# Set default virtualbox provider settings
config.vm.provider :virtualbox do |vb|
vb.name = NAME
vb.memory = "1024"
vb.gui = false
end
config.vm.provision "shell", inline: "DEBIAN_FRONTEND=noninteractive apt-get update"
end
/base/testvm/Vagrantfile:
# Load parent Vagrantfile
load "../Vagrantfile"
# define hostname
NAME = "testvm"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Change to jammy
config.vm.box = "bento/ubuntu-22.04"
config.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
apt-get -y install curl
SHELL
end
/base/testvm/nginx-test/Vagrantfile:
# Load parent Vagrantfile
load "../Vagrantfile"
# define hostname
NAME = "nginx-test"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
apt-get -y install nginx
SHELL
end
UPDATE: It looks like I can do multiple loading just fine as long as I use absolute file paths to the Vagrantfiles (or only use relative paths in the pwd's Vagrantfile, or if all Vagrantfiles are in the same directory). The relative path apparently is relative to the pwd, so when ../Vagrantfile also tries to load ../Vagrantfile, it does so from pwd and not from the parent dir. Then it becomes somewhat of an infinite loop as the parent dir's Vagrantfile keeps trying to load itself from the child dir until vagrant short-circuits itself.
Basically, it loads Vagrantfiles from wherever but the commands in them are executed from the pwd, so relative paths are always relative to the pwd, NOT relative to where the Vagrantfile lives.
Not being able to use relative paths makes things a little less flexible but at least it's a working alternative.
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.
I am new to Vagrant but good in Docker.
In Vagrant I am aware of the fact that
config.vm.provision :shell,path: "bootstrap.sh", run: 'always'
in the Vagrantfile will provision vagrant box while doing vagrant up. With this, the vagrant box interactive console appears after the intended provisioning is done.
But I need to configure in such a way that, first the control goes in to vagrant box console and then the intended script is up and running. Because my requirement is to run a script automatically post vagrant up and not to run a bootstrapped script.
In analogy with Docker, my question can be seen as
what is the Vagrant equivalent for CMD in Dockerfile ?
You can look at vagrant triggers. You can run dedicated script/command after each specific vagrant command (up, destroy ...)
For example
Vagrant.configure("2") do |config|
# Your existing Vagrant configuration
...
# start apache on the guest after the guest starts
config.trigger.after :up do |trigger|
trigger.run_remote = {inline: "service apache2 start"}
end
end
I’ve trying to set up a multi-machine Vagrant project. According to the docs (https://www.vagrantup.com/docs/multi-machine/), provisioning is “outside in”, meaning any top-level provisioning scripts are executed before provisioning scripts in individual machine blocks.
The project contains a Laravel project, and a Symfony project. My Vagrantfile looks like this:
require "json"
require "yaml"
confDir = $confDir ||= File.expand_path("vendor/laravel/homestead", File.dirname(__FILE__))
homesteadYamlPath = "web/Homestead.yaml"
homesteadJsonPath = "web/Homestead.json"
afterScriptPath = "web/after.sh"
aliasesPath = "web/aliases"
require File.expand_path(confDir + "/scripts/homestead.rb")
Vagrant.configure(2) do |config|
config.vm.provision "shell", path: "init.sh"
config.vm.define "web" do |web|
web.ssh.forward_x11 = true
if File.exists? aliasesPath then
web.vm.provision "file", source: aliasesPath, destination: "~/.bash_aliases"
end
if File.exists? homesteadYamlPath then
Homestead.configure(web, YAML::load(File.read(homesteadYamlPath)))
elsif File.exists? homesteadJsonPath then
Homestead.configure(web, JSON.parse(File.read(homesteadJsonPath)))
end
if File.exists? afterScriptPath then
web.vm.provision "shell", path: afterScriptPath
end
end
config.vm.define "api" do |api|
api.vm.box = "ubuntu/trusty64"
api.vm.provider :virtualbox do |vb|
vb.customize ["modifyvm", :id, "--memory", "2048"]
end
api.vm.network "private_network", ip: "10.1.1.34"
api.vm.network "forwarded_port", guest: 80, host: 8001
api.vm.network "forwarded_port", guest: 3306, host: 33061
api.vm.network "forwarded_port", guest: 9200, host: 9201
api.vm.synced_folder "api", "/var/www/api"
api.vm.provision "shell", path: "api/provision.sh"
end
end
I have a block (web) for the Laravel project, where I’ve copied the contents of the Homestead-based Vagrantfile, and an api block that uses the “standard” Vagrant configuration.
To bootstrap the projects, I created a simple shell script (init.sh) that simply clones the Git repositories into git-ignored directories. Given the documentation says configuration works outside-in, I’d therefore expect that script to run, and then the machine-specific blocks, but this doesn’t seem to be happening. Instead, on vagrant up, I receive the following error:
There are errors in the configuration of this machine. Please fix the following errors and try again:
vm:
* A box must be specified.
It seems it’s still trying to provision the individual machines, before running the shell script. I know the shell script isn’t getting called as I added an echo statement to it. Instead, the terminal just outputs the following:
Bringing machine 'web' up with 'virtualbox' provider...
Bringing machine 'api' up with 'virtualbox' provider...
So how can I get Vagrant to run my shell script first? I think it’s failing because the web group is checking if my web/Homestead.yaml file exists and if so, use the values in there for configuring (including the box name), but as my shell script hasn’t been ran and hasn’t cloned the repository that file does not exist, so there is no box specified, which Vagrant complains about.
The issue is that you do not define a box for the web machine. You need to either define the box in the outer space like
config.vm.box = "ubuntu/trusty64"
if you plan to use the same box/OS for both machines or define in the web scope
web.vm.box = "another box"
EDIT
Using the provision property will run the script in the VM, which is not what you want here, as you want the script to run on your host. (and because it runs in the VM, it needs the VM to be booted first)
Vagrantfile is just a simple ruby script, so you could add your script or even an execution to it (from ruby call), a potential issue I could see is that you cannot guarantee the execution and specially that the execution of your init script will be complete before vagrant does it things on the VM.
A possibility is to use the vagrant trigger plugin and execute your shell script before the up event
config.trigger.before :up do
info "Dumping the database before destroying the VM..."
run "init.sh"
end
Running it this way, vagrant will wait for the script to be executed before it runs its part of the up command.
You would need to do some check in your script to make sure it runs only when needed, otherwise, it will run everytime you start the machine (invoking vagrant up), e.g. you could make a check on the presence of the yaml file
How can I start the provisioning of Docker via an external Dockerfile?
My Vagrantfile looks like this at the moment
Vagrant.configure("2") do |config|
config.vm.box = "precise64"
config.vm.define :server_infrastructure do |t|
end
config.vm.provision "docker" do |d|
d.pull_images "ubuntu"
#how does the below work?
#d.build "new-container-name" "local-docker-file-name"
end
end
Your help is greatly appreciated
An option for the Docker provisioner to build images was added in v1.6.0. Download the latest version from the Vagrant website.
Once you've done that, put a Dockerfile next to your Vagrantfile. Add this to your Vagrantfile:
config.vm.provision "docker" do |d|
d.build_image "/vagrant", args: "-t my-name/my-new-image"
d.run "my-name/my-new-image"
end
Now your Docker image will be built and run with vagrant up.
one workaround is through shell provisioning:
config.vm.provision "shell", inline: "docker build -t username/image /vagrant; docker run -d username/image"
For docker to build an image from a dockerfile, dockerfile in question must be presented in the guest machine and the way to ensure this is to use shared folder feature of vagrant.
By default vagrant mounts your project root folder to the new vm under the name /vagrant. But in your case i suggest you share a different folder and put your dockerfiles there. Also by sharing a different folder you can make sure that your dockerfiles are seen read-only by the guest machine.
Now suppose you create a new folder in your projects root directory named "docker" and put your dockerfiles in it. Now if you mount this folder to your guest machine and point docker to use that file you are all set. if you add these lines to your vagrant file it will work as expected.
config.vm.synced_folder "docker/", "/docker_builds", create: true, mount_options: ["ro"]
config.vm.provision "docker" do |d|
d.build_image "/docker_builds", args: "-t my-name/my-new-image"
d.run "my-name/my-new-image"
end