DEV Community

Dan for Akkoro

Posted on

AssemblyLift alpha latest: easy API Gateway for Kubernetes functions, Ruby language support πŸ’Ž

Hello again y'all! πŸ‘‹πŸ»

Since our last post introducing the AssemblyLift v0.4-alpha series, we've had two new alpha releases which introduced some major additions & improvements!

In this post we'll talk about each change and what it means, and then we'll walk through a short example demonstrating the new features. πŸ™‚

Revised TOML manifest spec

Manifests (assemblylift.toml and service.toml) now use arrays to describe most entries which used to be written using tables.

For example, the [services] table in assemblylift.toml is now the [[services]] array.

Why?

The primary data structures in TOML are arrays and tables.

Previous releases leaned heavily on tables. A typical assemblylift.toml for example might be:

[project]
name = "my-project"

[services]
my_service = { name = "my-service" }
another = { name = "another-service" }
Enter fullscreen mode Exit fullscreen mode

Both [project] and [services] are tables. The my_service key also points to a table. Expressing services this way (as well as functions, authorizers, etc.) is a bit verbose considering that these things are also named.

By comparison, using arrays to add multiple services to assemblylift.toml for example looks like:

[project]
name = "project"

[[services]]
name = "service1"

[[services]]
name = "service2"
Enter fullscreen mode Exit fullscreen mode

Which avoids redundant information (the table key) and becomes easier to read.

Ruby language support

AssemblyLift is now bilingual! Functions can now be written in Ruby in addition to Rust.

To declare a function a Ruby function, set the language parameter.

For example:

[[api.functions]]
name     = "my-ruby-func"
language = "ruby"
http     = { verb = "GET", path = "/my-service/my-ruby-func" }
Enter fullscreen mode Exit fullscreen mode

A Ruby function requires handler.rb to be the entrypoint. AssemblyLift ships a custom build of the Ruby runtime which includes a module for interop with the AssemblyLift platform.

We'll explore this in more detail in the demo section below!

Why?

We ❀️ Ruby! Most commonly associated with the Rails framework, Ruby is a powerful & mature scripting language suitable for many purposes.

Ruby recently gained support for WebAssembly, and we almost immediately went about integrating it into AssemblyLift! πŸ’¨

While you can't (yet?) lift your existing Rails app to a Function, the Ruby language is familiar to many devs. Ruby could especially be preferable in cases where performance is less of a concern, such as automation tasks for example.

Support for Gloo Edge API Gateway

Another feature addition we're very excited about is HTTP API support for the new Kubernetes provider. The previous alpha release could deploy functions to a K8s cluster, but there was no practical way yet to invoke them!

This release will automatically install Gloo Edge on the target cluster. AssemblyLift will deploy a Gateway, and set up routing for any functions running in a K8s service which define an HTTP API.

Why?

An API Gateway is central to the AssemblyLift architecture. AWS Lambda has a natural pairing with Amazon API Gateway, but Kubernetes of course is open-ended.

For this first iteration, Gloo Edge was chosen as it is open-source, easy to automate, and very full-featured. Being itself based on Envoy Proxy we can eventually explore its support for WASM traffic filters in Gloo Edge Enterprise.

A potential drawback is that Gloo may be heavier than needed for your application (our test deployment required 2vCPU per node to run Gloo + an AssemblyLift service). We'll soon look at support for other (lighter) reverse proxies. Eventually we want to support multiple API Gateway providers per backend, and also allow for cross deployment (Amazon APIGW in front of K8s, Gloo in front of Lambda).

A short demonstration

Let's look at an example! If you want to try AssemblyLift yourself and follow along, you will need the prerequisites below. Otherwise if you just want the gist of things, you can skip that and keep reading πŸ™‚

In this example we'll deploy a basic Ruby function to a Kubernetes cluster that writes to the function log, and curl it via an API Gateway.

Prereqs

You will need a running Docker daemon on your development machine. You will also need a configured k8s config at the default ~/.kube/config. So far AssemblyLift has been tested against Docker Desktop Kubernetes as well as Digital Ocean Kubernetes. It isn't strictly necessary, but is recommended for testing and debugging, to have kubectl installed.

You will also need a Docker image registry. Right now AssemblyLift supports Docker Hub and AWS ECR, however ECR is recommended as your registries can be provisioned/destroyed automatically. Currently, AssemblyLift will attempt to use AWS credentials from your default profile in ~/.aws/credentials.

You can grab AssemblyLift for macOS with

curl -O public.assemblylift.akkoro.io/cli/0.4.0-alpha.3/x86_64-apple-darwin/asml
Enter fullscreen mode Exit fullscreen mode

Or for Linux with

curl -O public.assemblylift.akkoro.io/cli/0.4.0-alpha.3/x86_64-linux-gnu/asml
Enter fullscreen mode Exit fullscreen mode

Make the binary executable by running chmod +x asml and ensure it is available on your system PATH.

If your Linux system's glibc isn't compatible with the asml binary you may need to build from source instead. The easiest way to do this is to install with cargo by running cargo install assemblylift --version 0.4.0-alpha.3.

Create a Ruby function

We'll create a new project with a Ruby template:

asml init -n ruby-example -l ruby
cd ruby-example
Enter fullscreen mode Exit fullscreen mode

This will create a basic Ruby function called my-function inside a service my-service. These can be renamed as you like, however the directory names also currently need to be updated manually to match (we'll have this sync in a future release :)).

Add an HTTP route definition to the function in service.toml:

[[api.functions]]
name = "my-function"
language = "ruby"
registry = "ecr" # or "dockerhub" if using Docker Hub
http = { verb = "ALL", path = "/my-service/my-function" }
Enter fullscreen mode Exit fullscreen mode

Set provider and configure container registry

Again in service.toml, add the following to use the Kubernetes backend provider:

[service.provider]
name = "k8s"
Enter fullscreen mode Exit fullscreen mode

Then in assemblylift.toml, configure the container registry you wish to use:

[[registries]]
host = "ecr"
options = { aws_account_id = "12345890", aws_region = "us-east-1" }

[[registries]]
host = "dockerhub"
options = { registry_name = "<name>", username = "<username>", password = "<password>" }
Enter fullscreen mode Exit fullscreen mode

Write some Ruby!

Next, let's open up the function's handler.rb:

require 'asml'
require 'base64'
require 'json'

def main(input)
    # TODO implement your function code here!
    Asml.success(input.to_s)
end

main(JSON.parse(Asml.get_function_input()))
Enter fullscreen mode Exit fullscreen mode

The asml module includes the Asml class, which exposes basic methods for interacting with the AssemblyLift runtime environment.

The input object has shape:

{
  method: String,
  headers: Object,
  body_encoding: String,
  body: String
}
Enter fullscreen mode Exit fullscreen mode

We can decode the body and log it:

def main(input)
    body = input['body_encoding'] == 'base64' ? Base64.decode64(input['body']) : nil
    if body != nil then
        Asml.log(body)
    end

    Asml.success("OK")
end
Enter fullscreen mode Exit fullscreen mode

Build, deploy, test!

Generate your deployment by running asml cast. If there are no errors, you should see a Terraform plan in the output.

Deploy your service with asml bind! Once deployed successfully, your cluster should have a Gloo Edge installation in the gloo-system namespace and a function deployed as ClusterIPs in an asml-* namespace for your service (you can check this by running kubectl get namespaces).

The Gloo Edge Gateway will have created a LoadBalancer resource, which should be deployed by your Kubernetes host (e.g. Digital Ocean). You should be able to curl the LoadBalancer public IP with your function's route!

curl -X POST -d "Hello World" http://<ip>/my-service/my-function
Enter fullscreen mode Exit fullscreen mode

Reach out!

Whether you have questions about using AssemblyLift or just want to chat, you can find us on Discord!

Top comments (0)