欢迎来到 OpenFunction 的文档站点!
v1.2 (latest)
- 1: 简介
- 2: 快速入门
- 3: 概念术语
- 3.1: 函数定义
- 3.2: 函数构建
- 3.3: 构建策略
- 3.4: 函数触发器
- 3.5: 函数输出
- 3.6: 函数缩放
- 3.7: 函数签名
- 3.8: Wasm 函数
- 3.9: 无服务器应用
- 3.10: BaaS 集成
- 3.11: 网络
- 3.12: CI/CD
- 3.13: OpenFunction 事件
- 3.13.1: 简介
- 3.13.2: 使用 EventSource
- 3.13.3: 使用 EventBus 和 Trigger
- 3.13.4: 在一个 EventSource 中使用多个源
- 3.13.5: 使用 ClusterEventBus
- 3.13.6: 使用带有条件的 Trigger
- 4: 操作手册
- 4.1: 网络
- 4.1.1: 切换到其他 Kubernetes Gateway 实现
- 4.1.2: 配置本地域名
- 5: 最佳实践
- 6: 参考资料
- 6.1: 常见问题解答
- 7: 贡献指南
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 Version | Kubernetes 1.21 | Kubernetes 1.22 | Kubernetes 1.23 | Kubernetes 1.24 | Kubernetes 1.25 | Kubernetes 1.26+ |
---|---|---|---|---|---|---|
HEAD | N/A | N/A | √ | √ | √ | √ |
v1.2 | N/A | N/A | √ | √ | √ | √ |
v1.1.x | √ | √ | √ | √ | √ | N/A |
v1.0.x | √ | √ | √ | √ | √ | N/A |
安装 OpenFunction
现在您可以使用 helm charts 安装 OpenFunction 及其所有依赖项。
ofn
CLI 安装方法已经弃用。
如果您想在离线环境中安装 OpenFunction,请参考 在离线环境中安装 OpenFunction
环境要求
- Kubernetes version:
>=v1.21.0-0
- Helm version:
>=v3.6.3
安装 OpenFunction helm charts 的步骤
首先运行以下命令添加 OpenFunction chart 仓库:
helm repo add openfunction https://openfunction.github.io/charts/ helm repo update
然后您有几个选项来设置 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
Note
关于如何使用 Helm 安装 OpenFunction 的更多信息,请参见 使用 Helm 安装 OpenFunction.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
Note
关于如何使用 Helm 卸载 OpenFunction 的更多信息,请参见 使用 Helm 卸载 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
Note
有关如何使用 Helm 升级 OpenFunction 的更多信息,请参见 使用 Helm 升级 OpenFunction.2.2 - 开始使用
2.2.1 - 先决条件
镜像仓库凭证
在构建函数时,您需要将函数容器镜像推送到像Docker Hub或Quay.io这样的容器镜像仓库。为此,您首先需要为您的容器镜像仓库生成一个密钥。
您可以通过填写REGISTRY_SERVER
,REGISTRY_USER
和REGISTRY_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集群的步骤,用于演示目的。
在默认命名空间中安装strimzi-kafka-operator。
helm repo add strimzi https://strimzi.io/charts/ helm install kafka-operator -n default strimzi/strimzi-kafka-operator
运行以下命令在默认命名空间中创建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
运行以下命令检查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
首先,编辑配置/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默认使用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请求的有效载荷的函数,输出或响应在函数逻辑处理输入有效载荷后立即发送给等待的客户端。以下是不同语言的一些同步函数示例:
同步函数 | |
---|---|
Go | Hello World, 多函数, 带路径参数的同步函数, 日志处理, 带输出绑定的同步函数 |
Nodejs | Hello World, 带输出绑定的同步函数 |
Python | Hello World |
Java | Hello World, 带输出的同步函数 |
DotNet | Hello World |
您可以在这里找到更多函数示例
2.2.3 - 创建异步函数
在创建任何函数之前,请确保您已安装所有先决条件
异步函数是事件驱动的,它们的输入通常是来自非HTTP事件源的事件,如消息队列、cron触发器、MQTT代理等,通常客户端在通过传递事件触发异步函数后不会等待立即的响应。以下是不同语言的一些异步函数示例:
异步函数 | |
---|---|
Go | Kafka输入和HTTP输出绑定, Cron输入和Kafka输出绑定, Cron输入绑定, Kafka输入绑定, Kafka pubsub |
Nodejs | MQTT绑定和pubsub |
Python | |
Java | Cron输入和Kafka输出绑定, Kafka pubsub |
DotNet |
您可以在这里找到更多函数示例
2.2.4 - 创建无服务器应用
在创建任何函数之前,请确保您已安装所有的先决条件
除了构建和运行无服务器函数,您还可以使用OpenFunction构建和运行无服务器应用。
这里有一些无服务器应用的示例:
无服务器应用 | |
---|---|
Go | 带有Dockerfile的Go应用 |
Java | 带有Dockerfile的Java应用, 不带Dockerfile的Java应用 & 源代码 |
您可以在这里找到关于这些无服务器应用的更多信息
2.2.5 - 创建Wasm函数
在创建任何函数之前,请确保您已安装所有的先决条件
这里有一些Wasm函数的示例:
语言 | Wasm函数 | 运行时 |
---|---|---|
Rust | wasmedge-http-server | wasmedge |
您可以在这里找到关于这些函数的更多信息
3 - 概念术语
3.1 - 函数定义
函数
Function
是Build
和Serving
的控制平面,也是用户使用OpenFunction的接口。用户无需单独创建Build
或Serving
,因为Function
是定义函数的Build
和Serving
的唯一地方。
一旦创建了函数,它将控制Build
和Serving
的生命周期,无需用户干预:
如果在函数中定义了
Build
,则一旦部署函数,将创建一个builder自定义资源来构建函数的容器镜像。如果在函数中定义了
Serving
,则将创建一个serving自定义资源来控制函数的服务和自动缩放。Build
和Serving
可以一起定义,这意味着首先将构建函数镜像,然后将其用于服务。可以定义
Build
而不定义Serving
,在这种情况下,函数仅用于构建镜像。可以定义
Serving
而不定义Build
,函数将使用先前构建的函数镜像进行服务。
构建
OpenFunction使用Shipwright和Cloud Native Buildpacks将函数源代码构建成容器镜像。
一旦创建了包含Build
规范的函数,就会创建一个builder
自定义资源,该资源将使用Shipwright管理构建工具和策略。然后,Shipwright将使用Tekton控制构建容器镜像的过程,包括获取源代码、生成镜像工件和发布镜像。
服务
一旦创建了包含Serving
规范的函数,就会创建一个Serving
自定义资源来控制函数的服务阶段。目前,OpenFunction Serving支持两种运行时:Knative同步运行时和OpenFunction异步运行时。
同步运行时
对于同步函数,OpenFunction目前支持使用 Knative Serving 和 KEDA http-addon 作为运行时。
异步运行时
OpenFunction的异步运行时是一个基于KEDA和Dapr实现的事件驱动运行时。异步函数可以由各种事件类型触发,如消息队列、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社区维护的流行语言的构建器:
构建器 | |
---|---|
Go | openfunction/builder-go:v2.4.0 (openfunction/builder-go:latest) |
Nodejs | openfunction/builder-node:v2-16.15 (openfunction/builder-node:latest) |
Java | openfunction/builder-java:v2-11, openfunction/builder-java:v2-16, openfunction/builder-java:v2-17, openfunction/builder-java:v2-18 |
Python | openfunction/gcp-builder:v1 |
DotNet | openfunction/gcp-builder:v1 |
3.3 - 构建策略
构建策略用于控制构建过程。有两种类型的策略,ClusterBuildStrategy
和 BuildStrategy
。
这两种策略都定义了一组步骤,这些步骤是控制应用程序构建过程所必需的。
ClusterBuildStrategy
是集群范围的,而 BuildStrategy
是命名空间范围的。
在 OpenFunction 中有 4 种内置的 ClusterBuildStrategy
,你可以在以下各节中找到更多详细信息。
openfunction
openfunction
ClusterBuildStrategy 使用 Buildpacks 来构建函数镜像,这是默认的构建策略。
以下是 openfunction
ClusterBuildStrategy 的参数:
名称 | 类型 | 描述 |
---|---|---|
RUN_IMAGE | string | 使用的运行镜像的引用 |
CACHE_IMAGE | string | 缓存镜像是一种在不同构建之间保留缓存层的方式,当构建具有大量依赖项的函数或应用程序(如 Java 函数)时,可以提高构建性能。 |
BASH_IMAGE | string | 策略使用的 bash 镜像。 |
ENV_VARS | string | 在 构建时 设置的环境变量。格式为 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-search | string | 用于搜索短名称镜像(如 golang:latest )的镜像仓库,用逗号分隔。 | docker.io,quay.io |
registry-insecure | string | 不安全镜像仓库的全名。不安全的镜像仓库是没有有效 SSL 证书或只支持 HTTP 的镜像仓库。 | |
registry-block | string | 需要阻止拉取访问的镜像仓库。 | "" |
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-flags | string | Value for the GOFLAGS environment variable. | "" |
ko-version | string | Version of ko, must be either ’latest’, or a release name from https://github.com/google/ko/releases. | "" |
package-directory | string | The 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 bindings
或 Dapr 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 Spec 和 KEDA 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中有三种函数签名:HTTP
,CloudEvent
和OpenFunction
。让我们使用Go函数为例,详细解释一下。
HTTP
和CloudEvent
签名可以用来创建同步函数,而OpenFunction
签名可以用来创建同步和异步函数。
此外,OpenFunction
签名可以利用各种Dapr构建块,包括Bindings,Pub/sub等访问各种BaaS服务,帮助创建更强大的函数。(Dapr状态管理,配置将很快得到支持)
HTTP | CloudEvent | OpenFunction | |
---|---|---|---|
签名 | func(http.ResponseWriter, *http.Request) error | func(context.Context, cloudevents.Event) error | func(ofctx.Context, []byte) (ofctx.Out, error) |
同步函数 | 支持 | 支持 | 支持 |
异步函数 | 不支持 | 不支持 | 支持 |
Dapr Binding | 不支持 | 不支持 | 支持 |
Dapr Pub/sub | 不支持 | 不支持 | 支持 |
示例
如您所见,OpenFunction
签名是推荐的函数签名,我们正在努力在更多语言运行时支持此函数签名。
HTTP | CloudEvent | OpenFunction | |
---|---|---|---|
Go | Hello World, 多函数, 带路径参数的同步函数, 日志处理 | 带路径参数的同步函数 | 带路径参数的同步函数, 带输出绑定的同步函数, Kafka输入 & HTTP输出绑定, Cron输入 & Kafka输出绑定, Cron输入绑定, Kafka输入绑定, Kafka pubsub |
Nodejs | Hello World | 带输出绑定的同步函数, MQTT绑定 & pubsub | |
Python | Hello World | ||
Java | Hello World | CloudEvent | 带输出的同步函数, Cron输入 & Kafka输出绑定, Kafka pubsub |
DotNet | Hello 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-smart
,function.spec.build.shipwright.strategy
将根据名为 wasmedge
的 ClusterBuildStrategy
自动生成,以便构建带有 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/variant
,module.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
工作负载运行时并将镜像推送到容器镜像仓库, 请参考先决条件部分以获取更多信息。
你可以在这里找到关于这个示例函数的更多信息。
- 创建一个 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
- 检查 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
- 访问 wasm 函数
一旦 BUILDSTATE
变为 Succeeded
,SERVINGSTATE
变为 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 支持两种不同的方式将源代码构建成容器镜像:
- 使用 Cloud Native Buildpacks 在没有
Dockerfile
的情况下构建源代码 - 使用 Buildah 或 BuildKit 在有
Dockerfile
的情况下构建源代码
要将镜像推送到容器镜像仓库,你需要创建一个包含镜像仓库凭据的 secret,并将 secret 添加到
imageCredentials
。 请参考 先决条件 部分获取更多信息。
使用 Dockerfile 构建和运行无服务器应用
如果你已经为你的应用创建了一个 Dockerfile
,比如这个 Go 应用,你可以像 这样 以无服务器的方式构建和运行这个应用:
- 创建示例 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
- 检查应用状态 你可以通过 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
- 访问这个应用
一旦
BUILDSTATE
变成Succeeded
,SERVINGSTATE
变成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 应用 那样以无服务器的方式构建和运行你的应用:
- 创建示例 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
- 检查应用状态 你可以通过 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
- 访问这个应用
一旦
BUILDSTATE
变成Succeeded
,SERVINGSTATE
变成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/sub 和 bindings 构建块,未来将会添加更多。
在 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
可以设置为true
或false
openfunction.io/dapr-service-mode
可以设置为standalone
或sidecar
- 当
openfunction.io/enable-dapr
设置为true
时,用户可以通过将openfunction.io/dapr-service-mode
设置为standalone
或sidecar
来选择Dapr 服务模式
。 - 当
openfunction.io/enable-dapr
设置为false
时,openfunction.io/dapr-service-mode
的值将被忽略,既不会启动Dapr Sidecar
,也不会启动Dapr 代理服务
。
如果这两个标志没有设置,它们都有默认值。
- 如果
openfunction.io/enable-dapr
在spec.serving.annotations
中没有定义,并且函数定义包含spec.serving.inputs
或spec.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
的默认监听器。根据
domain
或clusterDomain
生成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
?
Route
是 Function
定义的一部分。Route
定义了来自 Gateway
监听器的流量如何路由到函数。
Route
在 GatewayRef
中指定了它将附加到的 Gateway
,使其能够从 Gateway
接收流量。
一旦创建了同步 Function
,函数控制器将:
- 查找
openfunction
命名空间中名为openfunction
的Gateway
,然后如果函数中没有定义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
注意
Funtion.status
中类型为 Internal
的地址提供了从集群内部访问函数的默认方法。
这个内部地址不受 route.gatewayRef
引用的 Gateway
的影响,它适合用作 EventSource
的 sink.url
。
Funtion.status
中类型为 External
的地址提供了从集群外部访问函数的方法(你可以选择配置 Magic DNS 或真实 DNS,请参考 通过外部地址访问函数 获取更多详情)。
这个外部地址是基于 route.gatewayRef
、router.hostnames
和 route.rules
生成的。路由模式只对这个外部地址生效,下面的文档将解释它是如何工作的。
有关如何访问函数的更多信息,请参考 函数入口点。
基于主机的路由
基于主机
是默认的路由模式。当 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/
基于主机和路径的路由
你可以同时定义主机名和路径,以自定义流量应如何路由到你的函数。
注意
在这种模式下,你需要自己解决 HTTPRoutes 之间可能存在的冲突。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}')
这个地址提供了在集群内部访问函数的默认方法,适合用作 EventSource
的 sink.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 | 是否启用修订控制器来检测此函数的源代码或镜像更改,可以设置为 enable 或 disable 。 |
openfunction.io/revision-controller-params | 修订控制器的参数。 |
参数
名称 | 描述 |
---|---|
type | 要检测的更改类型,包括 source 、source-image 和 image 。 |
polling-interval | 轮询镜像摘要或源代码头的间隔。 |
repo-type | git 仓库的类型,包括 github 、gitlab 和 gitee 。默认为 github 。 |
base-url | gitlab 服务器的基本 url。 |
auth-type | gitlab 服务器的认证类型。 |
project-id | gitlab 仓库的项目 id。 |
insecure-registry | 如果镜像仓库不安全,你应该将此设置为 true。 |
3.13 - OpenFunction 事件
3.13.1 - 简介
概述
OpenFunction 事件是 OpenFunction 的事件管理框架。它提供以下核心特性:
- 支持通过同步和异步调用触发目标函数
- 用户定义的触发判断逻辑
- OpenFunction 事件的组件可以由 OpenFunction 本身驱动
架构
以下图示说明了 OpenFunction 事件的架构。
概念
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 集群
运行以下命令在默认命名空间中安装 strimzi-kafka-operator。
helm repo add strimzi https://strimzi.io/charts/ helm install kafka-operator -n default strimzi/strimzi-kafka-operator
使用以下内容创建一个文件
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
运行以下命令在默认命名空间中部署一个名为
kafka-server
的 1-replica Kafka 服务器和一个名为events-sample
的 1-replica Kafka 主题。此命令创建的 Kafka 和 Zookeeper 集群的存储类型为 ephemeral,并使用 emptyDir 进行演示。kubectl apply -f kafka.yaml
运行以下命令检查 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
触发同步函数
创建 EventSource
使用以下内容创建一个 EventSource 配置文件(例如,
eventsource-sink.yaml
)。注意
- 以下示例定义了一个名为
my-eventsource
的事件源,并将指定 Kafka 服务器生成的事件标记为sample-one
事件。 spec.sink
引用了在先决条件中创建的目标函数(Knative 服务)。
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"
- 以下示例定义了一个名为
运行以下命令应用配置文件。
kubectl apply -f eventsource-sink.yaml
运行以下命令检查结果。
$ 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
注意
在此示例中触发同步函数,EventSource 控制器的工作流程描述如下:
- 创建一个名为
my-eventsource
的 EventSource 自定义资源。 - 创建一个名为
serving-xxxxx-component-esc-kafka-sample-one-xxxxx
的 Dapr 组件,使 EventSource 能够与事件源关联。 - 创建一个名为
serving-xxxxx-component-ts-my-eventsource-default-xxxxx
的 Dapr 组件,使 EventSource 能够与 sink 函数关联。 - 创建一个名为
serving-xxxxx-deployment-v100-xxxxx-xxxxxxxxxx-xxxxx
的 Deployment,用于处理事件。
- 创建一个名为
创建事件生产者
要启动目标函数,需要创建一些事件来触发函数。
使用以下内容创建一个事件生产者配置文件(例如,
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"
运行以下命令应用配置文件。
kubectl apply -f events-producer.yaml
运行以下命令实时检查结果。
$ 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 运行时函数
使用以下内容创建目标函数的配置文件(例如,
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"
运行以下命令应用配置文件。
kubectl apply -f openfuncasync-function.yaml
创建 EventBus 和 EventSource
使用以下内容创建 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"
使用以下内容创建 EventSource 的配置文件(例如,
eventsource.yaml
)。注意
通过spec.eventBus
设置事件总线的名称。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
运行以下命令应用这些配置文件。
kubectl apply -f eventbus.yaml kubectl apply -f eventsource.yaml
运行以下命令检查结果。
$ 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
注意
在使用事件总线的情况下,EventSource 控制器的工作流程描述如下:
- 创建一个名为
my-eventsource
的 EventSource 自定义资源。 - 检索并重新组织 EventBus 的配置,包括 EventBus 名称(在此示例中为
default
)和与 EventBus 关联的 Dapr 组件的名称。 - 创建一个名为
serving-xxxxx-component-ebfes-my-eventsource-xxxxx
的 Dapr 组件,使 EventSource 能够与事件总线关联。 - 创建一个名为
serving-xxxxx-component-esc-kafka-sample-two-xxxxx
的 Dapr 组件,使 EventSource 能够与事件源关联。 - 创建一个名为
serving-xxxxx-deployment-v100-xxxxx
的 Deployment,用于处理事件。
- 创建一个名为
创建 Trigger
使用以下内容创建 Trigger 的配置文件(例如,
trigger.yaml
)。注意
- 通过
spec.eventBus
设置与 Trigger 关联的事件总线。 - 通过
spec.inputs
设置事件输入源。 - 这是一个简单的触发器,它从名为
default
的 EventBus 中收集事件。当它从 EventSourcemy-eventsource
中检索到一个sample-two
事件时,它触发一个名为function-sample-serving-qrdx8-ksvc-fwml8
的 Knative 服务,并同时将事件发送到事件总线的metrics
主题。
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"
- 通过
运行以下命令应用配置文件。
kubectl apply -f trigger.yaml
运行以下命令检查结果。
$ 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
注意
在使用事件总线的情况下,Trigger 控制器的工作流程如下:
- 创建一个名为
my-trigger
的 Trigger 自定义资源。 - 检索并重新组织 EventBus 的配置,包括 EventBus 名称(在此示例中为
default
)和与 EventBus 关联的 Dapr 组件的名称。 - 创建一个名为
serving-xxxxx-component-ebft-my-trigger-xxxxx
的 Dapr 组件,使 Trigger 能够与事件总线关联。 - 创建一个名为
serving-xxxxx-deployment-v100-xxxxx
的 Deployment,用于处理触发任务。
- 创建一个名为
创建事件生产者
使用以下内容创建事件生产者配置文件(例如,
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"
运行以下命令应用配置文件。
kubectl apply -f events-producer.yaml
运行以下命令观察目标异步函数的变化。
$ 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 中使用多个源
使用以下内容创建一个 EventSource 配置文件(例如,
eventsource-multi.yaml
)。注意
- 以下示例定义了一个名为
my-eventsource
的事件源,并将指定 Kafka 服务器生成的事件标记为sample-three
事件。 spec.sink
引用了目标函数(Knative 服务)。spec.cron
的配置是每5秒触发一次在spec.sink
中定义的函数。
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"
- 以下示例定义了一个名为
运行以下命令应用配置文件。
kubectl apply -f eventsource-multi.yaml
运行以下命令观察变化。
$ 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
使用以下内容创建一个 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"
运行以下命令删除 EventBus。
kubectl delete eventbus.events.openfunction.io default
运行以下命令应用配置文件。
kubectl apply -f clustereventbus.yaml
运行以下命令检查结果。
$ 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 中描述的步骤。
使用带有条件的触发器
创建两个事件源
使用以下内容创建一个 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
使用以下内容创建另一个 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"
运行以下命令应用这两个配置文件。
kubectl apply -f eventsource-a.yaml kubectl apply -f eventsource-b.yaml
创建带有条件的触发器
使用以下内容创建一个带有
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"
注意
在这个例子中,定义了两个输入源和两个订阅者,它们的触发关系描述如下:
- 当接收到输入
eventB
时,将输入事件发送到 Knative 服务。 - 当接收到输入
eventB
和输入eventA
时,将输入事件同时发送到事件总线的metrics
主题和 Knative 服务。
- 当接收到输入
运行以下命令应用配置文件。
kubectl apply -f condition-trigger.yaml
运行以下命令检查结果。
$ 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
运行以下命令,您可以从输出中看到,由于事件源
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
参考 创建事件生产者 创建一个事件生产者。
运行以下命令,您可以从输出中看到,触发器中的
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
,如下所示:
- 安装 OpenFunction,但不包括
Contour
:
helm install openfunction --set global.Contour.enabled=false openfunction/openfunction -n openfunction
- 安装
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
- 创建一个名为
istio
的GatewayClass
:
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
- 创建一个
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
- 在
Function
的gatewayRef
字段中引用自定义的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.io
的 Gateway
,您需要通过以下命令更新 CoreDNS
配置:
- 编辑
coredns
配置映射:
kubectl -n kube-system edit cm coredns -o yaml
- 在
.: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
配置:
- 编辑
nodelocaldns
配置映射:
kubectl -n kube-system edit cm nodelocaldns -o yaml
- 在配置文件中添加
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 组件与中间件交互。本文档使用两个函数,function-front
和 kafka-input
,进行演示。
以下图表说明了这些函数之间的关系。
先决条件
- 您已经安装了 OpenFunction。
- 您已经创建了一个 secret。
创建 Kafka 服务器和主题
运行以下命令,在默认命名空间中安装 strimzi-kafka-operator。
helm repo add strimzi https://strimzi.io/charts/ helm install kafka-operator -n default strimzi/strimzi-kafka-operator
使用以下内容创建一个文件
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
运行以下命令,在默认命名空间中部署一个名为
kafka-server
的 1 副本 Kafka 服务器和一个名为sample-topic
的 1 副本 Kafka 主题。kubectl apply -f kafka.yaml
运行以下命令,检查 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 集群的元数据。
# 启动一个实用工具 pod。 $ kubectl run utils --image=arunvelsriram/utils -i --tty --rm # 检查 Kafka 集群的元数据。 $ kafkacat -L -b kafka-server-kafka-brokers:9092
创建函数
使用以下示例 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
运行以下命令创建函数
kafka-input
。kubectl apply -f kafka-input.yaml
使用以下示例 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
注意
metadata.plugins.pre
定义了在执行用户函数之前需要调用的插件的顺序。metadata.plugins.post
定义了在执行用户函数之后需要调用的插件的顺序。有关这两个插件的逻辑以及执行插件后的效果的更多信息,请参阅 插件机制。在清单中,
spec.serving.outputs
定义了一个指向 Kafka 服务器的 Dapr 组件的输出。这使您可以在function-front
函数中向输出target
发送自定义内容。func Sender(ctx ofctx.Context, in []byte) (ofctx.Out, error) { ... _, err := ctx.Send("target", greeting) ... }
运行以下命令创建函数
function-front
。kubectl apply -f function-front.yaml
检查结果
运行以下命令查看函数的状态。
$ 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
注意
URL
,由 OpenFunction Domain 提供,是可以访问的地址。要通过此 URL 地址访问函数,您需要确保 DNS 可以解析此地址。运行以下命令在集群中创建一个用于访问函数的 pod。
kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
运行以下命令通过
URL
访问函数。[ root@curl:/ ]$ curl -d '{"message":"Awesome OpenFunction!"}' -H "Content-Type: application/json" -X POST http://openfunction.io.svc.cluster.local/default/function-front
运行以下命令查看
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
运行以下命令查看
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 提供可观测能力
功能概览
尽管 FaaS 允许开发者专注于他们的业务代码而不用担心底层的实现,但对函数服务进行功能调试和故障排除是很困难的。因此,OpenFunction 设法引入了可观测性能力来提高其可用性和稳定性。
SkyWalking 提供了在许多不同场景下观测和监控分布式系统的解决方案。OpenFunction 将 go2sky(SkyWalking 的 Go 语言代理)捆绑在 OpenFunction 追踪器选项中,以提供分布式追踪、函数性能统计和函数依赖关系图。
先决条件
- 您已安装了 OpenFunction。
- 您已创建了一个 Kubernetes Secret。
- 您已安装了 SkyWalking v9。
- 您已创建了一个 Kafka 服务和主题。
追踪功能的可配置参数
下表描述了 OpenFunction 中目前可用的追踪参数。
名称 | 描述 | 示例 |
---|---|---|
enabled | 使能追踪功能,默认为 false | true , false |
provider.name | 可以设置为 skywalking 、opentelemetry (待定) | skywalking |
provider.oapServer | SkyWalking OAP 服务器地址 | skywalking-opa:11800 |
tags | 一组键值对,用于追踪中的追踪所使用的 Span 自定义标签 | |
tags.func | 函数的名称,该值将被自动填充 | function-a |
tags.layer | 表示被追踪的服务类型,当你使用该函数时,它应该被设置为 faas | faas |
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
服务的样例地址。
运行下面的命令,修改
openfunction
命名空间中的 ConfigMapopenfunction-config
。kubectl edit configmap openfunction-config -n openfunction
参照下面的例子修改
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 作为分布式追踪解决方案
通过参考 本文档 创建函数。
然后,您可以在 SkyWalking UI 界面上观察整个函数调用的链路。
您还可以比较 Knative 运行时函数(
function-front
)在运行状态和冷启动时的响应时间。在冷启动时:
在运行状态下:
5.3 - Elastic 日志告警
本文档描述了如何创建一个异步函数来查找错误日志。
概述
本文档使用一个异步函数来分析 Kafka 中的日志流,以找出错误日志。异步函数将然后向 Slack 发送告警。以下图表说明了整个工作流程。
先决条件
- 您已经安装了 OpenFunction。
- 您已经创建了一个 secret。
创建 Kafka 服务器和主题
运行以下命令在默认命名空间中安装 strimzi-kafka-operator。
helm repo add strimzi https://strimzi.io/charts/ helm install kafka-operator -n default strimzi/strimzi-kafka-operator
使用以下内容创建一个文件
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
运行以下命令在默认命名空间中部署一个名为
kafka-logs-receiver
的 1 副本 Kafka 服务器和一个名为logs
的 1 副本 Kafka 主题。kubectl apply -f kafka.yaml
运行以下命令检查 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
运行以下命令查看 Kafka 集群的元数据。
# 启动一个实用工具 pod。 $ kubectl run utils --image=arunvelsriram/utils -i --tty --rm # 检查 Kafka 集群的元数据。 $ kafkacat -L -b kafka-logs-receiver-kafka-brokers:9092
创建日志处理函数
- 使用以下示例 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
运行以下命令创建函数
logs-async-handler
。kubectl apply -f logs-handler-function.yaml
日志处理函数将由 Kafka 中 logs 主题的消息触发。
6 - 参考资料
6.1 - 常见问题解答
本文档描述在使用OpenFunction时的常见问题解答。
Q: 如何在OpenFunction中使用私有镜像仓库?
A: OpenFunction在构建阶段使用Shipwright(利用Tekton与Cloud Native Buildpacks集成)将用户函数打包到应用镜像中。
用户通常选择以不安全的方式访问私有镜像仓库,这在Cloud Native Buildpacks中尚不受支持。
我们提供以下解决方法,暂时绕过这个限制:
- 使用IP地址而不是主机名作为私有镜像仓库的访问地址。
- 运行Knative-runtime函数时,应该跳过标签解析。
参考链接:
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
注意
如果 helm install
命令卡住,可能是由于 job contour-contour-cergen
引起的。
运行以下命令确认 job 是否执行成功:
kubectl get job contour-contour-cergen -n projectcontour
如果 job 存在且 job 状态为完成,运行以下命令完成安装:
helm uninstall openfunction -n openfunction --no-hooks
helm install openfunction openfunction-v1.0.0-v0.5.0.tgz -n openfunction -f custom-values.yaml --no-hooks
补丁 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-secret
和git-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 种类型的问题:
- 问题/错误报告:您发现了一个错误,并希望报告并跟踪它。
- 问题/功能请求:您希望使用一个功能,但它尚未得到支持。
- 问题/提案:用于提出新的想法或功能的项目。这允许在编写代码之前从他人那里获得反馈。
提交前的检查
在您提交问题之前,请确保您已经检查了以下内容:
- 这是正确的仓库吗?
- OpenFunction 项目分布在多个仓库中。如果您不确定哪个仓库是正确的,请检查 仓库列表。
- 检查现有的问题
拉取请求
所有的贡献都通过拉取请求来进行。要提交建议的更改,请遵循以下工作流程:
- 确保已经提出了问题,这为您即将做出的贡献设定了期望。
- 分叉相关的仓库并创建一个新的分支
- 创建您的更改
- 代码更改需要测试
- 更新相关的文档以进行更改
- 带有 DCO 签名 的提交并打开一个 PR
- 等待 CI 过程完成并确保所有检查都是绿色的
- 您可以在几天内期待一次审查
使用工作进度中的 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 问题上发表评论,并包含您的解决方案提议。