Serverless

Overview

"Serverless" refers to an architecture in which the infrastructure of your applications is managed by cloud providers. Contrary to its name, a serverless application does require a server but it doesn't require you to run and manage it on your own. Instead, you subscribe to a given cloud provider, such as AWS, Azure or GCP, and pay a subscription fee only for the resources you actually use. Since the resource allocation can be dynamic and depends on your current needs, the serverless model is particularly cost-effective when you want to implement a certain logic that is triggered on demand. Simply, you get your things done and don't pay for the infrastructure that sits idle.

Similarly to cloud providers, Kyma offers a service (known as "functions-as-a-service" or "FaaS") that provides a platform on which you can build, run, and manage serverless applications in Kubernetes. These applications are called Functions and are based on the Function CR objects. They are simple code snippets that implement the exact business logic you define in them. After you create a Function, you can:

  • Configure it to be triggered by events coming from external sources to which you subscribe.
  • Expose it to an external endpoint (HTTPS).

Architecture

Serverless relies on Knative Serving for deploying and managing Functions and Kubernetes Jobs for creating Docker images. See how these and other resources process a Function within a Kyma cluster:

Serverless architecture

CAUTION: Serverless imposes some requirements on the setup of Namespaces. If you create a new Namespace, do not disable sidecar injection in it as Serverless requires Istio for other resources to communicate with Functions correctly. Also, if you apply custom LimitRanges for a new Namespace, they must be higher than or equal to the limits for building Jobs' resources.

  1. Create a Function either through the UI or by applying a Function custom resource (CR). This CR contains the Function definition (business logic that you want to execute) and information on the environment on which it should run.

    NOTE: Function Controller sets the Node.js 12 runtime by default.

  2. Function Controller (FC) detects a new Function CR.

  3. FC creates a ConfigMap with the Function definition.

  4. Based on the ConfigMap, FC creates a Kubernetes Job that triggers the creation of a Function image.

  5. The Job creates a Pod with the Docker image containing the Function definition. It also pushes the image to a Docker registry.

  6. FC monitors the Job status. When the image creation finishes successfully, FC creates a Service CR (KService) that points to the Pod with the image.

  7. Knative Serving Controller (KSC) detects the new KService and reads its definition.

  8. KSC creates these resources:

    • Service Placeholder - a Kubernetes Service which has exactly the same name as the KService but has no selectors (does not point to any Pods). Its purpose is only to register the actual service name, such as helloworld, so it is unique. This service is exposed on port 80.

    • Revision - a Kubernetes Service that KSC creates after any change in the KService. Each Revision is a different version of the KService. Revisions have selectors and point to specific Pods in a given Revision. Their names take the {service-name}-{revision-number} format, such as helloworld-48thy or helloworld-vge8m.

    • Virtual Service - a cluster-local Service that communicates only with resources within the cluster. This Virtual Service points to the Istio service mesh as a gateway to Service Revisions. The Virtual Service is registered for the name specified in the Service Placeholder, for example helloworld.default.svc.cluster.local.

      NOTE: The cluster-local label in the KService instructs the KSC that it should not create an additional public Virtual Service.

    • Route - a resource that redirects HTTP requests to specific Revisions.

    • Configuration - a resource that holds information on the desired Revision configuration.

    TIP: For more details on all Knative Serving resources, read the official Knative documentation.

To sum up, the overall eventing communication with the Function takes place through the Virtual Service that points to a given Revision. By default, the Virtual Service is set to the latest Revision but that can be modified to distribute traffic between Revisions if there is a high number of incoming events.

Details

Exposing Functions

By default, Functions in Kyma are not exposed outside the cluster. The Knative Serving Controller normally creates two virtual services, one for the internal cluster communication and the other one for exposing the Function outside the cluster. To restrict the access, the Function Controller automatically sets the default serving.knative.dev/visibility=cluster-local label on a KService CR that is added to its metadata when a Function CR is processed. This limits the Knative Serving Controller to create only one, local virtual service that allows only for cluster-wide access to Function services. Other resources can access such a Function within the cluster under the {service-name}.{namespace}.svc.cluster.local endpoint, such as test-function.default.svc.cluster.local.

TIP: For more details on cluster-local services in Knative, read this document.

To expose a Function outside the cluster, you must create an APIRule custom resource (CR):

Expose a Function service

  1. Create the APIRule CR where you specify the Function to expose, define a Oathkeeper Access Rule to secure it, and list which HTTP request methods you want to enable for it.

CAUTION: If you decide to expose your Function through an unsecured endpoint, use the noop handler for the accessStrategy you define in the APIRule CR. The allow value for the handler is not supported in the current Serverless implementation.

  1. The API Gateway Controller detects a new APIRule CR and reads its definition.

  2. The API Gateway Controller creates an Istio Virtual Service and Access Rules according to details specified in the CR. Such a Function service is available under the {host-name}.{domain} endpoint, such as my-function.kyma.local.

This way you can specify multiple API Rules with different authentication methods for a single Function service.

TIP: See this tutorial for a detailed example.

Security

To eliminate potential security risks when using Functions, bear in mind these few facts:

  • Kyma does not run any security scans against Functions and their images. Before you store any sensitive data in Functions, consider the potential risk of data leakage.

  • By default, JSON Web Tokens (JWTs) issued by Dex do not provide the scope parameter for Functions. This means that if you expose your Function and secure it with a JWT, you can use the token to validate access to all Functions within the cluster.

  • Kyma does not define any authorization policies that would restrict Functions' access to other resources within the Namespace. If you deploy a Function in a given Namespace, it can freely access all events and APIs of services within this Namespace.

  • All administrators and regular users who have access to a specific Namespace in a cluster can also access:

    • Source code of all Functions within this Namespace
    • Internal Docker registry that contains Function images

Function processing

From the moment you create a Function (Function CR) until the time it is ready, it goes through three processing stages that are defined as these condition types:

  1. ConfigurationReady (PrinterColumn CONFIGURED)
  2. BuildReady (PrinterColumn BUILT)
  3. Running (PrinterColumn RUNNING)

For a Function to be considered ready, the status of all three conditions must be True:

Click to copy
NAME CONFIGURED BUILT RUNNING VERSION AGE
test-function True True True 1 18m

When you update an existing Function, conditions change asynchronously depending on the change type.

The diagrams illustrate all three core status changes in the Function processing circle that the Function Controller handles. They also list all custom resources involved in this process and specify in which cases their update is required.

NOTE: Before you start reading, see the Function CR document for the custom resource detailed definition, the list of all Function's condition types and reasons for their success or failure.

Configured

This initial phase starts when you create a Function CR with configuration specifying the Function's setup. It ends with creating a ConfigMap that is used as a building block for a Function image.

Function configured

Built

This phase involves creating and processing the Job CR. It ends successfully when the Function image is built and sent to the Docker registry. If the image already exists and only an update is required, the Docker image receives a new tag.

Updating an existing Function requires an image rebuild only if you change the Function's body (source) or dependencies (deps). An update of Function's other configuration details, such as environment variables, replicas, resources, or labels, does not require image rebuild as it only affects KService.

NOTE: Each time you update Function's configuration, the Function Controller deletes all previous Job CRs for the given Function's UID.

Function built

Running

This stage revolves around creating a KService or updating it when configuration changes were made in the Function CR or the Function image was rebuilt. In general, the KService is considered updated when both configuration and the image tag in the KService are up to date. Thanks to the implemented reconciliation loop, the Function Controller constantly observes all newly created or updated KServices. If it detects one, it fetches the KService status and only then updates the Function's status.

Function running

Configuration

Serverless chart

To configure the Serverless chart, override the default values of its values.yaml file. This document describes parameters that you can configure.

TIP: To learn more about how to use overrides in Kyma, see the following documents:

Configurable parameters

This table lists the configurable parameters, their descriptions, and default values for both cluster and local installations.

ParameterDescriptionDefault valueMinikube override
containers.manager.envs.buildRequestsCPUNumber of CPUs requested by the image-building Pod to operate.700m100m
containers.manager.envs.buildRequestsMemoryAmount of memory requested by the image-building Pod to operate.700Mi200Mi
containers.manager.envs.buildLimitsCPUMaximum number of CPUs available for the image-building Pod to use.1100m200m
containers.manager.envs.buildLimitsMemoryMaximum amount of memory available for the image-building Pod to use.1100Mi400Mi

Custom Resource

Function

The functions.serverless.kyma-project.io CustomResourceDefinition (CRD) is a detailed description of the kind of data and the format used to manage Functions within Kyma. To get the up-to-date CRD and show the output in the YAML format, run this command:

Click to copy
kubectl get crd functions.serverless.kyma-project.io -o yaml

Sample custom resource

The following Function object creates a Function which responds to HTTP requests with the "Hello John" message.

Click to copy
apiVersion: serverless.kyma-project.io/v1alpha1
kind: Function
metadata:
name: my-test-function
spec:
env:
- name: PERSON_NAME
value: "John"
deps: |
{
"name": "hellowithdeps",
"version": "0.0.1",
"dependencies": {
"end-of-stream": "^1.4.1",
"from2": "^2.3.0",
"lodash": "^4.17.5"
}
}
labels:
app: my-test-function
minReplicas: 3
maxReplicas: 3
resources:
limits:
cpu: 1
memory: 1Gi
requests:
cpu: 500m
memory: 500Mi
source: |
module.exports = {
main: function(event, context) {
const name = process.env.PERSON_NAME;
return 'Hello ' + name;
}
}
status:
conditions:
- lastTransitionTime: "2020-04-14T08:17:11Z"
message: "Function my-test-function is ready"
reason: ServiceReady
status: "True"
type: Running
- lastTransitionTime: "2020-04-14T08:16:55Z"
message: "Job my-test-function-build-552ft finished"
reason: JobFinished
status: "True"
type: BuildReady
- lastTransitionTime: "2020-04-14T08:16:16Z"
message: "ConfigMap my-test-function-xv6pc created"
reason: ConfigMapCreated
status: "True"
type: ConfigurationReady

CAUTION: When you create a Function, the exported object in the Function's body must have main as the handler name.

Custom resource properties

This table lists all the possible properties of a given resource together with their descriptions:

PropertyRequiredDescription
metadata.nameYesSpecifies the name of the CR.
metadata.namespaceNoDefines the Namespace in which the CR is available. It is set to default unless you specify otherwise.
spec.envNoSpecifies environment variables you need to export for the Function.
spec.depsNoSpecifies the Function's dependencies.
spec.labelsNoSpecifies the Function's Pod labels.
spec.minReplicasNoDefines the minimum number of Function's Pods to run at a time.
spec.maxReplicasNoDefines the maximum number of Function's Pods to run at a time.
spec.resources.limits.cpuNoDefines the maximum number of CPUs available for the Function's Pod to use.
spec.resources.limits.memoryNoDefines the maximum amount of memory available for the Function's Pod to use.
spec.resources.requests.cpuNoSpecifies the number of CPUs requested by the Function's Pod to operate.
spec.resources.requests.memoryNoSpecifies the amount of memory requested by the Function's Pod to operate.
spec.sourceYesProvides the Function's source code.
status.conditions.lastTransitionTimeNot applicableProvides a timestamp for the last time the Function's condition status changed from one to another.
status.conditions.messageNot applicableDescribes a human-readable message on the CR processing progress, success, or failure.
status.conditions.reasonNot applicableProvides information on the Function CR processing success or failure. See the Reasons section for the full list of possible status reasons and their descriptions. All status reasons are in camelCase.
status.conditions.statusNot applicableDescribes the status of processing the Function CR by the Function Controller. It can be True for success, False for failure, or Unknown if the CR processing is still in progress. If the status of all conditions is True, the overall status of the Function CR is ready.
status.conditions.typeNot applicableDescribes a substage of the Function CR processing. There are three condition types that a Function has to meet to be ready: ConfigurationReady, BuildReady, and Running. When displaying the Function status in the terminal, these types are shown under CONFIGURED, BUILT, and RUNNING columns respectively. All condition types can change asynchronously depending on the type of Function modification, but all three need to be in the True status for the Function to be considered successfully processed.

Status reasons

Processing of a Function CR can succeed, continue, or fail for one of these reasons:

ReasonTypeDescription
ConfigMapCreatedConfigurationReadyA new ConfigMap was created based on the Function CR definition.
ConfigMapUpdatedConfigurationReadyThe existing ConfigMap was updated after changes in the Function CR name, its source code or dependencies.
ConfigMapErrorConfigurationReadyThe ConfigMap could not be created or updated due to an error.
JobFailedBuildReadyThe image with the Function's configuration could not be created due to an error.
JobCreatedBuildReadyThe Kubernetes Job resource that builds the Function image was created.
JobRunningBuildReadyThe Job is in progress.
JobsDeletedBuildReadyPrevious Jobs responsible for building Function images were deleted.
JobFinishedBuildReadyThe Job was finished and the Function's image was uploaded to the Docker Registry.
ServiceCreatedRunningA new KService referencing the Function's image was created.
ServiceUpdatedRunningThe existing KService was updated after such changes as the Function's image, scaling parameters, variables, or labels.
ServiceFailedRunningThe Function's Pod crashed or could not start due to an error.
ServiceWaitingRunningCreation or update of the KService is in progress.
ServiceReadyRunningThe Function was deployed in the Namespace.

The Function custom resource relies on these Kubernetes and Knative Serving resources:

ResourceDescription
KService CROrchestrates the deployment and availability of the Function.
Kubernetes JobBuilds an image with the Function code on a runtime.

These components use this CR:

ComponentDescription
Function ControllerUses the Function CR for the detailed Function definition, including the environment on which it should run.

Tutorials

Create a Function

This tutorial shows how you can create a simple "Hello World!" Function.

Steps

Follows these steps:

  • CLI
  • Console UI

Expose a Function with an API Rule

This tutorial shows how you can expose a Function to access it outside the cluster, through an HTTP proxy. To expose it, use an APIRule custom resource (CR) managed by the in-house API Gateway Controller. This controller reacts to an instance of the APIRule CR and, based on its details, it creates an Istio Virtual Service and Oathkeeper Access Rules that specify your permissions for the exposed Function.

When you complete this tutorial, you get a Function that:

  • Is available under an unsecured endpoint (handler set to noop in the APIRule CR).
  • Accepts GET, POST, PUT, and DELETE methods.

NOTE: To learn more about securing your Function, see this tutorial.

Prerequisites

This tutorial is based on an existing Function. To create one, follow the Create a Function tutorial.

Steps

Follows these steps:

  • CLI
  • Console UI

Bind a Service Instance to a Function

This tutorial shows how you can bind a sample instance of the Redis service to a Function. After completing all steps, you will get the Function with encoded Secrets to the service. You can use them for authentication when you connect to the service to implement custom business logic of your Function.

To create the binding, you will use ServiceBinding and ServiceBindingUsage custom resources (CRs) managed by the Service Catalog.

NOTE: To learn more about binding Service Instances to applications in Kyma, read this document.

Prerequisites

This tutorial is based on an existing Function. To create one, follow the Create a Function tutorial.

Steps

Follows these steps:

  • CLI
  • Console UI

Test the Function

To test if the Secret has been properly connected to the Function:

  1. Change the Function's code to:​

    Click to copy
    module.exports = {
    main: function (event, context) {
    return "Redis port: " + process.env.REDIS_PORT;
    }
    }
  2. Expose the Function through an API Rule, and access the Function's external address. You should get this result:

    Click to copy
    Redis port: 6379

Trigger a Function with an event

This tutorial shows how to trigger a Function with an event from an Application connected to Kyma.

NOTE: To learn more about events flow in Kyma, read the eventing documentation.

Prerequisites

This tutorial is based on an existing Function. To create one, follow the Create a Function tutorial.

You must also have:

  • An Application bound to a specific Namespace. Read the tutorials to learn how to create an Application and bind it to a Namespace.
  • An event service (an API of AsyncAPI type) registered in the desired Application. Learn here how to do it.
  • A Service Instance created for the registered service to expose events in a specific Namespace. See this tutorial for details.

Steps

Follows these steps:

  • CLI
  • Console UI

Test the trigger

CAUTION: Before you follow steps in this section and send a sample event, bear in mind that it will be propagated to all services subscribed to this event type.

To test if the Trigger CR is properly connected to the Function:

  1. Change the Function's code to:​

    Click to copy
    module.exports = {
    main: function (event, context) {
    console.log("User created: ", event.data);
    }
    }
  2. Send an event manually to trigger the Function:

    Click to copy
    curl -X POST -H "Content-Type: application/json" https://gateway.{CLUSTER_DOMAIN}/$APP_NAME/v1/events -k --cert {CERT_FILE_NAME}.crt --key {KEY_FILE_NAME}.key -d \
    '{
    "event-type": "{EVENT_TYPE}",
    "event-type-version": "{EVENT_VERSION}",
    "event-time": "2020-04-02T21:37:00Z",
    "data": "123456789"
    }'
    • CLUSTER_DOMAIN is the domain of your cluster, such as kyma.local.

    • CERT_FILE_NAME and KEY_FILE_NAME are client certificates for a given Application. You can get them by completing steps in this tutorial.

  3. After sending an event, you should get this result from logs of your Function's latest Pod:

    Click to copy
    User created: 123456789

Troubleshooting

KService revision is missing

If the Function is not running, and the KService is stuck with the RevisionMissing reason, try updating the Function. If the update does not solve the problem, follow the steps for a given Kyma release.

Kyma 1.12

  1. Copy the ServiceBindingUsage annotation to an environment variable:

    Click to copy
    export SBU_ANNOTATION=$(kubectl get services.serving.knative.dev {NAME} -n {NAMESPACE} -o=jsonpath="{.metadata.annotations['servicebindingusages\.servicecatalog\.kyma-project\.io/tracing-information']}")
  2. Delete the KService:

    Click to copy
    kubectl delete services.serving.knative.dev {NAME} -n {NAMESPACE}
  3. Add the ServiceBindingUsage annotation to the new KService that is automatically created:

    Click to copy
    if [[ -n "${SBU_ANNOTATION}" ]]; then; kubectl annotate services.serving.knative.dev {NAME} -n {NAMESPACE} "servicebindingusages.servicecatalog.kyma-project.io/tracing-information=${SBU_ANNOTATION}" --overwrite; fi

Kyma 1.13 and newer

  1. Delete the KService:

    Click to copy
    kubectl delete services.serving.knative.dev {NAME} -n {NAMESPACE}