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" }
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"
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" }
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
Or for Linux with
curl -O public.assemblylift.akkoro.io/cli/0.4.0-alpha.3/x86_64-linux-gnu/asml
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
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" }
Set provider and configure container registry
Again in service.toml
, add the following to use the Kubernetes backend provider:
[service.provider]
name = "k8s"
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>" }
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()))
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
}
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
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 ClusterIP
s 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
Reach out!
Whether you have questions about using AssemblyLift or just want to chat, you can find us on Discord!
Top comments (0)