Vagrant allows for the repeatable creation and management of virtual machine (VM) development environments.
The VM can contain numerous applications or systems in order to support development.
Created by HashiCorp
Vagrant supports many configuration management systems
including shell (Bash & Powershell), Ansible, Salt, Puppet & others.
Docker is a container platform that is intended to run single processes or small applications.
Docker containers share the host OS but isolate the processes between containers.
(The host still has access to the container processes)
Docker images share layers thus reduces the storage required for duplicate containers.
If the lifecycle of your application lives in containers, stick with containers.
Vagrant VMs have a complete guest OS and full isolation...
including processes, file system & network interfaces from host system and other VMs.
Vagrant itself doesn't have an equivalent to Docker layers,
Providers such as VirtualBox provide Linked Clones.
If your application lifecycle ends on steel or in virtual machines then give Vagrant a try.
Can Vagrant and Docker co-exist? Sure, Docker is a Vagrant provider.
VirtualBox is the default provider,
but VMWare is recommended as it provides better performance and stability.
I have never had any issues with VirtualBox stability, can't comment on performance.
I have had good success using BASH scripts for personal project environments.
In an enterprise environment, a DevOps team might use a configuration management system.
More about this later...
Run the init command in the root of the project folder.
This will create a Vagrantfile in your project.
$ vagrant init [box]
$ vagrant init ubuntu/xenial64
The Vagrantfile is a Ruby script and can be updated in any programmic way you like as long as it returns the appropriate values.
There are examples of consuming JSON configuration files using Rudy and translating the JSON objects into the necesary Rudy values.
If you plan on using Puppet for provisioning you might consider PuPHPet, "a gui configurator for the Vagrant automation tool" to create your Vagrantfile & Puppet configuration.
Vagrant.configure("2") do |config| # Every Vagrant development environment requires a box. You can search for # boxes at https://atlas.hashicorp.com/search. config.vm.box = "base" end
The Vagrantfile will initially have a lot of comments but only 1 line is initially important... "config.vm.box"
You have to search for a box from the Vagrant website that suits your needs.
You will find boxes available for various needs including...
full LAMP stacks, Rudy, Docker, Chef, Puppet.
$ vagrant box add "ubuntu/xenial64"
Popular boxes are "hashicorp/precise32" or "hashicorp/precise64" (Ubuntu 12.04)
Or "laravel/homestead" if you are a Laravel developer, you may have heard of it :P
I prefer "ubuntu/xenial64" (Ubuntu 16.04)
Vagrant Boxes are equivalent to Docker Images.
Vagrant.configure("2") do |config| # Every Vagrant development environment requires a box. You can search for # boxes at https://atlas.hashicorp.com/search. config.vm.box = "ubuntu/xenial64" # Ubuntu 16.04 end
You are now ready to start your Vagrant box with the image selected.
Inline shell script provisioning
config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y apache2 rm -rf /var/www/html ln -fs /vagrant /var/www/html systemctl restart apache2 SHELL
Associating a provisioning shell script
config.vm.provision "shell", path: "./scripts/update.sh"
config.vm.hostname = "vagrant-intro-simple" config.vm.network "forwarded_port", guest: 80, host: 8787
Multiple config.vm.network entries are allowed.
config.vm.synced_folder "src/", "/srv/website"
By default, the root of your project on the host is shared to the guest at /vagrant.
Provider specific configurations can be contained within a conditional section to be only applied appropriately.
config.vm.provider "virtualbox" do |v| # Overrides the name of the VM in VirtualBox v.name = "test-vagrant" v.linked_clone = true end config.vm.provider "vmware_fusion" do |v| v.linked_clone = false v.vmx["memsize"] = "1024" v.vmx["numvcpus"] = "2" end
When using linked clones 2 VMs will be created initially, 1 base VM and 1 cloned VM.
Additional cloned VM will only create a single additional VM.
The base VM will not be started, just used as a "data source", do NOT delete the base VM!
$ vagrant up
This will download the box image, if not cached, create the VM and start it in headless mode
The VM will be named after the folder the Vagrantfile exists appended by a timestamp.
$ vagrant halt
$ vagrant status # outputs status of the vagrant machine(s) $ vagrant ssh # connect to the machine using ssh $ vagrant rdp # connects to machine via RDP $ vagrant provision # provisions an existing machine $ vagrant destroy # remove VM - Danger Will Robertson! $ vagrant validate # validates the Vagrantfile
$ vagrant box # manages boxes: installation, removal, etc. $ vagrant box add # add box to the local cache $ vagrant box list # list boxes in local cache $ vagrant box outdated # check if boxes in use are up to date $ vagrant box prune # removes old versions of cached boxes $ vagrant box remove # remove box from local cache $ vagrant box repackage # package a new box to local cache $ vagrant box update # update the box to the latest version
Vagrant.configure("2") do |config| # set the box config.vm.box = "ubuntu/xenial64" # make the vm accessible from the host config.vm.network "forwarded_port", guest: 80, host: 8787 # give it a unique hostname so we don't collide with other VMs with the same box config.vm.hostname = "vagrant-intro-simple" # do some stuff after the initial boot... i.e.: install apache and create a link to the source code config.vm.provision "shell", inline: <<-SHELL apt-get update apt-get install -y apache2 rm -rf /var/www/html ln -fs /vagrant /var/www/html systemctl restart apache2 SHELL end
Vagrant.configure("2") do |config| config.vm.box = "ubuntu/xenial64" config.vm.provision "shell", inline: <<-SHELL apt-get update SHELL config.vm.define "web" do |web| web.vm.hostname = "vagrant-intro-multi-web" web.vm.network "forwarded_port", guest: 80, host: 8887 web.vm.network "private_network", ip: "10.0.0.10" web.vm.provision "shell", path: "./scripts/apache.sh" end config.vm.define "db" do |db| db.vm.hostname = "vagrant-intro-multi-db" db.vm.network "private_network", ip: "10.0.0.20" db.vm.provision "shell", path: "./scripts/mariadb.sh" end end
#!/usr/bin/env bash echo "Installing Apache, PHP, xDebug & MariaDB client" apt-get install -y apache2 libapache2-mod-php php php-mcrypt php-curl php-mysql php-xdebug mariadb-client-core-10.0 echo "Creating Apache html folder soft link" rm -rf /var/www/html ln -fs /vagrant /var/www/html echo "Enabling mod_rewrite" a2enmod rewrite echo "Disabling sendfile as per vagrant suggestion" cat <<- EOF >> /etc/apache2/apache2.conf EnableSendfile Off EOF echo "Adding xDebug configuration" cat <<- EOF >> /etc/php/7.0/mods-available/xdebug.ini xdebug.remote_enable=1 xdebug.remote_port=9000 xdebug.remote_connect_back=1 xdebug.idekey=PHPSTORM EOF echo "Restarting Apache" systemctl restart apache2
#!/usr/bin/env bash echo "Installing MariaDB" apt-get install -y mariadb-server echo "Creating vagrant user and vagrant_intro database" mysql -u root -e "CREATE USER 'vagrant'@'10.0.0.10' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON * . * TO 'vagrant'@'10.0.0.10'; CREATE USER 'admin'@'%' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON * . * TO 'admin'@'%'; FLUSH PRIVILEGES; CREATE DATABASE vagrant_intro; CREATE TABLE vagrant_intro.users(id int(10) unsigned auto_increment primary key, username varchar(255) not null);" echo "Updating bind address in /etc/mysql/mariadb.conf.d/50-server.cnf to 0.0.0.0 to allow external connections." sudo sed -i "s/.*bind-address.*/\bind-address = 0.0.0.0/" /etc/mysql/mariadb.conf.d/50-server.cnf echo "Restarting MySQL" systemctl restart mysql
Vagrant is integrated into numerous IDEs including Eclipse, Komodo and Jetbrains products.
Most Vagrant VMs are headless, but can still be managed from VirtualBox.
It is not recommended though as any changes made in VirtualBox will not be reproducable.