Tuesday, June 2, 2015

Running an Ansible command in multiple docker containers



I need to run a command against multiple running containers. For example, let's say my app needs to run a data structure update against a database after receiving code updates. To do this, we want to run docker build -t latest, then docker stop; docker rm; docker run. At this stage we can assume we have updated the container's core code, but we still need to run that database update using the app's own tooling.



Essentially, I need some way to get a list of running containers, filtered by some criteria, and register those container IDs with Ansible. Then for each container we run the command.




Like this:



- name: Get list of running containers
docker:
image: my-image:latest
state: running
register: container_ids



This task would store the list of running containers which use my-image:latest to container_ids. Then we exec the command:



- name: Exec the database update
cmd: "docker exec -it {{ item }} my-app-db-update.sh"
with: container_ids


Since we don't really want to use active containers for this type of operation, a better option would be to start a new single-use container acting on the same data instead:



- name: Run the database update

cmd: "docker run --rm --volumes-from {{ item }} --link:mydb:db my-app sh -c 'my-app-db-update.sh'"
with: container_ids


The above is only pseudo-code -- it won't actually run. How do I accomplish the task of storing a list of running docker containers which meet certain criteria, so that I can then apply a command to each container in the list using either docker exec or docker run?



There's surprisingly little about this online.


Answer



Given the following example output from docker ps:




$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e21761c9c44 busybox "top" 22 minutes ago Up 22 minutes agitated_yonath
7091d9c7cc56 nginx "nginx -g 'daemon off" 23 minutes ago Up 23 minutes 80/tcp, 443/tcp fervent_blackwell


This playbook will give you an idea of how to capture the data you need and how to run actions iterating on the provided list. It's a very simple example, and you'll have to adapt it to your needs. That's on purpose:



---
- hosts: localhost

gather_facts: no
tasks:

- name: gather list of containers
shell: docker ps | awk '/{{ item }}/{print $1}'
register: list_of_containers
with_items:
- busybox

#- name: debug

# debug: msg="{{ list_of_containers }}"

- name: run action in container(s)
docker_container:
name: temp-container
image: busybox
command: uptime
cleanup: yes
detach: yes
register: result_of_action

with_items:
- list_of_containers.results.stdout_lines


The interesting parts are:




  1. Gather the list of containers in a given host (localhost in my example).
    I've used a plain shell invocation to be able to use awk to filter out the output. The result is stored in a register. Since the input is a list, this will have a direct consequence on how to retrieve the data back, more below. Uncomment the debug task in between to compare the data stored in the register with and without a list.


  2. Iterate over the register's results (container ID) and use the docker_container module to run an action (command parameter). You can use links and volumes_from in your docker_container invocation. Check the on-line documentation of the module for the details.




No comments:

Post a Comment

linux - How to SSH to ec2 instance in VPC private subnet via NAT server

I have created a VPC in aws with a public subnet and a private subnet. The private subnet does not have direct access to external network. S...