Ansible with Docker¶
- In this lab we use Ansible to install Docker, manage containers, images, networks, and volumes, and orchestrate multi-container applications.
- Ansible’s Docker modules bridge configuration management and container orchestration.
What will we learn?¶
- Installing Docker with Ansible
- Managing containers with
docker_container - Managing images with
docker_image - Managing networks and volumes
- Deploying Docker Compose stacks
Prerequisites¶
- Complete Lab 017 in order to have a working understanding of package and service modules.
01. Install Docker with Ansible¶
---
- name: Install Docker
hosts: all
become: true
gather_facts: true
tasks:
- name: Install prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Add Docker GPG key
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
when: ansible_distribution == "Ubuntu"
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
when: ansible_distribution == "Ubuntu"
- name: Install Docker Engine
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: true
when: ansible_os_family == "Debian"
- name: Start and enable Docker
ansible.builtin.service:
name: docker
state: started
enabled: true
- name: Add users to docker group
ansible.builtin.user:
name: "{{ item }}"
groups: docker
append: true
loop:
- "{{ ansible_user }}"
ignore_errors: true
02. Install community.docker Collection¶
03. Manage Docker Containers¶
tasks:
# Run a container
- name: Run nginx container
community.docker.docker_container:
name: my-nginx
image: nginx:latest
state: started
restart_policy: unless-stopped
ports:
- "8080:80"
volumes:
- /opt/website:/usr/share/nginx/html:ro
env:
NGINX_HOST: "{{ inventory_hostname }}"
NGINX_PORT: "80"
# Stop a container
- name: Stop old container
community.docker.docker_container:
name: old-nginx
state: stopped
# Remove a container
- name: Remove old container
community.docker.docker_container:
name: old-nginx
state: absent
# Run container with resource limits
- name: Run app with limits
community.docker.docker_container:
name: myapp
image: myapp:v2.1
state: started
memory: "512m"
cpus: "0.5"
restart_policy: on-failure
restart_retries: 3
labels:
app: myapp
version: v2.1
managed-by: ansible
04. Manage Docker Images¶
tasks:
# Pull an image
- name: Pull nginx image
community.docker.docker_image:
name: nginx
tag: latest
source: pull
# Pull a specific version
- name: Pull specific version
community.docker.docker_image:
name: nginx
tag: "1.24"
source: pull
# Build an image from Dockerfile
- name: Build application image
community.docker.docker_image:
name: myapp
tag: "{{ app_version }}"
source: build
build:
path: /opt/myapp/src
dockerfile: Dockerfile
pull: true
args:
BUILD_DATE: "{{ ansible_date_time.date }}"
VERSION: "{{ app_version }}"
# Remove an image
- name: Remove old image
community.docker.docker_image:
name: myapp
tag: v1.0
state: absent
05. Manage Networks and Volumes¶
tasks:
# Create a network
- name: Create app network
community.docker.docker_network:
name: app-network
driver: bridge
ipam_config:
- subnet: "172.20.0.0/16"
gateway: "172.20.0.1"
# Create a named volume
- name: Create data volume
community.docker.docker_volume:
name: app-data
driver: local
# Remove a network
- name: Remove old network
community.docker.docker_network:
name: old-network
state: absent
06. Docker Compose with Ansible¶
tasks:
# Deploy a compose stack
- name: Deploy application stack
community.docker.docker_compose_v2:
project_name: myapp
project_src: /opt/myapp
state: present
pull: always
# Stop a compose stack
- name: Stop application stack
community.docker.docker_compose_v2:
project_name: myapp
project_src: /opt/myapp
state: stopped
# Remove a compose stack (including volumes)
- name: Remove application stack
community.docker.docker_compose_v2:
project_name: myapp
project_src: /opt/myapp
state: absent
remove_volumes: true
07. Inspect Container State¶
tasks:
- name: Get container info
community.docker.docker_container_info:
name: my-nginx
register: container_info
- name: Show container status
ansible.builtin.debug:
msg: "Container status: {{ container_info.container.State.Status }}"
when: container_info.exists
- name: Get all container facts
community.docker.docker_host_info:
containers: true
images: true
networks: true
volumes: true
register: docker_facts
- name: Show running containers
ansible.builtin.debug:
msg: "{{ docker_facts.containers | map(attribute='Names') | list }}"
08. Hands-on¶
- Create a Docker Compose file for a web + redis stack, then create an HTML index page for it.
??? success “Solution”
docker exec ansible-controller sh -c "cd /labs-scripts && mkdir -p docker-app/html && cat > docker-app/docker-compose.yml << 'EOF'
version: \"3.8\"
services:
web:
image: nginx:latest
ports:
- \"8080:80\"
volumes:
- ./html:/usr/share/nginx/html
networks:
- frontend
redis:
image: redis:7-alpine
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
EOF"
docker exec ansible-controller sh -c "echo '<h1>Deployed by Ansible!</h1>' > /labs-scripts/docker-app/html/index.html"
- Create a playbook
lab021-docker.ymlthat creates a network, starts an nginx container on port 8099, checks its status, then cleans up both the container and network.
??? success “Solution”
docker exec ansible-controller sh -c "cd /labs-scripts && cat > lab021-docker.yml << 'EOF'
---
- name: Docker Container Management
hosts: localhost
gather_facts: false
tasks:
- name: Ensure community.docker collection is available
ansible.builtin.debug:
msg: \"Using community.docker collection\"
- name: Create a test network
community.docker.docker_network:
name: lab021-network
driver: bridge
state: present
- name: Run nginx container
community.docker.docker_container:
name: lab021-nginx
image: nginx:latest
state: started
restart_policy: unless-stopped
ports:
- \"8099:80\"
networks:
- name: lab021-network
- name: Check container is running
community.docker.docker_container_info:
name: lab021-nginx
register: nginx_info
- name: Show container status
ansible.builtin.debug:
msg: \"nginx container: {{ nginx_info.container.State.Status }}\"
- name: Clean up
community.docker.docker_container:
name: lab021-nginx
state: absent
- name: Remove test network
community.docker.docker_network:
name: lab021-network
state: absent
EOF"
docker exec ansible-controller sh -c "cd /labs-scripts && ansible-playbook lab021-docker.yml"
- Install the
community.dockercollection and pull thealpine:latestDocker image using an Ansible playbook:
??? success “Solution”
docker exec ansible-controller sh -c "cd /labs-scripts && ansible-galaxy collection install community.docker --upgrade"
docker exec ansible-controller sh -c "cd /labs-scripts && cat > lab021-pull.yml << 'EOF'
---
- name: Pull Docker Image
hosts: localhost
gather_facts: false
tasks:
- name: Pull alpine image
community.docker.docker_image:
name: alpine
tag: latest
source: pull
register: pull_result
- name: Show image digest
ansible.builtin.debug:
msg: \"Pulled: {{ pull_result.image.RepoDigests | default(['alpine:latest']) }}\"
EOF
ansible-playbook lab021-pull.yml"
- Create a named Docker volume, start a container that mounts it, write a file into the volume, then verify the file survives container removal and recreation:
??? success “Solution”
docker exec ansible-controller sh -c "cd /labs-scripts && cat > lab021-volume.yml << 'EOF'
---
- name: Docker Volume Persistence
hosts: localhost
gather_facts: false
tasks:
- name: Create named volume
community.docker.docker_volume:
name: lab021-data
state: present
- name: Run container and write to volume
community.docker.docker_container:
name: lab021-writer
image: alpine:latest
state: started
command: sh -c \"echo 'persistent data' > /data/test.txt && sleep 5\"
volumes:
- lab021-data:/data
detach: true
- name: Wait for write to complete
ansible.builtin.pause:
seconds: 6
- name: Remove writer container
community.docker.docker_container:
name: lab021-writer
state: absent
- name: Read from volume in new container
community.docker.docker_container:
name: lab021-reader
image: alpine:latest
state: started
command: cat /data/test.txt
volumes:
- lab021-data:/data
detach: false
auto_remove: false
register: read_result
- name: Show persisted data
ansible.builtin.debug:
msg: \"Volume data survived: {{ read_result.container.Output | default('see container logs') }}\"
- name: Cleanup
community.docker.docker_container:
name: \"{{ item }}\"
state: absent
loop:
- lab021-reader
- name: Remove volume
community.docker.docker_volume:
name: lab021-data
state: absent
EOF
ansible-playbook lab021-volume.yml"
- Write a playbook that loops over a list of images, pulls each one, and prints a summary of pulled images:
??? success “Solution”
docker exec ansible-controller sh -c "cd /labs-scripts && cat > lab021-multi-image.yml << 'EOF'
---
- name: Pull Multiple Docker Images
hosts: localhost
gather_facts: false
vars:
images_to_pull:
- { name: nginx, tag: alpine }
- { name: redis, tag: 7-alpine }
- { name: alpine, tag: \"3.18\" }
tasks:
- name: Pull all images
community.docker.docker_image:
name: \"{{ item.name }}\"
tag: \"{{ item.tag }}\"
source: pull
loop: \"{{ images_to_pull }}\"
register: pull_results
- name: Summarize pulled images
ansible.builtin.debug:
msg: \"Pulled {{ item.item.name }}:{{ item.item.tag }}\"
loop: \"{{ pull_results.results }}\"
EOF
ansible-playbook lab021-multi-image.yml"
09. Summary¶
- Use
community.docker.docker_containerto manage the full container lifecycle docker_imagepulls, builds, and removes images declarativelydocker_networkanddocker_volumemanage Docker networking and storagedocker_compose_v2deploys entire application stacks from compose filesdocker_container_infoanddocker_host_infoinspect running infrastructure- Install the
community.dockercollection withansible-galaxy collection install community.docker