Docker & Container Management
What is Docker Management with Ansible? Ansible provides comprehensive modules for managing Docker containers, images, networks, volumes, and Docker Compose deployments. This enables Infrastructure as Code for containerized applications with full lifecycle management.
Understanding Docker Automation
Why Automate Docker with Ansible?
Ansible simplifies container management:
- Declarative Configuration: Define desired container state
- Multi-Host Management: Manage containers across many hosts
- Idempotency: Safe to run repeatedly
- Integration: Combine containers with traditional infra
- No Agents: Manage Docker via API or SSH
Use Cases
- Deploy and manage containerized applications
- Configure Docker hosts and registries
- Orchestrate multi-container applications
- Manage container lifecycle (start, stop, update)
- Build and push custom images
Prerequisites
Install Docker Collection
# Install community.docker collection ansible-galaxy collection install community.docker # Install on target hosts: Docker Python SDK pip install docker
Install Docker on Target Hosts
---
- name: Install Docker
hosts: docker_hosts
become: true
tasks:
- name: Install Docker (Ubuntu/Debian)
ansible.builtin.apt:
name:
- docker.io
- python3-docker
state: present
update_cache: yes
when: ansible_os_family == "Debian"
- name: Install Docker (RHEL/CentOS)
ansible.builtin.yum:
name:
- docker
- python3-docker
state: present
when: ansible_os_family == "RedHat"
- name: Start Docker service
ansible.builtin.service:
name: docker
state: started
enabled: true
- name: Add user to docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: docker
append: yes
Container Management
Running Containers
---
- name: Manage Docker containers
hosts: docker_hosts
tasks:
- name: Run nginx container
community.docker.docker_container:
name: my_nginx
image: nginx:latest
state: started
ports:
- "8080:80"
env:
NGINX_HOST: example.com
NGINX_PORT: "80"
- name: Run container with volumes
community.docker.docker_container:
name: webapp
image: myapp:v1.0
state: started
volumes:
- /host/data:/container/data
- /host/logs:/var/log/app
restart_policy: unless-stopped
- name: Run container with custom network
community.docker.docker_container:
name: database
image: postgres:15
state: started
networks:
- name: app_network
env:
POSTGRES_PASSWORD: "{{ db_password }}"
POSTGRES_DB: myapp
Container Lifecycle Management
---
- name: Container lifecycle operations
hosts: docker_hosts
tasks:
- name: Ensure container is running
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
- name: Stop container
community.docker.docker_container:
name: myapp
state: stopped
- name: Restart container
community.docker.docker_container:
name: myapp
state: started
restart: yes
- name: Remove container
community.docker.docker_container:
name: myapp
state: absent
- name: Update container (pull new image and recreate)
community.docker.docker_container:
name: myapp
image: myapp:v2.0
state: started
recreate: yes
pull: yes
Advanced Container Configuration
---
- name: Advanced container settings
hosts: docker_hosts
tasks:
- name: Container with resource limits
community.docker.docker_container:
name: resource_limited
image: myapp:latest
state: started
memory: 512m
memory_reservation: 256m
cpus: 1.5
cpu_shares: 1024
- name: Container with health check
community.docker.docker_container:
name: monitored_app
image: myapp:latest
state: started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
- name: Container with labels
community.docker.docker_container:
name: labeled_container
image: nginx:latest
state: started
labels:
environment: production
app: frontend
version: "1.0"
- name: Privileged container
community.docker.docker_container:
name: privileged_app
image: special:latest
state: started
privileged: yes
devices:
- /dev/sda:/dev/xvda:rwm
Image Management
Pulling and Managing Images
---
- name: Manage Docker images
hosts: docker_hosts
tasks:
- name: Pull image
community.docker.docker_image:
name: nginx
tag: latest
source: pull
- name: Pull image from private registry
community.docker.docker_image:
name: registry.example.com/myapp
tag: v1.0
source: pull
registry_url: registry.example.com
registry_username: "{{ registry_user }}"
registry_password: "{{ registry_pass }}"
- name: Remove unused images
community.docker.docker_prune:
images: yes
images_filters:
dangling: true
- name: Remove specific image
community.docker.docker_image:
name: old_image
tag: deprecated
state: absent
Building Images
---
- name: Build Docker images
hosts: docker_hosts
tasks:
- name: Build image from Dockerfile
community.docker.docker_image:
name: myapp
tag: v1.0
source: build
build:
path: /path/to/docker/context
dockerfile: Dockerfile
pull: yes
args:
BUILD_VERSION: "1.0.0"
ENVIRONMENT: production
- name: Build and push to registry
community.docker.docker_image:
name: registry.example.com/myapp
tag: "{{ version }}"
source: build
build:
path: ./app
push: yes
registry_url: registry.example.com
registry_username: "{{ registry_user }}"
registry_password: "{{ registry_pass }}"
- name: Build multi-stage image
community.docker.docker_image:
name: optimized_app
tag: latest
source: build
build:
path: .
dockerfile: Dockerfile.multi
target: production
Network Management
Creating and Managing Networks
---
- name: Manage Docker networks
hosts: docker_hosts
tasks:
- name: Create bridge network
community.docker.docker_network:
name: app_network
driver: bridge
ipam_config:
- subnet: 172.20.0.0/16
gateway: 172.20.0.1
- name: Create overlay network (Swarm)
community.docker.docker_network:
name: overlay_network
driver: overlay
attachable: yes
scope: swarm
- name: Remove network
community.docker.docker_network:
name: old_network
state: absent
force: yes
- name: Connect container to network
community.docker.docker_container:
name: webapp
image: myapp:latest
networks:
- name: app_network
ipv4_address: 172.20.0.10
- name: monitoring_network
Volume Management
Creating and Managing Volumes
---
- name: Manage Docker volumes
hosts: docker_hosts
tasks:
- name: Create named volume
community.docker.docker_volume:
name: app_data
state: present
- name: Create volume with driver options
community.docker.docker_volume:
name: nfs_volume
driver: local
driver_options:
type: nfs
o: addr=nfs-server.example.com,rw
device: ":/exports/data"
- name: Remove volume
community.docker.docker_volume:
name: old_volume
state: absent
force: yes
- name: Container with volume mounts
community.docker.docker_container:
name: data_container
image: postgres:15
volumes:
- app_data:/var/lib/postgresql/data
- /host/backup:/backup:ro
Docker Compose
Using Docker Compose Module
---
- name: Manage Docker Compose applications
hosts: docker_hosts
tasks:
- name: Deploy with docker-compose v2
community.docker.docker_compose_v2:
project_src: /path/to/compose/project
state: present
- name: Start services
community.docker.docker_compose_v2:
project_src: /path/to/project
state: present
services:
- web
- db
- name: Stop and remove
community.docker.docker_compose_v2:
project_src: /path/to/project
state: absent
remove_volumes: yes
- name: Pull and recreate
community.docker.docker_compose_v2:
project_src: /path/to/project
state: present
pull: always
recreate: always
Managing Compose Files
---
- name: Deploy application with compose
hosts: docker_hosts
tasks:
- name: Create project directory
ansible.builtin.file:
path: /opt/myapp
state: directory
- name: Template docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
dest: /opt/myapp/docker-compose.yml
- name: Template environment file
ansible.builtin.template:
src: .env.j2
dest: /opt/myapp/.env
- name: Deploy compose application
community.docker.docker_compose_v2:
project_src: /opt/myapp
state: present
pull: always
# templates/docker-compose.yml.j2
---
version: '3.8'
services:
web:
image: nginx:{{ nginx_version }}
ports:
- "{{ web_port }}:80"
volumes:
- ./html:/usr/share/nginx/html:ro
environment:
NGINX_HOST: {{ domain_name }}
networks:
- frontend
app:
image: {{ app_image }}:{{ app_version }}
environment:
DATABASE_URL: postgresql://{{ db_user }}:{{ db_password }}@db:5432/{{ db_name }}
REDIS_URL: redis://cache:6379
networks:
- frontend
- backend
depends_on:
- db
- cache
db:
image: postgres:{{ postgres_version }}
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: {{ db_user }}
POSTGRES_PASSWORD: {{ db_password }}
POSTGRES_DB: {{ db_name }}
networks:
- backend
cache:
image: redis:{{ redis_version }}
networks:
- backend
volumes:
db_data:
networks:
frontend:
backend:
Complete Application Deployment
Multi-Container Application
---
- name: Deploy full application stack
hosts: docker_hosts
become: true
vars:
app_version: "1.0.0"
environment: production
tasks:
- name: Create application network
community.docker.docker_network:
name: myapp_network
driver: bridge
- name: Create data volumes
community.docker.docker_volume:
name: "{{ item }}"
state: present
loop:
- postgres_data
- redis_data
- app_uploads
- name: Deploy PostgreSQL database
community.docker.docker_container:
name: myapp_postgres
image: postgres:15
state: started
restart_policy: unless-stopped
networks:
- name: myapp_network
volumes:
- postgres_data:/var/lib/postgresql/data
env:
POSTGRES_USER: "{{ db_user }}"
POSTGRES_PASSWORD: "{{ db_password }}"
POSTGRES_DB: "{{ db_name }}"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
interval: 10s
timeout: 5s
retries: 5
- name: Deploy Redis cache
community.docker.docker_container:
name: myapp_redis
image: redis:7-alpine
state: started
restart_policy: unless-stopped
networks:
- name: myapp_network
volumes:
- redis_data:/data
- name: Deploy application
community.docker.docker_container:
name: myapp
image: "registry.example.com/myapp:{{ app_version }}"
state: started
restart_policy: unless-stopped
networks:
- name: myapp_network
volumes:
- app_uploads:/app/uploads
env:
DATABASE_URL: "postgresql://{{ db_user }}:{{ db_password }}@myapp_postgres:5432/{{ db_name }}"
REDIS_URL: "redis://myapp_redis:6379"
ENVIRONMENT: "{{ environment }}"
depends_on:
- myapp_postgres
- myapp_redis
- name: Deploy nginx proxy
community.docker.docker_container:
name: myapp_nginx
image: nginx:alpine
state: started
restart_policy: unless-stopped
ports:
- "80:80"
- "443:443"
networks:
- name: myapp_network
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
Docker Registry Management
Login to Registry
---
- name: Docker registry operations
hosts: docker_hosts
tasks:
- name: Login to Docker Hub
community.docker.docker_login:
username: "{{ docker_hub_user }}"
password: "{{ docker_hub_password }}"
- name: Login to private registry
community.docker.docker_login:
registry_url: registry.example.com
username: "{{ registry_user }}"
password: "{{ registry_password }}"
- name: Logout from registry
community.docker.docker_login:
state: absent
registry_url: registry.example.com
Cleanup and Maintenance
Pruning Resources
---
- name: Docker cleanup tasks
hosts: docker_hosts
tasks:
- name: Remove dangling images
community.docker.docker_prune:
images: yes
images_filters:
dangling: true
- name: Remove unused containers
community.docker.docker_prune:
containers: yes
containers_filters:
until: 24h
- name: Remove unused volumes
community.docker.docker_prune:
volumes: yes
- name: Remove unused networks
community.docker.docker_prune:
networks: yes
- name: Full system prune
community.docker.docker_prune:
containers: yes
images: yes
networks: yes
volumes: yes
builder_cache: yes
Gathering Container Information
Inspect Containers and Images
---
- name: Get Docker information
hosts: docker_hosts
tasks:
- name: Get container info
community.docker.docker_container_info:
name: myapp
register: container_info
- name: Display container status
ansible.builtin.debug:
msg: "Container {{ container_info.container.Name }} is {{ container_info.container.State.Status }}"
- name: List all containers
community.docker.docker_host_info:
containers: yes
register: docker_info
- name: Get image info
community.docker.docker_image_info:
name: nginx:latest
register: image_info
Best Practices
Docker Management Best Practices:
- Use Specific Tags: Avoid 'latest' tag in production
- Health Checks: Implement container health checks
- Resource Limits: Set memory and CPU limits
- Named Volumes: Use named volumes for persistent data
- Networks: Isolate containers with custom networks
- Restart Policies: Configure appropriate restart behavior
- Secrets: Use secrets management, not environment variables
- Regular Cleanup: Prune unused resources periodically
Common Issues
Python Docker SDK Missing
# Install on target hosts
- name: Install Docker SDK
ansible.builtin.pip:
name: docker
state: present
Permission Denied
- Add user to docker group
- Use
become: truein playbooks - Restart SSH session after adding to docker group
Port Already Allocated
- Check for existing containers using the port
- Use different host port mapping
- Stop conflicting services
Next Steps
- Kubernetes - Container orchestration
- Cloud Providers - Deploy containers in cloud
- Advanced Topics - Complex deployments
- Try the Playground - Practice Ansible concepts