๐ SSH Remote Execution Tutorialยถ
This tutorial demonstrates how to use Clustrix for automated SSH-based remote execution without a job scheduler. Perfect for executing functions on remote servers, workstations, or cloud instances.
โจ New: Automated SSH Key Setupยถ
Clustrix now includes 15-second automated SSH key setup that eliminates manual configuration! No more spending 15-30 minutes on SSH setup.
๐ Prerequisitesยถ
Access to a remote server (cloud instance, workstation, or HPC login node)
Username and password for initial authentication
Python installed on the remote server
โจ Thatโs it! No manual SSH key setup required
[ ]:
# Install Clustrix (uncomment if needed)
# !pip install clustrix
import clustrix
from clustrix import cluster, configure, setup_ssh_keys_with_fallback
from clustrix.config import ClusterConfig
import numpy as np
print("โ
Clustrix imported successfully!")
print("๐ฑ Look for the interactive widget that appeared above or below.")
print("๐ You can use the widget's SSH Key Setup section for easy configuration.")
๐ Step 1: Automated SSH Key Setupยถ
This is the magic step! Instead of manually setting up SSH keys, Clustrix does it automatically.
[ ]:
# ๐ง Configure your remote server details
# Replace these with your actual server information
config = ClusterConfig(
cluster_type="ssh",
cluster_host="your-server.example.com", # Your server hostname or IP
username="your-username", # Your username on the server
port=22, # SSH port (usually 22)
# Remote execution settings
remote_work_dir="/tmp/clustrix", # Directory for temporary files
python_executable="python3", # Python command on remote server
cleanup_on_success=True, # Clean up after successful execution
max_parallel_jobs=5, # Limit concurrent executions
)
print("โ
Server configuration created!")
print(f"๐ฏ Target: {config.cluster_host}")
print(f"๐ค User: {config.username}")
print(f"๐ Port: {config.port}")
print("\n๐ Ready for automated SSH key setup...")
[ ]:
# ๐ AUTOMATED SSH KEY SETUP
# This replaces 15-30 minutes of manual work with 15 seconds of automation!
print("๐ Setting up SSH keys automatically...")
print("๐ก You'll be prompted for your password (this is normal and secure).")
print()
ssh_result = setup_ssh_keys_with_fallback(
config=config,
cluster_alias="my_server", # Creates SSH alias for easy access
key_type="ed25519", # Modern, secure key type
force_refresh=False, # Set True to generate new keys
)
# ๐ Display results
print("\n" + "="*60)
print("๐ SSH KEY SETUP RESULTS")
print("="*60)
if ssh_result["success"]:
print("๐ SUCCESS! SSH keys configured automatically!")
print(f"๐ Key path: {ssh_result['key_path']}")
print(f"๐ฆ Key already existed: {ssh_result['key_already_existed']}")
print(f"๐ Key deployed: {ssh_result['key_deployed']}")
print(f"๐ Connection tested: {ssh_result['connection_tested']}")
if "ssh_config_updated" in ssh_result.get("details", {}):
print("โ๏ธ SSH config updated with alias")
print("\n๐ฏ You can now connect with: ssh my_server")
print("\nโจ What just happened:")
print(" ๐ Generated Ed25519 SSH key pair")
print(" ๐ค Deployed public key to remote server")
print(" ๐งน Cleaned up any conflicting old keys")
print(" โ๏ธ Updated SSH configuration")
print(" โ
Tested connection to verify success")
else:
print("โ SSH key setup failed")
print(f"๐ Error: {ssh_result.get('error', 'Unknown error')}")
if "details" in ssh_result:
print("\n๐ง Troubleshooting details:")
for key, value in ssh_result["details"].items():
print(f" {key}: {value}")
print("\n๐ก Try:")
print(" - Check hostname and username are correct")
print(" - Verify network connectivity to the server")
print(" - Test manual SSH connection first")
print("\n" + "="*60)
โ๏ธ Step 2: Configure Clustrixยถ
Now that SSH keys are set up, configure Clustrix for remote execution:
[ ]:
# Configure Clustrix with the SSH setup
configure(
cluster_type="ssh",
cluster_host=config.cluster_host,
username=config.username,
port=config.port,
# Remote environment
remote_work_dir=config.remote_work_dir,
python_executable=config.python_executable,
# Execution settings
cleanup_on_success=True,
max_parallel_jobs=5,
# Optional: Remote environment activation
# conda_env_name="myenv", # Activate conda environment
# virtualenv_path="/path/to/venv", # Activate virtual environment
)
print("โ
Clustrix configured for SSH remote execution!")
print(f"๐ฏ Target server: {config.cluster_host}")
print(f"๐ Remote work directory: {config.remote_work_dir}")
print(f"๐ Python executable: {config.python_executable}")
print("\n๐ Ready to execute functions remotely!")
๐งฎ Example 1: Basic Remote Computationยถ
Execute a simple mathematical computation remotely:
[ ]:
@cluster
def basic_remote_computation(n=1000000):
"""
Simple computation executed on remote server.
"""
import math
import time
import platform
from datetime import datetime
print(f"๐ฅ๏ธ Executing on: {platform.node()}")
print(f"๐ Python version: {platform.python_version()}")
print(f"โก Starting computation at {datetime.now()}")
print(f"๐ข Computing sum of squares for {n:,} numbers")
start_time = time.time()
# Compute sum of squares
total = sum(i*i for i in range(n))
# Compute some mathematical functions
sqrt_total = math.sqrt(total)
log_total = math.log(total)
end_time = time.time()
execution_time = end_time - start_time
result = {
'n': n,
'sum_of_squares': total,
'sqrt_sum': sqrt_total,
'log_sum': log_total,
'execution_time_seconds': execution_time,
'hostname': platform.node(),
'python_version': platform.python_version(),
'completion_time': datetime.now().isoformat()
}
print(f"โ
Computation completed in {execution_time:.2f} seconds")
return result
# Execute on remote server
print("๐ Executing basic computation on remote server...")
result = basic_remote_computation(500000)
print(f"\n๐ REMOTE COMPUTATION COMPLETE")
print(f"๐ฅ๏ธ Executed on: {result['hostname']}")
print(f"๐ Python version: {result['python_version']}")
print(f"๐ข Numbers processed: {result['n']:,}")
print(f"๐ Sum of squares: {result['sum_of_squares']:,}")
print(f"๐ Square root of sum: {result['sqrt_sum']:,.2f}")
print(f"โฑ๏ธ Execution time: {result['execution_time_seconds']:.2f} seconds")
print(f"๐ Completed at: {result['completion_time']}")
๐ Example 2: Remote Data Processing with NumPyยถ
Process numerical data on the remote server:
[ ]:
@cluster
def remote_numpy_computation(matrix_size=1000, num_iterations=5):
"""
Perform numerical computations using NumPy on remote server.
"""
import numpy as np
import time
import platform
from datetime import datetime
print(f"๐ฅ๏ธ Remote execution on: {platform.node()}")
print(f"๐ NumPy version: {np.__version__}")
print(f"๐ข Matrix size: {matrix_size}x{matrix_size}")
print(f"๐ Iterations: {num_iterations}")
results = []
total_start_time = time.time()
for iteration in range(num_iterations):
print(f"\n๐ Iteration {iteration + 1}/{num_iterations}")
start_time = time.time()
# Generate random matrices
print(" ๐ Generating random matrices...")
A = np.random.randn(matrix_size, matrix_size)
B = np.random.randn(matrix_size, matrix_size)
# Matrix multiplication
print(" โ๏ธ Performing matrix multiplication...")
C = np.dot(A, B)
# Eigenvalue computation (smaller matrix for speed)
small_size = min(100, matrix_size)
print(f" ๐งฎ Computing eigenvalues ({small_size}x{small_size})...")
eigenvalues = np.linalg.eigvals(A[:small_size, :small_size])
# Statistical analysis
print(" ๐ Computing statistics...")
stats = {
'matrix_mean': float(np.mean(C)),
'matrix_std': float(np.std(C)),
'matrix_max': float(np.max(C)),
'matrix_min': float(np.min(C)),
'eigenvalue_mean': float(np.mean(eigenvalues.real)),
'eigenvalue_max': float(np.max(eigenvalues.real)),
'frobenius_norm': float(np.linalg.norm(C, 'fro')),
}
end_time = time.time()
iteration_time = end_time - start_time
iteration_result = {
'iteration': iteration + 1,
'execution_time': iteration_time,
'statistics': stats
}
results.append(iteration_result)
print(f" โฑ๏ธ Iteration completed in {iteration_time:.2f} seconds")
total_end_time = time.time()
total_time = total_end_time - total_start_time
# Aggregate statistics
execution_times = [r['execution_time'] for r in results]
final_result = {
'computation_info': {
'matrix_size': matrix_size,
'num_iterations': num_iterations,
'hostname': platform.node(),
'numpy_version': np.__version__,
'completion_time': datetime.now().isoformat()
},
'performance': {
'total_time': total_time,
'average_iteration_time': np.mean(execution_times),
'min_iteration_time': np.min(execution_times),
'max_iteration_time': np.max(execution_times),
'operations_per_second': (num_iterations * matrix_size * matrix_size) / total_time
},
'iteration_results': results
}
print(f"\nโ
All computations completed!")
print(f"โฑ๏ธ Total execution time: {total_time:.2f} seconds")
print(f"๐ Average iteration time: {np.mean(execution_times):.2f} seconds")
return final_result
# Execute numerical computation on remote server
print("๐ Starting remote NumPy computation...")
numpy_result = remote_numpy_computation(matrix_size=500, num_iterations=3)
print(f"\n๐ REMOTE NUMPY COMPUTATION COMPLETE")
info = numpy_result['computation_info']
print(f"๐ฅ๏ธ Executed on: {info['hostname']}")
print(f"๐ NumPy version: {info['numpy_version']}")
print(f"๐ข Matrix size: {info['matrix_size']}x{info['matrix_size']}")
print(f"๐ Iterations: {info['num_iterations']}")
perf = numpy_result['performance']
print(f"\n๐ Performance Metrics:")
print(f" โฑ๏ธ Total time: {perf['total_time']:.2f} seconds")
print(f" ๐ Average iteration: {perf['average_iteration_time']:.2f} seconds")
print(f" โก Operations/second: {perf['operations_per_second']:,.0f}")
print(f" ๐ Fastest iteration: {perf['min_iteration_time']:.2f} seconds")
print(f" ๐ Slowest iteration: {perf['max_iteration_time']:.2f} seconds")
# Show statistics from the last iteration
if numpy_result['iteration_results']:
last_stats = numpy_result['iteration_results'][-1]['statistics']
print(f"\n๐ Final Matrix Statistics:")
print(f" ๐ Mean: {last_stats['matrix_mean']:.4f}")
print(f" ๐ Std Dev: {last_stats['matrix_std']:.4f}")
print(f" ๐บ Max: {last_stats['matrix_max']:.4f}")
print(f" ๐ป Min: {last_stats['matrix_min']:.4f}")
print(f" ๐งฎ Eigenvalue Mean: {last_stats['eigenvalue_mean']:.4f}")
print(f" ๐ Frobenius Norm: {last_stats['frobenius_norm']:.2f}")
๐๏ธ Example 3: Remote File System Analysisยถ
Analyze the file system structure on the remote server:
[ ]:
@cluster
def remote_system_analysis():
"""
Analyze system information and file system on remote server.
"""
import os
import platform
import shutil
import subprocess
import psutil # Common on many systems
from datetime import datetime
print(f"๐ฅ๏ธ Analyzing system: {platform.node()}")
# Basic system information
system_info = {
'hostname': platform.node(),
'system': platform.system(),
'release': platform.release(),
'version': platform.version(),
'machine': platform.machine(),
'processor': platform.processor(),
'python_version': platform.python_version(),
'architecture': platform.architecture(),
}
print(f"๐ป System: {system_info['system']} {system_info['release']}")
print(f"๐๏ธ Architecture: {system_info['machine']}")
print(f"๐ Python: {system_info['python_version']}")
# Memory and CPU information
try:
memory = psutil.virtual_memory()
cpu_info = {
'cpu_count': psutil.cpu_count(),
'cpu_percent': psutil.cpu_percent(interval=1),
'memory_total_gb': memory.total / (1024**3),
'memory_available_gb': memory.available / (1024**3),
'memory_percent': memory.percent,
}
print(f"โก CPUs: {cpu_info['cpu_count']}")
print(f"๐ง Memory: {cpu_info['memory_total_gb']:.1f} GB total, {cpu_info['memory_available_gb']:.1f} GB available")
except ImportError:
print("๐ psutil not available, skipping detailed system metrics")
cpu_info = {'error': 'psutil not available'}
# Disk usage analysis
disk_info = {}
important_paths = ['/', '/home', '/tmp', '/var', '/usr']
print("\n๐พ Disk Usage Analysis:")
for path in important_paths:
if os.path.exists(path):
try:
usage = shutil.disk_usage(path)
disk_info[path] = {
'total_gb': usage.total / (1024**3),
'used_gb': usage.used / (1024**3),
'free_gb': usage.free / (1024**3),
'used_percent': (usage.used / usage.total) * 100
}
print(f" ๐ {path}: {disk_info[path]['used_gb']:.1f}GB used / {disk_info[path]['total_gb']:.1f}GB total ({disk_info[path]['used_percent']:.1f}%)")
except (OSError, PermissionError):
disk_info[path] = {'error': 'Permission denied or path inaccessible'}
# Environment analysis
env_info = {
'user': os.environ.get('USER', 'unknown'),
'home': os.environ.get('HOME', 'unknown'),
'shell': os.environ.get('SHELL', 'unknown'),
'path_entries': len(os.environ.get('PATH', '').split(':')),
'working_directory': os.getcwd(),
}
print(f"\n๐ค Environment Info:")
print(f" User: {env_info['user']}")
print(f" Home: {env_info['home']}")
print(f" Shell: {env_info['shell']}")
print(f" Working Dir: {env_info['working_directory']}")
# Available Python packages
print("\n๐ Checking Python Environment:")
common_packages = [
'numpy', 'pandas', 'scipy', 'matplotlib', 'sklearn', 'requests',
'psutil', 'jupyter', 'ipython', 'pytest', 'click', 'flask'
]
package_status = {}
for package in common_packages:
try:
__import__(package)
# Try to get version
try:
mod = __import__(package)
version = getattr(mod, '__version__', 'unknown')
package_status[package] = {'available': True, 'version': version}
except:
package_status[package] = {'available': True, 'version': 'unknown'}
except ImportError:
package_status[package] = {'available': False}
available_packages = [pkg for pkg, info in package_status.items() if info['available']]
print(f" โ
Available packages ({len(available_packages)}/{len(common_packages)}): {', '.join(available_packages[:8])}")
# Network connectivity test
network_info = {}
try:
import socket
hostname = socket.gethostname()
ip_address = socket.gethostbyname(hostname)
network_info = {
'hostname': hostname,
'ip_address': ip_address,
'connectivity': 'basic_ok'
}
print(f"\n๐ Network: {hostname} ({ip_address})")
except Exception as e:
network_info = {'error': str(e)}
print(f"\n๐ Network: Error getting network info")
# Final analysis result
analysis_result = {
'analysis_metadata': {
'timestamp': datetime.now().isoformat(),
'analysis_type': 'remote_system_analysis'
},
'system_information': system_info,
'performance_info': cpu_info,
'disk_usage': disk_info,
'environment': env_info,
'python_packages': package_status,
'network_info': network_info
}
print(f"\nโ
System analysis completed!")
return analysis_result
# Analyze remote system
print("๐ Starting remote system analysis...")
system_result = remote_system_analysis()
print(f"\n๐ REMOTE SYSTEM ANALYSIS COMPLETE")
sys_info = system_result['system_information']
print(f"๐ฅ๏ธ System: {sys_info['hostname']} ({sys_info['system']} {sys_info['release']})")
print(f"๐๏ธ Architecture: {sys_info['machine']}")
print(f"๐ Python: {sys_info['python_version']}")
if 'error' not in system_result['performance_info']:
perf = system_result['performance_info']
print(f"\n๐ Performance:")
print(f" โก CPUs: {perf['cpu_count']}")
print(f" ๐ง Memory: {perf['memory_total_gb']:.1f} GB ({perf['memory_percent']:.1f}% used)")
print(f" ๐ฅ CPU Usage: {perf['cpu_percent']:.1f}%")
env = system_result['environment']
print(f"\n๐ค Environment:")
print(f" User: {env['user']}")
print(f" Home: {env['home']}")
print(f" Working Dir: {env['working_directory']}")
packages = system_result['python_packages']
available = [pkg for pkg, info in packages.items() if info['available']]
print(f"\n๐ Python Environment:")
print(f" ๐ฆ Available packages: {len(available)}/{len(packages)}")
print(f" โ
Key packages: {', '.join(available[:6])}")
disk = system_result['disk_usage']
print(f"\n๐พ Storage:")
for path, info in disk.items():
if 'error' not in info:
print(f" ๐ {path}: {info['free_gb']:.1f} GB free")
๐งช Example 4: Remote Environment Testingยถ
Test specific capabilities and benchmark performance:
[ ]:
@cluster
def benchmark_remote_performance():
"""
Benchmark computational performance on remote server.
"""
import time
import math
import platform
from datetime import datetime
print(f"๐ Starting performance benchmarks on {platform.node()}")
benchmarks = {}
# CPU benchmark: Prime number calculation
print("\n๐ข CPU Benchmark: Prime number calculation")
start_time = time.time()
def is_prime(n):
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
primes = [n for n in range(2, 10000) if is_prime(n)]
cpu_time = time.time() - start_time
benchmarks['cpu_benchmark'] = {
'test': 'prime_calculation',
'range': '2-10000',
'primes_found': len(primes),
'execution_time': cpu_time,
'primes_per_second': len(primes) / cpu_time
}
print(f" โ
Found {len(primes)} primes in {cpu_time:.3f} seconds")
print(f" ๐ Rate: {len(primes) / cpu_time:.1f} primes/second")
# Memory benchmark: List operations
print("\n๐ง Memory Benchmark: Large list operations")
start_time = time.time()
# Create large list
large_list = list(range(1000000))
# Perform operations
reversed_list = large_list[::-1]
sorted_sample = sorted(large_list[::1000])
list_sum = sum(large_list[::100])
memory_time = time.time() - start_time
benchmarks['memory_benchmark'] = {
'test': 'list_operations',
'list_size': len(large_list),
'operations': ['reverse', 'sort_sample', 'sum_subset'],
'execution_time': memory_time,
'sum_result': list_sum
}
print(f" โ
Processed {len(large_list):,} elements in {memory_time:.3f} seconds")
print(f" ๐ Rate: {len(large_list) / memory_time:,.0f} elements/second")
# I/O benchmark: File operations
print("\n๐ I/O Benchmark: File read/write operations")
import tempfile
import os
start_time = time.time()
# Write test
test_data = "\n".join([f"Line {i}: {i*i}" for i in range(10000)])
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
temp_file = f.name
f.write(test_data)
# Read test
with open(temp_file, 'r') as f:
read_data = f.read()
# Verify and cleanup
lines_read = len(read_data.split('\n'))
file_size = os.path.getsize(temp_file)
os.unlink(temp_file)
io_time = time.time() - start_time
benchmarks['io_benchmark'] = {
'test': 'file_read_write',
'lines_written': 10000,
'lines_read': lines_read,
'file_size_bytes': file_size,
'execution_time': io_time,
'throughput_mb_per_sec': (file_size / (1024*1024)) / io_time
}
print(f" โ
Wrote/read {file_size:,} bytes in {io_time:.3f} seconds")
print(f" ๐ Throughput: {(file_size / (1024*1024)) / io_time:.2f} MB/second")
# Mathematical benchmark: Floating point operations
print("\n๐งฎ Math Benchmark: Floating point operations")
start_time = time.time()
total = 0.0
for i in range(100000):
total += math.sin(i) * math.cos(i) + math.sqrt(i + 1)
math_time = time.time() - start_time
benchmarks['math_benchmark'] = {
'test': 'trigonometric_operations',
'operations_count': 100000 * 3, # sin, cos, sqrt per iteration
'result': total,
'execution_time': math_time,
'operations_per_second': (100000 * 3) / math_time
}
print(f" โ
Performed {100000 * 3:,} operations in {math_time:.3f} seconds")
print(f" ๐ Rate: {(100000 * 3) / math_time:,.0f} operations/second")
# Summary
total_benchmark_time = sum([b['execution_time'] for b in benchmarks.values()])
result = {
'benchmark_metadata': {
'hostname': platform.node(),
'system': platform.system(),
'machine': platform.machine(),
'python_version': platform.python_version(),
'timestamp': datetime.now().isoformat(),
'total_benchmark_time': total_benchmark_time
},
'benchmarks': benchmarks
}
print(f"\n๐ All benchmarks completed!")
print(f"โฑ๏ธ Total benchmark time: {total_benchmark_time:.3f} seconds")
return result
# Run performance benchmarks
print("๐ Starting remote performance benchmarks...")
benchmark_result = benchmark_remote_performance()
print(f"\n๐ REMOTE BENCHMARKS COMPLETE")
meta = benchmark_result['benchmark_metadata']
print(f"๐ฅ๏ธ System: {meta['hostname']} ({meta['system']} {meta['machine']})")
print(f"๐ Python: {meta['python_version']}")
print(f"โฑ๏ธ Total time: {meta['total_benchmark_time']:.3f} seconds")
benchmarks = benchmark_result['benchmarks']
print(f"\n๐ Benchmark Results:")
cpu = benchmarks['cpu_benchmark']
print(f" ๐ข CPU: {cpu['primes_per_second']:.1f} primes/sec")
memory = benchmarks['memory_benchmark']
print(f" ๐ง Memory: {len(memory['operations'])} ops on {memory['list_size']:,} elements in {memory['execution_time']:.3f}s")
io = benchmarks['io_benchmark']
print(f" ๐ I/O: {io['throughput_mb_per_sec']:.2f} MB/sec throughput")
math_bench = benchmarks['math_benchmark']
print(f" ๐งฎ Math: {math_bench['operations_per_second']:,.0f} ops/sec")
print(f"\n๐ Remote server performance profile complete!")
๐ง SSH Connection Testing and Troubleshootingยถ
Test your SSH connection and get troubleshooting information:
[ ]:
def test_ssh_connection():
"""
Test SSH connection and provide troubleshooting information.
"""
from clustrix import get_config
from clustrix.executor import ClusterExecutor
try:
print("๐ Testing SSH connection...")
config = get_config()
if config.cluster_type != 'ssh':
print("โ Current configuration is not for SSH.")
print("๐ก Please run the SSH configuration cell above first.")
return False
print(f"๐ฏ Target: {config.cluster_host}:{getattr(config, 'port', 22)}")
print(f"๐ค User: {config.username}")
print(f"๐ Key: {getattr(config, 'key_file', 'auto-detected')}")
# Test basic connection
executor = ClusterExecutor(config)
executor.connect()
print("โ
SSH connection successful!")
# Test basic commands
print("\n๐งช Testing basic commands...")
commands = [
("hostname", "๐ฅ๏ธ Remote hostname"),
("whoami", "๐ค Remote user"),
("pwd", "๐ Working directory"),
("python3 --version", "๐ Python version"),
("uname -a", "๐ป System info")
]
for cmd, description in commands:
try:
stdout, stderr = executor._execute_command(cmd)
output = (stdout or stderr or "no output").strip()
print(f" โ
{description}: {output}")
except Exception as e:
print(f" โ {description}: {str(e)}")
# Test work directory
work_dir = getattr(config, 'remote_work_dir', '/tmp/clustrix')
print(f"\n๐ Testing work directory: {work_dir}")
try:
stdout, stderr = executor._execute_command(f"mkdir -p {work_dir} && echo 'Directory OK'")
if "Directory OK" in stdout:
print(f" โ
Work directory accessible and writable")
else:
print(f" โ ๏ธ Work directory test inconclusive")
except Exception as e:
print(f" โ Work directory error: {e}")
executor.disconnect()
print("\n๐ SSH connection test completed successfully!")
print("โ
Your SSH configuration is working correctly.")
return True
except Exception as e:
print(f"\nโ SSH connection test failed: {e}")
print("\n๐ง Troubleshooting suggestions:")
print(" 1. Check hostname and port are correct")
print(" 2. Verify username is correct")
print(" 3. Test manual SSH: ssh user@hostname")
print(" 4. Check firewall and network connectivity")
print(" 5. Try force refresh: setup_ssh_keys_with_fallback(..., force_refresh=True)")
return False
# Run connection test
print("๐ SSH CONNECTION TEST")
print("=" * 30)
test_success = test_ssh_connection()
if test_success:
print("\n๐ Ready for remote execution!")
else:
print("\n๐ง Please fix SSH issues before proceeding.")
๐ Summary and Best Practicesยถ
๐ What Youโve Learnedยถ
๐ Automated SSH Setup: 15-second setup vs 15-30 minute manual process
โ๏ธ Remote Configuration: Easy Clustrix setup for SSH execution
๐งฎ Remote Computing: Mathematical computations on remote servers
๐ Data Processing: NumPy operations and analysis remotely
๐๏ธ System Analysis: File system and environment inspection
๐ Performance Testing: Benchmarking remote server capabilities
๐ง Troubleshooting: Connection testing and problem resolution
๐ Security Best Practicesยถ
โ Use SSH keys: Automated setup creates secure Ed25519 keys
โ Unique keys: Different keys for different servers
โ Regular rotation: Use
force_refresh=Trueperiodicallyโ Secure storage: Keys stored with proper permissions (600/644)
โ Clean up: Enable
cleanup_on_success=Trueโ Monitor access: Check SSH logs on your servers
๐ก Performance Tipsยถ
Parallel execution: Set
max_parallel_jobsappropriatelyWork directory: Use fast storage (e.g.,
/tmpor SSD)Environment setup: Use conda/virtualenv for package management
Data transfer: Minimize large data transfers between local/remote
Connection reuse: Clustrix automatically reuses SSH connections
๐ฏ When to Use SSH vs Other Cluster Typesยถ
Choose SSH when:
Working with single servers or workstations
Need immediate execution (no queuing)
Prototyping and development
Cloud instances (AWS, GCP, Azure)
Personal computing resources
Choose SLURM/PBS/SGE when:
Large HPC clusters with job schedulers
Need resource management and fair sharing
Production workloads with resource constraints
Long-running computations requiring scheduling
Choose Kubernetes when:
Containerized execution environments
Auto-scaling and fault tolerance needed
Cloud-native applications
Microservices architecture
๐ Next Stepsยถ
Try other tutorials:
SLURM Tutorial for HPC clusters
Kubernetes Tutorial for container orchestration
Cost Monitoring Tutorial for cloud costs
Explore advanced features:
Multiple cluster configurations
Custom environment setup
Filesystem utilities
Cloud provider integrations
Read documentation:
SSH Setup Guide for detailed configuration
API Documentation for advanced options
Clustrix Documentation for comprehensive guides
๐ Congratulations!ยถ
Youโve successfully learned how to use Clustrixโs automated SSH setup and remote execution capabilities. You can now:
โก Set up SSH access in 15 seconds instead of 15-30 minutes
๐ Execute Python functions on any SSH-accessible server
๐ Perform complex computations remotely
๐ง Troubleshoot and optimize your setup
๐ Maintain security best practices
Happy remote computing! ๐