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 <project_dir> -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:
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?
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:
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.
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.
@thrig, this needs to be applied to containers running on one system, not multiple ones. If I needed to apply the command to multiple systems I would simply use an inventory to accomplish that.
No @techraf, it’s more efficient to use docker ps --filter in that instance. The question is about idempotency and best-practice, since running the above as shell commands is not idempotent. You’d have thought it would make more sense to use ansible’s own docker API to accomplish this, if able.
Idempotency is an important concept with ansible, @techraf. When running shell commands naively using ansible, we lose the ability to assess whether the system has met the playbook’s requirements (and skip the task) or not. That’s why it is always advisable to run ansible native API modules instead of shell. The ansible manual states, wrt to stdout_lines, not to use it often on this account.