This is the repo for the International Workshop on Serverless Computing (WoSC8) paper titled: "Migrating from Microservices to Serverless: An IoT Platform Case Study"
- TL;DR
- Architecture
- Deployment and Usage
- Components
- Data Model
- Security Concept
- Migration to FaaS
- Performance Results
- Outlook
This repository contains all you need to set up an IoT infrastructure that features secure communication, scalability, platform independence, HTTP/WS/MQTT protocol support, device/sensor administration, persistence and analytical extensibility. Ensure Docker is installed and run:
#!/bin/bash
git clone https://gitlab.com/tum-iot-lab/iotplatform.git
cd iotplatform/iotplatform
docker-compose up
The full composition of the pipeline of iotplatform:
Development is done via docker-compose to enable working locally, since cluster-resources are limited:
- Todo: Write deployment and how to use
- Todo: Write about integration with Kubernetes
- Metadata administration (CRUD-operations for device and sensor management)
- Automatically creates Kafka topics, Elasticsearch indices & Flink jobs within corresponding pods in the cluster upon device/sensor creation
- Generate JWTs and Consumer Secrets
- Check authorization of incoming requests
- Serve as authentication proxy for Elasticsearch, forwarded verified queries
In order to create a device and sensor follow these steps:
- Go to
http://<HOST>:8080/devices
- Login using the following credentials:
- user:
root
- passowrd:
x5KATOHT9zHczR49aPy0
- user:
- Click add device
- Enter the desired device details
- Click add
- Look for your device and click view to enter it's corresponding detail page
- Click add sensor
- Enter the desired sensor details
- Click add
- Click download device key and save the file. It obtains the token required for data ingestion via one of the protocols
- Hover over the info icon next to the sensor and note the sensor_id
- Turn towards one of the following chapters to connect your device to the platform.
Alternatively you can use the following sample functions to administer your devices using curl, or at least get a quick overview how you can automate device creation by issueing a couple of GET/POST-requests.
The following script creates a sample device (named: ${NAME}-device
) and sensor (named ${NAME}-sensor
) at the iotcore at ${IOTCORE_BACKEND}
and stores the token and device_id and sensor_id to corresponding environment variables for further use.
#!/bin/bash
set -e
export NAME=<YOUR_DESIRED_NAME>
# Login to retrieve initial token
get_initial_token() {
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"username":"root","password":"x5KATOHT9zHczR49aPy0"}' \
${IOTCORE_BACKEND}/api/users/signin \
> jwt.json
export JWT=$(cat jwt.json \
| jq -r '.token'
)
}
# Create device by issueing POST request to IoTCore backend
create_device() {
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer "${JWT} \
-d '{"name":"'${NAME}'-device","description":"'${NAME}'-integration-test-device"}' \
${IOTCORE_BACKEND}/api/devices/ \
> device.json
export DEVICE_ID=$(cat device.json \
| jq -r '.result.id'
)
}
# Create sensor by issueing POST request to IoTCore backend
create_sensor() {
curl \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer "${JWT} \
-d '{"name":"'${NAME}'-sensor","description":"'${NAME}'-integration-test-sensor","mapping":"{\"type\":\"double\"}"}' \
${IOTCORE_BACKEND}/api/devices/${DEVICE_ID}/sensors/ \
> sensor.json
export SENSOR_ID=$(cat sensor.json \
| jq -r '.id'
)
}
# Retrieve auth key for device from IoTCore backend
retrieve_auth_key() {
curl \
-X GET \
-H "Authorization: Bearer "${JWT} \
${IOTCORE_BACKEND}/api/devices/${DEVICE_ID}/key \
> token.json
export TOKEN=$(cat token.json \
| jq -r '.token'
)
}
################################################################################
################################################################################
################################################################################
get_initial_token
create_device
create_sensor
retrieve_auth_key
Currently there are two ways to extract your data from the platform:
Using Elasticsearch APIs:
- Go to
http://<HOST>:8080/devices
- Login using the following credentials:
- user:
root
- passowrd:
x5KATOHT9zHczR49aPy0
- user:
- Click consumer
- Click add consumer
- Enter consumer details
- Click add
- Click view to access the consumer you have just added
- Pick a sensor to grant that consumer and click grant
- Click download consumer key and keep this file for later
- ... tbc
Using Kibana:
Users can configure there own Kibana dashboards at http://<HOST>:5601
.
- Data ingestion pods
- Currently three different protocols are supported (HTTP, WebSockets, MQTT)
- Incoming requests/connections are verified, the included JWT decoded and the
device_id
obtained - Messages are forwarded to Kafka, the respective topic is assembled:
<DEVICE_ID>_<SENSOR_ID>
- In order to compare the performance of Go vs. Node.js within such usecases, the WS and HTTP gateways are implemented in both languages
Gateways are available at:
- MQTT Gateway:
<HOST>:1883/
(Node.js) - HTTP Gateway:
<HOST>:8084/
(Go) - HTTP Gateway:
<HOST>:8083/
(Node.js) - WS Gateway:
<HOST>:8765/
(Node.js) - WS Gateway:
<HOST>:8766/
(Go)
Depending on the environment <HOST>
has to be replaced by http://iot.pcxd.me
, http://localhost
or whereever you deploy iotplatform. Every gateway supports ingesting either single JSON objects
or an array of JSON objects of the following kind:
{
"sensor_id": "4",
"timestamp": 1526717967,
"value": "91827364"
}
The HTTP gateway exposes a single endpoint that allows ingesting single or
multiple sensor values. Authorization is done via the authorization
header.
The gateway decodes the token included here and uses the device_id
contained
in the payload of the JWT's subject field (sub
) to determine the Kafka topic.
The Content-Type
header has to be set to application/json
and the request
body must contain a JSON array (for multiple events) or a single JSON object.
The server responds with one of the following HTTP response codes:
200 OK
: On success, the format of the message was valid and the request authorized. Message is forwarded to Kafka401 Unauthorized
: Theauthorization
header is missing or the header's value is inproperly formatted403 Forbidden
: JWT token could not be verified, is expired or invalid
Sample Request:
#!/bin/bash
curl \
-X POST \
-H "Content-Type: application/json" \
-H "authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MzI4NzU3ODIsImlzcyI6ImlvdHBsYXRmb3JtIiwic3ViIjoiMyJ9.Rl8deAsB-VChSvYzaIrD7hf0NUhSaVZad_c9xaMjo8CoQRNTTBt0Pcss-4SrI2nNWIqIM_cHjMNbmojZKCM5MZWFTpFloLlu2x3-dqBWk5Cm4iYRuQqQ-ANKEqbTxIwwNIzgssHMs2s_Z6hzDNvZ6WVvlBHxgjy4avKGCdzN8dlg9UZZ7t2it5zQyIk4k6mi7gXEfaXZMX90OW8TYlolPAHi58b_duiSq8beM4azG6GilELUv123rVNENSranbwMjQz7xWqKGfAfSTWbNHr3cX0KK0PpjMXjAGQURSf_uPf_WJy3ynnbJTb4jfidNYvnVp3ZuydL8u9PJrAx85r-4ECd61F-koJuA3NEe4A67o5cg2bal_YjDr_VbFl4WizHIbLEP2jGfhFVjAoecivMouebdQWPxGpSEdRGfVwsri_-v4WHx6r9XOfjvOg66QD85fkkSSYSDJqJXW6ncuP3CphAAic5r_KvPNYn3pmlIgSF5ooXmoT9BsKmaZJTgpBhOilY2HTV13IA9AOJiiOiYVqlrZwxzmyJHJXUGVdpkNQaObMw-VZisdEP-Uf_cx3izs985EeYHYNNp_5LSgB6nNGJYv2X0OXfio9oxqg11CE3cETxFekO0YktjEqaLzyov5fTN8QezqonZUAl21AgVCYFsfvLbzfUGpRltREIb-g" \
-d '{"sensor_id":"4","timestamp":1526717967,"value":"91827364"}' \
http://iot.pcxd.me:8083/
Attention: At the moment we're using the header -H "authorization: Bearer <TOKEN>
for authorization with a lowercase a
, thus deviating from common practice. This is due to relying on Node.js libraries that convert all http headers to lowercase (nodejs/node-v0.x-archive#8502). It would be possible to avoid this workaround, by looping over the request's rawHeaders
-field, however it was decided against this for now, due to performance considerations.
There is a sample Java client available here.
The Websocket gateway is a simple websocket server that waits for incoming client
connections and holds the connection as long as messages are being sent or
the client closes the connection. Authorization is being done during the initial
HTTP-Upgrade (Connection: Upgrade
) request. In that request, an additional
authorization
header must be sent, containing a JSON Web Token. In case of a failure, the gateway may terminate the connection and respond with:
401 Unauthorized
:authorization
header is missing or the header's value is inproperly formatted403 Forbidden
: JWT could not be verified, is expired or invalid
There is a sample Python client available here.
The MQTT gateway is relying on Mosca MQTT with Redis as storage backing service.
In order to connect to the broker, the client has to provide authentication credentials,
using JWT
as username and the actual JWT as password. Additionally, the protocol
has to be specified explicitly on occasion. Once connected, clients can push JSON objects
or arrays of JSON objects by publishing using their device_id as topic.
An examplary connection initialization in Node.js:
var client = mqtt.connect({
host: <MQTT_GATEWAY_HOST>,
port: <MQTT_GATEWAY_PORT>,
username: 'JWT',
password: <TOKEN>,
keepalive: 1000,
settings: {
protocolId: 'MQIsdp',
protocolVersion: 3
}
});
There is a sample Node.js client available here.
- Collecting all incoming message from the different gateways
- Act as single point of information for subsequent processing layer
- Persistence capabilities of Kafka are neglected since it would be redundant due to the inclusion of Elasticsearch
- Dashboard access:
<HOST>:8081/
(Depending on the environment<HOST>
has to be replaced byhttp://iot.pcxd.me
,http://localhost
or whereever you deploy iotplatform) - Offers opportunity to add batch processing or analytical jobs
- Consume Kafka topics and forward data to Elasticsearch
- Jobs are created by IoTCore
- Default job is simply forwarding data without transformation/analysis
- Detects timestamp format and converts it to nanoseconds for Kibana
- REST Access:
<HOST>:9000/
(Depending on the environment<HOST>
has to be replaced byhttp://iot.pcxd.me
,http://localhost
or whereever you deploy iotplatform) - Persistence
- Querying capabilities for clients
- Access at
<HOST>:5601/
(Depending on the environment<HOST>
has to be replaced byhttp://iot.pcxd.me
,http://localhost
or whereever you deploy iotplatform) - Monitoring opportunity for Elasticsearch (e.g. for Dev Ops)
- Data visualization
- Sample producers to simulate the supported protocols and exemplify usage of the platform
The platform aims to support creation and data collection for generic devices. Thus we impose very few restrictions and attributes to devices. Additionally, we think that it is required to provide a mechanism that enables grouping sensors. Subsequently, we enable (optional) multi level nesting of sensors under on device and offer clients tree-like structuring of sensors under a device.
Definitions:
Device:
- Entity that may contain zero, one or many sensors
- Clients may add a description to each device containing additional information
- Represents a single (potentially physical) logical unit
Path:
- Optional
- Can point to either another path, or a sensor
- A path cannot be root-node or leaf of the device-tree
Sensor:
- Entity that is always attributed to a single device
- Clients may add a description to each sensor containing additional information
- Clients have to define the type of measurement to specify what values are ingested
- Represents a single physical measuring point, producing a continuous time series of data
An ER-diagram of the data model:
A practical example for leveraging the capabilities of the data model could be the following. A Car-device has four Wheels, that each contain a Pressure Sensor collecting values of type double. The Wheels can be logically separated into Front and Rear. These subgroups can divided again into Left and Right. Each of those path nodes points to a sensor that represents a physical measuring point. Additionally, the Car has an Engine that contains a Consumption Sensor.
The resulting structure:
We migrated the different API endpoints of the IoT Core onto OpenWhisk and Google Cloud Run (GCR).
All source code is present here and here.
All source code is present here and here.
Performance of the HTTP-Gateway for the different deployment strategies: (i) GKE-Standard with two HPA configurations, i.e, GKE-80, GKE-50, (ii) GCR, and (iii) OW across different load scenarios. For this API endpoint, OW performed best.
For a detailed analysis across different API endpoints, please see here.
If our work was helpful to you, please cite our paper:
@inproceedings{iotplatformmigration,
author = {Chadha, Mohak and Pacyna, Victor and Jindal, Anshul and Gu, Jianfeng and Gerndt, Michael},
title = {Migrating from microservices to serverless: an IoT platform case study},
year = {2022},
isbn = {9781450399272},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3565382.3565881},
doi = {10.1145/3565382.3565881},
booktitle = {Proceedings of the Eighth International Workshop on Serverless Computing},
pages = {19–24},
numpages = {6},
keywords = {CaaS, FaaS, container-as-a-service, function-as-a-service, microservices, performance analysis, serverless},
location = {Quebec, Quebec City, Canada},
series = {WoSC '22}
}