610 lines
15 KiB
Bash
Executable File
610 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# LLM Proxy Gateway Deployment Script
|
|
# This script automates the deployment of the LLM Proxy Gateway on a Linux server
|
|
|
|
set -e # Exit on error
|
|
set -u # Exit on undefined variable
|
|
|
|
# Configuration
|
|
APP_NAME="llm-proxy"
|
|
APP_USER="llmproxy"
|
|
APP_GROUP="llmproxy"
|
|
INSTALL_DIR="/opt/$APP_NAME"
|
|
CONFIG_DIR="/etc/$APP_NAME"
|
|
DATA_DIR="/var/lib/$APP_NAME"
|
|
LOG_DIR="/var/log/$APP_NAME"
|
|
SERVICE_FILE="/etc/systemd/system/$APP_NAME.service"
|
|
ENV_FILE="$CONFIG_DIR/.env"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Check if running as root
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Install system dependencies
|
|
install_dependencies() {
|
|
log_info "Installing system dependencies..."
|
|
|
|
# Detect package manager
|
|
if command -v apt-get &> /dev/null; then
|
|
# Debian/Ubuntu
|
|
apt-get update
|
|
apt-get install -y \
|
|
build-essential \
|
|
pkg-config \
|
|
libssl-dev \
|
|
sqlite3 \
|
|
curl \
|
|
git
|
|
elif command -v yum &> /dev/null; then
|
|
# RHEL/CentOS
|
|
yum groupinstall -y "Development Tools"
|
|
yum install -y \
|
|
openssl-devel \
|
|
sqlite \
|
|
curl \
|
|
git
|
|
elif command -v dnf &> /dev/null; then
|
|
# Fedora
|
|
dnf groupinstall -y "Development Tools"
|
|
dnf install -y \
|
|
openssl-devel \
|
|
sqlite \
|
|
curl \
|
|
git
|
|
elif command -v pacman &> /dev/null; then
|
|
# Arch Linux
|
|
pacman -Syu --noconfirm \
|
|
base-devel \
|
|
openssl \
|
|
sqlite \
|
|
curl \
|
|
git
|
|
else
|
|
log_warn "Could not detect package manager. Please install dependencies manually."
|
|
fi
|
|
}
|
|
|
|
# Install Rust if not present
|
|
install_rust() {
|
|
log_info "Checking for Rust installation..."
|
|
|
|
if ! command -v rustc &> /dev/null; then
|
|
log_info "Installing Rust..."
|
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
source "$HOME/.cargo/env"
|
|
else
|
|
log_info "Rust is already installed"
|
|
fi
|
|
|
|
# Verify installation
|
|
rustc --version
|
|
cargo --version
|
|
}
|
|
|
|
# Create system user and directories
|
|
setup_directories() {
|
|
log_info "Creating system user and directories..."
|
|
|
|
# Create user and group if they don't exist
|
|
if ! id "$APP_USER" &>/dev/null; then
|
|
useradd -r -s /usr/sbin/nologin -M "$APP_USER"
|
|
fi
|
|
|
|
# Create directories
|
|
mkdir -p "$INSTALL_DIR"
|
|
mkdir -p "$CONFIG_DIR"
|
|
mkdir -p "$DATA_DIR"
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
# Set permissions
|
|
chown -R "$APP_USER:$APP_GROUP" "$INSTALL_DIR"
|
|
chown -R "$APP_USER:$APP_GROUP" "$CONFIG_DIR"
|
|
chown -R "$APP_USER:$APP_GROUP" "$DATA_DIR"
|
|
chown -R "$APP_USER:$APP_GROUP" "$LOG_DIR"
|
|
|
|
chmod 750 "$INSTALL_DIR"
|
|
chmod 750 "$CONFIG_DIR"
|
|
chmod 750 "$DATA_DIR"
|
|
chmod 750 "$LOG_DIR"
|
|
}
|
|
|
|
# Build the application
|
|
build_application() {
|
|
log_info "Building the application..."
|
|
|
|
# Clone or update repository
|
|
if [[ ! -d "$INSTALL_DIR/.git" ]]; then
|
|
log_info "Cloning repository..."
|
|
git clone https://github.com/yourusername/llm-proxy.git "$INSTALL_DIR"
|
|
else
|
|
log_info "Updating repository..."
|
|
cd "$INSTALL_DIR"
|
|
git pull
|
|
fi
|
|
|
|
# Build in release mode
|
|
cd "$INSTALL_DIR"
|
|
log_info "Building release binary..."
|
|
cargo build --release
|
|
|
|
# Verify build
|
|
if [[ -f "target/release/$APP_NAME" ]]; then
|
|
log_info "Build successful"
|
|
else
|
|
log_error "Build failed"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Create configuration files
|
|
create_configuration() {
|
|
log_info "Creating configuration files..."
|
|
|
|
# Create .env file with API keys
|
|
cat > "$ENV_FILE" << EOF
|
|
# LLM Proxy Gateway Environment Variables
|
|
# Add your API keys here
|
|
|
|
# OpenAI API Key
|
|
# OPENAI_API_KEY=sk-your-key-here
|
|
|
|
# Google Gemini API Key
|
|
# GEMINI_API_KEY=AIza-your-key-here
|
|
|
|
# DeepSeek API Key
|
|
# DEEPSEEK_API_KEY=sk-your-key-here
|
|
|
|
# xAI Grok API Key
|
|
# GROK_API_KEY=gk-your-key-here
|
|
|
|
# Authentication tokens (comma-separated)
|
|
# LLM_PROXY__SERVER__AUTH_TOKENS=token1,token2,token3
|
|
EOF
|
|
|
|
# Create config.toml
|
|
cat > "$CONFIG_DIR/config.toml" << EOF
|
|
# LLM Proxy Gateway Configuration
|
|
|
|
[server]
|
|
port = 8080
|
|
host = "0.0.0.0"
|
|
# auth_tokens = ["token1", "token2", "token3"] # Uncomment to enable authentication
|
|
|
|
[database]
|
|
path = "$DATA_DIR/llm_proxy.db"
|
|
max_connections = 5
|
|
|
|
[providers.openai]
|
|
enabled = true
|
|
api_key_env = "OPENAI_API_KEY"
|
|
base_url = "https://api.openai.com/v1"
|
|
default_model = "gpt-4o"
|
|
|
|
[providers.gemini]
|
|
enabled = true
|
|
api_key_env = "GEMINI_API_KEY"
|
|
base_url = "https://generativelanguage.googleapis.com/v1"
|
|
default_model = "gemini-2.0-flash"
|
|
|
|
[providers.deepseek]
|
|
enabled = true
|
|
api_key_env = "DEEPSEEK_API_KEY"
|
|
base_url = "https://api.deepseek.com"
|
|
default_model = "deepseek-reasoner"
|
|
|
|
[providers.grok]
|
|
enabled = false # Disabled by default until API is researched
|
|
api_key_env = "GROK_API_KEY"
|
|
base_url = "https://api.x.ai/v1"
|
|
default_model = "grok-beta"
|
|
|
|
[model_mapping]
|
|
"gpt-*" = "openai"
|
|
"gemini-*" = "gemini"
|
|
"deepseek-*" = "deepseek"
|
|
"grok-*" = "grok"
|
|
|
|
[pricing]
|
|
openai = { input = 0.01, output = 0.03 }
|
|
gemini = { input = 0.0005, output = 0.0015 }
|
|
deepseek = { input = 0.00014, output = 0.00028 }
|
|
grok = { input = 0.001, output = 0.003 }
|
|
EOF
|
|
|
|
# Set permissions
|
|
chown "$APP_USER:$APP_GROUP" "$ENV_FILE"
|
|
chown "$APP_USER:$APP_GROUP" "$CONFIG_DIR/config.toml"
|
|
chmod 640 "$ENV_FILE"
|
|
chmod 640 "$CONFIG_DIR/config.toml"
|
|
}
|
|
|
|
# Create systemd service
|
|
create_systemd_service() {
|
|
log_info "Creating systemd service..."
|
|
|
|
cat > "$SERVICE_FILE" << EOF
|
|
[Unit]
|
|
Description=LLM Proxy Gateway
|
|
Documentation=https://github.com/yourusername/llm-proxy
|
|
After=network.target
|
|
Wants=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$APP_USER
|
|
Group=$APP_GROUP
|
|
WorkingDirectory=$INSTALL_DIR
|
|
EnvironmentFile=$ENV_FILE
|
|
Environment="RUST_LOG=info"
|
|
Environment="LLM_PROXY__CONFIG_PATH=$CONFIG_DIR/config.toml"
|
|
ExecStart=$INSTALL_DIR/target/release/$APP_NAME
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
# Security hardening
|
|
NoNewPrivileges=true
|
|
PrivateTmp=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
ReadWritePaths=$DATA_DIR $LOG_DIR
|
|
|
|
# Resource limits (adjust based on your server)
|
|
MemoryMax=400M
|
|
MemorySwapMax=100M
|
|
CPUQuota=50%
|
|
LimitNOFILE=65536
|
|
|
|
# Logging
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=$APP_NAME
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# Reload systemd
|
|
systemctl daemon-reload
|
|
}
|
|
|
|
# Setup nginx reverse proxy (optional)
|
|
setup_nginx_proxy() {
|
|
if ! command -v nginx &> /dev/null; then
|
|
log_warn "nginx not installed. Skipping reverse proxy setup."
|
|
return
|
|
fi
|
|
|
|
log_info "Setting up nginx reverse proxy..."
|
|
|
|
cat > "/etc/nginx/sites-available/$APP_NAME" << EOF
|
|
server {
|
|
listen 80;
|
|
server_name your-domain.com; # Change to your domain
|
|
|
|
# Redirect to HTTPS (recommended)
|
|
return 301 https://\$server_name\$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name your-domain.com; # Change to your domain
|
|
|
|
# SSL certificates (adjust paths)
|
|
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
|
|
|
|
# SSL configuration
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
|
ssl_prefer_server_ciphers off;
|
|
|
|
# Proxy to LLM Proxy Gateway
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
|
|
# Timeouts
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
# Health check endpoint
|
|
location /health {
|
|
proxy_pass http://127.0.0.1:8080/health;
|
|
access_log off;
|
|
}
|
|
|
|
# Dashboard
|
|
location /dashboard {
|
|
proxy_pass http://127.0.0.1:8080/dashboard;
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Enable site
|
|
ln -sf "/etc/nginx/sites-available/$APP_NAME" "/etc/nginx/sites-enabled/"
|
|
|
|
# Test nginx configuration
|
|
nginx -t
|
|
|
|
log_info "nginx configuration created. Please update the domain and SSL certificate paths."
|
|
}
|
|
|
|
# Setup firewall
|
|
setup_firewall() {
|
|
log_info "Configuring firewall..."
|
|
|
|
# Check for ufw (Ubuntu)
|
|
if command -v ufw &> /dev/null; then
|
|
ufw allow 22/tcp # SSH
|
|
ufw allow 80/tcp # HTTP
|
|
ufw allow 443/tcp # HTTPS
|
|
ufw --force enable
|
|
log_info "UFW firewall configured"
|
|
fi
|
|
|
|
# Check for firewalld (RHEL/CentOS)
|
|
if command -v firewall-cmd &> /dev/null; then
|
|
firewall-cmd --permanent --add-service=ssh
|
|
firewall-cmd --permanent --add-service=http
|
|
firewall-cmd --permanent --add-service=https
|
|
firewall-cmd --reload
|
|
log_info "Firewalld configured"
|
|
fi
|
|
}
|
|
|
|
# Initialize database
|
|
initialize_database() {
|
|
log_info "Initializing database..."
|
|
|
|
# Run the application once to create database
|
|
sudo -u "$APP_USER" "$INSTALL_DIR/target/release/$APP_NAME" --help &> /dev/null || true
|
|
|
|
log_info "Database initialized at $DATA_DIR/llm_proxy.db"
|
|
}
|
|
|
|
# Start and enable service
|
|
start_service() {
|
|
log_info "Starting $APP_NAME service..."
|
|
|
|
systemctl enable "$APP_NAME"
|
|
systemctl start "$APP_NAME"
|
|
|
|
# Check status
|
|
sleep 2
|
|
systemctl status "$APP_NAME" --no-pager
|
|
}
|
|
|
|
# Verify installation
|
|
verify_installation() {
|
|
log_info "Verifying installation..."
|
|
|
|
# Check if service is running
|
|
if systemctl is-active --quiet "$APP_NAME"; then
|
|
log_info "Service is running"
|
|
else
|
|
log_error "Service is not running"
|
|
journalctl -u "$APP_NAME" -n 20 --no-pager
|
|
exit 1
|
|
fi
|
|
|
|
# Test health endpoint
|
|
if curl -s http://localhost:8080/health | grep -q "OK"; then
|
|
log_info "Health check passed"
|
|
else
|
|
log_error "Health check failed"
|
|
exit 1
|
|
fi
|
|
|
|
# Test dashboard
|
|
if curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/dashboard | grep -q "200"; then
|
|
log_info "Dashboard is accessible"
|
|
else
|
|
log_warn "Dashboard may not be accessible (this is normal if not configured)"
|
|
fi
|
|
|
|
log_info "Installation verified successfully!"
|
|
}
|
|
|
|
# Print next steps
|
|
print_next_steps() {
|
|
cat << EOF
|
|
|
|
${GREEN}=== LLM Proxy Gateway Installation Complete ===${NC}
|
|
|
|
${YELLOW}Next steps:${NC}
|
|
|
|
1. ${GREEN}Configure API keys${NC}
|
|
Edit: $ENV_FILE
|
|
Add your API keys for the providers you want to use
|
|
|
|
2. ${GREEN}Configure authentication${NC}
|
|
Edit: $CONFIG_DIR/config.toml
|
|
Uncomment and set auth_tokens for client authentication
|
|
|
|
3. ${GREEN}Configure nginx${NC}
|
|
Edit: /etc/nginx/sites-available/$APP_NAME
|
|
Update domain name and SSL certificate paths
|
|
|
|
4. ${GREEN}Test the API${NC}
|
|
curl -X POST http://localhost:8080/v1/chat/completions \\
|
|
-H "Content-Type: application/json" \\
|
|
-H "Authorization: Bearer your-token" \\
|
|
-d '{
|
|
"model": "gpt-4o",
|
|
"messages": [{"role": "user", "content": "Hello!"}]
|
|
}'
|
|
|
|
5. ${GREEN}Access the dashboard${NC}
|
|
Open: http://your-server-ip:8080/dashboard
|
|
Or: https://your-domain.com/dashboard (if nginx configured)
|
|
|
|
${YELLOW}Useful commands:${NC}
|
|
systemctl status $APP_NAME # Check service status
|
|
journalctl -u $APP_NAME -f # View logs
|
|
systemctl restart $APP_NAME # Restart service
|
|
|
|
${YELLOW}Configuration files:${NC}
|
|
Service: $SERVICE_FILE
|
|
Config: $CONFIG_DIR/config.toml
|
|
Environment: $ENV_FILE
|
|
Database: $DATA_DIR/llm_proxy.db
|
|
Logs: $LOG_DIR/
|
|
|
|
${GREEN}For more information, see:${NC}
|
|
https://github.com/yourusername/llm-proxy
|
|
$INSTALL_DIR/README.md
|
|
$INSTALL_DIR/deployment.md
|
|
|
|
EOF
|
|
}
|
|
|
|
# Main deployment function
|
|
deploy() {
|
|
log_info "Starting LLM Proxy Gateway deployment..."
|
|
|
|
check_root
|
|
install_dependencies
|
|
install_rust
|
|
setup_directories
|
|
build_application
|
|
create_configuration
|
|
create_systemd_service
|
|
initialize_database
|
|
start_service
|
|
verify_installation
|
|
print_next_steps
|
|
|
|
# Optional steps (uncomment if needed)
|
|
# setup_nginx_proxy
|
|
# setup_firewall
|
|
|
|
log_info "Deployment completed successfully!"
|
|
}
|
|
|
|
# Update function
|
|
update() {
|
|
log_info "Updating LLM Proxy Gateway..."
|
|
|
|
check_root
|
|
|
|
# Stop service
|
|
systemctl stop "$APP_NAME"
|
|
|
|
# Update from git
|
|
cd "$INSTALL_DIR"
|
|
git pull
|
|
|
|
# Rebuild
|
|
cargo build --release
|
|
|
|
# Restart service
|
|
systemctl start "$APP_NAME"
|
|
|
|
log_info "Update completed successfully!"
|
|
systemctl status "$APP_NAME" --no-pager
|
|
}
|
|
|
|
# Uninstall function
|
|
uninstall() {
|
|
log_info "Uninstalling LLM Proxy Gateway..."
|
|
|
|
check_root
|
|
|
|
# Stop and disable service
|
|
systemctl stop "$APP_NAME" 2>/dev/null || true
|
|
systemctl disable "$APP_NAME" 2>/dev/null || true
|
|
rm -f "$SERVICE_FILE"
|
|
systemctl daemon-reload
|
|
|
|
# Remove application files
|
|
rm -rf "$INSTALL_DIR"
|
|
rm -rf "$CONFIG_DIR"
|
|
|
|
# Keep data and logs (comment out to remove)
|
|
log_warn "Data directory $DATA_DIR and logs $LOG_DIR have been preserved"
|
|
log_warn "Remove manually if desired:"
|
|
log_warn " rm -rf $DATA_DIR $LOG_DIR"
|
|
|
|
# Remove user (optional)
|
|
read -p "Remove user $APP_USER? [y/N]: " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
userdel "$APP_USER" 2>/dev/null || true
|
|
groupdel "$APP_GROUP" 2>/dev/null || true
|
|
fi
|
|
|
|
log_info "Uninstallation completed!"
|
|
}
|
|
|
|
# Show usage
|
|
usage() {
|
|
cat << EOF
|
|
LLM Proxy Gateway Deployment Script
|
|
|
|
Usage: $0 [command]
|
|
|
|
Commands:
|
|
deploy - Install and configure LLM Proxy Gateway
|
|
update - Update existing installation
|
|
uninstall - Remove LLM Proxy Gateway
|
|
help - Show this help message
|
|
|
|
Examples:
|
|
$0 deploy # Full installation
|
|
$0 update # Update to latest version
|
|
$0 uninstall # Remove installation
|
|
|
|
EOF
|
|
}
|
|
|
|
# Parse command line arguments
|
|
case "${1:-}" in
|
|
deploy)
|
|
deploy
|
|
;;
|
|
update)
|
|
update
|
|
;;
|
|
uninstall)
|
|
uninstall
|
|
;;
|
|
help|--help|-h)
|
|
usage
|
|
;;
|
|
*)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac |