chore: initial clean commit
This commit is contained in:
610
deploy.sh
Executable file
610
deploy.sh
Executable file
@@ -0,0 +1,610 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user