Hello Ansible!

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Welcome automation lovers!!! You've come to the right place, because this tutorial is all about Ansible! And boy, it's just a lot of fun. Imagine if we could take control of an army of tiny, but mighty robots... simply by writing a YAML file. Think of the servers we could launch! The infrastructure we could build! The code we could deploy! The laundry we could wash! Well, actually, these Ansible robots are sorta virtual robots, so yes to automating things like server setup... but probably not to automating your laundry. However, I do hope one of you proves me wrong!

To be the best robot overlord you can be, download the course code from this page and code along with me! After unzipping the file, you'll find a start/ directory, which will have the same code that you see here. Follow the README.md file to get your project up and running. Well actually, setting up the project isn't that important, because we will ultimately use Ansible to do that! But if you want to see things working now follow that file. The last step will be to go into your terminal, move into the directory, and run

php bin/console server:run

to launch the built-in PHP web server. In your browser, open up http://localhost:8000. Introducing, our new side project here at KnpUniversity: MooTube! Yes, far too many cows are out of shape and need some serious exercise! So in case this whole tutorial business falls through, we'll turn to bovine fitness: a subscription-based service to keep cows in tip-top shape.

The App & Our Mission!

The first version of our app is done actually! But there are two problems. First, we'd love to have a way to easily boot up development servers complete with PHP, a web server and anything else we need to get our app working. And second, it would be bananas if we could have an automated way to deploy code to Amazon EC2 servers. Well, that's exactly what we're going to do.

Our project is a Symfony application... but if you're not used to Symfony, that's no problem. From a server-perspective, the app requires a few interesting things:

... lines 1 - 9
class DefaultController extends Controller
{
... lines 12 - 14
public function indexAction()
{
$videos = $this->getVideoRepository()
->findAll();
$tags = $this->getUniqueOrderedTags($videos);
// Redis cache
try {
if ($this->getRedisClient()->exists('total_video_uploads_count')) {
$totalVideoUploadsCount = $this->getRedisClient()->get('total_video_uploads_count');
} else {
$totalVideoUploadsCount = $this->countTotalVideoUploads();
$this->getRedisClient()->set('total_video_uploads_count', $totalVideoUploadsCount, 'ex', 60); // 60s
}
... lines 29 - 35
} catch (ConnectionException $e) {
$totalVideoUploadsCount = $this->countTotalVideoUploads();
$totalVideoViewsCount = $this->countTotalVideoViews();
}
return $this->render('default/index.html.twig', [
'videos' => $videos,
'tags' => $tags,
'totalVideoUploadsCount' => $totalVideoUploadsCount,
'totalVideoViewsCount' => $totalVideoViewsCount,
]);
}
... lines 48 - 109
}

First, it needs a database connection to load the video info. And second, it uses Redis to cache a few things. So when we boot up our servers, that stuff needs to be there.

Introducing Ansible

There's a good chance you've setup a server before. I know I've setup a ton, and it's always the same, manual, error-prone, confusing process: SSH into the server, manually run a bunch of commands, have errors, Google things, run more commands, and edit a bunch of files.

Ansible... kinda does the same thing. When you execute something with Ansible, it ultimately SSH's onto the server and runs some commands. Ansible is said to be "agentless", which just means that you don't need to install anything on the target server. As long as you can SSH to a server, you can unleash your Ansible robot army onto it. That's pretty cool.

But Ansible is even more interesting than that. When you execute an Ansible task - that's what they're called - it is idempotent... well, usually it is. Idempotency is an obscure - but cool - word to mean that Ansible tasks don't just dumbly run a command. What they really do is guarantee that the server finishes in a specific state. For example, if we tell Ansible to create a directory, it doesn't necessarily mean that it will run a mkdir command. Instead, it means that Ansible will just make sure that the directory exists - only creating it if necessary.

This idea of guranteeing a "state" - like "this directory must exist" - is much more powerful than randomly running commands over SSH. These tasks also send back JSON info about what happened, which we can use to tweak and influence what happens next.

Installing Ansible

Ok, let's start playing already! First, we need to install Ansible of course! Since I'm on a Mac, I've already installed it with: brew install robotarmy. I mean,

brew install ansible

If you're on different system, check out the Ansible docs for your install instructions. Unfortunately, if you're using Windows, you can't use Ansible. Well, you can't natively. If you're virtualizing a Linux machine or are using Windows 10 with the Linux subsystem, then you can install Ansible there.

Once you've got it, run

ansible --version

to make sure you have at least version 2.0.

Ok team, let's boot up our robot army!

Leave a comment!

This tutorial is built using an older version of Symfony, but the core concepts of Ansible are still valid. New versions of Ansible may contain some features that we don't use here.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.12
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.0
        "snc/redis-bundle": "^2.0", // 2.0.0
        "predis/predis": "^1.1" // v1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.8
        "symfony/phpunit-bridge": "^3.0", // v3.1.4
        "doctrine/data-fixtures": "^1.1", // 1.3.3
        "hautelook/alice-bundle": "^1.3" // v1.4.1
    }
}