Setup scripts added

This commit is contained in:
2026-06-11 10:13:57 -04:00
parent c564ccc2ea
commit 0decccfaed
11 changed files with 778 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
# These ports are in format <host-port>:<container-port>
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
# Add any other Stream port you want to expose
# - '21:21' # FTP
environment:
- TZ=America/New_York
- PUID=1000
- PGID=1000
volumes:
- nginx-data:/data
- nginx-letsencrypt:/etc/letsencrypt
volumes:
nginx-data:
driver: local
driver_opts:
type: nfs
o: "addr=14.10.10.71,rw,nfsvers=4"
device: ":/volume1/docker/nginx/data/"
nginx-letsencrypt:
driver: local
driver_opts:
type: nfs
o: "addr=14.10.10.71,rw,nfsvers=4"
device: ":/volume1/docker/nginx/letsencrypt/"

View File

@@ -0,0 +1,47 @@
version: "3.9"
services:
coder:
# This MUST be stable for our documentation and
# other automations.
image: ghcr.io/coder/coder:${CODER_VERSION:-latest}
ports:
- "7080:7080"
environment:
CODER_PG_CONNECTION_URL: "postgresql://${POSTGRES_USER:-username}:${POSTGRES_PASSWORD:-password}@database/${POSTGRES_DB:-coder}?sslmode=disable"
CODER_HTTP_ADDRESS: "0.0.0.0:7080"
# You'll need to set CODER_ACCESS_URL to an IP or domain
# that workspaces can reach. This cannot be localhost
# or 127.0.0.1 for non-Docker templates!
CODER_ACCESS_URL: "${CODER_ACCESS_URL}"
# If the coder user does not have write permissions on
# the docker socket, you can uncomment the following
# lines and set the group ID to one that has write
# permissions on the docker socket.
#group_add: "27"
#- "998" # docker group on host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# depends_on:
# database:
# condition: service_healthy
database:
image: "postgres:14.2"
ports:
- "5432:5432"
environment:
POSTGRES_USER: ${POSTGRES_USER:-username} # The PostgreSQL user (useful to connect to the database)
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} # The PostgreSQL password (useful to connect to the database)
POSTGRES_DB: ${POSTGRES_DB:-coder} # The PostgreSQL default database (automatically created at first launch)
volumes:
- coder_data:/var/lib/postgresql/data # Use "docker volume rm coder_coder_data" to reset Coder
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-username} -d ${POSTGRES_DB:-coder}",
]
interval: 5s
timeout: 5s
retries: 5
volumes:
coder_data:

View File

@@ -0,0 +1,53 @@
#!/bin/bash
#
# Portainer Agent Deployment Script for Docker Swarm
# Usage: bash deploy_portainer_agent.sh
#
set -euo pipefail
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
log "Checking Docker Swarm status..."
if ! docker info --format '{{.Swarm.LocalNodeState}}' | grep -q "active"; then
log "ERROR: This node is not part of a Docker Swarm."
exit 1
fi
if ! docker info --format '{{.Swarm.ControlAvailable}}' | grep -q "true"; then
log "ERROR: This script must be run on a Swarm MANAGER node."
exit 1
fi
log "Creating overlay network for Portainer Agent..."
if ! docker network ls | grep -q "portainer_agent_network"; then
docker network create --driver overlay portainer_agent_network
else
log "Network 'portainer_agent_network' already exists, skipping."
fi
log "Deploying Portainer Agent as a global Swarm service..."
if docker service ls | grep -q "portainer_agent"; then
log "Agent service already exists. Updating to latest image..."
docker service update --image portainer/agent:latest portainer_agent
else
docker service create \
--name portainer_agent \
--network portainer_agent_network \
--publish mode=host,target=9001,published=9001 \
-e AGENT_CLUSTER_ADDR=tasks.portainer_agent \
--mode global \
--mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
--mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \
--mount type=bind,src=//,dst=/host \
portainer/agent:latest
fi
log "=== Portainer Agent Deployment Complete ==="
log "The agent is now deploying to all nodes in your Swarm."
log "To manage your Swarm:"
log "1. Open your Portainer Server UI."
log "2. Go to 'Environments' -> 'Add environment'."
log "3. Select 'Docker Swarm' and click 'Start Wizard'."
log "4. Choose 'Agent' and enter the Environment address: $(hostname -I | awk '{print $1}'):9001"
log "5. Click 'Connect'."

View File

@@ -0,0 +1,52 @@
#!/bin/bash
#
# Portainer Stack Exporter
# Connects to a Portainer instance via API and downloads all Stack compose files.
#
set -euo pipefail
# Portainer configuration (Legacy Cluster)
PORTAINER_URL="https://shipyard.snarfnet.net:9443"
USERNAME="admin"
PASSWORD="Cfxg0GqDfocWcteE"
EXPORT_DIR="./portainer_stacks_export"
if ! command -v jq &> /dev/null; then
echo "ERROR: 'jq' is required to parse the JSON responses. Please install it."
exit 1
fi
mkdir -p "$EXPORT_DIR"
echo "=== Portainer Stack Exporter ==="
echo "Authenticating with Portainer at $PORTAINER_URL..."
# 1. Authenticate and get JWT Token
JWT=$(curl -s -k -X POST "${PORTAINER_URL}/api/auth" \
-H "Content-Type: application/json" \
-d "{\"Username\":\"${USERNAME}\",\"Password\":\"${PASSWORD}\"}" | jq -r '.jwt')
if [ "$JWT" = "null" ] || [ -z "$JWT" ]; then
echo "Authentication failed! Check your credentials or URL."
exit 1
fi
echo "Authentication successful."
# 2. Fetch Stacks list
echo "Fetching stacks..."
STACKS_JSON=$(curl -s -k -H "Authorization: Bearer $JWT" "${PORTAINER_URL}/api/stacks")
# 3. Iterate through stacks, grab the file, and save it
echo "$STACKS_JSON" | jq -c '.[]' | while read -r stack; do
STACK_ID=$(echo "$stack" | jq -r '.Id')
STACK_NAME=$(echo "$stack" | jq -r '.Name')
echo " -> Exporting stack: $STACK_NAME (ID: $STACK_ID)"
FILE_CONTENT=$(curl -s -k -H "Authorization: Bearer $JWT" "${PORTAINER_URL}/api/stacks/${STACK_ID}/file" | jq -r '.StackFileContent')
echo "$FILE_CONTENT" > "${EXPORT_DIR}/${STACK_NAME}.yml"
done
echo "=== Export Complete ==="
echo "Your stack files have been saved to: ${EXPORT_DIR}/"

View File

@@ -0,0 +1,86 @@
#!/bin/bash
#
# Docker Installation Script for Raspberry Pi OS 64-bit Lite
# Usage: sudo bash install_docker_rpi.sh
#
set -euo pipefail
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
if [ "$(id -u)" -ne 0 ]; then
log "ERROR: This script must be run as root (use sudo)."
exit 1
fi
SWARM_INIT=false
SWARM_JOIN=false
SWARM_MANAGER_IP=""
SWARM_TOKEN=""
echo "=== Docker Swarm Configuration ==="
read -r -p "Is this the first node of a new Docker Swarm? (y/N): " init_choice
if [[ "$init_choice" =~ ^[Yy]$ ]]; then
SWARM_INIT=true
else
read -r -p "Do you want to join an existing Docker Swarm? (y/N): " join_choice
if [[ "$join_choice" =~ ^[Yy]$ ]]; then
SWARM_JOIN=true
read -r -p "Enter Swarm Manager IP address: " SWARM_MANAGER_IP
read -r -p "Enter Swarm Join Token: " SWARM_TOKEN
fi
fi
log "Updating system packages..."
apt-get update
apt-get upgrade -y
log "Downloading and running Docker's official convenience script..."
curl -fsSL https://get.docker.com -o /tmp/get-docker.sh
sh /tmp/get-docker.sh
log "Configuring user permissions..."
# Use the user who invoked sudo, otherwise default to 'pi'
TARGET_USER="${SUDO_USER:-pi}"
if id "$TARGET_USER" &>/dev/null; then
usermod -aG docker "$TARGET_USER"
log "Added user '$TARGET_USER' to the 'docker' group."
else
log "WARNING: User '$TARGET_USER' not found, skipping group assignment."
fi
log "Enabling Docker service to start on boot..."
systemctl enable docker
systemctl start docker
# Grab the primary local IP address to advertise for Swarm
LOCAL_IP=$(hostname -I | awk '{print $1}')
if [ "$SWARM_INIT" = true ]; then
log "Initializing Docker Swarm..."
docker swarm init --advertise-addr "$LOCAL_IP"
WORKER_TOKEN=$(docker swarm join-token worker -q)
elif [ "$SWARM_JOIN" = true ]; then
log "Joining Docker Swarm at $SWARM_MANAGER_IP..."
# Append default swarm port if the user didn't specify one
if [[ "$SWARM_MANAGER_IP" != *":"* ]]; then
SWARM_MANAGER_IP="${SWARM_MANAGER_IP}:2377"
fi
docker swarm join --advertise-addr "$LOCAL_IP" --token "$SWARM_TOKEN" "$SWARM_MANAGER_IP"
fi
log "Cleaning up..."
rm -f /tmp/get-docker.sh
log "=== Installation Complete ==="
log "Note: You must log out and log back in for the group changes to take effect,"
log " or run 'newgrp docker' to use Docker as a non-root user immediately."
if [ "$SWARM_INIT" = true ]; then
echo ""
log "=== Swarm Initialized ==="
log "To add worker nodes to this swarm, use the following as input variables:"
log "Manager IP: $LOCAL_IP"
log "Join Token: $WORKER_TOKEN"
fi

View File

@@ -0,0 +1,92 @@
#!/bin/bash
#
# Portainer Installation Script
# Usage: bash install_portainer.sh
#
set -euo pipefail
echo "=== Portainer Configuration ==="
read -r -s -p "Enter desired Portainer admin password (min 12 chars): " PORTAINER_PASSWORD
echo ""
read -r -p "Enter your Portainer EE license key: " PORTAINER_LICENSE
echo ""
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
log "Starting Portainer deployment..."
# Verify Docker is installed
if ! command -v docker &> /dev/null; then
log "ERROR: Docker is not installed or not in the PATH."
log "Please install Docker first."
exit 1
fi
# Verify Docker permissions (must be root or in the docker group)
if [ "$(id -u)" -ne 0 ] && ! groups | grep -q "\bdocker\b"; then
log "ERROR: Your user is not in the 'docker' group and you are not root."
log "Please run 'sudo usermod -aG docker \$USER', log out and back in, or run this script with sudo."
exit 1
fi
log "Creating secure admin password file..."
# Portainer requires a minimum 12-character password
if [ ${#PORTAINER_PASSWORD} -lt 12 ]; then
log "WARNING: Password is less than 12 characters. Portainer may reject it."
fi
mkdir -p /opt/portainer
echo -n "$PORTAINER_PASSWORD" > /opt/portainer/admin_password
chmod 600 /opt/portainer/admin_password
# Ensure password file is removed when the script exits
trap 'rm -f /opt/portainer/admin_password' EXIT
log "Creating Portainer data volume (if it doesn't already exist)..."
docker volume create portainer_data
log "Deploying Portainer container..."
docker run -d -p 8000:8000 -p 9443:9443 --name portainer \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
-v /opt/portainer/admin_password:/tmp/admin_password \
portainer/portainer-ee:latest \
--admin-password-file '/tmp/admin_password'
if [ -n "$PORTAINER_LICENSE" ] && [ -n "$PORTAINER_PASSWORD" ]; then
log "Waiting for Portainer to start up to apply license key via API..."
for i in {1..15}; do
if curl -ks -o /dev/null https://localhost:9443/; then
break
fi
sleep 2
done
sleep 2 # Extra padding for API initialization
log "Authenticating with Portainer API..."
JWT=$(curl -ks -X POST https://localhost:9443/api/auth \
-H "Content-Type: application/json" \
-d "{\"Username\":\"admin\",\"Password\":\"$PORTAINER_PASSWORD\"}" | grep -o '"jwt":"[^"]*"' | cut -d'"' -f4)
if [ -n "$JWT" ]; then
log "Applying license key..."
LICENSE_STATUS=$(curl -ks -w "%{http_code}" -o /dev/null -X POST https://localhost:9443/api/licenses \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d "{\"licenseKey\":\"$PORTAINER_LICENSE\"}")
if [ "$LICENSE_STATUS" = "200" ]; then
log "License key applied successfully!"
else
log "WARNING: Failed to apply license key (HTTP $LICENSE_STATUS). You may need to enter it manually."
fi
else
log "WARNING: Failed to authenticate with Portainer API. Please apply the license manually."
fi
fi
log "=== Portainer Installation Complete ==="
log "You can now access the Portainer web interface at:"
log "https://<YOUR_PI_IP_ADDRESS>:9443"

View File

@@ -0,0 +1,103 @@
#!/bin/bash
#
# Interactive Static IP Configuration Script using nmcli
# Usage: sudo bash set_static_ip_nmcli.sh
#
set -euo pipefail
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
if [ "$(id -u)" -ne 0 ]; then
log "ERROR: This script must be run as root (use sudo)."
exit 1
fi
if ! command -v nmcli &> /dev/null; then
log "ERROR: nmcli is not installed. This system may not be using NetworkManager."
exit 1
fi
echo "========================================="
echo " NetworkManager Static IP Setup"
echo "========================================="
echo ""
log "Available Network Connections:"
mapfile -t CONNECTIONS < <(nmcli -t -f NAME connection show)
if [ ${#CONNECTIONS[@]} -eq 0 ]; then
log "ERROR: No network connections found."
exit 1
fi
for i in "${!CONNECTIONS[@]}"; do
echo " $((i+1)). ${CONNECTIONS[$i]}"
done
echo ""
read -p "Enter the number of the connection to configure: " CONN_CHOICE
if ! [[ "$CONN_CHOICE" =~ ^[0-9]+$ ]] || [ "$CONN_CHOICE" -lt 1 ] || [ "$CONN_CHOICE" -gt "${#CONNECTIONS[@]}" ]; then
log "ERROR: Invalid selection. Please enter a valid number from the list."
exit 1
fi
CONN_NAME="${CONNECTIONS[$((CONN_CHOICE-1))]}"
echo ""
log "Current settings for '$CONN_NAME':"
CUR_METHOD=$(nmcli -g ipv4.method connection show "$CONN_NAME")
CUR_IP=$(nmcli -g IP4.ADDRESS connection show "$CONN_NAME")
if [ -z "$CUR_IP" ]; then
CUR_IP=$(nmcli -g ipv4.addresses connection show "$CONN_NAME")
fi
CUR_GW=$(nmcli -g ipv4.gateway connection show "$CONN_NAME")
CUR_DNS=$(nmcli -g ipv4.dns connection show "$CONN_NAME")
echo " - Method: ${CUR_METHOD:-<Not Set>}"
echo " - IP Addr (CIDR): ${CUR_IP:-<Not Set>}"
echo " - Gateway: ${CUR_GW:-<Not Set>}"
echo " - DNS: ${CUR_DNS:-<Not Set>}"
echo ""
IPV4_CIDR_REGEX="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/([0-9]|[1-2][0-9]|3[0-2])$"
IPV4_REGEX="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
DNS_REGEX="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(,[ ]?((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))*$"
read -p "Enter static IP address with CIDR (e.g., 192.168.1.100/24): " IP_ADDR
if [ -z "$IP_ADDR" ]; then
log "ERROR: IP address cannot be empty."
exit 1
fi
if ! [[ "$IP_ADDR" =~ $IPV4_CIDR_REGEX ]]; then
log "ERROR: Invalid IP address format. Must be a valid IPv4 CIDR notation (e.g., 192.168.1.100/24)."
exit 1
fi
read -p "Enter Gateway IP address (e.g., 192.168.1.1): " GATEWAY
if [ -z "$GATEWAY" ]; then
log "ERROR: Gateway cannot be empty."
exit 1
fi
if ! [[ "$GATEWAY" =~ $IPV4_REGEX ]]; then
log "ERROR: Invalid Gateway IP format (e.g., 192.168.1.1)."
exit 1
fi
read -p "Enter DNS server(s) separated by commas (e.g., 8.8.8.8,1.1.1.1): " DNS_SERVERS
if [ -z "$DNS_SERVERS" ]; then
log "ERROR: DNS servers cannot be empty."
exit 1
fi
if ! [[ "$DNS_SERVERS" =~ $DNS_REGEX ]]; then
log "ERROR: Invalid DNS server format. Must be comma-separated IPv4 addresses (e.g., 8.8.8.8,1.1.1.1)."
exit 1
fi
log "Applying static IP configuration to '$CONN_NAME'..."
nmcli connection modify "$CONN_NAME" ipv4.method manual ipv4.addresses "$IP_ADDR" ipv4.gateway "$GATEWAY" ipv4.dns "$DNS_SERVERS"
log "Restarting connection to apply changes (you may lose SSH briefly if connected remotely)..."
nmcli connection up "$CONN_NAME"
log "=== Configuration Complete ==="
log "Verify your new IP address using 'ip addr show' or 'nmcli device show'."

113
scripts/setup_keepalived.sh Normal file
View File

@@ -0,0 +1,113 @@
#!/bin/bash
#
# Keepalived Setup Script for 5-Node Docker Cluster
# VIP: 140.44.4.70
# Nodes: 140.44.4.71 - 140.44.4.75
#
# Usage: sudo bash setup_keepalived.sh
#
set -euo pipefail
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
if [ "$(id -u)" -ne 0 ]; then
log "ERROR: This script must be run as root (use sudo)."
exit 1
fi
VIP="140.44.4.70"
VIP_CIDR="24"
ROUTER_ID="70"
AUTH_PASS="DockerHA123!"
log "Detecting primary network interface and IP..."
# Find interface with default route to the gateway (140.44.4.1)
IFACE=$(ip route | awk '/default/ {print $5}' | head -n1)
if [ -z "$IFACE" ]; then
log "ERROR: Could not detect default network interface."
exit 1
fi
# Find IP of that interface
MY_IP=$(ip -4 addr show dev "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1)
if [ -z "$MY_IP" ]; then
log "ERROR: Could not detect IP address for interface $IFACE."
exit 1
fi
log "Detected IP: $MY_IP on interface: $IFACE"
# Validate IP and determine priority
case "$MY_IP" in
140.44.4.71) PRIORITY=150; STATE="MASTER" ;;
140.44.4.72) PRIORITY=140; STATE="BACKUP" ;;
140.44.4.73) PRIORITY=130; STATE="BACKUP" ;;
140.44.4.74) PRIORITY=120; STATE="BACKUP" ;;
140.44.4.75) PRIORITY=110; STATE="BACKUP" ;;
*)
log "ERROR: This IP ($MY_IP) is not part of the expected cluster (140.44.4.71-75)."
exit 1
;;
esac
log "Installing keepalived..."
if command -v apt-get &> /dev/null; then
apt-get update -y && apt-get install -y keepalived
elif command -v dnf &> /dev/null; then
dnf install -y keepalived
elif command -v yum &> /dev/null; then
yum install -y keepalived
else
log "ERROR: Unsupported package manager. Please install keepalived manually."
exit 1
fi
log "Configuring keepalived (State: $STATE, Priority: $PRIORITY)..."
# Create a health check script for Docker
mkdir -p /etc/keepalived/scripts
cat << 'EOF' > /etc/keepalived/scripts/check_docker.sh
#!/bin/bash
# Returns 0 if docker is active, 1 if it is stopped/crashed
systemctl is-active --quiet docker
EOF
chmod +x /etc/keepalived/scripts/check_docker.sh
# Backup existing config if any
[ -f /etc/keepalived/keepalived.conf ] && mv /etc/keepalived/keepalived.conf "/etc/keepalived/keepalived.conf.bak.$(date +%s)"
# Create new config
cat << EOF > /etc/keepalived/keepalived.conf
vrrp_script chk_docker {
script "/etc/keepalived/scripts/check_docker.sh"
interval 2
weight -20
}
vrrp_instance VI_1 {
state $STATE
interface $IFACE
virtual_router_id $ROUTER_ID
priority $PRIORITY
advert_int 1
authentication {
auth_type PASS
auth_pass $AUTH_PASS
}
virtual_ipaddress {
$VIP/$VIP_CIDR dev $IFACE
}
track_script {
chk_docker
}
}
EOF
log "Restarting and enabling keepalived service..."
systemctl enable keepalived
systemctl restart keepalived
log "=== Keepalived setup complete on $MY_IP ==="
log "Check status with: systemctl status keepalived"
log "Virtual IP $VIP will be active on the node with the highest priority."

View File

@@ -0,0 +1,92 @@
#!/bin/bash
#
# GlusterFS and NFS Cluster Setup Script
# Configures a 3-node replica GlusterFS volume and mounts it + NFS across all 5 Swarm nodes.
#
set -euo pipefail
# Cluster Configuration
ALL_NODES=("140.44.4.71" "140.44.4.72" "140.44.4.73" "140.44.4.74" "140.44.4.75")
STORAGE_NODES=("140.44.4.71" "140.44.4.72" "140.44.4.73") # Nodes holding the actual data bricks
USER="snarf"
PASS="Katzir476!"
# NFS Configuration
NFS_SERVER="14.10.10.71"
NFS_SHARE="/volume1/docker" # Based on your nginxproxy.yml context
if ! command -v sshpass &> /dev/null; then
echo "ERROR: 'sshpass' is required. Please install it first."
exit 1
fi
# Helper function to run sudo commands remotely via sshpass
run_remote() {
local node=$1
local cmd=$2
echo " [${node}] Running command..."
sshpass -p "$PASS" ssh -o StrictHostKeyChecking=no "$USER@$node" "echo '$PASS' | sudo -S bash -c '$cmd'"
}
echo "=== Phase 1: Installing Prerequisites & Creating Directories ==="
for NODE in "${ALL_NODES[@]}"; do
echo "-> Configuring Node $NODE"
run_remote "$NODE" "
apt-get update -y && \
apt-get install -y glusterfs-server glusterfs-client nfs-common && \
systemctl enable --now glusterd && \
mkdir -p /data/glusterfs/swarm/brick && \
mkdir -p /mnt/swarm_shared && \
mkdir -p /mnt/nfs_shares
"
done
echo ""
echo "=== Phase 2: Peering GlusterFS Storage Nodes ==="
PRIMARY_NODE="${STORAGE_NODES[0]}"
# Peer node 2 and 3 from node 1
for i in {1..2}; do
PEER_NODE="${STORAGE_NODES[$i]}"
echo "-> Peering $PRIMARY_NODE with $PEER_NODE"
run_remote "$PRIMARY_NODE" "gluster peer probe $PEER_NODE"
done
# Give peering a few seconds to stabilize
sleep 5
echo ""
echo "=== Phase 3: Creating and Starting GlusterFS Volume ==="
echo "-> Creating 'replica 3' volume 'swarm_vols'..."
run_remote "$PRIMARY_NODE" "
gluster volume create swarm_vols replica 3 \
${STORAGE_NODES[0]}:/data/glusterfs/swarm/brick \
${STORAGE_NODES[1]}:/data/glusterfs/swarm/brick \
${STORAGE_NODES[2]}:/data/glusterfs/swarm/brick \
force || echo 'Volume might already exist.'
"
echo "-> Starting volume 'swarm_vols'..."
run_remote "$PRIMARY_NODE" "gluster volume start swarm_vols || echo 'Volume already started.'"
echo ""
echo "=== Phase 4: Mounting GlusterFS and NFS on ALL Nodes ==="
for NODE in "${ALL_NODES[@]}"; do
echo "-> Mounting file systems on $NODE"
run_remote "$NODE" "
# Remove old fstab entries to prevent duplicates if script is re-run
sed -i '/swarm_vols/d' /etc/fstab
sed -i '/nfs_shares/d' /etc/fstab
# Add to fstab
echo 'localhost:/swarm_vols /mnt/swarm_shared glusterfs defaults,_netdev 0 0' >> /etc/fstab
echo '$NFS_SERVER:$NFS_SHARE /mnt/nfs_shares nfs defaults,nfsvers=4,_netdev 0 0' >> /etc/fstab
# Mount them
mount -a
"
done
echo "=== Storage Cluster Setup Complete ==="

View File

@@ -0,0 +1,67 @@
#!/bin/bash
#
# Legacy Docker Volumes Sync Script
# Migrates persistent data from the legacy Swarm cluster to the new GlusterFS storage.
#
set -euo pipefail
# Legacy Cluster Details
LEGACY_HOST="140.44.4.31"
LEGACY_USER="dietpi"
LEGACY_PASS="Katzir476!"
# New Cluster Details (Using node .71 as the entry point for GlusterFS)
NEW_HOST="140.44.4.71"
NEW_USER="snarf"
NEW_PASS="Katzir476!"
if ! command -v sshpass &> /dev/null; then
echo "ERROR: 'sshpass' is required. Please install it locally first."
exit 1
fi
echo "=== Preparing Destination Directory and Askpass ==="
sshpass -p "$NEW_PASS" ssh -o StrictHostKeyChecking=no "$NEW_USER@$NEW_HOST" "
echo '$NEW_PASS' | sudo -S mkdir -p /mnt/swarm_shared/legacy_volumes
echo '#!/bin/bash' > /tmp/askpass.sh
echo 'echo \"$NEW_PASS\"' >> /tmp/askpass.sh
chmod +x /tmp/askpass.sh
"
echo "=== Initiating Remote-to-Remote Rsync ==="
echo "Connecting to Legacy Cluster to start the transfer..."
# We execute a block of commands on the legacy server to perform the sync.
# By using --rsync-path="... sudo rsync", we ensure file UIDs and GIDs are perfectly preserved.
sshpass -p "$LEGACY_PASS" ssh -o StrictHostKeyChecking=no "$LEGACY_USER@$LEGACY_HOST" "
cat << 'EOF' > /tmp/run_rsync.sh
#!/bin/bash
N_PASS=\"\$1\"
N_USER=\"\$2\"
N_HOST=\"\$3\"
echo '-> Installing rsync and sshpass on the Legacy Cluster...'
apt-get update -y > /dev/null
apt-get install -y rsync sshpass openssh-client > /dev/null
echo '-> Configuring Docker data directory on Legacy Cluster...'
VOLUMES_DIR=\"/mnt/docker/\"
echo \"-> Source Volumes directory manually set to: \${VOLUMES_DIR}\"
echo '-> Starting Rsync Transfer (This may take a while depending on data size)...'
sshpass -p \"\$N_PASS\" rsync -avzh --progress \\
--rsync-path=\"SUDO_ASKPASS=/tmp/askpass.sh sudo -A rsync\" \\
-e \"ssh -o StrictHostKeyChecking=no\" \\
\"\${VOLUMES_DIR}\" \"\${N_USER}@\${N_HOST}:/mnt/swarm_shared/legacy_volumes/\"
EOF
chmod +x /tmp/run_rsync.sh
echo \"$LEGACY_PASS\" | sudo -S /tmp/run_rsync.sh \"$NEW_PASS\" \"$NEW_USER\" \"$NEW_HOST\"
rm /tmp/run_rsync.sh
"
echo "=== Cleaning up ==="
sshpass -p "$NEW_PASS" ssh -o StrictHostKeyChecking=no "$NEW_USER@$NEW_HOST" "rm -f /tmp/askpass.sh"
echo "=== Volume Sync Complete! ==="

View File

@@ -0,0 +1,38 @@
#!/bin/bash
#
# Docker Uninstallation Script for Raspberry Pi OS
# Usage: sudo bash uninstall_docker_rpi.sh
#
set -euo pipefail
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
if [ "$(id -u)" -ne 0 ]; then
log "ERROR: This script must be run as root (use sudo)."
exit 1
fi
log "Stopping Docker and Containerd services..."
systemctl stop docker.socket || true
systemctl stop docker || true
systemctl stop containerd || true
log "Uninstalling Docker packages..."
# Purge official Docker packages as well as any distribution-provided ones
apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras docker.io docker-doc docker-compose podman-docker runc || true
log "Cleaning up unused dependencies..."
apt-get autoremove -y --purge
log "Removing Docker data, images, volumes, and configurations..."
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
rm -rf /etc/docker
rm -rf /var/run/docker.sock
log "Removing the 'docker' user group..."
getent group docker > /dev/null && groupdel docker || true
log "=== Docker Uninstallation Complete ==="
log "Your system is clean and ready to test the installation script."