Skip to content

Instantly share code, notes, and snippets.

@kamaroly
Created November 22, 2024 21:01
Show Gist options
  • Save kamaroly/e0dd5f9fe3dd9776df814288b7faf418 to your computer and use it in GitHub Desktop.
Save kamaroly/e0dd5f9fe3dd9776df814288b7faf418 to your computer and use it in GitHub Desktop.
I use these batch script to achieve Zero-deployment for phoenix application with releases without docker.
#!/bin/bash
# Exit immediately if a command exits with a non-zero status
set -e
SCRIPT_PATH=$(dirname "$(realpath "$0")")
BUILD_PATH=$SCRIPT_PATH"/.."
# Change directory to build path
cd $BUILD_PATH
# Function to display an error and exit
function error_exit {
echo "Error: $1"
exit 1
}
# Step 1: Fetch production dependencies
echo "1. Fetching production dependencies..."
mix deps.get --only prod || error_exit "Failed to fetch dependencies."
# Step 2: Compile the application in production environment
echo "2. Compiling the application for production..."
MIX_ENV=prod mix compile || error_exit "Compilation failed."
# Step 3: Deploy assets
echo "3. Deploying assets..."
# setopt rm_star_silent # Disable prompt for delete confirmation
# Remove previously compiled assets
rm -rf priv/static/*
MIX_ENV=prod mix assets.deploy || error_exit "Asset deployment failed."
# Step 4: Generate the release
echo "Generating the Phoenix release..."
mix phx.gen.release || error_exit "Failed to generate the Phoenix release."
# Step 5: Build the release
echo "Building the release..."
yes | MIX_ENV=prod mix release || error_exit "Release build failed."
echo "Release build completed successfully!"
your-domain.com {
reverse_proxy localhost:4010 localhost:4020 localhost:4030 localhost:4040 localhost:4050 localhost:4060
}
#!/bin/bash
# Initialize variables
PORTS=("4010" "4020" "4030" "4040" "4050" "4060") # Default ports for zero-downtime deployment
USERNAME="postgres"
PASSWORD="ikijumba"
HOST="localhost"
DATABASE="hr_dev"
APP_NAME="hr"
MODE="daemon_iex"
# It's important for this to be static. It will be needed to restart apps/ releases.
SECRET_KEY="J1k3ARebfz5+zTGI5Uc4Z4gy6F6XxCJXN3V10dNiOIP1joQcExncj9JsQeQcsaYg"
SCRIPT_PATH=$(dirname "$(realpath "$0")")
BUILD_PATH="/home/kamaro/elixir/hr/_build/prod/rel/hr"
# Change directory to build path
cd $BUILD_PATH
# Change to the directory with the app for release
cd $BUILD_PATH
# Parse named parameters
while [[ $# -gt 0 ]]; do
case $1 in
--ports)
IFS=',' read -ra PORTS <<< "$2" # Split comma-separated ports into an array
shift 2
;;
--username)
USERNAME=$2
shift 2
;;
--password)
PASSWORD=$2
shift 2
;;
--host)
HOST=$2
shift 2
;;
--database)
DATABASE=$2
shift 2
;;
--app-name)
APP_NAME=$2
shift 2
;;
--build-path)
BUILD_PATH=$2
shift 2
;;
--secret-key)
SECRET_KEY=$2
shift 2
;;
--mode)
MODE=$2
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Check if all required parameters are provided
if [ -z "$PORTS" ] || [ -z "$USERNAME" ] || [ -z "$PASSWORD" ] || [ -z "$HOST" ] || [ -z "$DATABASE" ] || [ -z "$APP_NAME" ] || [ -z "$BUILD_PATH" ] || [ -z "$SECRET_KEY" ]; then
echo "Error: Missing required parameters."
echo "Usage: $0 --ports <port1,port2,...> --username <username> --password <password> --host <host> --database <database> --app-name <app_name> --build-path <build_path> --secret-key <secret_key>"
exit 1
fi
# Export required environment variables
export SECRET_KEY_BASE=$SECRET_KEY
export DATABASE_URL=ecto://$USERNAME:$PASSWORD@$HOST/$DATABASE
# Function to check if a port is responding
function is_port_responding() {
local PORT=$1
for i in {1..30}; do
if nc -z localhost $PORT; then
echo "Port $PORT is responding!"
return 0
fi
echo "Waiting for port $PORT to respond... (attempt $i)"
sleep 1
done
echo "Port $PORT did not respond in time."
return 1
}
# To Achieve Zero-downtime deployment, we need to have multiple instances of the same application on different port, then
# restart them in sequences. Since caddy is handling reverse proxy/ load balancing, it will automatically respond to the
# available port. See card configurations.
for PORT in "${PORTS[@]}"; do
echo "Stopping existing app_$PORT on port $PORT (if running)..."
RELEASE_NODE=app_$PORT PORT=$PORT $BUILD_PATH/bin/$APP_NAME stop || echo "No running instance on port $PORT"
echo "Restarting app_$PORT on port $PORT..."
RELEASE_NODE=app_$PORT PORT=$PORT $BUILD_PATH/bin/$APP_NAME daemon
echo "Checking if instance on port $PORT is responding..."
if ! is_port_responding $PORT; then
echo "Failed to start app_$PORT on port $PORT Aborting."
exit 1
fi
done
echo "Zero-downtime deployment complete. Running on ports: ${PORTS[*]}"
#!/bin/bash
SCRIPT_PATH=$(dirname "$(realpath "$0")")
BUILD_PATH=$SCRIPT_PATH"/.."
PRODUCTION_SERVER_HOST="SERVER_IP_ADDRESS"
PRODUCTION_USER="root"
APP_ENV="prod"
APP_NAME="hr"
echo "1. Zipping release into $APP_NAME.tar.gz..."
tar -czf $APP_NAME.tar.gz -C $BUILD_PATH/_build/${APP_ENV}/rel/$APP_NAME .
echo "2. Moving $APP_NAME.tar.gz to production server..."
scp $APP_NAME.tar.gz $PRODUCTION_USER@$PRODUCTION_SERVER_HOST:~/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment