Pulling images
The sandbox's control container has the Docker daemon you've been
using throughout the labs โ docker ps already shows the six worker
containers. Now drive it via Ansible.
community.docker.docker_image is the declarative wrapper. Set its
state / source and re-run it as many times as you like โ only
the first run actually does work.
Write pull.yml:
---
- name: Make sure the alpine image is present locally
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Pull alpine:3.20
community.docker.docker_image:
name: alpine
tag: '3.20'
source: pull
state: present
register: alpine_pull
- name: Report
debug:
msg: "alpine:3.20 changed={{ alpine_pull.changed }}"
...
The first run pulls (โ 3MB compressed), the second is a no-op:
ansible-playbook pull.yml
ansible-playbook pull.yml
You should see changed=true the first time and changed=false the
second โ that's idempotence.
Click Verify step.
Hint
Use `community.docker.docker_image` with `source: pull` against `localhost`.
Running a container
docker_container accepts most docker run flags. The key field is
state โ started, stopped, present, absent.
Write run.yml:
---
- name: Run an ephemeral utility container
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Start a long-lived alpine
community.docker.docker_container:
name: cac-sleeper
image: alpine:3.20
state: started
restart_policy: 'no'
command: ["sleep", "300"]
- name: Inspect it
community.docker.docker_container_info:
name: cac-sleeper
register: info
- name: Show the IP
debug:
msg: "cac-sleeper running, ID={{ info.container.Id[:12] }}"
...
Apply:
ansible-playbook run.yml
docker ps --filter name=cac-sleeper
You should see the container listed under docker ps.
To stop it via Ansible:
ansible localhost -c local -m community.docker.docker_container \
-a 'name=cac-sleeper state=absent'
That's the kind of one-liner the community.docker collection gives
you for free. Click Verify step.
Hint
`docker_container` is declarative โ set `state: started`, pass `image`, `ports`, `env`.
docker-compose from Ansible
community.docker.docker_compose_v2 applies a compose file the same
way docker compose up -d would, but with idempotence and the rest
of the playbook's variables in scope.
Create compose/docker-compose.yml:
services:
cache:
image: alpine:3.20
command: ["sh", "-c", "sleep 600"]
container_name: cac-compose-cache
worker:
image: alpine:3.20
command: ["sh", "-c", "sleep 600"]
container_name: cac-compose-worker
depends_on:
- cache
Then compose.yml:
---
- name: Stand up the compose stack
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Apply docker-compose
community.docker.docker_compose_v2:
project_src: ./compose
state: present
register: result
- name: List started containers
debug:
msg: "{{ result.containers | map(attribute='Name') | list }}"
...
mkdir -p compose
# (paste docker-compose.yml content above into compose/docker-compose.yml)
ansible-playbook compose.yml
docker ps --filter name=cac-compose
To tear it back down:
ansible localhost -c local -m community.docker.docker_compose_v2 \
-a 'project_src=./compose state=absent'
Click Verify step while both cac-compose-cache and
cac-compose-worker are running.
Hint
`community.docker.docker_compose_v2` consumes a compose YAML on the controller.
Talking to a REST API
Cloud-provider modules (amazon.aws.ec2, google.cloud.gcp_compute_instance,
azure.azcollection.azure_rm_*) all wrap HTTP REST APIs underneath.
The uri module is the same machinery, exposed directly.
Write api.yml โ talk to the public httpbin echo service:
---
- name: REST as a first-class module
hosts: localhost
connection: local
gather_facts: false
tasks:
- name: GET an echo endpoint
uri:
url: https://httpbin.org/json
method: GET
return_content: true
body_format: json
register: echo
- name: Show one field from the response
debug:
msg: "first slide title: {{ echo.json.slideshow.title }}"
- name: POST a payload
uri:
url: https://httpbin.org/post
method: POST
body_format: json
body:
deployed_by: ansible
host: "{{ inventory_hostname }}"
return_content: true
register: posted
- name: Confirm the server echoed our body
debug:
msg: "server saw deployed_by={{ posted.json.json.deployed_by }}"
...
ansible-playbook api.yml
That's the same pattern every cloud module uses internally:
- Build a request body from variables.
- POST/PUT/GET against a cloud API.
- Register the response into a variable.
- Branch on it (
when:).
- Re-run the task in check mode to preview without applying.
To plug in real AWS / GCP / Azure modules, install the matching
collection (ansible-galaxy collection install amazon.aws) and
set credentials via environment variables or a Vault file.
Click Verify step.
Hint
Most cloud modules wrap REST. `uri:` shows the underlying mechanic โ use `return_content: yes` + `body_format: json`.