Architecture and Kubernetes

Modio Architecture

This is a brief coverage of the Modio systems architecture, and how we use Kubernetes.

Embedded Devices

Our embedded devices in the field are the source of most of our data, so I'll explain a bit about how they function.

Generic

Our devices run a custom Linux for data collection and logging, batching & streaming data based on availability of network. Not using "realtime" techniques but preferring continuous small batching to improve network efficency, and because networks turned out to be not very reliable in practice.

Data model

Different protocols are integrated & decoded on the edge, mapped to a normalized format and stored as a tuple of key,value,timestamp, making it a key=value time-series.

Each key maps distinctly to all of the following:

  • Data type (down to bits of precision expected from hardware)
  • Name (human readable, default)
  • Unit
  • Description (Also for human consumption)
  • Alerting rules

This device is then responsible for normalizing the data it collects from meters, and storing (a hopefully small amount) for batch processing.

Transfer

Currently using "Modern" TLS with HTTP/2 + Client & Server ceritifcates from a separate PKI, (there is no public CA trust root on the devices), and each device has it's own key for identification.

Processing

This is a rough outline of our processing pipeline, not a complete description.

Ingress / Load Balancer

Nginx is acting as load balancer for a multi-provider kubernetes cluster, splitting data processing jobs geographically and logically.

Submit

Data is processed in batches via the submit service, which can be scaled out to be geographically close to devices and customers in order to improve network reliability.

The data submit process includes:

  • Validating each data point according to type rules
  • Ensuring processed values are accessible
  • Handling notifications & alerting systems

Data is then pushed into storage tier one, PostgreSQL.

API

The api service provides our REST API, primarily giving access to historical data, events, and trends. It is also deployed in kubernetes, and here we also have a cache layer as the API is designed to serve data using cache-friendly methods.

Storage

Any data you want to store, you want to store more than once.

Backend database

Using Tiered PostgreSQL storage, both with replication for data availability (backups, off-site) and hot+cold storage or archival data using Foreign Data Wrapper (FDW) to secondary storage of older data. This allows us to store large amounts of historical data accurately, saving up to 90% of disk capacity for older data.

Redis cache

Redis is used as a cluster-internal service to be able to quickly answer the question "what was the last value for sensor xxx.yy.zzz", which is one of our most common queries, and something that is surprisingly difficult to answer efficiently in a relational database.

Multi cloud & k8s native

Data storage (Postgres + offloading) are not managed in k8s, but past that, the entire server-side stack is horizontally scalable, and easy to split geographically. We've always had a multi-cloud approach to managing our k8s environment for availability & cost reasons, and our existing clusters span multiple providers.

Kubernetes adoptation

Our story with kubernetes began three years ago after a few outages caused by restarting docker daemons during updates, upon which no containers could be started again until the machine's container storage had been wiped and re-initialized, something that required manual attention during new years eve weekend.

We originally migrated to k8s mostly as a glorified container launcher, and have gradually come to adopt more and more of the functionality available from the platform.

Where originally our containers were "OS-level" (including init-systems and more) and a way to package an OS stack and improve life for deploying consistently, they have since turned into a fairly standard k8s load of multiple individually scalable components.

Things not in Kubernetes

We do not keep databases & data-storage in kubernetes, as we try to keep all our kubernetes nodes stateless and keep no data in volumes or similar, to make the architecture more maintainable and manageable.

Neither is VPN and static IP endpoints in kubernetes. Again because of stateful loads and availability. It could in theory be migrated, but was deemed not worth it in practice.

Release 3.13

Life with Kubernetes