mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 05:40:11 -08:00
- Introduced `docker-deployment-manager.sh` for managing Docker stack deployments across multiple servers, including initialization, deployment, and status checks. - Added `stack-assignment-helper.sh` to analyze Docker stacks and suggest server assignments based on predefined patterns. - Removed outdated `SETUP_COMPLETE.md` file as it is no longer relevant to the current setup process. - Ref - Documentation review #11
468 lines
15 KiB
Bash
Executable File
468 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# stack-assignment-helper.sh - Helper script to analyze and assign Docker stacks to servers
|
|
# Author: Shell Repository
|
|
# Description: Analyze Docker stacks and suggest server assignments based on patterns
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
RED='\033[0;31m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
DOCKER_DIR="$HOME/docker"
|
|
DEPLOYMENT_CONFIG_DIR="$HOME/.docker-deployment"
|
|
|
|
# Stack classification patterns
|
|
declare -A MEDIA_PATTERNS=(
|
|
["plex"]="media-server"
|
|
["jellyfin"]="media-server"
|
|
["emby"]="media-server"
|
|
["kodi"]="media-server"
|
|
["photoprism"]="media-server"
|
|
["immich"]="media-server"
|
|
["nextcloud"]="media-server"
|
|
)
|
|
|
|
# Multi-server patterns - stacks that should run on ALL servers
|
|
declare -A MULTI_SERVER_PATTERNS=(
|
|
["dozzle"]="monitoring"
|
|
["dockge"]="management"
|
|
["diun"]="monitoring"
|
|
["watchtower"]="monitoring"
|
|
["portainer"]="management"
|
|
)
|
|
|
|
declare -A DOWNLOAD_PATTERNS=(
|
|
["radarr"]="download-server"
|
|
["sonarr"]="download-server"
|
|
["lidarr"]="download-server"
|
|
["readarr"]="download-server"
|
|
["prowlarr"]="download-server"
|
|
["sabnzbd"]="download-server"
|
|
["nzbget"]="download-server"
|
|
["qbittorrent"]="download-server"
|
|
["transmission"]="download-server"
|
|
["deluge"]="download-server"
|
|
["overseerr"]="download-server"
|
|
["ombi"]="download-server"
|
|
["requestrr"]="download-server"
|
|
["jackett"]="download-server"
|
|
["metube"]="download-server"
|
|
["pinchflat"]="download-server"
|
|
["pdf"]="download-server"
|
|
)
|
|
|
|
declare -A UTILITY_PATTERNS=(
|
|
["traefik"]="reverse-proxy"
|
|
["nginx"]="reverse-proxy"
|
|
["caddy"]="reverse-proxy"
|
|
["nginxproxymanager"]="reverse-proxy"
|
|
["grafana"]="monitoring"
|
|
["prometheus"]="monitoring"
|
|
["uptime-kuma"]="monitoring"
|
|
["gatus"]="monitoring"
|
|
["ntfy"]="monitoring"
|
|
["adguard"]="security"
|
|
["portainer"]="management"
|
|
["watchtower"]="management"
|
|
["vaultwarden"]="security"
|
|
["authelia"]="security"
|
|
["authentik"]="security"
|
|
["duplicati"]="backup"
|
|
["restic"]="backup"
|
|
["database"]="infrastructure"
|
|
["cloudflare"]="infrastructure"
|
|
["tclip"]="utility"
|
|
["opengist"]="utility"
|
|
["newt"]="utility"
|
|
["pangolin"]="utility"
|
|
["omni-tools"]="utility"
|
|
["golinks"]="utility"
|
|
["hoarder"]="productivity"
|
|
["paperless-ng"]="productivity"
|
|
["docmost"]="productivity"
|
|
["wiki"]="productivity"
|
|
["filebrowser"]="productivity"
|
|
["n8n"]="automation"
|
|
)
|
|
|
|
# Analyze Docker directory structure
|
|
analyze_stacks() {
|
|
echo -e "${BLUE}=== Analyzing Docker Stacks ===${NC}"
|
|
echo ""
|
|
|
|
if [ ! -d "$DOCKER_DIR" ]; then
|
|
echo -e "${RED}Docker directory not found: $DOCKER_DIR${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
local total_stacks=0
|
|
local classified_stacks=0
|
|
|
|
# Arrays to store classifications
|
|
local media_stacks=()
|
|
local download_stacks=()
|
|
local utility_stacks=()
|
|
local unclassified_stacks=()
|
|
|
|
# Analyze each directory in docker folder
|
|
for stack_dir in "$DOCKER_DIR"/*; do
|
|
if [ -d "$stack_dir" ]; then
|
|
local stack_name=$(basename "$stack_dir")
|
|
total_stacks=$((total_stacks + 1))
|
|
|
|
local classification=""
|
|
local suggested_server=""
|
|
|
|
# Check against patterns - prioritize multi-server patterns first
|
|
if [[ -n "${MULTI_SERVER_PATTERNS[$stack_name]}" ]]; then
|
|
classification="Multi-Server Tool"
|
|
suggested_server="all-servers"
|
|
utility_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
elif [[ -n "${MEDIA_PATTERNS[$stack_name]}" ]]; then
|
|
classification="Media Server"
|
|
suggested_server="europa"
|
|
media_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
elif [[ -n "${DOWNLOAD_PATTERNS[$stack_name]}" ]]; then
|
|
classification="Download/Acquisition"
|
|
suggested_server="io"
|
|
download_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
elif [[ -n "${UTILITY_PATTERNS[$stack_name]}" ]]; then
|
|
classification="Utility/Monitoring"
|
|
case "${UTILITY_PATTERNS[$stack_name]}" in
|
|
"reverse-proxy"|"productivity")
|
|
suggested_server="europa"
|
|
;;
|
|
"monitoring"|"backup"|"security"|"infrastructure")
|
|
suggested_server="racknerd"
|
|
;;
|
|
"automation"|"utility")
|
|
suggested_server="io"
|
|
;;
|
|
*)
|
|
suggested_server="racknerd"
|
|
;;
|
|
esac
|
|
utility_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
else
|
|
# Try to classify based on docker-compose.yml content
|
|
local compose_file="$stack_dir/docker-compose.yml"
|
|
if [ -f "$compose_file" ]; then
|
|
if grep -qi "plex\|jellyfin\|emby\|photoprism\|immich" "$compose_file"; then
|
|
classification="Media Server (detected)"
|
|
suggested_server="europa"
|
|
media_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
elif grep -qi "radarr\|sonarr\|sabnzbd\|qbittorrent\|transmission" "$compose_file"; then
|
|
classification="Download/Acquisition (detected)"
|
|
suggested_server="io"
|
|
download_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
elif grep -qi "grafana\|prometheus\|monitoring\|uptime" "$compose_file"; then
|
|
classification="Monitoring (detected)"
|
|
suggested_server="racknerd"
|
|
utility_stacks+=("$stack_name")
|
|
classified_stacks=$((classified_stacks + 1))
|
|
else
|
|
classification="Unknown"
|
|
suggested_server="manual-review"
|
|
unclassified_stacks+=("$stack_name")
|
|
fi
|
|
else
|
|
classification="No compose file"
|
|
suggested_server="manual-review"
|
|
unclassified_stacks+=("$stack_name")
|
|
fi
|
|
fi
|
|
|
|
# Display analysis
|
|
local status_icon=""
|
|
local server_color=""
|
|
case "$suggested_server" in
|
|
"europa")
|
|
status_icon="🎬"
|
|
server_color="$GREEN"
|
|
;;
|
|
"io")
|
|
status_icon="📥"
|
|
server_color="$BLUE"
|
|
;;
|
|
"racknerd")
|
|
status_icon="🔧"
|
|
server_color="$YELLOW"
|
|
;;
|
|
"all")
|
|
status_icon="🌐"
|
|
server_color="$GREEN"
|
|
;;
|
|
*)
|
|
status_icon="❓"
|
|
server_color="$RED"
|
|
;;
|
|
esac
|
|
|
|
printf "%-20s %-25s ${server_color}%-10s${NC} %s\n" \
|
|
"$status_icon $stack_name" \
|
|
"$classification" \
|
|
"$suggested_server" \
|
|
"$(check_stack_health "$stack_dir")"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${BLUE}=== Classification Summary ===${NC}"
|
|
echo "Total stacks found: $total_stacks"
|
|
echo "Classified stacks: $classified_stacks"
|
|
echo "Unclassified stacks: $((total_stacks - classified_stacks))"
|
|
echo ""
|
|
|
|
# Show server assignments
|
|
if [ ${#media_stacks[@]} -gt 0 ]; then
|
|
echo -e "${GREEN}🎬 Europa (Media Server):${NC}"
|
|
printf '%s\n' "${media_stacks[@]}" | sed 's/^/ - /'
|
|
echo ""
|
|
fi
|
|
|
|
if [ ${#download_stacks[@]} -gt 0 ]; then
|
|
echo -e "${BLUE}📥 IO (Download Server):${NC}"
|
|
printf '%s\n' "${download_stacks[@]}" | sed 's/^/ - /'
|
|
echo ""
|
|
fi
|
|
|
|
if [ ${#utility_stacks[@]} -gt 0 ]; then
|
|
echo -e "${YELLOW}🔧 Racknerd (Utility/Backup):${NC}"
|
|
printf '%s\n' "${utility_stacks[@]}" | sed 's/^/ - /'
|
|
echo ""
|
|
fi
|
|
|
|
if [ ${#unclassified_stacks[@]} -gt 0 ]; then
|
|
echo -e "${RED}❓ Manual Review Required:${NC}"
|
|
printf '%s\n' "${unclassified_stacks[@]}" | sed 's/^/ - /'
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# Check stack health indicators
|
|
check_stack_health() {
|
|
local stack_dir="$1"
|
|
local health_indicators=()
|
|
|
|
# Check for docker-compose.yml
|
|
if [ -f "$stack_dir/docker-compose.yml" ]; then
|
|
health_indicators+=("compose")
|
|
fi
|
|
|
|
# Check for .env files
|
|
if find "$stack_dir" -name "*.env" -o -name ".env*" | grep -q .; then
|
|
health_indicators+=("env")
|
|
fi
|
|
|
|
# Check for data/config directories
|
|
if [ -d "$stack_dir/data" ] || [ -d "$stack_dir/config" ]; then
|
|
health_indicators+=("data")
|
|
fi
|
|
|
|
# Format output
|
|
if [ ${#health_indicators[@]} -eq 0 ]; then
|
|
echo "⚠️ Incomplete"
|
|
else
|
|
echo "✅ $(IFS=,; echo "${health_indicators[*]}")"
|
|
fi
|
|
}
|
|
|
|
# Generate deployment configuration
|
|
generate_deployment_config() {
|
|
echo -e "${YELLOW}Generating deployment configuration...${NC}"
|
|
|
|
# Ensure deployment config directory exists
|
|
mkdir -p "$DEPLOYMENT_CONFIG_DIR/stacks"
|
|
|
|
# Create individual stack configurations
|
|
for stack_dir in "$DOCKER_DIR"/*; do
|
|
if [ -d "$stack_dir" ]; then
|
|
local stack_name=$(basename "$stack_dir")
|
|
local config_file="$DEPLOYMENT_CONFIG_DIR/stacks/${stack_name}.yml"
|
|
|
|
# Determine server assignment
|
|
local server=""
|
|
if [[ -n "${MEDIA_PATTERNS[$stack_name]}" ]]; then
|
|
server="europa"
|
|
elif [[ -n "${DOWNLOAD_PATTERNS[$stack_name]}" ]]; then
|
|
server="io"
|
|
elif [[ -n "${UTILITY_PATTERNS[$stack_name]}" ]]; then
|
|
case "${UTILITY_PATTERNS[$stack_name]}" in
|
|
"reverse-proxy"|"management")
|
|
server="europa"
|
|
;;
|
|
*)
|
|
server="racknerd"
|
|
;;
|
|
esac
|
|
elif [[ -n "${MULTI_SERVER_PATTERNS[$stack_name]}" ]]; then
|
|
server="all"
|
|
else
|
|
server="manual-assignment-required"
|
|
fi
|
|
|
|
# Generate stack configuration
|
|
cat > "$config_file" << EOF
|
|
# Auto-generated stack configuration for $stack_name
|
|
name: "$stack_name"
|
|
description: "Docker stack for $stack_name"
|
|
|
|
deployment:
|
|
servers: ["$server"]
|
|
priority: "medium"
|
|
dependencies: []
|
|
restart_policy: "unless-stopped"
|
|
|
|
health_check:
|
|
enabled: false
|
|
timeout: 30
|
|
retries: 3
|
|
|
|
backup:
|
|
enabled: true
|
|
schedule: "0 2 * * *"
|
|
retention_days: 7
|
|
|
|
# Review and customize this configuration as needed
|
|
EOF
|
|
|
|
echo -e "${GREEN}✓ Generated: $stack_name.yml${NC}"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${BLUE}Configuration files generated in: $DEPLOYMENT_CONFIG_DIR/stacks/${NC}"
|
|
echo -e "${YELLOW}Please review and customize the generated configurations before deployment.${NC}"
|
|
}
|
|
|
|
# Show resource usage estimates
|
|
show_resource_estimates() {
|
|
echo -e "${BLUE}=== Resource Usage Estimates ===${NC}"
|
|
echo ""
|
|
|
|
# Define resource estimates for common stacks
|
|
declare -A CPU_ESTIMATES=(
|
|
["plex"]="2-4"
|
|
["jellyfin"]="1-3"
|
|
["radarr"]="0.5-1"
|
|
["sonarr"]="0.5-1"
|
|
["sabnzbd"]="1-2"
|
|
["qbittorrent"]="0.5-2"
|
|
["grafana"]="0.5-1"
|
|
["prometheus"]="1-2"
|
|
["traefik"]="0.5-1"
|
|
["nextcloud"]="1-2"
|
|
)
|
|
|
|
declare -A MEMORY_ESTIMATES=(
|
|
["plex"]="2-4GB"
|
|
["jellyfin"]="1-2GB"
|
|
["radarr"]="256-512MB"
|
|
["sonarr"]="256-512MB"
|
|
["sabnzbd"]="512MB-1GB"
|
|
["qbittorrent"]="256-512MB"
|
|
["grafana"]="256-512MB"
|
|
["prometheus"]="512MB-1GB"
|
|
["traefik"]="128-256MB"
|
|
["nextcloud"]="512MB-1GB"
|
|
)
|
|
|
|
# Aggregate by server
|
|
local europa_cpu=0
|
|
local io_cpu=0
|
|
local racknerd_cpu=0
|
|
|
|
for stack_dir in "$DOCKER_DIR"/*; do
|
|
if [ -d "$stack_dir" ]; then
|
|
local stack_name=$(basename "$stack_dir")
|
|
local cpu_est="${CPU_ESTIMATES[$stack_name]:-0.5}"
|
|
local mem_est="${MEMORY_ESTIMATES[$stack_name]:-256MB}"
|
|
|
|
# Extract numeric part for aggregation (use lower bound)
|
|
local cpu_num=$(echo "$cpu_est" | cut -d'-' -f1)
|
|
|
|
printf "%-20s CPU: %-8s Memory: %-10s\n" "$stack_name" "$cpu_est cores" "$mem_est"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}Note: These are rough estimates. Actual usage depends on workload and configuration.${NC}"
|
|
}
|
|
|
|
# Suggest optimization opportunities
|
|
suggest_optimizations() {
|
|
echo -e "${BLUE}=== Optimization Suggestions ===${NC}"
|
|
echo ""
|
|
|
|
echo -e "${GREEN}💡 Recommendations:${NC}"
|
|
echo "1. Co-locate Traefik with media services (Europa) for efficient reverse proxy"
|
|
echo "2. Keep download services (IO) on fast storage for temporary files"
|
|
echo "3. Place monitoring/backup services (Racknerd) on cost-effective hardware"
|
|
echo "4. Consider resource limits in docker-compose.yml files"
|
|
echo "5. Use shared volumes for media access across containers"
|
|
echo ""
|
|
|
|
echo -e "${YELLOW}🔧 Integration Points:${NC}"
|
|
echo "1. Integrate with existing backup-env-files.sh for environment management"
|
|
echo "2. Use existing crontab system for deployment automation"
|
|
echo "3. Leverage ntfy notifications for deployment status"
|
|
echo "4. Consider using existing logging infrastructure"
|
|
echo ""
|
|
}
|
|
|
|
# Main function
|
|
main() {
|
|
case "${1:-analyze}" in
|
|
analyze|--analyze|-a)
|
|
analyze_stacks
|
|
;;
|
|
generate|--generate|-g)
|
|
generate_deployment_config
|
|
;;
|
|
resources|--resources|-r)
|
|
show_resource_estimates
|
|
;;
|
|
optimize|--optimize|-o)
|
|
suggest_optimizations
|
|
;;
|
|
all|--all)
|
|
analyze_stacks
|
|
echo ""
|
|
show_resource_estimates
|
|
echo ""
|
|
suggest_optimizations
|
|
;;
|
|
help|--help|-h)
|
|
echo "Usage: $0 [COMMAND]"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " analyze Analyze and classify Docker stacks (default)"
|
|
echo " generate Generate deployment configuration files"
|
|
echo " resources Show resource usage estimates"
|
|
echo " optimize Show optimization suggestions"
|
|
echo " all Run all analysis commands"
|
|
echo " help Show this help message"
|
|
;;
|
|
*)
|
|
echo -e "${RED}Unknown command: $1${NC}"
|
|
echo "Use '$0 help' for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main "$@"
|