Lately I’ve been tinkering with some of my Ansible roles to improve the support for multiple-environment deployments. Previously, I’d been using a somewhat naive approach of simply including environment-specific task lists using conditionals in tasks/main.yml like so:
- include: ubuntu-install.yml when: ansible_distribution == "Ubuntu" - include: centos-install.yml when: ansible_distribution == "CentOS" However there are a number of problems with this approach: 1) it requires keeping and maintaining separate task lists for each environment, 2) deployments to different environments are done sequentially rather than in parallel, and 3) it pollutes the output from ansible with lots of skipped tasks for unused environments.
In general, it is better to use environment-specific variables with the same task list for different environments, this reduces the maintenance overhead, allows for parallel deployment, and reduces the size of the output.
I’ve been working with Ansible quite a lot for the past year, but until this weekend I really hadn’t had much of a chance to get acquainted with Docker. There’s a lot of talk on the Internet about how these two different pieces of technology “naturally” compliment each other, but clearly there’s also a lot of confusion as well, and there are still a few hard interface problems that need to be sorted out.
When I first hear that Ansible could be used to both provision and deploy Docker containers, I naturally assumed that this meant I’d be able to adapt my existing Ansible workflow, possibly with the addition of some new modules to use, and have access to the power of Docker, but this hasn’t proven to be the case at all.
In fact, the two functionalities, deployment and provisioning, are completely disconnected from each other. To build images and deploy containers there are two new modules: docker_image and docker, that work pretty much like you’d expect. It should be noted that the docker_image module can really only be used to build images using existing Dockerfiles, and in fact there is little that it can to do actually affect the internal workings of the image. To provision, that is configure, an image, an entirely different approach is required: the playbook or role needs to be copied into the container filesystem and then run as a deployment to localhost. The problem with this, of course, is that this makes it very difficult to coordinate configuration and state information. This probably has a lot to do with the approach to containerized deployment emphasized by Docker, wherein images are relatively static, and configuration is done at deploy time by running scripts in linked containers. This may even be the best way to do things, but it does make it difficult to adapt existing playbooks and roles to work with docker.
Today at the HARNESS project integration team meeting I gave a brief presentation wherein I discussed DevOps and some of my favorite tools, namely Ansible, Vagrant, and Docker.
The central thesis of my talk was that a modern devops approach, based in version-controlled configuration management implemented using industry-standard deployment tools and making use of continuous integration and rolling updates is a must for creating robust distributed systems with lots of moving parts.