celerity/api
v2024-07-22 (draft)
blueprint transform: celerity-2024-07-22
The celerity/api
resource type is used to define a HTTP API or a WebSocket API.
An API can be deployed to different target environments such as a Serverless API Gateway1, a containerised environment, or a custom server.
Containerised environments and custom servers use the Celerity runtime for the language of choice to be deployed to handle incoming requests and route them to your application handlers. The Celerity deploy engine and platforms built on top of it will take care of this for the supported target environments (e.g. Amazon ECS).
Specification
The specification is the structure of the resource definition that comes under the spec
field of the resource in a blueprint.
The rest of this section lists fields that are available to configure the celerity/api
resource followed by examples of different configurations for the resource and how the API behaves in target environments along with additional documentation.
protocols (required)
A list of protocols that the API supports. A Celerity API can support HTTP or WebSocket protocols. Multiple protocols for the same API are supported in certain cases. An API can be both a HTTP and WebSocket API. In a Serverless environment this will map to multiple API Gateways in most cases, in a custom server or containerised environment this will map to a hybrid server that handles both protocols.
The "websocket" protocol is not supported in Serverless environments for Google Cloud and Azure. "Serverless environments" in this context refers to environments where handlers are deployed as serverless functions (e.g. AWS Lambda).
type
array[( "http" | "websocket" | websocketNamedConfiguration )]
cors
Cross-Origin Resource Sharing (CORS) configuration for the API.
type
string | corsConfiguration
examples
cors: "*"
cors:
allowCredentials: true
allowOrigins:
- "https://example.com"
- "https://another.example.com"
allowMethods:
- "GET"
- "POST"
allowHeaders:
- "Content-Type"
- "Authorization"
exposeHeaders:
- "Content-Length"
maxAge: 3600
domain
Configure a custom domain for the API.
type
examples
domain:
domainName: "api.example.com"
basePaths:
- "/"
normalizeBasePath: false
certificateId: "${variables.certificateId}"
securityPolicy: "TLS_1_2"
tracingEnabled
Determines whether tracing is enabled for the API. Depending on the target environment this could mean enabling AWS X-Ray, Google Cloud Trace, or Azure Application Insights.
type
boolean
auth
Configure authorization to control access to the API.
type
examples
auth:
defaultGuard: "jwt"
guards:
jwt:
type: jwt
issuer: "https://identity.twohundred.cloud/oauth2/v1/"
tokenSource: "$.headers.Authorization"
audience:
- "https://identity.twohundred.cloud/api/manage/v1/"
Annotations
Annotations define additional metadata that can determine the behaviour of the resource in relation to other resources in the blueprint or to add behaviour to a resource that is not in its spec.
celerity/vpc
🔗 celerity/api
The following are a set of annotations that determine the behaviour of the API in relation to a VPC. Appropriate security groups are managed by the VPC to API link.
When a VPC is not defined for the container-backed AWS, Google Cloud and Azure target environments, the default VPC for the account will be used.
VPC annotations and links do not have any effect for serverless environments. Serverless APIs are managed by the cloud provider and are not deployed to a customer-managed VPC.
When a VPC is not defined for container-backed cloud environments, annotations in the celerity/api
will apply to the default VPC for the account.
celerity.api.vpc.subnetType
The kind of subnet that the API should be deployed to.
type
string
allowed values
public
| private
default value
public
- When a VPC links to an API, the API will be deployed to a public subnet.
celerity.api.vpc.lbSubnetType
The kind of subnet that the load balancer for an API should be deployed to. This is only relevant when the API is deployed to an environment that requires load balancers to run the API.
type
string
allowed values
public
| private
default value
public
- When a VPC links to an API, the load balancer for the API will be deployed to a public subnet.
Outputs
id
The ID of the created API in the target environment.
For customer server or containerised environments, this will be the same as the baseUrl
output field.
type
string
examples
arn:aws:apigateway:us-east-1::/apis/1234567890
(AWS Serverless)
projects/123456789/locations/global/apis/api-abc
(Google Cloud Serverless)
https://api.example.com/v1
(Custom Server or Containerised Environment)
baseUrl
The base URL of the deployed API.
type
string
examples
https://api.example.com/v1
https://abcdef.execute-api.us-east-1.amazonaws.com
(AWS Serverless)
https://my-gateway-a12bcd345e67f89g0h.uc.gateway.dev
(Google Cloud Serverless)
Data Types
websocketNamedConfiguration
A named entry for WebSocket configuration.
FIELDS
websocketConfig
Configuration for a WebSocket API.
field type
websocketConfiguration
Configuration for the WebSocket protocol of an API.
FIELDS
routeKey
The key in the message JSON object to use for routing messages to the correct handler. This is not used for binary messages, only for text WebSocket messages. For binary messages, the route is extracted from the prefix of the message as per the Celerity Binary Message Format.
field type
string
default value
event
authStrategy
Determines the strategy for authenticating WebSocket connections.
When authMessage
is set, the client is expected to send a message with an authentication token to authenticate the connection.
When connect
is set, the client is expected to send the authentication token in the connection request headers.
See the WebSocket Auth Strategy section for more information.
In Serverless environments that support WebSocket APIs, only the authMessage
strategy is supported.
This is because custom WebSocket status codes are not supported by Serverless WebSocket API offerings.
field type
string
allowed values
authMessage
| connect
default value
authMessage
corsConfiguration
Detailed Cross-Origin Resource Sharing (CORS) configuration for the API.
FIELDS
allowCredentials
Indicates whether the request is allowed to contain credentials that are set in cookies.
field type
boolean
allowOrigins
A list of origins that are allowed to access the endpoints of the API.
field type
array[string]
allowMethods
A list of HTTP methods that are allowed to be used when accessing the endpoints of the API from an allowed origin.
field type
array[string]
allowHeaders
A list of HTTP headers that are allowed to be used when accessing the endpoints of the API from an allowed origin.
field type
array[string]
exposeHeaders
A list of HTTP headers that can be exposed in responses of the API endpoints for a request from an allowed origin.
field type
array[string]
maxAge
A value that contains the number of seconds to cache a CORS Preflight request for.
field type
string
domainConfiguration
Configuration for attaching a custom domain to the API.
FIELDS
domainName (required)
The custom domain name to use for the API.
field type
string
examples
api.example.com
basePaths
A list of base paths to configure for the API. This is useful when you want to configure the same domain for multiple APIs and route requests to different APIs based on the base path.
When providing protocol-specific base paths, only one can be provided for each protocol; additionally, protocol-specific base paths can not be mixed with simple base paths.
Hybrid APIs that support both HTTP and WebSocket protocols must have protocol-specific base paths defined.
When deploying a Celerity application to a custom server or containerised environment, the base path is used by a reverse proxy or load balancer to route a request to the correct upstream service for a domain that hosts multiple APIs or applications. In a simple set up, the base path should be stripped from the request URL before it reaches the runtime router.
Simple examples where base path should be stripped:
["/"]
["/api/current", "/api/v1"]
When multiple base paths are provided for different protocols, the reverse proxy or load balancer must not strip the base path from the request URL. The presence of the base path in the request allows the runtime router to determine whether to route the request to a HTTP or WebSocket handler.
Advanced example where base path should not be stripped:
- protocol: "http"
basePath: "/api"
- protocol: "websocket"
basePath: "/ws"
field type
array[( string | basePathConfiguration )]
default value
["/"]
examples
- protocol: "http"
basePath: "/api"
- protocol: "websocket"
basePath: "/ws"
normalizeBasePath
A boolean value that indicates whether the base path should be normalised.
When set to true
, the base path will be normalised to remove non-alphanumeric characters.
field type
boolean
default value
true
certificateId (required)
The ID of the certificate to use for the custom domain. This is specific to the target environment where the API is deployed. For example, in AWS, this would be the ARN of an ACM certificate. Usually, it would be best to provide this as a variable to decouple the blueprint from a specific target environment like a cloud provider.
field type
string
examples
arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012
"${variables.certificateId}"
securityPolicy
The Transport Layer Security (TLS) version and cipher suite for this domain.
Supported values are TLS_1_0
and TLS_1_2
.
field type
string
allowed values
TLS_1_0
| TLS_1_2
authConfiguration
Configuration for authorization to control access to the API.
FIELDS
guards
A list of guards that are used to control access to the API.
field type
mapping[string, authGuardConfiguration]
defaultGuard
The identifier of the guard that should be used as the default guard for the API. This should only be set when you want to use the same guard for all endpoints of the API.
field type
string
authGuardConfiguration
Configuration for a guard that is used to control access to the API.
FIELDS
type
The type of guard that can be used to control access to endpoints
of the API.
When the guard type is set to custom, you can implement a custom guard with a handler.
In the blueprint definition, the handler must be linked to the API with the annotation celerity.handler.guard.custom
set to true
.
field type
string
allowed values
jwt
| custom
issuer (conditionally required)
The token issuer that is used to validate a JWT when the guard type is jwt
.
This is required when the guard type is jwt
, ignored otherwise.
field type
string
examples
https://identity.twohundred.cloud/oauth2/v1/
tokenSource (conditionally required)
The source of the token in the request or message.
This is required when the guard type is jwt
, ignored otherwise.
This is expected to be in the format $.*
where *
is the field in the request or message
that contains the token.
For example, in a HTTP request, a token in the Authorization
header would be $.headers.Authorization
.
For HTTP requests, the token sources can be $.headers.
, $.query.
, $.body.
, $.cookies.
.
In a WebSocket message a token in the message data would be $.data.token
, assuming a message structure such as:
{
"event": "authenticate",
"data": {
"token": "..."
}
}
field type
string | valueSourceConfiguration
examples
$.headers.Authorization
$.query.token
$.body.token
$.cookies.token
$.data.token
- protocol: "http"
source: "$.headers.Authorization"
- protocol: "websocket"
source: "$.data.token"
audience (conditionally required)
A list of intended recipients of a JWT.
This is required when the guard type is jwt
, ignored otherwise.
See RFC 7519 for more information about JWT audiences.
field type
array[string]
examples
["https://identity.twohundred.cloud/api/manage/v1/"]
["4f4948fac1b4b2b4c10deaf32"]
(a client ID for an OAuth2 application)
valueSourceConfiguration
Configuration for a value source used to extract values such as JWTs from a request or message.
FIELDS
protocol (required)
The protocol that the value source is used for.
This can be either http
or websocket
.
field type
string
allowed values
http
| websocket
source (required)
The source of the value in the request or message.
This is expected to be in the format $.*
where *
is the field in the request or message
that contains the value.
For example, in a HTTP request, the Authorization
header would be $.headers.Authorization
.
For HTTP requests, the token sources can be $.headers.
, $.query.
, $.body.
, $.cookies.
.
In a WebSocket message, a token in the message data would be $.data.authToken
, assuming a message structure such as:
{
"event": "authenticate",
"data": {
"authToken": "..."
}
}
field type
string
examples
$.headers.Authorization
$.query.token
$.cookies.authToken
basePathConfiguration
Configuration for a base path of an API that allows you to map base paths to different protocols.
FIELDS
protocol (required)
The protocol that the base path should serve.
This can be either http
or websocket
.
field type
string
allowed values
http
| websocket
basePath (required)
The base path to configure for the specified protocol in the API.
field type
string
examples
/api
/ws
Linked From
celerity/vpc
Depending on the target environment, the API may be deployed to a VPC for private access. When deploying to serverless environments such as AWS API Gateway, the API itself is not deployed to a VPC.
Links To
celerity/handler
Handlers contain the application functionality that is executed in response to an incoming request or message to an API. Annotations for the handler resource type can be used to configure the behaviour of the handler in relation to the API.
Handlers can also be custom auth guards that are used to control access to the API.
celerity/secrets
Secrets can be used to store configuration and sensitive information such as API keys, database passwords, and other credentials that are used by the application. An API can link to a secret store to access secrets at runtime, linking an application to a secret store will automatically make secrets accessible to all handlers in the application without having to link each handler to the secret store.
Examples
HTTP API
version: 2023-04-20
transform: celerity-2024-07-22
resources:
ordersApi:
type: "celerity/api"
metadata:
displayName: Orders API
annotations:
celerity.api.vpc.subnetType: "public"
linkSelector:
byLabel:
application: "orders"
spec:
protocols: ["http"]
cors:
allowCredentials: true
allowOrigins:
- "https://example.com"
- "https://another.example.com"
allowMethods:
- "GET"
- "POST"
allowHeaders:
- "Content-Type"
- "Authorization"
exposeHeaders:
- "Content-Length"
maxAge: 3600
domain:
domainName: "api.example.com"
basePaths:
- "/"
normalizeBasePath: false
certificateId: "${variables.certificateId}"
securityPolicy: "TLS_1_2"
tracingEnabled: true
auth:
defaultGuard: "jwt"
guards:
jwt:
type: jwt
issuer: "https://identity.twohundred.cloud/oauth2/v1/"
tokenSource: "$.headers.Authorization"
audience:
- "https://identity.twohundred.cloud/api/manage/v1/"
WebSocket API
version: 2023-04-20
transform: celerity-2024-07-22
resources:
orderStreamApi:
type: "celerity/api"
metadata:
displayName: Order Stream API
linkSelector:
byLabel:
application: "orders"
spec:
protocols:
- websocket:
routeKey: "action"
cors:
allowOrigins:
- "https://example.com"
- "https://another.example.com"
domain:
domainName: "api.example.com"
basePaths:
- "/"
normalizeBasePath: false
certificateId: "${variables.certificateId}"
securityPolicy: "TLS_1_2"
tracingEnabled: true
auth:
defaultGuard: "jwt"
guards:
jwt:
type: jwt
issuer: "https://identity.twohundred.cloud/oauth2/v1/"
tokenSource: "$.data.token"
audience:
- "https://identity.twohundred.cloud/api/manage/v1/"
Hybrid API
version: 2023-04-20
transform: celerity-2024-07-22
resources:
ordersApi:
type: "celerity/api"
metadata:
displayName: Orders API
linkSelector:
byLabel:
application: "orders"
spec:
protocols: ["http", "websocket"]
cors:
allowCredentials: true
allowOrigins:
- "https://example.com"
- "https://another.example.com"
allowMethods:
- "GET"
- "POST"
allowHeaders:
- "Content-Type"
- "Authorization"
exposeHeaders:
- "Content-Length"
maxAge: 3600
domain:
domainName: "api.example.com"
basePaths:
- protocol: "http"
basePath: "/api"
- protocol: "websocket"
basePath: "/ws"
normalizeBasePath: false
certificateId: "${variables.certificateId}"
securityPolicy: "TLS_1_2"
tracingEnabled: true
auth:
defaultGuard: "jwt"
guards:
jwt:
type: jwt
issuer: "https://identity.twohundred.cloud/oauth2/v1/"
tokenSource:
- protocol: "http"
source: "$.headers.Authorization"
- protocol: "websocket"
source: "$.data.token"
audience:
- "https://identity.twohundred.cloud/api/manage/v1/"
WebSockets
Celerity Binary Message Format
Celerity provides a binary message format that is used to route binary WebSocket messages to the correct handler. This format is purely for messages sent from a client to the server, messages sent from the server to the client can be in any format as per the API designed for a specific application.
WebSocket binary messages are only supported by the Celerity runtime. The runtime is used when deploying a Celerity application to a custom server or a containerised environment.
In environments that support binary messages (e.g. Celerity runtime in a containerised environment), a message is expected to be of the following format:
<routeLength><route><message>
<routeLength>
is a 1-byte unsigned integer that represents the length of the route in bytes.
<route>
is an encoded utf-8 string for the route of the message that is used to route the message to the correct handler, this will be exactly the length specified in <routeLength>
. A route can not have a length greater than 255 bytes, for performance reasons it is recommended to keep the route as short as possible.
<message>
is the actual binary data of the message.
WebSocket Auth Strategy
There are two strategies for authenticating WebSocket connections: authMessage
and connect
.
See the WebSocket Configuration section for the field that determines the strategy.
authMessage
approach
On successful connection, the client must send a message with the token in the format:
{
"event": "authenticate",
"data": {
"token": "..."
}
}
The API's auth guard token source must match the field containing the token in the message. (i.e. $.data.token
)
"event" in this example is using the default routeKey, when you specify a custom route key, that will be used instead.
Upon successful authentication, the client will receive a message with the event authenticated
in the following form:
{
"event": "authenticated",
"data": {
"success": true,
"message": "Authenticated successfully"
}
}
Upon failed authentication, the client will receive a message with the event authenticated
in the following form:
{
"event": "authenticated",
"data": {
"success": false,
"message": "Authentication failed"
}
}
After sending the failed authentication message, the connection will be closed from the server-side. The purpose of sending the failed authentication message is to provide a clear reason for failure to the client before closing the connection, this is essential in environments that do not support custom WebSocket status codes.
the "event" field in the response message is not related to the route key, this will always be event.
connect
approach
The HTTP header configured as the API's auth guard token source must be sent in the connection request headers.
For example, if the token source is $.headers.Authorization
, the client must send the token in the Authorization
header when connecting.
Upon successful authentication, the connection will be upgraded to WebSockets and the client will be able to send and receive messages.
Upon failed authentication, the connection will be closed with the custom status code 4001
(Unauthorized). The client should handle this status code as an authentication failure.
The custom status code is in the range reserved for application-specific status codes, see RFC 6455 for more information.
Target Environments
Celerity::1
In the Celerity::1 local environment, an API is deployed as a containerised version of the Celerity runtime in Docker. Links from VPCs to APIs are ignored for this environment as the API is deployed to a local container network on a developer or CI machine.
AWS
In the AWS environment, APIs are deployed as a containerised version of the Celerity runtime. Authentication, authorisation and CORS configuration defined for the API is taken care of by the runtime.
APIs can be deployed to ECS or EKS backed by Fargate or EC2 using deploy configuration for the AWS target environment.
ECS
When an API is first deployed to ECS, a new cluster is created for the API. A service is provisioned within the cluster to run the application. A public-facing application load balancer is created to route traffic to the service, if you require private access to the API, the load balancer can be configured to be internal. When domain configuration is provided and the load balancer is public-facing, an ACM certificate is created for the domain and attached to the load balancer, you will need to verify the domain ownership before the certificate can be used.
The service is deployed with an auto-scaling group that will scale the number of tasks running the API based on the CPU and memory usage of the tasks. The auto-scaling group will scale the desired task count with a minimum of 1 task and a maximum of 5 tasks by default. If backed by EC2, the auto-scaling group will scale the number instances based on the CPU and memory usage of the instances with a minimum of 1 instance and a maximum of 3 instances by default. Deploy configuration can be used to override this behaviour.
When it comes to networking, ECS services need to be deployed to VPCs; if a VPC is defined in the blueprint and linked to the API, it will be used, otherwise the default VPC for the account will be used. The load balancer will be placed in the public subnet by default, but can be configured to be placed in a private subnet by setting the celerity.api.vpc.lbSubnetType
annotation to private
. The service for the application will be deployed to a public subnet by default, but can be configured to be deployed to a private subnet by setting the celerity.api.vpc.subnetType
annotation to private
.
By default, 2 private subnets and 2 public subnets are provisioned for the associated VPC, the subnets are spread across 2 availability zones, the ECS resources that need to be associated with a subnet will be associated with these subnets, honouring the API subnet type defined in the annotations.
The CPU to memory ratio used by default for AWS deployments backed by EC2 is that of the t3.*
instance family. The auto-scaling launch configuration will use the appropriate instance type based on the requirements of the application, these requirements will be taken from the deploy configuration or derived from the handlers configured for the API. If the requirements can not be derived, the instance profile will be t3.small
with 2 vCPUs and 2GB of memory.
Fargate-backed ECS deployments use the same CPU to memory ratios allowed for Fargate tasks as per the task definition parameters.
If memory and CPU is not defined in the deploy configuration and can not be derived from the handlers, some defaults will be set. For an EC2-backed cluster, the task housing the containers that make up the service for the API will be deployed with 896MB of memory and 0.8 vCPUs. Less than half of the memory and CPU is allocated to the EC2 instance to allow for smooth deployments of new versions of the API, this is done by making sure there is enough memory and CPU available to the ECS agent. For a Fargate-backed cluster, the task housing the containers that make up the service for the API will be deployed with 1GB of memory and 0.5 vCPUs.
A sidecar ADOT collector container is deployed with the API to collect traces and metrics for the application, this will take up a small portion of the memory and CPU allocated to the API. Traces are only collected when tracing is enabled for the API.
EKS
When an API is first deployed to EKS, a new cluster is created for the API unless you specify an existing cluster to use in the deploy configuration.
When using an existing cluster, the cluster must be configured in a way that is compatible with the VPC annotations configured for the API as well as the target compute type.
For example, a cluster without a Fargate profile can not be used to deploy an API that is configured to use Fargate. Another example would be a cluster with a node group only associated with public subnets not being compatible with an API with the celerity.api.vpc.subnetType
annotation set to private
.
You also need to make sure there is enough memory and CPU allocated for node group instances to run the API in addition to other workloads running in the cluster.
The cluster is configured with a public endpoint for the Kubernetes API server by default, this can be overridden to be private in the deploy configuration. (VPC links will be required to access the Kubernetes API server when set to private)
For an EKS cluster backed by EC2, a node group is configured with auto-scaling configuration to have a minimum size of 2 nodes and a maximum size of 5 nodes by default. Auto-scaling is handled by the Kubernetes Cluster Autoscaler. The instance type configured for node groups is determined by the CPU and memory requirements defined in the deploy configuration or derived from the handlers of the API, if the requirements can not be derived, the instance type will be t3.small
with 2 vCPUs and 2GB of memory.
For an EKS cluster backed by Fargate, a Fargate profile is configured to run the API.
Once the cluster is up and running, Kubernetes services are provisioned to run the application, an Ingress service backed by an application load balancer is created to route traffic to the service, if you require private access to the API, the load balancer can be configured to be internal. When domain configuration is provided and the load balancer is public-facing, an ACM certificate is created for the domain and attached to the ingress service via annotations, you will need to verify the domain ownership before the certificate can be used.
When it comes to networking, EKS services need to be deployed to VPCs; if a VPC is defined in the blueprint and linked to the API, it will be used, otherwise the default VPC for the account will be used.
The ingress service backed by an application load balancer will be placed in the public subnet by default, but can be configured to be placed in a private subnet by setting the celerity.api.vpc.lbSubnetType
annotation to private
.
By default, 2 private subnets and 2 public subnets are provisioned for the associated VPC, the subnets are spread across 2 availability zones. For EC2-backed clusters, the EKS node group will be associated with all 4 subnets when celerity.api.vpc.subnetType
is set to public
; when celerity.api.vpc.subnetType
is set to private
, the node group will only be associated with the 2 private subnets. The orchestrator will take care of assigning one of the subnets defined for the node group.
For Fargate-backed clusters, the Fargate profile will be associated with the private subnets due to the limitations with Fargate profiles. For Fargate, the API will be deployed to one of the private subnets associated with the profile.
celerity.api.vpc.subnetType
has no effect for Fargate-based EKS deployments, the API will always be deployed to a private subnet.
If memory and CPU is not defined in the deploy configuration and can not be derived from the handlers, some defaults will be set. For an EC2-backed cluster, the containers that make up the service for the API will be deployed with 896MB of memory and 0.8 vCPUs. Less than half of the memory and CPU is allocated to a node that will host the containers to allow for smooth deployments of new versions of the API, this is done by making sure there is enough memory and CPU available to the Kubernetes agents. For a Fargate-backed cluster, the pod for the API will be deployed with 1GB of memory and 0.5 vCPUs, for Fargate there are a fixed set of CPU and memory configurations that can be used.
A sidecar ADOT collector container is deployed in the pod with the API to collect traces and metrics for the application, this will take up a small portion of the memory and CPU allocated to the API. Traces are only collected when tracing is enabled for the API.
AWS Serverless
In the AWS Serverless environment, APIs are deployed as Amazon API Gateway REST, WebSocket or HTTP APIs with AWS Lambda for the handlers.
Authorisers are configured in the API Gateway for the JWT auth guards. Custom auth guards will be deployed as custom authorizers with AWS Lambda housing the custom guard handler.
When domain configuration is provided, an ACM certificate is created for the domain and attached to the API Gateway, you will need to verify the domain ownership before the certificate can be used.
When tracing is enabled, an ADOT lambda layer is bundled with and configured to instrument each handler to collect traces and metrics. AWS API Gateway traces are collected in AWS X-Ray, API Gateway-specific traces can be collected into tools like Grafana with plugins that use AWS X-Ray as a data source.
APIs can be deployed to API Gateway using deploy configuration for the AWS Serverless target environment.
Google Cloud
In the Google Cloud environment, APIs are deployed as a containerised version of the Celerity runtime. Authentication, authorisation and CORS configuration defined for the API is taken care of by the runtime.
APIs can be deployed to Cloud Run, as well as Google Kubernetes Engine (GKE) in Autopilot or Standard mode using deploy configuration for the Google Cloud target environment.
Cloud Run
Cloud Run is a relatively simple environment to deploy applications to, the API is deployed as a containerised application that is fronted by either an internal or external load balancer.
Autoscaling is configured with the use of Cloud Run annotations through autoscaling.knative.dev/minScale
and autoscaling.knative.dev/maxScale
annotations. The knative autoscaler will scale the number of instances based on the number of requests and the CPU and memory usage of the instances. By default, the application will be configured to scale the number of instances with a minimum of 1 instance and a maximum of 5 instances. Deploy configuration can be used to override this behaviour.
When domain configuration is provided and the load balancer is public-facing and Google-managed, a managed TLS certificate is created for the domain and attached to the load balancer, you will need to verify the domain ownership before the certificate can be used.
For Cloud Run, the API will not be associated with a VPC, defining custom VPCs for Cloud Run applications is not supported. Creating and linking a VPC to the API will enable the Internal
networking mode in the network ingress settings. celerity.api.vpc.subnetType
has no effect for Cloud Run deployments, the API will always be deployed to a network managed by Google Cloud. Setting celerity.api.vpc.lbSubnetType
to private
will have the same affect as attaching a VPC to the application, making the application load balancer private. Setting celerity.api.vpc.lbSubnetType
to public
will have the same effect as not attaching a VPC to the API, making the application load balancer public. public
is equivalent to the "Internal and Cloud Load Balancing" ingress setting.
Memory and CPU resources allocated to the API can be defined in the deploy configuration, when not defined, memory and CPU will be derived from the handlers configured for the API. If memory and CPU is not defined in the deploy configuration and can not be derived from the handlers, some defaults will be set. The Cloud Run service will be allocated a limit of 1GB of memory and 1 vCPU per instance.
A sidecar OpenTelemetry Collector container is deployed in the service with the API to collect traces and metrics, this will take up a small portion of the memory and CPU allocated to the API. Traces will only be collected if tracing is enabled for the API.
GKE
When an API is first deployed to GKE, a new cluster is created for the API unless you specify an existing cluster to use in the deploy configuration.
When using an existing cluster, the cluster must be configured in a way that is compatible with the VPC annotations configured for the API as well as the target compute type.
When in standard mode, the cluster will be regional with 2 zones for better availability guarantees. A node pool is created with autoscaling enabled, by default, the pool will have a minimum of 1 node and a maximum of 3 nodes per zone. As the cluster has 2 zones, this will be a minimum of 2 nodes and a maximum of 6 nodes overall. The cluster autoscaler is used to manage scaling and choosing the appropriate instance type to use given the requirements of the API service. The minimum and maximum number of nodes can be overridden in the deploy configuration.
When in autopilot mode, Google manages scaling, security and node pools. Based on memory and CPU limits applied at the pod-level, appropriate node instance types will be selected and will be scaled automatically. There is no manual autoscaling configuration when running in autopilot mode, GKE Autopilot is priced per pod request rather than provisioned infrastructure, depending on the nature of your workloads, it could be both a cost-effective and convenient way to run your APIs. Read more about autopilot mode pricing.
When domain configuration is provided and the load balancer that powers the Ingress service is public-facing and Google-managed, a managed TLS certificate is created for the domain and attached to the Ingress object, you will need to verify the domain ownership before the certificate can be used.
When it comes to networking, a GKE cluster is deployed as a private cluster, nodes that the pods for the API run on only use internal IP addresses, isolating them from the public internet. The Control plane has both internal and external endpoints, the external endpoint can be disabled from the Google Cloud/Kubernetes side. When celerity.api.vpc.lbSubnetType
is set to public
, an Ingress service is provisioned using an external Application Load Balancer. When celerity.api.vpc.lbSubnetType
is set to private
, an Ingress service is provisioned using an internal Application Load Balancer. The Ingress service is used to route traffic from the public internet to the service running the API.
celerity.api.vpc.subnetType
has no effect for GKE clusters, the API will always be deployed to a private network, the API is exposed through the ingress service if the ingress is configured to be public.
If memory and CPU is not defined in the deploy configuration and can not be derived from the handlers, some defaults will be set. Limits of 1GB of memory and 0.5 vCPUs will be set for the pods that run the API.
The OpenTelemetry Operator is used to configure a sidecar collector container for the API to collect traces and metrics. Traces will only be collected if tracing is enabled for the API.
Google Cloud Serverless
In the Google Cloud Serverless environment, REST/HTTP APIs are deployed as Google Cloud API Gateways with Google Cloud Functions for the handlers.
JWT authentication for API Gateway is used for JWT auth guards.
Custom authorisers are not supported by Google Cloud API Gateway; to get around this, Celerity will bundle the custom authoriser handler code with each protected handler and the handlers SDK that is used to build a Celerity application will take care of calling the custom authoriser before the actual handler is called.
When tracing is enabled, the built-in Google Cloud metrics and tracing offerings will be used to collect traces and metrics for the handlers. Traces and metrics can be collected into tools like Grafana with plugins that use Google Cloud Trace as a data source. Logs and metrics are captured out of the box for the API Gateway and will be collected in Google Cloud Logging and Monitoring. You can export logs and metrics to other tools like Grafana with plugins that use Google Cloud Logging and Monitoring as a data source.
Google Cloud API Gateway does not currently support tracing.
APIs can be deployed to API Gateway using deploy configuration for the Google Cloud Serverless target environment.
Google Cloud API Gateway does not support WebSocket APIs. To deploy a WebSockets API to Google Cloud, you will need to use the "Google Cloud" target environment and deploy the API as a containerised application.
Azure
In the Azure environment, APIs are deployed as a containerised version of the Celerity runtime. Authentication, authorisation and CORS configuration defined for the API is taken care of by the runtime.
APIs can be deployed to Azure Container Apps or Azure Kubernetes Service (AKS) using deploy configuration for the Azure target environment.
Container Apps
Container Apps is a relatively simple environment to deploy applications to, the API is deployed as a containerised application that is fronted by either external HTTPS ingress or internal ingress.
Autoscaling is determined based on the number of concurrent HTTP requests for public APIs, for private APIs and hybrid applications (e.g. API and message processor) KEDA-supported CPU and memory scaling triggers are used. By default, the scale definition is set to scale from 1 to 5 replicas per revision, this can be overridden in the deploy configuration.
When domain configuration is provided and the API is configured to be public-facing, a managed TLS certificate is provisioned and attached to the Container App's HTTP ingress configuration. You will need to verify the domain ownership before the certificate can be used.
Container Apps will not be associated with a private network by default, a VNet is automatically generated for you and generated VNets are publicly accessible over the internet. Read about networking for Container Apps. When you define a VPC and link it to the API, a custom VNet will be provisioned and the API will be deployed to either a private or public subnet based on the celerity.api.vpc.subnetType
annotation, defaulting to a public subnet if not specified. When the celerity.api.vpc.lbSubnetType
is set to public
, a public HTTPS ingress is provisioned for the API; when set to private
, an internal HTTP ingress is provisioned for the API. Azure's built-in zone redundancy is used to ensure high availability of the API.
Memory and CPU resources allocated to the API can be defined in the deploy configuration, when not defined, memory and CPU will be derived from the handlers configured for the API. If memory and CPU is not defined in the deploy configuration and can not be derived from the handlers, some defaults will be set. The Container App service will be allocated a limit of 1GB of memory and 0.5 vCPU per instance in the consumption plan, see allocation requirements.
The OpenTelemetry Data Agent is used to collect traces and metrics for the application. Traces will only be collected if tracing is enabled for the API.
AKS
When an API is first deployed to AKS, a new cluster is created for the API unless you specify an existing cluster to use in the deploy configuration.
When using an existing cluster, it must be configured in a way that is compatible with the VPC annotations configured for the API as well as the target compute type.
The cluster is created across 2 availability zones for better availability guarantees. Best effort zone balancing is used with Azure VM Scale Sets. The cluster is configured with an autoscaler with a minimum of 2 nodes and a maximum of 5 nodes
distributed across availability zones as per Azure's zone balancing. The default node size is Standard_D4d_v5
with 4 vCPUs and 16GB of memory, this size is chosen because of the minimum requirements for system Node Pools and in the default configuration a single node pool is shared by the system and user workloads. If the CPU or memory requirements of the API mean the default node size would not be able to comfortably run 2 instances of the API, a larger node size will be selected.
Min and max node count along with the node size can be overridden in the deploy configuration.
When domain configuration is provided and the load balancer that powers the Ingress service is public-facing, a TLS certificate is generated with Let's Encrypt via cert-manager to provision certificates for domains associated with Ingress resources in Kubernetes. The certificate is used by the Ingress object to terminate TLS traffic. You will need to verify the domain ownership before the certificate can be used.
When it comes to networking, the API will be deployed with the overlay network model in a public network as per the default AKS access mode. Read about private and public clusters for AKS.
When you define a VPC and link it to the API, the API will be deployed as a private cluster using the VNET integration feature of AKS where the control plane will not be made available through a public endpoint. The celerity.api.vpc.subnetType
annotation has no effect for AKS deployments as the networking model for Azure with it's managed Kubernetes offering is different from other cloud providers and all services running on a cluster are private by default, exposed to the internet through a load balancer or ingress controller. When celerity.api.vpc.lbSubnetType
is set to public
, an Ingress service is provisioned using the nginx ingress controller that uses an external Azure Load Balancer under the hood. When celerity.api.vpc.lbSubnetType
is set to private
, the nginx Ingress controller is configured to use an internal Azure Load Balancer, read more about the private ingress controller.
Memory and CPU resources allocated to the API pod can be defined in the deploy configuration, if not specified, the API will derive memory and CPU from handlers configured for the API. If memory and CPU is not defined in the deploy configuration and can not be derived from the handlers, some defaults will be set. The pod that runs the API will be allocated a limit of 1GB of memory and 0.5 vCPUs.
The OpenTelemetry Operator is used to configure a sidecar collector container for the API to collect traces and metrics. Traces will only be collected if tracing is enabled for the API.
Azure Serverless
In the Azure Serverless environment, REST/HTTP APIs are deployed as Azure API Management APIs with Azure Functions for the handlers.
JWT authentication for API Management Gateways is used for JWT auth guards.
Custom authorisers are not supported by Azure API Management; to get around this, Celerity will bundle the custom authoriser handler code with each protected handler and the handlers SDK that is used to build a Celerity application will take care of calling the custom authoriser before the actual handler is called.
When tracing is enabled, the trace policy is used for the API Management Gateway, these traces will go to Application Insights. You can export logs, traces and metrics to other tools like Grafana with plugins that use Azure Monitor as a data source. When it comes to the Azure Functions that power the endpoints, traces and metrics go to Application Insights by default, from which you can export logs, traces and metrics to other tools like Grafana with plugins that use Azure Monitor as a data source. OpenTelemetry for Azure Functions is also supported for some languages, you can use the deploy configuration to enable OpenTelemetry for Azure Functions.
APIs can be deployed to API Gateway using deploy configuration for the Azure Serverless target environment.
Azure API Management does not support WebSocket APIs natively, WebSocket APIs can be used as the backend server but do not get the benefits of the API Management features such as authentication.
⚠️ Limitations
There are limitations when it comes to deploying a celerity/api
in Serverless environments. Only Amazon API Gateway supports the WebSocket protocol when opting for an architecture where handlers are deployed as serverless functions (e.g. AWS Lambda).
Azure and Google Cloud do not have an equivalent for WebSocket APIs in their Serverless API Gateway/Management offerings.
Azure API Management supports WebSocket APIs where the upstream backend is itself a WebSocket server. In this situation, you would have a Celerity runtime instance as your backend WebSocket server. Celerity does not manage this for you. You would need to setup and manage your own Azure API Management instance and configure the Celerity WebSocket API as the backend server.
Footnotes
-
Examples of Serverless API Gateways include Amazon API Gateway, Google Cloud API Gateway, and Azure API Management. ↩