mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:20:47 +00:00
Dynamic tunnels (#579)
## Description: Update deployment: 1. automatically create and configure CF tunnels and point it to subdomain.domain 2. Send loki logs to remote endpoint 3. create metric-exporter.sh to push prom metrics to remote endpoint 4. update and refactor deployment & env variables ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: <DISCORD USERNAME> --------- Co-authored-by: Evan Pellegrini <evan@Evans-Air.attlocal.net> Co-authored-by: evan <openfrontio@gmail.com>
This commit is contained in:
+1
-1
@@ -5,5 +5,5 @@ static/
|
|||||||
TODO.txt
|
TODO.txt
|
||||||
resources/images/.DS_Store
|
resources/images/.DS_Store
|
||||||
resources/.DS_Store
|
resources/.DS_Store
|
||||||
.env
|
.env*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
+2
-5
@@ -1,11 +1,8 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
# Run tests first
|
# Add PATH setup to ensure npx is found
|
||||||
npm run test || {
|
export PATH="/usr/local/bin:$HOME/.npm-global/bin:$HOME/.nvm/versions/node/$(node -v)/bin:$PATH"
|
||||||
echo "Tests failed - commit aborted"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Then run lint-staged if tests pass
|
# Then run lint-staged if tests pass
|
||||||
npx lint-staged
|
npx lint-staged
|
||||||
+25
-8
@@ -1,12 +1,28 @@
|
|||||||
# Use an official Node runtime as the base image
|
# Use an official Node runtime as the base image
|
||||||
FROM node:18
|
FROM node:18
|
||||||
|
|
||||||
ARG GIT_COMMIT=unknown
|
ARG GIT_COMMIT=unknown
|
||||||
ENV GIT_COMMIT=$GIT_COMMIT
|
ENV GIT_COMMIT=$GIT_COMMIT
|
||||||
|
|
||||||
# Install Nginx, Supervisor and Git (for Husky)
|
# Install Nginx, Supervisor, Git, jq, curl, and Node Exporter dependencies
|
||||||
RUN apt-get update && apt-get install -y nginx supervisor git && \
|
RUN apt-get update && apt-get install -y \
|
||||||
rm -rf /var/lib/apt/lists/*
|
nginx \
|
||||||
|
supervisor \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
jq \
|
||||||
|
wget \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Install Node Exporter
|
||||||
|
RUN mkdir -p /opt/node_exporter && \
|
||||||
|
wget -qO- https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz | \
|
||||||
|
tar xvz --strip-components=1 -C /opt/node_exporter && \
|
||||||
|
ln -s /opt/node_exporter/node_exporter /usr/local/bin/
|
||||||
|
|
||||||
|
# Install cloudflared
|
||||||
|
RUN curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb > cloudflared.deb \
|
||||||
|
&& dpkg -i cloudflared.deb \
|
||||||
|
&& rm cloudflared.deb
|
||||||
|
|
||||||
# Set the working directory in the container
|
# Set the working directory in the container
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
@@ -33,8 +49,9 @@ RUN rm -f /etc/nginx/sites-enabled/default
|
|||||||
RUN mkdir -p /var/log/supervisor
|
RUN mkdir -p /var/log/supervisor
|
||||||
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
|
||||||
# Expose only the Nginx port
|
# Copy and make executable the startup script
|
||||||
EXPOSE 80 443
|
COPY startup.sh /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/startup.sh
|
||||||
|
|
||||||
# Start Supervisor to manage both Node.js and Nginx
|
# Use the startup script as the entrypoint
|
||||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
ENTRYPOINT ["/usr/local/bin/startup.sh"]
|
||||||
@@ -7,6 +7,27 @@
|
|||||||
|
|
||||||
set -e # Exit immediately if a command exits with a non-zero status
|
set -e # Exit immediately if a command exits with a non-zero status
|
||||||
|
|
||||||
|
# Check command line arguments
|
||||||
|
if [ $# -lt 2 ] || [ $# -gt 3 ]; then
|
||||||
|
echo "Error: Please specify environment and host, with optional subdomain"
|
||||||
|
echo "Usage: $0 [prod|staging] [eu|us|staging] [subdomain]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate first argument (environment)
|
||||||
|
if [ "$1" != "prod" ] && [ "$1" != "staging" ]; then
|
||||||
|
echo "Error: First argument must be either 'prod' or 'staging'"
|
||||||
|
echo "Usage: $0 [prod|staging] [eu|us|staging] [subdomain]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate second argument (host)
|
||||||
|
if [ "$2" != "eu" ] && [ "$2" != "us" ] && [ "$2" != "staging" ]; then
|
||||||
|
echo "Error: Second argument must be either 'eu', 'us', or 'staging'"
|
||||||
|
echo "Usage: $0 [prod|staging] [eu|us|staging] [subdomain]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Function to print section headers
|
# Function to print section headers
|
||||||
print_header() {
|
print_header() {
|
||||||
echo "======================================================"
|
echo "======================================================"
|
||||||
@@ -14,59 +35,59 @@ print_header() {
|
|||||||
echo "======================================================"
|
echo "======================================================"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Load environment variables
|
ENV=$1
|
||||||
|
HOST=$2
|
||||||
|
SUBDOMAIN=$3 # Optional third argument for custom subdomain
|
||||||
|
|
||||||
|
# Set subdomain - use the custom subdomain if provided, otherwise use REGION
|
||||||
|
if [ -n "$SUBDOMAIN" ]; then
|
||||||
|
echo "Using custom subdomain: $SUBDOMAIN"
|
||||||
|
else
|
||||||
|
SUBDOMAIN=$HOST
|
||||||
|
echo "Using host as subdomain: $SUBDOMAIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load common environment variables first
|
||||||
if [ -f .env ]; then
|
if [ -f .env ]; then
|
||||||
echo "Loading configuration from .env file..."
|
echo "Loading common configuration from .env file..."
|
||||||
export $(grep -v '^#' .env | xargs)
|
export $(grep -v '^#' .env | xargs)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check command line argument
|
# Load environment-specific variables
|
||||||
if [ $# -ne 1 ] || ([ "$1" != "staging" ] && [ "$1" != "eu" ] && [ "$1" != "us" ]); then
|
if [ -f .env.$ENV ]; then
|
||||||
echo "Error: Please specify environment (staging, eu, or us)"
|
echo "Loading $ENV-specific configuration from .env.$ENV file..."
|
||||||
echo "Usage: $0 [staging|eu|us]"
|
export $(grep -v '^#' .env.$ENV | xargs)
|
||||||
|
else
|
||||||
|
echo "Error: Environment file .env.$ENV not found"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
REGION=$1
|
if [ "$HOST" == "staging" ]; then
|
||||||
VERSION_TAG="latest"
|
print_header "DEPLOYING TO STAGING HOST"
|
||||||
DOCKER_REPO=""
|
|
||||||
ENV=""
|
|
||||||
SSH_KEY=""
|
|
||||||
|
|
||||||
# Set environment-specific variables
|
|
||||||
if [ "$REGION" == "staging" ]; then
|
|
||||||
print_header "DEPLOYING TO STAGING ENVIRONMENT"
|
|
||||||
SERVER_HOST=$SERVER_HOST_STAGING
|
SERVER_HOST=$SERVER_HOST_STAGING
|
||||||
DOCKER_REPO=$DOCKER_REPO_STAGING
|
elif [ "$HOST" == "us" ]; then
|
||||||
ENV="staging"
|
print_header "DEPLOYING TO US HOST"
|
||||||
SSH_KEY=$SSH_KEY_STAGING
|
|
||||||
elif [ "$REGION" == "us" ]; then
|
|
||||||
print_header "DEPLOYING TO US ENVIRONMENT"
|
|
||||||
SERVER_HOST=$SERVER_HOST_US
|
SERVER_HOST=$SERVER_HOST_US
|
||||||
DOCKER_REPO=$DOCKER_REPO_PROD # Uses prod Docker repo for alt environment
|
|
||||||
SSH_KEY=$SSH_KEY_PROD
|
|
||||||
ENV="prod"
|
|
||||||
else
|
else
|
||||||
print_header "DEPLOYING TO EU ENVIRONMENT"
|
print_header "DEPLOYING TO EU HOST"
|
||||||
SERVER_HOST=$SERVER_HOST_EU
|
SERVER_HOST=$SERVER_HOST_EU
|
||||||
DOCKER_REPO=$DOCKER_REPO_PROD
|
|
||||||
SSH_KEY=$SSH_KEY_PROD
|
|
||||||
ENV="prod"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check required environment variables
|
# Check required environment variables
|
||||||
if [ -z "$SERVER_HOST" ]; then
|
if [ -z "$SERVER_HOST" ]; then
|
||||||
echo "Error: SERVER_HOST_${REGION^^} not defined in .env file or environment"
|
echo "Error: ${HOST} not defined in .env file or environment"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
DOCKER_USERNAME=${DOCKER_USERNAME} # Docker Hub username
|
|
||||||
UPDATE_SCRIPT="./update.sh" # Path to your update script
|
UPDATE_SCRIPT="./update.sh" # Path to your update script
|
||||||
REMOTE_USER="openfront"
|
REMOTE_USER="openfront"
|
||||||
REMOTE_UPDATE_PATH="/home/$REMOTE_USER"
|
REMOTE_UPDATE_PATH="/home/$REMOTE_USER"
|
||||||
REMOTE_UPDATE_SCRIPT="$REMOTE_UPDATE_PATH/update-openfront.sh" # Where to place the script on server
|
REMOTE_UPDATE_SCRIPT="$REMOTE_UPDATE_PATH/update-openfront.sh" # Where to place the script on server
|
||||||
|
|
||||||
|
IMAGE_NAME="${DOCKER_USERNAME}/${DOCKER_REPO}"
|
||||||
|
DOCKER_IMAGE="${IMAGE_NAME}:${VERSION_TAG}"
|
||||||
|
|
||||||
# Check if update script exists
|
# Check if update script exists
|
||||||
if [ ! -f "$UPDATE_SCRIPT" ]; then
|
if [ ! -f "$UPDATE_SCRIPT" ]; then
|
||||||
echo "Error: Update script $UPDATE_SCRIPT not found!"
|
echo "Error: Update script $UPDATE_SCRIPT not found!"
|
||||||
@@ -75,7 +96,9 @@ fi
|
|||||||
|
|
||||||
# Step 1: Build and upload Docker image to Docker Hub
|
# Step 1: Build and upload Docker image to Docker Hub
|
||||||
print_header "STEP 1: Building and uploading Docker image to Docker Hub"
|
print_header "STEP 1: Building and uploading Docker image to Docker Hub"
|
||||||
echo "Region: ${REGION}"
|
echo "Environment: ${ENV}"
|
||||||
|
echo "Host: ${HOST}"
|
||||||
|
echo "Subdomain: ${SUBDOMAIN}"
|
||||||
echo "Using version tag: $VERSION_TAG"
|
echo "Using version tag: $VERSION_TAG"
|
||||||
echo "Docker repository: $DOCKER_REPO"
|
echo "Docker repository: $DOCKER_REPO"
|
||||||
|
|
||||||
@@ -107,25 +130,32 @@ chmod +x $UPDATE_SCRIPT
|
|||||||
# Copy the update script to the server
|
# Copy the update script to the server
|
||||||
scp -i $SSH_KEY $UPDATE_SCRIPT $REMOTE_USER@$SERVER_HOST:$REMOTE_UPDATE_SCRIPT
|
scp -i $SSH_KEY $UPDATE_SCRIPT $REMOTE_USER@$SERVER_HOST:$REMOTE_UPDATE_SCRIPT
|
||||||
|
|
||||||
# Copy environment variables if needed
|
|
||||||
if [ -f .env ]; then
|
|
||||||
scp -i $SSH_KEY .env $REMOTE_USER@$SERVER_HOST:$REMOTE_UPDATE_PATH/.env
|
|
||||||
# Secure the .env file
|
|
||||||
ssh -i $SSH_KEY $REMOTE_USER@$SERVER_HOST "chmod 600 $REMOTE_UPDATE_PATH/.env"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "❌ Failed to copy update script to server. Stopping deployment."
|
echo "❌ Failed to copy update script to server. Stopping deployment."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✅ Update script successfully copied to server."
|
ssh -i $SSH_KEY $REMOTE_USER@$SERVER_HOST "chmod +x $REMOTE_UPDATE_SCRIPT && \
|
||||||
|
cat > $REMOTE_UPDATE_PATH/.env << 'EOL'
|
||||||
# Step 3: Execute the update script on the server
|
GAME_ENV=$ENV
|
||||||
print_header "STEP 3: Executing update script on server"
|
ENV=$ENV
|
||||||
|
HOST=$HOST
|
||||||
# Make the script executable on the remote server and execute it with the environment parameter
|
SUBDOMAIN=$SUBDOMAIN
|
||||||
ssh -i $SSH_KEY $REMOTE_USER@$SERVER_HOST "chmod +x $REMOTE_UPDATE_SCRIPT && $REMOTE_UPDATE_SCRIPT $REGION $DOCKER_USERNAME $DOCKER_REPO"
|
DOCKER_IMAGE=$DOCKER_IMAGE
|
||||||
|
DOCKER_TOKEN=$DOCKER_TOKEN
|
||||||
|
ADMIN_TOKEN=$ADMIN_TOKEN
|
||||||
|
CF_ACCOUNT_ID=$CF_ACCOUNT_ID
|
||||||
|
R2_ACCESS_KEY=$R2_ACCESS_KEY
|
||||||
|
R2_SECRET_KEY=$R2_SECRET_KEY
|
||||||
|
R2_BUCKET=$R2_BUCKET
|
||||||
|
CF_API_TOKEN=$CF_API_TOKEN
|
||||||
|
DOMAIN=$DOMAIN
|
||||||
|
SUBDOMAIN=$SUBDOMAIN
|
||||||
|
MON_USERNAME=$MON_USERNAME
|
||||||
|
MON_PASSWORD=$MON_PASSWORD
|
||||||
|
EOL
|
||||||
|
chmod 600 $REMOTE_UPDATE_PATH/.env && \
|
||||||
|
$REMOTE_UPDATE_SCRIPT"
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "❌ Failed to execute update script on server."
|
echo "❌ Failed to execute update script on server."
|
||||||
@@ -133,6 +163,6 @@ if [ $? -ne 0 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
print_header "DEPLOYMENT COMPLETED SUCCESSFULLY"
|
print_header "DEPLOYMENT COMPLETED SUCCESSFULLY"
|
||||||
echo "✅ New version deployed to ${REGION} environment!"
|
echo "✅ New version deployed to ${ENV} environment in ${HOST} with subdomain ${SUBDOMAIN}!"
|
||||||
echo "🌐 Check your ${REGION} server to verify the deployment."
|
echo "🌐 Check your server to verify the deployment."
|
||||||
echo "======================================================="
|
echo "======================================================="
|
||||||
+25
-13
@@ -1,20 +1,32 @@
|
|||||||
# Server Configuration
|
# SSH Configuration
|
||||||
SERVER_HOST_STAGING=xxx.xxx.xx.xxx
|
SSH_KEY=~/.ssh/your-ssh-key
|
||||||
SERVER_HOST_EU=xxx.xxx.xxx.xxx
|
|
||||||
SERVER_HOST_US=x.xxx.xxx.xxx
|
|
||||||
SSH_KEY_STAGING=~/.ssh/your-staging-key
|
|
||||||
SSH_KEY_PROD=~/.ssh/your-prod-key
|
|
||||||
|
|
||||||
# Docker Configuration
|
# Docker Configuration
|
||||||
DOCKER_USERNAME=username
|
DOCKER_USERNAME=username
|
||||||
DOCKER_REPO_PROD=your-prod-repo
|
DOCKER_REPO=your-repo-name
|
||||||
DOCKER_REPO_STAGING=your-staging-repo
|
DOCKER_TOKEN=your_docker_token_here
|
||||||
DOCKER_TOKEN=your_docker_token
|
|
||||||
|
|
||||||
# Admin credentials
|
# Admin credentials
|
||||||
ADMIN_TOKEN=your_admin_token
|
ADMIN_TOKEN=your_admin_token_here
|
||||||
|
|
||||||
|
# Cloudflare Configuration
|
||||||
|
CF_ACCOUNT_ID=your_cloudflare_account_id
|
||||||
|
CF_API_TOKEN=your_cloudflare_api_token
|
||||||
|
DOMAIN=your-domain.com
|
||||||
|
|
||||||
|
# R2 Configuration
|
||||||
R2_ACCESS_KEY=your_r2_access_key
|
R2_ACCESS_KEY=your_r2_access_key
|
||||||
R2_SECRET_KEY=your_r2_secret_key
|
R2_SECRET_KEY=your_r2_secret_key
|
||||||
R2_ACCOUNT_ID=your_r2_account_id
|
R2_BUCKET=your-bucket-name
|
||||||
R2_PROD_BUCKET=your-prod-bucket
|
|
||||||
R2_STAGING_BUCKET=your-staging-bucket
|
# Server Hosts
|
||||||
|
SERVER_HOST_STAGING=123.456.78.90
|
||||||
|
SERVER_HOST_EU=123.456.78.91
|
||||||
|
SERVER_HOST_US=123.456.78.92
|
||||||
|
|
||||||
|
# Monitoring Credentials
|
||||||
|
MON_USERNAME=monitor_username
|
||||||
|
MON_PASSWORD=monitor_password
|
||||||
|
|
||||||
|
# Version
|
||||||
|
VERSION_TAG="latest"
|
||||||
Executable
+103
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Metric Collector for Prometheus Pushgateway
|
||||||
|
# This script collects metrics from Node Exporter and application sources
|
||||||
|
# and pushes them to a Prometheus Pushgateway with custom labels.
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
NODE_EXPORTER_URL="http://localhost:9100/metrics"
|
||||||
|
APP_METRICS_URL="http://localhost:9090/metrics"
|
||||||
|
PUSHGATEWAY_BASE_URL="https://mon.openfront.io/pushgateway/metrics"
|
||||||
|
AUTH=$MON_USERNAME:$MON_PASSWORD
|
||||||
|
INTERVAL=15 # seconds
|
||||||
|
|
||||||
|
# Function to fetch metrics from Node Exporter
|
||||||
|
fetch_node_exporter_metrics() {
|
||||||
|
curl -s --connect-timeout 5 --max-time 10 "$NODE_EXPORTER_URL" ||
|
||||||
|
echo "# Error fetching Node Exporter metrics"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to fetch metrics from your application
|
||||||
|
fetch_app_metrics() {
|
||||||
|
curl -s --connect-timeout 5 --max-time 10 "$APP_METRICS_URL" ||
|
||||||
|
echo "# Error fetching application metrics"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to push metrics to Pushgateway
|
||||||
|
push_metrics() {
|
||||||
|
local metrics=$1
|
||||||
|
local job_name=$2
|
||||||
|
|
||||||
|
echo "Pushing $job_name metrics to Pushgateway..."
|
||||||
|
|
||||||
|
# Create a temporary file for the metrics
|
||||||
|
TEMP_FILE=$(mktemp)
|
||||||
|
echo "$metrics" > "$TEMP_FILE"
|
||||||
|
|
||||||
|
# Push to Pushgateway with instance label
|
||||||
|
curl -s -u "$AUTH" --data-binary @"$TEMP_FILE" \
|
||||||
|
"$PUSHGATEWAY_BASE_URL/job/$job_name/instance/$HOST"
|
||||||
|
|
||||||
|
# Check if push was successful
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "$job_name metrics pushed successfully"
|
||||||
|
else
|
||||||
|
echo "Error pushing $job_name metrics"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove temporary file
|
||||||
|
rm "$TEMP_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add labels to metrics
|
||||||
|
add_labels() {
|
||||||
|
local metrics=$1
|
||||||
|
|
||||||
|
# First, handle metrics with existing labels
|
||||||
|
metrics=$(echo "$metrics" | sed -E 's/(\{[^}]*)\}/\1,env="'$ENV'",host="'$HOST'",subdomain="'$SUBDOMAIN'"}/g')
|
||||||
|
|
||||||
|
# Then, handle metrics with no existing labels
|
||||||
|
metrics=$(echo "$metrics" | sed -E 's/^([a-zA-Z0-9_:]+)[ \t]+([0-9.e+-]+)$/\1{env="'$ENV'",host="'$HOST'",subdomain="'$SUBDOMAIN'"} \2/g')
|
||||||
|
|
||||||
|
echo "$metrics"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main function to collect and push metrics
|
||||||
|
collect_and_push_metrics() {
|
||||||
|
echo "Starting metrics collection cycle at $(date)"
|
||||||
|
|
||||||
|
# Get metrics from both sources
|
||||||
|
NODE_METRICS=$(fetch_node_exporter_metrics)
|
||||||
|
APP_METRICS=$(fetch_app_metrics)
|
||||||
|
|
||||||
|
# Clean up metrics (remove headers etc.)
|
||||||
|
NODE_METRICS=$(echo "$NODE_METRICS" | grep -v "^Fetching")
|
||||||
|
APP_METRICS=$(echo "$APP_METRICS" | grep -v "^Fetching")
|
||||||
|
|
||||||
|
# Add labels to metrics
|
||||||
|
NODE_METRICS=$(add_labels "$NODE_METRICS")
|
||||||
|
APP_METRICS=$(add_labels "$APP_METRICS")
|
||||||
|
|
||||||
|
# Push to Pushgateway separately
|
||||||
|
push_metrics "$NODE_METRICS" "node_exporter"
|
||||||
|
push_metrics "$APP_METRICS" "app_metrics"
|
||||||
|
|
||||||
|
echo "Metrics collection cycle completed at $(date)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
echo "===== Starting metrics collector ====="
|
||||||
|
echo "Environment: $ENV, HOST: $HOST, Subdomain: $SUBDOMAIN"
|
||||||
|
echo "Collecting and pushing metrics every $INTERVAL seconds"
|
||||||
|
echo "Node Exporter URL: $NODE_EXPORTER_URL"
|
||||||
|
echo "App Metrics URL: $APP_METRICS_URL"
|
||||||
|
echo "Pushgateway URL: $PUSHGATEWAY_BASE_URL"
|
||||||
|
|
||||||
|
# Wait for app to be ready.
|
||||||
|
sleep 30
|
||||||
|
|
||||||
|
# Then set up interval loop
|
||||||
|
while true; do
|
||||||
|
sleep $INTERVAL
|
||||||
|
collect_and_push_metrics
|
||||||
|
done
|
||||||
@@ -34,7 +34,7 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
|||||||
return process.env.GIT_COMMIT;
|
return process.env.GIT_COMMIT;
|
||||||
}
|
}
|
||||||
r2Endpoint(): string {
|
r2Endpoint(): string {
|
||||||
return `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
return `https://${process.env.CF_ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
||||||
}
|
}
|
||||||
r2AccessKey(): string {
|
r2AccessKey(): string {
|
||||||
return process.env.R2_ACCESS_KEY;
|
return process.env.R2_ACCESS_KEY;
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import promClient from "prom-client";
|
import promClient from "prom-client";
|
||||||
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
|
||||||
import { GameManager } from "./GameManager";
|
import { GameManager } from "./GameManager";
|
||||||
|
|
||||||
const config = getServerConfigFromServer();
|
|
||||||
const region = config.region();
|
|
||||||
|
|
||||||
// Initialize the Prometheus registry
|
// Initialize the Prometheus registry
|
||||||
const register = new promClient.Registry();
|
const register = new promClient.Registry();
|
||||||
|
|
||||||
@@ -15,21 +11,18 @@ promClient.collectDefaultMetrics({ register });
|
|||||||
const activeGamesGauge = new promClient.Gauge({
|
const activeGamesGauge = new promClient.Gauge({
|
||||||
name: "openfront_active_games_count",
|
name: "openfront_active_games_count",
|
||||||
help: "Number of active games on this worker",
|
help: "Number of active games on this worker",
|
||||||
labelNames: ["region"],
|
|
||||||
registers: [register],
|
registers: [register],
|
||||||
});
|
});
|
||||||
|
|
||||||
const connectedClientsGauge = new promClient.Gauge({
|
const connectedClientsGauge = new promClient.Gauge({
|
||||||
name: "openfront_connected_clients_count",
|
name: "openfront_connected_clients_count",
|
||||||
help: "Number of connected clients on this worker",
|
help: "Number of connected clients on this worker",
|
||||||
labelNames: ["region"],
|
|
||||||
registers: [register],
|
registers: [register],
|
||||||
});
|
});
|
||||||
|
|
||||||
const memoryUsageGauge = new promClient.Gauge({
|
const memoryUsageGauge = new promClient.Gauge({
|
||||||
name: "openfront_memory_usage_bytes",
|
name: "openfront_memory_usage_bytes",
|
||||||
help: "Current memory usage of the worker process in bytes",
|
help: "Current memory usage of the worker process in bytes",
|
||||||
labelNames: ["region"],
|
|
||||||
registers: [register],
|
registers: [register],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,11 +35,11 @@ export const metrics = {
|
|||||||
|
|
||||||
// Function to update game-related metrics
|
// Function to update game-related metrics
|
||||||
updateGameMetrics: (gameManager: GameManager) => {
|
updateGameMetrics: (gameManager: GameManager) => {
|
||||||
activeGamesGauge.set({ region: region }, gameManager.activeGames());
|
activeGamesGauge.set(gameManager.activeGames());
|
||||||
connectedClientsGauge.set({ region: region }, gameManager.activeClients());
|
connectedClientsGauge.set(gameManager.activeClients());
|
||||||
|
|
||||||
// Update memory usage metrics
|
// Update memory usage metrics
|
||||||
const memoryUsage = process.memoryUsage();
|
const memoryUsage = process.memoryUsage();
|
||||||
memoryUsageGauge.set({ region: region }, memoryUsage.heapUsed);
|
memoryUsageGauge.set(memoryUsage.heapUsed);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Check if required environment variables are set
|
||||||
|
if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ACCOUNT_ID" ] || [ -z "$SUBDOMAIN" ] || [ -z "$DOMAIN" ]; then
|
||||||
|
echo "Error: Required environment variables not set"
|
||||||
|
echo "Please set CF_API_TOKEN, CF_ACCOUNT_ID, SUBDOMAIN, and DOMAIN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate a unique tunnel name using timestamp
|
||||||
|
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
||||||
|
TUNNEL_NAME="${SUBDOMAIN}-tunnel-${TIMESTAMP}"
|
||||||
|
echo "Using unique tunnel name: ${TUNNEL_NAME}"
|
||||||
|
|
||||||
|
# Create a new tunnel
|
||||||
|
echo "Creating Cloudflare tunnel for subdomain ${SUBDOMAIN}..."
|
||||||
|
TUNNEL_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel" \
|
||||||
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"name\":\"${TUNNEL_NAME}\"}")
|
||||||
|
|
||||||
|
# Extract tunnel ID and token
|
||||||
|
TUNNEL_ID=$(echo $TUNNEL_RESPONSE | jq -r '.result.id')
|
||||||
|
TUNNEL_TOKEN=$(echo $TUNNEL_RESPONSE | jq -r '.result.token')
|
||||||
|
|
||||||
|
if [ -z "$TUNNEL_ID" ] || [ "$TUNNEL_ID" == "null" ]; then
|
||||||
|
echo "Failed to create tunnel"
|
||||||
|
echo $TUNNEL_RESPONSE
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Tunnel created with ID: ${TUNNEL_ID}"
|
||||||
|
|
||||||
|
# Configure the tunnel with hostname
|
||||||
|
echo "Configuring tunnel to point to ${SUBDOMAIN}.${DOMAIN}..."
|
||||||
|
curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" \
|
||||||
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"config\":{\"ingress\":[{\"hostname\":\"${SUBDOMAIN}.${DOMAIN}\",\"service\":\"http://localhost:80\"},{\"service\":\"http_status:404\"}]}}"
|
||||||
|
|
||||||
|
# Update DNS record to point to the new tunnel
|
||||||
|
echo "Updating DNS record to point to the new tunnel..."
|
||||||
|
|
||||||
|
# First check if DNS record exists
|
||||||
|
DNS_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${DOMAIN}" \
|
||||||
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json")
|
||||||
|
|
||||||
|
ZONE_ID=$(echo $DNS_RECORDS | jq -r '.result[0].id')
|
||||||
|
|
||||||
|
if [ -z "$ZONE_ID" ] || [ "$ZONE_ID" == "null" ]; then
|
||||||
|
echo "Could not find zone ID for domain ${DOMAIN}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for existing record
|
||||||
|
EXISTING_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=${SUBDOMAIN}.${DOMAIN}" \
|
||||||
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json")
|
||||||
|
|
||||||
|
RECORD_ID=$(echo $EXISTING_RECORDS | jq -r '.result[0].id')
|
||||||
|
|
||||||
|
# Create or update the DNS record
|
||||||
|
if [ -z "$RECORD_ID" ] || [ "$RECORD_ID" == "null" ]; then
|
||||||
|
# Create new record
|
||||||
|
echo "Creating new DNS record..."
|
||||||
|
DNS_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
|
||||||
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"type\":\"CNAME\",\"name\":\"${SUBDOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargotunnel.com\",\"ttl\":1,\"proxied\":true}")
|
||||||
|
else
|
||||||
|
# Update existing record
|
||||||
|
echo "Updating existing DNS record..."
|
||||||
|
DNS_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
|
||||||
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data "{\"type\":\"CNAME\",\"name\":\"${SUBDOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargotunnel.com\",\"ttl\":1,\"proxied\":true}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Log the tunnel information
|
||||||
|
echo "Tunnel is set up! Site will be available at: https://${SUBDOMAIN}.${DOMAIN}"
|
||||||
|
|
||||||
|
|
||||||
|
# Export the tunnel token for supervisord
|
||||||
|
export CLOUDFLARE_TUNNEL_TOKEN=${TUNNEL_TOKEN}
|
||||||
|
|
||||||
|
# Start supervisord
|
||||||
|
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||||
+23
-1
@@ -22,4 +22,26 @@ user=node
|
|||||||
stdout_logfile=/dev/stdout
|
stdout_logfile=/dev/stdout
|
||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:cloudflared]
|
||||||
|
command=cloudflared tunnel run --token %(ENV_CLOUDFLARE_TUNNEL_TOKEN)s
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/cloudflared.log
|
||||||
|
stderr_logfile=/var/log/cloudflared-err.log
|
||||||
|
|
||||||
|
[program:node_exporter]
|
||||||
|
command=/usr/local/bin/node_exporter
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/node_exporter.log
|
||||||
|
stderr_logfile=/var/log/node_exporter-err.log
|
||||||
|
|
||||||
|
|
||||||
|
[program:metrics_exporter]
|
||||||
|
command=/usr/src/app/metric-exporter.sh
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/var/log/metrics-exporter.log
|
||||||
|
stderr_logfile=/var/log/metrics-exporter-err.log
|
||||||
|
|||||||
@@ -2,35 +2,20 @@
|
|||||||
# update.sh - Script to update Docker container on Hetzner server
|
# update.sh - Script to update Docker container on Hetzner server
|
||||||
# Called by deploy.sh after uploading Docker image to Docker Hub
|
# Called by deploy.sh after uploading Docker image to Docker Hub
|
||||||
|
|
||||||
# Check if environment parameter is provided
|
|
||||||
if [ $# -lt 3 ]; then
|
|
||||||
echo "Error: Required parameters missing"
|
|
||||||
echo "Usage: $0 <REGION> <docker_username> <docker_repo>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set parameters
|
|
||||||
REGION=$1
|
|
||||||
DOCKER_USERNAME=$2
|
|
||||||
DOCKER_REPO=$3
|
|
||||||
|
|
||||||
# Container and image configuration
|
|
||||||
CONTAINER_NAME="openfront-${REGION}"
|
|
||||||
IMAGE_NAME="${DOCKER_USERNAME}/${DOCKER_REPO}"
|
|
||||||
FULL_IMAGE_NAME="${IMAGE_NAME}:latest"
|
|
||||||
|
|
||||||
echo "======================================================"
|
|
||||||
echo "🔄 UPDATING SERVER: ${REGION} ENVIRONMENT"
|
|
||||||
echo "======================================================"
|
|
||||||
echo "Container name: ${CONTAINER_NAME}"
|
|
||||||
echo "Docker image: ${FULL_IMAGE_NAME}"
|
|
||||||
|
|
||||||
# Load environment variables if .env exists
|
# Load environment variables if .env exists
|
||||||
if [ -f /home/openfront/.env ]; then
|
if [ -f /home/openfront/.env ]; then
|
||||||
echo "Loading environment variables from .env file..."
|
echo "Loading environment variables from .env file..."
|
||||||
export $(grep -v '^#' /home/openfront/.env | xargs)
|
export $(grep -v '^#' /home/openfront/.env | xargs)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "======================================================"
|
||||||
|
echo "🔄 UPDATING SERVER: ${HOST} ENVIRONMENT"
|
||||||
|
echo "======================================================"
|
||||||
|
|
||||||
|
|
||||||
|
# Container and image configuration
|
||||||
|
CONTAINER_NAME="openfront-${ENV}-${SUBDOMAIN}"
|
||||||
|
|
||||||
docker login -u $DOCKER_USERNAME -p $DOCKER_TOKEN
|
docker login -u $DOCKER_USERNAME -p $DOCKER_TOKEN
|
||||||
|
|
||||||
# Install Loki Docker plugin if not already installed
|
# Install Loki Docker plugin if not already installed
|
||||||
@@ -46,8 +31,8 @@ else
|
|||||||
echo "Loki Docker plugin already installed."
|
echo "Loki Docker plugin already installed."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Pulling latest image from Docker Hub..."
|
echo "Pulling ${DOCKER_IMAGE} from Docker Hub..."
|
||||||
docker pull $FULL_IMAGE_NAME
|
docker pull $DOCKER_IMAGE
|
||||||
|
|
||||||
echo "Checking for existing container..."
|
echo "Checking for existing container..."
|
||||||
# Check for running container
|
# Check for running container
|
||||||
@@ -86,27 +71,20 @@ if [ -n "$PORT_CHECK" ]; then
|
|||||||
echo "Attempting to proceed anyway..."
|
echo "Attempting to proceed anyway..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ENV="prod"
|
echo "Starting new container for ${HOST} environment..."
|
||||||
if [ "$REGION" == "staging" ]; then
|
docker run -d \
|
||||||
ENV="staging"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting new container for ${REGION} environment..."
|
|
||||||
docker run -d -p 80:80 -p 127.0.0.1:9090:9090 \
|
|
||||||
--restart=always \
|
--restart=always \
|
||||||
$VOLUME_MOUNTS \
|
$VOLUME_MOUNTS \
|
||||||
--log-driver=loki \
|
--log-driver=loki \
|
||||||
--log-opt loki-url="http://localhost:3100/loki/api/v1/push" \
|
--log-opt loki-url="https://${MON_USERNAME}:${MON_PASSWORD}@mon.openfront.io/loki/loki/api/v1/push" \
|
||||||
--log-opt loki-batch-size="400" \
|
--log-opt loki-batch-size="400" \
|
||||||
--log-opt loki-external-labels="job=docker,environment=${ENV},host=${REGION},region=${REGION}" \
|
--log-opt loki-external-labels="job=docker,environment=${ENV},host=${HOST},subdomain=${SUBDOMAIN}" \
|
||||||
--env GAME_ENV=${ENV} \
|
|
||||||
--env REGION=${REGION} \
|
|
||||||
--env-file /home/openfront/.env \
|
--env-file /home/openfront/.env \
|
||||||
--name ${CONTAINER_NAME} \
|
--name ${CONTAINER_NAME} \
|
||||||
$FULL_IMAGE_NAME
|
$DOCKER_IMAGE
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
echo "Update complete! New ${REGION} container is running."
|
echo "Update complete! New ${CONTAINER_NAME} container is running."
|
||||||
|
|
||||||
# Final cleanup after successful deployment
|
# Final cleanup after successful deployment
|
||||||
echo "Performing final cleanup of unused Docker resources..."
|
echo "Performing final cleanup of unused Docker resources..."
|
||||||
|
|||||||
Reference in New Issue
Block a user