411 lines
14 KiB
YAML
411 lines
14 KiB
YAML
---
|
||
# Docker Cleanup and Pruning Playbook
|
||
# Clean up unused containers, images, volumes, and networks
|
||
# Usage: ansible-playbook playbooks/prune_containers.yml
|
||
# Usage: ansible-playbook playbooks/prune_containers.yml -e "aggressive_cleanup=true"
|
||
# Usage: ansible-playbook playbooks/prune_containers.yml -e "dry_run=true"
|
||
|
||
- name: Docker System Cleanup and Pruning
|
||
hosts: "{{ host_target | default('all') }}"
|
||
gather_facts: yes
|
||
vars:
|
||
dry_run: "{{ dry_run | default(false) }}"
|
||
aggressive_cleanup: "{{ aggressive_cleanup | default(false) }}"
|
||
keep_images_days: "{{ keep_images_days | default(7) }}"
|
||
keep_volumes: "{{ keep_volumes | default(true) }}"
|
||
backup_before_cleanup: "{{ backup_before_cleanup | default(true) }}"
|
||
cleanup_logs: "{{ cleanup_logs | default(true) }}"
|
||
max_log_size: "{{ max_log_size | default('100m') }}"
|
||
|
||
tasks:
|
||
- name: Check if Docker is running
|
||
systemd:
|
||
name: docker
|
||
register: docker_status
|
||
failed_when: docker_status.status.ActiveState != "active"
|
||
|
||
- name: Create cleanup report directory
|
||
file:
|
||
path: "/tmp/docker_cleanup/{{ ansible_date_time.date }}"
|
||
state: directory
|
||
mode: '0755'
|
||
|
||
- name: Get pre-cleanup Docker system info
|
||
shell: |
|
||
echo "=== PRE-CLEANUP DOCKER SYSTEM INFO ==="
|
||
echo "Date: {{ ansible_date_time.iso8601 }}"
|
||
echo "Host: {{ inventory_hostname }}"
|
||
echo ""
|
||
|
||
echo "System Usage:"
|
||
docker system df
|
||
echo ""
|
||
|
||
echo "Container Count:"
|
||
echo "Running: $(docker ps -q | wc -l)"
|
||
echo "Stopped: $(docker ps -aq --filter status=exited | wc -l)"
|
||
echo "Total: $(docker ps -aq | wc -l)"
|
||
echo ""
|
||
|
||
echo "Image Count:"
|
||
echo "Total: $(docker images -q | wc -l)"
|
||
echo "Dangling: $(docker images -f dangling=true -q | wc -l)"
|
||
echo ""
|
||
|
||
echo "Volume Count:"
|
||
echo "Total: $(docker volume ls -q | wc -l)"
|
||
echo "Dangling: $(docker volume ls -f dangling=true -q | wc -l)"
|
||
echo ""
|
||
|
||
echo "Network Count:"
|
||
echo "Total: $(docker network ls -q | wc -l)"
|
||
echo "Custom: $(docker network ls --filter type=custom -q | wc -l)"
|
||
register: pre_cleanup_info
|
||
changed_when: false
|
||
|
||
- name: Display cleanup plan
|
||
debug:
|
||
msg: |
|
||
🧹 DOCKER CLEANUP PLAN
|
||
======================
|
||
🖥️ Host: {{ inventory_hostname }}
|
||
📅 Date: {{ ansible_date_time.date }}
|
||
🔍 Dry Run: {{ dry_run }}
|
||
💪 Aggressive: {{ aggressive_cleanup }}
|
||
📦 Keep Images: {{ keep_images_days }} days
|
||
💾 Keep Volumes: {{ keep_volumes }}
|
||
📝 Cleanup Logs: {{ cleanup_logs }}
|
||
|
||
{{ pre_cleanup_info.stdout }}
|
||
|
||
- name: Backup container list before cleanup
|
||
shell: |
|
||
backup_file="/tmp/docker_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_containers_backup.txt"
|
||
|
||
echo "=== CONTAINER BACKUP - {{ ansible_date_time.iso8601 }} ===" > "$backup_file"
|
||
echo "Host: {{ inventory_hostname }}" >> "$backup_file"
|
||
echo "" >> "$backup_file"
|
||
|
||
echo "=== RUNNING CONTAINERS ===" >> "$backup_file"
|
||
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" >> "$backup_file"
|
||
echo "" >> "$backup_file"
|
||
|
||
echo "=== ALL CONTAINERS ===" >> "$backup_file"
|
||
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.CreatedAt}}" >> "$backup_file"
|
||
echo "" >> "$backup_file"
|
||
|
||
echo "=== IMAGES ===" >> "$backup_file"
|
||
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" >> "$backup_file"
|
||
echo "" >> "$backup_file"
|
||
|
||
echo "=== VOLUMES ===" >> "$backup_file"
|
||
docker volume ls >> "$backup_file"
|
||
echo "" >> "$backup_file"
|
||
|
||
echo "=== NETWORKS ===" >> "$backup_file"
|
||
docker network ls >> "$backup_file"
|
||
when: backup_before_cleanup | bool
|
||
|
||
- name: Remove stopped containers
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would remove stopped containers:"
|
||
docker ps -aq --filter status=exited
|
||
{% else %}
|
||
echo "Removing stopped containers..."
|
||
stopped_containers=$(docker ps -aq --filter status=exited)
|
||
if [ -n "$stopped_containers" ]; then
|
||
docker rm $stopped_containers
|
||
echo "✅ Removed stopped containers"
|
||
else
|
||
echo "ℹ️ No stopped containers to remove"
|
||
fi
|
||
{% endif %}
|
||
register: remove_stopped_containers
|
||
|
||
- name: Remove dangling images
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would remove dangling images:"
|
||
docker images -f dangling=true -q
|
||
{% else %}
|
||
echo "Removing dangling images..."
|
||
dangling_images=$(docker images -f dangling=true -q)
|
||
if [ -n "$dangling_images" ]; then
|
||
docker rmi $dangling_images
|
||
echo "✅ Removed dangling images"
|
||
else
|
||
echo "ℹ️ No dangling images to remove"
|
||
fi
|
||
{% endif %}
|
||
register: remove_dangling_images
|
||
|
||
- name: Remove unused images (aggressive cleanup)
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would remove unused images older than {{ keep_images_days }} days:"
|
||
docker images --filter "until={{ keep_images_days * 24 }}h" -q
|
||
{% else %}
|
||
echo "Removing unused images older than {{ keep_images_days }} days..."
|
||
old_images=$(docker images --filter "until={{ keep_images_days * 24 }}h" -q)
|
||
if [ -n "$old_images" ]; then
|
||
# Check if images are not used by any container
|
||
for image in $old_images; do
|
||
if ! docker ps -a --format "{{.Image}}" | grep -q "$image"; then
|
||
docker rmi "$image" 2>/dev/null && echo "Removed image: $image" || echo "Failed to remove image: $image"
|
||
else
|
||
echo "Skipping image in use: $image"
|
||
fi
|
||
done
|
||
echo "✅ Removed old unused images"
|
||
else
|
||
echo "ℹ️ No old images to remove"
|
||
fi
|
||
{% endif %}
|
||
register: remove_old_images
|
||
when: aggressive_cleanup | bool
|
||
|
||
- name: Remove dangling volumes
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would remove dangling volumes:"
|
||
docker volume ls -f dangling=true -q
|
||
{% else %}
|
||
{% if not keep_volumes %}
|
||
echo "Removing dangling volumes..."
|
||
dangling_volumes=$(docker volume ls -f dangling=true -q)
|
||
if [ -n "$dangling_volumes" ]; then
|
||
docker volume rm $dangling_volumes
|
||
echo "✅ Removed dangling volumes"
|
||
else
|
||
echo "ℹ️ No dangling volumes to remove"
|
||
fi
|
||
{% else %}
|
||
echo "ℹ️ Volume cleanup skipped (keep_volumes=true)"
|
||
{% endif %}
|
||
{% endif %}
|
||
register: remove_dangling_volumes
|
||
|
||
- name: Remove unused networks
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would remove unused networks:"
|
||
docker network ls --filter type=custom -q
|
||
{% else %}
|
||
echo "Removing unused networks..."
|
||
docker network prune -f
|
||
echo "✅ Removed unused networks"
|
||
{% endif %}
|
||
register: remove_unused_networks
|
||
|
||
- name: Clean up container logs
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would clean up container logs larger than {{ max_log_size }}"
|
||
find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null | wc -l
|
||
{% else %}
|
||
{% if cleanup_logs %}
|
||
echo "Cleaning up large container logs (>{{ max_log_size }})..."
|
||
|
||
log_count=0
|
||
total_size_before=0
|
||
total_size_after=0
|
||
|
||
for log_file in $(find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null); do
|
||
if [ -f "$log_file" ]; then
|
||
size_before=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0)
|
||
total_size_before=$((total_size_before + size_before))
|
||
|
||
# Truncate log file to last 1000 lines
|
||
tail -1000 "$log_file" > "${log_file}.tmp" && mv "${log_file}.tmp" "$log_file"
|
||
|
||
size_after=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0)
|
||
total_size_after=$((total_size_after + size_after))
|
||
|
||
log_count=$((log_count + 1))
|
||
fi
|
||
done
|
||
|
||
if [ $log_count -gt 0 ]; then
|
||
saved_bytes=$((total_size_before - total_size_after))
|
||
echo "✅ Cleaned $log_count log files, saved $(echo $saved_bytes | numfmt --to=iec) bytes"
|
||
else
|
||
echo "ℹ️ No large log files to clean"
|
||
fi
|
||
{% else %}
|
||
echo "ℹ️ Log cleanup skipped (cleanup_logs=false)"
|
||
{% endif %}
|
||
{% endif %}
|
||
register: cleanup_logs_result
|
||
when: cleanup_logs | bool
|
||
|
||
- name: Run Docker system prune
|
||
shell: |
|
||
{% if dry_run %}
|
||
echo "DRY RUN: Would run docker system prune"
|
||
docker system df
|
||
{% else %}
|
||
echo "Running Docker system prune..."
|
||
{% if aggressive_cleanup %}
|
||
docker system prune -af --volumes
|
||
{% else %}
|
||
docker system prune -f
|
||
{% endif %}
|
||
echo "✅ Docker system prune complete"
|
||
{% endif %}
|
||
register: system_prune_result
|
||
|
||
- name: Get post-cleanup Docker system info
|
||
shell: |
|
||
echo "=== POST-CLEANUP DOCKER SYSTEM INFO ==="
|
||
echo "Date: {{ ansible_date_time.iso8601 }}"
|
||
echo "Host: {{ inventory_hostname }}"
|
||
echo ""
|
||
|
||
echo "System Usage:"
|
||
docker system df
|
||
echo ""
|
||
|
||
echo "Container Count:"
|
||
echo "Running: $(docker ps -q | wc -l)"
|
||
echo "Stopped: $(docker ps -aq --filter status=exited | wc -l)"
|
||
echo "Total: $(docker ps -aq | wc -l)"
|
||
echo ""
|
||
|
||
echo "Image Count:"
|
||
echo "Total: $(docker images -q | wc -l)"
|
||
echo "Dangling: $(docker images -f dangling=true -q | wc -l)"
|
||
echo ""
|
||
|
||
echo "Volume Count:"
|
||
echo "Total: $(docker volume ls -q | wc -l)"
|
||
echo "Dangling: $(docker volume ls -f dangling=true -q | wc -l)"
|
||
echo ""
|
||
|
||
echo "Network Count:"
|
||
echo "Total: $(docker network ls -q | wc -l)"
|
||
echo "Custom: $(docker network ls --filter type=custom -q | wc -l)"
|
||
register: post_cleanup_info
|
||
changed_when: false
|
||
|
||
- name: Generate cleanup report
|
||
copy:
|
||
content: |
|
||
🧹 DOCKER CLEANUP REPORT - {{ inventory_hostname }}
|
||
===============================================
|
||
|
||
📅 Cleanup Date: {{ ansible_date_time.iso8601 }}
|
||
🖥️ Host: {{ inventory_hostname }}
|
||
🔍 Dry Run: {{ dry_run }}
|
||
💪 Aggressive Mode: {{ aggressive_cleanup }}
|
||
📦 Image Retention: {{ keep_images_days }} days
|
||
💾 Keep Volumes: {{ keep_volumes }}
|
||
📝 Log Cleanup: {{ cleanup_logs }}
|
||
|
||
📊 BEFORE CLEANUP:
|
||
{{ pre_cleanup_info.stdout }}
|
||
|
||
🔧 CLEANUP ACTIONS:
|
||
|
||
🗑️ Stopped Containers:
|
||
{{ remove_stopped_containers.stdout }}
|
||
|
||
🖼️ Dangling Images:
|
||
{{ remove_dangling_images.stdout }}
|
||
|
||
{% if aggressive_cleanup %}
|
||
📦 Old Images:
|
||
{{ remove_old_images.stdout }}
|
||
{% endif %}
|
||
|
||
💾 Dangling Volumes:
|
||
{{ remove_dangling_volumes.stdout }}
|
||
|
||
🌐 Unused Networks:
|
||
{{ remove_unused_networks.stdout }}
|
||
|
||
{% if cleanup_logs %}
|
||
📝 Container Logs:
|
||
{{ cleanup_logs_result.stdout }}
|
||
{% endif %}
|
||
|
||
🧹 System Prune:
|
||
{{ system_prune_result.stdout }}
|
||
|
||
📊 AFTER CLEANUP:
|
||
{{ post_cleanup_info.stdout }}
|
||
|
||
💡 RECOMMENDATIONS:
|
||
- Schedule regular cleanup: cron job for this playbook
|
||
- Monitor disk usage: ansible-playbook playbooks/disk_usage_report.yml
|
||
- Consider log rotation: ansible-playbook playbooks/log_rotation.yml
|
||
{% if not aggressive_cleanup %}
|
||
- For more space: run with -e "aggressive_cleanup=true"
|
||
{% endif %}
|
||
|
||
✅ CLEANUP COMPLETE
|
||
|
||
dest: "/tmp/docker_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cleanup_report.txt"
|
||
|
||
- name: Display cleanup summary
|
||
debug:
|
||
msg: |
|
||
|
||
✅ DOCKER CLEANUP COMPLETE - {{ inventory_hostname }}
|
||
=============================================
|
||
|
||
🔍 Mode: {{ 'DRY RUN' if dry_run else 'LIVE CLEANUP' }}
|
||
💪 Aggressive: {{ aggressive_cleanup }}
|
||
|
||
📊 SUMMARY:
|
||
{{ post_cleanup_info.stdout }}
|
||
|
||
📄 Full report: /tmp/docker_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cleanup_report.txt
|
||
|
||
🔍 Next Steps:
|
||
{% if dry_run %}
|
||
- Run without dry_run to perform actual cleanup
|
||
{% endif %}
|
||
- Monitor: ansible-playbook playbooks/disk_usage_report.yml
|
||
- Schedule regular cleanup via cron
|
||
|
||
=============================================
|
||
|
||
- name: Restart Docker daemon if needed
|
||
systemd:
|
||
name: docker
|
||
state: restarted
|
||
when:
|
||
- restart_docker | default(false) | bool
|
||
- not dry_run | bool
|
||
register: docker_restart
|
||
|
||
- name: Verify services after cleanup
|
||
shell: |
|
||
echo "Verifying critical services are still running..."
|
||
sleep 10 # Give services time to start
|
||
|
||
critical_services=("plex" "immich-server" "vaultwarden" "grafana" "prometheus")
|
||
failed_services=()
|
||
|
||
for service in "${critical_services[@]}"; do
|
||
if ! docker ps --filter "name=$service" --format "{{.Names}}" | grep -q "$service"; then
|
||
failed_services+=("$service")
|
||
fi
|
||
done
|
||
|
||
if [ ${#failed_services[@]} -eq 0 ]; then
|
||
echo "✅ All critical services verified running"
|
||
else
|
||
echo "⚠️ Some services may need attention: ${failed_services[*]}"
|
||
fi
|
||
register: service_verification
|
||
when:
|
||
- not dry_run | bool
|
||
- verify_services | default(true) | bool
|
||
|
||
- name: Display service verification
|
||
debug:
|
||
msg: "{{ service_verification.stdout }}"
|
||
when: service_verification is defined
|