Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Serverless and Web API best practices on Google Cloud

Serverless and Web API best practices on Google Cloud

The goal of this workshop is to gain experience with "serverless" services offered by Google Cloud Platform:

Cloud Functions — to deploy small units of business logic in the shape of functions, that react to various events (Pub/Sub messages, new files in Cloud Storage, HTTP requests, and more),
App Engine — to deploy and serve web apps, web APIs, mobile backends, static assets, with fast scale up and down capabilities,
Cloud Run — to deploy and scale containers, that can contain any language, runtime or library.

And to discover how to take advantage of those serverless services to deploy and scale Web and REST APIs, while also seeing some good RESTful design principles along the way.

In this workshop, we'll create a bookshelf explorer consisting of:

A Cloud Function: to import the initial dataset of books available in our library, in the Cloud Firestore document database,
A Cloud Run container: that will expose a REST API over the content of our database,
An App Engine web frontend: to browse through the list of books, by calling our REST API.

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

April 14, 2021
Tweet

Transcript

  1. Proprietary + Confidential Serverless and Web API workshop on Google

    Cloud Guillaume Laforge — @glaforge Developer Advocate, Google Cloud April 2021
  2. Proprietary + Confidential Agenda In this workshop, we’ll introduce the

    definition of “Serverless” and discover the serverless compute options available on Google Cloud Platform. Then, we’ll move on to discuss best practices in the design of Web APIs, and how you can take advantage of serverless products. Finally, we’ll put everything in action, through hands-on codelabs. Introduction to Serverless on Google Cloud 1 Web API best practices 2 Hands-on codelabs 3 Q&A 4 Conclusion 5
  3. Proprietary + Confidential Serverless spectrum and compute options on Google

    Cloud
  4. Proprietary + Confidential FaaS App platform Container Virtual machine The

    serverless spectrum Dedicated server Physical hardware Virtual hardware Operation system Application runtime Application Function Virtual hardware Operation system Application runtime Application Function Operation system Application runtime Application Function Application Function Function Serverless Zone
  5. Proprietary + Confidential The serverless models and characteristics Operational Model

    Programming Model No Infra Management Managed Security Pay only for usage Service-based Event-driven Stateless
  6. Proprietary + Confidential Cloud Functions Serverless functions App Engine Web

    applications and APIs Cloud Run Containerized HTTP workloads Serverless compute options on Google Cloud Source-based deployments of event-driven functions Source and compiled deployments of web apps and web API backends Containerized applications serving HTTP requests, fully managed or on GKE clusters
  7. Proprietary + Confidential Cloud Functions

  8. Proprietary + Confidential Cloud Functions Cloud services “glue” Backend code

    that reacts to events • Background events • HTTP events Use cases • Integrating with 3rd-party services & APIs • IoT backends • Lightweight real-time data processing • Virtual assistants & chatbots Available runtimes • Node.JS • Python • Go • Java • .Net • Ruby
  9. Proprietary + Confidential The cloud “glue” — Functions event sources

    Cloud Storage Cloud Pub/Sub HTTPS Firebase Cloud Scheduler Cloud Tasks
  10. Proprietary + Confidential ✅ Good fit? — ❌ Constraints? ✅

    Good fit • Easy to deploy bits of code • React to Pub/Sub & Storage events • Nice for webhooks handlers or other asynchronous operations • Don’t want to think about runtime env. • Lightweight data transformation (ETL) • Cloud-based HTTP glue & webhooks ❌ Constraints • Limited set of runtime languages • Function granularity might be too small • Must interact via events • No custom domain (not out of the box) • Concurrency level of 1 (more cold starts) • Not for user-facing APIs or web fronts
  11. Proprietary + Confidential Demo — Cloud Functions

  12. Proprietary + Confidential App Engine

  13. Proprietary + Confidential App Engine Standard Available language runtimes: •

    Python 2.7 / 3.7 / 3.8 / 3.9 • Java 8 / 11 • Node.js 10 / 12 / 14 • PHP 5.5 / 7.2 / 7.3 / 7.4 • Ruby 2.5 / 2.6 / 2.7 • Go 1.11 / 1.12 / 1.13 / 1.14 / 1.15 Features: • Multiple services, multiple versions • Traffic splitting (A/B testing, blue/green, canary deploym.) • Concurrency (80 concurrent requests) • Custom domains • Built-in CDN for static asset serving • Batteries included (Memcache, Datastore…)
  14. Proprietary + Confidential ✅ Good fit? — ❌ Constraints? ✅

    Good fit • For stateless applications • For HTTP request/response workloads • Scaling high traffic, down to zero (good enough for Snapchat!) • Built-in CDN static asset serving • Traffic splitting (across services & versions) ❌ Constraints • Must use one of the available runtimes • No support for Web Sockets, gRPC streaming, or Server-Sent Events
  15. Proprietary + Confidential Demo — App Engine

  16. Proprietary + Confidential Cloud Run

  17. Proprietary + Confidential Containers Any language Rich ecosystem Any library

    of base images Any binary .js .rb .go .py .sh … 0 1 0 1 0 0 1 1 1 Containers Flexibility Serverless Velocity
  18. Proprietary + Confidential Serverless containers with Knative and Cloud Run

    Cloud Run Fully managed, deploy your workloads and don’t see the cluster. Cloud Run on Anthos Deploy into Anthos, run serverless side-by-side with your existing workloads. Knative everywhere Use the same APIs and tooling anywhere you run Kubernetes with Knative.
  19. Proprietary + Confidential Container contract & resources • Listen on

    0.0.0.0 on port $PORT (default 8080) • HTTP server must start < 4 min (timeout → 504) • Request time < 15 min (default → 5 min, up to 60 min) • Stateless (in-memory file system) • Computation only within request (No background activity) • 1 vCPU per container instance (configurable to 4 vCPU) • 256 MiB of memory up to a max of 8 GiB (configurable, soon 16 GiB) • 80 concurrent requests per container (configurable 1-80, soon 250) • 100 max containers by default (configurable 1-1000, support overridable) • Access to a Metadata Server • Sandboxed by gVisor
  20. Proprietary + Confidential Pay per use CPU / Memory /

    Requests 100ms
  21. Proprietary + Confidential Billable time Instance Billable Time Request 1

    Start Request 1 End Request 2 Start Request 2 End Instance Time Billable Non-billable
  22. Proprietary + Confidential Concurrency model concurrency = 1 concurrency =

    80
  23. Proprietary + Confidential ✅ Good fit? — ❌ Constraints? ✅

    Good fit • For stateless workloads • For HTTP request / response • Supports Web Sockets, gRPC, Server-Sent events • Scale up quickly, and down to zero • Specific runtime needs (language, dependencies, binary…) • Custom domains ❌ Constraints • Must use containers, adds complexity • Decide on build process (Cloud Build, Cloud Native Buildpacks…) • No built-in CDN, or Memcache
  24. Proprietary + Confidential Demo — Cloud Run

  25. Proprietary + Confidential Hosting Web APIs on Serverless products on

    Google Cloud
  26. Proprietary + Confidential Cloud Functions Good for simple single URL

    endpoints, for asynchronous calculations, or for webhooks callbacks Not for full blown Web APIs, synchronous calls (user waiting for cold start) No custom domain for nice Web API URLs Several hosting options, where to host my Web APIs? App Engine Great for deploying Web APIs, using one of the available language runtime Built-in static assets handy for serving API media files Services & versioning is useful for API versioning Custom domain useful for nice Web API URLs Cloud Run Any Web API framework and runtime in a container Support gRPC APIs, as well as Web Sockets and Server-Sent Events Services & revision can be used for API versioning Custom domain useful for nice Web API URLs
  27. Proprietary + Confidential An API gateway to rule them all!

    Provide a consistent and well-defined API contract (Open API) over existing Web APIs made of several services implemented in various languages, deployed on different serverless platforms Features • Decouple API contract & usage from underlying implementation • Regroup several deployed services under a common REST API • Manage API keys, JWT tokens, user authentication • Define rate limitations • Serverless gateway (scale to zero)
  28. Proprietary + Confidential Web API design and best practices

  29. Proprietary + Confidential REST — Principled design of the modern

    Web architecture Representational State Transfer, invented by Roy Field Architectural properties • Performance • Scalability • Simplicity • Modifiability • Visibility • Portability • Reliability Architectural constraints • Client-server • Stateless • Cacheable • Layered system • Code on demand (optional) • Uniform interface
  30. Proprietary + Confidential REST — Uniform interface Identification of resources

    Manipulation of resources through representations Self-descriptive messages HATEOAS (Hypermedia As The Engine of Application State) Resource as URIs: http://api.co/cars/123 JSON, XML, YAML, HTML… (and other media types) HTTP GET, POST, PUT, DELETE, PATCH Hypermedia: HAL, JSON-LD, Siren
  31. Proprietary + Confidential HTTP methods & URIs for collections /

    items Item http://api.co/v2/cars/1234 Collection http://api.co/v2/cars GET Retrieve a specific car List all the cars POST Create a new car if allowed to specify its ID or key, or an ERROR Create a new car PUT Update a specific car Replace the entire collection with a whole news list of cars PATCH Update some details of a specific car Update a batch of the cars in the list, or an ERROR DELETE Delete a specific car Delete all the cars Priority operations
  32. Proprietary + Confidential Nouns refer to resources Resources are handled

    with HTTP verbs: • GET /cars/123, not /getCar/123 • POST /cars, not /createCar • DELETE /cars/1, not POST /deleteCar/1 Prefer nouns over verbs! Verbs can be used for actions or calculations: • /login, /logout • /convert — convert temp. °C ⇆ °F • /transcode — a video into another format • /promote — a release, and employee...
  33. Proprietary + Confidential Naming conventions → Singular or plural resources?

    Prefer plural forms: /tickets/123 not /ticket/123 Avoid confusing odd singular vs plural forms • /person vs /people, or /goose vs /geese Easier for URL routing: same prefix for regular expressions Think of it as: “this is the 123th item of the tickets collection”
  34. Proprietary + Confidential Different casing in the wild: • UpperCamelCase

    or lowerCamelCase • snake_case or dashed-snake-case Prefer lowercase and snake case (underscores seem more commonly used) Naming conventions → Different casings But chose one casting and stick with it!
  35. Proprietary + Confidential Dealing with hierarchies & relationships in URIs

    ✅ /tickets/123/messages/4 a ticket could be a group of messages ❌ /usergroups/234/users/67 a user could belong to different user groups a user should have a URI of its own, referenced from the usergroup payload
  36. Proprietary + Confidential Prefer unwrapped collections • Unless there’s specific

    collection payload metadata (ex: a category, a label, a time information…) • Pagination is better handled with HTTP headers (Un)Wrapped collections GET /tickets { data: [ { id: 1, … } ] } GET /tickets [ { id: 1, … } ]
  37. Proprietary + Confidential Path — required, resource identifier /books/123 Query

    — optional, query collections /books/123?page=3 API parameters, a rule of thumb Body — resource specific logic Part of the payload Header — global, platform wide X-Client-ID: abc123
  38. Proprietary + Confidential Time for a cat break? HTTP Status

    Cats!
  39. None
  40. None
  41. Proprietary + Confidential HTTP status codes Use appropriate HTTP status

    codes when answering requests Not just 200, 300, 400, 500... • 1xx — Hold on! • 2xx — Here you go! • 3xx — Go away! • 4xx — You messed up! • 5xx — I messed up!
  42. Proprietary + Confidential HTTP status codes → Common status codes

    300 Multiple Choices 301 Moved Permanently 302 Found 303 See Other 304 Not Modified 307 Temporary Redirect 308 Permanent Redirect 200 OK 201 Created 202 Accepted. 204 No Content 206 Partial Content 400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 405 Method Not Allowed 406 Not Acceptable 409 Conflict 410 Gone 412 Precondition Failed 415 Unsupported Media Type 416 Requested range unsatisfiable 429 Too Many Requests 500 Internal Server Error 501 Not Implemented 502 Bad Gateway 503 Service Unavailable 504 Gateway Time-out 506 Variant Also Negotiates 509 Bandwidth Limit Exceeded
  43. Proprietary + Confidential HTTP status codes → 2xx — 201

    Created When a resource is created, return 201 Specify a Location header, pointing at the location of the newly created resource POST /cars … HTTP/1.1 201 Created Location: http://api.co/v2/cars/2543 API navigation is important to make the API more discoverable
  44. Proprietary + Confidential HTTP status codes → 2xx — 202

    Accepted Request accepted but might be handled asynchronously It can be a long-running operation, yielding a result later on (should return a payload representing that operation or have a Location header pointing at the operation for further status requests) POST /jobs … HTTP/1.1 202 Accepted
  45. Proprietary + Confidential Response ok, but no payload is returned

    DELETE /tickets/234 HTTP/1.1 204 No content The resource was successfully deleted, no need for a return payload. But could return 200 with the payload representing the deleted object. HTTP status codes → 2xx — 204 No content
  46. Proprietary + Confidential When the content is too big to

    be returned in one response: • A resource payload that is too big • A collection payload that is paginated GET /meteorites?page=4 HTTP/1.1 206 Partial content Link: <http://nasa.gov/meteorites?page=1>; rel=”first”, <http://nasa.gov/meteorites?page=3>; rel=”prev”, <http://nasa.gov/meteorites?page=5>; rel=”next”, <http://nasa.gov/meteorites?page=9>; rel=”last”, HTTP status codes → 2xx — 206 Partial content
  47. Proprietary + Confidential With a page number — ?page=3 •

    Can also specify a page size • Might get odd results on collection insertions With a cursor — ?cursor=abc123 • Insertion friendly With a semantic parameter — ?letter=A • For a limited or discrete number of elements Pagination
  48. Proprietary + Confidential Accept-Range header could be used (not just

    for bytes, but not very common) GET /users HTTP/1.1 206 Partial content Accept-Ranges: users Content-Range: users 0-9/200 Pagination with an Accept-Range header GET /users Range: users=30-39 HTTP/1.1 206 Partial content …
  49. Proprietary + Confidential When HTTP caching headers are in play,

    the client should have a version in cache already GET /meteorites/654 HTTP/1.1 304 Not modified HTTP status codes → 3xx — 304 Not modified
  50. Proprietary + Confidential Request with Modified-Since and reply with Last-Modified

    GET /users/123 Modified-Since: Wed, 7 Apr 2021 01:16:10 GMT HTTP/1.1 200 OK Last-Modified: Wed, 15 Apr 2021 09:16:10 GMT Caching → Last-Modified (date based)
  51. Proprietary + Confidential Request with If-None-Match and reply with Etag

    GET /users/123 If-None-Match: e9fea8ca2fbb4e6c HTTP/1.1 200 OK Etag: 686897696a7c876b Caching → ETag (tag based) GET /users/123 If-None-Match: 686897696a7c876b HTTP/1.1 304 Not modified
  52. Proprietary + Confidential Provide helpful error payloads No definitive standard

    yet • HTTP problem proposal • vnd-error mime type HTTP status codes → 4xx — Errors HTTP/1.1 403 Forbidden Content-Type: application/problem+json { "type": "https://ex.com/probs/out-of-credit", "title": "Not have enough credit.", "detail": "Current balance: 30, cost: 50.", "instance": "/account/12345/msgs/abc", "balance": 30, "accounts": ["/account/12345"] }
  53. Proprietary + Confidential An unknown status code should be treated

    as the first one of the family • 4xx → 400 generic client error • 5xx → 500 generic server error HTTP status codes → 4xx / 5xx
  54. Proprietary + Confidential When rate limitation is reached, send 429

    Too Many Requests Otherwise, indicate common rate limitation status headers: HTTP/1.1 200 OK Date: Mon, 01 Jul 2020 17:27:06 GMT X-RateLimit-Limit: 60 X-RateLimit-Remaining: 56 X-RateLimit-Reset: 1372700873 HTTP status codes → 4xx — 429 Too Many Requests
  55. Proprietary + Confidential Selecting • Query parameters One size does

    not fit all! Filtering • Include fields • Exclude fields • Expand fields • Style preference Sorting • Query parameters Searching • Combine filtering, sorting, selecting… • Custom query language
  56. Proprietary + Confidential Selecting with query parameters: GET /restaurants?type=chinese&stars=5 Expanding

    referenced resources: GET /users/123?fields=address.zip Selecting
  57. Proprietary + Confidential Include only fields you’re interested in GET

    /users/123?fields=name,age Specify excluded fields GET /users/123?exclude=biography Specify a “style” as a query parameter GET /users/123?style=compact Filtering Use the prefer header for different “styles” GET /users/123 HTTP/1.1 Content-Type: application/json Prefer: return=minimal Vary: Prefer,Accept,Accept-Encoding HTTP/1.1 200 OK Content-Type: application/json Vary: Prefer,Accept,Accept-Encoding Preference-Applied: return=minimal
  58. Proprietary + Confidential SQL style GET /books?sort=title+DESC GET /books?sort=title+DESC,author+ASC Sort

    and asc/desc query parameters GET /books?sort=title&order=DESC GET /books?sort=title,authors&desc=title&asc=author Sorting
  59. Proprietary + Confidential Combine various mechanisms: filtering fields, ordering, pagination…

    Other approaches like GraphQL... Searching Provide a full-blown query language • POST a query document to a /search endpoints • Encode a query into a query parameter
  60. Proprietary + Confidential • Most frequent, in the URL GET

    /v2/cars/123 • Custom header GET /cars/123 X-API-Version: 2 Versioning: different approaches • Less frequent, with an accept header Clients don’t have to change endpoint, but update headers GET /cars/123 Accept: application/vnd.cars.v2/json
  61. Proprietary + Confidential With hypermedia controls: • Easier discovery &

    navigation • More generic API clients • Can palliate need for versioning However: • Heavier payloads (for limited bandwidth) • Clients still need to understand the meaning of “links” • Many competing hypermedia formats ◦ HAL, JSON-LD, Collection+JSON, SIREN... Hypermedia — The Richardson’s API maturity model
  62. Proprietary + Confidential Frameworks provide custom Web API documentation approach

    Also often generate Open API documentation and playground Open API 2 still dominant (also known as Swagger) But Open API 3.1 released (→ check tooling support) Other (less popular) formats exist, like: • RAML • API Blueprint API documentation — Open API (Swagger)
  63. Proprietary + Confidential Hands-on — Let’s deploy serverless Web APIs!

  64. Proprietary + Confidential You’ll find step by step instructions in

    this codelab: codelabs.developers.google.com/ codelabs/serverless-web-apis Codelabs instructions
  65. Proprietary + Confidential You’ll find the code used in the

    codelabs on Github: github.com/glaforge/ serverless-web-apis Open source code
  66. Proprietary + Confidential REST API design resources The never ending

    REST API design debate https://speakerdeck.com/glaforge/ the-never-ending-rest-api-design-debate-devoxx-france-2016 Google API design guide https://cloud.google.com/apis/design Best practices for REST API design https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/ API design guidance: long-running background jobs https://tyk.io/api-design-guidance-long-running-background-jobs/ Best practices for designing a pragmatic RESTful API https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api PayPal REST API guide https://github.com/paypal/api-standards/blob/master/api-style-guide.md How to design a REST API https://blog.octo.com/en/design-a-rest-api/ HTTP API design guide (initially from Heroku) https://geemus.gitbooks.io/http-api-design/content/en/index.html On choosing a hypermedia type for your API https://sookocheff.com/post/api/on-choosing-a-hypermedia-format/ Your API versioning is wrong, by Troy Hunt https://www.troyhunt.com/your-api-versioning-is-wrong-which-is/
  67. Proprietary + Confidential Thank you! Guillaume Laforge — @glaforge Developer

    Advocate Google cloud