UPDATE: I wouldn't really recommend this approach anymore! Instead you should look into Docker for containerization and Stack for the Haskell side
I recently started looking into ways that I could improve my current deployment workflow. Since my server doesn't have much RAM, I currently build the binaries locally in a Virtual Machine (VM from here on out) and then send them to the server using scp
.
Although I can't do much about the server part (except buying a bigger server), I can do something about what I do locally. I set out to check what possibilities I had, and ended up looking at Vagrant.
While it doesn't cut hugely from the local part of my deployment hassles, it could at least streamline it a bit, and help remove the cabal hell that I'd sometimes run into with my current setup (having multiple applications that I deploy from the same VM).
A quick overview of the article:
Vagrant basically manages your VMs in a manner that is local to each project you use it in (although you can use it in a more general way). As standard it uses VirtualBox, but you can also use it with other providers, such as VMWare.
That said, for the purpose of being convenient to most people, I'm going to assume VirtualBox throughout this article, since that is the free alternative of the two I mentioned.
Vagrant has a nice getting started series that you should follow if you want to understand Vagrant better (I highly recommend it, it's not that long). I'm going to try and give a quick explanation of how it works though.
Vagrant uses something called a box
. These serve as the basis for the Vagrant setups you create, and are in essence just VMs with the bare minimum installed. To add a box you do,
which will add a VM with a 64-bit version of Ubuntu 12.04 (Precise Pangolin). It will from then on be under the name precise64
, stored on your computer. You can change the url with any other url or a local filepath, and the name with what you want your box to be refferred to as.
You can try and search around if you can find a box that matches your needs, but if you want to customize it completely, I recommend creating your own.
Before we start creating our Vagrant box, we need to know what our server looks like, so we can get the correct distro image.
On Debian, and most likely others, you can check out what version you're using with, cat /etc/*-release. This will output something like,
PRETTY_NAME="Debian GNU/Linux jessie/sid"
NAME="Debian GNU/Linux"
ID=debian
ANSI_COLOR="1;31"
HOME_URL="http://www.debian.org/"
SUPPORT_URL="http://www.debian.org/support/"
BUG_REPORT_URL="http://bugs.debian.org/"
which tells us we're dealing with Debian Jessie (testing).
Next up, is the architecture, and we get that by running, uname -r, which may look like,
with amd64
telling us it's 64 bit and built for amd. You should also try and match the kernel version as best you can though (the 3.2.0
).
First off, you need to install VirtualBox, and then go and download the installer image for the distro you want. Since my server uses the amd64 version of Debian Jessie, I'm going to go ahead and download the amd64 netinst CD image.
After downloading it you launch it in VirtualBox and install it as you normally would a Virtual Machine. Following Vagrant's guidelines for creating a base box, I set the following properties (with some altercations of my own):
40GB
dynamically resizing drive2GB
RAM (they recommend 360MB, but compiling is quite memory intensive, and my laptop has 8GB of memory already)And as recommended, I set the following values:
vagrant-debian-jessie
vagrantup.com
vagrant
vagrant
vagrant
Now that you have the VM up and running, it's time to set it up as a Vagrant box.
Vagrant assumes sudo access without password, since that allows Vagrant to do a lot of neat thigns without our intervention (installing packages, setting up network folders etc).
First we change to root, and then,
followed by editing the sudoers file with visudo. Here we add,
## /etc/sudoers
)
which allows users in the admin group to use sudo
without a password.
After that, we need to add the vagrant user to the admin group,
While still logged in as root, we need to uninstall VirtualBox packages and install linux headers so we can install the tools VirtualBox uses,
note that there might not be able to find any of the packages we tried to remove, and that is fine, but sometimes they get installed automatically depending on the distro etc.
After that, we mount the Guest Additions CD, which is located in the Devices
menu and then something like Insert Guest Additions CD Image...
. We then run,
The last thing we need to do as root is installing the packages that we need,
depending on what distro you use, rubygems might fail to install. On debian Jessie, gems was included in the ruby package. After this, we need to set SSH to not use DNS. This will give a small speedup when SSH'ing to the VM,
I intentionally didn't include chef
here, since I don't personally need it, and the install process is also a tad more bothersome than puppet's. You would just add it here if you need it though.
This step is a bit more personalized, whereas the rest is for creating a general Vagrant box. Since I intend to use this box for deploying and testing my Haskell packages, it's relevant for me to install the haskell-platform
and an updated version of cabal
to save some time when I create the VM with vagrant up later on.
You can also do these steps automatically on the creation of a box, by adding a shell script with the comamnds inside the VagrantFile
in your project, sorta like the example with automatically setting up Apache.
That said, I did the following,
This will save me a bit of time compiling things (especially cabal-install
) over and over again.
Finally, you can now jump back to the vagrant user with exit
, since the last bit needs to be done in the home folder of the vagrant user. We need to setup the authorized SSH keys for the vagrant user so vagrant ssh works easily using their standard insecure key pair,
Now we just need to cleanup the VM and shut it down,
The final step is to export the VM as a vagrant box and add it. Vagrant neatly picks up the VMs that VirtualBox manages (assuming you use the default location), so you can simply do,
where Jessie64
is the name I gave the VM inside VirtualBox. It will create a file named package.box
at the location you ran the command.
Now you can add the box to Vagrant just like any other box,
I'm going to give a little example of how I would set up and use Vagrant in one of my projects.
First I create the VagrantFile inside my project, using the box I just created,
After this, I create a small script that serves to setup my project with the packages that it needs. The content of the script should be self-explanatory. I name this bootstrap.sh
and it contains the following,
#!/usr/bin/env bash
&& &&
The /vagrant
folder is a folder that Vagrant keeps in sync with the host filesystem. It is the folder that Vagrant is running in, which in this case is my project folder.
I then add a line,
in my VagrantFile
inside the Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
section.
After this my project I can do,
and my project will get completely bootstrapped and ready for me to compile it and deploy. This usually means doing vagrant ssh and then two simple commands,
with this, I've now build my binary, packaged it up and sent it to my server.
If there is some trouble with some packages I can simply do,
This will destroy the VM in my project (it still keeps the box we created before, don't worry). You can change destroy with halt to shut down the VM or suspend to simply suspend it. There is some information about the pros and cons to the three options in the Vagrant documentation.
Vagrant has greatly made me more comfortable messing around with my local VMs, since it's so easy to just destroy one and spin up an exact replica.
It also allows me to easily sandbox my projects completely by having VagrantFile's in each of them, with exactly what they need. Since Vagrant syncs the project folder, I can make changes using my preferred tools and the VM keeps the folders in sync.
As to what comes after this; I'm looking into puppet at the moment, since I would like to make setting up my server completely automatic. This will be a future article for itself though.