Onboard Your Project to Raftt in 10 minutes
What is Raftt?
Raftt helps you have fast dev iterations. With Raftt it takes just seconds to see your code changes applied in your dev env on your Kubernetes cluster.
Raftt can work in two different modes -
- Connect-mode - to be used if you already have a dev env deployed on K8s and wish to enhance its dev experience using Raftt.
- Orchestration-mode - to be used if you want Raftt to be responsible for spawning and destroying ephemeral dev envs that also have Raftt's dev experience.
If you wish to experience developing with Raftt using a sample project, you can try one of our tutorials - the connect-mode tutorial or the orchestration-mode one. Going through the tutorials isn't required before using this guide.
Overview
This guide explains how to onboard your existing project to Raftt in connect-mode and includes the following steps -
- Installing Raftt's CLI tool and IDE Plugin
- Connecting Raftt to your dev env
- Inspecting the env using Raftt's IDE plugin
- Converting one of your workload's into dev-mode to allow fast iterations
- Configure run/debug configurations to allow interactive debugging of the workload's main process.
Prerequisites
This guide shows how to enhance the development experience of existing K8s envs using Raftt in connect-mode. To successfully go through this guide you first need to meet the following prerequisites -
- Install Raftt's CLI tool and IDE plugin (see here for instructions).
- Clone the repo of the workload you wish to develop to your local machine.
- Deploy the environment you wish to develop as a namespace in a K8s cluster, local or remote.
- Install
kubectl
locally with sufficient permissions (see here for installation guides).- If your Kube-context doesn't have enough permissions, you can use tools like minikube to create a local Kubernetes cluster to deploy your env, and then connect with Raftt.
Connecting to the Env
In this section, we'll add Raftt's environment controller to the namespace that contains your dev environment. Take the following steps -
- Make sure your current working directory is the repo of the service you wish to develop.
- Make sure your Kubernetes context is correct
kubectl config get-contexts
- Setup Raftt's initial configuration
raftt setup --direct
This command creates Raftt's basic configuration. The --direct
flag determines that you're working in connect-mode. The command creates two files in your repo root -
raftt.yml
- raftt's basic configuration fileenv.raftt
- the file that contains the definition of all the changes to resources when entering dev-mode (more on that later). For now it's an almost empty template that will be filled later.
- Connect to your dev env
raftt connect <NAMESPACE>
You will be prompted to log in to Raftt using one of several third-party services (Google, GitHub, etc.).
This command adds Raftt's env controller - a deployment called sandcastle
, to the namespace. The environment controller now waits for Raftt commands.
You can now inspect your workloads using raftt status
, raftt sh
and raftt logs
commands.
Env inspection in the IDE
Once connected to the environment you can inspect it and execute commands from your IDE using Raftt's plugin.
- JetBrains IDEs
- VS Code
The Raftt plugin is accessible from the navigation bar at the bottom of the IDE window. You can see the different workloads and their status, and use the different action buttons to execute Raftt commands.
The Raftt VS Code Extension is accessible from the activity bar at the left of the IDE window. You can see the different workloads and their status. You can execute different Raftt commands from the workloads context menu (after right-clicking) or from the command palette.
Devifying the First Workload
The raftt dev
command
This command gets resources into dev-mode, or "devifies" them. Devifying a resource means it is redeployed with modifications that make it much easier to develop. Try to run
raftt dev <workload_name>
and see that it's now in dev-mode, so you can stop and restart the main process without taking the whole workload down - use
raftt stop <workload_name>
and
raftt restart <workload_name>
respectively.
In the next sections we'll configure additional changes that will help accelerating your dev velocity.
What is a .raftt
file
The .raftt
file defines how Raftt will convert resources into dev-mode (using raftt dev
), or rebuild them (using raftt rebuild
). It is written in Starlark - a Python-like scripting language.
The namespace_resources()
function
There are several methods for importing the initial resource definitions. The most useful one for connect-mode is namespace_resources()
- a function that returns an object containing the definitions of the resources currently deployed in your namespace.
This line was already added to your env.raftt
file when you ran raftt setup --direct
resources = namespace_resources()
Keep the call to deploy(resources)
at the end of your .raftt
file - modifications to the resources
object won't be applied if there's no later call to deploy(resources)
.
Mounting the source code
To allow hot reloading, the source code needs to be mounted (and hence continually synced) into the workload.
For example, to mount the code located in the src/payments
dir in your repo to the /src
dir in your payments
deployment add the following line to your env.raftt file -
resources.deployments["payments"].mount(repo_volume().subpath("src/payments"), "/src")
For interpreted languages this (and raftt restart
) is enough for hot reloading. For compiled languages, you'll need to configure how to compile your code.
Compiling your code
For compiled languages, you'll want to compile the binary before restarting the main process. The compilation process is performed on one of your env's workloads. In some cases, you can compile on the workload on which the code eventually runs, but it may not be possible for many reasons. In such a case, you'd want to add an additional workload dedicated for compiling the code.
To do so, define the builder workload in your .raftt
file -
# Import the definitions from a pre-defined yml (see below for more details)
builder_resources = k8s_manifests("./k8s_manifests/builder.yml")
# Fetch the builder deployment from the imported Resources object
builder = builder_resources.deployments["builder"]
# Mount the repo to the container root
builder.mount(repo_volume(), "/src")
# Mark builder to be deploy upon connection to the env
deploy_on_connect(builder)
The specification of builder.yml
depends on the language used and on the specific requirements for compiling your code. Here are a few examples for builder.yml files that should work in most cases -
- Go
- Java
- Node.js
- .NET core
apiVersion: apps/v1
kind: Deployment
metadata:
name: builder
spec:
selector:
matchLabels:
app: builder
template:
metadata:
labels:
app: builder
spec:
containers:
- name: builder
image: golang:latest
apiVersion: apps/v1
kind: Deployment
metadata:
name: builder
spec:
selector:
matchLabels:
app: builder
template:
metadata:
labels:
app: builder
spec:
containers:
- name: builder
image: openjdk:latest
apiVersion: apps/v1
kind: Deployment
metadata:
name: builder
spec:
selector:
matchLabels:
app: builder
template:
metadata:
labels:
app: builder
spec:
containers:
- name: builder
image: node:latest
apiVersion: apps/v1
kind: Deployment
metadata:
name: builder
spec:
selector:
matchLabels:
app: builder
template:
metadata:
labels:
app: builder
spec:
containers:
- name: builder
image: mcr.microsoft.com/dotnet/sdk:latest
At the end of the compilation the artifacts must be located in the workload that runs them. The best way to do it is to have a shared volume between the builder and the workload. See here for instructions on how to do it.
After adding the workload in which you can compile your code, you need to configure when and how to trigger the compilation. There are several possible ways to do it, we'll describe three of them -
- Trigger the build directly using
raftt sh builder -- <build_command>
- Configure automatic build on any source code change (see below)
- Configure a pre-launch task in your IDE to run before every run/debug session (see below)
After configuring any of these methods, you can enjoy fast iterations from code change to seeing the change in the cluster, without rebuilding and redeploying any images.
File watching hooks
Raftt lets you define file watching hooks that trigger the execution of predefined actions on predefined workloads. Common use cases of such hooks are -
- Automatic compilation on any code change
- Automatically installing the dependencies on any change to the dependencies file
The following snippet defines a hook for each use case -
register_hook(
on=events.OnFileChanged(
workload=payments,
patterns="/src/**/*.ts"
),
do=actions.CMD(
workload=builder,
cmd=["npm", "build"]
)
)
register_hook(
on=events.OnFileChanged(
workload=payments,
patterns="/src/**/package.json"
),
do=actions.CMD(
workload=payments,
cmd=["npm", "install"]
)
)
Debug/run configurations
Raftt lets you interactively debug the workloads in your cluster. It currently supports debugging Python, Go, JavaScript (including TypeScript) and Java-based languages using VS Code and JetBrains IDEs.
Raftt debug/run configuration are almost identical to those used for local debugging. To get more information on creating these configurations, see our debugging docs.
Pre-launch tasks
For compiled languages, you'll want to rebuild the binary before launching a run/debug session. To do this you can define a task that will be executed before re-launching the main process. This is defined in the IDE.
- JetBrains IDEs
- VS Code
Assuming Raftt's plugin is already installed, you can add a new before launch
task of the type Run on Raftt workload
in which you can define what build command to run and on which workload.
The command can run on any workload of your choice, not just on the one running the binary.
Use VS Code's builtin prelaunch task mechanism. Define the task in the tasks.json
file to run a command on any workload using raftt sh
and than assign it to the relevant run/debug configuration(s) in your launch.json
file.
Port Mapping
In some cases, when developing, you'd want to map a remote port to localhost. This can be defined in the .raftt
file and the mapping will occur once the workload is in dev-mode.
For example to map port 27017 from your mongodb
StatefulSet to your local port 27017, add the following line to your .raftt
file -
resources.statefulsets["mongodb"].map_port(local=27017, remote=27017)
Conclusion and Next Steps
In this guide you went through configuring Raftt to develop one of your workloads. You can now enjoy dev iterations that take seconds instead of minutes, or much more, and enjoy interactive debugging directly in your cluster.
Now you can onboard the rest of your environment to have the great Raftt dev experience for all of your workloads.
You're welcome to join our Slack community or contact us for any further questions and enquiries.