lmrc-k3s 0.2.3

K3s management library for the LMRC Stack - manage K3s Kubernetes clusters via SSH
Documentation

lmrc-k3s

Part of the LMRC Stack - Infrastructure-as-Code toolkit for building production-ready Rust applications

Crates.io Documentation License: MIT OR Apache-2.0

A Rust library for managing K3s Kubernetes clusters via SSH.

Features

  • Simple API: Easy-to-use interface for cluster management
  • Declarative Reconciliation: Define desired cluster state and automatically add/remove workers
  • SSH-based: Manages remote nodes via SSH connections using the ssh-manager crate
  • Async/Await: Built on Tokio for efficient async operations
  • Flexible Configuration: Support for custom K3s versions, tokens, and disabled components
  • Private Network Support: Handles both public and private IP configurations for cloud deployments
  • Cluster Status Tracking: Query real-time cluster status from kubectl
  • Graceful Node Removal: Automatically drain nodes before removal
  • Idempotent Operations: Safe to run multiple times, won't reinstall if already present
  • Well Documented: Comprehensive API documentation with examples

Installation

Add this to your Cargo.toml:

[dependencies]
lmrc-k3s = "0.1"
tokio = { version = "1.0", features = ["full"] }

Quick Start

use lmrc_k3s::K3sManager;

#[tokio::main]
async fn main() -> Result<(), lmrc_k3s::K3sError> {
    // Create a manager with default settings
    let manager = K3sManager::builder()
        .token("my-secure-token")
        .build();

    // Install K3s on master node (force=false won't reinstall if already present)
    manager.install_master("192.168.1.10", None, false).await?;

    // Join worker nodes
    manager.join_worker("192.168.1.11", "192.168.1.10", None).await?;
    manager.join_worker("192.168.1.12", "192.168.1.10", None).await?;

    // Download kubeconfig
    manager.download_kubeconfig("192.168.1.10", "kubeconfig").await?;

    // Check cluster state
    if let Some(state) = manager.get_cluster_state() {
        println!("Cluster has {} ready workers", state.ready_workers_count());
    }

    Ok(())
}

Usage Examples

Basic Cluster Setup

use lmrc_k3s::K3sManager;

#[tokio::main]
async fn main() -> Result<(), lmrc_k3s::K3sError> {
    let manager = K3sManager::builder()
        .token("my-cluster-token")
        .build();

    // Install master (force=false won't reinstall if already present)
    manager.install_master("192.168.1.10", None, false).await?;

    // Join workers
    manager.join_worker("192.168.1.11", "192.168.1.10", None).await?;

    Ok(())
}

Custom K3s Version and Disabled Components

let manager = K3sManager::builder()
    .version("v1.28.5+k3s1")
    .token("my-secure-token")
    .disable(vec![
        "traefik".to_string(),
        "servicelb".to_string(),
    ])
    .build();

Private Network Setup (e.g., Hetzner Cloud)

// Install master with private IP for internal communication
manager.install_master(
    "203.0.113.10",      // Public IP for SSH access
    Some("10.0.0.10"),   // Private IP for cluster communication
    false                // Don't force reinstall
).await?;

// Join worker with private IP
manager.join_worker(
    "203.0.113.11",      // Worker public IP
    "10.0.0.10",         // Master private IP (for K3s API)
    Some("10.0.0.11")    // Worker private IP
).await?;

Check Cluster Status

// Check if K3s is installed
if manager.is_installed("192.168.1.10").await? {
    println!("K3s is installed");

    // Get node status
    let nodes = manager.get_nodes("192.168.1.10").await?;
    println!("Nodes: {}", nodes);
}

Uninstall K3s

// Uninstall from master
manager.uninstall("192.168.1.10", true).await?;

// Uninstall from worker
manager.uninstall("192.168.1.11", false).await?;

Declarative Cluster Reconciliation

The library supports declarative cluster management - define your desired state and let lmrc-k3s handle the rest!

use lmrc_k3s::{DesiredClusterConfig, K3sManager, WorkerConfig};

// Define desired state
let desired = DesiredClusterConfig {
    master_ip: "192.168.1.10".to_string(),
    master_private_ip: None,
    workers: vec![
        WorkerConfig::new("192.168.1.11".to_string()),
        WorkerConfig::new("192.168.1.12".to_string()),
        WorkerConfig::new("192.168.1.13".to_string()),
    ],
    k3s_version: "v1.28.5+k3s1".to_string(),
    token: "my-token".to_string(),
    disabled_components: vec!["traefik".to_string()],
};

// Reconcile - will add/remove workers to match desired state
manager.reconcile(&desired).await?;

// Later, scale up by adding to desired state
desired.workers.push(WorkerConfig::new("192.168.1.14".to_string()));
manager.reconcile(&desired).await?; // Adds worker .14

// Scale down by removing from desired state
desired.workers.retain(|w| w.ip != "192.168.1.13");
manager.reconcile(&desired).await?; // Removes worker .13 gracefully

Benefits:

  • Idempotent - Safe to run multiple times
  • Automatic diffing - Only makes necessary changes
  • Graceful removal - Drains nodes before deletion
  • State-aware - Won't reinstall if already present

Examples

The repository includes several complete examples:

Run examples with:

cargo run --example basic_setup
cargo run --example declarative_reconciliation

SSH Configuration

This crate uses the ssh-manager crate for SSH connections. Ensure you have:

  1. SSH access configured to your target nodes
  2. SSH keys set up (typically in ~/.ssh/)
  3. Root access on target nodes (K3s installation requires root)

Authentication Methods

Public Key Authentication (Default):

// Uses ~/.ssh/id_rsa by default
let manager = K3sManager::builder()
    .token("my-token")
    .build();

// Or specify a custom key path
let manager = K3sManager::builder()
    .token("my-token")
    .ssh_key_path("/path/to/custom/id_rsa")
    .build();

Password Authentication:

let manager = K3sManager::builder()
    .token("my-token")
    .ssh_username("root")
    .ssh_password("your-password")
    .build();

Custom SSH Username:

let manager = K3sManager::builder()
    .token("my-token")
    .ssh_username("admin")  // Default is "root"
    .build();

API Overview

K3sManager

The main struct for managing K3s clusters.

Methods

Cluster Setup:

  • builder() - Create a new builder for K3sManager
  • install_master(server_ip, private_ip, force) - Install K3s on a master node
  • join_worker(worker_ip, master_ip, worker_private_ip) - Join a worker to the cluster
  • download_kubeconfig(master_ip, output_path) - Download kubeconfig from master
  • uninstall(server_ip, is_master) - Uninstall K3s from a node

Declarative Management:

  • reconcile(desired) - Reconcile cluster to match desired configuration
  • plan_reconciliation(desired) - Create a reconciliation plan without applying
  • apply_reconciliation(desired, plan) - Apply a reconciliation plan
  • remove_worker(master_ip, worker_ip) - Gracefully remove a worker node

Status & Monitoring:

  • is_installed(server_ip) - Check if K3s is installed on a node
  • get_nodes(master_ip) - Get cluster node status
  • get_node_info_list(master_ip) - Get detailed node information list
  • get_cluster_state() - Get current cluster state with node information
  • refresh_cluster_state(master_ip) - Refresh cluster state from the master node

K3sManagerBuilder

Builder pattern for creating K3sManager instances.

Methods

Cluster Configuration:

  • version(version) - Set K3s version (default: "v1.28.5+k3s1")
  • token(token) - Set cluster token (required)
  • disable(components) - Set components to disable

SSH Configuration:

  • ssh_username(username) - Set SSH username (default: "root")
  • ssh_key_path(path) - Set SSH private key path (default: "~/.ssh/id_rsa")
  • ssh_password(password) - Set SSH password for password-based authentication

Build:

  • build() - Build the K3sManager instance

Requirements

  • Rust 1.70 or later
  • SSH access to target nodes
  • Root privileges on target nodes
  • Target nodes running a Linux distribution supported by K3s

License

Part of the LMRC Stack project. Licensed under either of:

at your option.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Acknowledgments

  • Built on top of the ssh-manager crate for SSH functionality
  • Uses K3s - Lightweight Kubernetes distribution

Resources