provisioning-platform/prov-ecosystem/crates/runtime/INTEGRATION.md

744 lines
18 KiB
Markdown

# Runtime Crate - Comprehensive Integration Guide
## Overview
The Runtime crate provides a unified abstraction layer for managing multiple container runtimes across different platforms and architectures. This guide covers advanced integration patterns, platform-specific considerations, and best practices for using the runtime crate in production environments.
## Table of Contents
1. [Supported Runtimes](#supported-runtimes)
2. [Platform Detection Strategy](#platform-detection-strategy)
3. [Socket Path Configuration](#socket-path-configuration)
4. [Remote Runtime Access](#remote-runtime-access)
5. [Docker Compose Integration](#docker-compose-integration)
6. [Configuration Patterns](#configuration-patterns)
7. [Error Handling](#error-handling)
8. [Performance Tuning](#performance-tuning)
9. [Security Considerations](#security-considerations)
10. [Troubleshooting](#troubleshooting)
## Supported Runtimes
### Docker
**Platforms**: Linux, macOS (Docker Desktop), Windows (WSL2)
**Socket Paths**:
- Linux: `/var/run/docker.sock`
- macOS: `~/.docker/run/docker.sock` (Docker Desktop)
- Windows/WSL2: `/var/run/docker.sock`
**Key Features**:
- Industry-standard container runtime
- Full Docker Compose support
- TLS certificate support
- Buildkit support
**Configuration Example**:
```rust
use runtime::{RuntimeConfig, RuntimeType};
let config = RuntimeConfig {
runtime: RuntimeType::Docker,
socket_path: "/var/run/docker.sock".to_string(),
environment: [
("DOCKER_HOST".to_string(), "unix:///var/run/docker.sock".to_string()),
].iter().cloned().collect(),
..Default::default()
};
```
### Podman
**Platforms**: Linux (all distributions), macOS (rootless mode)
**Socket Paths**:
- Linux root: `/run/podman/podman.sock`
- Linux rootless: `$XDG_RUNTIME_DIR/podman/podman.sock`
- macOS: `$XDG_RUNTIME_DIR/podman/podman.sock`
**Key Features**:
- Docker-compatible API
- Rootless container execution
- Systemd integration
- Podman Compose support
**Rootless Configuration**:
```rust
use runtime::{RuntimeConfig, RuntimeType};
let xdg_runtime = std::env::var("XDG_RUNTIME_DIR")
.unwrap_or_else(|_| format!("/run/user/{}", unsafe { libc::getuid() }));
let config = RuntimeConfig {
runtime: RuntimeType::Podman,
socket_path: format!("{}/podman/podman.sock", xdg_runtime),
..Default::default()
};
```
### OrbStack
**Platforms**: macOS (Intel and Apple Silicon) - Default macOS runtime
**Socket Path**: `~/.orbstack/run/docker.sock`
**Key Features**:
- Fastest macOS container runtime
- Native integration with macOS filesystem
- Full Docker Compose compatibility
- Minimal resource overhead
**Configuration Example**:
```rust
use runtime::{RuntimeConfig, RuntimeType};
let home = std::env::var("HOME").expect("HOME not set");
let config = RuntimeConfig {
runtime: RuntimeType::OrbStack,
socket_path: format!("{}/.orbstack/run/docker.sock", home),
..Default::default()
};
```
### Colima
**Platforms**: macOS, Linux (via Lima)
**Socket Paths**:
- macOS: `~/.colima/docker.sock`
- Linux: `/var/run/docker.sock` (proxied via Colima)
**Key Features**:
- Lightweight VM-based runtime
- Docker CLI compatibility
- Resource isolation
- Cross-platform support
### nerdctl
**Platforms**: Linux, macOS, Windows
**Socket Path**: `unix:///run/containerd/containerd.sock`
**Key Features**:
- containerd-based runtime
- Docker-compatible CLI
- Efficient resource usage
- Kubernetes integration
## Platform Detection Strategy
The runtime crate implements intelligent platform-specific detection:
### Linux Detection Priority
```
1. Docker (socket: /var/run/docker.sock)
2. Podman rootless (socket: $XDG_RUNTIME_DIR/podman/podman.sock)
3. Podman root (socket: /run/podman/podman.sock)
4. nerdctl (socket: unix:///run/containerd/containerd.sock)
5. Colima (socket: ~/.colima/docker.sock)
```
**Implementation**:
```rust
use runtime::detect_runtime;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = detect_runtime().await?;
match runtime {
RuntimeType::Docker => println!("Using Docker"),
RuntimeType::Podman => println!("Using Podman"),
_ => println!("Using: {}", runtime.name()),
}
Ok(())
}
```
### macOS Detection Priority
```
1. OrbStack (socket: ~/.orbstack/run/docker.sock)
2. Docker Desktop (socket: ~/.docker/run/docker.sock)
3. Colima (socket: ~/.colima/docker.sock)
4. Podman (socket: $XDG_RUNTIME_DIR/podman/podman.sock)
5. nerdctl (socket: unix:///run/containerd/containerd.sock)
```
**Rationale**: OrbStack offers superior performance on macOS compared to Docker Desktop, so it's preferred when available.
### Windows Detection
Windows requires WSL2 (Windows Subsystem for Linux 2) for container support. The detection mechanism checks for available runtimes within WSL2 and follows the Linux priority list.
## Socket Path Configuration
### Standard Socket Paths
Each runtime exposes a well-known socket path for communication:
```rust
use runtime::RuntimeType;
fn get_socket_path(runtime: RuntimeType) -> String {
match runtime {
RuntimeType::Docker => "/var/run/docker.sock".to_string(),
RuntimeType::Podman => {
std::env::var("XDG_RUNTIME_DIR")
.map(|xdg| format!("{}/podman/podman.sock", xdg))
.unwrap_or_else(|_| "/run/podman/podman.sock".to_string())
}
RuntimeType::OrbStack => {
let home = std::env::var("HOME").expect("HOME not set");
format!("{}/.orbstack/run/docker.sock", home)
}
RuntimeType::Colima => {
let home = std::env::var("HOME").expect("HOME not set");
format!("{}/.colima/docker.sock", home)
}
RuntimeType::Nerdctl => "unix:///run/containerd/containerd.sock".to_string(),
}
}
```
### Custom Socket Paths
For non-standard configurations (e.g., custom Docker socket location, remote access):
```rust
use runtime::{RuntimeConfig, RuntimeType};
let config = RuntimeConfig {
runtime: RuntimeType::Docker,
socket_path: "/custom/docker.sock".to_string(),
..Default::default()
};
```
### Environment Variable Overrides
Use environment variables for dynamic configuration:
```rust
use runtime::RuntimeConfig;
let socket_path = std::env::var("RUNTIME_SOCKET_PATH")
.unwrap_or_else(|_| "/var/run/docker.sock".to_string());
let config = RuntimeConfig {
socket_path,
..Default::default()
};
```
## Remote Runtime Access
### SSH-Based Remote Access
Configure remote runtime access via SSH:
```rust
use runtime::{RuntimeConfig, RemoteConfig};
let remote = RemoteConfig {
enabled: true,
host: "docker.example.com".to_string(),
user: "docker-user".to_string(),
port: 22,
private_key: Some("/home/user/.ssh/id_rsa".to_string()),
password: None,
known_hosts: Some("/home/user/.ssh/known_hosts".to_string()),
jump_host: None, // For bastion/jump host scenarios
};
let config = RuntimeConfig {
remote_config: Some(remote),
..Default::default()
};
```
### Jump Host Configuration
For accessing runtimes through a bastion host:
```rust
use runtime::{RuntimeConfig, RemoteConfig};
let jump_host = RemoteConfig {
host: "bastion.example.com".to_string(),
user: "bastion-user".to_string(),
private_key: Some("/home/user/.ssh/bastion_key".to_string()),
..Default::default()
};
let remote = RemoteConfig {
enabled: true,
host: "docker.internal.example.com".to_string(),
user: "docker-user".to_string(),
private_key: Some("/home/user/.ssh/docker_key".to_string()),
jump_host: Some(Box::new(jump_host)),
..Default::default()
};
let config = RuntimeConfig {
remote_config: Some(remote),
..Default::default()
};
```
### TLS/HTTPS Remote Access
```rust
use runtime::RuntimeConfig;
let config = RuntimeConfig {
socket_path: "tcp://docker.example.com:2376".to_string(),
environment: [
("DOCKER_HOST".to_string(), "tcp://docker.example.com:2376".to_string()),
("DOCKER_CERT_PATH".to_string(), "/path/to/certs".to_string()),
("DOCKER_TLS_VERIFY".to_string(), "1".to_string()),
].iter().cloned().collect(),
..Default::default()
};
```
## Docker Compose Integration
### Automatic Compose Adaptation
The runtime crate can adapt Docker Compose files for different runtimes:
```rust
use runtime::adapt_compose;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let adapted = adapt_compose(
"docker-compose.yaml",
RuntimeType::Podman
).await?;
println!("Adapted compose: {}", adapted);
Ok(())
}
```
### Compose File Differences
Different runtimes may require compose file adjustments:
**Docker** (Standard):
```yaml
version: '3.8'
services:
app:
image: my-app:latest
ports:
- "8080:8080"
```
**Podman** (Rootless considerations):
```yaml
version: '3.8'
services:
app:
image: my-app:latest
ports:
- "8080:8080"
# May require podman-compose specific directives
```
**OrbStack** (Filesystem integration):
```yaml
version: '3.8'
services:
app:
image: my-app:latest
volumes:
- ./data:/data:z # z flag for SELinux compatibility
```
### Compose Version Support
- **Version 3.x**: Fully supported across all runtimes
- **Version 2.x**: Limited support; migration recommended
- **Buildx Compose**: Docker and Podman only
## Configuration Patterns
### Environment-Based Configuration
```rust
use runtime::{RuntimeConfig, RuntimeType};
fn load_config_from_env() -> RuntimeConfig {
let runtime_type = std::env::var("CONTAINER_RUNTIME")
.unwrap_or_else(|_| "docker".to_string());
let socket_path = std::env::var("CONTAINER_SOCKET")
.unwrap_or_else(|_| {
match runtime_type.as_str() {
"podman" => "$XDG_RUNTIME_DIR/podman/podman.sock".to_string(),
_ => "/var/run/docker.sock".to_string(),
}
});
RuntimeConfig {
runtime: RuntimeType::from_str(&runtime_type).unwrap_or(RuntimeType::Docker),
socket_path,
..Default::default()
}
}
```
### File-Based Configuration
```rust
use serde::{Deserialize, Serialize};
use std::fs;
#[derive(Serialize, Deserialize)]
struct RuntimeSettings {
runtime: String,
socket_path: String,
environment: std::collections::HashMap<String, String>,
}
fn load_config_from_file(path: &str) -> Result<RuntimeSettings, Box<dyn std::error::Error>> {
let content = fs::read_to_string(path)?;
let settings: RuntimeSettings = serde_json::from_str(&content)?;
Ok(settings)
}
```
### Builder Pattern Configuration
```rust
use runtime::{RuntimeConfig, RuntimeType};
let config = RuntimeConfig::builder()
.runtime(RuntimeType::Docker)
.socket_path("/var/run/docker.sock")
.with_env("DOCKER_BUILDKIT", "1")
.with_env("COMPOSE_DOCKER_CLI_BUILD", "1")
.build();
```
## Error Handling
### Common Error Scenarios
**Socket Not Found**:
```rust
use runtime::{detect_runtime, RuntimeError};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
match detect_runtime().await {
Ok(runtime) => println!("Found: {}", runtime.name()),
Err(RuntimeError::SocketNotFound(path)) => {
eprintln!("Docker socket not found at {}", path);
eprintln!("Please ensure Docker/Podman is running");
}
Err(e) => eprintln!("Error: {}", e),
}
Ok(())
}
```
**Permission Denied**:
```rust
use runtime::RuntimeError;
match some_operation().await {
Err(RuntimeError::PermissionDenied) => {
eprintln!("Cannot access runtime socket");
eprintln!("Try: sudo usermod -aG docker $USER");
}
Err(e) => eprintln!("Error: {}", e),
Ok(result) => println!("Success: {:?}", result),
}
```
**Connection Timeout**:
```rust
use runtime::RuntimeError;
use std::time::Duration;
match runtime.with_timeout(Duration::from_secs(5)).info().await {
Err(RuntimeError::Timeout) => {
eprintln!("Runtime unresponsive (timeout after 5s)");
eprintln!("Check if Docker/Podman is running");
}
Err(e) => eprintln!("Error: {}", e),
Ok(info) => println!("Runtime info: {:?}", info),
}
```
## Performance Tuning
### Connection Pooling
For applications making many runtime calls:
```rust
use runtime::{RuntimeConfig, RuntimePool};
let pool = RuntimePool::with_size(
RuntimeConfig::default(),
10 // Pool size
);
// Acquire connections from pool
let runtime = pool.get().await?;
runtime.info().await?;
runtime.close().await?;
```
### Caching Runtime Information
```rust
use runtime::detect_runtime;
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let runtime = Arc::new(Mutex::new(detect_runtime().await?));
// Use cached runtime across async tasks
let runtime_clone = Arc::clone(&runtime);
tokio::spawn(async move {
let rt = runtime_clone.lock().await;
// Use cached runtime
}).await?;
Ok(())
}
```
### Batch Operations
```rust
use runtime::RuntimeBatch;
let batch = RuntimeBatch::new(runtime);
batch
.add_pull("image:tag")
.add_build("/path/to/dockerfile", "image:tag")
.add_run("image:tag", vec!["command"])
.execute()
.await?;
```
## Security Considerations
### Socket Permissions
Ensure proper socket file permissions:
```bash
# Docker socket (default)
ls -l /var/run/docker.sock
# Should be: srw-rw---- root docker
# Podman socket (rootless)
ls -l $XDG_RUNTIME_DIR/podman/podman.sock
# Should be: srw------- (user only)
```
### User Privileges
For rootless containers:
```rust
use runtime::RuntimeConfig;
// Verify running as unprivileged user
if unsafe { libc::getuid() } == 0 {
eprintln!("Warning: Running container operations as root");
eprintln!("Consider using rootless podman for security");
}
let config = RuntimeConfig {
// Configure for rootless operation
..Default::default()
};
```
### TLS Certificates
For remote access with TLS:
```rust
use runtime::RuntimeConfig;
let config = RuntimeConfig {
tls_config: Some(TlsConfig {
ca_cert: "/path/to/ca.pem".to_string(),
client_cert: "/path/to/cert.pem".to_string(),
client_key: "/path/to/key.pem".to_string(),
verify: true,
}),
..Default::default()
};
```
### Environment Variable Sanitization
```rust
use runtime::RuntimeConfig;
// Avoid storing sensitive data in environment variables
let config = RuntimeConfig {
environment: [
// Only non-sensitive variables
("DOCKER_BUILDKIT".to_string(), "1".to_string()),
].iter().cloned().collect(),
// Load secrets from secure sources instead
..Default::default()
};
```
## Troubleshooting
### Runtime Not Detected
**Symptoms**: `detect_runtime()` fails or returns unexpected runtime
**Diagnosis**:
```bash
# Check if socket exists
ls -l /var/run/docker.sock
# or
echo $XDG_RUNTIME_DIR
ls -l $XDG_RUNTIME_DIR/podman/podman.sock
```
**Solutions**:
1. Ensure container runtime is installed
2. Start the runtime service: `systemctl start docker` or `systemctl --user start podman`
3. Check socket permissions: `sudo usermod -aG docker $USER`
### Connection Refused
**Symptoms**: Operations fail with "Connection refused"
**Diagnosis**:
```bash
# Test socket connectivity
docker ps # or podman ps
# If this fails, daemon isn't running
```
**Solutions**:
1. Start Docker/Podman daemon
2. Check if socket is accessible
3. Verify firewall rules for remote access
### Compose Adaptation Issues
**Symptoms**: Adapted compose file has errors
**Solutions**:
1. Validate compose file: `docker compose config`
2. Check runtime-specific requirements
3. Review crate documentation for compatibility matrix
### Performance Issues
**Symptoms**: Slow image pulls, container operations
**Solutions**:
1. Check network connectivity
2. Monitor disk space for image storage
3. Enable BuildKit: `export DOCKER_BUILDKIT=1`
4. Consider using image registry caching
## Integration Patterns
### Kubernetes Integration
For Kubernetes environments using Docker or CRI-compatible runtimes:
```rust
use runtime::detect_runtime;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Detect runtime as per container spec
let runtime = detect_runtime().await?;
// Use for image operations before pod deployment
runtime.pull("image:tag").await?;
Ok(())
}
```
### CI/CD Pipeline Integration
```rust
use runtime::{detect_runtime, RuntimeConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Auto-detect available runtime in CI environment
let runtime = detect_runtime().await?;
// Build image
runtime.build("/app", "my-image:latest").await?;
// Test container
runtime.run("my-image:latest", vec!["cargo", "test"]).await?;
// Push to registry
runtime.push("my-image:latest").await?;
Ok(())
}
```
### Multi-Environment Deployment
```rust
use runtime::{RuntimeConfig, RuntimeType};
fn get_config_for_environment(env: &str) -> RuntimeConfig {
match env {
"development" => {
// Local development: use detected runtime
RuntimeConfig::default()
}
"staging" => {
// Staging: connect to staging Docker host
RuntimeConfig {
socket_path: "tcp://docker.staging.internal:2376".to_string(),
..Default::default()
}
}
"production" => {
// Production: use production Docker host
RuntimeConfig {
socket_path: "tcp://docker.prod.internal:2376".to_string(),
..Default::default()
}
}
_ => RuntimeConfig::default(),
}
}
```
## Conclusion
The Runtime crate provides flexible, production-ready container runtime abstraction. Proper configuration, error handling, and security practices ensure reliable container operations across diverse environments.
For more information, see:
- [Runtime README.md](README.md) - API documentation
- [Examples](../../../examples/) - Complete examples
- [Integration Architecture](../../../docs/integration-architecture.md) - System integration patterns