Ansible is a configuration management tool. Generally, configuration management tools give you a consistent and reliable way to ensure that your system is at the desired state you want it to be.
For example, instead of logging into your server and manually running 20 ad-hoc commands to set up a few things on your server, you would declare those 20 commands with a tool such as Ansible.
These commands could be anything. Perhaps it’s to set up a deploy user on your server, or install and configure a service like nginx. The possibilities are endless.
Another upside to using Ansible is that it will only make these changes on your server if it has to. For example, if you manually extracted a zip file a few times, then the entire contents of that zip file will be extracted each time you ran the command.
With Ansible, you could tell it to only extract that zip file if it hasn’t been extracted already. Sure, you could wrap your unzip command in an if statement without Ansible, but when you want to do 150 different things to your server, that strategy becomes extremely tedious and error prone.
Basically Ansible lets you say “I have 1 or more servers and I would like to set up a bunch of things on that server. When I run Ansible, this server will match the specifications I supplied. If I run Ansible again, nothing will change unless I made a change to the specifications”.
The gist of it is, you declare YAML based tasks and then Ansible processes and runs them.
For example, here’s how you could install nginx with Ansible:
- name: Install nginx
In addition to this, you’d likely end up writing a dozen other tasks to do standard “nginx things”, such as deleting the default nginx site, setting up your custom configurations and configuring your system so that nginx starts on boot.
But there’s a couple of problems using Ansible to install nginx (or ANY service):
We’re using apt, which means this will only work if you’re running a Debian based distribution of Linux. You would need a different task if you were running CentOS or something else.
When you run this task, it’s “really” happening at run-time. Imagine the scenario where you test this on a staging server and it works, and then you run it in production but it fails because an APT mirror happens to be down. That is something you cannot predict, but since the installation is happening at deploy time, there’s no way to know if it will work for sure.
Different versions of a distro have different versions of nginx. For example Ubuntu 16 might have nginx 1.9 where as Ubuntu 14 has an older version. To combat this, you have to jump through hoops to set up custom upstreams and pin versions for each package you install.
Honestly, the first problem isn’t that big of a deal since it’s not like you’re changing distros of Linux all the time but the other 2 problems are a serious concern for any system.
You’re also responsible for configuring all of your services yourself. You could reach for third party Ansible “roles” but there are no official roles. The best you can do is find an author who you like and use their stuff, but who knows how long they will maintain their roles.
For example, ~3 years ago I wrote about 25 Ansible roles. A lot of them haven’t been touched in 2+ years because I do not use them personally. I do my best to maintain the roles I continue to use now, but my point is, if you use 3rd party roles, you’re depending on random community authors.
First up, we need to define what Docker lets you do:
Long story short, it’s a way for you to build and distribute your applications. Docker is not a configuration management tool.
Instead of declaring what a system should be in YAML and then running it, you simply build your services into individual Docker images, and then run those images on your server(s).
You can take a Docker image and run it on any operating system that’s capable of running Docker, which at the time of writing this article is every major distribution of Linux. But don’t worry, there are ways to run Docker on MacOS and Windows too.
There’s no YAML to define if all you want to do is run nginx. All you would do is, run a specific Docker command once you have Docker installed. For example:
docker run --rm --name nginx -p 80:80 nginx:1.13-alpine.
docker run --rm --name nginx -p 80:80 nginx:1.13-alpine
Congrats nginx is now up and running. Don’t believe me? Try going to http://localhost or your Docker Machine IP address which is likely http://192.168.99.100.
There’s a couple of interesting things to note here:
You don’t know or care about what distro of Linux nginx is running under. Technically you do know it’s running on Alpine because we’re supplying the version in the command, but you don’t need to know how it works. Also, it will work on any Linux distro that’s running on your server.
nginx was “working” long before you ran it. nginx was already built into a thing called a Docker image, and the above command just ran it. There’s a 0% chance of failure in staging or production, because you’re moving a fully working service from 1 environment to the next.
You always get the same version of nginx. Since we’re not depending on a specific package, you’ll always get the version that you defined when you ran it.
In the above case we used the official nginx Docker image, which means that it’s being maintained by not only Docker, but someone working for nginx.
The image has been rigorously tested and was created with the highest standards. It’s also very well maintained. My point is, both Docker and nginx are vested in this image. Their brands depend on it, so you can be sure it’s top notch.
You can also use community images too, but that’s discussion for another time.
Now that you know a little bit about each tool, it’s time to decide when you should use Docker or Ansible. If I were you, I would just use both because they each solve different problems.
I like to think of it like this. If I’m running a service, such as nginx, Redis, PostgreSQL, or my own web applications then I’m most definitely going to use Docker.
This works great in development, testing and production because the same images get ran across each environment. If I’m hacking away on some code on my dev box then I can be confident it will work the same way in production.
Docker gives you a number of tools to help move these images between servers, and even perform complicated tasks such as load balancing services and performing rolling updates.
I like to use Ansible for things that exist at the server level but aren’t necessarily processes that run. For example, if I wanted to create a deploy user on the system, then it makes sense to use Ansible. This is something that needs to exist on the main system, not inside of Docker.
Ansible is also great for bootstrapping Docker itself. That means installing Docker, along with Docker Compose or any other Docker related tools you need to configure (such as Docker Swarm).
Basically, I would reach for Ansible for performing tasks that set up core functionality for the server to do its thing. That includes the above, along with doing things like hardening SSH’s configuration to make it more secure, or copying over config files that I plan to volume mount with Docker.
Once that server is production ready, then I run my custom services on it with Docker.