mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 03:20:12 -08:00
feat: Add Docker deployment manager and stack assignment helper scripts
- 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
This commit is contained in:
467
docker-deployment/stack-assignment-helper.sh
Executable file
467
docker-deployment/stack-assignment-helper.sh
Executable file
@@ -0,0 +1,467 @@
|
||||
#!/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 "$@"
|
||||
Reference in New Issue
Block a user