1

OpenFunction 0.6.0 was officially released last week, bringing many notable features including function plugins, distributed tracing of functions, control of autoscaling, HTTP functions triggering asynchronous functions, and more. At the same time, the asynchronous runtime definition has also been refactored. The core API has also been upgraded from v1alpha1 to v1beta1.

Official announcement link🔗: https://openfunction.dev/blog/2022/03/25/announcing-openfunction-0.6.0-faas-observability-http-trigger-and-more/

In recent years, with the rise of serverless computing , there have been many excellent Serverless open source projects, among which Knative and OpenFaaS are more outstanding. However, Knative Serving can only run applications, not functions, and the core of Serverless is function computing, which is FaaS, so it is a pity; although OpenFaaS has been out of the circle for a long time, its technology stack is too old to meet modern function computing. platform needs.

OpenFunction is such a modern cloud-native FaaS (Function as a Service) framework. It introduces many excellent open source technology stacks, including Knative, Tekton, Shipwright, Dapr, KEDA, etc. These technology stacks are used to create a new generation of open source function computing. The platform offers endless possibilities:

  • Shipwright allows users to freely select and switch image building tools in the process of function construction, and abstracts them to provide a unified API;
  • Knative provides an excellent synchronous function runtime with powerful automatic scaling capabilities;
  • KEDA can automatically scale based on more types of indicators, making it more flexible;
  • Dapr can abstract the general capabilities of different applications and reduce the workload of developing distributed applications.

This article is not going to talk about some very advanced theories. As a user who has just stepped into the serverless threshold, what is more needed is how to get started quickly, so as to have a perceptual understanding of function computing. In the process of subsequent use, we will slowly understand it. The architecture and design of it.

This article will lead you to quickly deploy and get started with OpenFunction, and experience how the synchronization function works through a demo.

Introduction to OpenFunction CLI

Since version 0.5, OpenFunction uses a new command-line tool ofn to install each dependent component. It has more comprehensive functions and supports one-click deployment, one-click uninstallation, and demo demonstration functions. Users can customize and install each component by setting the corresponding parameters, and can choose a specific version to make the installation more flexible. The installation process also provides real-time display, making the interface more beautiful. The components it supports and the Kubernetes versions it depends on are as follows:

Components Kubernetes 1.17 Kubernetes 1.18 Kubernetes 1.19 Kubernetes 1.20+
Knative Serving 0.21.1 0.23.3 0.25.2 1.0.1
Kourier 0.21.0 0.23.0 0.25.0 1.0.1
Serving Default Domain 0.21.0 0.23.0 0.25.0 1.0.1
Dapr 1.5.1 1.5.1 1.5.1 1.5.1
Keda 2.4.0 2.4.0 2.4.0 2.4.0
Shipwright 0.6.1 0.6.1 0.6.1 0.6.1
Tekton Pipelines 0.23.0 0.26.0 0.29.0 0.30.0
Cert Manager 1.5.4 1.5.4 1.5.4 1.5.4
Ingress Nginx na na 1.1.0 1.1.0

<center>Table 1 The Kubernetes version that the third-party components used by OpenFunction depend on</center>


The installation parameter of ofn install solves the compatibility problem between OpenFunction and Kubernetes, and automatically selects compatible components for installation according to the Kubernetes version, and provides various parameters for users to choose.

parameter Function
--all Used to install OpenFunction and all its dependencies.
--async Asynchronous runtime (Dapr & Keda) for installing OpenFunction.
--cert-manager * Used to install Cert Manager.
--dapr * Used to install Dapr.
--dry-run Used to prompt the current command to install the components and their versions.
--ingress * Used to install Ingress Nginx.
--keda* Used to install Keda.
--knative Used to install Knative Serving (with Kourier as the default gateway)
--region-cn For users with limited access to gcr.io or github.com.
--shipwright * Used to install ShipWright.
--sync Used to install OpenFunction Sync Runtime (to be supported).
--upgrade Upgrade components to the target version at install time.
--verbose Displays rough information.
--version Used to specify the version of OpenFunction to install. (default "v0.6.0")
--timeout Set the timeout period. The default is 5 minutes.

<center>Table II install command parameter list</center>

Deploy OpenFunction using OpenFunction CLI

With the command line tool ofn, OpenFunction is very simple to deploy. First of all, you need to install ofn. Taking the amd64 version of Linux as an example, it only takes two steps:

1. Download ofn

 $ wget -c  https://github.com/OpenFunction/cli/releases/download/v0.5.1/ofn_linux_amd64.tar.gz -O - | tar -xz

2. Grant permissions to ofn and move to the /usr/local/bin/ folder.

 $ chmod +x ofn && mv ofn /usr/local/bin/

Once ofn is installed, OpenFunction can be installed in just one step. Although all components can be installed by using the --all option, I know that most of my friends do not want to install an additional Ingress Controller. ingress, the command is as follows:

 $ ofn install --knative --async --shipwright --cert-manager --region-cn
Start installing OpenFunction and its dependencies.
The following components will be installed:
+------------------+---------+
| COMPONENT        | VERSION |
+------------------+---------+
| OpenFunction     | 0.6.0   |
| Keda             | 2.4.0   |
| Dapr             | 1.5.1   |
| Shipwright       | 0.6.1   |
| CertManager      | 1.5.4   |
| Kourier          | 1.0.1   |
| DefaultDomain    | 1.0.1   |
| Knative Serving  | 1.0.1   |
| Tekton Pipelines | 0.30.0  |
+------------------+---------+
 ✓ Dapr - Completed!
 ✓ Keda - Completed!
 ✓ Knative Serving - Completed!
 ✓ Shipwright - Completed!
 ✓ Cert Manager - Completed!
 ✓ OpenFunction - Completed!
🚀 Completed in 2m47.901328069s.

 ██████╗ ██████╗ ███████╗███╗   ██╗
██╔═══██╗██╔══██╗██╔════╝████╗  ██║
██║   ██║██████╔╝█████╗  ██╔██╗ ██║
██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║
╚██████╔╝██║     ███████╗██║ ╚████║
 ╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝

███████╗██╗   ██╗███╗   ██╗ ██████╗████████╗██╗ ██████╗ ███╗   ██╗
██╔════╝██║   ██║████╗  ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗  ██║
█████╗  ██║   ██║██╔██╗ ██║██║        ██║   ██║██║   ██║██╔██╗ ██║
██╔══╝  ██║   ██║██║╚██╗██║██║        ██║   ██║██║   ██║██║╚██╗██║
██║     ╚██████╔╝██║ ╚████║╚██████╗   ██║   ██║╚██████╔╝██║ ╚████║
╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝  ╚═══╝

Although this article demonstrates the synchronous function, the asynchronous runtime is also installed here. If you don't need it, you can remove the --async parameter, which does not affect the experiment in this article.

After the installation is complete, these namespaces will be created:

 $ kubectl get ns
NAME                              STATUS   AGE
cert-manager                      Active   17m
dapr-system                       Active   4m34s
io                                Active   3m31s
keda                              Active   4m49s
knative-serving                   Active   4m41s
kourier-system                    Active   3m57s
openfunction                      Active   3m37s
shipwright-build                  Active   4m26s
tekton-pipelines                  Active   4m50s

Each namespace corresponds to each component installed above. Currently, OpenFunction's Webhook needs to use CertManager to verify API access. We will remove this dependency in the future and no longer need to install CertManager .

custom domain suffix

Knative Serving currently uses Kourier as the ingress gateway. Since we have not deployed the Ingress Controller, we only have access to the function Kourier this entry.

Kourier is a lightweight gateway based on Envoy Proxy , a gateway implementation specially provided for Knative Serving service access. The details of the Envoy control plane will not be repeated in this article. If you are interested, you can read the official documentation and source code of Kourier. Here we only need to know that Kourier will provide an entry for function access. This access entry is provided through the domain name. What we need to do is to resolve the relevant domain name to the ClusterIP of Kourier.

Kourier creates two Services by default:

 $ kubectl -n kourier-system get svc
NAME               TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
kourier            LoadBalancer   10.233.7.202   <pending>     80:31655/TCP,443:30980/TCP   36m
kourier-internal   ClusterIP      10.233.47.71   <none>        80/TCP                       36m

Just resolve the domain name related to function access to 10.233.47.71 .

Although the domain name of each function is different, the domain name suffix is the same, and pan-domain name resolution can be used to resolve all domain names related to the function. The default domain name suffix of Kourier is example.com , which is configured through Knative's ConfigMap config-domain :

 $ kubectl -n knative-serving get cm config-domain -o yaml
apiVersion: v1
data:
  _example: |
    ################################
    #                              #
    #    EXAMPLE CONFIGURATION     #
    #                              #
    ################################

    # This block is not actually functional configuration,
    # but serves to illustrate the available configuration
    # options and document them in a way that is accessible
    # to users that `kubectl edit` this config map.
    #
    # These sample configuration options may be copied out of
    # this example block and unindented to be in the data block
    # to actually change the configuration.

    # Default value for domain.
    # Although it will match all routes, it is the least-specific rule so it
    # will only be used if no other domain matches.
    example.com: |

    # These are example settings of domain.
    # example.org will be used for routes having app=nonprofit.
    example.org: |
      selector:
        app: nonprofit

    # Routes having the cluster domain suffix (by default 'svc.cluster.local')
    # will not be exposed through Ingress. You can define your own label
    # selector to assign that domain suffix to your Route here, or you can set
    # the label
    #    "networking.knative.dev/visibility=cluster-local"
    # to achieve the same effect.  This shows how to make routes having
    # the label app=secret only exposed to the local cluster.
    svc.cluster.local: |
      selector:
        app: secret
kind: ConfigMap
metadata:
  annotations:
    knative.dev/example-checksum: 81552d0b
  labels:
    app.kubernetes.io/part-of: knative-serving
    app.kubernetes.io/version: 1.0.1
    serving.knative.dev/release: v1.0.1
  name: config-domain
  namespace: knative-serving

Delete the _example object and add a default domain name (such as openfunction.dev ), the final modification result is as follows:

 $ kubectl -n knative-serving get cm config-domain -o yaml
apiVersion: v1
data:
  openfunction.dev: ""
kind: ConfigMap
metadata:
  annotations:
    knative.dev/example-checksum: 81552d0b
  labels:
    app.kubernetes.io/part-of: knative-serving
    app.kubernetes.io/version: 1.0.1
    serving.knative.dev/release: v1.0.1
  name: config-domain
  namespace: knative-serving

Configure cluster domain name resolution

In order to facilitate access to functions in the Pod of Kubernetes, the CoreDNS of the Kubernetes cluster can be modified to enable the domain name suffix openfunction.dev to perform pan-resolution, and you need to add a paragraph to the CoreDNS configuration:

         template IN A openfunction.dev {
          match .*\.openfunction\.dev
          answer "{{ .Name }} 60 IN A 10.233.47.71"
          fallthrough
        }

The modified CoreDNS configuration is as follows:

 $ kubectl -n kube-system get cm coredns -o yaml
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health
        ready
        template IN A openfunction.dev {
          match .*\.openfunction\.dev
          answer "{{ .Name }} 60 IN A 10.233.47.71"
          fallthrough
        }
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          fallthrough in-addr.arpa ip6.arpa
        }
        hosts /etc/coredns/NodeHosts {
          ttl 60
          reload 15s
          fallthrough
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }
    ...

Synchronous function demo example

After configuring domain name resolution, you can run an example of a synchronous function to verify it. The OpenFunction official repository provides examples of synchronization functions in multiple languages :

Here we choose the function example of the Go language, let's take a look at the core deployment list first:

 # function-sample.yaml
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: function-sample
spec:
  version: "v2.0.0"
  image: "openfunctiondev/sample-go-func:latest"
  imageCredentials:
    name: push-secret
  port: 8080 # default to 8080
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "HelloWorld"
      FUNC_CLEAR_SOURCE: "true"
    srcRepo:
      url: "https://github.com/OpenFunction/samples.git"
      sourceSubPath: "functions/knative/hello-world-go"
      revision: "main"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    runtime: "knative"

Function is a CR defined by the CRD to convert the function into the final running application. This example contains two components:

  • build : Select different image building tools through Shipwright, and finally build the application as a container image;
  • Serving : Deploy applications to different runtimes through Serving CRD, you can choose synchronous runtime or asynchronous runtime. The synchronous runtime knative is chosen here.

Due to force majeure factors in the domestic environment, you can quickly pull the required dependency code from the public proxy image through GOPROXY , just add an environment variable in the FUNC_GOPROXY build stage in the deployment list- FUNC_GOPROXY can:

 # function-sample.yaml
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: function-sample
spec:
  version: "v2.0.0"
  image: "openfunctiondev/sample-go-func:latest"
  imageCredentials:
    name: push-secret
  port: 8080 # default to 8080
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "HelloWorld"
      FUNC_CLEAR_SOURCE: "true"
      FUNC_GOPROXY: "https://proxy.golang.com.cn,direct"
    srcRepo:
      url: "https://github.com/OpenFunction/samples.git"
      sourceSubPath: "functions/knative/hello-world-go"
      revision: "main"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    runtime: "knative"

Before creating the function, you need to create a secret to store your Docker Hub username and password:

 $ REGISTRY_SERVER=https://index.docker.io/v1/ REGISTRY_USER=<your_registry_user> REGISTRY_PASSWORD=<your_registry_password>
$ kubectl create secret docker-registry push-secret \
    --docker-server=$REGISTRY_SERVER \
    --docker-username=$REGISTRY_USER \
    --docker-password=$REGISTRY_PASSWORD

Let's create this Function with kubectl:

 $ kubectl apply -f function-sample.yaml

Check Function health:

 $ kubectl get function
NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING   URL   AGE
function-sample   Building                    builder-6ht76                   5s

Currently in the Build stage, the name of the builder is builder-6ht76 . Check the running status of the builder:

 $ kubectl get builder
NAME            PHASE   STATE      REASON   AGE
builder-6ht76   Build   Building            50s

This builder will start a Pod to build the image:

 $ kubectl get pod
NAME                                     READY   STATUS     RESTARTS   AGE
builder-6ht76-buildrun-jvtwk-vjlgt-pod   2/4     NotReady   0          2m11s

This Pod contains 4 containers:

  • step-source-default : pull source code;

  • step-prepare : set environment variables;

  • step-create : build the image;

  • step-results : Digest of the output image.

Check the function status again:

 $ kubectl get function
NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         URL                                              AGE
function-sample   Succeeded    Running        builder-6ht76   serving-6w4rn   http://openfunction.io/default/function-sample   6m

It has changed from the previous Building state to the Running state.

The URL here we cannot access directly because there is no Ingress Controller deployed. However, we can access it in other ways. Kourier abstracts each access entry into a CR called ksvc . Each ksvc corresponds to the access entry of a function. You can see if ksvc is currently created:

 $ kubectl get ksvc
NAME                       URL                                                        LATESTCREATED                   LATESTREADY                     READY   REASON
serving-6w4rn-ksvc-k4x29   http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev   serving-6w4rn-ksvc-k4x29-v200   serving-6w4rn-ksvc-k4x29-v200   True

The access entry of the function is http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev . Since the domain name resolution has been configured in the previous chapter, you can start a Pod to access the domain name directly:

 $ kubectl run curl --image=radial/busyboxplus:curl -i --tty
If you don't see a command prompt, try pressing enter.
[ root@curl:/ ]$
[ root@curl:/ ]$ curl http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev/default/function-sample/World
Hello, default/function-sample/World!
[ root@curl:/ ]$ curl http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev/default/function-sample/OpenFunction
Hello, default/function-sample/OpenFunction!

Accessing this function will automatically trigger a Pod to run:

 $ kubectl get pod
NAME                                                       READY   STATUS    RESTARTS   AGE
serving-6w4rn-ksvc-k4x29-v200-deployment-688d58bfb-6fvcg   2/2     Running   0          7s

The image used by this Pod is the image built in the previous build phase. In fact, this Pod is controlled by the Deployment. When there is no traffic, the number of copies of this Deployment is 0. When new traffic enters, it will first enter the Activator of Knative. After the Activator receives the traffic, it will notify the Autoscaler (automatic scaling controller), then the Autoscaler will expand the number of copies of the Deployment to 1, and finally the Activator will forward the traffic to the actual Pod to implement service calls. This process is also called a cold start .

If you no longer access this entry, after a period of time, the number of replicas of the Deployment will be shrunk to 0:

 $ kubectl get deploy
NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
serving-6w4rn-ksvc-k4x29-v200-deployment   0/0     0            0           22m

Summarize

Through the examples in this article, I believe that everyone should be able to appreciate some advantages of function computing, which brings us the ability to quickly disassemble and reconstruct business scenarios that we expect. As a user, you only need to focus on their development intentions, write functional code, and upload it to the code repository. You don't need to care about other things, you don't need to know about the infrastructure, and you don't even need to know the existence of containers and Kubernetes. The function computing platform will automatically allocate computing resources for you, and run tasks flexibly. Only when you need access, will you run tasks through capacity expansion, and will not consume computing resources at other times.

This article is published by OpenWrite , a multi-post blog platform!

KubeSphere
124 声望57 粉丝

KubeSphere 是一个开源的以应用为中心的容器管理平台,支持部署在任何基础设施之上,并提供简单易用的 UI,极大减轻日常开发、测试、运维的复杂度,旨在解决 Kubernetes 本身存在的存储、网络、安全和易用性等痛...