Introduction

Welcome to the documentation of the project RIK, we hope you will enjoy your journey here. In case of mistakes or typos, please open an issue or a pull request on the GitHub repository.

Getting started

Prerequisites

Our worker component, Riklet, is currently only available for Linux systems with a x86_64 architecture.

Install packages required to build the project:

  • Ubuntu: sudo apt update && sudo apt install libssl-dev protobuf-compiler
  • Fedora: sudo dnf update && sudo dnf install openssl-devel protobuf-compiler protobuf-devel

Clone the project

Start by cloning the project using Git:

git clone https://github.com/rik-org/rik.git

Start a cluster

Using Docker

docker compose up

Build from source

Be aware that each component of the project is a separate binary, and that you need to execute them in a specific order.

Build all components of the project

cargo build --release

Start the scheduler in a terminal

cargo run --release --bin scheduler

Start the controller in a terminal

DATABASE_LOCATION=~/.rik/data cargo run --release --bin controller

Start the worker in a terminal

sudo ./target/release/riklet

If you experience any issue, please refer to the troubleshooting, if you can't find a solution, please open an issue.

Create your first workload

Create a definition of your workload

Create a workload using example in examples/workload-1.json:

# Create an alpine container workload
RIKCONFIG=docs/src/examples/config.json cargo run \
  --bin rikctl -- create workload \
  --file docs/src/examples/workloads/workload-1.json

# The ID of the workload is returned, it will be useful next
# Workload alpine has been successfully created with ID : "0e4c1da4-0277-4088-9f37-8f445cbe8e46"

Deploy an instance

Based on your workload ID you can now deploy an instance:

# Please replace the following value with the ID of your workload
export WORKLOAD_ID=0e4c1da4-0277-4088-9f37-8f445cbe8e46

RIKCONFIG=docs/src/examples/config.json cargo run \
  --bin rikctl -- create instance \
  --workload-id ${WORKLOAD_ID}

Check your instance

You should now see your instance running:

RIKCONFIG=examples/config.json cargo run \
  --bin rikctl -- get instances

Configuration

You can configure a remote cluster by setting the RIKCONFIG environment variable to the path of a configuration file. Here is an example of configuration:

{
  "cluster": {
    "name": "default",
    "server": "http://127.0.0.1:5000"
  }
}

Workloads

A workload is a definition of a piece of software that should be run on a cluster. It can be seen as a collection of containers, VM and other resources that are required to run a software. Defining a workload won't deploy an instance immediately, but it will be used as a definition to deploy an instance.

The implementation only support JSON format, but the API is designed to be extensible to support other formats in the future. In case you expect to have a specific format, please open an issue to discuss it.

RIK supports two types of workload:

  • Pod: A pod is a collection of containers that should be run together. As you might imagine in Kubernetes, for each instance, they will be packed together and share the same resource limits. They are the most basic workload type, and they are used to run a single application.
  • Function1: A function is a definition for a microVM that should be running in a more isolated way than a pod. This kind of workload supports network capabilities, it means it can be exposed easily on the network.

Lifecycle

Workloads have a common lifecycle which goes through various states. Each time the state is updated at any step of the lifecycle, the scheduler and the controller are aware of it and will act accordingly. In the case a failure is detected, the controller won't try to recover the workload, but it will give the error message to the user.

sequenceDiagram
    actor Bob
    participant Primary as RIK Primary Node
    participant Scheduler as RIK Scheduler
    participant Secondary as RIK Secondary Node
    Bob-->>Primary: Request new instance <br/>of a workload
    note over Primary: Status: PENDING
    Primary-->>Scheduler: Request Schedule
    note over Scheduler: Status: PENDING
    Scheduler-->>Secondary: Determine appropriate node and<br/> order scheduler
    note over Secondary: Status: CREATING
    Secondary-->>Secondary: Download and run <br/> necessary components
    note over Secondary: Status: RUNNING
    Secondary-->>Scheduler: Return instance running OK
    Scheduler-->>Primary: Return instance running OK
    Primary-->>Bob: Return instance running OK

JSON Schema Reference

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "$id": "https://rik-org.github.io/rik/workloads/schema-v1.json",
    "title": "Workload-v1",
    "description": "Reference for workload in RIK with apiVersion v1",
    "type": "object",
    "properties": {
        "apiVersion": {
          "description": "The version associated to the current schema",
          "type": "string",
          "default": "v1"
        },
        "kind": {
          "description": "The kind of the resource",
          "type": "string",
          "enum": [ "Pod", "Function" ]
        },
        "name": {
          "description": "Unique name of the workload",
          "type": "string"
        },
        "replicas": {
          "description": "Number of replicas expected (won't be deployed)",
          "type": "integer",
          "minimum": 1
        },
        "spec": {
          "description": "Full specification of the workload",
          "type": "object",
          "required": [ "containers", "function" ],
          "properties": {
            "containers": {
              "description": "List of containers to be deployed",
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Name of the container"
                  },
                  "image": {
                    "type": "string",
                    "description": "Image to be used for the container"
                  },
                }
              }
            },
            "function": {
              "description": "Function to be deployed",
              "type": "object",
              "properties": {
                "execution": {
                  "type": "object",
                  "properties": {
                    "rootfs": {
                      "type": "string",
                      "description": "Rootfs to be used for the container, must a be URL that can be publicly accesed"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "required": [ "apiVersion", "kind", "name", "replicas", "spec" ]
  }
1
This workload will probably be renamed in the future, as it is not
strictly related to functions.

Troubleshooting

cargo build fails because cannot build openssl-sys

This is due to missing packages in your system, install libssl-dev to fix this.

  • Ubuntu: sudo apt update && sudo apt install libssl-dev protobuf-compiler
  • Fedora: sudo dnf update && sudo dnf install -y openssl-devel protobuf-compiler protobuf-devel

protoc failed: Explicit 'optional' labels are disallowed in the Proto3 Syntax

This is due to the version of protoc you are using, you need to use version 3.14 or later.

controller failed to run: panic, Permission denied

Controller component tries to create a folder in /var/lib/rik/data to store your cluster data. You can either run the controller as root or change the saved directory by setting DATABASE_LOCATION to another folder location.

Controller

Configuration

Environment variableDefaultDescription
DATABASE_LOCATION/var/lib/rik/data/Database data location
SCHEDULER_URLhttp://localhost:4996Host location of the scheduler
PORT5000Port to listen on

Database structure

Workloads:

  • element_type: /workload

  • element_id: /workload/${WORKLOAD_KIND}/${NAMESPACE}/${WORKLOAD_NAME}

    • WORKLOAD_KIND: One ofpods, function
    • NAMESPACE: Static default
    • WORKLOAD_NAME: Dynamically defined

Instances:

  • element_type: /instance

  • element_id: /instance/${WORKLOAD_KIND}/${NAMESPACE}/${INSTANCE_NAME}

    • WORKLOAD_KIND: One ofpods, function
    • NAMESPACE: Static default
    • INSTANCE_NAME: Dynamically defined

Network

This project has network features, it's state is unstable and should be used with caution as it is managing your network interfaces and routing. We are doing our best not to break your system's network!

Workloads

Current workloads cannot be configured with network implementation, however Function workload implement a first version of network configuration which can't be configured yet.

Riklet SDN

This component onboard a network component which will manage network exposure and routing. Depending on the workload, it will be configured to use a specific network implementation. For now, only Function workload have an implementation of network configuration. This implementation is based on iptables and rtnetlink.

Function network implementation

This network feature allows you to forward traffic from a specific port to a Function instance port. We achieve this using iptables, a widely used linux tool for managing network traffic. The translation of IP and port is targetting a TAP interface on the machine that is communicating with the Function instance (microVM).


┌──────────────────────────────────────────────────────────────────┐
│                      Host Machine (riklet)                       │
│                                                                  │
│                                                                  │
│  ┌─────────────────────────────┐   ┌─────────────────────────┐   │
│  │Iptables                     │   │    Function Instance    │   │
│  │                             │   │                         │   │
│  │ ┌─────────────────────────┐ │   │                         │   │
│  │ │APPLY NAT ON             │ │   │┌───────────────────────┐│   │
│  │ │host:${port}             │ │   ││      Guest_veth       ││   │
│  │ │                         │ │   ││                       ││   │
│  │ │TO                       │─┼┐  │└───────────────────────┘│   │
│  │ │host_tap:${service_port} │ ││  │            ▲            │   │
│  │ │                         │ ││  └────────────┼────────────┘   │
│  │ └─────────────────────────┘ ││               │                │
│  │              ▲              ││               │                │
│  └──────────────┼──────────────┘│   ┌───────────────────────┐    │
│                 │               │   │       Host_tap        │    │
│                 │               └──▶│                       │    │
│                 │                   └───────────────────────┘    │
│     ┌───────────────────────┐                                    │
│     │Host Ethernet Interface│                                    │
│     │                       │                                    │
│     └───────────────────────┘                                    │
│                 ▲                                                │
└─────────────────┼────────────────────────────────────────────────┘
                  │

This is what the network configuration looks like when you deploy a Function instance with a port mapping, please not it is very specific to Function. The host_tap interface is created by the riklet and is used to communicate with the Function instance. The Guest_veth interface is created by the firecracker microVM and is used to communicate with the host_tap interface. The host_tap is connecteed to the internet and is not restricted in bandwidth.

Iptables

Riklet will use a custom chain called RIKLET on the table nat to do DNAT (Destination NAT), it matches two use cases:

  • Local processes: when another workload wants to communicate with a Function instance
  • Internet: when the workload needs to be exposed externally on the worker node
    .─────────────────.               .─────────────────.
 ,─'                   '─.         ,─'                   '─.
(     Local processes     )       (        Internet         )
 `──.                 _.─'         `──.                 _.─'
     `───────────────'                 `───────────────'
             │                                 │
             │                                 │
             │                                 │
             ▼                                 ▼
  ┌────────────────────┐            ┌────────────────────┐
  │    OUTPUT (nat)    │            │  PREROUTING (nat)  │
  └────────────────────┘            └────────────────────┘
             │                                 │
             │                                 │
             │      ┌──────────────────────────┤
             │      │                          │
             ▼      ▼                          ▼
  ┌────────────────────┐            ┌────────────────────┐
  │    RIKLET (nat)    │───────────▶│      FORWARD       │
  └────────────────────┘            └────────────────────┘
                                                │
                                                │
                                                │
                                                │
                                                ▼
                                     ┌────────────────────┐
                                     │    POSTROUTING     │
                                     └────────────────────┘
                                                │
                                                │
                                                │
                                                ▼