Files
homelab-optimized/ansible/automation/playbooks/log_rotation.yml
Gitea Mirror Bot 82b69ea7e3
Some checks failed
Documentation / Build Docusaurus (push) Failing after 17m9s
Documentation / Deploy to GitHub Pages (push) Has been skipped
Sanitized mirror from private repository - 2026-03-21 07:31:47 UTC
2026-03-21 07:31:47 +00:00

426 lines
16 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
# Log Rotation and Cleanup Playbook
# Manage log files across all services and system components
# Usage: ansible-playbook playbooks/log_rotation.yml
# Usage: ansible-playbook playbooks/log_rotation.yml -e "aggressive_cleanup=true"
# Usage: ansible-playbook playbooks/log_rotation.yml -e "dry_run=true"
- name: Log Rotation and Cleanup
hosts: "{{ host_target | default('all') }}"
gather_facts: yes
vars:
dry_run: "{{ dry_run | default(false) }}"
aggressive_cleanup: "{{ aggressive_cleanup | default(false) }}"
max_log_age_days: "{{ max_log_age_days | default(30) }}"
max_log_size: "{{ max_log_size | default('100M') }}"
keep_compressed_logs: "{{ keep_compressed_logs | default(true) }}"
compress_old_logs: "{{ compress_old_logs | default(true) }}"
tasks:
- name: Create log cleanup report directory
file:
path: "/tmp/log_cleanup/{{ ansible_date_time.date }}"
state: directory
mode: '0755'
- name: Display log cleanup plan
debug:
msg: |
📝 LOG ROTATION AND CLEANUP PLAN
================================
🖥️ Host: {{ inventory_hostname }}
📅 Date: {{ ansible_date_time.date }}
🧪 Dry Run: {{ dry_run }}
💪 Aggressive: {{ aggressive_cleanup }}
📅 Max Age: {{ max_log_age_days }} days
📦 Max Size: {{ max_log_size }}
🗜️ Compress: {{ compress_old_logs }}
- name: Analyze current log usage
shell: |
echo "📊 LOG USAGE ANALYSIS"
echo "===================="
total_log_size=0
log_file_count=0
echo "=== SYSTEM LOGS ==="
if [ -d "/var/log" ]; then
system_log_size=$(du -sh /var/log 2>/dev/null | cut -f1 || echo "0")
system_log_count=$(find /var/log -type f -name "*.log" 2>/dev/null | wc -l)
echo "System logs: $system_log_size ($system_log_count files)"
echo "Largest system logs:"
find /var/log -type f -name "*.log" -exec du -h {} \; 2>/dev/null | sort -hr | head -10 || echo "No system logs found"
fi
echo ""
echo "=== DOCKER CONTAINER LOGS ==="
if [ -d "/var/lib/docker/containers" ]; then
docker_log_size=$(du -sh /var/lib/docker/containers 2>/dev/null | cut -f1 || echo "0")
docker_log_count=$(find /var/lib/docker/containers -name "*-json.log" 2>/dev/null | wc -l)
echo "Docker logs: $docker_log_size ($docker_log_count files)"
echo "Largest container logs:"
find /var/lib/docker/containers -name "*-json.log" -exec du -h {} \; 2>/dev/null | sort -hr | head -10 || echo "No Docker logs found"
fi
echo ""
echo "=== APPLICATION LOGS ==="
for log_dir in /volume1/docker /opt/docker /home; do
if [ -d "$log_dir" ]; then
app_logs=$(find "$log_dir" -name "*.log" -type f 2>/dev/null | head -20)
if [ -n "$app_logs" ]; then
echo "Application logs in $log_dir:"
echo "$app_logs" | while read log_file; do
if [ -f "$log_file" ]; then
du -h "$log_file" 2>/dev/null || echo "Cannot access $log_file"
fi
done
fi
fi
done
echo ""
echo "=== LARGE LOG FILES (>{{ max_log_size }}) ==="
find /var/log /var/lib/docker/containers /volume1 /opt -name "*.log" -size +{{ max_log_size }} -type f 2>/dev/null | head -20 | while read large_log; do
echo "$(du -h "$large_log" 2>/dev/null || echo "? $large_log")"
done || echo "No large log files found"
echo ""
echo "=== OLD LOG FILES (>{{ max_log_age_days }} days) ==="
old_logs=$(find /var/log /var/lib/docker/containers /volume1 /opt -name "*.log" -mtime +{{ max_log_age_days }} -type f 2>/dev/null | wc -l)
echo "Old log files found: $old_logs"
register: log_analysis
changed_when: false
- name: Rotate system logs
shell: |
echo "🔄 SYSTEM LOG ROTATION"
echo "====================="
rotated_logs=()
{% if dry_run %}
echo "DRY RUN: System log rotation simulation"
# Check what would be rotated
if command -v logrotate >/dev/null 2>&1; then
echo "Would run: logrotate -d /etc/logrotate.conf"
logrotate -d /etc/logrotate.conf 2>/dev/null | head -20 || echo "Logrotate config not found"
fi
{% else %}
# Force log rotation
if command -v logrotate >/dev/null 2>&1; then
echo "Running logrotate..."
logrotate -f /etc/logrotate.conf 2>/dev/null && echo "✅ System log rotation completed" || echo "⚠️ Logrotate had issues"
rotated_logs+=("system_logs")
else
echo "⚠️ Logrotate not available"
fi
# Manual rotation for specific large logs
for log_file in /var/log/syslog /var/log/auth.log /var/log/kern.log; do
if [ -f "$log_file" ] && [ $(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0) -gt 104857600 ]; then # 100MB
echo "Rotating large log: $log_file"
{% if compress_old_logs %}
gzip -c "$log_file" > "${log_file}.$(date +%Y%m%d).gz" && > "$log_file"
{% else %}
cp "$log_file" "${log_file}.$(date +%Y%m%d)" && > "$log_file"
{% endif %}
rotated_logs+=("$(basename $log_file)")
fi
done
{% endif %}
echo "📊 ROTATION SUMMARY:"
for log in "${rotated_logs[@]}"; do
echo " - $log"
done
register: system_log_rotation
- name: Manage Docker container logs
shell: |
echo "🐳 DOCKER LOG MANAGEMENT"
echo "========================"
managed_containers=()
total_space_saved=0
{% if dry_run %}
echo "DRY RUN: Docker log management simulation"
# Show what would be cleaned
large_logs=$(find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null)
if [ -n "$large_logs" ]; then
echo "Would truncate large container logs:"
echo "$large_logs" | while read log_file; do
size=$(du -h "$log_file" 2>/dev/null | cut -f1)
container_id=$(basename $(dirname "$log_file"))
container_name=$(docker ps -a --filter "id=$container_id" --format "{{.Names}}" 2>/dev/null || echo "unknown")
echo " - $container_name: $size"
done
fi
{% else %}
# Truncate large container logs
find /var/lib/docker/containers -name "*-json.log" -size +{{ max_log_size }} 2>/dev/null | while read log_file; do
if [ -f "$log_file" ]; then
container_id=$(basename $(dirname "$log_file"))
container_name=$(docker ps -a --filter "id=$container_id" --format "{{.Names}}" 2>/dev/null || echo "unknown")
size_before=$(stat -f%z "$log_file" 2>/dev/null || stat -c%s "$log_file" 2>/dev/null || echo 0)
echo "Truncating log for container: $container_name"
# Keep 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)
space_saved=$((size_before - size_after))
total_space_saved=$((total_space_saved + space_saved))
managed_containers+=("$container_name")
echo " ✅ Truncated: $(echo $space_saved | numfmt --to=iec) saved"
fi
done
# Clean up old rotated Docker logs
{% if aggressive_cleanup %}
echo "Cleaning old Docker log files..."
find /var/lib/docker/containers -name "*.log.*" -mtime +{{ max_log_age_days }} -delete 2>/dev/null
{% endif %}
{% endif %}
echo "📊 DOCKER LOG SUMMARY:"
echo "Containers managed: ${#managed_containers[@]}"
if [ $total_space_saved -gt 0 ]; then
echo "Total space saved: $(echo $total_space_saved | numfmt --to=iec)"
fi
for container in "${managed_containers[@]}"; do
echo " - $container"
done
register: docker_log_management
- name: Clean up application logs
shell: |
echo "📱 APPLICATION LOG CLEANUP"
echo "=========================="
cleaned_apps=()
{% if dry_run %}
echo "DRY RUN: Application log cleanup simulation"
# Show what would be cleaned
for log_dir in /volume1/docker /opt/docker; do
if [ -d "$log_dir" ]; then
old_app_logs=$(find "$log_dir" -name "*.log" -mtime +{{ max_log_age_days }} -type f 2>/dev/null)
if [ -n "$old_app_logs" ]; then
echo "Would clean logs in $log_dir:"
echo "$old_app_logs" | head -10
fi
fi
done
{% else %}
# Clean old application logs
for log_dir in /volume1/docker /opt/docker; do
if [ -d "$log_dir" ]; then
echo "Cleaning logs in $log_dir..."
# Compress old logs if requested
{% if compress_old_logs %}
find "$log_dir" -name "*.log" -mtime +7 -mtime -{{ max_log_age_days }} -type f 2>/dev/null | while read log_file; do
if [ -f "$log_file" ] && [[ "$log_file" != *.gz ]]; then
gzip "$log_file" 2>/dev/null && echo " Compressed: $(basename $log_file)"
fi
done
{% endif %}
# Remove very old logs
old_logs_removed=$(find "$log_dir" -name "*.log" -mtime +{{ max_log_age_days }} -type f -delete 2>/dev/null | wc -l)
{% if keep_compressed_logs %}
old_gz_removed=$(find "$log_dir" -name "*.log.gz" -mtime +$(({{ max_log_age_days }} * 2)) -type f -delete 2>/dev/null | wc -l)
{% else %}
old_gz_removed=$(find "$log_dir" -name "*.log.gz" -mtime +{{ max_log_age_days }} -type f -delete 2>/dev/null | wc -l)
{% endif %}
if [ $old_logs_removed -gt 0 ] || [ $old_gz_removed -gt 0 ]; then
echo " ✅ Cleaned $old_logs_removed logs, $old_gz_removed compressed logs"
cleaned_apps+=("$(basename $log_dir)")
fi
fi
done
{% endif %}
echo "📊 APPLICATION CLEANUP SUMMARY:"
for app in "${cleaned_apps[@]}"; do
echo " - $app"
done
register: app_log_cleanup
- name: Configure log rotation for services
shell: |
echo "⚙️ LOG ROTATION CONFIGURATION"
echo "============================="
config_updates=()
{% if dry_run %}
echo "DRY RUN: Would configure log rotation"
{% else %}
# Create custom logrotate config for Docker containers
logrotate_config="/etc/logrotate.d/docker-containers"
if [ ! -f "$logrotate_config" ]; then
echo "Creating Docker container log rotation config..."
cat > "$logrotate_config" << 'LOGROTATE_EOF'
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
size 100M
missingok
delaycompress
copytruncate
}
LOGROTATE_EOF
config_updates+=("docker-containers")
echo " ✅ Docker container log rotation configured"
fi
# Update Docker daemon configuration for log limits
docker_config="/etc/docker/daemon.json"
if [ -f "$docker_config" ]; then
# Check if log driver is already configured
if ! grep -q "log-driver" "$docker_config" 2>/dev/null; then
echo "Updating Docker daemon log configuration..."
# Backup existing config
cp "$docker_config" "${docker_config}.backup.$(date +%Y%m%d)"
# Add log configuration (this would need careful JSON manipulation in practice)
echo " Manual Docker daemon config update recommended"
echo " Add: \"log-driver\": \"json-file\", \"log-opts\": {\"max-size\": \"{{ max_log_size }}\", \"max-file\": \"3\"}"
fi
fi
{% endif %}
echo "📊 CONFIGURATION SUMMARY:"
for config in "${config_updates[@]}"; do
echo " - $config"
done
register: log_rotation_config
- name: Generate log cleanup report
copy:
content: |
📝 LOG ROTATION AND CLEANUP REPORT - {{ inventory_hostname }}
==========================================================
📅 Cleanup Date: {{ ansible_date_time.iso8601 }}
🖥️ Host: {{ inventory_hostname }}
🧪 Dry Run: {{ dry_run }}
💪 Aggressive Mode: {{ aggressive_cleanup }}
📅 Max Age: {{ max_log_age_days }} days
📦 Max Size: {{ max_log_size }}
📊 LOG USAGE ANALYSIS:
{{ log_analysis.stdout }}
🔄 SYSTEM LOG ROTATION:
{{ system_log_rotation.stdout }}
🐳 DOCKER LOG MANAGEMENT:
{{ docker_log_management.stdout }}
📱 APPLICATION LOG CLEANUP:
{{ app_log_cleanup.stdout }}
⚙️ CONFIGURATION UPDATES:
{{ log_rotation_config.stdout }}
💡 RECOMMENDATIONS:
- Schedule regular log rotation via cron
- Monitor disk usage: ansible-playbook playbooks/disk_usage_report.yml
- Configure application-specific log rotation
- Set up log monitoring and alerting
{% if not dry_run %}
- Verify services are functioning after log cleanup
{% endif %}
{% if 'Large log files' in log_analysis.stdout %}
- Consider more aggressive log size limits
{% endif %}
📊 CLEANUP SUMMARY:
- System logs: {{ 'Rotated' if 'system_logs' in system_log_rotation.stdout else 'No action needed' }}
- Docker logs: {{ 'Managed' if 'managed' in docker_log_management.stdout else 'No action needed' }}
- Application logs: {{ 'Cleaned' if 'cleaned' in app_log_cleanup.stdout else 'No action needed' }}
- Configuration: {{ 'Updated' if 'config_updates' in log_rotation_config.stdout else 'No changes' }}
✅ LOG CLEANUP COMPLETE
dest: "/tmp/log_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_log_cleanup_report.txt"
- name: Display log cleanup summary
debug:
msg: |
📝 LOG CLEANUP COMPLETE - {{ inventory_hostname }}
==========================================
📅 Date: {{ ansible_date_time.date }}
🧪 Mode: {{ 'Dry Run' if dry_run else 'Live Cleanup' }}
💪 Aggressive: {{ aggressive_cleanup }}
📊 ACTIONS TAKEN:
{{ system_log_rotation.stdout | regex_replace('\n.*', '') }}
{{ docker_log_management.stdout | regex_replace('\n.*', '') }}
{{ app_log_cleanup.stdout | regex_replace('\n.*', '') }}
📄 Full report: /tmp/log_cleanup/{{ ansible_date_time.date }}/{{ inventory_hostname }}_log_cleanup_report.txt
🔍 Next Steps:
{% if dry_run %}
- Run without dry_run to perform actual cleanup
{% endif %}
- Monitor disk usage improvements
- Schedule regular log rotation
- Verify service functionality
==========================================
- name: Restart services if needed
shell: |
echo "🔄 SERVICE RESTART CHECK"
echo "========================"
# Check if any critical services need restarting after log cleanup
services_to_restart=()
# Check if rsyslog needs restart (if log files were rotated)
if systemctl is-active --quiet rsyslog && [[ "{{ system_log_rotation.stdout }}" == *"system_logs"* ]]; then
services_to_restart+=("rsyslog")
fi
# Check if Docker daemon needs restart (if config was changed)
if [[ "{{ log_rotation_config.stdout }}" == *"docker"* ]]; then
echo "⚠️ Docker daemon config changed - manual restart may be needed"
echo " Run: sudo systemctl restart docker"
fi
{% if not dry_run %}
# Restart services that need it
for service in "${services_to_restart[@]}"; do
echo "Restarting $service..."
systemctl restart "$service" && echo " ✅ $service restarted" || echo " ❌ Failed to restart $service"
done
{% else %}
echo "DRY RUN: Would restart services: ${services_to_restart[*]}"
{% endif %}
if [ ${#services_to_restart[@]} -eq 0 ]; then
echo " No services need restarting"
fi
register: service_restart
when: restart_services | default(true) | bool