这是本节可打印的多页视图 点击这里开始打印.

返回常规视图.

v1.2 (latest)

欢迎来到 OpenFunction 的文档站点!

1 - 简介

概述

OpenFunction 是一个云原生的开源 FaaS(函数即服务)平台,旨在让您专注于业务逻辑,而不必维护底层的运行环境和基础设施。您只需要以函数的形式提交与业务相关的源代码,就可以生成事件驱动和动态伸缩的 Serverless 工作负载。

架构和设计

核心特性

  • 云无关、和云厂商的 BaaS 服务解耦

  • 可插拔架构、支持多种函数运行时

  • 支持同步和异步函数

  • 独有的异步函数支持,直接从事件源消费事件

  • 支持从函数源代码直接生成符合 OCI 标准的镜像

  • 实现从 0 到 N 的弹性自动伸缩

  • 基于事件源的特定指标实现领先的异步函数自动伸缩

  • 引入 Dapr 以简化同步和异步函数和 BaaS 服务的集成

  • K8s Gateway API 提供了领先的函数入口和流量管理能力

  • 灵活易用的事件管理框架

License

OpenFunction 采用 Apache License 2.0 版本授权。更多信息,请参见 LICENSE.

2 - 快速入门

2.1 - 安装

本文档将介绍如何安装 OpenFunction。

先决条件

  • 您需要拥有一个 Kubernetes 集群。

  • 您需要确保您的 Kubernetes 版本满足以下兼容性矩阵中描述的要求。

OpenFunction VersionKubernetes 1.21Kubernetes 1.22Kubernetes 1.23Kubernetes 1.24Kubernetes 1.25Kubernetes 1.26+
HEADN/AN/A
v1.2N/AN/A
v1.1.xN/A
v1.0.xN/A

安装 OpenFunction

现在您可以使用 helm charts 安装 OpenFunction 及其所有依赖项。

ofn CLI 安装方法已经弃用。

如果您想在离线环境中安装 OpenFunction,请参考 在离线环境中安装 OpenFunction

环境要求

  • Kubernetes version: >=v1.21.0-0
  • Helm version: >=v3.6.3

安装 OpenFunction helm charts 的步骤

  1. 首先运行以下命令添加 OpenFunction chart 仓库:

    helm repo add openfunction https://openfunction.github.io/charts/
    helm repo update
    
  2. 然后您有几个选项来设置 OpenFunction,您可以选择:

    • 安装所有组件:

      kubectl create namespace openfunction
      helm install openfunction openfunction/openfunction -n openfunction
      
    • 安装所有组件和 Revision Controller:

      kubectl create namespace openfunction
      helm install openfunction openfunction/openfunction -n openfunction --set revisionController.enable=true
      
    • 只安装 Serving(不包括 build):

      kubectl create namespace openfunction
      helm install openfunction --set global.ShipwrightBuild.enabled=false --set global.TektonPipelines.enabled=false openfunction/openfunction -n openfunction
      
    • 只安装 Knative 同步运行时:

      kubectl create namespace openfunction
      helm install openfunction --set global.Keda.enabled=false openfunction/openfunction -n openfunction
      
    • 只安装 OpenFunction 异步运行时:

      kubectl create namespace openfunction
      helm install openfunction --set global.Contour.enabled=false  --set global.KnativeServing.enabled=false openfunction/openfunction -n openfunction
      
  3. Run the following command to verify OpenFunction is up and running:

    kubectl get po -n openfunction
    

卸载 OpenFunction

Helm

如果您使用 Helm 安装了 OpenFunction,请运行以下命令来卸载 OpenFunction 及其依赖项。

helm uninstall openfunction -n openfunction

升级 OpenFunction

helm upgrade [RELEASE_NAME] openfunction/openfunction -n openfunction

使用 Helm v3,chart 创建的 CRD 默认不会更新,应手动更新。 另请参阅 Helm 关于 CRD 的文档

参考 to helm upgrade 以获取命令文档。

将现有的发布升级到新版本

从 OpenFunction v0.6.0 升级到 OpenFunction v0.7.x

从 v0.6.0 升级到 0.7.x 有一个破坏性的变化,需要额外的手动操作。

卸载 Chart

首先,您需要卸载旧的 openfunction release:

helm uninstall openfunction -n openfunction

确认组件命名空间已被删除,这需要一些时间:

kubectl get ns -o=jsonpath='{range .items[?(@.metadata.annotations.meta\.helm\.sh/release-name=="openfunction")]}{.metadata.name}: {.status.phase}{"\n"}{end}'

如果 knative-serving 命名空间长时间处于终止状态,尝试运行以下命令并删除 finalizers:

kubectl edit ingresses.networking.internal.knative.dev -n knative-serving

升级 OpenFunction CRDs

kubectl apply -f https://openfunction.sh1a.qingstor.com/crds/v0.7.0/openfunction.yaml

升级依赖组件的 CRDs

您只需要处理现有发布中包含的组件。

  • knative-serving CRDs
    kubectl apply -f https://openfunction.sh1a.qingstor.com/crds/v0.7.0/knative-serving.yaml
    
  • shipwright-build CRDs
    kubectl apply -f https://openfunction.sh1a.qingstor.com/crds/v0.7.0/shipwright-build.yaml
    
  • tekton-pipelines CRDs
    kubectl apply -f https://openfunction.sh1a.qingstor.com/crds/v0.7.0/tekton-pipelines.yaml
    

安装新 chart

helm repo update
helm install openfunction openfunction/openfunction -n openfunction

2.2 - 开始使用

2.2.1 - 先决条件

镜像仓库凭证

在构建函数时,您需要将函数容器镜像推送到像Docker HubQuay.io这样的容器镜像仓库。为此,您首先需要为您的容器镜像仓库生成一个密钥。

您可以通过填写REGISTRY_SERVERREGISTRY_USERREGISTRY_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

源代码库凭证

如果您的源代码位于私有git仓库中,您需要创建一个包含私有git仓库的用户名和密码的密钥:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: git-repo-secret
  annotations:
    build.shipwright.io/referenced.secret: "true"
type: kubernetes.io/basic-auth
stringData:
  username: <cleartext username>
  password: <cleartext password>
EOF

然后,您可以在Function CR的spec.build.srcRepo.credentials中引用此密钥

apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: function-sample
spec:
  version: "v2.0.0"
  image: "openfunctiondev/sample-go-func:v1"
  imageCredentials:
    name: push-secret
  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"
      credentials:
         name: git-repo-secret
  serving:
    template:
      containers:
        - name: function # DO NOT change this
          imagePullPolicy: IfNotPresent
    runtime: "knative"

Kafka

异步函数可以由消息队列中的事件触发,如Kafka,这里您可以找到设置Kafka集群的步骤,用于演示目的。

  1. 在默认命名空间中安装strimzi-kafka-operator

    helm repo add strimzi https://strimzi.io/charts/
    helm install kafka-operator -n default strimzi/strimzi-kafka-operator
    
  2. 运行以下命令在默认命名空间中创建Kafka集群和Kafka Topic。此命令创建的Kafka和Zookeeper集群的存储类型为ephemeral,并使用emptyDir进行演示。

    这里我们创建一个名为<kafka-server>的1副本Kafka服务器和一个名为<kafka-topic>的1副本主题,带有10个分区

    cat <<EOF | kubectl apply -f -
    apiVersion: kafka.strimzi.io/v1beta2
    kind: Kafka
    metadata:
      name: <kafka-server>
      namespace: default
    spec:
      kafka:
        version: 3.3.1
        replicas: 1
        listeners:
          - name: plain
            port: 9092
            type: internal
            tls: false
          - name: tls
            port: 9093
            type: internal
            tls: true
        config:
          offsets.topic.replication.factor: 1
          transaction.state.log.replication.factor: 1
          transaction.state.log.min.isr: 1
          default.replication.factor: 1
          min.insync.replicas: 1
          inter.broker.protocol.version: "3.1"
        storage:
          type: ephemeral
      zookeeper:
        replicas: 1
        storage:
          type: ephemeral
      entityOperator:
        topicOperator: {}
        userOperator: {}
    ---
    apiVersion: kafka.strimzi.io/v1beta2
    kind: KafkaTopic
    metadata:
      name: <kafka-topic>
      namespace: default
      labels:
        strimzi.io/cluster: <kafka-server>
    spec:
      partitions: 10
      replicas: 1
      config:
        cleanup.policy: delete
        retention.ms: 7200000
        segment.bytes: 1073741824
    EOF
    
  3. 运行以下命令检查Pod状态,并等待Kafka和Zookeeper运行并启动。

    $ kubectl get po
    NAME                                              READY   STATUS        RESTARTS   AGE
    <kafka-server>-entity-operator-568957ff84-nmtlw   3/3     Running       0          8m42s
    <kafka-server>-kafka-0                            1/1     Running       0          9m13s
    <kafka-server>-zookeeper-0                        1/1     Running       0          9m46s
    strimzi-cluster-operator-687fdd6f77-cwmgm         1/1     Running       0          11m
    

    运行以下命令查看Kafka集群的元数据。

    $ kafkacat -L -b <kafka-server>-kafka-brokers:9092
    

WasmEdge

函数现在支持使用WasmEdge作为工作负载运行时,这里您可以找到在Kubernetes集群中设置WasmEdge工作负载运行时的步骤(以containerd为容器运行时)。

您应在集群的所有节点(或将承载wasm工作负载的节点的子集)上运行以下步骤。

步骤1:安装WasmEdge

安装WasmEdge的最简单方法是运行以下命令。您的系统应已安装git和curl。

wget -qO- https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash -s -- -p /usr/local

步骤2:安装容器运行时

crun

crun项目已经内置了WasmEdge支持。现在,最简单的方法就是下载二进制文件并将其移动到/usr/local/bin/

wget https://github.com/OpenFunction/OpenFunction/releases/latest/download/crun-linux-amd64
mv crun-linux-amd64 /usr/local/bin/crun

如果上述方法对您不起作用,请参考构建并安装带有WasmEdge支持的crun二进制文件。

步骤3:设置CRI运行时

选项1:containerd

您可以按照此安装指南来安装containerd,并按照此设置指南来为Kubernetes设置containerd。

首先,编辑配置/etc/containerd/config.toml,添加以下部分来设置crun运行时,确保BinaryName等于您的crun二进制路径

# Add crun runtime here
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.crun]
  runtime_type = "io.containerd.runc.v2"
  pod_annotations = ["*.wasm.*", "wasm.*", "module.wasm.image/*", "*.module.wasm.image", "module.wasm.image/variant.*"]
  privileged_without_host_devices = false
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.crun.options]
    BinaryName = "/usr/local/bin/crun"

接下来,重启containerd服务:

sudo systemctl restart containerd

选项2:CRI-O

您可以按照此安装指南来安装CRI-O,并按照此设置指南来为Kubernetes设置CRI-O。

CRI-O默认使用runc运行时,我们需要配置它来使用crun。这是通过添加到两个配置文件来完成的。

首先,创建一个/etc/crio/crio.conf文件,并添加以下行作为其内容。它告诉CRI-O默认使用crun。

[crio.runtime]
default_runtime = "crun"

crun运行时反过来在/etc/crio/crio.conf.d/01-crio-runc.conf文件中定义。

[crio.runtime.runtimes.runc]
runtime_path = "/usr/lib/cri-o-runc/sbin/runc"
runtime_type = "oci"
runtime_root = "/run/runc"
# The above is the original content

# Add crun runtime here
[crio.runtime.runtimes.crun]
runtime_path = "/usr/local/bin/crun"
runtime_type = "oci"
runtime_root = "/run/crun"

接下来,重启CRI-O以应用配置更改。

systemctl restart crio

2.2.2 - 创建同步函数

在创建任何函数之前,请确保您已安装所有的先决条件

同步函数是其输入为HTTP请求的有效载荷的函数,输出或响应在函数逻辑处理输入有效载荷后立即发送给等待的客户端。以下是不同语言的一些同步函数示例:

同步函数
GoHello World, 多函数, 带路径参数的同步函数, 日志处理, 带输出绑定的同步函数
NodejsHello World, 带输出绑定的同步函数
PythonHello World
JavaHello World, 带输出的同步函数
DotNetHello World

您可以在这里找到更多函数示例

2.2.3 - 创建异步函数

在创建任何函数之前,请确保您已安装所有先决条件

异步函数是事件驱动的,它们的输入通常是来自非HTTP事件源的事件,如消息队列、cron触发器、MQTT代理等,通常客户端在通过传递事件触发异步函数后不会等待立即的响应。以下是不同语言的一些异步函数示例:

异步函数
GoKafka输入和HTTP输出绑定, Cron输入和Kafka输出绑定, Cron输入绑定, Kafka输入绑定, Kafka pubsub
NodejsMQTT绑定和pubsub
Python
JavaCron输入和Kafka输出绑定, Kafka pubsub
DotNet

您可以在这里找到更多函数示例

2.2.4 - 创建无服务器应用

在创建任何函数之前,请确保您已安装所有的先决条件

除了构建和运行无服务器函数,您还可以使用OpenFunction构建和运行无服务器应用。

这里有一些无服务器应用的示例:

无服务器应用
Go带有Dockerfile的Go应用
Java带有Dockerfile的Java应用, 不带Dockerfile的Java应用 & 源代码

您可以在这里找到关于这些无服务器应用的更多信息

2.2.5 - 创建Wasm函数

在创建任何函数之前,请确保您已安装所有的先决条件

这里有一些Wasm函数的示例:

语言Wasm函数运行时
Rustwasmedge-http-serverwasmedge

您可以在这里找到关于这些函数的更多信息

3 - 概念术语

3.1 - 函数定义

函数

FunctionBuildServing的控制平面,也是用户使用OpenFunction的接口。用户无需单独创建BuildServing,因为Function是定义函数的BuildServing的唯一地方。

一旦创建了函数,它将控制BuildServing的生命周期,无需用户干预:

  • 如果在函数中定义了Build,则一旦部署函数,将创建一个builder自定义资源来构建函数的容器镜像。

  • 如果在函数中定义了Serving,则将创建一个serving自定义资源来控制函数的服务和自动缩放。

  • BuildServing可以一起定义,这意味着首先将构建函数镜像,然后将其用于服务。

  • 可以定义Build而不定义Serving,在这种情况下,函数仅用于构建镜像。

  • 可以定义Serving而不定义Build,函数将使用先前构建的函数镜像进行服务。

构建

OpenFunction使用ShipwrightCloud Native Buildpacks将函数源代码构建成容器镜像。

一旦创建了包含Build规范的函数,就会创建一个builder自定义资源,该资源将使用Shipwright管理构建工具和策略。然后,Shipwright将使用Tekton控制构建容器镜像的过程,包括获取源代码、生成镜像工件和发布镜像。

服务

一旦创建了包含Serving规范的函数,就会创建一个Serving自定义资源来控制函数的服务阶段。目前,OpenFunction Serving支持两种运行时:Knative同步运行时和OpenFunction异步运行时。

同步运行时

对于同步函数,OpenFunction目前支持使用 Knative Serving 和 KEDA http-addon 作为运行时。

异步运行时

OpenFunction的异步运行时是一个基于KEDADapr实现的事件驱动运行时。异步函数可以由各种事件类型触发,如消息队列、cronjob和MQTT等。

3.2 - 函数构建

目前,OpenFunction支持使用Cloud Native Buildpacks构建函数镜像,无需创建Dockerfile

同时,您也可以使用OpenFunction通过Dockerfile构建无服务器应用

通过定义构建部分来构建函数

您可以从git仓库的源代码或本地存储的源代码构建函数或应用。

从git仓库的源代码构建函数

您可以通过在Function定义中添加一个构建部分来构建函数镜像。如果还定义了服务部分,那么构建完成后将立即启动函数。

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
spec:
  version: "v2.0.0"
  image: openfunctiondev/logs-async-handler:v1
  imageCredentials:
    name: push-secret
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "LogsHandler"
      FUNC_CLEAR_SOURCE: "true"
      ## 自定义函数框架版本,目前仅对functions-framework-go有效
      ## 通常情况下,您不需要这样做,因为构建器会自带最新的函数框架
      # FUNC_FRAMEWORK_VERSION: "v0.4.0"
      ## 使用FUNC_GOPROXY设置goproxy
      # FUNC_GOPROXY: "https://goproxy.cn"
    srcRepo:
      url: "https://github.com/OpenFunction/samples.git"
      sourceSubPath: "functions/async/logs-handler-function/"
      revision: "main"

要将函数镜像推送到容器仓库,您需要创建一个包含仓库凭证的秘密,并将该秘密添加到imageCredentials。 您可以参考先决条件以获取更多信息。

从本地源代码构建函数

要从本地源代码构建函数或应用,您需要将本地源代码打包成容器镜像,并将此镜像推送到容器仓库。

假设您的源代码在samples目录中,您可以使用以下Dockerfile来构建源代码包镜像。

FROM scratch
WORKDIR /
COPY samples samples/

然后,您可以像这样构建源代码包镜像:

docker build -t <your registry name>/sample-source-code:latest -f </path/to/the/dockerfile> .
docker push <your registry name>/sample-source-code:latest

建议使用空镜像scratch作为构建源代码包镜像的基础镜像,非空基础镜像可能会导致源代码复制失败。
与为git仓库方法定义spec.build.srcRepo.url字段不同,您需要定义spec.build.srcRepo.bundleContainer.image字段。

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
spec:
  build:
    srcRepo:
      bundleContainer:
        image: openfunctiondev/sample-source-code:latest
      sourceSubPath: "/samples/functions/async/logs-handler-function/"

sourceSubPath是源代码包镜像中源代码的绝对路径。

使用pack CLI构建函数

通常,直接从本地源代码构建函数镜像是必要的,特别是出于调试目的或用于离线环境。您可以使用pack CLI来实现这一点。

Pack是由Cloud Native Buildpacks项目维护的一个工具,用于支持使用buildpacks。它启用了以下功能:

  • 使用buildpacks构建应用。
  • 重新基准使用buildpacks创建的应用镜像。
  • 创建生态系统内使用的各种组件。

按照这里的说明安装pack CLI工具。 您可以在这里找到更多关于如何使用pack CLI的详细信息。

要从本地源代码构建OpenFunction函数镜像,您可以按照以下步骤操作:

下载函数示例

git clone https://github.com/OpenFunction/samples.git
cd samples/functions/knative/hello-world-go

使用pack CLI构建函数镜像

pack build func-helloworld-go --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld"  --env FUNC_CLEAR_SOURCE=true

本地启动函数镜像

docker run --rm --env="FUNC_CONTEXT={\"name\":\"HelloWorld\",\"version\":\"v1.0.0\",\"port\":\"8080\",\"runtime\":\"Knative\"}" --env="CONTEXT_MODE=self-host" --name func-helloworld-go -p 8080:8080 func-helloworld-go

访问函数

curl http://localhost:8080

输出示例:

hello, world!

OpenFunction构建器

要使用Cloud Native Buildpacks构建函数镜像,需要一个构建器镜像。

在这里,您可以找到OpenFunction社区维护的流行语言的构建器:

构建器
Goopenfunction/builder-go:v2.4.0 (openfunction/builder-go:latest)
Nodejsopenfunction/builder-node:v2-16.15 (openfunction/builder-node:latest)
Javaopenfunction/builder-java:v2-11, openfunction/builder-java:v2-16, openfunction/builder-java:v2-17, openfunction/builder-java:v2-18
Pythonopenfunction/gcp-builder:v1
DotNetopenfunction/gcp-builder:v1

3.3 - 构建策略

构建策略用于控制构建过程。有两种类型的策略,ClusterBuildStrategyBuildStrategy。 这两种策略都定义了一组步骤,这些步骤是控制应用程序构建过程所必需的。

ClusterBuildStrategy 是集群范围的,而 BuildStrategy 是命名空间范围的。

在 OpenFunction 中有 4 种内置的 ClusterBuildStrategy,你可以在以下各节中找到更多详细信息。

openfunction

openfunction ClusterBuildStrategy 使用 Buildpacks 来构建函数镜像,这是默认的构建策略。

以下是 openfunction ClusterBuildStrategy 的参数:

名称类型描述
RUN_IMAGEstring使用的运行镜像的引用
CACHE_IMAGEstring缓存镜像是一种在不同构建之间保留缓存层的方式,当构建具有大量依赖项的函数或应用程序(如 Java 函数)时,可以提高构建性能。
BASH_IMAGEstring策略使用的 bash 镜像。
ENV_VARSstring构建时 设置的环境变量。格式为 key1=value1,key2=value2

用户可以像这样设置这些参数:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
spec:
  build:
    shipwright:
      params:
        RUN_IMAGE: ""
        ENV_VARS: ""

buildah

buildah ClusterBuildStrategy 使用 buildah 来构建应用程序镜像。

要使用 buildah ClusterBuildStrategy,你可以定义一个 Function,如下所示:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: sample-go-app
  namespace: default
spec:
  build:
    builder: openfunction/buildah:v1.23.1
    dockerfile: Dockerfile
    shipwright:
      strategy:
        kind: ClusterBuildStrategy
        name: buildah

以下是 buildah ClusterBuildStrategy 的参数:

名称类型描述默认值
registry-searchstring用于搜索短名称镜像(如 golang:latest)的镜像仓库,用逗号分隔。docker.io,quay.io
registry-insecurestring不安全镜像仓库的全名。不安全的镜像仓库是没有有效 SSL 证书或只支持 HTTP 的镜像仓库。
registry-blockstring需要阻止拉取访问的镜像仓库。""

kaniko

kaniko ClusterBuildStrategy 使用 kaniko 来构建应用程序镜像。

要使用 kaniko ClusterBuildStrategy,你可以定义一个 Function,如下所示:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-kaniko
  namespace: default
spec:
  build:
    builder: openfunction/kaniko-executor:v1.7.0
    dockerfile: Dockerfile
    shipwright:
      strategy:
        kind: ClusterBuildStrategy
        name: kaniko

ko

ko ClusterBuildStrategy 使用 ko 来构建 Go 应用程序镜像。

要使用 ko ClusterBuildStrategy,你可以定义一个 Function,如下所示:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-ko
  namespace: default
spec:
  build:
    builder: golang:1.17
    dockerfile: Dockerfile
    shipwright:
      strategy:
        kind: ClusterBuildStrategy
        name: ko

以下是 ko ClusterBuildStrategy 的参数:

名称类型描述默认值
go-flagsstringValue for the GOFLAGS environment variable.""
ko-versionstringVersion of ko, must be either ’latest’, or a release name from https://github.com/google/ko/releases.""
package-directorystringThe directory inside the context directory containing the main package.“.”

自定义策略

用户可以自定义他们自己的策略。要自定义策略,你可以参考 这里

3.4 - 函数触发器

函数 触发器 用于定义如何触发函数。目前有两种触发器:HTTP 触发器Dapr 触发器。默认的触发器是 HTTP 触发器

HTTP 触发器

HTTP 触发器 通过 HTTP 请求触发函数。你可以像这样为函数定义一个 HTTP 触发器

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    triggers:
      http:
        port: 8080
        route:
          rules:
            - matches:
                - path:
                    type: PathPrefix
                    value: /echo

Dapr 触发器

Dapr 触发器 通过 Dapr bindingsDapr pubsub 的事件触发函数。你可以像这样定义一个带有 Dapr 触发器 的函数:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
  namespace: default
spec:
  serving:
    bindings:
      kafka-receiver:
        metadata:
          - name: brokers
            value: kafka-server-kafka-brokers:9092
          - name: authRequired
            value: "false"
          - name: publishTopic
            value: logs
          - name: topics
            value: logs
          - name: consumerGroup
            value: logs-handler
        type: bindings.kafka
        version: v1
    triggers:
      dapr:
        - name: kafka-receiver
          type: bindings.kafka

函数输入

输入 是函数可以从中获取额外输入数据的地方,目前支持 Dapr 状态存储 作为 输入

你可以像这样定义函数输入:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: logs-async-handler
  namespace: default
spec:
  serving:
    triggers:
      inputs:
        - dapr:
            name: mysql
            type: state.mysql

3.5 - 函数输出

函数输出

输出是函数可以发送数据的组件,包括:

例如,这里 你可以找到一个带有 cron 输入绑定和 Kafka 输出绑定的异步函数:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: cron-input-kafka-output
spec:
  ...
  serving:
    ...
    outputs:
      - dapr:
          name: kafka-server
          type: bindings.kafka
          operation: "create"
    bindings:
      kafka-server:
        type: bindings.kafka
        version: v1
        metadata:
        - name: brokers
          value: "kafka-server-kafka-brokers:9092"
        - name: topics
          value: "sample-topic"
        - name: consumerGroup
          value: "bindings-with-output"
        - name: publishTopic
          value: "sample-topic"
        - name: authRequired
          value: "false"

这里 是另一个使用 Kafka Pub/sub 组件作为输入的异步函数示例

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: autoscaling-subscriber
spec:
  ...
  serving:
    ...
    runtime: "async"
    outputs:
      - dapr:
          name: kafka-server
          type: pubsub.kafka
          topic: "sample-topic"
    pubsub:
      kafka-server:
        type: pubsub.kafka
        version: v1
        metadata:
          - name: brokers
            value: "kafka-server-kafka-brokers:9092"
          - name: authRequired
            value: "false"
          - name: allowedTopics
            value: "sample-topic"
          - name: consumerID
            value: "autoscaling-subscriber"

3.6 - 函数缩放

缩放是 FaaS 或 Serverless 平台的核心特性之一。

OpenFunction 在 ScaleOptions 中定义了函数缩放,并在 Triggers 中定义了激活函数缩放的触发器。

ScaleOptions

您可以为同步和异步函数定义统一的函数缩放选项,如下所示,这将对同步和异步函数都有效:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    scaleOptions:
      minReplicas: 0
      maxReplicas: 10

通常,仅定义 minReplicas 和 maxReplicas 对于异步函数来说是不够的。您可以像下面这样为异步函数定义单独的缩放选项,这将覆盖 minReplicas 和 maxReplicas。

您可以在 KEDA ScaleObject SpecKEDA ScaledJob Spec 中找到更多关于异步函数缩放选项的详细信息。

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    scaleOptions:
      minReplicas: 0
      maxReplicas: 10
      keda:
        scaledObject:
          pollingInterval: 15
          cooldownPeriod: 60
          advanced:
            horizontalPodAutoscalerConfig:
              behavior:
                scaleDown:
                  stabilizationWindowSeconds: 45
                  policies:
                  - type: Percent
                    value: 50
                    periodSeconds: 15
                scaleUp:
                  stabilizationWindowSeconds: 0

您也可以为 Knative 同步函数设置高级缩放选项,这将覆盖 minReplicas 和 maxReplicas。

您可以在 这里 找到更多关于 Knative 同步函数缩放选项的详细信息。

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    scaleOptions:
      knative:
        autoscaling.knative.dev/initial-scale: "1"
        autoscaling.knative.dev/scale-down-delay: "0"
        autoscaling.knative.dev/window: "60s"
        autoscaling.knative.dev/panic-window-percentage: "10.0"
        autoscaling.knative.dev/metric: "concurrency"
        autoscaling.knative.dev/target: "100"

触发器

触发器定义了如何激活异步函数的函数缩放。您可以使用所有 KEDA 缩放器 中定义的触发器作为 OpenFunction 的触发器规范。

同步函数的缩放是通过 HTTP 请求的各种选项激活的,这些选项已经在前面的 ScaleOption 部分中定义了。

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    scaleOptions:
      keda:
        triggers:
          - type: kafka
            metadata:
              topic: logs
              bootstrapServers: kafka-server-kafka-brokers.default.svc.cluster.local:9092
              consumerGroup: logs-handler
              lagThreshold: "20"

3.7 - 函数签名

不同函数签名的比较

OpenFunction中有三种函数签名:HTTPCloudEventOpenFunction。让我们使用Go函数为例,详细解释一下。

HTTPCloudEvent签名可以用来创建同步函数,而OpenFunction签名可以用来创建同步和异步函数。

此外,OpenFunction签名可以利用各种Dapr构建块,包括Bindings,Pub/sub等访问各种BaaS服务,帮助创建更强大的函数。(Dapr状态管理,配置将很快得到支持)

HTTPCloudEventOpenFunction
签名func(http.ResponseWriter, *http.Request) errorfunc(context.Context, cloudevents.Event) errorfunc(ofctx.Context, []byte) (ofctx.Out, error)
同步函数支持支持支持
异步函数不支持不支持支持
Dapr Binding不支持不支持支持
Dapr Pub/sub不支持不支持支持

示例

如您所见,OpenFunction签名是推荐的函数签名,我们正在努力在更多语言运行时支持此函数签名。

HTTPCloudEventOpenFunction
GoHello World, 多函数, 带路径参数的同步函数, 日志处理带路径参数的同步函数带路径参数的同步函数, 带输出绑定的同步函数, Kafka输入 & HTTP输出绑定, Cron输入 & Kafka输出绑定, Cron输入绑定, Kafka输入绑定, Kafka pubsub
NodejsHello World带输出绑定的同步函数, MQTT绑定 & pubsub
PythonHello World
JavaHello WorldCloudEvent带输出的同步函数, Cron输入 & Kafka输出绑定, Kafka pubsub
DotNetHello World

3.8 - Wasm 函数

WasmEdge 是一个轻量级、高性能、可扩展的 WebAssembly 运行时,用于云原生、边缘和去中心化应用。它支持无服务器应用、嵌入式函数、微服务、智能合约和物联网设备。

OpenFunction 现在支持使用 WasmEdge 作为工作负载运行时来构建和运行 wasm 函数。

你可以在这里找到 WasmEdge 集成提案

Wasm 容器镜像

包含 wasm 二进制文件的 wasm 镜像是一个没有操作系统层的特殊容器镜像。应该在这个 wasm 容器镜像中添加一个特殊的注解 module.wasm.image/variant: compat-smart,以便像 WasmEdge 这样的 wasm 运行时能够识别它。这在 OpenFunction 中是自动处理的,用户只需要将 workloadRuntime 指定为 wasmedge

Wasm 容器镜像的构建阶段

如果 function.spec.workloadRuntime 设置为 wasmedge,或者函数的注解包含 module.wasm.image/variant: compat-smartfunction.spec.build.shipwright.strategy 将根据名为 wasmedgeClusterBuildStrategy 自动生成,以便构建带有 module.wasm.image/variant: compat-smart 注解的 wasm 容器镜像。

Wasm 容器镜像的服务阶段

function.spec.workloadRuntime 设置为 wasmedge,或者函数的注解包含 module.wasm.image/variant: compat-smart 时:

  • 如果 function.spec.serving.annotations 不包含 module.wasm.image/variantmodule.wasm.image/variant: compat-smart 将自动添加到 function.spec.serving.annotations
  • 如果 function.spec.serving.template.runtimeClassName 没有设置,这个 runtimeClassName 将自动设置为默认的 openfunction-crun

如果你的 kubernetes 集群在像 Azure 这样的公共云中,你可以手动设置 spec.serving.template.runtimeClassName 来覆盖默认的 runtimeClassName

构建和运行 wasm 函数

要在 kubernetes 集群中设置 WasmEdge 工作负载运行时并将镜像推送到容器镜像仓库, 请参考先决条件部分以获取更多信息。

你可以在这里找到关于这个示例函数的更多信息。

  1. 创建一个 wasm 函数
cat <<EOF | kubectl apply -f -
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: wasmedge-http-server
spec:
  workloadRuntime: wasmedge
  image: openfunctiondev/wasmedge_http_server:0.1.0
  imageCredentials:
    name: push-secret
  build:
    dockerfile: Dockerfile
    srcRepo:
      revision: main
      sourceSubPath: functions/knative/wasmedge/http-server
      url: https://github.com/OpenFunction/samples
  serving:
    scaleOptions:
      minReplicas: 0
    template:
      containers:
        - command:
            - /wasmedge_hyper_server.wasm
          imagePullPolicy: IfNotPresent
          livenessProbe:
            initialDelaySeconds: 3
            periodSeconds: 30
            tcpSocket:
              port: 8080
          name: function
    triggers:
      http:
        port: 8080
        route:
          rules:
            - matches:
                - path:
                    type: PathPrefix
                    value: /echo
EOF
  1. 检查 wasm 函数的状态
kubectl get functions.core.openfunction.io -w
NAME                   BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         ADDRESS                                                      AGE
wasmedge-http-server   Succeeded    Running        builder-4p2qq   serving-lrd8c   http://wasmedge-http-server.default.svc.cluster.local/echo   12m
  1. 访问 wasm 函数

一旦 BUILDSTATE 变为 SucceededSERVINGSTATE 变为 Running,你就可以通过 ADDRESS 字段中的地址访问这个函数:

kubectl run curl --image=radial/busyboxplus:curl -i --tty
curl http://wasmedge-http-server.default.svc.cluster.local/echo  -X POST -d "WasmEdge"
WasmEdge

3.9 - 无服务器应用

除了构建和运行无服务器函数外,你还可以使用 OpenFuntion 构建和运行无服务器应用。

OpenFunction 支持两种不同的方式将源代码构建成容器镜像:

要将镜像推送到容器镜像仓库,你需要创建一个包含镜像仓库凭据的 secret,并将 secret 添加到 imageCredentials。 请参考 先决条件 部分获取更多信息。

使用 Dockerfile 构建和运行无服务器应用

如果你已经为你的应用创建了一个 Dockerfile,比如这个 Go 应用,你可以像 这样 以无服务器的方式构建和运行这个应用:

  1. 创建示例 Go 无服务器应用
cat <<EOF | kubectl apply -f -
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: sample-go-app
  namespace: default
spec:
  build:
    builder: openfunction/buildah:v1.23.1
    shipwright:
      strategy:
        kind: ClusterBuildStrategy
        name: buildah
    srcRepo:
      revision: main
      sourceSubPath: apps/buildah/go
      url: https://github.com/OpenFunction/samples.git
  image: openfunctiondev/sample-go-app:v1
  imageCredentials:
    name: push-secret
  serving:
    template:
      containers:
        - imagePullPolicy: IfNotPresent
          name: function
    triggers:
      http:
        port: 8080
  version: v1.0.0
  workloadRuntime: OCIContainer
EOF
  1. 检查应用状态 你可以通过 kubectl get functions.core.openfunction.io -w 检查无服务器应用的状态:
kubectl get functions.core.openfunction.io -w
NAME                    BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         ADDRESS                                                   AGE
sample-go-app           Succeeded    Running        builder-jgnzp   serving-q6wdp   http://sample-go-app.default.svc.cluster.local/           22m
  1. 访问这个应用 一旦 BUILDSTATE 变成 SucceededSERVINGSTATE 变成 Running,你就可以通过 ADDRESS 字段中的地址访问这个 Go 无服务器应用:
kubectl run curl --image=radial/busyboxplus:curl -i --tty
curl http://sample-go-app.default.svc.cluster.local

这里你可以找到一个 Java 无服务器应用(带有 Dockerfile)的示例。

不使用 Dockerfile 构建和运行无服务器应用

如果你有一个没有 Dockerfile 的应用,比如这个 Java 应用,你也可以像这个 Java 应用 那样以无服务器的方式构建和运行你的应用:

  1. 创建示例 Java 无服务器应用
cat <<EOF | kubectl apply -f -
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: sample-java-app-buildpacks
  namespace: default
spec:
  build:
    builder: cnbs/sample-builder:alpine
    srcRepo:
      revision: main
      sourceSubPath: apps/java-maven
      url: https://github.com/buildpacks/samples.git
  image: openfunction/sample-java-app-buildpacks:v1
  imageCredentials:
    name: push-secret
  serving:
    template:
      containers:
        - imagePullPolicy: IfNotPresent
          name: function
          resources: {}
    triggers:
      http:
        port: 8080
  version: v1.0.0
  workloadRuntime: OCIContainer

EOF
  1. 检查应用状态 你可以通过 kubectl get functions.core.openfunction.io -w 检查无服务器应用的状态:
kubectl get functions.core.openfunction.io -w
NAME                                 BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         ADDRESS                                                                AGE
sample-java-app-buildpacks           Succeeded    Running        builder-jgnzp   serving-q6wdp   http://sample-java-app-buildpacks.default.svc.cluster.local/           22m
  1. 访问这个应用 一旦 BUILDSTATE 变成 SucceededSERVINGSTATE 变成 Running,你就可以通过 ADDRESS 字段中的地址访问这个 Java 无服务器应用:
kubectl run curl --image=radial/busyboxplus:curl -i --tty
curl http://sample-java-app-buildpacks.default.svc.cluster.local

3.10 - BaaS 集成

OpenFunction 的一个独特特性是它可以通过 Dapr 简单地集成各种后端服务 (BaaS)。目前,OpenFunction 支持 Dapr 的 pub/subbindings 构建块,未来将会添加更多。

在 OpenFunction v0.7.0 及之前的版本中,OpenFunction 通过在每个函数实例的 pod 中注入一个 dapr sidecar 容器来集成 BaaS,这导致了以下问题:

  • dapr sidecar 容器的启动拖慢了整个函数实例的启动时间。
  • dapr sidecar 容器可能消耗的资源比函数容器本身还要多。

为了解决上述问题,OpenFunction 在 v0.8.0 中引入了 Dapr 独立模式

Dapr 独立模式

在 Dapr 独立模式中,每个函数都会创建一个 Dapr 代理服务,然后由该函数的所有实例共享。这样,就不再需要为每个函数实例启动一个单独的 Dapr sidecar 容器,从而大大减少了函数的启动时间。

选择适当的 Dapr 服务模式

所以现在你有两个选项来集成 BaaS:

  • Dapr Sidecar 模式
  • Dapr 独立模式

你可以为你的函数选择适当的 Dapr 服务模式。Dapr 独立模式 是推荐的和默认的模式。如果你的函数不经常扩展,或者你难以使用 Dapr 独立模式,你可以使用 Dapr Sidecar 模式

你可以通过设置两个标志来控制如何集成 BaaS,这两个标志都可以在函数的 spec.serving.annotations 中设置:

  • openfunction.io/enable-dapr 可以设置为 truefalse
  • openfunction.io/dapr-service-mode 可以设置为 standalonesidecar
  • openfunction.io/enable-dapr 设置为 true 时,用户可以通过将 openfunction.io/dapr-service-mode 设置为 standalonesidecar 来选择 Dapr 服务模式
  • openfunction.io/enable-dapr 设置为 false 时,openfunction.io/dapr-service-mode 的值将被忽略,既不会启动 Dapr Sidecar,也不会启动 Dapr 代理服务

如果这两个标志没有设置,它们都有默认值。

  • 如果 openfunction.io/enable-daprspec.serving.annotations 中没有定义,并且函数定义包含 spec.serving.inputsspec.serving.outputs,那么 openfunction.io/enable-dapr 的值将被设置为 true。否则,它将被设置为 false
  • 如果没有设置 openfunction.io/dapr-service-mode,其默认值为 standalone

下面你可以找到一个设置这两个标志的函数示例:

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: cron-input-kafka-output
spec:
  version: "v2.0.0"
  image: openfunctiondev/cron-input-kafka-output:v1
  imageCredentials:
    name: push-secret
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "HandleCronInput"
      FUNC_CLEAR_SOURCE: "true"
    srcRepo:
      url: "https://github.com/OpenFunction/samples.git"
      sourceSubPath: "functions/async/bindings/cron-input-kafka-output"
      revision: "main"
  serving:
    annotations:
      openfunction.io/enable-dapr: "true"
      openfunction.io/dapr-service-mode: "standalone"
    template:
      containers:
        - name: function # DO NOT change this
          imagePullPolicy: IfNotPresent
    triggers:
      dapr:
        - name: cron
          type: bindings.cron
    outputs:
      - dapr:
          component: kafka-server
          operation: "create"
    bindings:
      cron:
        type: bindings.cron
        version: v1
        metadata:
        - name: schedule
          value: "@every 2s"
      kafka-server:
        type: bindings.kafka
        version: v1
        metadata:
        - name: brokers
          value: "kafka-server-kafka-brokers:9092"
        - name: topics
          value: "sample-topic"
        - name: consumerGroup
          value: "bindings-with-output"
        - name: publishTopic
          value: "sample-topic"
        - name: authRequired
          value: "false"

3.11 - 网络

3.11.1 - 简介

概述

从 v0.5.0 开始,OpenFunction 使用 Kubernetes Ingress 为同步函数提供统一的入口点,并且必须安装 nginx ingress 控制器。

随着 Kubernetes Gateway API 的成熟,我们决定在 OpenFunction v0.7.0 中实现基于 Kubernetes Gateway API 的 OpenFunction Gateway,以替代之前基于 ingress 的域方法。

你可以在这里找到 OpenFunction Gateway 的提案

OpenFunction Gateway 提供了一个更强大、更灵活的函数网关,包括以下特性:

  • 使用户能够以更简单、厂商中立的方式切换到任何支持 Kubernetes Gateway API网关实现,如 Contour、Istio、Apache APISIX、Envoy Gateway(未来)等。

  • 用户可以选择安装默认的网关实现(Contour),然后定义一个新的 gateway.networking.k8s.io,或者使用他们环境中的任何现有网关实现,然后引用现有的 gateway.networking.k8s.io

  • 允许用户自定义他们自己的函数访问模式,如 hostTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}" 用于基于主机的访问。

  • 允许用户自定义他们自己的函数访问模式,如 pathTemplate: "{{.Namespace}}/{{.Name}}" 用于基于路径的访问。

  • 允许用户在函数定义中自定义每个函数的路由规则(基于主机、基于路径或两者都有),如果没有定义自定义的路由规则,将为每个函数提供默认的路由规则。

  • 直接将流量发送到 Knative 服务修订版,而不再通过 Knative 自己的网关。从 OpenFunction 0.7.0 开始,你只需要 OpenFunction Gateway 就可以访问 OpenFunction 同步函数,如果你不需要直接访问 Knative 服务,你可以忽略 Knative 的域配置错误。

  • 在函数修订版之间分割流量(未来)

下图说明了客户端流量如何通过 OpenFunction Gateway,然后直接到达函数:

3.11.2 - 网关

深入理解 OpenFunction 网关

OpenFunction 网关由 Kubernetes 网关支持,定义了用户如何访问同步函数。

每当创建一个 OpenFunction 网关 时,网关控制器将:

  • 如果 gatewaySpec.listeners 中没有,将添加一个名为 ofn-http-internal 的默认监听器。

  • 根据 domainclusterDomain 生成 gatewaySpec.listeners.[*].hostname

  • gatewaySpec.listenters 注入到 OpenFunction 网关gatewayRef 定义的现有 Kubernetes 网关 中。

  • 根据 OpenFunction 网关gatewayDef 中的 gatewaySpec.listenters 字段创建一个新的 Kubernetes 网关

  • 创建一个名为 gateway.openfunction.svc.cluster.local 的服务,该服务定义了同步函数的统一入口。

部署 OpenFunction 网关 后,你将能够在 OpenFunction 网关 状态中找到 Kubernetes 网关 和其 listeners 的状态:

status:
  conditions:
  - message: Gateway is scheduled
    reason: Scheduled
    status: "True"
    type: Scheduled
  - message: Valid Gateway
    reason: Valid
    status: "True"
    type: Ready
  listeners:
  - attachedRoutes: 0
    conditions:
    - message: Valid listener
      reason: Ready
      status: "True"
      type: Ready
    name: ofn-http-internal
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
  - attachedRoutes: 0
    conditions:
    - message: Valid listener
      reason: Ready
      status: "True"
      type: Ready
    name: ofn-http-external
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute

默认的 OpenFunction 网关

OpenFunction 网关 使用 Contour 作为默认的 Kubernetes 网关 实现。 安装 OpenFunction 后,将自动创建以下 OpenFunction 网关

apiVersion: networking.openfunction.io/v1alpha1
kind: Gateway
metadata:
  name: openfunction
  namespace: openfunction
spec:
  domain: ofn.io
  clusterDomain: cluster.local
  hostTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}"
  pathTemplate: "{{.Namespace}}/{{.Name}}"
  httpRouteLabelKey: "app.kubernetes.io/managed-by"
  gatewayRef:
    name: contour
    namespace: projectcontour
  gatewaySpec:
    listeners:
      - name: ofn-http-internal
        hostname: "*.cluster.local"
        protocol: HTTP
        port: 80
        allowedRoutes:
          namespaces:
            from: All
      - name: ofn-http-external
        hostname: "*.ofn.io"
        protocol: HTTP
        port: 80
        allowedRoutes:
          namespaces:
            from: All

你可以像下面这样自定义默认的 OpenFunction 网关

kubectl edit gateway openfunction -n openfunction

切换到不同的 Kubernetes 网关

你可以以更简单、厂商中立的方式切换到任何支持 Kubernetes 网关 API网关实现,如 Contour、Istio、Apache APISIX、Envoy Gateway(未来)等。

这里 你可以找到更多详细信息。

多个 OpenFunction 网关

对于 OpenFunction,多个 Gateway 没有意义,我们目前只支持一个 OpenFunction Gateway

3.11.3 - 路由

什么是 Route

RouteFunction 定义的一部分。Route 定义了来自 Gateway 监听器的流量如何路由到函数。

RouteGatewayRef 中指定了它将附加到的 Gateway,使其能够从 Gateway 接收流量。

一旦创建了同步 Function,函数控制器将:

  • 查找 openfunction 命名空间中名为 openfunctionGateway,然后如果函数中没有定义 route.gatewayRef,则附加到此 Gateway
  • 如果函数中没有定义 route.hostnames,则根据 Gateway.spec.hostTemplate 自动生成 route.hostnames
  • 如果函数中没有定义 route.rules,则根据路径 / 自动生成 route.rules
  • 根据 Route 创建一个 HTTPRoute 自定义资源。BackendRefs 将自动链接到相应的 Knative 服务修订版,并将 HTTPRouteLabelKey 标签添加到此 HTTPRoute
  • 创建服务 {{.Name}}.{{.Namespace}}.svc.cluster.local,此服务定义了从集群内部访问函数的入口。
  • 如果 route.gatewayRef 引用的 Gateway 发生变化,将更新 HTTPRoute

部署同步 Function 后,你将能够在 Function 的状态字段中找到 Function 地址和 Route 状态,例如:

status:
  addresses:
    - type: External
      value: http://function-sample-serving-only.default.ofn.io/
    - type: Internal
      value: http://function-sample-serving-only.default.svc.cluster.local/
  build:
    resourceHash: "14903236521345556383"
    state: Skipped
  route:
    conditions:
      - message: Valid HTTPRoute
        reason: Valid
        status: "True"
        type: Accepted
    hosts:
      - function-sample-serving-only.default.ofn.io
      - function-sample-serving-only.default.svc.cluster.local
    paths:
      - type: PathPrefix
        value: /
  serving:
    lastSuccessfulResourceRef: serving-znk54
    resourceHash: "10715302888241374768"
    resourceRef: serving-znk54
    service: serving-znk54-ksvc-nbg6f
    state: Running

基于主机的路由

基于主机 是默认的路由模式。当 route.hostnames 未定义时, 将根据 gateway.spec.hostTemplate 自动生成 route.hostnames。 如果 route.rules 未定义,将根据路径 / 自动生成 route.rules

kubectl apply -f - <<EOF
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  version: "v1.0.0"
  image: "openfunctiondev/v1beta1-http:latest"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    triggers:
      http:
        route:
          gatewayRef:
            name: openfunction
            namespace: openfunction
EOF

如果你正在使用默认的 OpenFunction Gateway,函数的外部地址将如下:

http://function-sample.default.ofn.io/

基于路径的路由

如果你在函数中定义了 route.hostnames,将根据 gateway.spec.pathTemplate 自动生成 route.rules

kubectl apply -f - <<EOF
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  version: "v1.0.0"
  image: "openfunctiondev/v1beta1-http:latest"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    triggers:
      http:
        route:
          gatewayRef:
            name: openfunction
            namespace: openfunction
           hostnames:
           - "sample.ofn.io"
EOF

如果你正在使用默认的 OpenFunction Gateway,函数的外部地址将如下:

http://sample.default.ofn.io/default/function-sample/

基于主机和路径的路由

你可以同时定义主机名和路径,以自定义流量应如何路由到你的函数。

kubectl apply -f - <<EOF
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  version: "v1.0.0"
  image: "openfunctiondev/v1beta1-http:latest"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    triggers:
      http:
        route:
          gatewayRef:
            name: openfunction
            namespace: openfunction
          rules:
            - matches:
                - path:
                    type: PathPrefix
                    value: /v2/foo
          hostnames:
          - "sample.ofn.io"
EOF

如果你正在使用默认的 OpenFunction Gateway,函数的外部地址将如下:

http://sample.default.ofn.io/v2/foo/

3.11.4 - 函数入口

有几种方法可以访问同步函数。我们将在以下部分详细说明。

本文档假设你正在使用默认的 OpenFunction 网关 并且你有一个名为 function-sample 的同步函数。

从集群内部访问函数

通过内部地址访问函数

OpenFunction 会为每个同步 Function 创建此服务:{{.Name}}.{{.Namespace}}.svc.cluster.local。此服务将用于提供函数的内部地址。

通过运行以下命令获取 Function 内部地址:

export FUNC_INTERNAL_ADDRESS=$(kubectl get function function-sample -o=jsonpath='{.status.addresses[?(@.type=="Internal")].value}')

这个地址提供了在集群内部访问函数的默认方法,适合用作 EventSourcesink.url

在 pod 中使用 curl 访问 Function

kubectl run --rm ofn-test -i --tty --image=radial/busyboxplus:curl -- curl -sv $FUNC_INTERNAL_ADDRESS

从集群外部访问函数

通过 Kubernetes 网关的 IP 地址访问函数

获取 Kubernetes 网关的 ip 地址:

export IP=$(kubectl get node -l "! node.kubernetes.io/exclude-from-external-load-balancers" -o=jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')

获取函数的 HOST 和 PATH:

export FUNC_HOST=$(kubectl get function function-sample -o=jsonpath='{.status.route.hosts[0]}')
export FUNC_PATH=$(kubectl get function function-sample -o=jsonpath='{.status.route.paths[0].value}')

直接使用 curl 访问 Function

curl -sv -HHOST:$FUNC_HOST http://$IP$FUNC_PATH

通过外部地址访问函数

要通过外部地址访问同步函数,你需要先配置 DNS。Magic DNS 或真实 DNS 都可以:

  • Magic DNS

    获取 Kubernetes 网关的 ip 地址:

    export IP=$(kubectl get node -l "! node.kubernetes.io/exclude-from-external-load-balancers" -o=jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
    

    用 Magic DNS 替换 OpenFunction 网关中定义的域:

    export DOMAIN="$IP.sslip.io"
    kubectl patch gateway.networking.openfunction.io/openfunction -n openfunction --type merge --patch '{"spec": {"domain": "'$DOMAIN'"}}'
    

    然后,你可以在 Function 的状态字段中看到 Function 外部地址:

    kubectl get function function-sample -oyaml
    
    status:
      addresses:
      - type: External
        value: http://function-sample.default.172.31.73.53.sslip.io/
      - type: Internal
        value: http://function-sample.default.svc.cluster.local/
      build:
        resourceHash: "14903236521345556383"
        state: Skipped
      route:
        conditions:
        - message: Valid HTTPRoute
          reason: Valid
          status: "True"
          type: Accepted
        hosts:
        - function-sample.default.172.31.73.53.sslip.io
        - function-sample.default.svc.cluster.local
        paths:
        - type: PathPrefix
          value: /
      serving:
        lastSuccessfulResourceRef: serving-t56fq
        resourceHash: "2638289828407595605"
        resourceRef: serving-t56fq
        service: serving-t56fq-ksvc-bv8ng
        state: Running
    
  • Real DNS

    如果你有一个外部 IP 地址,你可以配置一个通配符 A 记录作为你的域:

    # 这里 example.com 是 OpenFunction 网关中定义的域
    *.example.com == A <external-ip>
    

    如果你有一个 CNAME,你可以配置一个 CNAME 记录作为你的域:

    # 这里 example.com 是 OpenFunction 网关中定义的域
    *.example.com == CNAME <external-name>
    

    用你上面配置的域替换 OpenFunction 网关中定义的域:

    export DOMAIN="example.com"
    kubectl patch gateway.networking.openfunction.io/openfunction -n openfunction --type merge --patch '{"spec": {"domain": "'$DOMAIN'"}}'
    

    然后,你可以在 Function 的状态字段中看到 Function 外部地址:

    kubectl get function function-sample -oyaml
    
    status:
      addresses:
      - type: External
        value: http://function-sample.default.example.com/
      - type: Internal
        value: http://function-sample.default.svc.cluster.local/
      build:
        resourceHash: "14903236521345556383"
        state: Skipped
      route:
        conditions:
        - message: Valid HTTPRoute
          reason: Valid
          status: "True"
          type: Accepted
        hosts:
        - function-sample.default.example.com
        - function-sample.default.svc.cluster.local
        paths:
        - type: PathPrefix
          value: /
      serving:
        lastSuccessfulResourceRef: serving-t56fq
        resourceHash: "2638289828407595605"
        resourceRef: serving-t56fq
        service: serving-t56fq-ksvc-bv8ng
        state: Running
    

然后,你可以通过运行以下命令获取 Function 外部地址:

export FUNC_EXTERNAL_ADDRESS=$(kubectl get function function-sample -o=jsonpath='{.status.addresses[?(@.type=="External")].value}')

现在,你可以直接使用 curl 访问 Function

curl -sv $FUNC_EXTERNAL_ADDRESS

3.12 - CI/CD

概述

以前,用户可以使用 OpenFunction 将函数或应用程序源代码构建成容器镜像,然后直接将构建的镜像部署到底层的同步/异步 Serverless 运行时,无需用户干预。

但是,OpenFunction 无法在函数或应用程序源代码更改时重新构建镜像,然后重新部署它,也无法在此镜像更改时重新部署镜像(当镜像在另一个函数中手动构建和推送时)

从 v1.0.0 开始,OpenFunction 在一个新的组件 Revision Controller 中添加了检测源代码或镜像更改,然后重新构建和/或重新部署新构建的镜像的能力。修订控制器能够:

  • 检测 github、gitlab 或 gitee 中的源代码更改,然后在源代码更改时重新构建和重新部署新构建的镜像。
  • 检测包含源代码的捆绑容器镜像(镜像)的更改,然后在捆绑镜像更改时重新构建和重新部署新构建的镜像。
  • 检测函数或应用程序镜像的更改,然后在函数或应用程序镜像更改时重新部署新镜像。

快速开始

安装 Revision Controller

你可以在安装 OpenFunction 时启用 Revision Controller,只需在 helm 命令中添加以下标志即可。

--set revisionController.enable=true

你也可以在 OpenFunction 安装后启用 Revision Controller

kubectl apply -f https://raw.githubusercontent.com/OpenFunction/revision-controller/release-1.0/deploy/bundle.yaml

Revision Controller 默认将安装到 openfunction 命名空间。如果你想将其安装到另一个命名空间,你可以下载 bundle.yaml 并手动更改命名空间。

检测源代码或镜像更改

要检测源代码或镜像更改,你需要将修订控制器开关和参数添加到函数的注解中,如下所示。

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  annotations:
    openfunction.io/revision-controller: enable
    openfunction.io/revision-controller-params: |
      type: source
      repo-type: github
      polling-interval: 1m      
  name: function-http-java
  namespace: default
spec:
  build:
    ...
  serving:
    ...

注解

描述
openfunction.io/revision-controller是否启用修订控制器来检测此函数的源代码或镜像更改,可以设置为 enabledisable
openfunction.io/revision-controller-params修订控制器的参数。

参数

名称描述
type要检测的更改类型,包括 sourcesource-imageimage
polling-interval轮询镜像摘要或源代码头的间隔。
repo-typegit 仓库的类型,包括 githubgitlabgitee。默认为 github
base-urlgitlab 服务器的基本 url。
auth-typegitlab 服务器的认证类型。
project-idgitlab 仓库的项目 id。
insecure-registry如果镜像仓库不安全,你应该将此设置为 true。

3.13 - OpenFunction 事件

3.13.1 - 简介

概述

OpenFunction 事件是 OpenFunction 的事件管理框架。它提供以下核心特性:

  • 支持通过同步和异步调用触发目标函数
  • 用户定义的触发判断逻辑
  • OpenFunction 事件的组件可以由 OpenFunction 本身驱动

架构

以下图示说明了 OpenFunction 事件的架构。

openfunction-events

概念

EventSource

EventSource 定义了事件的生产者,例如 Kafka 服务,对象存储服务,甚至是函数。它包含了这些事件生产者的描述和发送这些事件的信息。

EventSource 支持以下类型的事件源服务器:

  • Kafka
  • Cron(调度器)
  • Redis

EventBus(ClusterEventBus)

EventBus 负责聚合事件并使其持久化。它包含了一个通常是消息队列(如 NATS Streaming 和 Kafka)的事件总线 broker 的描述,并为 EventSource 和 Trigger 提供这些配置。

EventBus 默认处理命名空间范围内的事件总线适配。对于集群范围,ClusterEventBus 可作为事件总线适配器,并在其他组件无法找到命名空间下的 EventBus 时生效。

EventBus 支持以下事件总线 broker:

  • NATS Streaming

Trigger

Trigger 是事件目的的抽象,例如接收到消息时需要做什么。它包含了由您定义的事件的目的,这告诉触发器它应该从哪个 EventSource 获取事件,并根据给定的条件决定是否触发目标函数。

参考

有关更多信息,请参阅 EventSource 规范EventBus 规范

3.13.2 - 使用 EventSource

本文档提供了一个示例,说明如何使用事件源触发同步函数。

在此示例中,定义了一个 EventSource,用于同步调用,使用事件源(一个 Kafka 服务器)作为函数(一个 Knative 服务)的输入绑定。当事件源生成事件时,它将通过 spec.sink 配置调用函数并获取同步返回。

创建函数

使用以下内容创建一个函数作为 EventSource Sink。有关如何创建函数的更多信息,请参见 创建同步函数

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: sink
spec:
  version: "v1.0.0"
  image: "openfunction/sink-sample:latest"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    triggers:
      http:
        port: 8080

创建函数后,运行以下命令获取函数的 URL。

$ kubectl get functions.core.openfunction.io
NAME   BUILDSTATE   SERVINGSTATE   BUILDER   SERVING         URL                                   AGE
sink   Skipped      Running                  serving-4x5wh   https://openfunction.io/default/sink   13s

创建 Kafka 集群

  1. 运行以下命令在默认命名空间中安装 strimzi-kafka-operator

    helm repo add strimzi https://strimzi.io/charts/
    helm install kafka-operator -n default strimzi/strimzi-kafka-operator
    
  2. 使用以下内容创建一个文件 kafka.yaml

    apiVersion: kafka.strimzi.io/v1beta2
    kind: Kafka
    metadata:
      name: kafka-server
      namespace: default
    spec:
      kafka:
        version: 3.3.1
        replicas: 1
        listeners:
          - name: plain
            port: 9092
            type: internal
            tls: false
          - name: tls
            port: 9093
            type: internal
            tls: true
        config:
          offsets.topic.replication.factor: 1
          transaction.state.log.replication.factor: 1
          transaction.state.log.min.isr: 1
          default.replication.factor: 1
          min.insync.replicas: 1
          inter.broker.protocol.version: "3.1"
        storage:
          type: ephemeral
      zookeeper:
        replicas: 1
        storage:
          type: ephemeral
      entityOperator:
        topicOperator: {}
        userOperator: {}
    ---
    apiVersion: kafka.strimzi.io/v1beta2
    kind: KafkaTopic
    metadata:
      name: events-sample
      namespace: default
      labels:
        strimzi.io/cluster: kafka-server
    spec:
      partitions: 10
      replicas: 1
      config:
        retention.ms: 7200000
        segment.bytes: 1073741824
    
  3. 运行以下命令在默认命名空间中部署一个名为 kafka-server 的 1-replica Kafka 服务器和一个名为 events-sample 的 1-replica Kafka 主题。此命令创建的 Kafka 和 Zookeeper 集群的存储类型为 ephemeral,并使用 emptyDir 进行演示。

    kubectl apply -f kafka.yaml
    
  4. 运行以下命令检查 pod 状态,并等待 Kafka 和 Zookeeper 启动并运行。

    $ kubectl get po
    NAME                                              READY   STATUS        RESTARTS   AGE
    kafka-server-entity-operator-568957ff84-nmtlw     3/3     Running       0          8m42s
    kafka-server-kafka-0                              1/1     Running       0          9m13s
    kafka-server-zookeeper-0                          1/1     Running       0          9m46s
    strimzi-cluster-operator-687fdd6f77-cwmgm         1/1     Running       0          11m
    
  5. 运行以下命令查看 Kafka 集群的元数据。

    kafkacat -L -b kafka-server-kafka-brokers:9092
    

触发同步函数

创建 EventSource

  1. 使用以下内容创建一个 EventSource 配置文件(例如,eventsource-sink.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: EventSource
    metadata:
      name: my-eventsource
    spec:
      logLevel: "2"
      kafka:
        sample-one:
          brokers: "kafka-server-kafka-brokers.default.svc.cluster.local:9092"
          topic: "events-sample"
          authRequired: false
      sink:
        uri: "http://openfunction.io.svc.cluster.local/default/sink"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f eventsource-sink.yaml
    
  3. 运行以下命令检查结果。

    $ kubectl get eventsources.events.openfunction.io
    NAME             EVENTBUS   SINK   STATUS
    my-eventsource                     Ready
    
    $ kubectl get components
    NAME                                                      AGE
    serving-8f6md-component-esc-kafka-sample-one-r527t        68m
    serving-8f6md-component-ts-my-eventsource-default-wz8jt   68m
    
    $ kubectl get deployments.apps
    NAME                                           READY   UP-TO-DATE   AVAILABLE   AGE
    serving-8f6md-deployment-v100-pg9sd            1/1     1            1           68m
    

创建事件生产者

要启动目标函数,需要创建一些事件来触发函数。

  1. 使用以下内容创建一个事件生产者配置文件(例如,events-producer.yaml)。

    apiVersion: core.openfunction.io/v1beta1
    kind: Function
    metadata:
      name: events-producer
    spec:
      version: "v1.0.0"
      image: openfunctiondev/v1beta1-bindings:latest
      serving:
        template:
          containers:
            - name: function
              imagePullPolicy: Always
        runtime: "async"
        inputs:
          - name: cron
            component: cron
        outputs:
          - name: target
            component: kafka-server
            operation: "create"
        bindings:
          cron:
            type: bindings.cron
            version: v1
            metadata:
              - name: schedule
                value: "@every 2s"
          kafka-server:
            type: bindings.kafka
            version: v1
            metadata:
              - name: brokers
                value: "kafka-server-kafka-brokers:9092"
              - name: topics
                value: "events-sample"
              - name: consumerGroup
                value: "bindings-with-output"
              - name: publishTopic
                value: "events-sample"
              - name: authRequired
                value: "false"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f events-producer.yaml
    
  3. 运行以下命令实时检查结果。

    $ kubectl get po --watch
    NAME                                                           READY   STATUS              RESTARTS   AGE
    serving-k6zw8-deployment-v100-fbtdc-dc96c4589-s25dh            0/2     ContainerCreating   0          1s
    serving-8f6md-deployment-v100-pg9sd-6666c5577f-4rpdg           2/2     Running             0          23m
    serving-k6zw8-deployment-v100-fbtdc-dc96c4589-s25dh            0/2     ContainerCreating   0          1s
    serving-k6zw8-deployment-v100-fbtdc-dc96c4589-s25dh            1/2     Running             0          5s
    serving-k6zw8-deployment-v100-fbtdc-dc96c4589-s25dh            2/2     Running             0          8s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      0/2     Pending             0          0s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      0/2     Pending             0          0s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      0/2     ContainerCreating   0          0s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      0/2     ContainerCreating   0          2s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      1/2     Running             0          4s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      1/2     Running             0          4s
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-8n6mk      2/2     Running             0          4s
    

3.13.3 - 使用 EventBus 和 Trigger

本文档提供了一个示例,说明如何使用 EventBus 和 Trigger。

先决条件

  • 您需要创建一个作为目标函数的函数以被触发。有关更多详细信息,请参见 创建函数
  • 您需要创建一个 Kafka 集群。有关更多详细信息,请参见 创建 Kafka 集群

部署 NATS 流服务器

运行以下命令部署 NATS 流服务器。本文档使用 nats://nats.default:4222 作为 NATS 流服务器的访问地址,stan 作为集群 ID。有关更多信息,请参见 NATS Streaming (STAN)

helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm install nats nats/nats
helm install stan nats/stan --set stan.nats.url=nats://nats:4222

创建 OpenFuncAsync 运行时函数

  1. 使用以下内容创建目标函数的配置文件(例如,openfuncasync-function.yaml),该函数由 Trigger CRD 触发并打印接收到的消息。

    apiVersion: core.openfunction.io/v1beta2
    kind: Function
    metadata:
      name: trigger-target
    spec:
      version: "v1.0.0"
      image: openfunctiondev/v1beta1-trigger-target:latest
      serving:
        scaleOptions:
          keda:
            scaledObject:
              pollingInterval: 15
              minReplicaCount: 0
              maxReplicaCount: 10
              cooldownPeriod: 30
            triggers:
              - type: stan
                metadata:
                  natsServerMonitoringEndpoint: "stan.default.svc.cluster.local:8222"
                  queueGroup: "grp1"
                  durableName: "ImDurable"
                  subject: "metrics"
                  lagThreshold: "10"
        triggers:
          dapr:
            - name: eventbus
              topic: metrics
        pubsub:
          eventbus:
            type: pubsub.natsstreaming
            version: v1
            metadata:
              - name: natsURL
                value: "nats://nats.default:4222"
              - name: natsStreamingClusterID
                value: "stan"
              - name: subscriptionType
                value: "queue"
              - name: durableSubscriptionName
                value: "ImDurable"
              - name: consumerID
                value: "grp1"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f openfuncasync-function.yaml
    

创建 EventBus 和 EventSource

  1. 使用以下内容创建 EventBus 的配置文件(例如,eventbus.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: EventBus
    metadata:
      name: default
    spec:
      natsStreaming:
        natsURL: "nats://nats.default:4222"
        natsStreamingClusterID: "stan"
        subscriptionType: "queue"
        durableSubscriptionName: "ImDurable"
    
  2. 使用以下内容创建 EventSource 的配置文件(例如,eventsource.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: EventSource
    metadata:
      name: my-eventsource
    spec:
      logLevel: "2"
      eventBus: "default"
      kafka:
        sample-two:
          brokers: "kafka-server-kafka-brokers.default.svc.cluster.local:9092"
          topic: "events-sample"
          authRequired: false
    
  3. 运行以下命令应用这些配置文件。

    kubectl apply -f eventbus.yaml
    kubectl apply -f eventsource.yaml
    
  4. 运行以下命令检查结果。

    $ kubectl get eventsources.events.openfunction.io
    NAME             EVENTBUS   SINK   STATUS
    my-eventsource   default           Ready
    
    $ kubectl get eventbus.events.openfunction.io
    NAME      AGE
    default   62m
    
    $ kubectl get components
    NAME                                                 AGE
    serving-9689d-component-ebfes-my-eventsource-cmcbw   46m
    serving-9689d-component-esc-kafka-sample-two-l99cg   46m
    serving-dxrhd-component-eventbus-t65q7               13m
    serving-zwlj4-component-ebft-my-trigger-4925n        100s
    

创建 Trigger

  1. 使用以下内容创建 Trigger 的配置文件(例如,trigger.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: Trigger
    metadata:
      name: my-trigger
    spec:
      logLevel: "2"
      eventBus: "default"
      inputs:
        inputDemo:
          eventSource: "my-eventsource"
          event: "sample-two"
      subscribers:
        - condition: inputDemo
          topic: "metrics"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f trigger.yaml
    
  3. 运行以下命令检查结果。

    $ kubectl get triggers.events.openfunction.io
    NAME         EVENTBUS   STATUS
    my-trigger   default    Ready
    
    $ kubectl get eventbus.events.openfunction.io
    NAME      AGE
    default   62m
    
    $ kubectl get components
    NAME                                                 AGE
    serving-9689d-component-ebfes-my-eventsource-cmcbw   46m
    serving-9689d-component-esc-kafka-sample-two-l99cg   46m
    serving-dxrhd-component-eventbus-t65q7               13m
    serving-zwlj4-component-ebft-my-trigger-4925n        100s
    

创建事件生产者

  1. 使用以下内容创建事件生产者配置文件(例如,events-producer.yaml)。

    apiVersion: core.openfunction.io/v1beta2
    kind: Function
    metadata:
      name: events-producer
    spec:
      version: "v1.0.0"
      image: openfunctiondev/v1beta1-bindings:latest
      serving:
        template:
          containers:
            - name: function
              imagePullPolicy: Always
        triggers:
          dapr:
            - name: cron
              type: bindings.cron
        outputs:
          - dapr:
              name: kafka-server
              operation: "create"
        bindings:
          cron:
            type: bindings.cron
            version: v1
            metadata:
              - name: schedule
                value: "@every 2s"
          kafka-server:
            type: bindings.kafka
            version: v1
            metadata:
              - name: brokers
                value: "kafka-server-kafka-brokers:9092"
              - name: topics
                value: "events-sample"
              - name: consumerGroup
                value: "bindings-with-output"
              - name: publishTopic
                value: "events-sample"
              - name: authRequired
                value: "false"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f events-producer.yaml
    
  3. 运行以下命令观察目标异步函数的变化。

    $ kubectl get functions.core.openfunction.io
    NAME                                  BUILDSTATE   SERVINGSTATE   BUILDER   SERVING         URL                                   AGE
    trigger-target                        Skipped      Running                  serving-dxrhd                                         20m
    
    $ kubectl get po --watch
    NAME                                                     READY   STATUS              RESTARTS   AGE
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      0/2     Pending             0          0s
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      0/2     Pending             0          0s
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      0/2     ContainerCreating   0          0s
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      0/2     ContainerCreating   0          2s
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      1/2     Running             0          4s
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      1/2     Running             0          4s
    serving-dxrhd-deployment-v100-xmrkq-785cb5f99-6hclm      2/2     Running             0          4s
    

3.13.4 - 在一个 EventSource 中使用多个源

本文档描述了如何在一个 EventSource 中使用多个源。

先决条件

  • 您需要创建一个作为目标函数的函数以被触发。有关更多详细信息,请参见 创建函数
  • 您需要创建一个 Kafka 集群。有关更多详细信息,请参见 创建 Kafka 集群

在一个 EventSource 中使用多个源

  1. 使用以下内容创建一个 EventSource 配置文件(例如,eventsource-multi.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: EventSource
    metadata:
      name: my-eventsource
    spec:
      logLevel: "2"
      kafka:
        sample-three:
          brokers: "kafka-server-kafka-brokers.default.svc.cluster.local:9092"
          topic: "events-sample"
          authRequired: false
      cron:
        sample-three:
          schedule: "@every 5s"
      sink:
        uri: "http://openfunction.io.svc.cluster.local/default/sink"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f eventsource-multi.yaml
    
  3. 运行以下命令观察变化。

    $ kubectl get eventsources.events.openfunction.io
    NAME             EVENTBUS   SINK   STATUS
    my-eventsource                     Ready
    
    $ kubectl get components
    NAME                                                      AGE
    serving-vqfk5-component-esc-cron-sample-three-dzcpv       35s
    serving-vqfk5-component-esc-kafka-sample-one-nr9pq        35s
    serving-vqfk5-component-ts-my-eventsource-default-q6g6m   35s
    
    $ kubectl get deployments.apps
    NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
    serving-4x5wh-ksvc-wxbf2-v100-deployment   1/1     1            1           3h14m
    serving-vqfk5-deployment-v100-vdmvj        1/1     1            1           48s
    

3.13.5 - 使用 ClusterEventBus

本文档描述了如何使用 ClusterEventBus。

先决条件

您已完成 使用 EventBus 和 Trigger 中描述的步骤。

使用 ClusterEventBus

  1. 使用以下内容创建一个 ClusterEventBus 配置文件(例如,clustereventbus.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: ClusterEventBus
    metadata:
      name: default
    spec:
      natsStreaming:
        natsURL: "nats://nats.default:4222"
        natsStreamingClusterID: "stan"
        subscriptionType: "queue"
        durableSubscriptionName: "ImDurable"
    
  2. 运行以下命令删除 EventBus。

    kubectl delete eventbus.events.openfunction.io default
    
  3. 运行以下命令应用配置文件。

    kubectl apply -f clustereventbus.yaml
    
  4. 运行以下命令检查结果。

    $ kubectl get eventbus.events.openfunction.io
    No resources found in default namespace.
    
    $ kubectl get clustereventbus.events.openfunction.io
    NAME      AGE
    default   21s
    

3.13.6 - 使用带有条件的 Trigger

本文档描述了如何使用带有条件的触发器。

先决条件

您已完成 使用 EventBus 和 Trigger 中描述的步骤。

使用带有条件的触发器

创建两个事件源

  1. 使用以下内容创建一个 EventSource 配置文件(例如,eventsource-a.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: EventSource
    metadata:
      name: eventsource-a
    spec:
      logLevel: "2"
      eventBus: "default"
      kafka:
        sample-five:
          brokers: "kafka-server-kafka-brokers.default.svc.cluster.local:9092"
          topic: "events-sample"
          authRequired: false
    
  2. 使用以下内容创建另一个 EventSource 配置文件(例如,eventsource-b.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: EventSource
    metadata:
      name: eventsource-b
    spec:
      logLevel: "2"
      eventBus: "default"
      cron:
        sample-five:
          schedule: "@every 5s"
    
  3. 运行以下命令应用这两个配置文件。

    kubectl apply -f eventsource-a.yaml
    kubectl apply -f eventsource-b.yaml
    

创建带有条件的触发器

  1. 使用以下内容创建一个带有 condition 的触发器配置文件(例如,condition-trigger.yaml)。

    apiVersion: events.openfunction.io/v1alpha1
    kind: Trigger
    metadata:
      name: condition-trigger
    spec:
      logLevel: "2"
      eventBus: "default"
      inputs:
        eventA:
          eventSource: "eventsource-a"
          event: "sample-five"
        eventB:
          eventSource: "eventsource-b"
          event: "sample-five"
      subscribers:
      - condition: eventB
        sink:
          uri: "http://openfunction.io.svc.cluster.local/default/sink"
      - condition: eventA && eventB
        topic: "metrics"
    
  2. 运行以下命令应用配置文件。

    kubectl apply -f condition-trigger.yaml
    
  3. 运行以下命令检查结果。

    $ kubectl get eventsources.events.openfunction.io
    NAME            EVENTBUS   SINK   STATUS
    eventsource-a   default           Ready
    eventsource-b   default           Ready
    
    $ kubectl get triggers.events.openfunction.io
    NAME                EVENTBUS   STATUS
    condition-trigger   default    Ready
    
    $ kubectl get eventbus.events.openfunction.io
    NAME      AGE
    default   12s
    
  4. 运行以下命令,您可以从输出中看到,由于事件源 eventsource-b 是一个 cron 任务,所以触发器中的 eventB 条件匹配,触发了 Knative 服务。

    $ kubectl get functions.core.openfunction.io
    NAME                                  BUILDSTATE   SERVINGSTATE   BUILDER   SERVING         URL                                   AGE
    sink                                  Skipped      Running                  serving-4x5wh   https://openfunction.io/default/sink   3h25m
    
    $ kubectl get po
    NAME                                                        READY   STATUS    RESTARTS   AGE
    serving-4x5wh-ksvc-wxbf2-v100-deployment-5c495c84f6-k2jdg   2/2     Running   0          46s
    
  5. 参考 创建事件生产者 创建一个事件生产者。

  6. 运行以下命令,您可以从输出中看到,触发器中的 eventA && eventB 条件匹配,事件同时发送到事件总线的 metrics 主题。触发了 OpenFuncAsync 函数。

    $ kubectl get functions.core.openfunction.io
    NAME                                  BUILDSTATE   SERVINGSTATE   BUILDER   SERVING         URL                                   AGE
    trigger-target                        Skipped      Running                  serving-7hghp                                         103s
    
    $ kubectl get po
    NAME                                                        READY   STATUS    RESTARTS   AGE
    serving-7hghp-deployment-v100-z8wrf-946b4854d-svf55         2/2     Running   0          18s
    

4 - 操作手册

4.1 - 网络

4.1.1 - 切换到其他 Kubernetes Gateway 实现

您可以以更简单、厂商中立的方式切换到任何支持 Kubernetes Gateway API网关实现,如 Contour、Istio、Apache APISIX、Envoy Gateway(未来)等。

例如,您可以选择使用 Istio 作为底层的 Kubernetes Gateway,如下所示:

  1. 安装 OpenFunction,但不包括 Contour
helm install openfunction --set global.Contour.enabled=false openfunction/openfunction -n openfunction
  1. 安装 Istio,然后启用其 Knative 集成:
kubectl apply -l knative.dev/crd-install=true -f https://github.com/knative/net-istio/releases/download/knative-v1.3.0/istio.yaml
kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.3.0/istio.yaml
kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.3.0/net-istio.yaml
  1. 创建一个名为 istioGatewayClass
kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: GatewayClass
metadata:
  name: istio
spec:
  controllerName: istio.io/gateway-controller
  description: The default Istio GatewayClass
EOF
  1. 创建一个 OpenFunction Gateway
kubectl apply -f - <<EOF
apiVersion: networking.openfunction.io/v1alpha1
kind: Gateway
metadata:
  name: custom-gateway
  namespace: openfunction
spec:
  domain: ofn.io
  clusterDomain: cluster.local
  hostTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}"
  pathTemplate: "{{.Namespace}}/{{.Name}}"
  gatewayDef:
    namespace: openfunction
    gatewayClassName: istio
  gatewaySpec:
    listeners:
    - name: ofn-http-external
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: All
EOF
  1. FunctiongatewayRef 字段中引用自定义的 OpenFunction Gateway(Istio):
kubectl apply -f - <<EOF
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  version: "v1.0.0"
  image: "openfunctiondev/v1beta1-http:latest"
  serving:
    template:
      containers:
        - name: function
          imagePullPolicy: Always
    triggers:
      http:
        route:
          gatewayRef:
            name: custom-gateway
            namespace: openfunction
EOF

4.1.2 - 配置本地域名

配置本地域名

通过配置本地域名,您可以通过函数的外部地址从 Kubernetes 集群内部访问函数。

基于 Gateway.spec.domain 配置 CoreDNS

假设您有一个定义了此 domain*.ofn.ioGateway,您需要通过以下命令更新 CoreDNS 配置:

  1. 编辑 coredns 配置映射:
kubectl -n kube-system edit cm coredns -o yaml
  1. .:53 部分的配置文件中添加 rewrite stop name regex .*\.ofn\.io gateway.openfunction.svc.cluster.local,例如:
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        rewrite stop name regex .*\.ofn\.io gateway.openfunction.svc.cluster.local
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }    
...

基于 Gateway.spec.domain 配置 nodelocaldns

如果您也在使用 nodelocaldns,如 Kubesphere,您需要通过以下命令更新 nodelocaldns 配置:

  1. 编辑 nodelocaldns 配置映射:
kubectl -n kube-system edit cm nodelocaldns -o yaml
  1. 在配置文件中添加 ofn.io:53 部分,例如:
apiVersion: v1
data:
  Corefile: |
    ofn.io:53 {
        errors
        cache {
            success 9984 30
            denial 9984 5
        }
        reload
        loop
        bind 169.254.25.10
        forward . 10.233.0.3 {
            force_tcp
        }
        prometheus :9253
    }
    cluster.local:53 {
        errors
        cache {
            success 9984 30
            denial 9984 5
        }
        reload
        loop
        bind 169.254.25.10
        forward . 10.233.0.3 {
            force_tcp
        }
        prometheus :9253
        health 169.254.25.10:9254
    }
    .:53 {
        errors
        cache 30
        reload
        loop
        bind 169.254.25.10
        forward . /etc/resolv.conf
        prometheus :9253
    }    
...

5 - 最佳实践

要获取更多使用 OpenFunction 的示例,请参考 示例仓库,例如 基于队列深度的服务自动扩展

5.1 - 创建一个基于 Knative 的函数以通过 Dapr 组件与中间件交互

了解如何创建一个基于 Knative 的函数,通过 Dapr 组件与中间件交互。

本文档描述了如何创建一个基于 Knative 的函数,通过 Dapr 组件与中间件交互。

概述

与异步函数类似,基于 Knative 运行时的函数可以通过 Dapr 组件与中间件交互。本文档使用两个函数,function-frontkafka-input,进行演示。

以下图表说明了这些函数之间的关系。

先决条件

创建 Kafka 服务器和主题

  1. 运行以下命令,在默认命名空间中安装 strimzi-kafka-operator

    helm repo add strimzi https://strimzi.io/charts/
    helm install kafka-operator -n default strimzi/strimzi-kafka-operator
    
  2. 使用以下内容创建一个文件 kafka.yaml

    apiVersion: kafka.strimzi.io/v1beta2
    kind: Kafka
    metadata:
      name: kafka-server
      namespace: default
    spec:
      kafka:
        version: 3.3.1
        replicas: 1
        listeners:
          - name: plain
            port: 9092
            type: internal
            tls: false
          - name: tls
            port: 9093
            type: internal
            tls: true
        config:
          offsets.topic.replication.factor: 1
          transaction.state.log.replication.factor: 1
          transaction.state.log.min.isr: 1
          default.replication.factor: 1
          min.insync.replicas: 1
          inter.broker.protocol.version: "3.1"
        storage:
          type: ephemeral
      zookeeper:
        replicas: 1
        storage:
          type: ephemeral
      entityOperator:
        topicOperator: {}
        userOperator: {}
    ---
    apiVersion: kafka.strimzi.io/v1beta2
    kind: KafkaTopic
    metadata:
      name: sample-topic
      namespace: default
      labels:
        strimzi.io/cluster: kafka-server
    spec:
      partitions: 10
      replicas: 1
      config:
        retention.ms: 7200000
        segment.bytes: 1073741824
    
  3. 运行以下命令,在默认命名空间中部署一个名为 kafka-server 的 1 副本 Kafka 服务器和一个名为 sample-topic 的 1 副本 Kafka 主题。

    kubectl apply -f kafka.yaml
    
  4. 运行以下命令,检查 pod 状态,等待 Kafka 和 Zookeeper 启动并运行。

    $ kubectl get po
    NAME                                              READY   STATUS        RESTARTS   AGE
    kafka-server-entity-operator-568957ff84-nmtlw     3/3     Running       0          8m42s
    kafka-server-kafka-0                              1/1     Running       0          9m13s
    kafka-server-zookeeper-0                          1/1     Running       0          9m46s
    strimzi-cluster-operator-687fdd6f77-cwmgm         1/1     Running       0          11m
    
  5. 运行以下命令,查看 Kafka 集群的元数据。

    # 启动一个实用工具 pod。
    $ kubectl run utils --image=arunvelsriram/utils -i --tty --rm
    # 检查 Kafka 集群的元数据。
    $ kafkacat -L -b kafka-server-kafka-brokers:9092
    

创建函数

  1. 使用以下示例 YAML 文件创建一个清单 kafka-input.yaml,并修改 spec.image 的值以设置您自己的镜像仓库地址。字段 spec.serving.inputs 定义了一个指向 Kafka 服务器的 Dapr 组件的输入源。这意味着 kafka-input 函数将由 Kafka 服务器的主题 sample-topic 中的事件驱动。

    apiVersion: core.openfunction.io/v1beta2
    kind: Function
    metadata:
      name: kafka-input
    spec:
      version: "v1.0.0"
      image: <your registry name>/kafka-input:latest
      imageCredentials:
        name: push-secret
      build:
        builder: openfunction/builder-go:latest
        env:
          FUNC_NAME: "HandleKafkaInput"
          FUNC_CLEAR_SOURCE: "true"
        srcRepo:
          url: "https://github.com/OpenFunction/samples.git"
          sourceSubPath: "functions/async/bindings/kafka-input"
          revision: "main"
      serving:
        scaleOptions:
          minReplicas: 0
          maxReplicas: 10
          keda:
            triggers:
            - type: kafka
              metadata:
                topic: sample-topic
                bootstrapServers: kafka-server-kafka-brokers.default.svc:9092
                consumerGroup: kafka-input
                lagThreshold: "20"
            scaledObject:
              pollingInterval: 15
              cooldownPeriod: 60
              advanced:
                horizontalPodAutoscalerConfig:
                  behavior:
                    scaleDown:
                      stabilizationWindowSeconds: 45
                      policies:
                      - type: Percent
                        value: 50
                        periodSeconds: 15
                    scaleUp:
                      stabilizationWindowSeconds: 0
    
        triggers:
          dapr:
          - name: target-topic
            type: bindings.kafka
        bindings:
          target-topic:
            type: bindings.kafka
            version: v1
            metadata:
              - name: brokers
                value: "kafka-server-kafka-brokers:9092"
              - name: topics
                value: "sample-topic"
              - name: consumerGroup
                value: "kafka-input"
              - name: publishTopic
                value: "sample-topic"
              - name: authRequired
                value: "false"
        template:
          containers:
            - name: function
              imagePullPolicy: Always
    
  2. 运行以下命令创建函数 kafka-input

    kubectl apply -f kafka-input.yaml
    
  3. 使用以下示例 YAML 文件创建一个清单 function-front.yaml,并修改 spec.image 的值以设置您自己的镜像仓库地址。

       apiVersion: core.openfunction.io/v1beta2
       kind: Function
       metadata:
         name: function-front
       spec:
         version: "v1.0.0"
         image: "<your registry name>/sample-knative-dapr:latest"
         imageCredentials:
           name: push-secret
         build:
           builder: openfunction/builder-go:latest
           env:
             FUNC_NAME: "ForwardToKafka"
             FUNC_CLEAR_SOURCE: "true"
           srcRepo:
             url: "https://github.com/OpenFunction/samples.git"
             sourceSubPath: "functions/knative/with-output-binding"
             revision: "main"
         serving:
            hooks:
              pre:
                - plugin-custom
                - plugin-example
              post:
                - plugin-example
                - plugin-custom
           scaleOptions:
             minReplicas: 0
             maxReplicas: 5
           outputs:
             - dapr:
                 name: kafka-server
                 operation: "create"
           bindings:
             kafka-server:
               type: bindings.kafka
               version: v1
               metadata:
                 - name: brokers
                   value: "kafka-server-kafka-brokers:9092"
                 - name: authRequired
                   value: "false"
                 - name: publishTopic
                   value: "sample-topic"
                 - name: topics
                   value: "sample-topic"
                 - name: consumerGroup
                   value: "function-front"
           template:
             containers:
               - name: function
                 imagePullPolicy: Always
    
  4. 在清单中,spec.serving.outputs 定义了一个指向 Kafka 服务器的 Dapr 组件的输出。这使您可以在 function-front 函数中向输出 target 发送自定义内容。

    func Sender(ctx ofctx.Context, in []byte) (ofctx.Out, error) {
      ...
     _, err := ctx.Send("target", greeting)
     ...
    }
    
  5. 运行以下命令创建函数 function-front

    kubectl apply -f function-front.yaml
    

检查结果

  1. 运行以下命令查看函数的状态。

    $ kubectl get functions.core.openfunction.io
    
    NAME             BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         URL                                             AGE
    function-front   Succeeded    Running        builder-bhbtk   serving-vc6jw   https://openfunction.io/default/function-front   2m41s
    kafka-input      Succeeded    Running        builder-dprfd   serving-75vrt                                                   2m21s
    
  2. 运行以下命令在集群中创建一个用于访问函数的 pod。

    kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
    
  3. 运行以下命令通过 URL 访问函数。

    [ root@curl:/ ]$ curl -d '{"message":"Awesome OpenFunction!"}' -H "Content-Type: application/json" -X POST http://openfunction.io.svc.cluster.local/default/function-front
    
  4. 运行以下命令查看 function-front 的日志。

    kubectl logs -f \
      $(kubectl get po -l \
      openfunction.io/serving=$(kubectl get functions function-front -o jsonpath='{.status.serving.resourceRef}') \
      -o jsonpath='{.items[0].metadata.name}') \
      function
    

    输出如下所示。

    dapr client initializing for: 127.0.0.1:50001
    I0125 06:51:55.584973       1 framework.go:107] Plugins for pre-hook stage:
    I0125 06:51:55.585044       1 framework.go:110] - plugin-custom
    I0125 06:51:55.585052       1 framework.go:110] - plugin-example
    I0125 06:51:55.585057       1 framework.go:115] Plugins for post-hook stage:
    I0125 06:51:55.585062       1 framework.go:118] - plugin-custom
    I0125 06:51:55.585067       1 framework.go:118] - plugin-example
    I0125 06:51:55.585179       1 knative.go:46] Knative Function serving http: listening on port 8080
    2022/01/25 06:52:02 http - Data: {"message":"Awesome OpenFunction!"}
    I0125 06:52:02.246450       1 plugin-example.go:83] the sum is: 2
    
  5. 运行以下命令查看 kafka-input 的日志。

    kubectl logs -f \
      $(kubectl get po -l \
      openfunction.io/serving=$(kubectl get functions kafka-input -o jsonpath='{.status.serving.resourceRef}') \
      -o jsonpath='{.items[0].metadata.name}') \
      function
    

    输出如下所示。

    dapr client initializing for: 127.0.0.1:50001
    I0125 06:35:28.332381       1 framework.go:107] Plugins for pre-hook stage:
    I0125 06:35:28.332863       1 framework.go:115] Plugins for post-hook stage:
    I0125 06:35:28.333749       1 async.go:39] Async Function serving grpc: listening on port 8080
    message from Kafka '{Awesome OpenFunction!}'
    

5.2 - 使用 SkyWalking 为 OpenFunction 提供可观测能力

本文介绍了如何使用 SkyWalking 为 OpenFunction 构建可观测性解决方案。

功能概览

尽管 FaaS 允许开发者专注于他们的业务代码而不用担心底层的实现,但对函数服务进行功能调试和故障排除是很困难的。因此,OpenFunction 设法引入了可观测性能力来提高其可用性和稳定性。

SkyWalking 提供了在许多不同场景下观测和监控分布式系统的解决方案。OpenFunction 将 go2sky(SkyWalking 的 Go 语言代理)捆绑在 OpenFunction 追踪器选项中,以提供分布式追踪、函数性能统计和函数依赖关系图。

先决条件

追踪功能的可配置参数

下表描述了 OpenFunction 中目前可用的追踪参数。

名称描述示例
enabled使能追踪功能,默认为 falsetrue, false
provider.name可以设置为 skywalkingopentelemetry(待定)skywalking
provider.oapServerSkyWalking OAP 服务器地址skywalking-opa:11800
tags一组键值对,用于追踪中的追踪所使用的 Span 自定义标签
tags.func函数的名称,该值将被自动填充function-a
tags.layer表示被追踪的服务类型,当你使用该函数时,它应该被设置为 faasfaas
baggage一个键值对的集合,存在于追踪中,也需要跨进程边界传输

下面是一个 JSON 格式的配置参考,您可以基于此了解追踪配置的大致数据格式。

{
  "enabled": true,
  "provider": {
    "name": "skywalking",
    "oapServer": "skywalking-oap:11800"
  },
  "tags": {
    "func": "function-a",
    "layer": "faas",
    "tag1": "value1",
    "tag2": "value2"
  },
  "baggage": {
    "key": "key1",
    "value": "value1"
  }
}

启用 OpenFunction 的追踪功能

选项一:全局配置

下文使用 skywalking-oap.default:11800 作为集群中 skywalking-oap 服务的样例地址。

  1. 运行下面的命令,修改 openfunction 命名空间中的 ConfigMap openfunction-config

    kubectl edit configmap openfunction-config -n openfunction
    
  2. 参照下面的例子修改 data.plugins.tracing 中的内容,并保存这个修改。

    data:
      plugins.tracing: |
        enabled: true
        provider:
          name: "skywalking"
          oapServer: "skywalking-oap:11800"
        tags:
          func: tracing-function
          layer: faas
          tag1: value1
          tag2: value2
        baggage:
          key: "key1"
          value: "value1"    
    

选项二:函数级别配置

要在函数级别启用跟踪配置,请在函数清单的 metadata.annotations 下添加 plugins.tracing 字段,如下所示。

metadata:
  name: tracing-function
  annotations:
    plugins.tracing: |
      enabled: true
      provider:
        name: "skywalking"
        oapServer: "skywalking-oap:11800"
      tags:
        func: tracing-function
        layer: faas
        tag1: value1
        tag2: value2
      baggage:
        key: "key1"
        value: "value1"      

建议您使用全局跟踪配置,否则您必须为您创建的每个函数逐一添加函数级跟踪配置。

使用 SkyWalking 作为分布式追踪解决方案

  1. 通过参考 本文档 创建函数。

  2. 然后,您可以在 SkyWalking UI 界面上观察整个函数调用的链路。

  3. 您还可以比较 Knative 运行时函数(function-front)在运行状态和冷启动时的响应时间。

    在冷启动时:

    在运行状态下:

5.3 - Elastic 日志告警

学习如何创建一个异步函数来查找错误日志。

本文档描述了如何创建一个异步函数来查找错误日志。

概述

本文档使用一个异步函数来分析 Kafka 中的日志流,以找出错误日志。异步函数将然后向 Slack 发送告警。以下图表说明了整个工作流程。

先决条件

创建 Kafka 服务器和主题

  1. 运行以下命令在默认命名空间中安装 strimzi-kafka-operator

    helm repo add strimzi https://strimzi.io/charts/
    helm install kafka-operator -n default strimzi/strimzi-kafka-operator
    
  2. 使用以下内容创建一个文件 kafka.yaml

    apiVersion: kafka.strimzi.io/v1beta2
    kind: Kafka
    metadata:
      name: kafka-logs-receiver
      namespace: default
    spec:
      kafka:
        version: 3.3.1
        replicas: 1
        listeners:
          - name: plain
            port: 9092
            type: internal
            tls: false
          - name: tls
            port: 9093
            type: internal
            tls: true
        config:
          offsets.topic.replication.factor: 1
          transaction.state.log.replication.factor: 1
          transaction.state.log.min.isr: 1
          default.replication.factor: 1
          min.insync.replicas: 1
          inter.broker.protocol.version: "3.1"
        storage:
          type: ephemeral
      zookeeper:
        replicas: 1
        storage:
          type: ephemeral
      entityOperator:
        topicOperator: {}
        userOperator: {}
    ---
    apiVersion: kafka.strimzi.io/v1beta2
    kind: KafkaTopic
    metadata:
      name: logs
      namespace: default
      labels:
        strimzi.io/cluster: kafka-logs-receiver
    spec:
      partitions: 10
      replicas: 1
      config:
        retention.ms: 7200000
        segment.bytes: 1073741824
    
  3. 运行以下命令在默认命名空间中部署一个名为 kafka-logs-receiver 的 1 副本 Kafka 服务器和一个名为 logs 的 1 副本 Kafka 主题。

    kubectl apply -f kafka.yaml
    
  4. 运行以下命令检查 pod 状态,等待 Kafka 和 Zookeeper 启动并运行。

    $ kubectl get po
    NAME                                                     READY   STATUS        RESTARTS   AGE
    kafka-logs-receiver-entity-operator-57dc457ccc-tlqqs     3/3     Running       0          8m42s
    kafka-logs-receiver-kafka-0                              1/1     Running       0          9m13s
    kafka-logs-receiver-zookeeper-0                          1/1     Running       0          9m46s
    strimzi-cluster-operator-687fdd6f77-cwmgm                1/1     Running       0          11m
    
  5. 运行以下命令查看 Kafka 集群的元数据。

    # 启动一个实用工具 pod。
    $ kubectl run utils --image=arunvelsriram/utils -i --tty --rm
    # 检查 Kafka 集群的元数据。
    $ kafkacat -L -b kafka-logs-receiver-kafka-brokers:9092
    

创建日志处理函数

  1. 使用以下示例 YAML 文件创建一个清单 logs-handler-function.yaml,并修改 spec.image 的值以设置您自己的镜像仓库地址。
   apiVersion: core.openfunction.io/v1beta2
   kind: Function
   metadata:
     name: logs-async-handler
     namespace: default
   spec:
     build:
       builder: openfunction/builder-go:latest
       env:
         FUNC_CLEAR_SOURCE: "true"
         FUNC_NAME: LogsHandler
       srcRepo:
         revision: main
         sourceSubPath: functions/async/logs-handler-function/
         url: https://github.com/OpenFunction/samples.git
     image: openfunctiondev/logs-async-handler:v1
     imageCredentials:
       name: push-secret
     serving:
       bindings:
         kafka-receiver:
           metadata:
             - name: brokers
               value: kafka-server-kafka-brokers:9092
             - name: authRequired
               value: "false"
             - name: publishTopic
               value: logs
             - name: topics
               value: logs
             - name: consumerGroup
               value: logs-handler
           type: bindings.kafka
           version: v1
         notification-manager:
           metadata:
             - name: url
               value: http://notification-manager-svc.kubesphere-monitoring-system.svc.cluster.local:19093/api/v2/alerts
           type: bindings.http
           version: v1
       outputs:
         - dapr:
             name: notification-manager
             operation: post
             type: bindings.http
       scaleOptions:
         keda:
           scaledObject:
             advanced:
               horizontalPodAutoscalerConfig:
                 behavior:
                   scaleDown:
                     policies:
                       - periodSeconds: 15
                         type: Percent
                         value: 50
                     stabilizationWindowSeconds: 45
                   scaleUp:
                     stabilizationWindowSeconds: 0
             cooldownPeriod: 60
             pollingInterval: 15
           triggers:
             - metadata:
                 bootstrapServers: kafka-server-kafka-brokers.default.svc.cluster.local:9092
                 consumerGroup: logs-handler
                 lagThreshold: "20"
                 topic: logs
               type: kafka
         maxReplicas: 10
         minReplicas: 0
       template:
         containers:
           - imagePullPolicy: IfNotPresent
             name: function
       triggers:
         dapr:
           - name: kafka-receiver
             type: bindings.kafka
       workloadType: Deployment
     version: v2.0.0
     workloadRuntime: OCIContainer
  1. 运行以下命令创建函数 logs-async-handler

    kubectl apply -f logs-handler-function.yaml
    
  2. 日志处理函数将由 Kafka 中 logs 主题的消息触发。

6 - 参考资料

参阅 OpenFunction 中资源的详细使用说明

6.1 - 常见问题解答

本文档描述在使用OpenFunction时的常见问题解答。

Q: 如何在OpenFunction中使用私有镜像仓库?

A: OpenFunction在构建阶段使用Shipwright(利用Tekton与Cloud Native Buildpacks集成)将用户函数打包到应用镜像中。

用户通常选择以不安全的方式访问私有镜像仓库,这在Cloud Native Buildpacks中尚不受支持。

我们提供以下解决方法,暂时绕过这个限制:

  1. 使用IP地址而不是主机名作为私有镜像仓库的访问地址。
  2. 运行Knative-runtime函数时,应该跳过标签解析

参考链接:

buildpacks/lifecycle#524

buildpacks/tekton-integration#31

Q: 如何在不引入新的入口控制器的情况下访问Knative-runtime函数?

A: OpenFunction提供了函数可访问性的统一入口点,它基于Ingress Nginx实现。然而,对于一些用户来说,这是不必要的,引入新的入口控制器可能会影响当前集群。

一般来说,可访问的地址用于同步(Knative-runtime)函数。以下是解决此问题的两种方法:

  • Magic DNS

    您可以按照此指南配置DNS。

  • CoreDNS

    这类似于使用Magic DNS,不同之处在于DNS解析的配置放置在CoreDNS内部。假设用户已在knative-serving命名空间的config-domain ConfigMap下配置了一个名为"openfunction.dev"的域(如下所示):

    $ 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
    

接下来,让我们为此域添加一个A记录。OpenFunction使用Kourier作为Knative Serving的默认网络层,该域名应该流向到Kourier。

$ 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

然后,用户只需在CoreDNS中配置此通配符DNS解析,以解析集群中任何Knative服务的URL地址。

其中"10.233.47.71"是Service kourier-internal的地址。

$ 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
    }
    ...

如果用户在集群外无法解析此函数的URL地址,请配置hosts文件如下:

其中"serving-sr5v2-ksvc-sbtgr.default.openfunction.dev"是从命令"kubectl get ksvc"中获取的URL地址。

10.233.47.71 serving-sr5v2-ksvc-sbtgr.default.openfunction.dev

配置完成后,可以使用以下命令获取函数的URL地址。然后,您可以使用curl或浏览器触发该函数。

$ kubectl get ksvc

NAME                       URL
serving-sr5v

2-ksvc-sbtgr   http://serving-sr5v2-ksvc-sbtgr.default.openfunction.dev

Q: 如何为函数启用和配置并发性?

A: OpenFunction将函数类型分为"同步运行时“和”异步运行时",基于正在处理的请求类型。这两种类型的函数由Knative Serving和Dapr + KEDA驱动。

因此,要启用和配置函数的并发性,您需要参考上述组件中的具体实现。

以下部分介绍了如何根据"同步运行时“和”异步运行时“部分在OpenFunction中启用和配置函数的并发性。

同步运行时

您可以首先参考Knative Serving中的此文档,了解如何启用和配置并发性功能。

软限制

您可以参考此文档中的Global(ConfigMap)Global(Operator)部分,配置全局并发性功能。

而对于Per Revision,您可以像这样进行配置此处

apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    scaleOptions:
      knative:
        autoscaling.knative.dev/target: "200"

硬限制

OpenFunction目前不支持为Per Revision配置硬限制。您可以参考此文档中的Global(ConfigMap)Global(Operator)部分,配置全局并发性功能。

简而言之

简而言之,您可以为每个函数配置Knative Serving的与自动缩放相关的配置项,如下所示,只要它们可以作为注释传递,否则只能进行全局设置。

# 在Knative Serving中的配置
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: helloworld-go
  namespace: default
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/<key>: "value"

# 在OpenFunction中的配置(推荐)
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    scaleOptions:
      knative:
          <key>: "value"

# 替代方法
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    annotations:
      autoscaling.knative.dev/<key>: "value"

异步运行时

您可以首先参考Dapr中的此文档,了解如何启用和配置并发性功能。

与同步运行时的并发性配置相比,异步运行时的并发性配置更简单。

# 在Dapr中的配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodesubscriber
  namespace: default
spec:
  template:
    metadata:
      annotations:
        dapr.io/app-max-concurrency: "value"

# 在OpenFunction中的配置(推荐)
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  serving:
    annotations:
      dapr.io/app-max-concurrency: "value"

Q: 如何为函数镜像构建过程创建源代码仓库凭据?

A: 在使用spec.build.srcRepo.credentials时,您可能会遇到类似Unsupported type of credentials provided, either SSH private key or username/password is supported (exit code 110)的错误,这意味着您正在使用不正确的Secret资源作为源代码仓库凭据。

OpenFunction目前基于ShipWright实现函数镜像构建框架,因此我们需要参考此文档来设置正确的源代码仓库凭据。

Q: 如何在离线环境中安装OpenFunction?

A: 您可以通过以下步骤在离线环境中安装和使用OpenFunction:

拉取Helm Chart

在可以访问GitHub的环境中拉取Helm Chart:

helm repo add openfunction https://openfunction.github.io/charts/
helm repo update
helm pull openfunction/openfunction

然后使用诸如scp之类的工具将Helm包复制到离线环境,例如:

scp openfunction-v1.0.0-v0.5.0.tgz <username>@<your-machine-ip>:/home/<username>/

同步镜像

您需要将这些镜像同步到您的私有镜像仓库:

# dapr
docker.io/daprio/dashboard:0.10.0
docker.io/daprio/dapr:1.8.3

# keda
openfunction/keda:2.8.1
openfunction/keda-metrics-apiserver:2.8.1

# contour
docker.io/bitnami/contour:1.21.1-debian-11-r5
docker.io/bitnami/envoy:1.22.2-debian-11-r6
docker.io/bitnami/nginx:1.21.6-debian-11-r10

# tekton-pipelines
openfunction/tektoncd-pipeline-cmd-controller:v0.37.2
openfunction/tektoncd-pipeline-cmd-kubeconfigwriter:v0.37.2
openfunction/tektoncd-pipeline-cmd-git-init:v0.37.2
openfunction/tektoncd-pipeline-cmd-entrypoint:v0.37.2
openfunction/tektoncd-pipeline-cmd-nop:v0.37.2
openfunction/tektoncd-pipeline-cmd-imagedigestexporter:v0.37.2
openfunction/tektoncd-pipeline-cmd-pullrequest-init:v0.37.2
openfunction/tektoncd-pipeline-cmd-pullrequest-init:v0.37.2
openfunction/tektoncd-pipeline-cmd-workingdirinit:v0.37.2
openfunction/cloudsdktool-cloud-sdk@sha256:27b2c22bf259d9bc1a291e99c63791ba0c27a04d2db0a43241ba0f1f20f4067f
openfunction/distroless-base@sha256:b16b57be9160a122ef048333c68ba205ae4fe1a7b7cc6a5b289956292ebf45cc
openfunction/tektoncd-pipeline-cmd-webhook:v0.37.2

# knative-serving
openfunction/knative.dev-serving-cmd-activator:v1.3.2
openfunction/knative.dev-serving-cmd-autoscaler:v1.3.2
openfunction/knative.dev-serving-cmd-queue:v1.3.2
openfunction/knative.dev-serving-cmd-controller:v1.3.2
openfunction/knative.dev-serving-cmd-domain-mapping:v1.3.2
openfunction/knative.dev-serving-cmd-domain-mapping-webhook:v1.3.2
openfunction/knative.dev-net-contour-cmd-controller:v1.3.0
openfunction/knative.dev-serving-cmd-default-domain:v1.3.2
openfunction/knative.dev-serving-cmd-webhook:v1.3.2

# shipwright-build
openfunction/shipwright-shipwright-build-controller:v0.10.0
openfunction/shipwright-io-build-git:v0.10.0
openfunction/shipwright-mutate-image:v0.10.0
openfunction/shipwright-bundle:v0.10.0
openfunction/shipwright-waiter:v0.10.0
openfunction/buildah:v1.23.3
openfunction/buildah:v1.28.0

# openfunction
openfunction/openfunction:v1.0.0
openfunction/kube-rbac-proxy:v0.8.0
openfunction/eventsource-handler:v4
openfunction/trigger-handler:v4
openfunction/dapr-proxy:v0.1.1
openfunction/revision-controller:v1.0.0

创建自定义值

在您的离线环境中创建 custom-values.yaml 文件:

touch custom-values.yaml

编辑 custom-values.yaml,添加以下内容:

knative-serving:
  activator:
    activator:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-activator
  autoscaler:
    autoscaler:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-autoscaler
  configDeployment:
    queueSidecarImage:
      repository: <您的私有镜像仓库>/knative.dev-serving-cmd-queue
  controller:
    controller:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-controller
  domainMapping:
    domainMapping:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-domain-mapping
  domainmappingWebhook:
    domainmappingWebhook:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-domain-mapping-webhook
  netContourController:
    controller:
      image:
        repository: <您的私有镜像仓库>/knative.dev-net-contour-cmd-controller
  defaultDomain:
    job:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-default-domain
  webhook:
    webhook:
      image:
        repository: <您的私有镜像仓库>/knative.dev-serving-cmd-webhook
shipwright-build:
  shipwrightBuildController:
    shipwrightBuild:
      image:
        repository: <您的私有镜像仓库>/shipwright-shipwright-build-controller
      GIT_CONTAINER_IMAGE:
        repository: <您的私有镜像仓库>/shipwright-io-build-git
      MUTATE_IMAGE_CONTAINER_IMAGE:
        repository: <您的私有镜像仓库>/shipwright-mutate-image
      BUNDLE_CONTAINER_IMAGE:
        repository: <您的私有镜像仓库>/shipwright-bundle
      WAITER_CONTAINER_IMAGE:
        repository: <您的私有镜像仓库>/shipwright-waiter
tekton-pipelines:
  controller:
    tektonPipelinesController:
      image:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-controller
      kubeconfigWriterImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-kubeconfigwriter
      gitImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-git-init
      entrypointImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-entrypoint
      nopImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-nop
      imagedigestExporterImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-imagedigestexporter
      prImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-pullrequest-init
      workingdirinitImage:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-workingdirinit
      gsutilImage:
        repository: <您的私有镜像仓库>/cloudsdktool-cloud-sdk
        digest: sha256:27b2c22bf259d9bc1a291e99c63791ba0c27a04d2db0a43241ba0f1f20f4067f
      shellImage:
        repository: <您的私有镜像仓库>/distroless-base
        digest: sha256:b16b57be9160a122ef048333c68ba205ae4fe1a7b7cc6a5b289956292ebf45cc
  webhook:
    webhook:
      image:
        repository: <您的私有镜像仓库>/tektoncd-pipeline-cmd-webhook
keda:
  image:
    keda:
      repository: <您的私有镜像仓库>/keda
      tag: 2.8.1
    metricsApiServer:
      repository: <您的私有镜像仓库>/keda-metrics-apiserver
      tag: 2.8.1
dapr:
  global:
    registry: <您的私有镜像仓库>/daprio
    tag: '1.8.3'
contour:
  contour:
    image:
      registry: <您的私有镜像仓库>
      repository: <您的私有镜像仓库>/contour
      tag: 1.21.1-debian-11-r5
  envoy:
    image:
      registry: <您的私有镜像仓库>
      repository: <您的私有镜像仓库>/envoy
      tag: 1.22.2-debian-11-r6
  defaultBackend:
    image:
      registry: <您的私有镜像仓库>
      repository: <您的私有镜像仓库>/nginx
      tag: 1.21.6-debian-11-r10

请将 <您的私有镜像仓库> 替换为您的私有镜像仓库的地址。

安装 OpenFunction

在离线环境中运行以下命令尝试安装 OpenFunction:

kubectl create namespace openfunction
helm install openfunction openfunction-v1.0.0-v0.5.0.tgz -n openfunction -f custom-values.yaml

补丁 ClusterBuildStrategy

如果您想在离线环境中构建 wasm 函数或使用 buildah 构建函数,运行以下命令补丁 ClusterBuildStrategy

kubectl patch clusterbuildstrategy buildah --type='json' -p='[{"op": "replace", "path": "/spec/buildSteps/0/image", "value":"openfunction/buildah:v1.28.0"}]'
kubectl patch clusterbuildstrategy wasmedge --type='json' -p='[{"op": "replace", "path": "/spec/buildSteps/0/image", "value":"openfunction/buildah:v1.28.0"}]'

Q: 如何在离线环境中构建和运行函数

A: 以 Java 函数 为例,说明如何在离线环境中构建和运行函数:

  • https://github.com/OpenFunction/samples.git 同步到您的私有代码仓库

  • 按照此 prerequisites 文档创建 push-secretgit-repo-secret

  • 将公共 maven 仓库更改为私有 maven 仓库:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>dev.openfunction.samples</groupId>
        <artifactId>samples</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
        </properties>
    
        <repositories>
            <repository>
                <id>snapshots</id>
                <name>Maven snapshots</name>
                    <!--<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>-->
                    <url>your private maven repository</url>
                <releases>
                    <enabled>false</enabled>
                </releases>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
        </repositories>
    
        <dependencies>
            <dependency>
                <groupId>dev.openfunction.functions</groupId>
                <artifactId>functions-framework-api</artifactId>
                <version>1.0.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    
    </project>
    

    确保将更改提交到代码仓库。

  • openfunction/buildpacks-java18-run:v1 同步到您的私有镜像仓库

  • 根据您的环境修改 functions/knative/java/hello-world/function-sample.yaml

    apiVersion: core.openfunction.io/v1beta2
    kind: Function
    metadata:
      name: function-http-java
    spec:
      version: "v2.0.0"
      image: "<your private image repository>/sample-java-func:v1"
      imageCredentials:
        name: push-secret
      build:
        builder: <your private image repository>/builder-java:v2-18
        params:
          RUN_IMAGE: "<your private image repository>/buildpacks-java18-run:v1"
        env:
          FUNC_NAME: "dev.openfunction.samples.HttpFunctionImpl"
          FUNC_CLEAR_SOURCE: "true"
        srcRepo:
          url: "https://<your private code repository>/OpenFunction/samples.git"
          sourceSubPath: "functions/knative/java"
          revision: "main"
          credentials:
           name: git-repo-secret
      serving:
        template:
          containers:
            - name: function # DO NOT change this
              imagePullPolicy: IfNotPresent
        triggers:
          http:
            port: 8080
    

    如果您的私有镜像仓库是不安全的,请参考 以不安全的方式使用私有镜像仓库

  • 运行以下命令构建和运行函数:

    kubectl apply -f functions/knative/java/hello-world/function-sample.yaml
    

7 - 贡献指南

7.1 - 概述

本文档提供了如何通过问题和拉取请求向 OpenFunction 做出贡献的指南。贡献也可以通过其他方式进行,例如在社区电话会议中与社区进行互动,在问题或拉取请求上发表评论等。

请参阅 OpenFunction 社区仓库 以获取有关社区参与和社区成员资格的更多信息。

问题

问题类型

在大多数 OpenFunction 仓库中,通常有 4 种类型的问题:

  • 问题/错误报告:您发现了一个错误,并希望报告并跟踪它。
  • 问题/功能请求:您希望使用一个功能,但它尚未得到支持。
  • 问题/提案:用于提出新的想法或功能的项目。这允许在编写代码之前从他人那里获得反馈。

提交前的检查

在您提交问题之前,请确保您已经检查了以下内容:

  1. 这是正确的仓库吗?
    • OpenFunction 项目分布在多个仓库中。如果您不确定哪个仓库是正确的,请检查 仓库列表
  2. 检查现有的问题
    • 在您创建新问题之前,请在 开放问题 中搜索,看看问题或功能请求是否已经被提交。
    • 如果您发现您的问题已经存在,请发表相关评论并添加您的 反应。使用反应:
      • 👍 赞成
      • 👎 反对

拉取请求

所有的贡献都通过拉取请求来进行。要提交建议的更改,请遵循以下工作流程:

  1. 确保已经提出了问题,这为您即将做出的贡献设定了期望。
  2. 分叉相关的仓库并创建一个新的分支
  3. 创建您的更改
    • 代码更改需要测试
  4. 更新相关的文档以进行更改
  5. 带有 DCO 签名 的提交并打开一个 PR
  6. 等待 CI 过程完成并确保所有检查都是绿色的
  7. 您可以在几天内期待一次审查

使用工作进度中的 PR 获取早期反馈

在投入太多时间之前,一种进行沟通的好方法是创建一个 “工作进度中” 的 PR 并与您的审查者分享。做到这一点的标准方式是在您的 PR 的标题中添加 “[WIP]” 前缀并分配 do-not-merge 标签。这将让查看您的 PR 的人知道它还没有准备好。

开发者原创证书:签署您的工作

每个提交都需要签署

开发者原创证书 (DCO) 是一种轻量级的方式,供贡献者证明他们编写或者以其他方式有权提交他们正在向项目贡献的代码。这里是 DCO 的全文。

贡献者通过在提交消息中添加 Signed-off-by 行来签署他们遵守这些要求。

这是我的提交消息
Signed-off-by: 随机 J 开发者 <random@developer.example.org>

Git 有一个 -s 命令行选项,可以自动将此内容添加到您的提交消息中:

git commit -s -m '这是我的提交消息'

每个拉取请求都会检查拉取请求中的提交是否包含有效的 Signed-off-by 行。

我没有签署我的提交,现在该怎么办?!

别担心 - 您可以轻松地重播您的更改,签署它们并强制推送它们!

git checkout <branch-name>
git commit --amend --no-edit --signoff
git push --force-with-lease <remote-name> <branch-name>

开发指南

这里 您可以找到一个开发指南,它将指导您如何开始在您的本地环境中构建 OpenFunction。

行为准则

请参阅 OpenFunction 社区行为准则

7.2 - 路线图

OpenFunction 鼓励社区帮助确定优先级。OpenFunction 的路线图的 GitHub 项目 可供社区对提议的问题提供反馈并跟踪它们的开发。

请通过在 GitHub 问题上添加 👍 来投票,选择您最希望 OpenFunction 支持的功能。这将帮助 OpenFunction 的维护者更好地理解您的需求。

我们始终欢迎社区的贡献。如果路线图上有您感兴趣的功能,并且您想要对其进行贡献,请在 GitHub 问题上发表评论,并包含您的解决方案提议。