From zero to "wasm microservices hero" with Atmo
In September, I wrote a blog post about wasm in the browser with GoLang, and this post was hosted by Suborbital. I'm talking to you about Suborbital because they are building tools to ease the deployment of microservices written in wasm.
It's a shortcut to define the work of Suborbital, and it's my own vision/definition of what they are doing. I let Connor Hicks add some comments to this post to correct or complete my description.
The baseline of Suborbital is "Build cloud-native software that's ready for anything", and with the Atmo project, Suborbital offers us this "superpower", and the cherry on the cake, we'll do it in Rust with WebAssembly
Atmo?
To say it short, Atmo is a toolchain allowing writing and deploying wasm microservices smoothly. With Atmo, you can write microservices in Rust, Swift, and AssemblyScript without worrying about complicated things, and you only have to care about your source code:
- No fight with wasm and string data type
- No complex toolchain to install
- Easy deployment with Docker (and then it becomes trivial to deploy wasm microservices on Kubernetes)
- ...
Today, you will learn to:
- Create your first Atmo project
- Modify your first Runnable (see it as a microservice or a function)
- Build and deploy your first Runnable
- Add a new Runnable to the project
1️⃣ [Step 01] Create your first project
To create a new Atmo project, you'll use the subo CLI (It's easy to install: brew install subo
):
subo create project services-demo
services-demo
is the name of the project
The subo CLI will create a project structure like below:
└── services-demo
├── Directive.yaml
├── Dockerfile
└── helloworld
├── Cargo.toml
└── src
└── lib.rs
Content of the project
The build of the generated project will create a "Runnable Bundle"; by default, this project contains
- One function, named
helloworld
(all the code is inhelloworld/src/lib.rs
); we'll call this function a Runnable (and it's written in Rust) - A
Directive.yaml
file where is defined the route to the function - And a Dockerfile to embed the Bundle in a container and ease its deployment.
Source code of the Runnable
The source code of the Runnable is pretty simple, and it's composed of a main run
function. When you make an HTTP request to call the helloworld
service, the run
function is triggered and returns a Result
type:
extract of
services-demo/helloworld/src/lib.rs
struct HelloWorld{}
impl Runnable for HelloWorld {
fn run(&self, input: Vec<u8>) -> Result<Vec<u8>, RunErr> {
let in_string = String::from_utf8(input).unwrap();
Ok(String::from(format!("hello {}", in_string)).as_bytes().to_vec())
}
}
Ok(String::from(format!("👋 hello {} 😃\n", in_string)).as_bytes().to_vec())
Directive.yaml
Content of extract of
services-demo/Directive.yaml
handlers:
- type: request
resource: /hello
method: POST
steps:
- fn: helloworld
This means that when you will do a request on http://your-domain/hello
, you will call the helloworld
function.
2️⃣ [Step 02] Build and serve the Bundle
Build the Bundle
To build the Bundle, type the following commands and wait for some seconds:
cd services-demo
subo build .
The subo CLI builds a wasm file helloworld.wasm
, and generates a new file: runnables.wasm.zip
at the root of the project containing helloworld.wasm
and Directive.yaml
:
.
└── services-demo
├── Directive.yaml
├── Dockerfile
├── helloworld
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── helloworld.wasm ⬅️👋
│ └── src
│ └── lib.rs
└── runnables.wasm.zip ⬅️👋
Serve the Bundle
To serve the bundle, it's simple, just use the command below:
docker run \
-v /workspace/atmo-workspace/services-demo:/home/atmo \
-e ATMO_HTTP_PORT=8080 \
-p 8080:8080 \
suborbital/atmo:latest atmo
subo dev
(in the project directory)
see the documentation: https://atmo.suborbital.dev/getstarted/building-and-running#running-a-development-server
Open a new terminal and type the below command to call your new service:
curl http://localhost:8080/hello -d 'Jane Doe'
# you'll get: 👋 hello Jane Doe 😃
👋 you can stop the service withctrl + c
3️⃣ [Step 03] Add a new "function"
It's straightforward to add a new function (aka Runnable) thanks to the subo CLI. For example, if you want to add a new function written in Swift, type the below commands:
cd services-demo
subo create runnable hey --lang swift
A new directory is created:
├── hey
│ ├── Package.swift
│ └── Sources
│ └── hey
│ └── main.swift
Examine and change the source code
You'll find the source code in /hey/Sources/hey/main.swift
:
import Suborbital
class Hey: Suborbital.Runnable {
func run(input: String) -> String {
return "hello " + input
}
}
Suborbital.Set(runnable: Hey())
The principle is the same as in Rust, but the code is simpler. Change the returned string of the function like this:
func run(input: String) -> String {
return "😍 hey " + input + "\n"
}
Directive.yaml
Update the Before building the Runnable Bundle, you need to add a route to be able to call the new hey
service. Then edit the Directive.yaml
file and add the below source code to the handlers
section:
- type: request
resource: /hey
method: POST
steps:
- fn: hey
Build and serve
Like in the previous step:
cd services-demo
subo build .
When the Bundle is created, serve it again:
docker run \
-v /workspace/atmo-workspace/services-demo:/home/atmo \
-e ATMO_HTTP_PORT=8080 \
-p 8080:8080 \
suborbital/atmo:latest atmo
subo dev
Open a new terminal and type the below command to call your new service:
curl http://localhost:8080/hey -d 'John Doe'
# you'll get: 😍 hey John Doe
And of course, you can still call the previous service:
curl http://localhost:8080/hello -d 'Jane Doe'
# you'll get: 👋 hello Jane Doe 😃
4️⃣ [Step 04] Embed the Bundle in a container and serve it
Every Atmo project contains a Dockerfile
:
.
└── services-demo
├── Directive.yaml
├── Dockerfile ⬅️👋
├── helloworld
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── hey
│ ├── Package.resolved
│ ├── Package.swift
│ └── Sources
│ └── hey
│ └── main.swift
└── runnables.wasm.zip
It's very convenient to deploy your wasm services everywhere (especially on Kubernetes, stay tuned for a next blog post).
Build the container image like this:
cd services-demo
docker build -t services-demo .
Remark: if you get an error message like
error checking context: 'can't stat ' ...
, typesudo chown -R $USER .
and try again.
Now, you can simply run your container and serve the services like this:
docker run \
-e ATMO_HTTP_PORT=8080 \
-p 8080:8080 \
services-demo:latest
And call your service again:
curl http://localhost:8080/hey -d 'John Doe'
# you'll get: 😍 hey John Doe
That's all for today
🎁
One more thing I'm a big fan of Gitpod. So, I created a Gitpod project with all that you need to start playing with Atmo right now without installing anything. So, to get the benefit of that, you have to open this project with Gitpod.
You can see this project as a "freestanding workshop" version of this blog post. And it's here: https://gitlab.com/k33g_org/discovering-atmo/atmo-workspace
Enjoy
👋
- If you loved this "post" (or not), don't forget to use the emojis reactions
- Don't hesitate to add comments and/or ask questions
- You can subscribe to the Rss feed