This post is an introduction to automatic server management. It is particularly relevant if you have been using a single server, maybe your laptop, to learn to program, and you are starting to get projects interfering with each other. Things like conflicting software versions, or some bug that you can't seem to solve. If you want to start using VMs to separate things, then read on...
If you have ever tinkered around with a web server, you will have probably broken it by accident, probably whilst trying to update something. This puts you in a frustrating and awkward situation, because you still want carry out the upgrade, but now you also have to fix the thing you have just broken. But how do you know your ‘fix’ won’t cause more problems? And you still have fix the initial problem. A nice way to get round this would be to have a place to experiment, until you get the fix working. Then you can apply the fix to your nice, clean, working server, and get back to coding.
This is where virtual machines come in. They allow you create disposable servers, and experiment to your heart’s content. Even better than that, you can save the state of a VM, so you don’t have to start from scratch each time. This is called a snapshot. If you get into the swing of things with this workflow, you will start snapshotting your VM before you start making system changes. After a while, you no longer dread updates - you just have to be careful to save your VM, and remember what you did last time that broke it, so you don’t repeat your mistake. Now your server is much more robust. Excellent - you can spend more time than ever coding!
VM’s also provided another great bit of functionality - the ability to clone VMs. This meant that you could setup your first server, get everything working, then clone it, and use your clone as a live server. Now you have two identical VMs, and you can be working on one, while the other is serving your users. You just have to be doubly careful - snapshot your test VM before getting a fix working, then apply the working fix to your live server.
So now you have 2 VMs, and you are working away on your app. It’s going great, and you realise it’s time to get some help - so you get a front-end person involved, to make your site look slick. You copy your VM and give it to your new front-end dev. Everything is going well still, but suddenly your front-end dev encounters a problem that you haven’t found yet. Ok - so snapshot the VM, fix it, send round the fix and carry on. Oh - and apply the fix to your original VM and update the live VM. But you also have a fix for your front-end dev. Before you know it, you are passing round fixes between 3 VMs, and things are getting complicated. Gah - you’re spending more time fixing your server than writing code.
So this is where Vagrant, Docker and Ansible come in - they all do slightly different things, but they all help to solve a major problem - how can you make sure that your app runs reliably for different users, on different VMs and eventually, at different stages in the development process (e.g. dev vs test vs live)? The solution is to create a kind of ‘recipe’ for your server, which you can then pass to your chosen tool, and then ask it to cook you up a brand new server. In fact, one of the tools using for this is called Chef, so this analogy has already been thought about.
So this idea of a recipe for a server is really useful - but people want to use it in very different ways. The basic usage is one developer with a simple site, probably just personal project. A low-level use might be a small tech company, with 5 developers and 1 product. A mid-level use might be an agency with 30 staff and 10 products for various clients. Finally you might have a large multinational, with hundreds of developers and dozens of apps, spread across different datacentres. These use cases explain why there isn’t just one tool that can do everything, and you might want to think about your plans, before you jump into the deep-end with one of the tools.
Vagrant - a tool for managing VMs
Vagrant is a program that launches virtual machines from a chosen template, and gives you the option of passing over your server recipe. Often, a Vagrant machine will use a ‘base box’ (e.g. an unmodified version of Linux), and then use programs called Chef or Puppet to put install your server on the base box. You can choose exactly which base box you want from some pre-made ones available online, or make your own. Then you can tweak your server recipe, so it works in exactly the way you want. All you need to do then is give you recipe and your base box to your friend, and tell them to turn it on using vagrant. They should end up with exactly the same server as you. If you are using a pre-made base box, the advantage is that the recipe is only a tiny file, so it is very portable.
The problem with Vagrant is that every time you update your Vagrant file, you need to restart your VM. The other problem is that vagrant wants to create a whole VM for each instance - which can quickly take up a lot of space and resources (RAM / CPU etc)
Docker - a tool for managing & deploying application environments
Docker is a containerisation app. It specialises in putting everything your app needs in 1 place. However, it doesn’t come with a ‘kernel’, so it needs to be installed on a pre-built VM. This is fine if you already have some VMs floating around - you can just put the docker container on there, and turn it on. This means you can put more than 1 container on a VM, and you can clone your app to more than 1 VM (don’t worry about how to have an app working across more than 1 VM - that’s another topic).
This is great in two ways - firstly imagine you are an agency, and you have clients that you work with one after the other. You will start from scratch for each client, but technology will keep improving. Eventually, you will have older clients on older technology, and newer clients based newer technology. You might have a VM that has an old project on it, and it could easy host another few projects. The problem is that it has this old version of e.g. PHP on it. It’s a pain to have different versions of PHP on the same VM. with Docker, the PHP is inside the container, and doesn’t interfere with any other parts of the server. You can easily put another container on the VM, with a newer version of PHP, and have them running side-by-side.
The second reason containerisation is great is when you have a surge in traffic - imagine a betting app during the World Cup. You can copy and paste your container onto loads of extra VMs, to give you extra capacity, without having to buy extra hardware.
Ansible - a tool for building VMs and applications
Ansible is a configuration management and orchestration tool. This means Ansible can help you setup VMs and applications automatically. The aim is for your app to be setup in the same way each time. You can also have parts of your recipe that will only be followed if a certain condition is met. This condition could be a particular OS (e.g. use yum on Red Hat and apt-get on Ubuntu), or a variable that you define yourself (e.g. environment = dev / test / live).
Unlike Vagrant, Ansible assumes you have a VM ready and waiting to follow instructions. This means you will have to use another tool to actually create your remote VMs. This could be Vagrant itself, or your hypervisor (e.g. VirtualBox)
So as you can see, there are lots of ways to approach the problem of building, maintaining and deploying servers. These modern tools all help to solve the previous generation of problems - updating servers and keeping the updates in sync - but of course, the tools come with their own caveats and perks. It’s also worth noting that these are cutting edge tools, and are being rapidly developed, so they may start to encroach on each other’s previous domains.
Finally, I can give you a little flavour of what I do. I like to use Vagrant to spin up VMs, and have Ansible configure them for me. As I am just running a small number of servers, I don’t mind that Ansible is a little slower to deploy than VM, and the added complexity of converting my app into a Docker container isn’t worth the effort for me. However, in another project, I am planning to have a high-availability setup - where I have 3-5 production servers working in parallel. I this case, I might well use Docker to push the production version of my app, inside a nice neat Docker container, to my live servers.