--- - name: Container Dependency Mapping and Orchestration hosts: all gather_facts: yes vars: dependency_timestamp: "{{ ansible_date_time.iso8601 }}" dependency_report_dir: "/tmp/dependency_reports" restart_timeout: 300 health_check_retries: 5 health_check_delay: 10 tasks: - name: Create dependency reports directory file: path: "{{ dependency_report_dir }}" state: directory mode: '0755' delegate_to: localhost run_once: true - name: Check if Docker is available shell: command -v docker >/dev/null 2>&1 register: docker_available changed_when: false ignore_errors: yes - name: Skip Docker tasks if not available set_fact: skip_docker: "{{ docker_available.rc != 0 }}" - name: Get all running containers shell: | docker ps --format "{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo "No containers" register: running_containers changed_when: false when: not skip_docker - name: Get all containers (including stopped) shell: | docker ps -a --format "{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo "No containers" register: all_containers changed_when: false when: not skip_docker - name: Analyze Docker Compose dependencies shell: | echo "=== DOCKER COMPOSE DEPENDENCY ANALYSIS ===" # Find all docker-compose files compose_files=$(find /opt /home -name "docker-compose*.yml" -o -name "compose*.yml" 2>/dev/null | head -20) if [ -z "$compose_files" ]; then echo "No Docker Compose files found" exit 0 fi echo "Found Docker Compose files:" echo "$compose_files" echo "" # Analyze dependencies in each compose file for compose_file in $compose_files; do if [ -f "$compose_file" ]; then echo "=== Analyzing: $compose_file ===" # Extract service names services=$(grep -E "^ [a-zA-Z0-9_-]+:" "$compose_file" | sed 's/://g' | sed 's/^ //' | sort) echo "Services: $(echo $services | tr '\n' ' ')" # Look for depends_on relationships echo "Dependencies found:" grep -A 5 -B 1 "depends_on:" "$compose_file" 2>/dev/null || echo " No explicit depends_on found" # Look for network dependencies echo "Networks:" grep -E "networks:|external_links:" "$compose_file" 2>/dev/null | head -5 || echo " Default networks" # Look for volume dependencies echo "Shared volumes:" grep -E "volumes_from:|volumes:" "$compose_file" 2>/dev/null | head -5 || echo " No shared volumes" echo "" fi done register: compose_analysis changed_when: false when: not skip_docker - name: Analyze container network connections shell: | if ! command -v docker >/dev/null 2>&1; then echo "Docker not available" exit 0 fi echo "=== CONTAINER NETWORK ANALYSIS ===" # Get all Docker networks echo "Docker Networks:" docker network ls --format "table {{.Name}}\t{{.Driver}}\t{{.Scope}}" 2>/dev/null || echo "No networks found" echo "" # Analyze each network networks=$(docker network ls --format "{{.Name}}" 2>/dev/null | grep -v "bridge\|host\|none") for network in $networks; do echo "=== Network: $network ===" containers_in_network=$(docker network inspect "$network" --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null) if [ -n "$containers_in_network" ]; then echo "Connected containers: $containers_in_network" else echo "No containers connected" fi echo "" done # Check for port conflicts echo "=== PORT USAGE ANALYSIS ===" docker ps --format "{{.Names}}\t{{.Ports}}" 2>/dev/null | grep -E ":[0-9]+->" | while read line; do container=$(echo "$line" | cut -f1) ports=$(echo "$line" | cut -f2 | grep -oE "[0-9]+:" | sed 's/://' | sort -n) if [ -n "$ports" ]; then echo "$container: $(echo $ports | tr '\n' ' ')" fi done register: network_analysis changed_when: false when: not skip_docker - name: Detect service health endpoints shell: | if ! command -v docker >/dev/null 2>&1; then echo "Docker not available" exit 0 fi echo "=== HEALTH ENDPOINT DETECTION ===" # Common health check patterns health_patterns="/health /healthz /ping /status /api/health /health/ready /health/live" # Get containers with exposed ports docker ps --format "{{.Names}}\t{{.Ports}}" 2>/dev/null | grep -E ":[0-9]+->" | while read line; do container=$(echo "$line" | cut -f1) ports=$(echo "$line" | cut -f2 | grep -oE "0\.0\.0\.0:[0-9]+" | cut -d: -f2) echo "Container: $container" for port in $ports; do echo " Port $port:" for pattern in $health_patterns; do # Test HTTP health endpoint if curl -s -f -m 2 "http://localhost:$port$pattern" >/dev/null 2>&1; then echo " ✅ http://localhost:$port$pattern" break elif curl -s -f -m 2 "https://localhost:$port$pattern" >/dev/null 2>&1; then echo " ✅ https://localhost:$port$pattern" break fi done done echo "" done register: health_endpoints changed_when: false when: not skip_docker ignore_errors: yes - name: Analyze container resource dependencies shell: | if ! command -v docker >/dev/null 2>&1; then echo "Docker not available" exit 0 fi echo "=== RESOURCE DEPENDENCY ANALYSIS ===" # Check for containers that might be databases or core services echo "Potential Core Services (databases, caches, etc.):" docker ps --format "{{.Names}}\t{{.Image}}" 2>/dev/null | grep -iE "(postgres|mysql|mariadb|redis|mongo|elasticsearch|rabbitmq|kafka)" || echo "No obvious database containers found" echo "" # Check for reverse proxies and load balancers echo "Potential Reverse Proxies/Load Balancers:" docker ps --format "{{.Names}}\t{{.Image}}" 2>/dev/null | grep -iE "(nginx|apache|traefik|haproxy|caddy)" || echo "No obvious proxy containers found" echo "" # Check for monitoring services echo "Monitoring Services:" docker ps --format "{{.Names}}\t{{.Image}}" 2>/dev/null | grep -iE "(prometheus|grafana|influxdb|telegraf|node-exporter)" || echo "No obvious monitoring containers found" echo "" # Analyze container restart policies echo "Container Restart Policies:" docker ps -a --format "{{.Names}}" 2>/dev/null | while read container; do if [ -n "$container" ]; then policy=$(docker inspect "$container" --format '{{.HostConfig.RestartPolicy.Name}}' 2>/dev/null) echo "$container: $policy" fi done register: resource_analysis changed_when: false when: not skip_docker - name: Create dependency map set_fact: dependency_map: timestamp: "{{ dependency_timestamp }}" hostname: "{{ inventory_hostname }}" docker_available: "{{ not skip_docker }}" containers: running: "{{ running_containers.stdout_lines | default([]) | length }}" total: "{{ all_containers.stdout_lines | default([]) | length }}" analysis: compose_files: "{{ compose_analysis.stdout | default('Docker not available') }}" network_topology: "{{ network_analysis.stdout | default('Docker not available') }}" health_endpoints: "{{ health_endpoints.stdout | default('Docker not available') }}" resource_dependencies: "{{ resource_analysis.stdout | default('Docker not available') }}" - name: Display dependency analysis debug: msg: | ========================================== 🔗 DEPENDENCY ANALYSIS - {{ inventory_hostname }} ========================================== 📊 CONTAINER SUMMARY: - Running Containers: {{ dependency_map.containers.running }} - Total Containers: {{ dependency_map.containers.total }} - Docker Available: {{ dependency_map.docker_available }} 🐳 COMPOSE FILE ANALYSIS: {{ dependency_map.analysis.compose_files }} 🌐 NETWORK TOPOLOGY: {{ dependency_map.analysis.network_topology }} 🏥 HEALTH ENDPOINTS: {{ dependency_map.analysis.health_endpoints }} 📦 RESOURCE DEPENDENCIES: {{ dependency_map.analysis.resource_dependencies }} ========================================== - name: Generate dependency report copy: content: | { "timestamp": "{{ dependency_map.timestamp }}", "hostname": "{{ dependency_map.hostname }}", "docker_available": {{ dependency_map.docker_available | lower }}, "container_summary": { "running": {{ dependency_map.containers.running }}, "total": {{ dependency_map.containers.total }} }, "analysis": { "compose_files": {{ dependency_map.analysis.compose_files | to_json }}, "network_topology": {{ dependency_map.analysis.network_topology | to_json }}, "health_endpoints": {{ dependency_map.analysis.health_endpoints | to_json }}, "resource_dependencies": {{ dependency_map.analysis.resource_dependencies | to_json }} }, "recommendations": [ {% if dependency_map.containers.running > 20 %} "Consider implementing container orchestration for {{ dependency_map.containers.running }} containers", {% endif %} {% if 'No explicit depends_on found' in dependency_map.analysis.compose_files %} "Add explicit depends_on relationships to Docker Compose files", {% endif %} {% if 'No obvious database containers found' not in dependency_map.analysis.resource_dependencies %} "Ensure database containers have proper backup and recovery procedures", {% endif %} "Regular dependency mapping recommended for infrastructure changes" ] } dest: "{{ dependency_report_dir }}/{{ inventory_hostname }}_dependencies_{{ ansible_date_time.epoch }}.json" delegate_to: localhost - name: Orchestrated container restart (when service_name is provided) block: - name: Validate service name parameter fail: msg: "service_name parameter is required for restart operations" when: service_name is not defined - name: Check if service exists shell: | if command -v docker >/dev/null 2>&1; then docker ps -a --format "{{.Names}}" | grep -x "{{ service_name }}" || echo "not_found" else echo "docker_not_available" fi register: service_exists changed_when: false - name: Fail if service not found fail: msg: "Service '{{ service_name }}' not found on {{ inventory_hostname }}" when: service_exists.stdout == "not_found" - name: Get service dependencies (from compose file) shell: | # Find compose file containing this service compose_file="" for file in $(find /opt /home -name "docker-compose*.yml" -o -name "compose*.yml" 2>/dev/null); do if grep -q "^ {{ service_name }}:" "$file" 2>/dev/null; then compose_file="$file" break fi done if [ -n "$compose_file" ]; then echo "Found in: $compose_file" # Extract dependencies awk '/^ {{ service_name }}:/,/^ [a-zA-Z]/ { if (/depends_on:/) { getline while (/^ - /) { gsub(/^ - /, "") print $0 getline } } }' "$compose_file" 2>/dev/null || echo "no_dependencies" else echo "no_compose_file" fi register: service_dependencies changed_when: false - name: Stop dependent services first shell: | if [ "{{ service_dependencies.stdout }}" != "no_dependencies" ] && [ "{{ service_dependencies.stdout }}" != "no_compose_file" ]; then echo "Stopping dependent services..." # This would need to be implemented based on your specific dependency chain echo "Dependencies found: {{ service_dependencies.stdout }}" fi register: stop_dependents when: cascade_restart | default(false) | bool - name: Restart the target service shell: | echo "Restarting {{ service_name }}..." docker restart "{{ service_name }}" # Wait for container to be running timeout {{ restart_timeout }} bash -c ' while [ "$(docker inspect {{ service_name }} --format "{{.State.Status}}" 2>/dev/null)" != "running" ]; do sleep 2 done ' register: restart_result - name: Verify service health shell: | # Wait a moment for service to initialize sleep {{ health_check_delay }} # Check if container is running if [ "$(docker inspect {{ service_name }} --format '{{.State.Status}}' 2>/dev/null)" = "running" ]; then echo "✅ Container is running" # Try to find and test health endpoint ports=$(docker port {{ service_name }} 2>/dev/null | grep -oE "[0-9]+$" | head -1) if [ -n "$ports" ]; then for endpoint in /health /healthz /ping /status; do if curl -s -f -m 5 "http://localhost:$ports$endpoint" >/dev/null 2>&1; then echo "✅ Health endpoint responding: http://localhost:$ports$endpoint" exit 0 fi done echo "⚠️ No health endpoint found, but container is running" else echo "⚠️ No exposed ports found, but container is running" fi else echo "❌ Container is not running" exit 1 fi register: health_check retries: "{{ health_check_retries }}" delay: "{{ health_check_delay }}" - name: Restart dependent services shell: | if [ "{{ service_dependencies.stdout }}" != "no_dependencies" ] && [ "{{ service_dependencies.stdout }}" != "no_compose_file" ]; then echo "Restarting dependent services..." # This would need to be implemented based on your specific dependency chain echo "Would restart dependencies: {{ service_dependencies.stdout }}" fi when: cascade_restart | default(false) | bool when: service_name is defined and not skip_docker - name: Summary message debug: msg: | 🔗 Dependency analysis complete for {{ inventory_hostname }} 📄 Report saved to: {{ dependency_report_dir }}/{{ inventory_hostname }}_dependencies_{{ ansible_date_time.epoch }}.json {% if service_name is defined %} 🔄 Service restart summary: - Target service: {{ service_name }} - Restart result: {{ restart_result.rc | default('N/A') }} - Health check: {{ 'PASSED' if health_check.rc == 0 else 'FAILED' }} {% endif %} 💡 Use -e service_name= to restart specific services 💡 Use -e cascade_restart=true to restart dependent services