Apache YuniKorn Core

repository·master·Indexed 21 days ago

https://github.com/apache/yunikorn-core

The central scheduling logic for Apache YuniKorn, a universal, lightweight resource scheduler for mixed workloads across container orchestrators. This repository contains the 'scheduler brain' that makes placement decisions based on scheduling policies and is agnostic to the underlying resource manager. It includes tools like queueconfigchecker for configuration validation, a reference SimpleScheduler implementation of the si.SchedulerServer interface, and gRPC utilities for managing resource manager registrations and allocation updates.

Tokens
2.8K
Snippets
7
Records
14
Agent score
76%

What's inside apache-yunikorn-core

  1. Overview of Apache YuniKorn

    master

    Apache YuniKorn is a lightweight, universal resource scheduler designed for container orchestrator systems. It enables fine-grained resource sharing for mixed workloads (stateless batch and stateful services) in large-scale, multi-tenant, and cloud-native environments.

    Key characteristics:

    • Unified Experience: Provides a cross-platform scheduling experience.
    • Extensible Architecture: While it supports Kubernetes (via a shim layer), the core design allows for different shim layers to adopt various Resource Managers, such as Apache Hadoop YARN.
  2. Understand the YuniKorn project structure

    master

    The YuniKorn ecosystem is distributed across several repositories. The yunikorn-core repository contains the 'scheduler brain' which is agnostic to the underlying resource manager and makes placement decisions (e.g., allocating container X on node Y) based on built-in scheduling policies.

    Core repositories:

    • yunikorn-core: The central scheduling logic.
    • yunikorn-k8shim: The adapter specifically for Kubernetes.
    • yunikorn-scheduler-interface: The common scheduling interface used across implementations.
    • yunikorn-web: The web-based user interface.
    • yunikorn-release: Manages releases and includes Helm charts.
    • yunikorn-site: Source code for the official website.
  3. Use the Scheduler Client to interact with YuniKorn via gRPC

    master

    The schedulerclient provides a way to interact with the YuniKorn scheduler using the yunikorn-scheduler-interface. It establishes a gRPC connection to the scheduler (defaulting to localhost:3333) and uses the si.SchedulerClient to perform operations like registering a resource manager and streaming allocation updates.

    To use the client, you must:

    1. Establish a gRPC connection using grpc.NewClient.
    2. Initialize the client using si.NewSchedulerClient(conn).
    3. Call RegisterResourceManager to register the client with the scheduler.
    4. Use UpdateAllocation to open a bidirectional stream for sending AllocationRequest objects and receiving responses.
    import (
    	"context"
    	"time"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    	"github.com/apache/yunikorn-scheduler-interface/lib/go/si"
    )
    
    // ... setup connection
    conn, err := grpc.NewClient("localhost:3333", grpc.WithTransportCredentials(insecure.NewCredentials()))
    c := si.NewSchedulerClient(conn)
    
    ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
    defer cancel()
    
    // Register the resource manager
    _, err = c.RegisterResourceManager(ctx, &si.RegisterResourceManagerRequest{})
    
    // Start streaming allocation updates
    stream, err := c.UpdateAllocation(ctx)
    // Use stream.Send(&si.AllocationRequest{}) to send requests
    // Use stream.Recv() to receive responses
  4. Use NonBlockingGRPCServer to manage gRPC endpoints

    master

    The NonBlockingGRPCServer interface provides a way to start and manage gRPC servers without blocking the main execution thread. This is useful for initializing scheduler services that need to run in the background while the rest of the application continues its setup.

    To use it, call NewNonBlockingGRPCServer() to get an instance, then use Start(endpoint, ss) to launch the server. The endpoint must follow a specific URI format (see ParseEndpoint). The ss parameter is the si.SchedulerServer implementation you wish to register.

    Lifecycle management methods:

    • Start(endpoint string, ss si.SchedulerServer): Launches the server in a background goroutine.
    • Wait(): Blocks the caller until the server has fully stopped.
    • Stop(): Performs a graceful shutdown of the gRPC server.
    • ForceStop(): Performs an immediate, forceful shutdown.
    import (
    	"github.com/apache/yunikorn-core/pkg/common"
    	"github.com/apache/yunikorn-scheduler-interface/lib/go/si"
    )
    
    // Assuming 'myScheduler' implements si.SchedulerServer
    server := common.NewNonBlockingGRPCServer()
    
    // Start the server on a TCP endpoint
    server.Start("tcp://0.0.0.0:8080", myScheduler)
    
    // ... do other work ...
    
    // Gracefully shut down
    server.Stop()
    server.Wait()
  5. Stream allocation updates via si.SchedulerClient.UpdateAllocation

    master

    The UpdateAllocation method initiates a bidirectional gRPC stream used to communicate allocation requests and responses between the client and the scheduler.

    Signature: UpdateAllocation(ctx context.Context, opts ...grpc.CallOption) (Scheduler_UpdateAllocationClient, error)

    Once the stream is opened, you can use:

    • stream.Send(*si.AllocationRequest) to push allocation requests to the scheduler.
    • stream.Recv() to receive responses from the scheduler. The stream ends when io.EOF is returned by Recv().
  6. Run the SimpleScheduler gRPC Server

    master

    The SimpleScheduler can be started as a gRPC server using the Run method. You must provide a network endpoint (e.g., :5000) where the server will listen for incoming connections from resource managers.

    Internally, it uses common.NewNonBlockingGRPCServer() to manage the lifecycle of the gRPC server and waits for it to terminate.

    scheduler := &SimpleScheduler{}
    scheduler.Run(":5000")
  7. Parse endpoint format for gRPC

    master

    The ParseEndpoint function validates and splits a connection string into a protocol and an address. Supported protocols are unix:// and tcp://.

    Format: {protocol}://{address}

    Examples:

    • tcp://127.0.0.1:8080 returns proto: "tcp", addr: "127.0.0.1:8080"
    • unix:///tmp/yunikorn.sock returns proto: "unix", addr: "/tmp/yunikorn.sock" (Note: the implementation prepends a / to the address if the protocol is unix).
  8. Register a Resource Manager with si.SchedulerClient

    master

    The RegisterResourceManager method is used to register a resource manager with the YuniKorn scheduler. It requires a context.Context and a *si.RegisterResourceManagerRequest object.

    Signature: RegisterResourceManager(ctx context.Context, in *RegisterResourceManagerRequest, opts ...grpc.CallOption) (*RegisterResourceManagerResponse, error)

  9. Configure gRPC unary interceptors for logging

    master

    The package provides a built-in unary interceptor withServerUnaryInterceptor() that can be used when configuring a gRPC server. This interceptor automatically logs:

    • The RPC method being called.
    • The request payload.
    • The response payload.
    • Any errors encountered during the RPC execution.

    Logs are emitted using the log.RPC logger.

  10. Implement the Scheduler Interface with SimpleScheduler

    master

    The SimpleScheduler is a reference implementation of the si.SchedulerServer interface. It is used to demonstrate how to handle gRPC streams for managing resource managers, allocations, applications, and nodes within the YuniKorn ecosystem.

    To implement a custom scheduler, you must satisfy the methods defined in the si.SchedulerServer interface, which include:

    • RegisterResourceManager: Handles the registration of a resource manager.
    • UpdateAllocation: Manages a long-lived stream for allocation updates.
    • UpdateApplication: Manages a long-lived stream for application updates.
    • UpdateNode: Manages a long-lived stream for node updates.

    Each stream-based method (UpdateAllocation, UpdateApplication, UpdateNode) follows a pattern of receiving data from the stream via conn.Recv(), processing it, and sending a response back via conn.Send() until the context is cancelled or an io.EOF is received.

    type SimpleScheduler struct {
    	si.UnimplementedSchedulerServer
    }
    
    // Example method implementation for handling allocation updates
    func (scheduler *SimpleScheduler) UpdateAllocation(conn si.Scheduler_UpdateAllocationServer) error {
        ctx := conn.Context()
        for {
            select {
            case <-ctx.Done():
                return ctx.Err()
            default:
            }
    
            _, err := conn.Recv()
            if err == io.EOF {
                return nil
            }
            if err != nil {
                return err
            }
    
            resp := si.AllocationResponse{}
            if err := conn.Send(&resp); err != nil {
                return err
            }
        }
    }