SingleStore Script

March 14, 2022 (2y ago)

The following script is designed to set up a SingleStore database on a dedicated server. It will prompt you for the

  • App domain
  • Git repository
  • PHP version
  • Email
  • Node.js version
  • and whether you want to lock down the machine to a specific IP.

It will then update the system, install PHP, Composer, Node.js, Yarn, Git, Nginx, UFW, and Redis.

It will also configure Nginx, set up a cron job, install Let's Encrypt, configure Redis, install Supervisor, set up a SingleStore cluster, and configure the firewall. Finally, it will install SingleStore Studio and set up recommended vm settings.

singlestore.sh
#!/bin/bash
 
# Prompt user for app domain
read -p "Enter the app domain (default: staging.analyse.net): " APP_DOMAIN
APP_DOMAIN="${APP_DOMAIN:-staging.analyse.net}"
 
# Prompt user for Git repository
read -p "Enter the Git repository (default: track/analytics.tebex.io): " GIT_REPO
GIT_REPO="${GIT_REPO:-track/analytics.tebex.io}"
 
# Prompt user for PHP version
read -p "Enter the PHP version (default: 8.2): " PHP_VERSION
PHP_VERSION="${PHP_VERSION:-8.2}"
 
# Prompt user for email
read -p "Enter your email: " EMAIL
 
# Prompt user for Node.js version
read -p "Enter the Node.js version (default: 18): " NODE_MAJOR
NODE_MAJOR="${NODE_MAJOR:-18}"
 
# Prompt user if they want to lock down the machine to a specific IP
read -p "Do you want to lock down the machine to a specific IP? (y/n): " LOCK_IP
if [ "$LOCK_IP" == "y" ]; then
    read -p "Enter the IP address: " VPN_IP
fi
 
PUBLIC_IP=$(curl api.ipify.org)
 
# Update system
sudo apt upgrade -y
sudo apt update -y
export COMPOSER_ALLOW_SUPERUSER=1
 
# PHP installation
sudo apt install lsb-release apt-transport-https ca-certificates software-properties-common -y
sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
sudo sh -c "echo 'deb https://packages.sury.org/php/ $(lsb_release -sc) main' > /etc/apt/sources.list.d/php.list"
sudo apt update
sudo apt install php$PHP_VERSION php$PHP_VERSION-{cli,zip,mysql,fpm,bz2,curl,mbstring,intl,redis,dom,common} -y
systemctl enable php$PHP_VERSION-fpm
systemctl start php$PHP_VERSION-fpm
 
export PHP_INI_PATH="/etc/php/$PHP_VERSION/fpm/php.ini"
sed -i 's/max_execution_time = 30/max_execution_time = 180/' $PHP_INI_PATH
sed -i 's/memory_limit = 128M/memory_limit = 512M/' $PHP_INI_PATH
 
sudo systemctl restart php$PHP_VERSION-fpm
 
# Composer installation
cd ~
curl -sS https://getcomposer.org/installer -o composer-setup.php
sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
rm -rf composer-setup.php
 
# Node.js and Yarn installation
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt update
sudo apt install nodejs -y
npm install --global yarn
 
# Git installation and configuration
sudo apt install git -y
ssh-keygen -t ed25519 -C "$EMAIL"
eval "$(ssh-agent -s)"
git config --global pull.rebase false
 
echo "Add the following key to your GitHub (https://github.com/settings/ssh/new)..."
cat ~/.ssh/id_ed25519.pub
echo ""
echo "Then press any key to continue."
 
# Wait for a key press to continue
read -s -n 1
 
# Project setup
git clone git@github.com:$GIT_REPO.git /var/www/$APP_DOMAIN
cd /var/www/$APP_DOMAIN
composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev
yarn install --prod
cp -rf .env.example .env
yarn build
setfacl -R -m g:www-data:rwx /var/www/
 
# Add Cronjob
(crontab -l; echo "* * * * * cd /var/www/$APP_DOMAIN && php artisan schedule:run >> /dev/null 2>&1") | awk '!x[$0]++' | crontab -
 
# Nginx configuration
sudo apt install ufw nginx -y
 
# Path to the update_proxies.sh script
UPDATE_SCRIPT="~/scripts/update_proxies.sh"
 
# Create the update_proxies.sh script
cat << 'EOF' > $UPDATE_SCRIPT
#!/bin/bash
 
# Define the URLs for the Cloudflare IP lists
IPV4_URL="https://www.cloudflare.com/ips-v4/"
IPV6_URL="https://www.cloudflare.com/ips-v6/"
 
# Fetch the IP lists
IPV4_LIST=$(curl -s $IPV4_URL)
IPV6_LIST=$(curl -s $IPV6_URL)
 
# Prepare the Nginx configuration content
CF_PROXIES="# Cloudflare IPs\n"
 
# Add the IPv4 addresses
for IP in $IPV4_LIST; do
    CF_PROXIES+="set_real_ip_from $IP;\n"
done
 
# Add the IPv6 addresses
for IP in $IPV6_LIST; do
    CF_PROXIES+="set_real_ip_from $IP;\n"
done
 
# Add the real_ip_header directive
CF_PROXIES+="real_ip_header CF-Connecting-IP;"
 
# Write the configuration to the file
echo -e "$CF_PROXIES" > "/etc/nginx/snippets/proxies.conf"
 
# Reload Nginx to apply the new configuration
nginx -t && nginx -s reload
EOF
 
# Make the update_proxies.sh script executable
chmod +x $UPDATE_SCRIPT
 
# Add the cron job to run the update_proxies.sh script weekly
(crontab -l 2>/dev/null; echo "0 0 * * 0 $UPDATE_SCRIPT") | crontab -
 
CONFIG="server {
    server_name $APP_DOMAIN;
    root /var/www/$APP_DOMAIN/public;
 
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
 
    real_ip_header X-Forwarded-For;
 
    include /etc/nginx/snippets/proxies.conf;
 
    ssl_client_certificate /etc/ssl/certs/cloudflare.crt;
    ssl_verify_client on;
 
    index index.php;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    proxy_buffer_size   128k;
    proxy_buffers   4 256k;
    proxy_busy_buffers_size   256k;
 
    charset utf-8;
    client_max_body_size 4M;
 
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
 
    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
 
    error_page 404 /index.php;
 
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/var/run/php/php$PHP_VERSION-fpm.sock;
    }
}"
 
echo "$CONFIG" > "/etc/nginx/sites-available/$APP_DOMAIN.conf"
sudo ln -s /etc/nginx/sites-available/$APP_DOMAIN.conf /etc/nginx/sites-enabled
sudo curl -L https://developers.cloudflare.com/ssl/static/authenticated_origin_pull_ca.pem -o /etc/ssl/certs/cloudflare.crt
nginx -s reload
 
ufw allow 'Nginx Full'
 
# Firewall configuration
if [ "$LOCK_IP" == "y" ]; then
    ufw allow from $VPN_IP to any port 8080
    ufw allow from $VPN_IP to any port 8081
    ufw allow from $VPN_IP to any port 3306
    ufw allow from $VPN_IP to any port 22
else
    ufw allow 8080/tcp
    ufw allow 8081/tcp
    ufw allow 3306/tcp
    ufw allow 22/tcp
fi
 
# Enable the firewall
ufw enable
 
# LetsEncrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx --non-interactive --agree-tos -m $EMAIL -d $APP_DOMAIN
 
# Redis installation and configuration
sudo apt install redis-server -y
sudo systemctl enable redis-server.service
sudo systemctl start redis-server.service
 
# Supervisor installation and configuration
sudo apt install supervisor
sudo systemctl enable supervisor.service
sudo systemctl start supervisor.service
 
# Define the content of the file
CONTENT="[program:horizon]
process_name=%(program_name)s
command=php /var/www/$APP_DOMAIN/artisan horizon
autostart=true
autorestart=true
user=root
redirect_stderr=true
stdout_logfile=/var/www/$APP_DOMAIN/horizon.log
stopwaitsecs=3600"
 
echo "$CONTENT" > "/etc/supervisor/conf.d/horizon.conf"
 
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start "horizon:*"
 
# SingleStore installation
wget -O - 'https://release.memsql.com/release-aug2018.gpg' 2>/dev/null | sudo apt-key add - && apt-key list
apt-cache policy apt-transport-https
echo "deb [arch=amd64] https://release.memsql.com/production/debian memsql main" | sudo tee /etc/apt/sources.list.d/memsql.list
sudo apt update && sudo apt -y install singlestoredb-toolbox singlestoredb-studio
sudo systemctl start singlestoredb-studio
sudo systemctl enable singlestoredb-studio.service
 
# Function to calculate min_free_kbytes based on system RAM
calculate_min_free_kbytes() {
    total_ram_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}')
    min_free_kb=$((total_ram_kb / 100))  # 1% of total RAM
    if (( min_free_kb > 4000000 )); then
        min_free_kb=4000000  # Cap at 4 GB
    fi
    echo $min_free_kb
}
 
# Set recommended vm settings (According to SingleStore)
echo "Setting recommended vm settings..."
sudo sysctl -w vm.max_map_count=1000000000
sudo sysctl -w vm.overcommit_memory=0
sudo sysctl -w vm.swappiness=10
sudo sysctl -w vm.overcommit_ratio=99  # Only if vm.overcommit_memory is set to 2
sudo sysctl -w net.core.rmem_max=8388606
sudo sysctl -w net.core.wmem_max=8388606
 
# Calculate min_free_kbytes
min_free_kbytes=$(calculate_min_free_kbytes)
 
# Set min_free_kbytes
sudo sysctl -w vm.min_free_kbytes=$min_free_kbytes
 
# Prompt user for SingleStore key
read -p "Enter your SingleStore license key (from portal.singlestore.com): " SINGLESTORE_KEY
 
CLUSTER_YAML="license: $SINGLESTORE_KEY
high_availability: false
memsql_server_version: 8.5.15
hosts:
  - hostname: $PUBLIC_IP
    nodes:
      - role: Master
        config:
          port: 3306
      - role: Leaf
        config:
          port: 3307
    localhost: true"
 
echo "$CLUSTER_YAML" > "cluster.yml"
 
sdb-deploy setup-cluster --cluster-file cluster.yml
rm -rf install-dedi.sh cluster.yml