Initial commit
This commit is contained in:
294
skills/ansible/references/docker/compose-patterns.md
Normal file
294
skills/ansible/references/docker/compose-patterns.md
Normal file
@@ -0,0 +1,294 @@
|
||||
# Ansible Docker Compose Patterns
|
||||
|
||||
Common patterns for managing Docker Compose stacks with Ansible.
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
roles/
|
||||
└── docker_app/
|
||||
├── tasks/
|
||||
│ └── main.yml
|
||||
├── templates/
|
||||
│ ├── docker-compose.yml.j2
|
||||
│ └── .env.j2
|
||||
├── defaults/
|
||||
│ └── main.yml
|
||||
└── handlers/
|
||||
└── main.yml
|
||||
```
|
||||
|
||||
## Role Template
|
||||
|
||||
### defaults/main.yml
|
||||
|
||||
```yaml
|
||||
app_name: myapp
|
||||
app_version: latest
|
||||
app_port: 8080
|
||||
app_data_dir: "/opt/{{ app_name }}"
|
||||
|
||||
# Compose settings
|
||||
compose_pull: always
|
||||
compose_recreate: auto # auto, always, never
|
||||
|
||||
# Resource limits
|
||||
app_memory_limit: 512M
|
||||
app_cpu_limit: 1.0
|
||||
```
|
||||
|
||||
### templates/docker-compose.yml.j2
|
||||
|
||||
```yaml
|
||||
name: {{ app_name }}
|
||||
|
||||
services:
|
||||
app:
|
||||
image: {{ app_image }}:{{ app_version }}
|
||||
container_name: {{ app_name }}
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{{ app_port }}:{{ app_internal_port | default(app_port) }}"
|
||||
volumes:
|
||||
- {{ app_data_dir }}/data:/app/data
|
||||
{% if app_config_file is defined %}
|
||||
- {{ app_data_dir }}/config:/app/config:ro
|
||||
{% endif %}
|
||||
environment:
|
||||
TZ: {{ timezone | default('UTC') }}
|
||||
{% for key, value in app_env.items() %}
|
||||
{{ key }}: "{{ value }}"
|
||||
{% endfor %}
|
||||
{% if app_memory_limit is defined or app_cpu_limit is defined %}
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
{% if app_memory_limit is defined %}
|
||||
memory: {{ app_memory_limit }}
|
||||
{% endif %}
|
||||
{% if app_cpu_limit is defined %}
|
||||
cpus: '{{ app_cpu_limit }}'
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:{{ app_internal_port | default(app_port) }}/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- {{ app_network | default('default') }}
|
||||
|
||||
{% if app_network is defined %}
|
||||
networks:
|
||||
{{ app_network }}:
|
||||
external: true
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
### tasks/main.yml
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Create application directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ app_data_dir }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Create data directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ app_data_dir }}/{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
loop:
|
||||
- data
|
||||
- config
|
||||
|
||||
- name: Deploy compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: "{{ app_data_dir }}/docker-compose.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
notify: Redeploy stack
|
||||
|
||||
- name: Deploy environment file
|
||||
ansible.builtin.template:
|
||||
src: .env.j2
|
||||
dest: "{{ app_data_dir }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
notify: Redeploy stack
|
||||
when: app_secrets is defined
|
||||
|
||||
- name: Ensure stack is running
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
state: present
|
||||
pull: "{{ compose_pull }}"
|
||||
recreate: "{{ compose_recreate }}"
|
||||
register: compose_result
|
||||
|
||||
- name: Show deployment result
|
||||
ansible.builtin.debug:
|
||||
msg: "Deployed {{ compose_result.containers | length }} containers"
|
||||
when: compose_result is changed
|
||||
```
|
||||
|
||||
### handlers/main.yml
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Redeploy stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
state: present
|
||||
pull: always
|
||||
recreate: always
|
||||
```
|
||||
|
||||
## Multi-Service Stack
|
||||
|
||||
### templates/docker-compose.yml.j2 (full stack)
|
||||
|
||||
```yaml
|
||||
name: {{ stack_name }}
|
||||
|
||||
services:
|
||||
app:
|
||||
image: {{ app_image }}:{{ app_version }}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
environment:
|
||||
DATABASE_URL: "postgres://{{ db_user }}:{{ db_password }}@db:5432/{{ db_name }}"
|
||||
REDIS_URL: "redis://redis:6379"
|
||||
networks:
|
||||
- internal
|
||||
- web
|
||||
|
||||
db:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: {{ db_user }}
|
||||
POSTGRES_PASSWORD: {{ db_password }}
|
||||
POSTGRES_DB: {{ db_name }}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U {{ db_user }}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- internal
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- internal
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "{{ http_port | default(80) }}:80"
|
||||
- "{{ https_port | default(443) }}:443"
|
||||
volumes:
|
||||
- {{ app_data_dir }}/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- {{ app_data_dir }}/nginx/ssl:/etc/nginx/ssl:ro
|
||||
depends_on:
|
||||
- app
|
||||
networks:
|
||||
- web
|
||||
|
||||
networks:
|
||||
internal:
|
||||
driver: bridge
|
||||
web:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
redis_data:
|
||||
```
|
||||
|
||||
## Zero-Downtime Update
|
||||
|
||||
```yaml
|
||||
- name: Zero-downtime update
|
||||
hosts: docker_hosts
|
||||
serial: 1 # One host at a time
|
||||
tasks:
|
||||
- name: Pull new image
|
||||
community.docker.docker_image:
|
||||
name: "{{ app_image }}"
|
||||
tag: "{{ app_version }}"
|
||||
source: pull
|
||||
|
||||
- name: Drain connections (if load balanced)
|
||||
# ... remove from load balancer ...
|
||||
|
||||
- name: Update stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
state: present
|
||||
recreate: always
|
||||
|
||||
- name: Wait for health
|
||||
ansible.builtin.uri:
|
||||
url: "http://localhost:{{ app_port }}/health"
|
||||
status_code: 200
|
||||
register: health
|
||||
until: health.status == 200
|
||||
retries: 30
|
||||
delay: 2
|
||||
|
||||
- name: Restore to load balancer
|
||||
# ... add back to load balancer ...
|
||||
```
|
||||
|
||||
## Secrets Management
|
||||
|
||||
### With ansible-vault
|
||||
|
||||
```yaml
|
||||
# group_vars/secrets.yml (encrypted)
|
||||
app_secrets:
|
||||
DB_PASSWORD: supersecret
|
||||
API_KEY: abc123
|
||||
JWT_SECRET: longsecret
|
||||
```
|
||||
|
||||
```yaml
|
||||
# templates/.env.j2
|
||||
{% for key, value in app_secrets.items() %}
|
||||
{{ key }}={{ value }}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
### With external secrets
|
||||
|
||||
```yaml
|
||||
- name: Fetch secret from 1Password
|
||||
ansible.builtin.set_fact:
|
||||
db_password: "{{ lookup('community.general.onepassword', 'database', field='password') }}"
|
||||
|
||||
- name: Deploy with secret
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_data_dir }}"
|
||||
env_files:
|
||||
- "{{ app_data_dir }}/.env"
|
||||
state: present
|
||||
```
|
||||
307
skills/ansible/references/docker/deployment.md
Normal file
307
skills/ansible/references/docker/deployment.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Docker Deployment with Ansible
|
||||
|
||||
Managing Docker containers and compose stacks via Ansible.
|
||||
|
||||
## Collection Setup
|
||||
|
||||
```bash
|
||||
ansible-galaxy collection install community.docker
|
||||
```
|
||||
|
||||
## Compose Deployment (Recommended)
|
||||
|
||||
### Deploy from local compose file
|
||||
|
||||
```yaml
|
||||
- name: Deploy application stack
|
||||
hosts: docker_hosts
|
||||
become: true
|
||||
tasks:
|
||||
- name: Create project directory
|
||||
ansible.builtin.file:
|
||||
path: /opt/myapp
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Copy compose file
|
||||
ansible.builtin.template:
|
||||
src: docker-compose.yml.j2
|
||||
dest: /opt/myapp/docker-compose.yml
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
|
||||
- name: Copy environment file
|
||||
ansible.builtin.template:
|
||||
src: .env.j2
|
||||
dest: /opt/myapp/.env
|
||||
owner: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
|
||||
- name: Deploy with compose
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: present
|
||||
pull: always
|
||||
register: deploy_result
|
||||
|
||||
- name: Show deployed services
|
||||
ansible.builtin.debug:
|
||||
var: deploy_result.containers
|
||||
```
|
||||
|
||||
### Compose operations
|
||||
|
||||
```yaml
|
||||
# Pull latest images and recreate
|
||||
- name: Update stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: present
|
||||
pull: always
|
||||
recreate: always
|
||||
|
||||
# Stop stack (keep volumes)
|
||||
- name: Stop stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: stopped
|
||||
|
||||
# Remove stack
|
||||
- name: Remove stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: absent
|
||||
remove_volumes: false # Keep data volumes
|
||||
```
|
||||
|
||||
## Container Deployment (Individual)
|
||||
|
||||
### Run container
|
||||
|
||||
```yaml
|
||||
- name: Run nginx container
|
||||
community.docker.docker_container:
|
||||
name: nginx
|
||||
image: nginx:1.25
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- /opt/nginx/html:/usr/share/nginx/html:ro
|
||||
- /opt/nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
env:
|
||||
TZ: "America/Los_Angeles"
|
||||
labels:
|
||||
app: web
|
||||
env: production
|
||||
|
||||
- name: Run database
|
||||
community.docker.docker_container:
|
||||
name: postgres
|
||||
image: postgres:15
|
||||
state: started
|
||||
restart_policy: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
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
|
||||
```
|
||||
|
||||
### Container lifecycle
|
||||
|
||||
```yaml
|
||||
# Stop container
|
||||
- name: Stop container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
state: stopped
|
||||
|
||||
# Restart container
|
||||
- name: Restart container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
state: started
|
||||
restart: true
|
||||
|
||||
# Remove container
|
||||
- name: Remove container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
state: absent
|
||||
|
||||
# Force recreate
|
||||
- name: Recreate container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: myapp:latest
|
||||
state: started
|
||||
recreate: true
|
||||
```
|
||||
|
||||
## Image Management
|
||||
|
||||
```yaml
|
||||
# Pull image
|
||||
- name: Pull latest image
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: latest
|
||||
source: pull
|
||||
force_source: true # Always check for updates
|
||||
|
||||
# Build from Dockerfile
|
||||
- name: Build image
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: "{{ version }}"
|
||||
source: build
|
||||
build:
|
||||
path: /opt/myapp
|
||||
dockerfile: Dockerfile
|
||||
pull: true # Pull base image updates
|
||||
|
||||
# Remove image
|
||||
- name: Remove old images
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: old
|
||||
state: absent
|
||||
```
|
||||
|
||||
## Network Management
|
||||
|
||||
```yaml
|
||||
# Create 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 macvlan network
|
||||
- name: Create macvlan network
|
||||
community.docker.docker_network:
|
||||
name: lan
|
||||
driver: macvlan
|
||||
driver_options:
|
||||
parent: eth0
|
||||
ipam_config:
|
||||
- subnet: 192.168.1.0/24
|
||||
gateway: 192.168.1.1
|
||||
|
||||
# Attach container to network
|
||||
- name: Run container on network
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: myapp:latest
|
||||
networks:
|
||||
- name: app_network
|
||||
ipv4_address: 172.20.0.10
|
||||
```
|
||||
|
||||
## Volume Management
|
||||
|
||||
```yaml
|
||||
# Create named volume
|
||||
- name: Create data volume
|
||||
community.docker.docker_volume:
|
||||
name: app_data
|
||||
driver: local
|
||||
|
||||
# Create volume with options
|
||||
- name: Create NFS volume
|
||||
community.docker.docker_volume:
|
||||
name: shared_data
|
||||
driver: local
|
||||
driver_options:
|
||||
type: nfs
|
||||
device: ":/exports/data"
|
||||
o: "addr=192.168.1.10,rw"
|
||||
|
||||
# Backup volume
|
||||
- name: Backup volume
|
||||
community.docker.docker_container:
|
||||
name: backup
|
||||
image: alpine
|
||||
command: tar czf /backup/data.tar.gz /data
|
||||
volumes:
|
||||
- app_data:/data:ro
|
||||
- /opt/backups:/backup
|
||||
auto_remove: true
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Wait for service health
|
||||
|
||||
```yaml
|
||||
- name: Deploy database
|
||||
community.docker.docker_container:
|
||||
name: postgres
|
||||
image: postgres:15
|
||||
# ... config ...
|
||||
|
||||
- name: Wait for database
|
||||
community.docker.docker_container_info:
|
||||
name: postgres
|
||||
register: db_info
|
||||
until: db_info.container.State.Health.Status == "healthy"
|
||||
retries: 30
|
||||
delay: 2
|
||||
```
|
||||
|
||||
### Rolling update
|
||||
|
||||
```yaml
|
||||
- name: Pull new image
|
||||
community.docker.docker_image:
|
||||
name: myapp
|
||||
tag: "{{ new_version }}"
|
||||
source: pull
|
||||
|
||||
- name: Update container
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
image: "myapp:{{ new_version }}"
|
||||
state: started
|
||||
recreate: true
|
||||
restart_policy: unless-stopped
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```yaml
|
||||
- name: Remove stopped containers
|
||||
community.docker.docker_prune:
|
||||
containers: true
|
||||
containers_filters:
|
||||
status: exited
|
||||
|
||||
- name: Remove unused images
|
||||
community.docker.docker_prune:
|
||||
images: true
|
||||
images_filters:
|
||||
dangling: true
|
||||
|
||||
- name: Full cleanup (careful!)
|
||||
community.docker.docker_prune:
|
||||
containers: true
|
||||
images: true
|
||||
networks: true
|
||||
volumes: false # Don't remove data!
|
||||
builder_cache: true
|
||||
```
|
||||
292
skills/ansible/references/docker/troubleshooting.md
Normal file
292
skills/ansible/references/docker/troubleshooting.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Ansible Docker Troubleshooting
|
||||
|
||||
Common issues and debugging patterns.
|
||||
|
||||
## Module Issues
|
||||
|
||||
### "Could not find docker-compose"
|
||||
|
||||
```yaml
|
||||
# docker_compose_v2 requires Docker Compose V2 (plugin)
|
||||
# NOT standalone docker-compose binary
|
||||
|
||||
# Check on target host:
|
||||
# docker compose version # V2 (plugin)
|
||||
# docker-compose version # V1 (standalone) - won't work
|
||||
```
|
||||
|
||||
Fix: Install Docker Compose V2:
|
||||
```yaml
|
||||
- name: Install Docker Compose plugin
|
||||
ansible.builtin.apt:
|
||||
name: docker-compose-plugin
|
||||
state: present
|
||||
```
|
||||
|
||||
### "Permission denied"
|
||||
|
||||
```yaml
|
||||
# User not in docker group
|
||||
- name: Add user to docker group
|
||||
ansible.builtin.user:
|
||||
name: "{{ ansible_user }}"
|
||||
groups: docker
|
||||
append: true
|
||||
become: true
|
||||
|
||||
# Then reconnect or use become
|
||||
- name: Run with become
|
||||
community.docker.docker_container:
|
||||
name: myapp
|
||||
# ...
|
||||
become: true
|
||||
```
|
||||
|
||||
### "Cannot connect to Docker daemon"
|
||||
|
||||
```yaml
|
||||
# Docker not running
|
||||
- name: Ensure Docker is running
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: true
|
||||
become: true
|
||||
|
||||
# Socket permission issue
|
||||
# Add become: true to docker tasks
|
||||
```
|
||||
|
||||
## Container Issues
|
||||
|
||||
### Get container logs
|
||||
|
||||
```yaml
|
||||
- name: Get logs
|
||||
community.docker.docker_container_exec:
|
||||
container: myapp
|
||||
command: cat /var/log/app.log
|
||||
register: logs
|
||||
ignore_errors: true
|
||||
|
||||
- name: Alternative - docker logs
|
||||
ansible.builtin.command: docker logs --tail 100 myapp
|
||||
register: docker_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Show logs
|
||||
ansible.builtin.debug:
|
||||
var: docker_logs.stdout_lines
|
||||
```
|
||||
|
||||
### Container keeps restarting
|
||||
|
||||
```yaml
|
||||
- name: Get container info
|
||||
community.docker.docker_container_info:
|
||||
name: myapp
|
||||
register: container_info
|
||||
|
||||
- name: Show restart count
|
||||
ansible.builtin.debug:
|
||||
msg: "Restart count: {{ container_info.container.RestartCount }}"
|
||||
|
||||
- name: Show last exit code
|
||||
ansible.builtin.debug:
|
||||
msg: "Exit code: {{ container_info.container.State.ExitCode }}"
|
||||
|
||||
- name: Get logs from dead container
|
||||
ansible.builtin.command: docker logs myapp
|
||||
register: crash_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Show crash logs
|
||||
ansible.builtin.debug:
|
||||
var: crash_logs.stderr_lines
|
||||
```
|
||||
|
||||
### Health check failing
|
||||
|
||||
```yaml
|
||||
- name: Check health status
|
||||
community.docker.docker_container_info:
|
||||
name: myapp
|
||||
register: info
|
||||
|
||||
- name: Show health
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Status: {{ info.container.State.Health.Status }}
|
||||
Failing: {{ info.container.State.Health.FailingStreak }}
|
||||
Log: {{ info.container.State.Health.Log | last }}
|
||||
|
||||
# Manual health check
|
||||
- name: Test health endpoint
|
||||
ansible.builtin.command: >
|
||||
docker exec myapp curl -f http://localhost:8080/health
|
||||
register: health
|
||||
ignore_errors: true
|
||||
changed_when: false
|
||||
```
|
||||
|
||||
## Network Issues
|
||||
|
||||
### Container can't reach external network
|
||||
|
||||
```yaml
|
||||
- name: Test DNS from container
|
||||
ansible.builtin.command: docker exec myapp nslookup google.com
|
||||
register: dns_test
|
||||
changed_when: false
|
||||
ignore_errors: true
|
||||
|
||||
- name: Test connectivity
|
||||
ansible.builtin.command: docker exec myapp ping -c 1 8.8.8.8
|
||||
register: ping_test
|
||||
changed_when: false
|
||||
ignore_errors: true
|
||||
|
||||
# Check iptables
|
||||
- name: Check IP forwarding
|
||||
ansible.builtin.command: sysctl net.ipv4.ip_forward
|
||||
register: ip_forward
|
||||
changed_when: false
|
||||
|
||||
- name: Enable IP forwarding
|
||||
ansible.posix.sysctl:
|
||||
name: net.ipv4.ip_forward
|
||||
value: '1'
|
||||
state: present
|
||||
become: true
|
||||
when: "'0' in ip_forward.stdout"
|
||||
```
|
||||
|
||||
### Containers can't communicate
|
||||
|
||||
```yaml
|
||||
- name: List networks
|
||||
community.docker.docker_network_info:
|
||||
name: "{{ network_name }}"
|
||||
register: network_info
|
||||
|
||||
- name: Show connected containers
|
||||
ansible.builtin.debug:
|
||||
var: network_info.network.Containers
|
||||
|
||||
# Verify both containers on same network
|
||||
- name: Test inter-container connectivity
|
||||
ansible.builtin.command: >
|
||||
docker exec app ping -c 1 db
|
||||
register: ping_result
|
||||
changed_when: false
|
||||
```
|
||||
|
||||
## Compose Issues
|
||||
|
||||
### Services not starting in order
|
||||
|
||||
```yaml
|
||||
# depends_on only waits for container start, not readiness
|
||||
# Use healthcheck + condition
|
||||
|
||||
# In compose template:
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy # Wait for health check
|
||||
|
||||
db:
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
```
|
||||
|
||||
### Orphaned containers
|
||||
|
||||
```yaml
|
||||
# Containers from old compose runs
|
||||
- name: Remove orphans
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: /opt/myapp
|
||||
state: present
|
||||
remove_orphans: true
|
||||
```
|
||||
|
||||
### Volume data not persisting
|
||||
|
||||
```yaml
|
||||
# Check volume exists
|
||||
- name: List volumes
|
||||
ansible.builtin.command: docker volume ls
|
||||
register: volumes
|
||||
changed_when: false
|
||||
|
||||
# Check volume contents
|
||||
- name: Inspect volume
|
||||
ansible.builtin.command: docker volume inspect myapp_data
|
||||
register: volume_info
|
||||
changed_when: false
|
||||
|
||||
- name: Show volume mountpoint
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ (volume_info.stdout | from_json)[0].Mountpoint }}"
|
||||
```
|
||||
|
||||
## Debug Playbook
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Docker debug
|
||||
hosts: docker_hosts
|
||||
tasks:
|
||||
- name: Docker version
|
||||
ansible.builtin.command: docker version
|
||||
register: docker_version
|
||||
changed_when: false
|
||||
|
||||
- name: Compose version
|
||||
ansible.builtin.command: docker compose version
|
||||
register: compose_version
|
||||
changed_when: false
|
||||
|
||||
- name: List containers
|
||||
ansible.builtin.command: docker ps -a
|
||||
register: containers
|
||||
changed_when: false
|
||||
|
||||
- name: List images
|
||||
ansible.builtin.command: docker images
|
||||
register: images
|
||||
changed_when: false
|
||||
|
||||
- name: Disk usage
|
||||
ansible.builtin.command: docker system df
|
||||
register: disk
|
||||
changed_when: false
|
||||
|
||||
- name: Show all
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Docker: {{ docker_version.stdout_lines[0] }}
|
||||
Compose: {{ compose_version.stdout }}
|
||||
Containers:
|
||||
{{ containers.stdout }}
|
||||
Images:
|
||||
{{ images.stdout }}
|
||||
Disk:
|
||||
{{ disk.stdout }}
|
||||
```
|
||||
|
||||
## Common Error Reference
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| `docker.errors.DockerException` | Docker not running | Start docker service |
|
||||
| `docker.errors.APIError: 404` | Container/image not found | Check name/tag |
|
||||
| `docker.errors.APIError: 409` | Container name conflict | Remove or rename |
|
||||
| `PermissionError` | Not in docker group | Add user or use become |
|
||||
| `requests.exceptions.ConnectionError` | Docker socket inaccessible | Check socket permissions |
|
||||
| `FileNotFoundError: docker-compose` | V1 compose not installed | Use docker_compose_v2 |
|
||||
Reference in New Issue
Block a user