Setup scripts added
This commit is contained in:
53
scripts/deploy_portainer_agent.sh
Normal file
53
scripts/deploy_portainer_agent.sh
Normal 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'."
|
||||
52
scripts/export_portainer_stacks.sh
Normal file
52
scripts/export_portainer_stacks.sh
Normal 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}/"
|
||||
86
scripts/install_docker_rpi.sh
Normal file
86
scripts/install_docker_rpi.sh
Normal 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
|
||||
92
scripts/install_portainer.sh
Normal file
92
scripts/install_portainer.sh
Normal 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"
|
||||
103
scripts/set_static_ip_nmcli.sh
Normal file
103
scripts/set_static_ip_nmcli.sh
Normal 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
113
scripts/setup_keepalived.sh
Normal 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."
|
||||
92
scripts/setup_storage_cluster.sh
Normal file
92
scripts/setup_storage_cluster.sh
Normal 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 ==="
|
||||
67
scripts/sync_legacy_volumes.sh
Normal file
67
scripts/sync_legacy_volumes.sh
Normal 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! ==="
|
||||
38
scripts/uninstall_docker_rpi.sh
Normal file
38
scripts/uninstall_docker_rpi.sh
Normal 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."
|
||||
Reference in New Issue
Block a user