## Global Serverless Architectures ### with Cloudflare Workers Mitch McGinnis

The AWS Way

## A Simpler Path #### Cloudflare Workers - Runtime & Platform - Cloudflare Provided Tools - Demos ![Cloudflare Logo](dist/cf_logo.svg)

Cloudflare

  • Global Cloud Network
    • 300 Cities
  • Networking & Security Tools
  • Serverless Compute
Cloudflare network global map
### Cloudflare Workers Runtime - V8 Engine - JS/TS & WASM (C, C++, Rust, etc) - 0ms Startup - Built-in - Fetch - Web Crypto - Streams ![Protected by Cloudflare](dist/protected.png)
### Workers - Bundled - CPU Limit: Under 50ms (10ms free) - Unbundled - CPU Limit: 30s HTTP, 15m Cron - Not killed after limit but higher chance of eviction - Duration: No Limit, only killed for worker runtime updates - Max Size of Worker 5MB (1MB) - 128 MB Memory
### Workers ```typescript export interface Env { // MY_KV_NAMESPACE: KVNamespace; // MY_DURABLE_OBJECT: DurableObjectNamespace; // MY_BUCKET: R2Bucket; // MY_SERVICE: Fetcher; // MY_QUEUE: Queue; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { return new Response('Hello World!'); }, }; ```
### Pages ![Cloudflare Pages Frameworks](dist/pages.png) - Front-end deployments with Github/GitLab - preview deploys - functions, uses worker runtime
![Simple Diagram](dist/basic_diagram.svg)
### Tool - Wrangler & Miniflare - CLI Tool ⛅️ - Includes init, deploy, dev, rollback, etc. - create cloudflare resources - Miniflare ![Miniflare](dist/miniflare.svg) - run locally - includes most features - Local development can use Miniflare or Proxy
### wrangler.toml ```toml name = "fancy-hall-c7cc" main = "src/worker.ts" compatibility_date = "2023-06-20" [vars] API_TOKEN = "dev_token" # [env.production] # name = "fancier-hall" [env.production.vars] API_TOKEN = "prod_token" ```
### [River Me This](https://rivermethis.com/) Daily Game to choose the winning hand in texas hold-em, uses websockets with a server based timer ![River Me This](dist/river-me-this.png)
### Storage - Cache - local cache - 512 MB Objects - KV - Key Value Store - 512 byte Key - 25MiB Value - 1KB Metadata - R2 - Object Storage - S3 Compatible API - No Egress Fees - up to 5TB (5GB per upload) ![KV/R2 Diagram](dist/kv_diagram.svg)
```javascript export default { async fetch(request, env) { const contents = await env.MY_KV_NAMESPACE .get("groups::foo"); const groupLogs = await env.LOGS .list(contents.prefix); return new Response(JSON.stringify({ contents, groupLogs })) }, }; ``` ### Storage ```toml [[kv_namespaces]] binding = "MY_KV_NAMESPACE" id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" [[buckets]] binding = "MY_BUCKET" name = "my-bucket" bucket_id = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" ```
### Storage - Durable Objects - Atomic Storage - Single Instance (per ID) - Unlimited Objects - 50 GB per Account (contact Cloudflare to raise) - 2048 B Key - 128 KiB Value - Unlimited Storage per Class & Object
```javascript export default { async fetch(request, env) { let id = env.BATCHER.idFromName("foo"); return await env.BATCHER.get(id).fetch(request); }, }; ``` ```toml [[durable_objects]] binding = "BATCHER" class_name = "Batcher" [[migrations]] tag = "v1" # Should be unique for each entry new_classes = ["DurableObjectExample"] [[migrations]] tag = "v2" renamed_classes = [{ from = "DurableObjectExample", to = "UpdatedName" }] deleted_classes = ["DeprecatedClass"] ```
### ![D1](dist/d1.svg) Storage - D1 - Global sqlite - 1MB row limit - 100 MB per DB during Alpha - 10 per Account - Queries per worker 50 (Bundled) / 1000 (Unbound) - Coming Soon - Read Replicas - Metrics and Observability
```javascript const { results } = await env.DB.prepare( "SELECT * FROM Customers WHERE CompanyName = ?" ) .bind("Open AI") .all(); ``` ```toml [[ d1_databases ]] binding = "DB" # available in your Worker on `env.DB` database_name = "d1-example" database_id = "4e1c28a9-90e4-41da-8b4b-6cf36e5abb29" ```
### Services - Service Bindings - Zero-overhead to call other services - dependent services are loaded up on invocation ![Service Bindings](dist/service-bindings.png)
```javascript export default { async fetch(req, env) { return await env.OPENAI.fetch(req); }, }; ``` ```toml [[services]] binding = "OPENAI" service = "openai" environment = "production" [[routes]] binding = "MY_SERVICE" pattern = "/api/*" script = "api.js" ```
### Services - Pub/sub - Distributed MQTT Service - 1k subscribers per topic - 64 KB Message Size - Queues - Queue Messages for Async Processing - Triggers Worker by Batch Size or Timeout - One Consumer per Queue
```javascript const mqtt = require('mqtt') const options = { port: 8883, protocolVersion: 5, // MQTT 5 password: process.env.BROKER_TOKEN, } const client = mqtt.connect(process.env.BROKER_ENDPOINT, options) client.subscribe("example-topic") client.publish("example-topic", `message from ${client.options.clientId}: hello at ${Date.now()}`) client.on("message", function (topic, message) { console.log(`received message on ${topic}: ${message}`) }) /// Queues await env.MY_QUEUE.send(log); ``` ```toml [[queues]] binding = "MY_QUEUE" name = "my-queue" zone_id = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" ```
### Services - Constellation - Run Machine Learning Models - Image or audio classification, Object detection, Anomaly detection - Natural language processing (NLP) - Sentiment analysis, Speech recognition (or text to speech) - Open Neural Network Exchange (ONNX) Runtime - XGBoost in testing
```typescript import { Tensor, run } from "@cloudflare/constellation"; async function processImage(env: Env, data: ArrayBuffer) { /// if (input) { const output = await run( env.CLASSIFIER, // Replace this with your actual model ID generated when you created your Constellation project "297f3cda-5e55-33c0-8ffe-224876a76a39", new Tensor("float32", [1, 3, 224, 224], input); ); const softmaxResult = softmax(output.squeezenet0_flatten0_reshape0.value); const results = topClasses(softmaxResult, 5); result = results[0]; } return result; } ``` ```toml [[constellation]] binding = 'CLASSIFIER' project_id = '2193053a-af0a-40a6-b757-00fa73908ef6' ```
### Types of Workers - fetch & Web Sockets - Queue - Scheduled - run on cron schedule - Email - process emails with a worker - reject, forward, or drop emails - Tail Workers - run after a worker invocation - Has access to logs and exceptions
```javascript export default { async email(message, env, ctx) { message.forward("test@example.com"); }, fetch(request, env, context) { return new Response('Hello'); }, async queue(batch, env) { let messages = JSON.stringify(batch.messages); console.log(`consumed from our queue: ${messages}`); }, async scheduled(event, env, ctx) { ctx.waitUntil(doSomeTaskOnASchedule()); }, async tail(events, env, ctx) { fetch("https://log-storage", { method: "POST", body: JSON.stringify(events), }) }, } ```
### Tools #### Terraform - Alternative to wrangler.toml - missing latest worker features ```terraform terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare" version = "~> 4.0" } } } provider "cloudflare" { api_token = var.cloudflare_api_token } ```
```terraform resource "cloudflare_workers_kv_namespace" "kv" { account_id = "f037e56e89293a057740de681ac9abbe" title = "example" } resource "cloudflare_worker_script" "my_script" { account_id = "f037e56e89293a057740de681ac9abbe" name = "script_1" content = file("build/index.js") kv_namespace_binding { name = "MY_EXAMPLE_KV_NAMESPACE" namespace_id = cloudflare_workers_kv_namespace.kv.id } } ```
### Open Source Workers Platform (Beta) - workerd - https://github.com/cloudflare/workerd - Missing Support for: - Persistent Durable Objects - Cache API - Cron Triggers ![workerd](https://github.com/cloudflare/workerd/raw/main/docs/assets/banner.png)
### Gotchas - Pages and workers can't be renamed - Charged per invocation (Service Bindings) - CPU & Memory Limits - Lots of alpha/beta services
# Comparison ||AWS|Cloudflare| |-|-|-| |Key Value|DynamoDB|KV, DurableObjects| |Compute|EC2,ECS,EKS|x| |Machine Learning|Bedrock,SageMaker|Constellation*| |Object Storage|S3|R2| |Pub/Sub|EventBridge, SNS, SQS|Pub/Sub*, Queues| |Relational Storage|RDS|D1| |Secrets|Paramater Store, Secrets Manager|Secrets Store*| |Serverless|Amplify, AppSync, Lambda|Workers, Pages|
# Demo & Questions ### [Adventure GPT](https://adventure-gpt.pages.dev/) Text Based Adventure Game powered by Open AI
### References - https://developers.cloudflare.com/workers/ - https://developers.cloudflare.com/workers/platform/limits/ - https://developers.cloudflare.com/d1/ - https://developers.cloudflare.com/r2/ - https://blog.cloudflare.com/introducing-cloudflare-workers/ - https://blog.cloudflare.com/introducing-r2-object-storage/ - https://blog.cloudflare.com/announcing-pubsub-programmable-mqtt-messaging/ - https://blog.cloudflare.com/introducing-d1/ - https://blog.cloudflare.com/d1-turning-it-up-to-11/ - https://blog.cloudflare.com/introducing-cursor-the-ai-assistant-for-docs/ - https://developer.mozilla.org/en-US/docs/Web/API - https://developers.cloudflare.com/workers/platform/bindings/about-service-bindings/ - https://github.com/cloudflare/miniflare/tree/master