Skip to content

Commit e6b1d27

Browse files
juntaodvdksnmikesir87
authored
Feat: add Docker+wasm examples (#309)
* Add a Docker+wasm sample application featuring a WasmEdge-based microservice, a MySQL database and an Nginx web server for frontend UI files. Signed-off-by: Michael Yuan <[email protected]> * Add a logo to indicate Docker+wasm compatibility. Add project descriptions to README. Signed-off-by: Michael Yuan <[email protected]> * Add the example for WasmEdge + Kafka / Redpanda + MySQL application to take messages from a queue and save into a database table. Signed-off-by: Michael Yuan <[email protected]> * Add a SVG icon to indicate Docker + Wasm req Signed-off-by: Michael Yuan <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update the docker compose files for the new Docker Desktop release Signed-off-by: Michael Yuan <[email protected]> * Use the correct platform to be compatible with Docker Desktop 4.15 Signed-off-by: Michael Yuan <[email protected]> * Update README.md Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-kafka-mysql/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-kafka-mysql/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-kafka-mysql/etl/Dockerfile Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Update wasmedge-mysql-nginx/README.md Co-authored-by: David Karlsson <[email protected]> Signed-off-by: Michael Yuan <[email protected]> * Change the Nginx port to the default non-privileged 8090 Signed-off-by: Michael Yuan <[email protected]> * My apologies. Need to correct the syntax for the Nginx port 8090. Signed-off-by: Michael Yuan <[email protected]> * Remove commented lines Signed-off-by: Michael Yuan <[email protected]> * Change wasi/wasm32 to wasi/wasm to conform with the latest spec Signed-off-by: Michael Yuan <[email protected]> * Update README.md Co-authored-by: Michael Irwin <[email protected]> Signed-off-by: Michael Yuan <[email protected]> Signed-off-by: Michael Yuan <[email protected]> Signed-off-by: Michael Yuan <[email protected]> Co-authored-by: David Karlsson <[email protected]> Co-authored-by: Michael Irwin <[email protected]>
1 parent 6f15838 commit e6b1d27

File tree

20 files changed

+1057
-0
lines changed

20 files changed

+1057
-0
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ These samples provide a starting point for how to integrate different services u
2020

2121
<a href="https://docs.docker.com/desktop/dev-environments/"><img src="icon_devenvs.svg" alt="Use with Docker Dev Environments" height="30" align="top"/></a> Icon indicates Sample is compatible with [Docker Dev Environments](https://docs.docker.com/desktop/dev-environments/) in Docker Desktop version 4.10 or later.
2222

23+
<a href="https://docs.docker.com/desktop/wasm/"><img src="icon_wasm.svg" alt="Docker + wasm" height="30" align="top"/></a> Icon indicates Sample is compatible with [Docker+Wasm](https://docs.docker.com/desktop/wasm/).
24+
2325
- [`ASP.NET / MS-SQL`](aspnet-mssql) - Sample ASP.NET core application
2426
with MS SQL server database.
2527
- [`Elasticsearch / Logstash / Kibana`](elasticsearch-logstash-kibana) - Sample Elasticsearch, Logstash, and Kibana stack.
@@ -49,6 +51,8 @@ application with a Rust backend and a Postgres database.&nbsp;<a href="react-rus
4951
- [`React / Nginx`](react-nginx) - Sample React application with Nginx.&nbsp;<a href="react-nginx"><img src="icon_devenvs.svg" alt="Use with Docker Dev Environments" height="30" align="top"/></a>
5052
- [`Spring / PostgreSQL`](spring-postgres) - Sample Java application
5153
with Spring framework and a Postgres database.&nbsp;<a href="spring-postgres"><img src="icon_devenvs.svg" alt="Use with Docker Dev Environments" height="30" align="top"/></a>
54+
- [`WasmEdge / MySQL / Nginx`](wasmedge-mysql-nginx) - Sample Wasm-based web application with a static HTML frontend, using a MySQL (MariaDB) database. The frontend connects to a Wasm microservice written in Rust, that runs using the WasmEdge runtime.&nbsp;<a href="wasmedge-mysql-nginx"><img src="icon_wasm.svg" alt="Compatible with Docker+wasm" height="30" align="top"/></a>
55+
- [`WasmEdge / Kafka / MySQL`](wasmedge-kafka-mysql) - Sample Wasm-based microservice that subscribes to a Kafka (Redpanda) queue topic, and transforms and saves any incoming message into a MySQL (MariaDB) database.&nbsp;<a href="wasmedge-kafka-mysql"><img src="icon_wasm.svg" alt="Compatible with Docker+wasm" height="30" align="top"/></a>
5256

5357
## Single service samples
5458

icon_wasm.svg

+13
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
services:
2+
redpanda:
3+
image: docker.redpanda.com/vectorized/redpanda:v22.2.2
4+
command:
5+
- redpanda start
6+
- --smp 1
7+
- --overprovisioned
8+
- --node-id 0
9+
- --kafka-addr PLAINTEXT://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092
10+
- --advertise-kafka-addr PLAINTEXT://redpanda:29092,OUTSIDE://redpanda:9092
11+
- --pandaproxy-addr 0.0.0.0:8082
12+
- --advertise-pandaproxy-addr localhost:8082
13+
ports:
14+
- 8081:8081
15+
- 8082:8082
16+
- 9092:9092
17+
- 9644:9644
18+
- 29092:29092
19+
volumes:
20+
- ./kafka:/app
21+
etl:
22+
image: etl-kafka
23+
platform: wasi/wasm
24+
build:
25+
context: etl
26+
environment:
27+
DATABASE_URL: mysql://root:whalehello@db:3306/mysql
28+
KAFKA_URL: kafka://redpanda:9092/order
29+
RUST_BACKTRACE: full
30+
RUST_LOG: info
31+
restart: unless-stopped
32+
runtime: io.containerd.wasmedge.v1
33+
db:
34+
image: mariadb:10.9
35+
environment:
36+
MYSQL_ROOT_PASSWORD: whalehello

wasmedge-kafka-mysql/README.md

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Compose sample application
2+
3+
![Compatible with Docker+Wasm](../icon_wasm.svg)
4+
5+
This sample demonstrates a WebAssembly (Wasm) microservice written in Rust. It subscribes to a Kafka queue topic on a Redpanda server, and then transforms and saves each message into a MySQL (MariaDB) database table. The microservice is compiled into Wasm and runs in the WasmEdge runtime, which is a secure and lightweight alternative to natively compiled Rust apps in Linux containers.
6+
7+
## Use with Docker Development Environments
8+
9+
You will need a version of Docker Desktop or Docker CLI with Wasm support.
10+
11+
* [Install Docker Desktop + Wasm (Beta)](https://docs.docker.com/desktop/wasm/)
12+
* [Install Docker CLI + Wasm](https://github.com/chris-crone/wasm-day-na-22/tree/main/server)
13+
14+
## WasmEdge server with Redpanda and MySQL database
15+
16+
Project structure:
17+
18+
```
19+
.
20+
+-- compose.yml
21+
|-- etl
22+
|-- Dockerfile
23+
|-- Cargo.toml
24+
+-- src
25+
|-- main.rs
26+
|-- kafka
27+
|-- order.json
28+
|-- db
29+
|-- db-password.txt
30+
```
31+
32+
The [compose.yml](compose.yml) is as follows.
33+
34+
```yaml
35+
services:
36+
redpanda:
37+
image: docker.redpanda.com/vectorized/redpanda:v22.2.2
38+
command:
39+
- redpanda start
40+
- --smp 1
41+
- --overprovisioned
42+
- --node-id 0
43+
- --kafka-addr PLAINTEXT://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092
44+
- --advertise-kafka-addr PLAINTEXT://redpanda:29092,OUTSIDE://redpanda:9092
45+
- --pandaproxy-addr 0.0.0.0:8082
46+
- --advertise-pandaproxy-addr localhost:8082
47+
ports:
48+
- 8081:8081
49+
- 8082:8082
50+
- 9092:9092
51+
- 9644:9644
52+
- 29092:29092
53+
volumes:
54+
- ./kafka:/app
55+
56+
etl:
57+
image: etl-kafka
58+
build:
59+
context: etl
60+
platforms:
61+
- wasi/wasm32
62+
environment:
63+
DATABASE_URL: mysql://root:whalehello@db:3306/mysql
64+
KAFKA_URL: kafka://redpanda:9092/order
65+
RUST_BACKTRACE: full
66+
RUST_LOG: info
67+
restart: unless-stopped
68+
runtime: io.containerd.wasmedge.v1
69+
70+
db:
71+
image: mariadb:10.9
72+
environment:
73+
MYSQL_ROOT_PASSWORD: whalehello
74+
```
75+
76+
The compose file defines an application with three services `redpanda`, `etl` and `db`. The `redpanda` service is a Kafka-compatible messaging server that produces messages in a queue topic. The `etl` service, in the WasmEdge container that subscribes to the queue topic and receives incoming messages. Each incoming message is parsed and stored in the `db` MySQL (MariaDB) database server.
77+
78+
## Deploy with docker compose
79+
80+
```bash
81+
$ docker compose up -d
82+
...
83+
⠿ Network wasmedge-kafka-mysql_default Created 0.1s
84+
⠿ Container wasmedge-kafka-mysql-redpanda-1 Created 0.3s
85+
⠿ Container wasmedge-kafka-mysql-etl-1 Created 0.3s
86+
⠿ Container wasmedge-kafka-mysql-db-1 Created 0.3s
87+
```
88+
89+
## Expected result
90+
91+
```bash
92+
$ docker compose ps
93+
NAME COMMAND SERVICE STATUS PORTS
94+
wasmedge-kafka-mysql-db-1 "docker-entrypoint.s…" db running 3306/tcp
95+
wasmedge-kafka-mysql-etl-1 "kafka.wasm" etl running
96+
wasmedge-kafka-mysql-redpanda-1 "/entrypoint.sh 'red…" redpanda running 0.0.0.0:8081-8082->8081-8082/tcp, :::8081-8082->8081-8082/tcp, 0.0.0.0:9092->9092/tcp, :::9092->9092/tcp, 0.0.0.0:9644->9644/tcp, :::9644->9644/tcp, 0.0.0.0:29092->29092/tcp, :::29092->29092/tcp
97+
```
98+
99+
After the application starts,
100+
log into the Redpanda container and send a message to the queue topic `order` as follows.
101+
102+
```bash
103+
$ docker compose exec redpanda /bin/bash
104+
redpanda@1add2615774b:/$ cd /app
105+
redpanda@1add2615774b:/app$ cat order.json | rpk topic produce order
106+
Produced to partition 0 at offset 0 with timestamp 1667922788523.
107+
```
108+
109+
To see the data in the database container, you can use the following commands.
110+
111+
```bash
112+
$ docker compose exec db /bin/bash
113+
root@c97c472db02e:/# mysql -u root -pwhalehello mysql
114+
mysql> select * from orders;
115+
... ...
116+
```
117+

wasmedge-kafka-mysql/compose.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
services:
2+
redpanda:
3+
image: docker.redpanda.com/vectorized/redpanda:v22.2.2
4+
command:
5+
- redpanda start
6+
- --smp 1
7+
- --overprovisioned
8+
- --node-id 0
9+
- --kafka-addr PLAINTEXT://0.0.0.0:29092,OUTSIDE://0.0.0.0:9092
10+
- --advertise-kafka-addr PLAINTEXT://redpanda:29092,OUTSIDE://redpanda:9092
11+
- --pandaproxy-addr 0.0.0.0:8082
12+
- --advertise-pandaproxy-addr localhost:8082
13+
ports:
14+
- 8081:8081
15+
- 8082:8082
16+
- 9092:9092
17+
- 9644:9644
18+
- 29092:29092
19+
volumes:
20+
- ./kafka:/app
21+
etl:
22+
image: etl-kafka
23+
platform: wasi/wasm
24+
build:
25+
context: etl
26+
environment:
27+
DATABASE_URL: mysql://root:whalehello@db:3306/mysql
28+
KAFKA_URL: kafka://redpanda:9092/order
29+
RUST_BACKTRACE: full
30+
RUST_LOG: info
31+
restart: unless-stopped
32+
runtime: io.containerd.wasmedge.v1
33+
db:
34+
image: mariadb:10.9
35+
environment:
36+
MYSQL_ROOT_PASSWORD: whalehello
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
whalehello

wasmedge-kafka-mysql/etl/Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "kafka"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
anyhow = "1.0.65"
10+
mega_etl = {git = "https://github.com/second-state/MEGA.git"}
11+
tokio_wasi = {version = '1.21', features = ["rt", "macros"]}
12+
env_logger = "0.9"
13+
log = "0.4"
14+
serde = { version = "1.0", features = ["derive"] }
15+
serde_json = "1.0"
16+
http_req_wasi = "0.10"
17+
lazy_static = "1.4.0"

wasmedge-kafka-mysql/etl/Dockerfile

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# syntax=docker/dockerfile:1
2+
FROM --platform=$BUILDPLATFORM rust:1.64 AS buildbase
3+
RUN <<EOT bash
4+
set -ex
5+
apt-get update
6+
apt-get install -y \
7+
git \
8+
clang
9+
rustup target add wasm32-wasi
10+
EOT
11+
# This line installs WasmEdge including the AOT compiler
12+
RUN curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
13+
14+
FROM buildbase AS build
15+
COPY Cargo.toml .
16+
COPY src ./src
17+
# Build the Wasm binary
18+
RUN --mount=type=cache,target=/usr/local/cargo/git/db \
19+
--mount=type=cache,target=/usr/local/cargo/registry/cache \
20+
--mount=type=cache,target=/usr/local/cargo/registry/index \
21+
cargo build --target wasm32-wasi --release
22+
# This line builds the AOT Wasm binary
23+
RUN /root/.wasmedge/bin/wasmedgec target/wasm32-wasi/release/kafka.wasm kafka.wasm
24+
25+
FROM scratch
26+
ENTRYPOINT [ "kafka.wasm" ]
27+
COPY --link --from=build /kafka.wasm /kafka.wasm

wasmedge-kafka-mysql/etl/src/main.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use mega_etl::{async_trait, Pipe, Transformer, TransformerError, TransformerResult};
2+
3+
use serde::{Deserialize, Serialize};
4+
#[derive(Serialize, Deserialize, Debug)]
5+
struct Order {
6+
order_id: i32,
7+
product_id: i32,
8+
quantity: i32,
9+
amount: f32,
10+
shipping: f32,
11+
tax: f32,
12+
shipping_address: String,
13+
}
14+
15+
#[async_trait]
16+
impl Transformer for Order {
17+
async fn transform(inbound_data: &Vec<u8>) -> TransformerResult<Vec<String>> {
18+
let s = std::str::from_utf8(&inbound_data)
19+
.map_err(|e| TransformerError::Custom(e.to_string()))?;
20+
let order: Order = serde_json::from_str(String::from(s).as_str())
21+
.map_err(|e| TransformerError::Custom(e.to_string()))?;
22+
log::info!("{:?}", &order);
23+
let mut ret = vec![];
24+
let sql_string = format!(
25+
r"INSERT INTO orders VALUES ({:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, CURRENT_TIMESTAMP);",
26+
order.order_id,
27+
order.product_id,
28+
order.quantity,
29+
order.amount,
30+
order.shipping,
31+
order.tax,
32+
order.shipping_address,
33+
);
34+
dbg!(sql_string.clone());
35+
ret.push(sql_string);
36+
Ok(ret)
37+
}
38+
39+
async fn init() -> TransformerResult<String> {
40+
Ok(String::from(
41+
r"CREATE TABLE IF NOT EXISTS orders (order_id INT, product_id INT, quantity INT, amount FLOAT, shipping FLOAT, tax FLOAT, shipping_address VARCHAR(50), date_registered TIMESTAMP DEFAULT CURRENT_TIMESTAMP);",
42+
))
43+
}
44+
}
45+
46+
#[tokio::main(flavor = "current_thread")]
47+
async fn main() -> anyhow::Result<()> {
48+
env_logger::init();
49+
50+
// can use builder later
51+
let database_uri = std::env::var("DATABASE_URL")?;
52+
let kafka_uri = std::env::var("KAFKA_URL")?;
53+
let mut pipe = Pipe::new(database_uri, kafka_uri).await;
54+
55+
// This is async because this calls the async transform() function in Order
56+
pipe.start::<Order>().await?;
57+
Ok(())
58+
}

wasmedge-kafka-mysql/kafka/order.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"order_id": 1,"product_id": 12,"quantity": 2,"amount": 56.0,"shipping": 15.0,"tax": 2.0,"shipping_address": "Mataderos 2312"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
services:
2+
frontend:
3+
image: nginx:alpine
4+
ports:
5+
- 8090:80
6+
volumes:
7+
- ./frontend:/usr/share/nginx/html
8+
9+
backend:
10+
image: demo-microservice
11+
platform: wasi/wasm
12+
build:
13+
context: backend/
14+
ports:
15+
- 8080:8080
16+
environment:
17+
DATABASE_URL: mysql://root:whalehello@db:3306/mysql
18+
RUST_BACKTRACE: full
19+
restart: unless-stopped
20+
runtime: io.containerd.wasmedge.v1
21+
22+
db:
23+
image: mariadb:10.9
24+
environment:
25+
MYSQL_ROOT_PASSWORD: whalehello

0 commit comments

Comments
 (0)