From f4077fef7bc46c02470cdf06c055647c0a0d3872 Mon Sep 17 00:00:00 2001 From: Adam Overa Date: Thu, 19 Sep 2024 14:20:28 -0400 Subject: [PATCH 1/7] Migrating from AWS Lambda to Knative --- .../index.md | 753 ++++++++++++++++++ 1 file changed, 753 insertions(+) create mode 100644 docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md new file mode 100644 index 0000000000..618d2aa41d --- /dev/null +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -0,0 +1,753 @@ +--- +slug: migrating-from-aws-lambda-to-knative +title: "Migrating from AWS Lambda to Knative" +description: "Two to three sentences describing your guide." +og_description: "Optional two to three sentences describing your guide when shared on social media. If omitted, the `description` parameter is used within social links." +authors: ["Linode"] +contributors: ["Linode"] +published: 2024-09-19 +keywords: ['list','of','keywords','and key phrases'] +license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' +external_resources: +- '[Link Title 1](http://www.example.com)' +- '[Link Title 2](http://www.example.net)' +--- + +This guide walks through how to migrate an AWS Lambda function to a Knative function running on Linode Kubernetes Engine. + +Knative is an open-source platform that extends Kubernetes to manage serverless workloads. It provides components to deploy, run, and manage serverless applications and functions, enabling automatic scaling and efficient use of resources. Knative consists of several components: + +- **Serving**: Deploying and running serverless containers +- **Eventing**: Managing event-driven architectures +- **Functions**: Deploying and running functions locally and on Kubernetes + +## Prerequisites + +To follow along in this walkthrough, you’ll need the following: + +- A [Linode account](https://www.linode.com/cfe) +- A [Linode API token](https://www.linode.com/docs/products/platform/accounts/guides/manage-api-tokens/) +- [Git](https://git-scm.com/downloads) +- [Kubectl](https://kubernetes.io/docs/tasks/tools/) +- The [Linode CLI](https://www.linode.com/docs/products/tools/cli/guides/install/) +- Knative’s [`func` CLI](https://knative.dev/docs/functions/install-func/) + +## Step 1: Provision a Kubernetes Cluster on Linode + +There are many ways to provision resources on Linode, but this guide will use the [Linode CLI](https://github.com/linode/linode-cli). + +- [Linode Cloud Manager](https://cloud.linode.com/) +- The [Linode CLI](https://github.com/linode/linode-cli) +- The [Linode API](https://techdocs.akamai.com/linode-api/reference/api) +- [Terraform](https://registry.terraform.io/providers/linode/linode/latest/docs) +- [Pulumi](https://www.pulumi.com/registry/packages/linode/) + +### Check Kubernetes versions + +First, use the Linode CLI (`linode`) to see what Kubernetes versions are available: + +$ linode lke versions-list +┌───────┐ +│ id │ +├───────┤ +│ 1.30 │ +├───────┤ +│ 1.29 │ +└───────┘ + +In general, you should provision the latest version, unless there are some special circumstances. + +### Check available node types + +Next, check what Linode plans are available. View Linode’s pricing information [here](https://www.linode.com/cloud-computing-calculator/?promo=sitelin100-02162023&promo_value=100&promo_length=60&utm_source=google&utm_medium=cpc&utm_campaign=11178784975_112607711747&utm_term=g_kwd-46671155961_e_linode%20pricing&utm_content=467094105814&locationid=9073501&device=c_c&gad_source=1&gclid=Cj0KCQjw9Km3BhDjARIsAGUb4nzNzPsxMOeTdk2wyBd77ysa3K1UTZKH8STVYjuWeg1VeEjoubqv6GIaAl59EALw_wcB). + +$ linode linodes types + +This will print information on different Linode plans, including pricing and performance details. This guide uses the **g6-standard-2** Linode, which comes with two CPU cores and 4 GB of memory. + +To display the information for this Linode, run the following command: + +$ linode linodes types --label "Linode 4GB" --json --pretty +[ + { + "addons": {...}, + "class": "standard", + "disk": 81920, + "gpus": 0, + "id": "g6-standard-2", + "label": "Linode 4GB", + "memory": 4096, + "network_out": 4000, + "price": { + "hourly": 0.036, + "monthly": 24.0 + }, + "region_prices": [...], + "successor": null, + "transfer": 4000, + "vcpus": 2 + } +] + +### Create the Kubernetes cluster + +After selecting a Kubernetes version and a Linode type for your cluster, create a cluster in the `ca-central` region with three nodes and auto-scaling enabled. + +$ linode lke cluster-create \ + --label knative-playground \ + --k8s_version 1.30 \ + --region ca-central \ + --node_pools '[{ + "type": "g6-standard-2", + "count": 3, + "autoscaler": { + "enabled": true, + "min": 3, + "max": 8 + } + }]' + +After your cluster is successfully created, you will see the following: + +Using default values: {}; use the --no-defaults flag to disable defaults +┌─────────┬────────────────────┬────────────┬─────────────┬─────────────────────────────────┐ +│ id │ label │ region │ k8s_version │ control_plane.high_availability │ +├─────────┼────────────────────┼────────────│─────────────┼─────────────────────────────────┤ +│ 202679 │ knative-playground │ ca-central │ 1.30 │ False │ +└─────────┴────────────────────┴────────────┴─────────────┴─────────────────────────────────┘ + +### Access the Kubernetes cluster + +Next, fetch your cluster credentials to access it in the form of a kubeconfig file. The following commands will fetch a kubeconfig file for the cluster and save it in `~/.kube/lke-config`. + +$ CLUSTER_ID=$(linode lke clusters-list --json | \ + jq -r \ + '.[] | select(.label == "knative-playground") | .id') + +$ linode lke kubeconfig-view --json "$CLUSTER_ID" | \ + jq -r '.[0].kubeconfig' | \ + base64 --decode > ~/.kube/lke-config + +Access your cluster using kubectl by specifying the kubeconfig file: + +$ kubectl get no --kubeconfig ~/.kube/lke-config + +NAME STATUS ROLES AGE VERSION +lke202679-293551-06f33ccf0000 Ready 8h v1.30.1 +lke202679-293551-0bb2596c0000 Ready 8h v1.30.1 +lke202679-293551-58ccf2360000 Ready 8h v1.30.1 + +**Note**: Optionally, you can avoid needing to include `--kubeconfig ~/.kube/lke-config` with every kubectl command by setting an environment variable for your current terminal window session. + +$ export KUBECONFIG=~/.kube/lke-config + +$ kubectl get no + +NAME STATUS ROLES AGE VERSION +lke202679-293551-06f33ccf0000 Ready 8h v1.30.1 +lke202679-293551-0bb2596c0000 Ready 8h v1.30.1 +lke202679-293551-58ccf2360000 Ready 8h v1.30.1 + +** Step 2: Set Up Knative on LKE + +There are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/). This walkthrough will use the YAML manifests method. + +### Install the Knative CRDs + +Run the following command to install the Knative CRDs: + +$ RELEASE=releases/download/knative-v1.14.1/serving-crds.yaml \ + kubectl apply -f "https://github.com/knative/serving/$RELEASE" + +cus...k8s.io/certificates.networking.internal.knative.dev configured +cus...k8s.io/configurations.serving.knative.dev configured +cus...k8s.io/clusterdomainclaims.networking.internal.knative.dev configured +cus...k8s.io/domainmappings.serving.knative.dev configured cus...k8s.io/ingresses.networking.internal.knative.dev configured cus...k8s.io/metrics.autoscaling.internal.knative.dev configured cus...k8s.io/podautoscalers.autoscaling.internal.knative.dev configured +cus...k8s.io/revisions.serving.knative.dev configured cus...k8s.io/routes.serving.knative.dev configured +cus...k8s.io/serverlessservices.networking.internal.knative.dev configured +cus...k8s.io/services.serving.knative.dev configured cus...k8s.io/images.caching.internal.knative.dev configured + +### Install Knative Serving + +Next, install the Knative **Serving** component. + +$ RELEASE=releases/download/knative-v1.14.1/serving-core.yaml \ + kubectl apply -f "https://github.com/knative/serving/$RELEASE" + +pod/autoscaler-6c785b5655-r995x +pod/controller-6dd9b8448-dckv8 +pod/webhook-7dbc5d48d7-dkxm7 +service/activator-service +service/autoscaler +service/autoscaler-bucket-00-of-01 +service/controller +service/webhook +deployment.apps/activator +deployment.apps/autoscaler +deployment.apps/controller +deployment.apps/webhook +replicaset.apps/activator-5886599f75 +replicaset.apps/autoscaler-6c785b5655 +replicaset.apps/controller-6dd9b8448 +replicaset.apps/webhook-7dbc5d48d7 +horizontalpodautoscaler.autoscaling/activator horizontalpodautoscaler.autoscaling/webhook + +### Install the networking layer + +Knative relies on an underlying networking layer. There are [several options for Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer). [Kourier](https://github.com/knative-extensions/net-kourier) was designed specifically for Knative. The following commands will install Kourier and configure Knative to use Kourier as the networking layer: + +$ RELEASE=releases/download/knative-v1.14.0/kourier.yaml \ + kubectl apply -f "https://github.com/knative/net-kourier/$RELEASE" + +$ kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch \ + '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' + +**Note**: If Istio is already installed in your cluster, you may choose to [reuse it for Knative](https://knative.dev/docs/install/operator/knative-with-operators/#__tabbed_1_2) as well. + +### Record the external IP address + +With Kourier configured, the Knative serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service that can be used for external access. Retrieve the external IP address in case you want to set up your own DNS later: + +$ kubectl get service kourier -n kourier-system + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +kourier LoadBalancer 10.128.128.65 172.105.12.189 80:30580/TCP, + 443:31780/TCP 2m56s +### Verify installation + +Since Kourier added a few deployments, look at the updated list to make sure everything is in order. + +$ kubectl get deploy -n knative-serving + +NAME READY UP-TO-DATE AVAILABLE AGE +activator 1/1 1 1 5m21s +autoscaler 1/1 1 1 5m20s +controller 1/1 1 1 5m19s +net-kourier-controller 1/1 1 1 4m50s +Webhook 1/1 1 1 5m18s + +### Configure DNS + +Knative provides [multiple ways to configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns). The Magic DNS method from Knative uses the [sslip.io](http://sslip.io) DNS service. When a request is sent to a subdomain of sslip.io that has an IP address embedded, the service returns that IP address. For example, sending a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. + +The `default-domain` job configures Knative Serving to use sslip.io. + +$ MANIFEST=knative-v1.14.1/serving-default-domain.yaml \ + kubectl apply -f \ + "https://github.com/knative/serving/releases/download/$MANIFEST" + +job.batch/default-domain created +service/default-domain-service created + +With Knative now operational in your cluster, you can begin working with Knative Functions. + +## Step 3: Work with Knative Functions and the `func` CLI + +Knative Functions is a programming model that simplifies the writing of distributed applications that run on Kubernetes and Knative, without requiring in-depth knowledge of containers, Kubernetes, or Knative itself. Developers can create stateless, event-driven functions that run as Knative services and run their functions locally during development and testing without needing a local Kubernetes cluster. + +The [`func`](https://github.com/knative/func) CLI streamlines the developer experience for working with Knative Functions. + +### The `func` CLI + +The `func` CLI lets developers go through the complete lifecycle of functions—creating, building, deploying, and invoking. + +$ func + +func is the command line interface for managing Knative Function resources + + Create a new Node.js function in the current directory: + func create --language node myfunction + + Deploy the function using Docker hub to host the image: + func deploy --registry docker.io/alice + +Learn more about Functions: https://knative.dev/docs/functions/ +Learn more about Knative at: https://knative.dev + +Primary Commands: + create Create a function + describe Describe a function + deploy Deploy a function + delete Undeploy a function + list List deployed functions + subscribe Subscribe a function to events + +Development Commands: + run Run the function locally + invoke Invoke a local or remote function + build Build a function container + +System Commands: + config Configure a function + languages List available function language runtimes + templates List available function source templates + repository Manage installed template repositories + environment Display function execution environment information + +Other Commands: + completion Output functions shell completion code + version Function client version information + +Use "func --help" for more information about a given command. + +### Create a function + +Create a Python function that can be invoked via an HTTP endpoint (the default invocation method): + +$ func create -l python get-emojis + +This command creates a complete directory with multiple files. + +$ ls -laGh get-emojis +total 48K +drwxr-xr-x 3 coder 4.0K Aug 4 16:04 . +-rwxr-xr-x 1 coder 55 Aug 4 16:04 app.sh +drwxrwxr-x 2 coder 4.0K Aug 4 16:04 .func +-rw-r--r-- 1 coder 217 Aug 4 16:04 .funcignore +-rw-r--r-- 1 coder 1.8K Aug 4 16:04 func.py +-rw-r--r-- 1 coder 98 Aug 4 16:04 func.yaml +-rw-r--r-- 1 coder 28 Aug 4 16:04 Procfile +-rw-r--r-- 1 coder 862 Aug 4 16:04 README.md +-rw-r--r-- 1 coder 28 Aug 4 16:04 requirements.txt +-rw-r--r-- 1 coder 259 Aug 4 16:04 test_func.py +drwxrwxr-x 3 coder 4.0K Aug 4 16:04 .. +-rw-r--r-- 1 coder 235 Aug 4 16:04 .gitignore + +Covering the purpose of each file is outside the scope of this guide. However, you should examine the code for `func.py`, which is the default implementation that Knative generates. + +from parliament import Context +from flask import Request +import json + +# parse request body, json data or URL query parameters +def payload_print(req: Request) -> str: + if req.method == "POST": + if req.is_json: + return json.dumps(req.json) + "\n" + else: + # MultiDict needs some iteration + ret = "{" + + for key in req.form.keys(): + ret += '"' + key + '": "'+ req.form[key] + '", ' + + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + + elif req.method == "GET": + # MultiDict needs some iteration + ret = "{" + + for key in req.args.keys(): + ret += '"' + key + '": "' + req.args[key] + '", ' + + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + +# pretty print the request to stdout instantaneously +def pretty_print(req: Request) -> str: + ret = str(req.method) + ' ' + str(req.url) + ' ' + str(req.host) + '\n' + for (header, values) in req.headers: + ret += " " + str(header) + ": " + values + '\n' + + if req.method == "POST": + ret += "Request body:\n" + ret += " " + payload_print(req) + '\n' + + elif req.method == "GET": + ret += "URL Query String:\n" + ret += " " + payload_print(req) + '\n' + + return ret + +def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ + + # Add your business logic here + print("Received request") + + if 'request' in context.keys(): + ret = pretty_print(context.request) + print(ret, flush=True) + return payload_print(context.request), 200 + else: + print("Empty request", flush=True) + return "{}", 200 + +This function works as a server that returns the query params or form fields of the request sent to it. + +### Build a function image + +The next step is to create a container image from your function. Remember that the function must eventually run on a Kubernetes cluster, and that requires a containerized workload. Knative Functions facilitates this containerization for developers, abstracting the concerns related to Dockerfiles and Docker. run the `func build` command. All you need is access to a container registry. + +~/get-emojis$ func build --registry docker.io/your_username --push + +Building function image +Still building +Still building +Yes, still building +Don't give up on me +🙌 Function build: docker.io/your_username/get-emojis:latest +Pushing function image to the registry "index.docker.io" using the "your_username" user credentials + +This command fetches a base image, builds a Docker image from the function, and pushes it to the Docker registry. + +$ docker images | rg 'knative|get-emojis|ID' + +REPOSITORY TAG IMAGE ID CREATED SIZE +ghcr.io/…/builder-jammy-base latest 58e634e9a771 44 years ago 1.6GB +your_username/get-emojis latest a5c58cce8219 44 years ago 293MB + +The `CREATED` timestamp is incorrect, but the image is valid. + +### Run the function locally + +To run the function locally, use the `func run` command. + +~/get-emojis$ func run + +function up-to-date. Force rebuild with --build +Running on host port 8080 + +The function now runs on localhost at port 8080. As previously mentioned, this initial implementation returns the URL query parameters as a JSON object. With your function running, navigate to `http://localhost:8080?a=1&b=2` in your browser. You should see the following in your terminal window: + +Received request +GET http://localhost:8080/?a=1&b=2 localhost:8080 + Host: localhost:8080 + Connection: keep-alive + Cache-Control: max-age=0 + Sec-Ch-Ua: "Chromium"; v="124", "Google Chrome"… + Sec-Ch-Ua-Mobile: ?0 + Sec-Ch-Ua-Platform: "macOS" + Upgrade-Insecure-Requests: 1 + User-Agent: Mozilla/5.0 (Macintosh; Intel Mac O… + Accept: text/html,application/xhtml+xml,applica… + Sec-Fetch-Site: none + Sec-Fetch-Mode: navigate + Sec-Fetch-User: ?1 + Sec-Fetch-Dest: document + Accept-Encoding: gzip, deflate, br, zstd + Accept-Language: en-US,en;q=0.9 +URL Query String: + {"a": "1", "b": "2"} + +### Deploy the function + +Deploy the function to your Kubernetes cluster as a Knative function: + +~/get-emojis$ func deploy + +… +Pushing function image to the registry "index.docker.io" using the "your_username" user credentials +⬆️ Deploying function to the cluster +🎯 Creating Triggers on the cluster +✅ Function deployed in namespace "default" and exposed at URL: + http://get-emojis.default.172.105.12.189.sslip.io + +Once the function has been deployed and the Magic DNS record has been established, the function is ready to be invoked. + +### Invoke the function via an HTTP endpoint + +In a web browser, visit your function’s URL, adding query parameters. An example invocation may look like this: + +[SCREENSHOT} + +Your Knative function is accessible through a public HTTP endpoint. Now, it is time to migrate an AWS Lambda function to Knative. + +## Step 4: Migrate your AWS Lambda Function to Knative + +This guide will examine a sample Lambda function and walk you through how to migrate it to Knative. Conceptually, Lambda functions are similar to Knative functions. They have a trigger and a way to extract their input arguments from a context or event. + +In the handler below, the actual application logic of the Lambda function has been highlighted (in red). The function instantiates a `FuzzEmoji` object and calls its `get_emojis()` method, passing a list of emoji descriptions. The emoji descriptions may or may not map to official emoji names like "fire" (🔥) or "confused_face" (😕). The function performs a fuzzy search of the descriptions and finds matching emojis. + +def handler(event, context): + try: + logger.info("Received event: %s", event) + + # The descriptions may arrive as attribute of the event + descriptions = event.get("descriptions") + if descriptions is None: + # Parse the JSON body of the event + body = json.loads(event.get("body", "{}")) + logger.info("Parsed body: %s", body) + + descriptions = body.get("descriptions", []) + logger.info("Descriptions: %s", descriptions) + + fuzz_emoji = FuzzEmoji() + result = fuzz_emoji.get_emojis(descriptions) + response = { + 'statusCode': 200, + 'body': json.dumps(result) + } + except Exception as e: + response = { + 'statusCode': 500, + 'body': json.dumps({'error': str(e)}) + } + + return response + +Before the highlighted lines, there is code that extracts emoji descriptions from the `event` object passed to the handler. The code below the highlighted lines focuses on packaging the result with a proper status code for success or failure. + +At the time of this writing, this Lambda function was deployed and available at the following HTTP endpoint: + +https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji + +Invoking the function yields the following result: + +$ curl -s -X POST --header "Content-type:application/json" \ + --data '{"descriptions":["flame","confused"]}' \ + https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji | \ + json_pp + +{ + "confused" : "('confused_face', '😕')", + "flame" : "('fire', '🔥')" +} + +The function correctly returns the "fire" (🔥) emoji for the description "flame" and the "confused_face" emoji (😕) for the description "confused.” + +### Isolating the AWS Lambda code from AWS specifics + +The next step is to make sure the core application logic is separate from the AWS specifics. In this case, the work for this is already done, since the interface for the `get_emojis()` method only takes a list of strings as descriptions. + +If the `get_emojis()` method took the AWS Lambda `event` object as input and extracted the descriptions, itwouldn't work for a Knative function since it doesn't provide an `event` object. This would require some refactoring. + +### Migrating a single-file function to a Knative function + +The core logic for the function has been isolated into a single Python module named `fuzz_emoji.py`. This can be migrated to your Knative function. The code looks like this: + +from typing import List, Mapping, Tuple + +import emoji +import requests + +class FuzzEmoji: + def __init__(self): + self.emoji_dict = {} + emoji_list = {name: data for name, data in emoji.EMOJI_DATA.items() if 'en' in data} + for emoji_char, data in emoji_list.items(): + name = data['en'].strip(':') + self.emoji_dict[name.lower()] = emoji_char + + @staticmethod + def get_synonyms(word): + response = requests.get(f"https://api.datamuse.com/words?rel_syn={word}") + if response.status_code == 200: + synonyms = [word_data['word'] for word_data in response.json()] + return synonyms + + raise RuntimeError(response.content) + + def get_emoji(self, description) -> Tuple[str, str]: + description = description.lower() + # direct match + if description in self.emoji_dict: + return description, self.emoji_dict[description] + + # Subset match + for name in self.emoji_dict: + if description in name: + return name, self.emoji_dict[name] + + synonyms = self.get_synonyms(description) + # Synonym match + for syn in synonyms: + if syn in self.emoji_dict: + return syn, self.emoji_dict[syn] + return '', '' + + def get_emojis(self, descriptions: List[str]) -> Mapping[str, str]: + return {d: str(self.get_emoji(d)) for d in descriptions} + +Copy the module into the same directory that Knative `func` created for your function. The folder structure should now look like this: + +$ ls -laGh get-emojis/ +total 52K +drwxr-xr-x 3 coder 4.0K Aug 4 16:47 . +-rw-rw-r-- 1 coder 1.7K Aug 4 16:47 fuzz_emoji.py +-rwxr-xr-x 1 coder 55 Aug 4 16:04 app.sh +drwxrwxr-x 2 coder 4.0K Aug 4 16:04 .func +-rw-r--r-- 1 coder 217 Aug 4 16:04 .funcignore +-rw-r--r-- 1 coder 1.8K Aug 4 16:04 func.py +-rw-r--r-- 1 coder 98 Aug 4 16:04 func.yaml +-rw-r--r-- 1 coder 28 Aug 4 16:04 Procfile +-rw-r--r-- 1 coder 862 Aug 4 16:04 README.md +-rw-r--r-- 1 coder 28 Aug 4 16:04 requirements.txt +-rw-r--r-- 1 coder 259 Aug 4 16:04 test_func.py +drwxrwxr-x 3 coder 4.0K Aug 4 16:04 .. +-rw-r--r-- 1 coder 235 Aug 4 16:04 .gitignore + +Next, change the implementation of your Knative function so that it calls the `fuzz_emoji` module: + +import json + +from parliament import Context +from fuzz_emoji import FuzzEmoji + +def main(context: Context): + descriptions = context.request.args.get('descriptions').split(',') + fuzz_emoji = FuzzEmoji() + result = fuzz_emoji.get_emojis(descriptions) + return json.dumps(result, ensure_ascii=False), 200 + +Here is a breakdown of what is happening in this code: + +1. Import the built-in `json`, the `Context` from [parliament](https://github.com/boson-project/parliament) (which is the function invocation framework that Knative uses for Python functions), and the `FuzzEmoji` class. + +1. The `main()` function takes the parliament `Context` as its only parameter. The context contains a Flask `request` property. + +1. The first line extracts the emoji descriptions from the Flask `request` arguments. It expects the descriptions to be a single comma-separated string, which it splits to get a list of `descriptions`. + +1. Instantiate a `FuzzEmoji` object and call its `get_emojis()` method. + +1. Use the `json` module to serialize the response and return it with a `200` status code. + +Repeat the workflow you used earlier for building and deploying the Knative function. Before starting, it's important to add the requirements of `fuzz_emoji.py` (the `requests` and `emoji` packages) to the `requirements.txt` file of your Knative function since these dependencies are added to the Docker image of the function. + +parliament-functions==0.1.0 +emoji==2.12.1 +requests==2.32.3 + +Build and deploy the container: + +~/get-emojis$ func build --registry docker.io/your_username +~/get-emojis$ func deploy + +Finally, test your function using the public URL: + +[SCREENSHOT] + +The `descriptions` provided as a query parameter are echoed back, along with a corresponding emoji name and emoji for each description. The Knative function works as expected. + +### Migrating a multi-file function to a Knative function + +In the above case, the entire application logic was contained in a single file called `fuzz_emoji.py`. For larger workloads, you might have an implementation with multiple files or even multiple directories and packages. + +Migrating to Knative follows a similar process. You would take the following steps: + +1. Copy all the files and directories into the same `get-emojis` directory. + +1. Import any module you need in `func.py`. + +1. Update the `requirements.txt` file with all the dependencies imported in any module. + +### Migrating external dependencies + +When migrating an AWS Lambda function, it’s possible that the function depends on various AWS services—like S3, DynamoDB, SQS, or others. You need to understand the use case for each dependency so that you can decide which option best suits your situation. + +There are typically three options: + +1. **Keep it as it is**: The Knative function will also interact with the AWS service. + +1. **Replace the service**: For example, you might switch from an AWS service like DynamoDB to an alternative key-value store in the Kubernetes cluster. + +1. **Drop the functionality**: For example, don’t write messages to AWS SQS anymore. + +### Namespace and service account + +The Knative function eventually runs as a pod in the Kubernetes cluster. This means it runs in a namespace and has a Kubernetes service account associated with it. These are determined when you run `func deploy`. You can specify `-n` (or `--namespace`) and `--service-account` arguments. + +If you don't specify these, then the function will be deployed in the currently configured namespace for the cluster and will use the default service account of the namespace. + +If your Knative function needs to access any Kubernetes resources, it’s recommended that you explicitly specify a dedicated namespace and create a dedicated service account. This is the preferred approach, rather than granting permissions to the default service account of the namespace. + +### Configuration and secrets + +Your AWS Lambda function may have used the `ParameterStore` and `SecretsManager` for configuration and sensitive information, neither of which should be embedded in the function's image. For example, if your function needs to access some AWS services, it would require AWS credentials to authenticate. + +Kubernetes offers the [`ConfigMap`](https://kubernetes.io/docs/concepts/configuration/configmap/) and [`Secret`](https://kubernetes.io/docs/concepts/configuration/secret/) resources for this purpose. The migration process involves the following steps: + +1. Identify all the parameters and secrets the Lambda function uses. + +1. Create corresponding `ConfigMap` and `Secret` resources in the namespace for your Knative function. + +1. Grant the service account for your Knative function permissions to read the `ConfigMap` and `Secret`. + +### Roles and permissions + +As part of the migration, your Knative function may need to interact with various Kubernetes resources and services—such as data stores, `ConfigMaps`, and `Secrets`. Create a dedicated role with the necessary permissions, binding that role to the function's service account. + +If your architecture is based on multiple Knative functions, it is considered best practice to share the same service account, role, and role bindings between all the Knative functions. + +### Logging, metrics, and distributed tracing + +The logging experience in Knative is similar to printing something in your AWS Lambda function. In AWS Lambda, output is automatically logged to CloudWatch. That same print statement in a Knative function automatically sends log messages to your container's logs. If you have centralized logging, these messages are automatically recorded in your log system. + +LKE provides the native Kubernetes dashboard by default. It runs on the control plane, so it doesn't take resources from your workloads. You can use the dashboard to explore your entire cluster: + +[SCREENSHOT] + +For production systems, consider using a centralized logging system like ELK/EFK, Loki, or Graylog. Also, use an observability solution like Prometheus and Grafana. Consider leveraging OpenTelemetry, too. These are good practices for production systems because they enhance the ability to monitor, troubleshoot, and optimize application performance while ensuring reliability and scalability. + +Knative has built-in support for distributed tracing, and it can be configured globally. Your Knative function will automatically participate. + +### The debugging experience + +Knative offers a debugging experience at multiple levels: + +- Unit test your core logic +- Unit test your Knative function +- Invoke your function locally + +When you create a Python Knative function, Knative also generates a skeleton for a unit test, called `test_func.py`. At the time of this writing, the generated test is invalid and requires some changes. See this [GitHub issue](https://github.com/knative/func/issues/2448) for details. + +Below is the modified test, updated for testing the fuzzy emoji search: + +import unittest +from parliament import Context + +func = __import__("func") + +class DummyRequest: + def __init__(self, descriptions): + self.descriptions = descriptions + + @property + def args(self): + return dict(descriptions=self.descriptions) + +class TestFunc(unittest.TestCase): + # noinspection PyTypeChecker + def test_func(self): + result, code = func.main(Context(DummyRequest('flame,confused'))) + expected = """{"flame": "('fire', '🔥')", "confused": "('confused_face', '😕')"}""" + self.assertEqual(expected, result) + self.assertEqual(code, 200) + +if __name__ == "__main__": + unittest.main() + +With this unit test, you can test the invocation of the function in any Python IDE or by using [pdb](https://docs.python.org/3/library/pdb.html) to place breakpoints and step through the code. If your function interacts with external services or the Kubernetes API server, you will need to mock these dependencies. Simulating external services or components that a function interacts with during testing (or mocking dependencies) allows you to isolate the specific function or piece of code you are testing to ensure that its behavior is correct. + +When you're satisfied with the code, you can test locally by packaging the function in a Docker container and running it with `func invoke`. This is done fully through Docker, without need for a local Kubernetes cluster. + +At this point, you may want to fine-tune the size of the function image by removing any redundant dependencies. This helps to optimize resource utilization. + +Finally, you can deploy your function to a full-fledged staging environment (Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment you can conduct integration, regression, and stress tests. + +The resources below are provided to help you become familiar with migrating AWS Lambda functions to Knative functions on Linode Kubernetes Engine. + +## Resources + +- [Knative](https://knative.dev/docs/) +- [Knative Functions](https://knative.dev/docs/functions/) +- [Knative Functions - Deep Dive (Video)](https://www.youtube.com/watch?v=l0EooTOGW84) +- [Accessing request traces - Knative](https://knative.dev/docs/serving/accessing-traces/) +- [Migrating from AWS Lambda to Knative Functions](https://knative.dev/blog/articles/aws_to_func_migration/) +- [GitHub - boson-project/parliament: A function invocation framework for Python](https://github.com/boson-project/parliament) +- [Logging and Metrics with Amazon CloudWatch](https://docs.aws.amazon.com/lambda/latest/operatorguide/logging-metrics.html) +- [Prometheus](https://prometheus.io) +- [Grafana Labs - Loki, Grafana, Tempo, Mimir](https://grafana.com) +- [OpenTelemetry](https://opentelemetry.io) + +The source code for this demo walkthrough is available here: + +- [AWS Lambda function](https://github.com/the-gigi/fuzz-emoji/tree/main/aws_lambda) +- [Knative function (Python)](https://github.com/the-gigi/fuzz-emoji/tree/main/knative_functions/python) \ No newline at end of file From fbc986b44a3e696e509c88d5ebcf6e46878373c3 Mon Sep 17 00:00:00 2001 From: Adam Overa Date: Wed, 2 Oct 2024 13:00:07 -0400 Subject: [PATCH 2/7] Tech Edit 1 --- ci/vale/dictionary.txt | 2 + .../index.md | 509 ++++++++++++------ 2 files changed, 356 insertions(+), 155 deletions(-) diff --git a/ci/vale/dictionary.txt b/ci/vale/dictionary.txt index c1b9a0b15b..566a16650e 100644 --- a/ci/vale/dictionary.txt +++ b/ci/vale/dictionary.txt @@ -1247,6 +1247,7 @@ klei klocwork kloth kloxo +knative knockpy knowledgebase Kodu @@ -1255,6 +1256,7 @@ Konqueror konsole konversation kotin +kourier KPI KPIs krita diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md index 618d2aa41d..dfbf3cb97b 100644 --- a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -13,64 +13,98 @@ external_resources: - '[Link Title 2](http://www.example.net)' --- -This guide walks through how to migrate an AWS Lambda function to a Knative function running on Linode Kubernetes Engine. +This guide walks through the process of migrating an AWS Lambda function to a Knative function running on the Linode Kubernetes Engine (LKE). -Knative is an open-source platform that extends Kubernetes to manage serverless workloads. It provides components to deploy, run, and manage serverless applications and functions, enabling automatic scaling and efficient use of resources. Knative consists of several components: +Knative is an open source platform that extends Kubernetes to manage serverless workloads. It provides tools to deploy, run, and manage serverless applications and functions, enabling automatic scaling and efficient resource usage. Knative consists of several components: -- **Serving**: Deploying and running serverless containers -- **Eventing**: Managing event-driven architectures -- **Functions**: Deploying and running functions locally and on Kubernetes +- **Serving**: Deployment and runs serverless containers. +- **Eventing**: Facilitates event-driven architectures. +- **Functions**: Deploys and runs functions locally and on Kubernetes. ## Prerequisites -To follow along in this walkthrough, you’ll need the following: +To complete this walkthrough, you need the following: -- A [Linode account](https://www.linode.com/cfe) -- A [Linode API token](https://www.linode.com/docs/products/platform/accounts/guides/manage-api-tokens/) +- [Linode Account](https://www.linode.com/cfe) +- [Linode API Token](https://www.linode.com/docs/products/platform/accounts/guides/manage-api-tokens/) - [Git](https://git-scm.com/downloads) - [Kubectl](https://kubernetes.io/docs/tasks/tools/) -- The [Linode CLI](https://www.linode.com/docs/products/tools/cli/guides/install/) -- Knative’s [`func` CLI](https://knative.dev/docs/functions/install-func/) +- [Linode CLI](https://www.linode.com/docs/products/tools/cli/guides/install/) +- [Knative’s `func` CLI](https://knative.dev/docs/functions/install-func/) -## Step 1: Provision a Kubernetes Cluster on Linode +## Step 1: Provision a Kubernetes Cluster -There are many ways to provision resources on Linode, but this guide will use the [Linode CLI](https://github.com/linode/linode-cli). +This guide uses the [Linode CLI](https://github.com/linode/linode-cli) to provision resources, but there are several ways to create a Kubernetes cluster on Linode: - [Linode Cloud Manager](https://cloud.linode.com/) -- The [Linode CLI](https://github.com/linode/linode-cli) -- The [Linode API](https://techdocs.akamai.com/linode-api/reference/api) +- [Linode CLI](https://github.com/linode/linode-cli) +- [Linode API](https://techdocs.akamai.com/linode-api/reference/api) - [Terraform](https://registry.terraform.io/providers/linode/linode/latest/docs) - [Pulumi](https://www.pulumi.com/registry/packages/linode/) -### Check Kubernetes versions +### Check Available Kubernetes Versions First, use the Linode CLI (`linode`) to see what Kubernetes versions are available: -$ linode lke versions-list -┌───────┐ -│ id │ -├───────┤ -│ 1.30 │ -├───────┤ -│ 1.29 │ -└───────┘ +```command +linode lke versions-list +``` -In general, you should provision the latest version, unless there are some special circumstances. +```output +┌──────┐ +│ id │ +├──────┤ +│ 1.31 │ +├──────┤ +│ 1.30 │ +├──────┤ +│ 1.29 │ +└──────┘ +``` -### Check available node types +It's generally recommended to provision the latest version unless specific requirements dictate otherwise. + +### Check Available Node Types Next, check what Linode plans are available. View Linode’s pricing information [here](https://www.linode.com/cloud-computing-calculator/?promo=sitelin100-02162023&promo_value=100&promo_length=60&utm_source=google&utm_medium=cpc&utm_campaign=11178784975_112607711747&utm_term=g_kwd-46671155961_e_linode%20pricing&utm_content=467094105814&locationid=9073501&device=c_c&gad_source=1&gclid=Cj0KCQjw9Km3BhDjARIsAGUb4nzNzPsxMOeTdk2wyBd77ysa3K1UTZKH8STVYjuWeg1VeEjoubqv6GIaAl59EALw_wcB). -$ linode linodes types +Use the following command to list different Linode plans, including pricing and performance details: + +```command +linode linodes types +``` -This will print information on different Linode plans, including pricing and performance details. This guide uses the **g6-standard-2** Linode, which comes with two CPU cores and 4 GB of memory. +This guide uses the **g6-standard-2** Linode, which features two CPU cores and 4 GB of memory. -To display the information for this Linode, run the following command: +To display detailed information for this Linode, run the following command: -$ linode linodes types --label "Linode 4GB" --json --pretty +```command +linode linodes types --label "Linode 4GB" --json --pretty +``` + +```output [ { - "addons": {...}, + "addons": { + "backups": { + "price": { + "hourly": 0.008, + "monthly": 5.0 + }, + "region_prices": [ + { + "hourly": 0.009, + "id": "id-cgk", + "monthly": 6.0 + }, + { + "hourly": 0.01, + "id": "br-gru", + "monthly": 7.0 + } + ] + } + }, "class": "standard", "disk": 81920, "gpus": 0, @@ -82,21 +116,34 @@ $ linode linodes types --label "Linode 4GB" --json --pretty "hourly": 0.036, "monthly": 24.0 }, - "region_prices": [...], + "region_prices": [ + { + "hourly": 0.043, + "id": "id-cgk", + "monthly": 28.8 + }, + { + "hourly": 0.05, + "id": "br-gru", + "monthly": 33.6 + } + ], "successor": null, "transfer": 4000, "vcpus": 2 } ] +``` -### Create the Kubernetes cluster +### Create the Kubernetes Cluster -After selecting a Kubernetes version and a Linode type for your cluster, create a cluster in the `ca-central` region with three nodes and auto-scaling enabled. +With a Kubernetes version and Linode type selected, use the following command to create a cluster in the `ca-central` region with three nodes and auto-scaling: -$ linode lke cluster-create \ +```command +linode lke cluster-create \ --label knative-playground \ - --k8s_version 1.30 \ - --region ca-central \ + --k8s_version 1.31 \ + --region us-mia \ --node_pools '[{ "type": "g6-standard-2", "count": 3, @@ -106,59 +153,89 @@ $ linode lke cluster-create \ "max": 8 } }]' +``` -After your cluster is successfully created, you will see the following: +Once your cluster is successfully created, you should see a message similar to the following: +```output Using default values: {}; use the --no-defaults flag to disable defaults ┌─────────┬────────────────────┬────────────┬─────────────┬─────────────────────────────────┐ │ id │ label │ region │ k8s_version │ control_plane.high_availability │ ├─────────┼────────────────────┼────────────│─────────────┼─────────────────────────────────┤ │ 202679 │ knative-playground │ ca-central │ 1.30 │ False │ └─────────┴────────────────────┴────────────┴─────────────┴─────────────────────────────────┘ +``` -### Access the Kubernetes cluster +### Access the Kubernetes Cluster -Next, fetch your cluster credentials to access it in the form of a kubeconfig file. The following commands will fetch a kubeconfig file for the cluster and save it in `~/.kube/lke-config`. +To access your cluster, fetch the cluster credentials in the form of a `kubeconfig` file. Use the following commands to retrieve the cluster's `kubeconfig` file and save it to `~/.kube/lke-config`: -$ CLUSTER_ID=$(linode lke clusters-list --json | \ +```command +CLUSTER_ID=$(linode lke clusters-list --json | \ jq -r \ '.[] | select(.label == "knative-playground") | .id') +``` -$ linode lke kubeconfig-view --json "$CLUSTER_ID" | \ +```command +mkdir ~/.kube +``` + +```command +linode lke kubeconfig-view --json "$CLUSTER_ID" | \ jq -r '.[0].kubeconfig' | \ base64 --decode > ~/.kube/lke-config +``` -Access your cluster using kubectl by specifying the kubeconfig file: +Once you have the `kubeconfig` file, access your cluster using `kubectl` and specifying the file: -$ kubectl get no --kubeconfig ~/.kube/lke-config +```command +kubectl get no --kubeconfig ~/.kube/lke-config +``` +```output NAME STATUS ROLES AGE VERSION lke202679-293551-06f33ccf0000 Ready 8h v1.30.1 lke202679-293551-0bb2596c0000 Ready 8h v1.30.1 lke202679-293551-58ccf2360000 Ready 8h v1.30.1 +``` + +{{< note >}} +Optionally, to avoid specifying `--kubeconfig ~/.kube/lke-config` with every `kubectl` command, you can set an environment variable for your current terminal session: -**Note**: Optionally, you can avoid needing to include `--kubeconfig ~/.kube/lke-config` with every kubectl command by setting an environment variable for your current terminal window session. +```command +export KUBECONFIG=~/.kube/lke-config +``` -$ export KUBECONFIG=~/.kube/lke-config +Then you can simply run: -$ kubectl get no +```command +kubectl get no +``` +```output NAME STATUS ROLES AGE VERSION lke202679-293551-06f33ccf0000 Ready 8h v1.30.1 lke202679-293551-0bb2596c0000 Ready 8h v1.30.1 lke202679-293551-58ccf2360000 Ready 8h v1.30.1 +``` +{{< /note >}} -** Step 2: Set Up Knative on LKE +## Step 2: Set Up Knative on LKE -There are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/). This walkthrough will use the YAML manifests method. +There are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/). This walkthrough uses the YAML manifests method. ### Install the Knative CRDs Run the following command to install the Knative CRDs: -$ RELEASE=releases/download/knative-v1.14.1/serving-crds.yaml \ - kubectl apply -f "https://github.com/knative/serving/$RELEASE" +```command +RELEASE=releases/download/knative-v1.15.2/serving-crds.yaml +kubectl apply -f "https://github.com/knative/serving/$RELEASE" +``` +Upon successful execution, you should see a similar output indicating that the CRDs are configured: + +```output cus...k8s.io/certificates.networking.internal.knative.dev configured cus...k8s.io/configurations.serving.knative.dev configured cus...k8s.io/clusterdomainclaims.networking.internal.knative.dev configured @@ -166,14 +243,20 @@ cus...k8s.io/domainmappings.serving.knative.dev configured cus...k8s.io/ingresse cus...k8s.io/revisions.serving.knative.dev configured cus...k8s.io/routes.serving.knative.dev configured cus...k8s.io/serverlessservices.networking.internal.knative.dev configured cus...k8s.io/services.serving.knative.dev configured cus...k8s.io/images.caching.internal.knative.dev configured +``` ### Install Knative Serving -Next, install the Knative **Serving** component. +Next, run the following command to install the Knative **Serving** component: + +```command +RELEASE=releases/download/knative-v1.15.2/serving-core.yaml +kubectl apply -f "https://github.com/knative/serving/$RELEASE" +``` -$ RELEASE=releases/download/knative-v1.14.1/serving-core.yaml \ - kubectl apply -f "https://github.com/knative/serving/$RELEASE" +Upon successful completion, you should see similar output indicating that various resources are now created: +```output pod/autoscaler-6c785b5655-r995x pod/controller-6dd9b8448-dckv8 pod/webhook-7dbc5d48d7-dkxm7 @@ -191,71 +274,103 @@ replicaset.apps/autoscaler-6c785b5655 replicaset.apps/controller-6dd9b8448 replicaset.apps/webhook-7dbc5d48d7 horizontalpodautoscaler.autoscaling/activator horizontalpodautoscaler.autoscaling/webhook +``` -### Install the networking layer +### Install the Networking Layer -Knative relies on an underlying networking layer. There are [several options for Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer). [Kourier](https://github.com/knative-extensions/net-kourier) was designed specifically for Knative. The following commands will install Kourier and configure Knative to use Kourier as the networking layer: +Knative relies on an underlying networking layer. There are [several options for Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer). This guide uses [Kourier](https://github.com/knative-extensions/net-kourier), which is designed specifically for Knative. Use the following commands to install Kourier and configure Knative to use it as the networking layer: -$ RELEASE=releases/download/knative-v1.14.0/kourier.yaml \ - kubectl apply -f "https://github.com/knative/net-kourier/$RELEASE" +```command +RELEASE=releases/download/knative-v1.15.1/kourier.yaml +kubectl apply -f "https://github.com/knative-extensions/net-kourier/$RELEASE" +``` -$ kubectl patch configmap/config-network \ +```command +kubectl patch configmap/config-network \ --namespace knative-serving \ --type merge \ --patch \ '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' +``` -**Note**: If Istio is already installed in your cluster, you may choose to [reuse it for Knative](https://knative.dev/docs/install/operator/knative-with-operators/#__tabbed_1_2) as well. +{{< note >}} +If Istio is already installed in your cluster, you may choose to [reuse it for Knative](https://knative.dev/docs/install/operator/knative-with-operators/#__tabbed_1_2) as well. +{{< /note >}} -### Record the external IP address +### Record the External IP Address -With Kourier configured, the Knative serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service that can be used for external access. Retrieve the external IP address in case you want to set up your own DNS later: +With Kourier configured, the Knative Serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service for external access. Use the following command to retrieve the external IP address, in case you want to set up your own DNS later: -$ kubectl get service kourier -n kourier-system +```command +kubectl get service kourier -n kourier-system +``` +The output should resemble the following, with the external IP address shown: + +```output NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kourier LoadBalancer 10.128.128.65 172.105.12.189 80:30580/TCP, 443:31780/TCP 2m56s +``` + ### Verify installation -Since Kourier added a few deployments, look at the updated list to make sure everything is in order. +Since Kourier added several deployments, check the updated list to ensure everything is functioning correctly: -$ kubectl get deploy -n knative-serving +```command +kubectl get deploy -n knative-serving +``` +You should see output similar to the following, confirming the availability of the various components: + +```output NAME READY UP-TO-DATE AVAILABLE AGE activator 1/1 1 1 5m21s autoscaler 1/1 1 1 5m20s controller 1/1 1 1 5m19s net-kourier-controller 1/1 1 1 4m50s Webhook 1/1 1 1 5m18s +``` ### Configure DNS -Knative provides [multiple ways to configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns). The Magic DNS method from Knative uses the [sslip.io](http://sslip.io) DNS service. When a request is sent to a subdomain of sslip.io that has an IP address embedded, the service returns that IP address. For example, sending a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. +Knative offers [multiple ways to configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns). This guide uses the Magic DNS method, which leverages the [sslip.io](http://sslip.io) DNS service. When a request is made to a subdomain of sslip.io containing an embedded IP address, the service resolves that IP address. For example, a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. + +Use the `default-domain` job to configures Knative Serving to use sslip.io: -The `default-domain` job configures Knative Serving to use sslip.io. +```command +MANIFEST=knative-v1.15.2/serving-default-domain.yaml +kubectl apply -f "https://github.com/knative/serving/releases/download/$MANIFEST" +``` -$ MANIFEST=knative-v1.14.1/serving-default-domain.yaml \ - kubectl apply -f \ - "https://github.com/knative/serving/releases/download/$MANIFEST" +You should see output similar to the following upon successful execution, confirming the creation of the default domain job and service: +```output job.batch/default-domain created service/default-domain-service created +``` With Knative now operational in your cluster, you can begin working with Knative Functions. ## Step 3: Work with Knative Functions and the `func` CLI -Knative Functions is a programming model that simplifies the writing of distributed applications that run on Kubernetes and Knative, without requiring in-depth knowledge of containers, Kubernetes, or Knative itself. Developers can create stateless, event-driven functions that run as Knative services and run their functions locally during development and testing without needing a local Kubernetes cluster. +Knative Functions is a programming model that simplifies writing distributed applications on Kubernetes and Knative. It allows developers to create stateless, event-driven functions without requiring in-depth knowledge of containers, Kubernetes, or Knative itself. -The [`func`](https://github.com/knative/func) CLI streamlines the developer experience for working with Knative Functions. +The [`func`](https://github.com/knative/func) CLI streamlines the developer experience by providing tools to work with Knative Functions. This enables local development and testing of functions without the need for a local Kubernetes cluster. ### The `func` CLI -The `func` CLI lets developers go through the complete lifecycle of functions—creating, building, deploying, and invoking. +The `func` CLI allows developers to manage the entire lifecycle of functions (creating, building, deploying, and invoking). -$ func +To get started, run the following command: +```command +func +``` + +This displays help information for managing Knative Function resources: + +```output func is the command line interface for managing Knative Function resources Create a new Node.js function in the current directory: @@ -292,16 +407,27 @@ Other Commands: version Function client version information Use "func --help" for more information about a given command. +``` + +### Create a Function + +To create a Python function that can be invoked via an HTTP endpoint (the default invocation method), use the following command: -### Create a function +```command +func create -l python get-emojis +``` -Create a Python function that can be invoked via an HTTP endpoint (the default invocation method): +```output +Created python function in /home/{{< placeholder "USERNAME" >}}/get-emojis +``` -$ func create -l python get-emojis +This command creates a complete directory structure with multiple files: -This command creates a complete directory with multiple files. +```command +ls -laGh get-emojis +``` -$ ls -laGh get-emojis +```output total 48K drwxr-xr-x 3 coder 4.0K Aug 4 16:04 . -rwxr-xr-x 1 coder 55 Aug 4 16:04 app.sh @@ -315,9 +441,11 @@ drwxrwxr-x 2 coder 4.0K Aug 4 16:04 .func -rw-r--r-- 1 coder 259 Aug 4 16:04 test_func.py drwxrwxr-x 3 coder 4.0K Aug 4 16:04 .. -rw-r--r-- 1 coder 235 Aug 4 16:04 .gitignore +``` -Covering the purpose of each file is outside the scope of this guide. However, you should examine the code for `func.py`, which is the default implementation that Knative generates. +Covering the purpose of each file is outside the scope of this guide. However, you should examine the `func.py` file, which is the default implementation that Knative generates.: +```file {title="func.py" lang="python"} from parliament import Context from flask import Request import json @@ -378,15 +506,23 @@ def main(context: Context): else: print("Empty request", flush=True) return "{}", 200 +``` -This function works as a server that returns the query params or form fields of the request sent to it. +This function acts as a server that returns the query parameters or form fields of the incoming request. -### Build a function image +### Build a Function Image -The next step is to create a container image from your function. Remember that the function must eventually run on a Kubernetes cluster, and that requires a containerized workload. Knative Functions facilitates this containerization for developers, abstracting the concerns related to Dockerfiles and Docker. run the `func build` command. All you need is access to a container registry. +The next step is to create a container image from your function. Since the function will run on a Kubernetes cluster, it must be containerized. Knative Functions facilitates this process for developers, abstracting the complexities of Docker and Dockerfiles. -~/get-emojis$ func build --registry docker.io/your_username --push +To build and push your function, run the following `func build` command, ensuring you have access to a container registry: +```commandf +func build --registry docker.io/your_username --push +``` + +You should see output similar to the following as the function image is built: + +```output Building function image Still building Still building @@ -394,28 +530,50 @@ Yes, still building Don't give up on me 🙌 Function build: docker.io/your_username/get-emojis:latest Pushing function image to the registry "index.docker.io" using the "your_username" user credentials +``` -This command fetches a base image, builds a Docker image from the function, and pushes it to the Docker registry. +This command fetches a base image, builds a Docker image from your function, and then pushes it to the Docker registry. -$ docker images | rg 'knative|get-emojis|ID' +To verify that the image is successfully created, use the following command to list your Docker images: +```command +docker images | rg 'knative|get-emojis|ID' +``` + +```output REPOSITORY TAG IMAGE ID CREATED SIZE ghcr.io/…/builder-jammy-base latest 58e634e9a771 44 years ago 1.6GB your_username/get-emojis latest a5c58cce8219 44 years ago 293MB +``` + +Note that while the `CREATED` timestamp may be incorrect, the image is valid. -The `CREATED` timestamp is incorrect, but the image is valid. +### Run the Function Locally -### Run the function locally +You can run the function locally to test its behavior before deploying it to a Kubernetes cluster. Use the following `func run` command to run the function locally: -To run the function locally, use the `func run` command. +```command +func run +``` -~/get-emojis$ func run +The terminal should display output indicating that the function is set up and running: +```output function up-to-date. Force rebuild with --build Running on host port 8080 +``` -The function now runs on localhost at port 8080. As previously mentioned, this initial implementation returns the URL query parameters as a JSON object. With your function running, navigate to `http://localhost:8080?a=1&b=2` in your browser. You should see the following in your terminal window: +The function now runs on `localhost` at port `8080`. By default, this initial implementation returns the URL query parameters as a JSON object. +With your function running, open a web browser and navigate to the following URL: + +```command +http://localhost:8080?a=1&b=2 +``` + +You should see the output similar to the following in your terminal window: + +```output Received request GET http://localhost:8080/?a=1&b=2 localhost:8080 Host: localhost:8080 @@ -435,36 +593,52 @@ GET http://localhost:8080/?a=1&b=2 localhost:8080 Accept-Language: en-US,en;q=0.9 URL Query String: {"a": "1", "b": "2"} +``` + +### Deploy the Function -### Deploy the function +To deploy your function to a Kubernetes cluster as a Knative function, use the following `func deploy` command: -Deploy the function to your Kubernetes cluster as a Knative function: +```command +func deploy +``` -~/get-emojis$ func deploy +You should see output similar to the following during deployment: +```output … Pushing function image to the registry "index.docker.io" using the "your_username" user credentials ⬆️ Deploying function to the cluster 🎯 Creating Triggers on the cluster ✅ Function deployed in namespace "default" and exposed at URL: http://get-emojis.default.172.105.12.189.sslip.io +``` -Once the function has been deployed and the Magic DNS record has been established, the function is ready to be invoked. +The function is ready to be invoked once the function is deployed and the Magic DNS record established. -### Invoke the function via an HTTP endpoint +### Invoke the Function via an HTTP Endpoint -In a web browser, visit your function’s URL, adding query parameters. An example invocation may look like this: +To invoke your Knative function, open a web browser and visit the function’s public URL, adding any required query parameters. For example: -[SCREENSHOT} +```command +http://get-emojis.default.172.105.12.189.sslip.io/?yeah=it-works! +``` -Your Knative function is accessible through a public HTTP endpoint. Now, it is time to migrate an AWS Lambda function to Knative. +The browser should display a JSON object containing the query parameters: -## Step 4: Migrate your AWS Lambda Function to Knative +[SCREENSHOT] + +Your Knative function is now accessible through this public HTTP endpoint. + +With your Knative function running, the next step is migrate an AWS Lambda function to Knative. + +## Step 4: Migrate Your AWS Lambda Function to Knative -This guide will examine a sample Lambda function and walk you through how to migrate it to Knative. Conceptually, Lambda functions are similar to Knative functions. They have a trigger and a way to extract their input arguments from a context or event. +This guide examines a sample Lambda function and walks you through how to migrate it to Knative. Conceptually, Lambda functions are similar to Knative functions. They both have a trigger and extract their input arguments from a context or event. -In the handler below, the actual application logic of the Lambda function has been highlighted (in red). The function instantiates a `FuzzEmoji` object and calls its `get_emojis()` method, passing a list of emoji descriptions. The emoji descriptions may or may not map to official emoji names like "fire" (🔥) or "confused_face" (😕). The function performs a fuzzy search of the descriptions and finds matching emojis. +The main application logic is highlighted in the sample Lambda function below: +```file {lang="python" hl_lines="15-16"} def handler(event, context): try: logger.info("Received event: %s", event) @@ -492,15 +666,21 @@ def handler(event, context): } return response +``` -Before the highlighted lines, there is code that extracts emoji descriptions from the `event` object passed to the handler. The code below the highlighted lines focuses on packaging the result with a proper status code for success or failure. +This function instantiates a `FuzzEmoji` object and calls its `get_emojis()` method, passing a list of emoji descriptions. The emoji descriptions may or may not map to official emoji names like `fire` (🔥) or `confused_face` (😕). The function performs a fuzzy search of the descriptions to find matching emojis. -At the time of this writing, this Lambda function was deployed and available at the following HTTP endpoint: +The code above the highlighted lines extracts emoji descriptions from the `event` object passed to the handler. The code below the highlighted lines wraps the result in a response with a proper status code for success or failure. +At the time of this writing, this sample Lambda function was deployed and available at the following HTTP endpoint: + +```command https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji +``` -Invoking the function yields the following result: +Invoking the function returns the following result: +```output $ curl -s -X POST --header "Content-type:application/json" \ --data '{"descriptions":["flame","confused"]}' \ https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji | \ @@ -510,19 +690,21 @@ $ curl -s -X POST --header "Content-type:application/json" \ "confused" : "('confused_face', '😕')", "flame" : "('fire', '🔥')" } +``` -The function correctly returns the "fire" (🔥) emoji for the description "flame" and the "confused_face" emoji (😕) for the description "confused.” +The function successfully returns the `fire` (🔥) emoji for the description "flame", and the `confused_face` emoji (😕) for the description "confused.” -### Isolating the AWS Lambda code from AWS specifics +### Isolating the AWS Lambda Code from AWS Specifics -The next step is to make sure the core application logic is separate from the AWS specifics. In this case, the work for this is already done, since the interface for the `get_emojis()` method only takes a list of strings as descriptions. +To migrate the Lambda function to Knative, the core application logic must be decoupled from AWS-specific dependencies. Fortunately, in this case, the function's main logic is already isolated. The `get_emojis()` method only accepts a list of strings as input, which makes it easy to adapt for other platforms. -If the `get_emojis()` method took the AWS Lambda `event` object as input and extracted the descriptions, itwouldn't work for a Knative function since it doesn't provide an `event` object. This would require some refactoring. +If the `get_emojis()` method were dependent on the AWS Lambda `event` object, it would not be compatible with Knative, as Knative does not provide an `event` object. This scenario would require some refactoring. -### Migrating a single-file function to a Knative function +### Migrating a Single-File Function to a Knative Function -The core logic for the function has been isolated into a single Python module named `fuzz_emoji.py`. This can be migrated to your Knative function. The code looks like this: +The core logic of the function is encapsulated into a single Python module named `fuzz_emoji.py`, which can be migrated to your Knative function. Find the module code below: +```file {lang="python"} from typing import List, Mapping, Tuple import emoji @@ -565,10 +747,17 @@ class FuzzEmoji: def get_emojis(self, descriptions: List[str]) -> Mapping[str, str]: return {d: str(self.get_emoji(d)) for d in descriptions} +``` + +Copy the module into the same directory that Knative `func` created for your function: + +```command +ls -laGh get-emojis/ +``` -Copy the module into the same directory that Knative `func` created for your function. The folder structure should now look like this: +The folder structure should now look like this: -$ ls -laGh get-emojis/ +```output total 52K drwxr-xr-x 3 coder 4.0K Aug 4 16:47 . -rw-rw-r-- 1 coder 1.7K Aug 4 16:47 fuzz_emoji.py @@ -583,9 +772,11 @@ drwxrwxr-x 2 coder 4.0K Aug 4 16:04 .func -rw-r--r-- 1 coder 259 Aug 4 16:04 test_func.py drwxrwxr-x 3 coder 4.0K Aug 4 16:04 .. -rw-r--r-- 1 coder 235 Aug 4 16:04 .gitignore +``` Next, change the implementation of your Knative function so that it calls the `fuzz_emoji` module: +```file import json from parliament import Context @@ -596,71 +787,77 @@ def main(context: Context): fuzz_emoji = FuzzEmoji() result = fuzz_emoji.get_emojis(descriptions) return json.dumps(result, ensure_ascii=False), 200 +``` -Here is a breakdown of what is happening in this code: +Here's a breakdown of what this code does: -1. Import the built-in `json`, the `Context` from [parliament](https://github.com/boson-project/parliament) (which is the function invocation framework that Knative uses for Python functions), and the `FuzzEmoji` class. +1. Imports the built-in `json`, the `Context` from [parliament](https://github.com/boson-project/parliament) (the function invocation framework that Knative uses for Python functions), and the `FuzzEmoji` class. 1. The `main()` function takes the parliament `Context` as its only parameter. The context contains a Flask `request` property. -1. The first line extracts the emoji descriptions from the Flask `request` arguments. It expects the descriptions to be a single comma-separated string, which it splits to get a list of `descriptions`. +1. The first line extracts the emoji descriptions from the Flask `request` arguments. It expects the descriptions to be a single comma-separated string, which it splits into a list of `descriptions`. -1. Instantiate a `FuzzEmoji` object and call its `get_emojis()` method. +1. Instantiates a `FuzzEmoji` object and calls its `get_emojis()` method. -1. Use the `json` module to serialize the response and return it with a `200` status code. +1. Uses the `json` module to serialize the response and return it with a `200` status code. -Repeat the workflow you used earlier for building and deploying the Knative function. Before starting, it's important to add the requirements of `fuzz_emoji.py` (the `requests` and `emoji` packages) to the `requirements.txt` file of your Knative function since these dependencies are added to the Docker image of the function. +Next, add the requirements of `fuzz_emoji.py` (the `requests` and `emoji` packages) to the `requirements.txt` file of your Knative function to include these dependencies in the Docker image. +```file parliament-functions==0.1.0 emoji==2.12.1 requests==2.32.3 +``` -Build and deploy the container: +Now build and deploy the container: -~/get-emojis$ func build --registry docker.io/your_username -~/get-emojis$ func deploy +```command +cd ~/get-emojis +func build --registry docker.io/your_username +func deploy +``` Finally, test your function using the public URL: [SCREENSHOT] -The `descriptions` provided as a query parameter are echoed back, along with a corresponding emoji name and emoji for each description. The Knative function works as expected. +The `descriptions` provided as a query parameter are echoed back, along with a corresponding emoji name and emoji for each description. This confirms that the Knative function works as expected. -### Migrating a multi-file function to a Knative function +### Migrating a Multi-File Function to a Knative Function -In the above case, the entire application logic was contained in a single file called `fuzz_emoji.py`. For larger workloads, you might have an implementation with multiple files or even multiple directories and packages. +In the previous example, the entire application logic was contained in a single file called `fuzz_emoji.py`. For larger workloads, your function might involve multiple files or even multiple directories and packages. -Migrating to Knative follows a similar process. You would take the following steps: +Migrating such a setup to Knative follows a similar process: -1. Copy all the files and directories into the same `get-emojis` directory. +1. Copy all relevant files and directories into the same `get-emojis` directory. -1. Import any module you need in `func.py`. +1. Import any required modules in `func.py`. -1. Update the `requirements.txt` file with all the dependencies imported in any module. +1. Update the `requirements.txt` file to include all of the dependencies used across any of the modules. -### Migrating external dependencies +### Migrating External Dependencies -When migrating an AWS Lambda function, it’s possible that the function depends on various AWS services—like S3, DynamoDB, SQS, or others. You need to understand the use case for each dependency so that you can decide which option best suits your situation. +When migrating an AWS Lambda function, it may depend on various AWS services, such as S3, DynamoDB, SQS, or others. It's important to evaluate each dependency to determine the best option to suit your situation. There are typically three options: -1. **Keep it as it is**: The Knative function will also interact with the AWS service. +1. **Keep it as-is**: Continue using the Knative function to interact with the AWS service. 1. **Replace the service**: For example, you might switch from an AWS service like DynamoDB to an alternative key-value store in the Kubernetes cluster. -1. **Drop the functionality**: For example, don’t write messages to AWS SQS anymore. +1. **Drop the functionality**: Eliminate certain AWS-specific functionalities, such as no longer writing messages to AWS SQS. -### Namespace and service account +### Namespace and Service Account -The Knative function eventually runs as a pod in the Kubernetes cluster. This means it runs in a namespace and has a Kubernetes service account associated with it. These are determined when you run `func deploy`. You can specify `-n` (or `--namespace`) and `--service-account` arguments. +The Knative function eventually runs as a pod in the Kubernetes cluster. This means it runs in a namespace and has a Kubernetes service account associated with it. These are determined when you run the `func deploy` command. You can specify them using the `-n` (or `--namespace`) and `--service-account` arguments. -If you don't specify these, then the function will be deployed in the currently configured namespace for the cluster and will use the default service account of the namespace. +If you don't specify these options, the function deploys in the currently configured namespace and uses the default service account of the namespace. -If your Knative function needs to access any Kubernetes resources, it’s recommended that you explicitly specify a dedicated namespace and create a dedicated service account. This is the preferred approach, rather than granting permissions to the default service account of the namespace. +If your Knative function needs to access any Kubernetes resources, it’s recommended to explicitly specify a dedicated namespace and create a dedicated service account. This is the preferred approach because it avoids granting excessive permissions to the default service account. -### Configuration and secrets +### Configuration and Secrets -Your AWS Lambda function may have used the `ParameterStore` and `SecretsManager` for configuration and sensitive information, neither of which should be embedded in the function's image. For example, if your function needs to access some AWS services, it would require AWS credentials to authenticate. +If your AWS Lambda function uses `ParameterStore` and `SecretsManager` for configuration and sensitive information, these details should not be embedded directly in the function's image. For example, if your function needs to access AWS services, it would require AWS credentials to authenticate. Kubernetes offers the [`ConfigMap`](https://kubernetes.io/docs/concepts/configuration/configmap/) and [`Secret`](https://kubernetes.io/docs/concepts/configuration/secret/) resources for this purpose. The migration process involves the following steps: @@ -668,27 +865,27 @@ Kubernetes offers the [`ConfigMap`](https://kubernetes.io/docs/concepts/configur 1. Create corresponding `ConfigMap` and `Secret` resources in the namespace for your Knative function. -1. Grant the service account for your Knative function permissions to read the `ConfigMap` and `Secret`. +1. Grant the service account for your Knative function permissions to read `ConfigMap` and `Secret`. -### Roles and permissions +### Roles and Permissions -As part of the migration, your Knative function may need to interact with various Kubernetes resources and services—such as data stores, `ConfigMaps`, and `Secrets`. Create a dedicated role with the necessary permissions, binding that role to the function's service account. +Your Knative function may need to interact with various Kubernetes resources and services during migration, such as data stores, `ConfigMaps`, and `Secrets`. To enable this, create a dedicated role with the necessary permissions and bind it to the function's service account. -If your architecture is based on multiple Knative functions, it is considered best practice to share the same service account, role, and role bindings between all the Knative functions. +If your architecture includes multiple Knative functions, it's best practice to share the same service account, role, and role bindings between all the Knative functions. -### Logging, metrics, and distributed tracing +### Logging, Metrics, and Distributed Tracing -The logging experience in Knative is similar to printing something in your AWS Lambda function. In AWS Lambda, output is automatically logged to CloudWatch. That same print statement in a Knative function automatically sends log messages to your container's logs. If you have centralized logging, these messages are automatically recorded in your log system. +The logging experience in Knative is similar to printing something in your AWS Lambda function. In AWS Lambda, output is automatically logged to CloudWatch. In Knative, that same print statement automatically sends log messages to your container's logs. If you have centralized logging, these messages are automatically recorded in your log system. -LKE provides the native Kubernetes dashboard by default. It runs on the control plane, so it doesn't take resources from your workloads. You can use the dashboard to explore your entire cluster: +LKE provides the native Kubernetes dashboard by default. It runs on the control plane, so it doesn't take resources from your workloads. You can use the dashboard to explore and monitor your entire cluster: [SCREENSHOT] -For production systems, consider using a centralized logging system like ELK/EFK, Loki, or Graylog. Also, use an observability solution like Prometheus and Grafana. Consider leveraging OpenTelemetry, too. These are good practices for production systems because they enhance the ability to monitor, troubleshoot, and optimize application performance while ensuring reliability and scalability. +For production systems, consider using a centralized logging system like ELK/EFK, Loki, or Graylog. Also use an observability solution like Prometheus and Grafana. Consider leveraging OpenTelemetry as well. These tools can enhance the ability to monitor, troubleshoot, and optimize application performance while ensuring reliability and scalability. -Knative has built-in support for distributed tracing, and it can be configured globally. Your Knative function will automatically participate. +Knative also has built-in support for distributed tracing, which can be configured globally. This means that your Knative function automatically participates in tracing without requiring additional changes. -### The debugging experience +### The Debugging Experience Knative offers a debugging experience at multiple levels: @@ -696,10 +893,11 @@ Knative offers a debugging experience at multiple levels: - Unit test your Knative function - Invoke your function locally -When you create a Python Knative function, Knative also generates a skeleton for a unit test, called `test_func.py`. At the time of this writing, the generated test is invalid and requires some changes. See this [GitHub issue](https://github.com/knative/func/issues/2448) for details. +When you create a Python Knative function, Knative generates a skeleton for a unit test, called `test_func.py`. At the time of this writing, the generated test is invalid and requires some modifications to work correctly. See this [GitHub issue](https://github.com/knative/func/issues/2448) for details. -Below is the modified test, updated for testing the fuzzy emoji search: +Below is the modified test, updated for testing the fuzzy emoji search functionality: +```file {lang="python"} import unittest from parliament import Context @@ -723,16 +921,17 @@ class TestFunc(unittest.TestCase): if __name__ == "__main__": unittest.main() +``` -With this unit test, you can test the invocation of the function in any Python IDE or by using [pdb](https://docs.python.org/3/library/pdb.html) to place breakpoints and step through the code. If your function interacts with external services or the Kubernetes API server, you will need to mock these dependencies. Simulating external services or components that a function interacts with during testing (or mocking dependencies) allows you to isolate the specific function or piece of code you are testing to ensure that its behavior is correct. +This can test the invocation of the function in any Python IDE, or by using [`pdb`](https://docs.python.org/3/library/pdb.html) to place breakpoints and step through the code. If your function interacts with external services or the Kubernetes API server, you should to *mock* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. -When you're satisfied with the code, you can test locally by packaging the function in a Docker container and running it with `func invoke`. This is done fully through Docker, without need for a local Kubernetes cluster. +Once the code behaves as expected, you can test the function locally by packaging it in a Docker container and using `func invoke` to run it. This approach is handled completely through Docker, without the need for a local Kubernetes cluster. -At this point, you may want to fine-tune the size of the function image by removing any redundant dependencies. This helps to optimize resource utilization. +After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. -Finally, you can deploy your function to a full-fledged staging environment (Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment you can conduct integration, regression, and stress tests. +Finally, deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment, you can conduct integration, regression, and stress testing. -The resources below are provided to help you become familiar with migrating AWS Lambda functions to Knative functions on Linode Kubernetes Engine. +The resources below can help you get started with migrating AWS Lambda functions to Knative functions on the Linode Kubernetes Engine (LKE). ## Resources From 1eed3fe7c45b26d4916863f14aa7b8d05b8de82a Mon Sep 17 00:00:00 2001 From: Adam Overa Date: Fri, 11 Oct 2024 15:49:05 -0400 Subject: [PATCH 3/7] Tech Edit 2 --- .../Docker-Hub-Get-Emojis.png | Bin 0 -> 72382 bytes .../Kubernetes-Dashboard.png | Bin 0 -> 187268 bytes .../index.md | 1504 ++++++++++------- 3 files changed, 849 insertions(+), 655 deletions(-) create mode 100644 docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/Docker-Hub-Get-Emojis.png create mode 100644 docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/Kubernetes-Dashboard.png diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/Docker-Hub-Get-Emojis.png b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/Docker-Hub-Get-Emojis.png new file mode 100644 index 0000000000000000000000000000000000000000..8488ec727938d37b31fe056ef08b09a3c9266087 GIT binary patch literal 72382 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

-G{VaH-L#uCO59h$_Z*DF0< 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�}iu>pn7KT=n~* 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 PGXR07tDnm{r-UW|&E=l& literal 0 HcmV?d00001 diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/Kubernetes-Dashboard.png b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/Kubernetes-Dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..be7d2a2dcd754b626098f20345a66b9dc930038e GIT binary patch literal 187268 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 RBcMI$j;r5I-jkT9gRn+ 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%0Df-{kmt{`A|S`>8BrnWY6+pUfcp7`3G#n+AF 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 S+te-Ag(ywEtNB-MJ@`M0-XA0Y literal 0 HcmV?d00001 diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md index dfbf3cb97b..7a4acbf425 100644 --- a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -1,640 +1,708 @@ --- slug: migrating-from-aws-lambda-to-knative title: "Migrating from AWS Lambda to Knative" -description: "Two to three sentences describing your guide." -og_description: "Optional two to three sentences describing your guide when shared on social media. If omitted, the `description` parameter is used within social links." +description: "Learn how to migrate from AWS Lambda to Knative for a flexible, open source, cloud-native serverless platform on Kubernetes." authors: ["Linode"] contributors: ["Linode"] published: 2024-09-19 -keywords: ['list','of','keywords','and key phrases'] +keywords: ['knative','lambda','kubernetes','aws lambda migration','aws lambda alternatives','knative migration','knative vs lambda','knative serverless','knative kubernetes'] license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' external_resources: -- '[Link Title 1](http://www.example.com)' -- '[Link Title 2](http://www.example.net)' +- '[Knative](https://knative.dev/docs/)' +- '[Knative Functions](https://knative.dev/docs/functions/)' +- '[Knative Functions - Deep Dive (Video)](https://www.youtube.com/watch?v=l0EooTOGW84)' +- '[Accessing request traces - Knative](https://knative.dev/docs/serving/accessing-traces/)' +- '[Migrating from AWS Lambda to Knative Functions](https://knative.dev/blog/articles/aws_to_func_migration/)' +- '[GitHub: boson-project/parliament](https://github.com/boson-project/parliament)' +- '[Logging and Metrics with Amazon CloudWatch](https://docs.aws.amazon.com/lambda/latest/operatorguide/logging-metrics.html)' +- '[Prometheus](https://prometheus.io)' +- '[Grafana Labs - Loki, Grafana, Tempo, Mimir](https://grafana.com)' +- '[OpenTelemetry](https://opentelemetry.io)' +- '[Sample AWS Lambda function](https://github.com/the-gigi/fuzz-emoji/tree/main/aws_lambda)' +- '[Sample Knative function (Python)](https://github.com/the-gigi/fuzz-emoji/tree/main/knative_functions/python)' --- -This guide walks through the process of migrating an AWS Lambda function to a Knative function running on the Linode Kubernetes Engine (LKE). - Knative is an open source platform that extends Kubernetes to manage serverless workloads. It provides tools to deploy, run, and manage serverless applications and functions, enabling automatic scaling and efficient resource usage. Knative consists of several components: -- **Serving**: Deployment and runs serverless containers. +- **Serving**: Deploys and runs serverless containers. - **Eventing**: Facilitates event-driven architectures. - **Functions**: Deploys and runs functions locally and on Kubernetes. -## Prerequisites +This guide walks through the process of migrating an AWS Lambda function to a Knative function running on the Linode Kubernetes Engine (LKE). -To complete this walkthrough, you need the following: +## Before You Begin -- [Linode Account](https://www.linode.com/cfe) -- [Linode API Token](https://www.linode.com/docs/products/platform/accounts/guides/manage-api-tokens/) -- [Git](https://git-scm.com/downloads) -- [Kubectl](https://kubernetes.io/docs/tasks/tools/) -- [Linode CLI](https://www.linode.com/docs/products/tools/cli/guides/install/) -- [Knative’s `func` CLI](https://knative.dev/docs/functions/install-func/) +1. Read our [Getting Started with Linode](/docs/products/platform/get-started/) guide to create a Linode account. -## Step 1: Provision a Kubernetes Cluster +1. Read our [Manage personal access tokens](https://techdocs.akamai.com/cloud-computing/docs/manage-personal-access-tokens) guide to create a personal access token. -This guide uses the [Linode CLI](https://github.com/linode/linode-cli) to provision resources, but there are several ways to create a Kubernetes cluster on Linode: +1. Ensure that you have [Git](https://git-scm.com/downloads) installed. -- [Linode Cloud Manager](https://cloud.linode.com/) -- [Linode CLI](https://github.com/linode/linode-cli) -- [Linode API](https://techdocs.akamai.com/linode-api/reference/api) -- [Terraform](https://registry.terraform.io/providers/linode/linode/latest/docs) -- [Pulumi](https://www.pulumi.com/registry/packages/linode/) +1. Follow the steps in the *Install kubectl* section of our [Getting started with LKE](https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-lke-linode-kubernetes-engine) guide to install `kubectl`. -### Check Available Kubernetes Versions +1. Follow the instructions in our [Install and configure the CLI](https://techdocs.akamai.com/cloud-computing/docs/install-and-configure-the-cli) guide to install the Linode CLI. -First, use the Linode CLI (`linode`) to see what Kubernetes versions are available: +1. Ensure that you have Knative's [`func` CLI](https://knative.dev/docs/functions/install-func/) installed. -```command -linode lke versions-list -``` +1. Ensure that you have [Docker](https://www.docker.com/products/docker-desktop/) installed and have a [Docker Hub](https://www.docker.com/products/docker-hub/) account. -```output -┌──────┐ -│ id │ -├──────┤ -│ 1.31 │ -├──────┤ -│ 1.30 │ -├──────┤ -│ 1.29 │ -└──────┘ -``` +1. Install `jq`, a lightweight command line JSON processor: -It's generally recommended to provision the latest version unless specific requirements dictate otherwise. + ```command + sudo apt install jq + ``` -### Check Available Node Types +{{< note >}} +This guide is written for a non-root user. Commands that require elevated privileges are prefixed with `sudo`. If you’re not familiar with the `sudo` command, see the [Users and Groups](/docs/guides/linux-users-and-groups/) guide. +{{< /note >}} -Next, check what Linode plans are available. View Linode’s pricing information [here](https://www.linode.com/cloud-computing-calculator/?promo=sitelin100-02162023&promo_value=100&promo_length=60&utm_source=google&utm_medium=cpc&utm_campaign=11178784975_112607711747&utm_term=g_kwd-46671155961_e_linode%20pricing&utm_content=467094105814&locationid=9073501&device=c_c&gad_source=1&gclid=Cj0KCQjw9Km3BhDjARIsAGUb4nzNzPsxMOeTdk2wyBd77ysa3K1UTZKH8STVYjuWeg1VeEjoubqv6GIaAl59EALw_wcB). +## Provision a Kubernetes Cluster -Use the following command to list different Linode plans, including pricing and performance details: +While there are several ways to create a Kubernetes cluster on Linode, this guide uses the [Linode CLI](https://github.com/linode/linode-cli) to provision resources. -```command -linode linodes types -``` +1. First, use the Linode CLI command (`linode`) to see the available Kubernetes versions: -This guide uses the **g6-standard-2** Linode, which features two CPU cores and 4 GB of memory. + ```command + linode lke versions-list + ``` -To display detailed information for this Linode, run the following command: + ```output + ┌──────┐ + │ id │ + ├──────┤ + │ 1.31 │ + ├──────┤ + │ 1.30 │ + ├──────┤ + │ 1.29 │ + └──────┘ + ``` -```command -linode linodes types --label "Linode 4GB" --json --pretty -``` + It's generally recommended to provision the latest version unless specific requirements dictate otherwise. -```output -[ - { - "addons": { - "backups": { +1. Use the following command to list the available Linode plans, including pricing and performance details: + + ```command + linode linodes types + ``` + + {{< note >}} + View Linode’s pricing information [here](https://www.linode.com/cloud-computing-calculator/?promo=sitelin100-02162023&promo_value=100&promo_length=60&utm_source=google&utm_medium=cpc&utm_campaign=11178784975_112607711747&utm_term=g_kwd-46671155961_e_linode%20pricing&utm_content=467094105814&locationid=9073501&device=c_c&gad_source=1&gclid=Cj0KCQjw9Km3BhDjARIsAGUb4nzNzPsxMOeTdk2wyBd77ysa3K1UTZKH8STVYjuWeg1VeEjoubqv6GIaAl59EALw_wcB). + {{< /note >}} + +1. The examples in this guide use the **g6-standard-2** Linode, which features two CPU cores and 4 GB of memory. Run the following command to display detailed information for this Linode: + + ```command + linode linodes types --label "Linode 4GB" --json --pretty + ``` + + ```output + [ + { + "addons": { + "backups": { + "price": { + "hourly": 0.008, + "monthly": 5.0 + }, + "region_prices": [ + { + "hourly": 0.009, + "id": "id-cgk", + "monthly": 6.0 + }, + { + "hourly": 0.01, + "id": "br-gru", + "monthly": 7.0 + } + ] + } + }, + "class": "standard", + "disk": 81920, + "gpus": 0, + "id": "g6-standard-2", + "label": "Linode 4GB", + "memory": 4096, + "network_out": 4000, "price": { - "hourly": 0.008, - "monthly": 5.0 + "hourly": 0.036, + "monthly": 24.0 }, "region_prices": [ { - "hourly": 0.009, + "hourly": 0.043, "id": "id-cgk", - "monthly": 6.0 + "monthly": 28.8 }, { - "hourly": 0.01, + "hourly": 0.05, "id": "br-gru", - "monthly": 7.0 + "monthly": 33.6 } - ] - } - }, - "class": "standard", - "disk": 81920, - "gpus": 0, - "id": "g6-standard-2", - "label": "Linode 4GB", - "memory": 4096, - "network_out": 4000, - "price": { - "hourly": 0.036, - "monthly": 24.0 - }, - "region_prices": [ - { - "hourly": 0.043, - "id": "id-cgk", - "monthly": 28.8 - }, - { - "hourly": 0.05, - "id": "br-gru", - "monthly": 33.6 + ], + "successor": null, + "transfer": 4000, + "vcpus": 2 } - ], - "successor": null, - "transfer": 4000, - "vcpus": 2 - } -] -``` + ] + ``` + +1. With a Kubernetes version and Linode type selected, use the following command to create a cluster in the `us-mia` region with three nodes and auto-scaling: + + ```command + linode lke cluster-create \ + --label knative-playground \ + --k8s_version 1.31 \ + --region us-mia \ + --node_pools '[{ + "type": "g6-standard-2", + "count": 3, + "autoscaler": { + "enabled": true, + "min": 3, + "max": 8 + } + }]' + ``` -### Create the Kubernetes Cluster + Once your cluster is successfully created, you should see a message similar to the following: -With a Kubernetes version and Linode type selected, use the following command to create a cluster in the `ca-central` region with three nodes and auto-scaling: - -```command -linode lke cluster-create \ - --label knative-playground \ - --k8s_version 1.31 \ - --region us-mia \ - --node_pools '[{ - "type": "g6-standard-2", - "count": 3, - "autoscaler": { - "enabled": true, - "min": 3, - "max": 8 - } - }]' -``` - -Once your cluster is successfully created, you should see a message similar to the following: - -```output -Using default values: {}; use the --no-defaults flag to disable defaults -┌─────────┬────────────────────┬────────────┬─────────────┬─────────────────────────────────┐ -│ id │ label │ region │ k8s_version │ control_plane.high_availability │ -├─────────┼────────────────────┼────────────│─────────────┼─────────────────────────────────┤ -│ 202679 │ knative-playground │ ca-central │ 1.30 │ False │ -└─────────┴────────────────────┴────────────┴─────────────┴─────────────────────────────────┘ -``` + ```output + Using default values: {}; use the --no-defaults flag to disable defaults + ┌────────────────────┬────────┬─────────────┐ + │ label │ region │ k8s_version │ + ├────────────────────┼────────┼─────────────┤ + │ knative-playground │ us-mia │ 1.31 │ + └────────────────────┴────────┴─────────────┘ + ``` ### Access the Kubernetes Cluster -To access your cluster, fetch the cluster credentials in the form of a `kubeconfig` file. Use the following commands to retrieve the cluster's `kubeconfig` file and save it to `~/.kube/lke-config`: - -```command -CLUSTER_ID=$(linode lke clusters-list --json | \ - jq -r \ - '.[] | select(.label == "knative-playground") | .id') -``` - -```command -mkdir ~/.kube -``` - -```command -linode lke kubeconfig-view --json "$CLUSTER_ID" | \ - jq -r '.[0].kubeconfig' | \ - base64 --decode > ~/.kube/lke-config -``` - -Once you have the `kubeconfig` file, access your cluster using `kubectl` and specifying the file: - -```command -kubectl get no --kubeconfig ~/.kube/lke-config -``` - -```output -NAME STATUS ROLES AGE VERSION -lke202679-293551-06f33ccf0000 Ready 8h v1.30.1 -lke202679-293551-0bb2596c0000 Ready 8h v1.30.1 -lke202679-293551-58ccf2360000 Ready 8h v1.30.1 -``` - -{{< note >}} -Optionally, to avoid specifying `--kubeconfig ~/.kube/lke-config` with every `kubectl` command, you can set an environment variable for your current terminal session: - -```command -export KUBECONFIG=~/.kube/lke-config -``` - -Then you can simply run: - -```command -kubectl get no -``` - -```output -NAME STATUS ROLES AGE VERSION -lke202679-293551-06f33ccf0000 Ready 8h v1.30.1 -lke202679-293551-0bb2596c0000 Ready 8h v1.30.1 -lke202679-293551-58ccf2360000 Ready 8h v1.30.1 -``` -{{< /note >}} - -## Step 2: Set Up Knative on LKE - -There are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/). This walkthrough uses the YAML manifests method. - -### Install the Knative CRDs - -Run the following command to install the Knative CRDs: - -```command -RELEASE=releases/download/knative-v1.15.2/serving-crds.yaml -kubectl apply -f "https://github.com/knative/serving/$RELEASE" -``` - -Upon successful execution, you should see a similar output indicating that the CRDs are configured: - -```output -cus...k8s.io/certificates.networking.internal.knative.dev configured -cus...k8s.io/configurations.serving.knative.dev configured -cus...k8s.io/clusterdomainclaims.networking.internal.knative.dev configured -cus...k8s.io/domainmappings.serving.knative.dev configured cus...k8s.io/ingresses.networking.internal.knative.dev configured cus...k8s.io/metrics.autoscaling.internal.knative.dev configured cus...k8s.io/podautoscalers.autoscaling.internal.knative.dev configured -cus...k8s.io/revisions.serving.knative.dev configured cus...k8s.io/routes.serving.knative.dev configured -cus...k8s.io/serverlessservices.networking.internal.knative.dev configured -cus...k8s.io/services.serving.knative.dev configured cus...k8s.io/images.caching.internal.knative.dev configured -``` - -### Install Knative Serving - -Next, run the following command to install the Knative **Serving** component: - -```command -RELEASE=releases/download/knative-v1.15.2/serving-core.yaml -kubectl apply -f "https://github.com/knative/serving/$RELEASE" -``` - -Upon successful completion, you should see similar output indicating that various resources are now created: - -```output -pod/autoscaler-6c785b5655-r995x -pod/controller-6dd9b8448-dckv8 -pod/webhook-7dbc5d48d7-dkxm7 -service/activator-service -service/autoscaler -service/autoscaler-bucket-00-of-01 -service/controller -service/webhook -deployment.apps/activator -deployment.apps/autoscaler -deployment.apps/controller -deployment.apps/webhook -replicaset.apps/activator-5886599f75 -replicaset.apps/autoscaler-6c785b5655 -replicaset.apps/controller-6dd9b8448 -replicaset.apps/webhook-7dbc5d48d7 -horizontalpodautoscaler.autoscaling/activator horizontalpodautoscaler.autoscaling/webhook -``` - -### Install the Networking Layer - -Knative relies on an underlying networking layer. There are [several options for Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer). This guide uses [Kourier](https://github.com/knative-extensions/net-kourier), which is designed specifically for Knative. Use the following commands to install Kourier and configure Knative to use it as the networking layer: - -```command -RELEASE=releases/download/knative-v1.15.1/kourier.yaml -kubectl apply -f "https://github.com/knative-extensions/net-kourier/$RELEASE" -``` - -```command -kubectl patch configmap/config-network \ - --namespace knative-serving \ - --type merge \ - --patch \ - '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' -``` - -{{< note >}} -If Istio is already installed in your cluster, you may choose to [reuse it for Knative](https://knative.dev/docs/install/operator/knative-with-operators/#__tabbed_1_2) as well. -{{< /note >}} - -### Record the External IP Address - -With Kourier configured, the Knative Serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service for external access. Use the following command to retrieve the external IP address, in case you want to set up your own DNS later: - -```command -kubectl get service kourier -n kourier-system -``` +To access your cluster, fetch the cluster credentials in the form of a `kubeconfig` file. + +1. First, use the following command to retrieve the cluster's ID: + + ```command + CLUSTER_ID=$(linode lke clusters-list --json | \ + jq -r \ + '.[] | select(.label == "knative-playground") | .id') + ``` + +1. Create the hidden `.kube` folder in your user's home directory: + + ```command + mkdir ~/.kube + ``` + +1. Retrieve the `kubeconfig` file and save it to `~/.kube/lke-config`:: + + ```command + linode lke kubeconfig-view --json "$CLUSTER_ID" | \ + jq -r '.[0].kubeconfig' | \ + base64 --decode > ~/.kube/lke-config + ``` + +1. Once you have the `kubeconfig` file, access your cluster using `kubectl` by specifying the file: + + ```command + kubectl get no --kubeconfig ~/.kube/lke-config + ``` + + ```output + NAME STATUS ROLES AGE VERSION + lke242177-380780-1261b5670000 Ready 49s v1.31.0 + lke242177-380780-3496ef070000 Ready 47s v1.31.0 + lke242177-380780-53e2290c0000 Ready 51s v1.31.0 + ``` + + {{< note >}} + Optionally, to avoid specifying `--kubeconfig ~/.kube/lke-config` with every `kubectl` command, you can set an environment variable for your current terminal session: + + ```command + export KUBECONFIG=~/.kube/lke-config + ``` + + Then you can simply run: + + ```command + kubectl get no + ``` + {{< /note >}} + +## Set Up Knative on LKE + +While there are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/), the examples in this guide use the YAML manifests method. + +1. First, run the following command to install the Knative CRDs: + + ```command + RELEASE=releases/download/knative-v1.15.2/serving-crds.yaml + kubectl apply -f "https://github.com/knative/serving/$RELEASE" + ``` + + Upon successful execution, you should see a similar output indicating that the CRDs are configured: + + ```output + customresourcedefinition.apiextensions.k8s.io/certificates.networking.internal.knative.dev created + customresourcedefinition.apiextensions.k8s.io/configurations.serving.knative.dev created + customresourcedefinition.apiextensions.k8s.io/clusterdomainclaims.networking.internal.knative.dev created + customresourcedefinition.apiextensions.k8s.io/domainmappings.serving.knative.dev created + customresourcedefinition.apiextensions.k8s.io/ingresses.networking.internal.knative.dev created + customresourcedefinition.apiextensions.k8s.io/metrics.autoscaling.internal.knative.dev created + customresourcedefinition.apiextensions.k8s.io/podautoscalers.autoscaling.internal.knative.dev created + customresourcedefinition.apiextensions.k8s.io/revisions.serving.knative.dev created + customresourcedefinition.apiextensions.k8s.io/routes.serving.knative.dev created + customresourcedefinition.apiextensions.k8s.io/serverlessservices.networking.internal.knative.dev created + customresourcedefinition.apiextensions.k8s.io/services.serving.knative.dev created + customresourcedefinition.apiextensions.k8s.io/images.caching.internal.knative.dev created + ``` + +1. Next, run the following command to install the Knative **Serving** component: + + ```command + RELEASE=releases/download/knative-v1.15.2/serving-core.yaml + kubectl apply -f "https://github.com/knative/serving/$RELEASE" + ``` + + Upon successful completion, you should see similar output indicating that various resources are now created: + + ```output + namespace/knative-serving created + role.rbac.authorization.k8s.io/knative-serving-activator created + clusterrole.rbac.authorization.k8s.io/knative-serving-activator-cluster created + clusterrole.rbac.authorization.k8s.io/knative-serving-aggregated-addressable-resolver created + clusterrole.rbac.authorization.k8s.io/knative-serving-addressable-resolver created + clusterrole.rbac.authorization.k8s.io/knative-serving-namespaced-admin created + clusterrole.rbac.authorization.k8s.io/knative-serving-namespaced-edit created + clusterrole.rbac.authorization.k8s.io/knative-serving-namespaced-view created + clusterrole.rbac.authorization.k8s.io/knative-serving-core created + clusterrole.rbac.authorization.k8s.io/knative-serving-podspecable-binding created + serviceaccount/controller created + clusterrole.rbac.authorization.k8s.io/knative-serving-admin created + clusterrolebinding.rbac.authorization.k8s.io/knative-serving-controller-admin created + clusterrolebinding.rbac.authorization.k8s.io/knative-serving-controller-addressable-resolver created + serviceaccount/activator created + rolebinding.rbac.authorization.k8s.io/knative-serving-activator created + clusterrolebinding.rbac.authorization.k8s.io/knative-serving-activator-cluster created + customresourcedefinition.apiextensions.k8s.io/images.caching.internal.knative.dev unchanged + certificate.networking.internal.knative.dev/routing-serving-certs created + customresourcedefinition.apiextensions.k8s.io/certificates.networking.internal.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/configurations.serving.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/clusterdomainclaims.networking.internal.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/domainmappings.serving.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/ingresses.networking.internal.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/metrics.autoscaling.internal.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/podautoscalers.autoscaling.internal.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/revisions.serving.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/routes.serving.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/serverlessservices.networking.internal.knative.dev unchanged + customresourcedefinition.apiextensions.k8s.io/services.serving.knative.dev unchanged + image.caching.internal.knative.dev/queue-proxy created + configmap/config-autoscaler created + configmap/config-certmanager created + configmap/config-defaults created + configmap/config-deployment created + configmap/config-domain created + configmap/config-features created + configmap/config-gc created + configmap/config-leader-election created + configmap/config-logging created + configmap/config-network created + configmap/config-observability created + configmap/config-tracing created + horizontalpodautoscaler.autoscaling/activator created + poddisruptionbudget.policy/activator-pdb created + deployment.apps/activator created + service/activator-service created + deployment.apps/autoscaler created + service/autoscaler created + deployment.apps/controller created + service/controller created + horizontalpodautoscaler.autoscaling/webhook created + poddisruptionbudget.policy/webhook-pdb created + deployment.apps/webhook created + service/webhook created + validatingwebhookconfiguration.admissionregistration.k8s.io/config.webhook.serving.knative.dev created + mutatingwebhookconfiguration.admissionregistration.k8s.io/webhook.serving.knative.dev created + validatingwebhookconfiguration.admissionregistration.k8s.io/validation.webhook.serving.knative.dev created + secret/webhook-certs created + ``` + +1. Knative relies on an underlying networking layer. While there are [several options for Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer), the examples in guide use [Kourier](https://github.com/knative-extensions/net-kourier), which is designed specifically for Knative. Use the commands below to download and install the latest Kourier release: + + ```command + RELEASE=releases/download/knative-v1.15.1/kourier.yaml + kubectl apply -f "https://github.com/knative-extensions/net-kourier/$RELEASE" + ``` + + The output should again indicate the creation of multiple new elements: + + ```output + namespace/kourier-system created + configmap/kourier-bootstrap created + configmap/config-kourier created + serviceaccount/net-kourier created + clusterrole.rbac.authorization.k8s.io/net-kourier created + clusterrolebinding.rbac.authorization.k8s.io/net-kourier created + deployment.apps/net-kourier-controller created + service/net-kourier-controller created + deployment.apps/3scale-kourier-gateway created + service/kourier created + service/kourier-internal created + horizontalpodautoscaler.autoscaling/3scale-kourier-gateway created + poddisruptionbudget.policy/3scale-kourier-gateway-pdb created + ``` + +1. The following command configures Knative to use Kourier as the default ingress controller: + + ```command + kubectl patch configmap/config-network \ + --namespace knative-serving \ + --type merge \ + --patch \ + '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' + ``` + + ```output + configmap/config-network patched + ``` + + {{< note >}} + If Istio is already installed in your cluster, you may choose to [reuse it for Knative](https://knative.dev/docs/install/operator/knative-with-operators/#__tabbed_1_2) as well. + {{< /note >}} + +1. With Kourier configured, the Knative Serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service for external access. Use the following command to retrieve the external IP address, in case you want to set up your own DNS later: + + ```command + kubectl get service kourier -n kourier-system + ``` + + The output should resemble the following, with the external IP address shown: + + ```output + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + kourier LoadBalancer 10.128.48.124 172.235.159.7 80:31938/TCP,443:30800/TCP 4m37s + ``` + +1. Since Kourier added several deployments, check the updated list to ensure everything is functioning correctly: + + ```command + kubectl get deploy -n knative-serving + ``` + + You should see output similar to the following, confirming the availability of the various components: + + ```output + NAME READY UP-TO-DATE AVAILABLE AGE + activator 1/1 1 1 7m36s + autoscaler 1/1 1 1 7m36s + controller 1/1 1 1 7m36s + net-kourier-controller 1/1 1 1 5m7s + webhook 1/1 1 1 7m36s + ``` + +1. While Knative offers [multiple ways to configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns), this guide uses the Magic DNS method, which leverages the [sslip.io](http://sslip.io) DNS service. When a request is made to a subdomain of sslip.io containing an embedded IP address, the service resolves that IP address. For example, a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. Use the `default-domain` job to configures Knative Serving to use sslip.io: + + ```command + MANIFEST=knative-v1.15.2/serving-default-domain.yaml + kubectl apply -f "https://github.com/knative/serving/releases/download/$MANIFEST" + ``` + + Upon successful execution, you should see output confirming the creation of the `default-domain` job and service: + + ```output + job.batch/default-domain created + service/default-domain-service created + ``` -The output should resemble the following, with the external IP address shown: +With Knative now operational in your cluster, you can begin working with Knative Functions. -```output -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -kourier LoadBalancer 10.128.128.65 172.105.12.189 80:30580/TCP, - 443:31780/TCP 2m56s -``` +## Work with Knative Functions and the `func` CLI -### Verify installation +Knative Functions is a programming model that simplifies writing distributed applications on Kubernetes and Knative. It allows developers to create stateless, event-driven functions without requiring in-depth knowledge of containers, Kubernetes, or Knative itself. -Since Kourier added several deployments, check the updated list to ensure everything is functioning correctly: +The [`func`](https://github.com/knative/func) CLI streamlines the developer experience by providing tools to work with Knative Functions. It allows developers to manage the entire lifecycle of functions (creating, building, deploying, and invoking). This allows for local development and testing of functions without the need for a local Kubernetes cluster. -```command -kubectl get deploy -n knative-serving -``` +1. To get started, run the following command: -You should see output similar to the following, confirming the availability of the various components: + ```command + func + ``` -```output -NAME READY UP-TO-DATE AVAILABLE AGE -activator 1/1 1 1 5m21s -autoscaler 1/1 1 1 5m20s -controller 1/1 1 1 5m19s -net-kourier-controller 1/1 1 1 4m50s -Webhook 1/1 1 1 5m18s -``` + This displays help information for managing Knative Function resources: -### Configure DNS + ```output + func is the command line interface for managing Knative Function resources -Knative offers [multiple ways to configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns). This guide uses the Magic DNS method, which leverages the [sslip.io](http://sslip.io) DNS service. When a request is made to a subdomain of sslip.io containing an embedded IP address, the service resolves that IP address. For example, a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. + Create a new Node.js function in the current directory: + func create --language node myfunction -Use the `default-domain` job to configures Knative Serving to use sslip.io: + Deploy the function using Docker hub to host the image: + func deploy --registry docker.io/alice -```command -MANIFEST=knative-v1.15.2/serving-default-domain.yaml -kubectl apply -f "https://github.com/knative/serving/releases/download/$MANIFEST" -``` + Learn more about Functions: https://knative.dev/docs/functions/ + Learn more about Knative at: https://knative.dev -You should see output similar to the following upon successful execution, confirming the creation of the default domain job and service: + Primary Commands: + create Create a function + describe Describe a function + deploy Deploy a function + delete Undeploy a function + list List deployed functions + subscribe Subscribe a function to events -```output -job.batch/default-domain created -service/default-domain-service created -``` + Development Commands: + run Run the function locally + invoke Invoke a local or remote function + build Build a function container -With Knative now operational in your cluster, you can begin working with Knative Functions. + System Commands: + config Configure a function + languages List available function language runtimes + templates List available function source templates + repository Manage installed template repositories + environment Display function execution environment information -## Step 3: Work with Knative Functions and the `func` CLI + Other Commands: + completion Output functions shell completion code + version Function client version information -Knative Functions is a programming model that simplifies writing distributed applications on Kubernetes and Knative. It allows developers to create stateless, event-driven functions without requiring in-depth knowledge of containers, Kubernetes, or Knative itself. - -The [`func`](https://github.com/knative/func) CLI streamlines the developer experience by providing tools to work with Knative Functions. This enables local development and testing of functions without the need for a local Kubernetes cluster. + Use "func --help" for more information about a given command. + ``` -### The `func` CLI +1. Use the following command to create a Python function (`get-emojis`) that can be invoked via an HTTP endpoint (the default invocation method): -The `func` CLI allows developers to manage the entire lifecycle of functions (creating, building, deploying, and invoking). + ```command + func create -l python get-emojis + ``` -To get started, run the following command: + This command creates a complete directory structure with multiple files: -```command -func -``` + ```output + Created python function in /home/{{< placeholder "USERNAME" >}}/get-emojis + ``` -This displays help information for managing Knative Function resources: +1. Examine the contents of the newly created `~/get-emojis` directory: -```output -func is the command line interface for managing Knative Function resources - - Create a new Node.js function in the current directory: - func create --language node myfunction - - Deploy the function using Docker hub to host the image: - func deploy --registry docker.io/alice - -Learn more about Functions: https://knative.dev/docs/functions/ -Learn more about Knative at: https://knative.dev - -Primary Commands: - create Create a function - describe Describe a function - deploy Deploy a function - delete Undeploy a function - list List deployed functions - subscribe Subscribe a function to events - -Development Commands: - run Run the function locally - invoke Invoke a local or remote function - build Build a function container - -System Commands: - config Configure a function - languages List available function language runtimes - templates List available function source templates - repository Manage installed template repositories - environment Display function execution environment information - -Other Commands: - completion Output functions shell completion code - version Function client version information - -Use "func --help" for more information about a given command. -``` + ```command + ls -laGh get-emojis + ``` -### Create a Function + ```output + total 48K + drwxr-xr-x 3 {{< placeholder "USERNAME" >}} 4.0K Oct 9 15:57 . + drwxr-x--- 9 {{< placeholder "USERNAME" >}} 4.0K Oct 9 15:57 .. + -rwxr-xr-x 1 {{< placeholder "USERNAME" >}} 55 Oct 9 15:57 app.sh + drwxrwxr-x 2 {{< placeholder "USERNAME" >}} 4.0K Oct 9 15:57 .func + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 217 Oct 9 15:57 .funcignore + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 1.8K Oct 9 15:57 func.py + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 97 Oct 9 15:57 func.yaml + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 235 Oct 9 15:57 .gitignore + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 28 Oct 9 15:57 Procfile + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 862 Oct 9 15:57 README.md + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 28 Oct 9 15:57 requirements.txt + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 259 Oct 9 15:57 test_func.py + ``` -To create a Python function that can be invoked via an HTTP endpoint (the default invocation method), use the following command: +1. While covering the purpose of each file is outside the scope of this guide, you should examine the `func.py` file, the default implementation that Knative generates: -```command -func create -l python get-emojis -``` + ```command + cat ~/get-emojis/func.py + ``` -```output -Created python function in /home/{{< placeholder "USERNAME" >}}/get-emojis -``` + ```file {title="~/get-emojis/func.py" lang="python"} + from parliament import Context + from flask import Request + import json -This command creates a complete directory structure with multiple files: -```command -ls -laGh get-emojis -``` + # parse request body, json data or URL query parameters + def payload_print(req: Request) -> str: + if req.method == "POST": + if req.is_json: + return json.dumps(req.json) + "\n" + else: + # MultiDict needs some iteration + ret = "{" -```output -total 48K -drwxr-xr-x 3 coder 4.0K Aug 4 16:04 . --rwxr-xr-x 1 coder 55 Aug 4 16:04 app.sh -drwxrwxr-x 2 coder 4.0K Aug 4 16:04 .func --rw-r--r-- 1 coder 217 Aug 4 16:04 .funcignore --rw-r--r-- 1 coder 1.8K Aug 4 16:04 func.py --rw-r--r-- 1 coder 98 Aug 4 16:04 func.yaml --rw-r--r-- 1 coder 28 Aug 4 16:04 Procfile --rw-r--r-- 1 coder 862 Aug 4 16:04 README.md --rw-r--r-- 1 coder 28 Aug 4 16:04 requirements.txt --rw-r--r-- 1 coder 259 Aug 4 16:04 test_func.py -drwxrwxr-x 3 coder 4.0K Aug 4 16:04 .. --rw-r--r-- 1 coder 235 Aug 4 16:04 .gitignore -``` - -Covering the purpose of each file is outside the scope of this guide. However, you should examine the `func.py` file, which is the default implementation that Knative generates.: - -```file {title="func.py" lang="python"} -from parliament import Context -from flask import Request -import json - -# parse request body, json data or URL query parameters -def payload_print(req: Request) -> str: - if req.method == "POST": - if req.is_json: - return json.dumps(req.json) + "\n" - else: + for key in req.form.keys(): + ret += '"' + key + '": "'+ req.form[key] + '", ' + + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + + elif req.method == "GET": # MultiDict needs some iteration ret = "{" - for key in req.form.keys(): - ret += '"' + key + '": "'+ req.form[key] + '", ' + for key in req.args.keys(): + ret += '"' + key + '": "' + req.args[key] + '", ' return ret[:-2] + "}\n" if len(ret) > 2 else "{}" - elif req.method == "GET": - # MultiDict needs some iteration - ret = "{" - for key in req.args.keys(): - ret += '"' + key + '": "' + req.args[key] + '", ' + # pretty print the request to stdout instantaneously + def pretty_print(req: Request) -> str: + ret = str(req.method) + ' ' + str(req.url) + ' ' + str(req.host) + '\n' + for (header, values) in req.headers: + ret += " " + str(header) + ": " + values + '\n' - return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + if req.method == "POST": + ret += "Request body:\n" + ret += " " + payload_print(req) + '\n' -# pretty print the request to stdout instantaneously -def pretty_print(req: Request) -> str: - ret = str(req.method) + ' ' + str(req.url) + ' ' + str(req.host) + '\n' - for (header, values) in req.headers: - ret += " " + str(header) + ": " + values + '\n' + elif req.method == "GET": + ret += "URL Query String:\n" + ret += " " + payload_print(req) + '\n' - if req.method == "POST": - ret += "Request body:\n" - ret += " " + payload_print(req) + '\n' + return ret - elif req.method == "GET": - ret += "URL Query String:\n" - ret += " " + payload_print(req) + '\n' - return ret + def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ -def main(context: Context): - """ - Function template - The context parameter contains the Flask request object and any - CloudEvent received with the request. - """ + # Add your business logic here + print("Received request") - # Add your business logic here - print("Received request") - - if 'request' in context.keys(): - ret = pretty_print(context.request) - print(ret, flush=True) - return payload_print(context.request), 200 - else: - print("Empty request", flush=True) - return "{}", 200 -``` + if 'request' in context.keys(): + ret = pretty_print(context.request) + print(ret, flush=True) + return payload_print(context.request), 200 + else: + print("Empty request", flush=True) + return "{}", 200 + ``` -This function acts as a server that returns the query parameters or form fields of the incoming request. + This function acts as a server that returns the query parameters or form fields of incoming requests. ### Build a Function Image -The next step is to create a container image from your function. Since the function will run on a Kubernetes cluster, it must be containerized. Knative Functions facilitates this process for developers, abstracting the complexities of Docker and Dockerfiles. +The next step is to create a container image from your function. Since the function is intended run on a Kubernetes cluster, it must be containerized. Knative Functions facilitates this process for developers, abstracting the complexities of Docker and Dockerfiles. -To build and push your function, run the following `func build` command, ensuring you have access to a container registry: +1. Change into the `~/get-emojis` directory before running the `build` command: -```commandf -func build --registry docker.io/your_username --push -``` + ```command + cd ~/get-emojis + ``` -You should see output similar to the following as the function image is built: +1. To build your function, run the following `build` command, specifying Docker Hub (`docker.io`) as the registry along with your {{< placeholder "DOCKER_HUB_USERNAME" >}}: -```output -Building function image -Still building -Still building -Yes, still building -Don't give up on me -🙌 Function build: docker.io/your_username/get-emojis:latest -Pushing function image to the registry "index.docker.io" using the "your_username" user credentials -``` + ```command + func build --registry docker.io/{{< placeholder "DOCKER_HUB_USERNAME" >}} + ``` -This command fetches a base image, builds a Docker image from your function, and then pushes it to the Docker registry. + This command fetches a base image and builds a Docker image from your function. You should see output similar to the following as the function image is built: -To verify that the image is successfully created, use the following command to list your Docker images: + ```output + Building function image + Still building + Still building + Yes, still building + Don't give up on me + Still building + This is taking a while + 🙌 Function built: index.docker.io/{{< placeholder "DOCKER_HUB_USERNAME" >}}/get-emojis:latest + ``` -```command -docker images | rg 'knative|get-emojis|ID' -``` +1. To verify that the image is successfully created, use the following command to list your Docker images: -```output -REPOSITORY TAG IMAGE ID CREATED SIZE -ghcr.io/…/builder-jammy-base latest 58e634e9a771 44 years ago 1.6GB -your_username/get-emojis latest a5c58cce8219 44 years ago 293MB -``` + ```command + docker images | grep -E 'knative|get-emojis|ID' + ``` -Note that while the `CREATED` timestamp may be incorrect, the image is valid. + ```output + REPOSITORY TAG IMAGE ID CREATED SIZE + ghcr.io/knative/builder-jammy-base 0.4.283 204e70721072 44 years ago 1.45GB + {{< placeholder "DOCKER_HUB_USERNAME" >}}/get-emojis latest {{< placeholder "IMAGE_ID" >}} 44 years ago 293MB + ``` -### Run the Function Locally + {{< note >}} + While the `CREATED` timestamp may be incorrect, the image is valid. + {{< /note >}} -You can run the function locally to test its behavior before deploying it to a Kubernetes cluster. Use the following `func run` command to run the function locally: +1. Now use the `run` command to run the function locally: -```command -func run -``` + ```command + func run + ``` -The terminal should display output indicating that the function is set up and running: + The terminal should display output indicating that the function now runs on `localhost` at port `8080`.: -```output -function up-to-date. Force rebuild with --build -Running on host port 8080 -``` + ```output + function up-to-date. Force rebuild with --build + Running on host port 8080 + ``` -The function now runs on `localhost` at port `8080`. By default, this initial implementation returns the URL query parameters as a JSON object. +1. With your function running, open a second terminal enter the following command: -With your function running, open a web browser and navigate to the following URL: + ```command + curl "http://localhost:8080?a=1&b=2" + ``` -```command -http://localhost:8080?a=1&b=2 -``` + By default, this initial implementation returns the URL query parameters as a JSON object. The resulting output should be: -You should see the output similar to the following in your terminal window: + ```output + {"a": "1", "b": "2"} + ``` -```output -Received request -GET http://localhost:8080/?a=1&b=2 localhost:8080 - Host: localhost:8080 - Connection: keep-alive - Cache-Control: max-age=0 - Sec-Ch-Ua: "Chromium"; v="124", "Google Chrome"… - Sec-Ch-Ua-Mobile: ?0 - Sec-Ch-Ua-Platform: "macOS" - Upgrade-Insecure-Requests: 1 - User-Agent: Mozilla/5.0 (Macintosh; Intel Mac O… - Accept: text/html,application/xhtml+xml,applica… - Sec-Fetch-Site: none - Sec-Fetch-Mode: navigate - Sec-Fetch-User: ?1 - Sec-Fetch-Dest: document - Accept-Encoding: gzip, deflate, br, zstd - Accept-Language: en-US,en;q=0.9 -URL Query String: - {"a": "1", "b": "2"} -``` + Meanwhile, you should see the output similar to the following in your original terminal: -### Deploy the Function + ```output + Received request + GET http://localhost:8080/?a=1&b=2 localhost:8080 + Host: localhost:8080 + User-Agent: curl/7.81.0 + Accept: */* + URL Query String: + {"a": "1", "b": "2"} + ``` -To deploy your function to a Kubernetes cluster as a Knative function, use the following `func deploy` command: +1. When done, close the second terminal and stop the function in the original terminal by pressing the CTRL+C keys. -```command -func deploy -``` +### Deploy the Function -You should see output similar to the following during deployment: +1. To deploy your function to your Kubernetes cluster as a Knative function and push it to the Docker registry, use the `deploy` command: -```output -… -Pushing function image to the registry "index.docker.io" using the "your_username" user credentials -⬆️ Deploying function to the cluster -🎯 Creating Triggers on the cluster -✅ Function deployed in namespace "default" and exposed at URL: - http://get-emojis.default.172.105.12.189.sslip.io -``` + ```command + func deploy + ``` -The function is ready to be invoked once the function is deployed and the Magic DNS record established. + You should see output similar to the following during deployment: -### Invoke the Function via an HTTP Endpoint + ```output + function up-to-date. Force rebuild with --build + Pushing function image to the registry "index.docker.io" using the "{{< placeholder "DOCKER_HUB_USERNAME" >}}" user credentials + 🎯 Creating Triggers on the cluster + ✅ Function deployed in namespace "default" and exposed at URL: + http://get-emojis.default.{{< placeholder "IP_ADDRESS" >}}.sslip.io + ``` -To invoke your Knative function, open a web browser and visit the function’s public URL, adding any required query parameters. For example: + Once the function is deployed and the Magic DNS record established, your Knative function is accessible through this public HTTP endpoint. Additionally, the new `get-emojis` repository should now exist on your Docker Hub account: -```command -http://get-emojis.default.172.105.12.189.sslip.io/?yeah=it-works! -``` + ![The get-emojis repository on Docker Hub.](Docker-Hub-Get-Emojis.png) + +1. To invoke your Knative function, `curl` the function’s public URL, adding any required query parameters. For example: -The browser should display a JSON object containing the query parameters: + ```command + curl http://get-emojis.default.{{< placeholder "IP_ADDRESS" >}}.sslip.io/?yeah=it-works! + ``` -[SCREENSHOT] + The output should display a JSON object containing the query parameters: -Your Knative function is now accessible through this public HTTP endpoint. + ```output + {"yeah": "it-works!"} + ``` With your Knative function running, the next step is migrate an AWS Lambda function to Knative. -## Step 4: Migrate Your AWS Lambda Function to Knative +## Migrate Your AWS Lambda Function to Knative -This guide examines a sample Lambda function and walks you through how to migrate it to Knative. Conceptually, Lambda functions are similar to Knative functions. They both have a trigger and extract their input arguments from a context or event. +This guide examines a sample Lambda function and walks through how to migrate it to Knative. Conceptually, Lambda functions are similar to Knative functions. They both have a trigger and extract their input arguments from a context or event. The main application logic is highlighted in the sample Lambda function below: @@ -675,17 +743,15 @@ The code above the highlighted lines extracts emoji descriptions from the `event At the time of this writing, this sample Lambda function was deployed and available at the following HTTP endpoint: ```command -https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji +curl -s -X POST --header "Content-type:application/json" \ + --data '{"descriptions":["flame","confused"]}' \ + https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji | \ + json_pp ``` Invoking the function returns the following result: ```output -$ curl -s -X POST --header "Content-type:application/json" \ - --data '{"descriptions":["flame","confused"]}' \ - https://64856ijzmi.execute-api.us-west-2.amazonaws.com/default/fuzz-emoji | \ - json_pp - { "confused" : "('confused_face', '😕')", "flame" : "('fire', '🔥')" @@ -702,126 +768,212 @@ If the `get_emojis()` method were dependent on the AWS Lambda `event` object, it ### Migrating a Single-File Function to a Knative Function -The core logic of the function is encapsulated into a single Python module named `fuzz_emoji.py`, which can be migrated to your Knative function. Find the module code below: - -```file {lang="python"} -from typing import List, Mapping, Tuple - -import emoji -import requests - -class FuzzEmoji: - def __init__(self): - self.emoji_dict = {} - emoji_list = {name: data for name, data in emoji.EMOJI_DATA.items() if 'en' in data} - for emoji_char, data in emoji_list.items(): - name = data['en'].strip(':') - self.emoji_dict[name.lower()] = emoji_char - - @staticmethod - def get_synonyms(word): - response = requests.get(f"https://api.datamuse.com/words?rel_syn={word}") - if response.status_code == 200: - synonyms = [word_data['word'] for word_data in response.json()] - return synonyms - - raise RuntimeError(response.content) - - def get_emoji(self, description) -> Tuple[str, str]: - description = description.lower() - # direct match - if description in self.emoji_dict: - return description, self.emoji_dict[description] - - # Subset match - for name in self.emoji_dict: - if description in name: - return name, self.emoji_dict[name] - - synonyms = self.get_synonyms(description) - # Synonym match - for syn in synonyms: - if syn in self.emoji_dict: - return syn, self.emoji_dict[syn] - return '', '' - - def get_emojis(self, descriptions: List[str]) -> Mapping[str, str]: - return {d: str(self.get_emoji(d)) for d in descriptions} -``` +The core logic of the function is encapsulated into a single Python module named `fuzz_emoji.py`, which can be migrated to your Knative function. + +1. First, create the `fuzz_emoji.py` file in the `get-emojis` directory: + + ```command + nano ~/get-emojis/fuzz_emoji.py + ``` + + Give the file the following content: + + ```file {title="~/get-emojis/fuzz_emoji/py" lang="python"} + from typing import List, Mapping, Tuple + + import emoji + import requests + + class FuzzEmoji: + def __init__(self): + self.emoji_dict = {} + emoji_list = {name: data for name, data in emoji.EMOJI_DATA.items() if 'en' in data} + for emoji_char, data in emoji_list.items(): + name = data['en'].strip(':') + self.emoji_dict[name.lower()] = emoji_char + + @staticmethod + def get_synonyms(word): + response = requests.get(f"https://api.datamuse.com/words?rel_syn={word}") + if response.status_code == 200: + synonyms = [word_data['word'] for word_data in response.json()] + return synonyms + + raise RuntimeError(response.content) + + def get_emoji(self, description) -> Tuple[str, str]: + description = description.lower() + # direct match + if description in self.emoji_dict: + return description, self.emoji_dict[description] + + # Subset match + for name in self.emoji_dict: + if description in name: + return name, self.emoji_dict[name] + + synonyms = self.get_synonyms(description) + # Synonym match + for syn in synonyms: + if syn in self.emoji_dict: + return syn, self.emoji_dict[syn] + return '', '' + + def get_emojis(self, descriptions: List[str]) -> Mapping[str, str]: + return {d: str(self.get_emoji(d)) for d in descriptions} + ``` + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Run the `ls` command again: + + ```command + ls -laGh ~/get-emojis/ + ``` + + The folder structure should now look like this: + + ```output + total 52K + drwxr-xr-x 3 {{< placeholder "USERNAME" >}} 4.0K Oct 10 17:32 . + drwxr-x--- 9 {{< placeholder "USERNAME" >}} 4.0K Oct 10 16:51 .. + -rwxr-xr-x 1 {{< placeholder "USERNAME" >}} 55 Oct 10 16:51 app.sh + drwxrwxr-x 3 {{< placeholder "USERNAME" >}} 4.0K Oct 10 17:20 .func + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 217 Oct 10 16:51 .funcignore + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 1.8K Oct 10 16:51 func.py + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 317 Oct 10 17:22 func.yaml + -rw-rw-r-- 1 {{< placeholder "USERNAME" >}} 1.4K Oct 10 17:32 fuzz_emoji.py + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 235 Oct 10 16:51 .gitignore + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 28 Oct 10 16:51 Procfile + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 862 Oct 10 16:51 README.md + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 28 Oct 10 16:51 requirements.txt + -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 259 Oct 10 16:51 test_func.py + ``` + +1. Next, edit your `func.py` file so that it calls the `fuzz_emoji` module: + + ```command + nano ~/get-emojis/func.py + ``` + + Insert or adjust the highlighted lines so that the contents of your `fuzz_emoji.py` file appear as below: + + ```file {title="~/get-emojis/func.py" lang="python" hl_lines="4,34,61-64"} + from parliament import Context + from flask import Request + import json + from fuzz_emoji import FuzzEmoji + + + # parse request body, json data or URL query parameters + def payload_print(req: Request) -> str: + if req.method == "POST": + if req.is_json: + return json.dumps(req.json) + "\n" + else: + # MultiDict needs some iteration + ret = "{" + + for key in req.form.keys(): + ret += '"' + key + '": "'+ req.form[key] + '", ' + + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" + + elif req.method == "GET": + # MultiDict needs some iteration + ret = "{" -Copy the module into the same directory that Knative `func` created for your function: + for key in req.args.keys(): + ret += '"' + key + '": "' + req.args[key] + '", ' -```command -ls -laGh get-emojis/ -``` + return ret[:-2] + "}\n" if len(ret) > 2 else "{}" -The folder structure should now look like this: -```output -total 52K -drwxr-xr-x 3 coder 4.0K Aug 4 16:47 . --rw-rw-r-- 1 coder 1.7K Aug 4 16:47 fuzz_emoji.py --rwxr-xr-x 1 coder 55 Aug 4 16:04 app.sh -drwxrwxr-x 2 coder 4.0K Aug 4 16:04 .func --rw-r--r-- 1 coder 217 Aug 4 16:04 .funcignore --rw-r--r-- 1 coder 1.8K Aug 4 16:04 func.py --rw-r--r-- 1 coder 98 Aug 4 16:04 func.yaml --rw-r--r-- 1 coder 28 Aug 4 16:04 Procfile --rw-r--r-- 1 coder 862 Aug 4 16:04 README.md --rw-r--r-- 1 coder 28 Aug 4 16:04 requirements.txt --rw-r--r-- 1 coder 259 Aug 4 16:04 test_func.py -drwxrwxr-x 3 coder 4.0K Aug 4 16:04 .. --rw-r--r-- 1 coder 235 Aug 4 16:04 .gitignore -``` + # pretty print the request to stdout instantaneously + def pretty_print(req: Request) -> str: + ret = str(req.method) + ' ' + str(req.url) + ' ' + str(req.host) + '\n' + for header, values in req.headers.items(): + ret += " " + str(header) + ": " + values + '\n' -Next, change the implementation of your Knative function so that it calls the `fuzz_emoji` module: + if req.method == "POST": + ret += "Request body:\n" + ret += " " + payload_print(req) + '\n' -```file -import json + elif req.method == "GET": + ret += "URL Query String:\n" + ret += " " + payload_print(req) + '\n' -from parliament import Context -from fuzz_emoji import FuzzEmoji + return ret -def main(context: Context): - descriptions = context.request.args.get('descriptions').split(',') - fuzz_emoji = FuzzEmoji() - result = fuzz_emoji.get_emojis(descriptions) - return json.dumps(result, ensure_ascii=False), 200 -``` -Here's a breakdown of what this code does: + def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ -1. Imports the built-in `json`, the `Context` from [parliament](https://github.com/boson-project/parliament) (the function invocation framework that Knative uses for Python functions), and the `FuzzEmoji` class. + # Add your business logic here + print("Received request") -1. The `main()` function takes the parliament `Context` as its only parameter. The context contains a Flask `request` property. + if 'request' in context.keys(): + ret = pretty_print(context.request) + print(ret, flush=True) + descriptions = context.request.args.get('descriptions').split(',') + fuzz_emoji = FuzzEmoji() + result = fuzz_emoji.get_emojis(descriptions) + return json.dumps(result, ensure_ascii=False), 200 + else: + print("Empty request", flush=True) + return "{}", 200 + ``` -1. The first line extracts the emoji descriptions from the Flask `request` arguments. It expects the descriptions to be a single comma-separated string, which it splits into a list of `descriptions`. + Here's a breakdown of what this code does: -1. Instantiates a `FuzzEmoji` object and calls its `get_emojis()` method. + - Imports the built-in `json`, the `Context` from [parliament](https://github.com/boson-project/parliament) (the function invocation framework that Knative uses for Python functions), and the `FuzzEmoji` class. + - The `main()` function accepts the parliament `Context` as its only parameter, which contains a Flask `request` property. + - The first line extracts the emoji descriptions from the Flask `request` arguments. It expects the descriptions to be a single comma-separated string, which it splits into a list of `descriptions`. + - Instantiates a `FuzzEmoji` object and calls the `get_emojis()` method. + - Uses the `json` module to serialize the response and return it with a `200` status code. -1. Uses the `json` module to serialize the response and return it with a `200` status code. + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. -Next, add the requirements of `fuzz_emoji.py` (the `requests` and `emoji` packages) to the `requirements.txt` file of your Knative function to include these dependencies in the Docker image. +1. Next, edit the `requirements.txt` file to include the dependencies of `fuzz_emoji.py` (the `requests` and `emoji` packages) in the Docker image: -```file -parliament-functions==0.1.0 -emoji==2.12.1 -requests==2.32.3 -``` + ```command + nano ~/get-emojis/requirements.txt + ``` -Now build and deploy the container: + Append the highlighted lines to the end of the file: -```command -cd ~/get-emojis -func build --registry docker.io/your_username -func deploy -``` + ```file {title="~/get-emojis/requirements.txt" hl_lines="2,3"} + parliament-functions==0.1.0 + emoji==2.12.1 + requests==2.32.3 + ``` + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Now re-build and re-deploy the container: -Finally, test your function using the public URL: + ```command + func build --registry docker.io/{{< placeholder "DOCKER_HUB_USERNAME" >}} + func deploy + ``` -[SCREENSHOT] +1. Finally, test your function using the public URL: -The `descriptions` provided as a query parameter are echoed back, along with a corresponding emoji name and emoji for each description. This confirms that the Knative function works as expected. + ```command + curl http://get-emojis.default.{{< placeholder "IP_ADDRESS" >}}.sslip.io/?descriptions=cold,plane,fam + ``` + + The `descriptions` provided as a query parameter are echoed back, along with a corresponding emoji name and emoji for each description: + + ```output + {"cold": "('cold_face', '🥶')", "plane": "('airplane', '✈')", "fam": "('family', '👪')"} + ``` + + This confirms that the Knative function works as expected. ### Migrating a Multi-File Function to a Knative Function @@ -839,7 +991,7 @@ Migrating such a setup to Knative follows a similar process: When migrating an AWS Lambda function, it may depend on various AWS services, such as S3, DynamoDB, SQS, or others. It's important to evaluate each dependency to determine the best option to suit your situation. -There are typically three options: +There are typically three choices: 1. **Keep it as-is**: Continue using the Knative function to interact with the AWS service. @@ -879,11 +1031,11 @@ The logging experience in Knative is similar to printing something in your AWS L LKE provides the native Kubernetes dashboard by default. It runs on the control plane, so it doesn't take resources from your workloads. You can use the dashboard to explore and monitor your entire cluster: -[SCREENSHOT] +![The default Kubernetes Dashboard showing workload status and deployments.](Kubernetes-Dashboard.png) For production systems, consider using a centralized logging system like ELK/EFK, Loki, or Graylog. Also use an observability solution like Prometheus and Grafana. Consider leveraging OpenTelemetry as well. These tools can enhance the ability to monitor, troubleshoot, and optimize application performance while ensuring reliability and scalability. -Knative also has built-in support for distributed tracing, which can be configured globally. This means that your Knative function automatically participates in tracing without requiring additional changes. +Knative also has built-in support for distributed tracing, which can be configured globally. This means your Knative function automatically participates in tracing without requiring additional changes. ### The Debugging Experience @@ -895,58 +1047,100 @@ Knative offers a debugging experience at multiple levels: When you create a Python Knative function, Knative generates a skeleton for a unit test, called `test_func.py`. At the time of this writing, the generated test is invalid and requires some modifications to work correctly. See this [GitHub issue](https://github.com/knative/func/issues/2448) for details. -Below is the modified test, updated for testing the fuzzy emoji search functionality: +1. Open the `test_func.py` file in the `get-emojis` directory: -```file {lang="python"} -import unittest -from parliament import Context + ```command + nano ~/get-emojis/test_func.py + ``` -func = __import__("func") + Replace its content with the test code below which is updated for testing the fuzzy emoji search functionality: -class DummyRequest: - def __init__(self, descriptions): - self.descriptions = descriptions + ```file {title="~/get-emojis/test_func.py" lang="python"} + import unittest + from parliament import Context - @property - def args(self): - return dict(descriptions=self.descriptions) + func = __import__("func") -class TestFunc(unittest.TestCase): - # noinspection PyTypeChecker - def test_func(self): - result, code = func.main(Context(DummyRequest('flame,confused'))) - expected = """{"flame": "('fire', '🔥')", "confused": "('confused_face', '😕')"}""" - self.assertEqual(expected, result) - self.assertEqual(code, 200) + class DummyRequest: + def __init__(self, descriptions): + self.descriptions = descriptions -if __name__ == "__main__": - unittest.main() -``` + @property + def args(self): + return dict(descriptions=self.descriptions) + + @property + def method(self): + return 'GET' + + @property + def url(self): + return 'http://localhost/' + + @property + def host(self): + return 'localhost' + + @property + def headers(self): + return {'Content-Type': 'application/json'} + + + class TestFunc(unittest.TestCase): + # noinspection PyTypeChecker + def test_func(self): + result, code = func.main(Context(DummyRequest('flame,confused'))) + expected = """{"flame": "('fire', '🔥')", "confused": "('confused_face', '😕')"}""" + self.assertEqual(expected, result) + self.assertEqual(code, 200) + + if __name__ == "__main__": + unittest.main() + ``` + + When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + +1. Use the `python3` command to run the `test_func.py` file and test the invocation of your function: + + ```command + python3 ~/get-emojis/test_func.py + ``` + + A successful test should produce the following output: + + ```output + Received request + GET http://localhost/ localhost + Content-Type: application/json + URL Query String: + {"descriptions": "flame,confused"} + + + . + ---------------------------------------------------------------------- + Ran 1 test in 0.395s -This can test the invocation of the function in any Python IDE, or by using [`pdb`](https://docs.python.org/3/library/pdb.html) to place breakpoints and step through the code. If your function interacts with external services or the Kubernetes API server, you should to *mock* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. + OK + ``` -Once the code behaves as expected, you can test the function locally by packaging it in a Docker container and using `func invoke` to run it. This approach is handled completely through Docker, without the need for a local Kubernetes cluster. + {{< note >}} + This test can also be performed in any Python IDE, or by using [`pdb`](https://docs.python.org/3/library/pdb.html) to place breakpoints and step through the code. If your function interacts with external services or the Kubernetes API server, you should to *mock* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. + {{< /note >}} -After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. +1. Once the code behaves as expected, you can test the function locally by packaging it in a Docker container and using `func invoke` to run it: -Finally, deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment, you can conduct integration, regression, and stress testing. + ```command + func invoke + ``` -The resources below can help you get started with migrating AWS Lambda functions to Knative functions on the Linode Kubernetes Engine (LKE). + This approach is handled completely through Docker, without the need for a local Kubernetes cluster. After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. -## Resources +1. Finally, deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`: -- [Knative](https://knative.dev/docs/) -- [Knative Functions](https://knative.dev/docs/functions/) -- [Knative Functions - Deep Dive (Video)](https://www.youtube.com/watch?v=l0EooTOGW84) -- [Accessing request traces - Knative](https://knative.dev/docs/serving/accessing-traces/) -- [Migrating from AWS Lambda to Knative Functions](https://knative.dev/blog/articles/aws_to_func_migration/) -- [GitHub - boson-project/parliament: A function invocation framework for Python](https://github.com/boson-project/parliament) -- [Logging and Metrics with Amazon CloudWatch](https://docs.aws.amazon.com/lambda/latest/operatorguide/logging-metrics.html) -- [Prometheus](https://prometheus.io) -- [Grafana Labs - Loki, Grafana, Tempo, Mimir](https://grafana.com) -- [OpenTelemetry](https://opentelemetry.io) + ```command + func deploy + ``` -The source code for this demo walkthrough is available here: + In the staging environment, you can conduct integration, regression, and stress testing. -- [AWS Lambda function](https://github.com/the-gigi/fuzz-emoji/tree/main/aws_lambda) -- [Knative function (Python)](https://github.com/the-gigi/fuzz-emoji/tree/main/knative_functions/python) \ No newline at end of file +The resources below can help you get started with migrating AWS Lambda functions to Knative functions on the Linode Kubernetes Engine (LKE). \ No newline at end of file From 7d3c94ab1231367063b91f184fe8ce503251670f Mon Sep 17 00:00:00 2001 From: Adam Overa Date: Fri, 11 Oct 2024 15:54:50 -0400 Subject: [PATCH 4/7] CI Tests Fix 1 --- .../migrating-from-aws-lambda-to-knative/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md index 7a4acbf425..8f15ec2239 100644 --- a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -436,11 +436,11 @@ The [`func`](https://github.com/knative/func) CLI streamlines the developer expe ```output func is the command line interface for managing Knative Function resources - Create a new Node.js function in the current directory: - func create --language node myfunction + Create a new Node.js function in the current directory: + func create --language node myfunction - Deploy the function using Docker hub to host the image: - func deploy --registry docker.io/alice + Deploy the function using Docker hub to host the image: + func deploy --registry docker.io/alice Learn more about Functions: https://knative.dev/docs/functions/ Learn more about Knative at: https://knative.dev From d35eabfe2a310167376c43a6d9ac1d8855e75bd3 Mon Sep 17 00:00:00 2001 From: Adam Overa Date: Fri, 11 Oct 2024 16:46:07 -0400 Subject: [PATCH 5/7] Tech Edit 3 --- .../index.md | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md index 8f15ec2239..55552ba351 100644 --- a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -1100,7 +1100,13 @@ When you create a Python Knative function, Knative generates a skeleton for a un When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. -1. Use the `python3` command to run the `test_func.py` file and test the invocation of your function: +1. Before running the `test_func.py`, use `pip3` to install the dependencies listed in the `requirementss.txt` file: + + ```command + pip3 install -r ~/get-emojis/requirements.txt + ``` + +1. Now use the `python3` command to run the `test_func.py` file and test the invocation of your function: ```command python3 ~/get-emojis/test_func.py @@ -1123,24 +1129,10 @@ When you create a Python Knative function, Knative generates a skeleton for a un OK ``` - {{< note >}} - This test can also be performed in any Python IDE, or by using [`pdb`](https://docs.python.org/3/library/pdb.html) to place breakpoints and step through the code. If your function interacts with external services or the Kubernetes API server, you should to *mock* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. - {{< /note >}} - -1. Once the code behaves as expected, you can test the function locally by packaging it in a Docker container and using `func invoke` to run it: - - ```command - func invoke - ``` - - This approach is handled completely through Docker, without the need for a local Kubernetes cluster. After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. - -1. Finally, deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`: +Once the code behaves as expected, you can test the function locally by packaging it in a Docker container using `func invoke` to run it. This approach is handled completely through Docker, without the need for a local Kubernetes cluster. After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. Finally, deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment, you can conduct integration, regression, and stress testing. - ```command - func deploy - ``` - - In the staging environment, you can conduct integration, regression, and stress testing. +{{< note >}} +If your function interacts with external services or the Kubernetes API server, you should to *mock* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. +{{< /note >}} The resources below can help you get started with migrating AWS Lambda functions to Knative functions on the Linode Kubernetes Engine (LKE). \ No newline at end of file From 4ec13c88e53c794a0190c33a8b784c9ea4006c01 Mon Sep 17 00:00:00 2001 From: John Dutton Date: Mon, 21 Oct 2024 16:33:50 -0400 Subject: [PATCH 6/7] copy edits --- .../index.md | 150 +++++++++--------- 1 file changed, 73 insertions(+), 77 deletions(-) diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md index 55552ba351..d27a51d1a4 100644 --- a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -2,8 +2,8 @@ slug: migrating-from-aws-lambda-to-knative title: "Migrating from AWS Lambda to Knative" description: "Learn how to migrate from AWS Lambda to Knative for a flexible, open source, cloud-native serverless platform on Kubernetes." -authors: ["Linode"] -contributors: ["Linode"] +authors: ["Akamai"] +contributors: ["Akamai"] published: 2024-09-19 keywords: ['knative','lambda','kubernetes','aws lambda migration','aws lambda alternatives','knative migration','knative vs lambda','knative serverless','knative kubernetes'] license: '[CC BY-ND 4.0](https://creativecommons.org/licenses/by-nd/4.0)' @@ -32,15 +32,15 @@ This guide walks through the process of migrating an AWS Lambda function to a Kn ## Before You Begin -1. Read our [Getting Started with Linode](/docs/products/platform/get-started/) guide to create a Linode account. +1. Read our [Getting Started with Linode](/docs/products/platform/get-started/) guide, and create a Linode account if you do not already have one. -1. Read our [Manage personal access tokens](https://techdocs.akamai.com/cloud-computing/docs/manage-personal-access-tokens) guide to create a personal access token. +1. Create a personal access token using the instructions in our [Manage personal access tokens](https://techdocs.akamai.com/cloud-computing/docs/manage-personal-access-tokens) guide. 1. Ensure that you have [Git](https://git-scm.com/downloads) installed. 1. Follow the steps in the *Install kubectl* section of our [Getting started with LKE](https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-lke-linode-kubernetes-engine) guide to install `kubectl`. -1. Follow the instructions in our [Install and configure the CLI](https://techdocs.akamai.com/cloud-computing/docs/install-and-configure-the-cli) guide to install the Linode CLI. +1. Install the Linode CLI using the instructions in our [Install and configure the CLI](https://techdocs.akamai.com/cloud-computing/docs/install-and-configure-the-cli) guide. 1. Ensure that you have Knative's [`func` CLI](https://knative.dev/docs/functions/install-func/) installed. @@ -60,7 +60,7 @@ This guide is written for a non-root user. Commands that require elevated privil While there are several ways to create a Kubernetes cluster on Linode, this guide uses the [Linode CLI](https://github.com/linode/linode-cli) to provision resources. -1. First, use the Linode CLI command (`linode`) to see the available Kubernetes versions: +1. Use the Linode CLI command (`linode`) to see available Kubernetes versions: ```command linode lke versions-list @@ -78,19 +78,15 @@ While there are several ways to create a Kubernetes cluster on Linode, this guid └──────┘ ``` - It's generally recommended to provision the latest version unless specific requirements dictate otherwise. + It's generally recommended to provision the latest version of Kubernetes unless specific requirements dictate otherwise. -1. Use the following command to list the available Linode plans, including pricing and performance details: +1. Use the following command to list available Linode plans, including plan ID, pricing, and performance details. For more detailed pricing information, see [Akamai Connected Cloud: Pricing](https://www.linode.com/pricing/): ```command linode linodes types ``` - {{< note >}} - View Linode’s pricing information [here](https://www.linode.com/cloud-computing-calculator/?promo=sitelin100-02162023&promo_value=100&promo_length=60&utm_source=google&utm_medium=cpc&utm_campaign=11178784975_112607711747&utm_term=g_kwd-46671155961_e_linode%20pricing&utm_content=467094105814&locationid=9073501&device=c_c&gad_source=1&gclid=Cj0KCQjw9Km3BhDjARIsAGUb4nzNzPsxMOeTdk2wyBd77ysa3K1UTZKH8STVYjuWeg1VeEjoubqv6GIaAl59EALw_wcB). - {{< /note >}} - -1. The examples in this guide use the **g6-standard-2** Linode, which features two CPU cores and 4 GB of memory. Run the following command to display detailed information for this Linode: +1. The examples in this guide use the **g6-standard-2** Linode, which features two CPU cores and 4 GB of memory. Run the following command to display detailed information in JSON for this Linode plan: ```command linode linodes types --label "Linode 4GB" --json --pretty @@ -149,13 +145,19 @@ While there are several ways to create a Kubernetes cluster on Linode, this guid ] ``` -1. With a Kubernetes version and Linode type selected, use the following command to create a cluster in the `us-mia` region with three nodes and auto-scaling: +1. View available regions with the `regions list` command: + + ```command + linode regions list + ``` + +1. With a Kubernetes version and Linode type selected, use the following command to create a cluster named `knative-playground` in the `us-mia` (Miami, FL) region with three nodes and auto-scaling. Replace {{< placeholder "knative-playground" >}} and {{< placeholder "us-mia" >}} with a cluster label and region of your choosing, respectively: ```command linode lke cluster-create \ - --label knative-playground \ + --label {{< placeholder "knative-playground" >}} \ --k8s_version 1.31 \ - --region us-mia \ + --region {{< placeholder "us-mia" >}} \ --node_pools '[{ "type": "g6-standard-2", "count": 3, @@ -167,7 +169,7 @@ While there are several ways to create a Kubernetes cluster on Linode, this guid }]' ``` - Once your cluster is successfully created, you should see a message similar to the following: + Once your cluster is successfully created, you should see output similar to the following: ```output Using default values: {}; use the --no-defaults flag to disable defaults @@ -182,7 +184,7 @@ While there are several ways to create a Kubernetes cluster on Linode, this guid To access your cluster, fetch the cluster credentials in the form of a `kubeconfig` file. -1. First, use the following command to retrieve the cluster's ID: +1. Use the following command to retrieve the cluster's ID: ```command CLUSTER_ID=$(linode lke clusters-list --json | \ @@ -190,13 +192,13 @@ To access your cluster, fetch the cluster credentials in the form of a `kubeconf '.[] | select(.label == "knative-playground") | .id') ``` -1. Create the hidden `.kube` folder in your user's home directory: +1. Create a hidden `.kube` folder in your user's home directory: ```command mkdir ~/.kube ``` -1. Retrieve the `kubeconfig` file and save it to `~/.kube/lke-config`:: +1. Retrieve the `kubeconfig` file and save it to `~/.kube/lke-config`: ```command linode lke kubeconfig-view --json "$CLUSTER_ID" | \ @@ -204,7 +206,7 @@ To access your cluster, fetch the cluster credentials in the form of a `kubeconf base64 --decode > ~/.kube/lke-config ``` -1. Once you have the `kubeconfig` file, access your cluster using `kubectl` by specifying the file: +1. Once you have the `kubeconfig` file saved, access your cluster by using `kubectl` and specifying the file: ```command kubectl get no --kubeconfig ~/.kube/lke-config @@ -224,7 +226,7 @@ To access your cluster, fetch the cluster credentials in the form of a `kubeconf export KUBECONFIG=~/.kube/lke-config ``` - Then you can simply run: + Then run: ```command kubectl get no @@ -233,9 +235,9 @@ To access your cluster, fetch the cluster credentials in the form of a `kubeconf ## Set Up Knative on LKE -While there are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/), the examples in this guide use the YAML manifests method. +There are multiple ways to [install Knative on a Kubernetes cluster](https://knative.dev/docs/install/). The examples in this guide use the YAML manifests method. -1. First, run the following command to install the Knative CRDs: +1. Run the following command to install the Knative CRDs: ```command RELEASE=releases/download/knative-v1.15.2/serving-crds.yaml @@ -259,14 +261,14 @@ While there are multiple ways to [install Knative on a Kubernetes cluster](https customresourcedefinition.apiextensions.k8s.io/images.caching.internal.knative.dev created ``` -1. Next, run the following command to install the Knative **Serving** component: +1. Next, install the Knative **Serving** component: ```command RELEASE=releases/download/knative-v1.15.2/serving-core.yaml kubectl apply -f "https://github.com/knative/serving/$RELEASE" ``` - Upon successful completion, you should see similar output indicating that various resources are now created: + You should see similar output indicating that various resources are now created: ```output namespace/knative-serving created @@ -330,7 +332,7 @@ While there are multiple ways to [install Knative on a Kubernetes cluster](https secret/webhook-certs created ``` -1. Knative relies on an underlying networking layer. While there are [several options for Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer), the examples in guide use [Kourier](https://github.com/knative-extensions/net-kourier), which is designed specifically for Knative. Use the commands below to download and install the latest Kourier release: +1. Knative relies on an underlying networking layer. [Kourier](https://github.com/knative-extensions/net-kourier) is designed specifically for Knative, and the examples in this guide use Kourier for [Knative networking](https://knative.dev/docs/install/operator/knative-with-operators/#install-the-networking-layer). Use the commands below to download and install the latest Kourier release: ```command RELEASE=releases/download/knative-v1.15.1/kourier.yaml @@ -373,13 +375,13 @@ While there are multiple ways to [install Knative on a Kubernetes cluster](https If Istio is already installed in your cluster, you may choose to [reuse it for Knative](https://knative.dev/docs/install/operator/knative-with-operators/#__tabbed_1_2) as well. {{< /note >}} -1. With Kourier configured, the Knative Serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service for external access. Use the following command to retrieve the external IP address, in case you want to set up your own DNS later: +1. With Kourier configured, the Knative Serving installation now has a [`LoadBalancer`](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) service for external access. Use the following command to retrieve the external IP address in case you want to set up your own DNS later: ```command kubectl get service kourier -n kourier-system ``` - The output should resemble the following, with the external IP address shown: + The output should display the external IP address of the `LoadBalancer`: ```output NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE @@ -392,7 +394,7 @@ While there are multiple ways to [install Knative on a Kubernetes cluster](https kubectl get deploy -n knative-serving ``` - You should see output similar to the following, confirming the availability of the various components: + Use the output to confirm availability of the various components: ```output NAME READY UP-TO-DATE AVAILABLE AGE @@ -403,7 +405,7 @@ While there are multiple ways to [install Knative on a Kubernetes cluster](https webhook 1/1 1 1 7m36s ``` -1. While Knative offers [multiple ways to configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns), this guide uses the Magic DNS method, which leverages the [sslip.io](http://sslip.io) DNS service. When a request is made to a subdomain of sslip.io containing an embedded IP address, the service resolves that IP address. For example, a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. Use the `default-domain` job to configures Knative Serving to use sslip.io: +1. This guide uses the Magic DNS method to [configure DNS](https://knative.dev/docs/install/operator/knative-with-operators/#configure-dns), which leverages the [sslip.io](http://sslip.io) DNS service. When a request is made to a subdomain of sslip.io containing an embedded IP address, the service resolves that IP address. For example, a request to [https://52.0.56.137.sslip.io](https://52.0.56.137.sslip.io) returns `52.0.56.137` as the IP address. Use the `default-domain` job to configure Knative Serving to use sslip.io: ```command MANIFEST=knative-v1.15.2/serving-default-domain.yaml @@ -472,7 +474,7 @@ The [`func`](https://github.com/knative/func) CLI streamlines the developer expe Use "func --help" for more information about a given command. ``` -1. Use the following command to create a Python function (`get-emojis`) that can be invoked via an HTTP endpoint (the default invocation method): +1. Use the following command to create an example Python function (`get-emojis`) that can be invoked via an HTTP endpoint (the default invocation method): ```command func create -l python get-emojis @@ -506,7 +508,7 @@ The [`func`](https://github.com/knative/func) CLI streamlines the developer expe -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 259 Oct 9 15:57 test_func.py ``` -1. While covering the purpose of each file is outside the scope of this guide, you should examine the `func.py` file, the default implementation that Knative generates: +1. While reviewing the purpose of each file is outside the scope of this guide, you should examine the `func.py` file, the default implementation that Knative generates: ```command cat ~/get-emojis/func.py @@ -578,19 +580,19 @@ The [`func`](https://github.com/knative/func) CLI streamlines the developer expe return "{}", 200 ``` - This function acts as a server that returns the query parameters or form fields of incoming requests. + Note that this function acts as a server that returns the query parameters or form fields of incoming requests. ### Build a Function Image The next step is to create a container image from your function. Since the function is intended run on a Kubernetes cluster, it must be containerized. Knative Functions facilitates this process for developers, abstracting the complexities of Docker and Dockerfiles. -1. Change into the `~/get-emojis` directory before running the `build` command: +1. Navigate into the `~/get-emojis` directory: ```command cd ~/get-emojis ``` -1. To build your function, run the following `build` command, specifying Docker Hub (`docker.io`) as the registry along with your {{< placeholder "DOCKER_HUB_USERNAME" >}}: +1. To build your function, run the following `build` command while in the `~/get-emojis` directory, specifying Docker Hub (`docker.io`) as the registry along with your {{< placeholder "DOCKER_HUB_USERNAME" >}}: ```command func build --registry docker.io/{{< placeholder "DOCKER_HUB_USERNAME" >}} @@ -625,7 +627,7 @@ The next step is to create a container image from your function. Since the funct While the `CREATED` timestamp may be incorrect, the image is valid. {{< /note >}} -1. Now use the `run` command to run the function locally: +1. Use the `run` command to run the function locally: ```command func run @@ -638,7 +640,7 @@ The next step is to create a container image from your function. Since the funct Running on host port 8080 ``` -1. With your function running, open a second terminal enter the following command: +1. With your function running, open a second terminal session and enter the following command: ```command curl "http://localhost:8080?a=1&b=2" @@ -650,7 +652,7 @@ The next step is to create a container image from your function. Since the funct {"a": "1", "b": "2"} ``` - Meanwhile, you should see the output similar to the following in your original terminal: + Meanwhile, you should see the output similar to the following in your original terminal window: ```output Received request @@ -666,14 +668,12 @@ The next step is to create a container image from your function. Since the funct ### Deploy the Function -1. To deploy your function to your Kubernetes cluster as a Knative function and push it to the Docker registry, use the `deploy` command: +1. Use the `deploy` command to deploy your function to your Kubernetes cluster as a Knative function and push it to the Docker registry: ```command func deploy ``` - You should see output similar to the following during deployment: - ```output function up-to-date. Force rebuild with --build Pushing function image to the registry "index.docker.io" using the "{{< placeholder "DOCKER_HUB_USERNAME" >}}" user credentials @@ -682,7 +682,7 @@ The next step is to create a container image from your function. Since the funct http://get-emojis.default.{{< placeholder "IP_ADDRESS" >}}.sslip.io ``` - Once the function is deployed and the Magic DNS record established, your Knative function is accessible through this public HTTP endpoint. Additionally, the new `get-emojis` repository should now exist on your Docker Hub account: + Once the function is deployed and the Magic DNS record is established, your Knative function is accessible through this public HTTP endpoint. The new `get-emojis` repository should also now exist on your Docker Hub account: ![The get-emojis repository on Docker Hub.](Docker-Hub-Get-Emojis.png) @@ -704,7 +704,7 @@ With your Knative function running, the next step is migrate an AWS Lambda funct This guide examines a sample Lambda function and walks through how to migrate it to Knative. Conceptually, Lambda functions are similar to Knative functions. They both have a trigger and extract their input arguments from a context or event. -The main application logic is highlighted in the sample Lambda function below: +The main application logic is highlighted in the example Lambda function below: ```file {lang="python" hl_lines="15-16"} def handler(event, context): @@ -736,7 +736,7 @@ def handler(event, context): return response ``` -This function instantiates a `FuzzEmoji` object and calls its `get_emojis()` method, passing a list of emoji descriptions. The emoji descriptions may or may not map to official emoji names like `fire` (🔥) or `confused_face` (😕). The function performs a fuzzy search of the descriptions to find matching emojis. +This example function instantiates a `FuzzEmoji` object and calls its `get_emojis()` method, passing a list of emoji descriptions. The emoji descriptions may or may not map to official emoji names like `fire` (🔥) or `confused_face` (😕). The function performs a "fuzzy" search of the descriptions to find matching emojis. The code above the highlighted lines extracts emoji descriptions from the `event` object passed to the handler. The code below the highlighted lines wraps the result in a response with a proper status code for success or failure. @@ -762,15 +762,15 @@ The function successfully returns the `fire` (🔥) emoji for the description "f ### Isolating the AWS Lambda Code from AWS Specifics -To migrate the Lambda function to Knative, the core application logic must be decoupled from AWS-specific dependencies. Fortunately, in this case, the function's main logic is already isolated. The `get_emojis()` method only accepts a list of strings as input, which makes it easy to adapt for other platforms. +To migrate the Lambda function to Knative, the core application logic must be decoupled from AWS-specific dependencies. In this case, the function's main logic is already isolated. The `get_emojis()` method only accepts a list of strings as input, which makes it more adaptable for other platforms. -If the `get_emojis()` method were dependent on the AWS Lambda `event` object, it would not be compatible with Knative, as Knative does not provide an `event` object. This scenario would require some refactoring. +If the `get_emojis()` method were dependent on the AWS Lambda `event` object, it would not be compatible with Knative and would require some refactoring, as Knative does not provide an `event` object. ### Migrating a Single-File Function to a Knative Function The core logic of the function is encapsulated into a single Python module named `fuzz_emoji.py`, which can be migrated to your Knative function. -1. First, create the `fuzz_emoji.py` file in the `get-emojis` directory: +1. Using a text editor of your choice, create the `fuzz_emoji.py` file in the `get-emojis` directory: ```command nano ~/get-emojis/fuzz_emoji.py @@ -823,9 +823,9 @@ The core logic of the function is encapsulated into a single Python module named return {d: str(self.get_emoji(d)) for d in descriptions} ``` - When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. + When complete, save your changes. -1. Run the `ls` command again: +1. Run the `ls` command: ```command ls -laGh ~/get-emojis/ @@ -850,13 +850,13 @@ The core logic of the function is encapsulated into a single Python module named -rw-r--r-- 1 {{< placeholder "USERNAME" >}} 259 Oct 10 16:51 test_func.py ``` -1. Next, edit your `func.py` file so that it calls the `fuzz_emoji` module: +1. Edit your `func.py` file so that it calls the `fuzz_emoji` module: ```command nano ~/get-emojis/func.py ``` - Insert or adjust the highlighted lines so that the contents of your `fuzz_emoji.py` file appear as below: + Insert or adjust the highlighted lines so that the contents of your `fuzz_emoji.py` file appear as below. Remember to save your changes: ```file {title="~/get-emojis/func.py" lang="python" hl_lines="4,34,61-64"} from parliament import Context @@ -928,7 +928,7 @@ The core logic of the function is encapsulated into a single Python module named return "{}", 200 ``` - Here's a breakdown of what this code does: + Below is a breakdown of the file code functionality: - Imports the built-in `json`, the `Context` from [parliament](https://github.com/boson-project/parliament) (the function invocation framework that Knative uses for Python functions), and the `FuzzEmoji` class. - The `main()` function accepts the parliament `Context` as its only parameter, which contains a Flask `request` property. @@ -936,15 +936,13 @@ The core logic of the function is encapsulated into a single Python module named - Instantiates a `FuzzEmoji` object and calls the `get_emojis()` method. - Uses the `json` module to serialize the response and return it with a `200` status code. - When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. - 1. Next, edit the `requirements.txt` file to include the dependencies of `fuzz_emoji.py` (the `requests` and `emoji` packages) in the Docker image: ```command nano ~/get-emojis/requirements.txt ``` - Append the highlighted lines to the end of the file: + Append the highlighted lines to the end of the file, and save your changes: ```file {title="~/get-emojis/requirements.txt" hl_lines="2,3"} parliament-functions==0.1.0 @@ -952,16 +950,14 @@ The core logic of the function is encapsulated into a single Python module named requests==2.32.3 ``` - When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. - -1. Now re-build and re-deploy the container: +1. Re-build and re-deploy the container: ```command func build --registry docker.io/{{< placeholder "DOCKER_HUB_USERNAME" >}} func deploy ``` -1. Finally, test your function using the public URL: +1. Test your function using the public URL: ```command curl http://get-emojis.default.{{< placeholder "IP_ADDRESS" >}}.sslip.io/?descriptions=cold,plane,fam @@ -977,7 +973,7 @@ The core logic of the function is encapsulated into a single Python module named ### Migrating a Multi-File Function to a Knative Function -In the previous example, the entire application logic was contained in a single file called `fuzz_emoji.py`. For larger workloads, your function might involve multiple files or even multiple directories and packages. +In the previous example, the entire application logic was contained in a single file called `fuzz_emoji.py`. For larger workloads, your function may involve multiple files or multiple directories and packages. Migrating such a setup to Knative follows a similar process: @@ -989,9 +985,9 @@ Migrating such a setup to Knative follows a similar process: ### Migrating External Dependencies -When migrating an AWS Lambda function, it may depend on various AWS services, such as S3, DynamoDB, SQS, or others. It's important to evaluate each dependency to determine the best option to suit your situation. +When migrating an AWS Lambda function, it may depend on various AWS services such as S3, DynamoDB, or SQS. It's important to evaluate each dependency to determine the best option to suit your situation. -There are typically three choices: +There are typically three options to consider: 1. **Keep it as-is**: Continue using the Knative function to interact with the AWS service. @@ -1003,9 +999,9 @@ There are typically three choices: The Knative function eventually runs as a pod in the Kubernetes cluster. This means it runs in a namespace and has a Kubernetes service account associated with it. These are determined when you run the `func deploy` command. You can specify them using the `-n` (or `--namespace`) and `--service-account` arguments. -If you don't specify these options, the function deploys in the currently configured namespace and uses the default service account of the namespace. +If these options are not specified, the function deploys in the currently configured namespace and uses the default service account of the namespace. -If your Knative function needs to access any Kubernetes resources, it’s recommended to explicitly specify a dedicated namespace and create a dedicated service account. This is the preferred approach because it avoids granting excessive permissions to the default service account. +If your Knative function needs to access any Kubernetes resources, it’s recommended to explicitly specify a dedicated namespace and create a dedicated service account. This is the preferred approach since it avoids granting excessive permissions to the default service account. ### Configuration and Secrets @@ -1023,7 +1019,7 @@ Kubernetes offers the [`ConfigMap`](https://kubernetes.io/docs/concepts/configur Your Knative function may need to interact with various Kubernetes resources and services during migration, such as data stores, `ConfigMaps`, and `Secrets`. To enable this, create a dedicated role with the necessary permissions and bind it to the function's service account. -If your architecture includes multiple Knative functions, it's best practice to share the same service account, role, and role bindings between all the Knative functions. +If your architecture includes multiple Knative functions, it is considered a best practice to share the same service account, role, and role bindings between all the Knative functions. ### Logging, Metrics, and Distributed Tracing @@ -1033,19 +1029,19 @@ LKE provides the native Kubernetes dashboard by default. It runs on the control ![The default Kubernetes Dashboard showing workload status and deployments.](Kubernetes-Dashboard.png) -For production systems, consider using a centralized logging system like ELK/EFK, Loki, or Graylog. Also use an observability solution like Prometheus and Grafana. Consider leveraging OpenTelemetry as well. These tools can enhance the ability to monitor, troubleshoot, and optimize application performance while ensuring reliability and scalability. +For production systems, consider using a centralized logging system like ELK/EFK, Loki, or Graylog, along with an observability solution consisting of Prometheus and Grafana. You can also supplement your observability by leveraging a telemetry data-oriented solution such as OpenTelemetry. These tools can enhance your ability to monitor, troubleshoot, and optimize application performance while ensuring reliability and scalability. Knative also has built-in support for distributed tracing, which can be configured globally. This means your Knative function automatically participates in tracing without requiring additional changes. ### The Debugging Experience -Knative offers a debugging experience at multiple levels: +Knative offers debugging at multiple levels: - Unit test your core logic - Unit test your Knative function - Invoke your function locally -When you create a Python Knative function, Knative generates a skeleton for a unit test, called `test_func.py`. At the time of this writing, the generated test is invalid and requires some modifications to work correctly. See this [GitHub issue](https://github.com/knative/func/issues/2448) for details. +When you create a Python Knative function, Knative generates a skeleton for a unit test called `test_func.py`. At the time of this writing, the generated test is invalid and requires some modifications to work correctly. See this [GitHub issue](https://github.com/knative/func/issues/2448) for details. 1. Open the `test_func.py` file in the `get-emojis` directory: @@ -1053,7 +1049,7 @@ When you create a Python Knative function, Knative generates a skeleton for a un nano ~/get-emojis/test_func.py ``` - Replace its content with the test code below which is updated for testing the fuzzy emoji search functionality: + Replace its content with the test code below, and save your changes. This code is updated for testing the fuzzy emoji search functionality: ```file {title="~/get-emojis/test_func.py" lang="python"} import unittest @@ -1098,15 +1094,13 @@ When you create a Python Knative function, Knative generates a skeleton for a un unittest.main() ``` - When done, press CTRL+X, followed by Y then Enter to save the file and exit `nano`. - -1. Before running the `test_func.py`, use `pip3` to install the dependencies listed in the `requirementss.txt` file: +1. Use `pip3` to install the dependencies listed in the `requirementss.txt` file: ```command pip3 install -r ~/get-emojis/requirements.txt ``` -1. Now use the `python3` command to run the `test_func.py` file and test the invocation of your function: +1. Use the `python3` command to run the `test_func.py` file and test the invocation of your function: ```command python3 ~/get-emojis/test_func.py @@ -1129,10 +1123,12 @@ When you create a Python Knative function, Knative generates a skeleton for a un OK ``` -Once the code behaves as expected, you can test the function locally by packaging it in a Docker container using `func invoke` to run it. This approach is handled completely through Docker, without the need for a local Kubernetes cluster. After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. Finally, deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment, you can conduct integration, regression, and stress testing. +Once the code behaves as expected, you can test the function locally by packaging it in a Docker container using `func invoke` to run it. This approach is handled completely through Docker, without the need for a local Kubernetes cluster. + +After local testing, you may want to optimize the function's image size by removing any redundant dependencies to improve resource utilization. Deploy your function to a staging environment (a Kubernetes cluster with Knative installed) using `func deploy`. In the staging environment, you can conduct integration, regression, and stress testing. {{< note >}} -If your function interacts with external services or the Kubernetes API server, you should to *mock* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. +If your function interacts with external services or the Kubernetes API server, you should *"mock"* these dependencies. Mocking, or simulating external services or components that a function interacts with, allows you to isolate a specific function or piece of code to ensure it behaves correctly. {{< /note >}} -The resources below can help you get started with migrating AWS Lambda functions to Knative functions on the Linode Kubernetes Engine (LKE). \ No newline at end of file +See **More Information** below for resources to help you get started with migrating AWS Lambda functions to Knative functions on the Linode Kubernetes Engine (LKE). \ No newline at end of file From d658f3b433ddab7474fc5a2a97035e85014f7f08 Mon Sep 17 00:00:00 2001 From: John Dutton Date: Tue, 22 Oct 2024 08:33:26 -0400 Subject: [PATCH 7/7] typo fix --- .../kubernetes/migrating-from-aws-lambda-to-knative/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md index d27a51d1a4..3b94724245 100644 --- a/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md +++ b/docs/guides/kubernetes/migrating-from-aws-lambda-to-knative/index.md @@ -1094,7 +1094,7 @@ When you create a Python Knative function, Knative generates a skeleton for a un unittest.main() ``` -1. Use `pip3` to install the dependencies listed in the `requirementss.txt` file: +1. Use `pip3` to install the dependencies listed in the `requirements.txt` file: ```command pip3 install -r ~/get-emojis/requirements.txt