PnR: Configuration-Intention Driven Container Orchestration with Go’s Platform Abstraction
December 22, 2024

PnR: Configuration-Intention Driven Container Orchestration with Go’s Platform Abstraction

Have you ever wished that container orchestration could be more flexible than static dependency chains, yet simpler than Kubernetes? Meet PnR (Prompt and Respond) – A configuration-driven approach that leverages Go’s powerful platform abstractions to orchestrate containers based on actual readiness status rather than simple dependencies.


The power of abstraction in the Go platform

Before we delve into PnR, let’s understand why Go is particularly suitable for cross-platform container orchestration:

  1. Unified Docker API interface: Go’s Docker client library provides a consistent interface across Windows, Linux, and macOS via platform-specific socket connections:

    • Unix system use /var/run/docker.sock
    • Windows uses named pipes
    • this client.NewClientWithOpts() The function handles these differences automatically
  2. Native concurrency support: Go’s Goroutine and Channel can achieve efficient container monitoring:

    • Health checks for each container run simultaneously
    • Intent loop to coordinate multiple containers without blocking
    • Mutex-protected state updates prevent race conditions
  3. Cross-platform web processing: Go ahead net Packages abstract platform-specific networking details:

    • TCP health checks work the same way on different operating systems
    • HTTP client handles platform-specific DNS resolution
    • Port binding uses consistent syntax regardless of platform


Core Concept: Configuration is better than code

PnR orchestrates containers through three key components:

  1. Domain configuration (JSON)
  2. Platform-independent health checks
  3. Runtime state management

Let’s look at this through a typical web stack: MongoDB, API server, and web client.


Domain configuration structure

{
    "name": "dev_stack",
    "cpuxs": {
        "stack_startup": {
            "design_chunks": [
                {
                    "name": "mongodb",
                    "gatekeeper": {
                        "system_ready": {
                            "prompt": "Is system ready?",
                            "response": ["yes"],
                            "tv": "Y"
                        }
                    },
                    "flowout": {
                        "mongodb_ready": {
                            "prompt": "Is MongoDB ready?",
                            "response": ["yes"],
                            "tv": "Y"
                        }
                    },
                    "health_check": {
                        "type": "tcp",
                        "port_key": "27017",
                        "timeout_seconds": 2,
                        "status_mapping": {
                            "success": {
                                "key": "mongodb_ready",
                                "response": ["yes"],
                                "tv": "Y"
                            },
                            "failure": {
                                "key": "mongodb_ready",
                                "response": ["no"],
                                "tv": "N"
                            }
                        }
                    },
                    "container": {
                        "name": "pnr_mongodb",
                        "image": "mongo:latest",
                        "ports": {
                            "27017": "27017"
                        }
                    }
                }
            ]
        }
    }
}
Enter full screen mode

Exit full screen mode


Platform-agnostic container management

At the heart of PnR is its platform-agnostic container management. Here’s how it works:

func (il *ContainerIntentionLoop) Execute() error {
    // Create platform-specific network
    _, err := il.dockerClient.NetworkCreate(il.ctx, "pnr_network", types.NetworkCreate{})
    if err != nil {
        return fmt.Errorf("failed to create network: %v", err)
    }

    for {
        // Update runtime state
        if err := il.updateRTStateFromRuntime(); err != nil {
            return err
        }

        allCompleted := true
        anyExecuting := false

        // Process each container
        for i := range il.cpux.DesignChunks {
            chunk := &il.cpux.DesignChunks[i]

            // Container state machine
            switch chunk.Status {
            case "completed":
                continue
            case "executing":
                anyExecuting = true
                allCompleted = false
                if il.checkChunkCompletion(chunk) {
                    chunk.Status = "completed"
                }
            case "", "ready":
                allCompleted = false
                if il.checkGatekeeper(chunk) {
                    if err := il.startContainer(chunk); err != nil {
                        return err
                    }
                    chunk.Status = "executing"
                    anyExecuting = true
                }
            }
        }

        // Check termination conditions
        if allCompleted {
            return nil
        }
        if !anyExecuting && !allCompleted {
            return fmt.Errorf("no progress possible - execution stalled")
        }

        time.Sleep(5 * time.Second)
    }
}
Enter full screen mode

Exit full screen mode


Cross-platform health checks

PnR uses Go’s standard library to implement platform-independent health checks:

func (il *ContainerIntentionLoop) checkChunkCompletion(chunk *DesignChunk) bool {
    // Platform-agnostic container status check
    isRunning, err := il.isContainerRunning(chunk.Container.Name)
    if !isRunning {
        il.updateChunkStatus(chunk, false)
        return false
    }

    // Health check based on configuration
    status := false
    switch chunk.HealthCheck.Type {
    case "tcp":
        addr := fmt.Sprintf("localhost:%s", chunk.Container.Ports[chunk.HealthCheck.PortKey])
        conn, err := net.DialTimeout("tcp", addr, timeout)
        if err == nil {
            conn.Close()
            status = true
        }

    case "http":
        url := fmt.Sprintf("http://localhost:%s%s", 
            chunk.Container.Ports[chunk.HealthCheck.PortKey],
            chunk.HealthCheck.Path)
        resp, err := client.Get(url)
        if err == nil {
            status = (resp.StatusCode == chunk.HealthCheck.ExpectedCode)
        }
    }

    il.updateChunkStatus(chunk, status)
    return status
}
Enter full screen mode

Exit full screen mode


Main advantages

  1. True cross-platform support: Works the same on Windows, Linux, and macOS
  2. Configure driver: All orchestration logic in domain.json
  3. Container-agnostic: No modifications required to PnR specific containers
  4. Flexible health checks:TCP, HTTP, and can be extended to other protocols
  5. status visibility: Clear status updates via runtime files
  6. Concurrent execution: Efficient parallel container management


getting Started


The complete code is available here: jitub


Prerequisites

  1. Install Go (1.19 or higher):

  2. Install Docker


Project structure

pnr-orchestrator/
├── main.go
├── containers.go
├── config/
│   └── domain.json
└── runtime/          # Created automatically
Enter full screen mode

Exit full screen mode


Install

# Create project directory
mkdir pnr-orchestrator
cd pnr-orchestrator

# Initialize Go module
go mod init pnr-orchestrator

# Install dependencies
go get github.com/docker/docker/client
go get github.com/docker/docker/api/types
go get github.com/docker/go-connections/nat
Enter full screen mode

Exit full screen mode


Build and run

# Option 1: Direct run
go run main.go containers.go

# Option 2: Build and run separately
go build
./pnr-orchestrator   # Unix/Linux/Mac
pnr-orchestrator.exe # Windows
Enter full screen mode

Exit full screen mode


Beyond mere dependencies

Traditional Docker combination:

api:
  depends_on:
    - mongodb
Enter full screen mode

Exit full screen mode

PnR’s smart orchestration:

"gatekeeper": {
    "mongodb_ready": {
        "prompt": "Is MongoDB ready?",
        "response": ["yes"],
        "tv": "Y"
    }
}
Enter full screen mode

Exit full screen mode

What’s the key difference? PnR ensures actual service readiness across any platform, not just container startup.


Next step

  1. Explore more complex orchestration patterns
  2. Add custom health check type
  3. Implement graceful shutdown and cleanup
  4. Create platform-specific optimization tips

PnR demonstrates how Go’s powerful platform abstractions can create powerful cross-platform container orchestration tools without sacrificing simplicity or functionality.

If you’d like to see more examples or have questions about platform-specific implementations, let me know in the comments!

2024-12-22 13:14:17

Leave a Reply

Your email address will not be published. Required fields are marked *