Sept 4th, 2022 - update following release 0.1.9
Some days ago, I presented my new side project Capsule (see Capsule, my personal wasm multi-tools knife (part 1)). Quickly, at the beginning, Capsule is a "wasm module launcher" (you can run it as a CLI or as a tiny HTTP server).
I'm developing Capsule thanks to the Wazero project. Initially, the project's goal was to progress with GoLang and WebAssembly (especially with WASI) and to experiment with Wazero. Very quickly, I was able to serve wasm modules through HTTP.
But dealing with HTTP ports was a bit messy (every wasm module is a function, and each module has a different port). Then I wanted to be able to call every function like this: http://localhost:8888/functions/<name_of_the_function>
or even like this http://localhost:8888/functions/<name_of_the_function>/<revision_name>
(I wanted to serve several versions of the same function at the same time ... like bleu, green revisions π). So I developed a small reverse proxy thanks to the Gin Web Framework. At the same time, I decided to create a kind of "wasm modules registry" (always with Gin π).
At this moment, I needed to deploy remotely (from my laptop) a wasm module to a remote version of my reverse proxy (hosted on the cloud or somewhere else) with a REST API. It was the birth of the Capsule Worker (I even did a CLI to ease the deployment of the wasm module).
Now, I can say, "Oh, it seems I have a little FaaS" based on Wasm, Go, and TinyGo. π
How to play with "Capsule FaaS".
Setup
There are five components to run "Capsule FaaS":
- The Capsule launcher
- The Capsule registry
- The Capsule reverse-proxy
- The Capsule worker
- The Capsule CLI (aka
caps
, thank you Darek Dwornikowski for the nickname π)
You can download every component on: https://github.com/bots-garden/capsule/releases/tag/0.1.9, untar each of them, copy the executables to /usr/local/bin
... or use a "quick'n dirty" script like this one:
#!/bin/bash
CAPSULE_VERSION="0.1.9"
CAPSULE_OS="linux"
CAPSULE_ARCH="amd64"
# Install the capsule launcher
wget https://github.com/bots-garden/capsule/releases/download/${CAPSULE_VERSION}/capsule-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
sudo tar -zxf capsule-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz --directory /usr/local/bin
rm capsule-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
# Install the capsule registry
wget https://github.com/bots-garden/capsule/releases/download/${CAPSULE_VERSION}/capsule-registry-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
sudo tar -zxf capsule-registry-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz --directory /usr/local/bin
rm capsule-registry-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
# Install the capsule reverse-proxy
wget https://github.com/bots-garden/capsule/releases/download/${CAPSULE_VERSION}/capsule-reverse-proxy-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
sudo tar -zxf capsule-reverse-proxy-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz --directory /usr/local/bin
rm capsule-reverse-proxy-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
# Install the capsule worker
wget https://github.com/bots-garden/capsule/releases/download/${CAPSULE_VERSION}/capsule-worker-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
sudo tar -zxf capsule-worker-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz --directory /usr/local/bin
rm capsule-worker-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
# Install the capsule CLI
wget https://github.com/bots-garden/capsule/releases/download/${CAPSULE_VERSION}/caps-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
sudo tar -zxf caps-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz --directory /usr/local/bin
rm caps-${CAPSULE_VERSION}-${CAPSULE_OS}-${CAPSULE_ARCH}.tar.gz
you can find the script here: https://github.com/bots-garden/capsule-faas-demo/blob/main/install-capsule-faas.sh
Launch π the Capsule FaaS
Start the Capsule Registry
Run the below command:
DOWNLOADED_FILES_PATH="${PWD}/registry/functions"
CAPSULE_REGISTRY_ADMIN_TOKEN="AZERTYUIOP" \
capsule-registry \
-files="${DOWNLOADED_FILES_PATH}" \
-httpPort=4999
CAPSULE_REGISTRY_ADMIN_TOKEN="AZERTYUIOP"
is not mandatory, but if you want to protect the wasm module uploading to the registry, use it.- We will see later how to use the registry
Start the Capsule Reverse Proxy
Run the below command:
CAPSULE_REVERSE_PROXY_ADMIN_TOKEN="1234567890" \
capsule-reverse-proxy \
-backend=memory \
-httpPort=8888
CAPSULE_REVERSE_PROXY_ADMIN_TOKEN="1234567890"
is not mandatory, but if you want to protect the API, use it.-backend=memory
: right now, everything is handle in memory (persistence will come in a future release).
Start the Capsule Worker
Run the below command:
CAPSULE_REVERSE_PROXY_ADMIN_TOKEN="1234567890" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
capsule-worker \
-reverseProxy=http://localhost:8888 \
-backend=memory \
-capsulePath=capsule \
-httpPortCounter=10000 \
-httpPort=9999
CAPSULE_REVERSE_PROXY_ADMIN_TOKEN="1234567890"
andCAPSULE_WORKER_ADMIN_TOKEN="0987654321"
are not mandatory, but if you want to protect the API, use them.
Publish a wasm function to the registry
First, create and build a hello
function
hello.go
:
package main
// TinyGo wasm module
import (
hf "github.com/bots-garden/capsule/capsulemodule/hostfunctions"
)
func main() {
hf.SetHandleHttp(Handle)
}
func Handle(req hf.Request) (resp hf.Response, errResp error) {
message, _ := hf.GetEnv("MESSAGE")
token, _ := hf.GetEnv("TOKEN")
html := `
<html>
<head>
<meta charset="utf-8">
<title>Wasm is fantastic π</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.container { min-height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; }
.title { font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; display: block; font-weight: 300; font-size: 100px; color: #35495e; letter-spacing: 1px; }
.subtitle { font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif; font-weight: 300; font-size: 42px; color: #526488; word-spacing: 5px; padding-bottom: 15px; }
.links { padding-top: 15px; }
</style>
</head>
<body>
<section class="container">
<div>
<h1 class="title">π Hello World π</h1>
<h2 class="subtitle">Served with π by Capsule π</h2>
<h2 class="subtitle">` + message + `</h2>
<h2 class="subtitle">` + token + `</h2>
</div>
</section>
</body>
</html>
`
headers := map[string]string{
"Content-Type": "text/html; charset=utf-8",
}
return hf.Response{Body: html, Headers: headers}, nil
}
Find the entire sample here: https://github.com/bots-garden/capsule-faas-demo/tree/main/src/functions/hello
Build the wasm module:
tinygo build -o hello.wasm -scheduler=none -target wasi ./hello.go
Then, publish the wasm module to the registry
We will use the Capsule CLI: caps
CAPSULE_REGISTRY_ADMIN_TOKEN="AZERTYUIOP" \
caps publish \
-wasmFile=./src/functions/hello/hello.wasm -wasmInfo="this is the hello module" \
-wasmOrg=demo -wasmName=hello -wasmTag=0.0.0 \
-registryUrl=http://localhost:4999
π the wasm modules are published to
registry/functions/demo
(the.info
file is generated automatically)
./registry
βββ functions
β βββ demo
β βββ hello
β β βββ 0.0.0
β β βββ hello.info
β β βββ hello.wasm
β βββ hey
β βββ 0.0.0
β βββ hey.info
β βββ hey.wasm
Deploy the hello
wasm function to the worker
We will deploy a first revision (-revision=orange
) of the hello
function:
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps deploy \
-function=hello \
-revision=orange \
-downloadUrl=http://localhost:4999/demo/hello/0.0.0/hello.wasm \
-envVariables='{"MESSAGE": "Revision π ","TOKEN": "π‘"}'
- A function deployment is always related to a revision
- The revision is created at the same time
- A function can be "linked" to several revisions
- A revision is like a version of a running function
- Use
-envVariables
to pass environment variables to the module
The output of the command will be:
β³ [deploying to worker] hello / orange
π [deployed to worker] hello / orange
π [serving] http://localhost:8888/functions/hello/orange
π Now you can access the function with http://localhost:8888/functions/hello/orange
Deploy again (twice) the hello
wasm function to the worker
Now, deploy several times the same revision (orange
) of the hello
function and change the environment variables:
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps deploy \
-function=hello \
-revision=orange \
-downloadUrl=http://localhost:4999/demo/hello/0.0.0/hello.wasm \
-envVariables='{"MESSAGE": "Revision π ","TOKEN": "π‘π€¬"}'
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps deploy \
-function=hello \
-revision=orange \
-downloadUrl=http://localhost:4999/demo/hello/0.0.0/hello.wasm \
-envVariables='{"MESSAGE": "Revision π ","TOKEN": "π‘π€¬π₯΅"}'
If you refresh the web page, you can notice that the "angry smileys" are changing:
We have 3 running instances of the orange
revision (and the reverse-proxy is doing a kind of load balancing on these 3 instances).
Deploy 2 new revisions of the hello
wasm function to the worker (blue and green)
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps deploy \
-function=hello \
-revision=blue \
-downloadUrl=http://localhost:4999/demo/hello/0.0.0/hello.wasm \
-envVariables='{"MESSAGE": "Revision π΅","TOKEN": "π©βπ§π§βπ§π¨βπ§"}'
output:
β³ [deploying to worker] hello / blue
π [deployed to worker] hello / blue
π [serving] http://localhost:8888/functions/hello/blue
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps deploy \
-function=hello \
-revision=green \
-downloadUrl=http://localhost:4999/demo/hello/0.0.0/hello.wasm \
-envVariables='{"MESSAGE": "Revision π’","TOKEN": "ππ₯π"}'
output:
β³ [deploying to worker] hello / green
π [deployed to worker] hello / green
π [serving] http://localhost:8888/functions/hello/green
Open the 2 links in your browser:
So, now, we have 3 revisions of the hello
function:
- http://localhost:8888/functions/hello/orange
- http://localhost:8888/functions/hello/blue
- http://localhost:8888/functions/hello/green
Then we can define which revision is the default revision.
Define a "default" revision
Defining a "default" revision means you can call the default revision without the name of the selected revision, like this:
For example, we set the default revision to the blue revision=
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps set-default \
-function=hello \
-revision=blue
output:
β³ [setting default revision] hello / blue
π [the default revision is set]-> hello / blue
π [serving] http://localhost:8888/functions/hello
Now, we can call the blue revision directly with http://localhost:8888/functions/hello
Switch to another revision
To change again the default revision, use the same command with only changing the name of the revision:
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps set-default \
-function=hello \
-revision=green
output:
β³ [setting default revision] hello / green
π [the default revision is set]-> hello / green
π [serving] http://localhost:8888/functions/hello
Remove the default revision of a function
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps unset-default \
-function=hello
output:
β³ [unsetting default revision] hello
π [the default revision is unset]-> hello
Un-deploy a revision
You can un-deploy the revision of a function (and kill the related process == stop the HTTP server):
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
CAPSULE_WORKER_ADMIN_TOKEN="0987654321" \
caps un-deploy \
-function=hello \
-revision=green
output:
β³ [un-deploying revision] hello / green
π [the revision is un-deployed (all processes killed)]-> hello / green
Get information about the worker
To get the list of all the deployed revisions, use the following command:
CAPSULE_WORKER_URL="http://localhost:9999" \
CAPSULE_BACKEND="memory" \
caps worker
output:
{
"hello": {
"blue": {
"isDefaultRevision": "false",
"wasmModules": {
"8540": {
"envVariables": {
"MESSAGE": "Revision π΅",
"TOKEN": "π©βπ§π§βπ§π¨βπ§"
},
"localUrl": "http://localhost:10004",
"remoteUrl": "http://localhost:8888/functions/hello/blue"
}
},
"wasmRegistryUrl": "http://localhost:4999/demo/hello/0.0.0/hello.wasm"
},
"orange": {
"isDefaultRevision": "false",
"wasmModules": {
"7518": {
"envVariables": {
"MESSAGE": "Revision π ",
"TOKEN": "π‘"
},
"localUrl": "http://localhost:10001",
"remoteUrl": "http://localhost:8888/functions/hello/orange"
},
"7908": {
"envVariables": {
"MESSAGE": "Revision π ",
"TOKEN": "π‘π€¬"
},
"localUrl": "http://localhost:10002",
"remoteUrl": "http://localhost:8888/functions/hello/orange"
},
"8095": {
"envVariables": {
"MESSAGE": "Revision π ",
"TOKEN": "π‘π€¬π₯΅"
},
"localUrl": "http://localhost:10003",
"remoteUrl": "http://localhost:8888/functions/hello/orange"
}
},
"wasmRegistryUrl": "http://localhost:4999/demo/hello/0.0.0/hello.wasm"
}
}
}
That's all for this sunny Sunday π€.
One more thing: Capsule is running pretty well on a Pi3A+ (use the arm64
files of the release)
If you want to play with Capsule FaaS without installing anything, you can use this project with Gitpod: https://github.com/bots-garden/capsule-faas-demo
Photo by danilo.alvesd on Unsplash
Top comments (0)