

一个 CNCF Sandbox 函数计算平台项目
OpenFunction 的云原生特性:
Cloud Agnostic —— 与云商的 BaaS 解耦
"Serverless computing = FaaS + BaaS." [1]
🔒 云商通过提供 “托管的”(Hosted)函数计算框架(FaaS)和各类中间件服务(BaaS),把开发者锁定在托管的云平台上。
🤔 即使函数计算框架是跨语言跨平台的,谁又来填补各云商平台 BaaS 服务的接口差异?
借助 Dapr 来集成各云商平台的 BaaS 服务

Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架。
Dapr 是和平台无关的,这意味着您可以在 Kubernetes 集群、本地或者其它集成 Dapr 的托管环境中运行应用程序。 这使得您能够在云平台和边缘计算中运行微服务应用。
OpenFunction 现已支持 Go, Node.js, Python, Java, .Net 等多种语言
// Standard Express style HTTP sync function
export const tryKnative = (req, res) => {
res.send(`Hello, ${req.query.u || 'World'}!`);
};{
"main": "index.mjs",
"scripts": {
"start": "functions-framework --target=tryKnative"
},
"dependencies": {
"@openfunction/functions-framework": "^0.6.0"
}
}apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
name: sample-node-knative
spec:
version: v2.0.0
image: '<image-repo>/<image-name>:<image-tag>'
build:
builder: openfunction/builder-node:v2-16.15
env:
FUNC_NAME: tryKnative
srcRepo:
url: https://github.com/OpenFunction/samples.git
sourceSubPath: functions/async/mqtt-io-node
revision: main
# app port default to "8080"
port: 8080
serving:
runtime: knative
template:
containers:
- name: function
imagePullPolicy: IfNotPresent
params:
FUNCTION_TARGET: tryKnative
DEBUG: common:*,ofn:*🔍 参见 OpenFunction Function CRD 定义
// Standard Express style HTTP sync function
export const tryKnative = (req, res) => {
res.send(`Hello, ${req.query.u || 'World'}!`);
};
// Async function
export const tryAsync = (ctx, data) => {
// Function Framework received and parsed "data"
console.log('Data received: %o', data);
// Use "send" utility to broadcast data to outputs
ctx.send(data);
};💡 多个函数可以被放在同一个 JS 文件中,并在 CR 中动态指定
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
name: sample-node-async
spec:
version: v2.0.0
image: '<image-repo>/<image-name>:<tag>'
serving:
# default to knative
runtime: async
annotations:
# default to "grpc"
dapr.io/app-protocol: http
template:
containers:
- name: function
params:
# default to FUNC_NAME value
FUNCTION_TARGET: tryAsync
inputs:
- name: mqtt-input
component: mqtt-in
outputs:
- name: mqtt-output
component: mqtt-out
operation: create
bindings:
mqtt-in:
type: bindings.mqtt
version: v1
metadata:
- name: consumerID
value: '{uuid}'
- name: url
value: tcp://admin:public@emqx:1883
- name: topic
value: in
mqtt-out:
type: bindings.mqtt
version: v1
metadata:
- name: consumerID
value: '{uuid}'
- name: url
value: tcp://admin:public@emqx:1883
- name: topic
value: out🔍 OpenFunction 函数定义中的输入输出部分:可以直接引用 Dapr Components 的元数据定义
// Standard Express style HTTP sync function
export const tryKnative = (req, res) => {
res.send(`Hello, ${req.query.u || 'World'}!`);
};
// OpenFunction standard function which could
// set bridge between sync and async function
export const tryKnativeAsync = async (ctx, data) => {
console.log('Data received: %o', data);
// Forward HTTP body to Dapr outputs
await ctx.send(data);
// Send something back as HTTP response
// when necessary
// ctx.res.send(data);
};💡
openfunction函数形态 可以同时支持同步和异步函数
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
name: sample-node-knative-async
spec:
version: v2.0.0
image: '<image-repo>/<image-name>:<image-tag>'
serving:
runtime: knative
params:
FUNCTION_TARGET: tryKnativeAsync
FUNCTION_SIGNATURE_TYPE: openfunction
outputs:
- name: mqtt-output
component: mqtt-out
operation: create
bindings:
mqtt-out:
type: bindings.mqtt
version: v1
metadata:
- name: consumerID
value: '{uuid}'
- name: url
value: tcp://admin:public@emqx:1883
- name: topic
value: out💡 此场景中 Function CRD 只生效
outputs输出部分
OpenFunction 简化数据预处理
import (
// ...
"github.com/OpenFunction/functions-framework-go/functions"
"github.com/xanzy/go-gitlab"
)
var GitlabClient *Client
func init() {
functions.HTTP("HelloWorld", SendBeginUnittest, functions.WithFunctionPath("/sendmsg"))
GitlabClient, _ = gitlab.NewClient("4g9gZCsex36Z9FFXspwJ", gitlab.WithBaseURL("https://gitlab.uisee.ai/"))
}
func SendBeginUnittest(w http.ResponseWriter, r *http.Request) {
// ...
projectMergeRequestList, _, err := GitlabClient.MergeRequests.ListProjectMergeRequests(projectId, listGroupMergeRequestsOptions, nil)
mergeRequest := projectMergeRequestList[0]
msg := "大佬又提交代码了!! 666666666"
mergeRequestDiscussion := &CreateMergeRequestDiscussionOptions{ Body: &msg }
res, _, err := GitlabClient.Discussions.CreateMergeRequestDiscussion(projectId, mergeRequest.IID, mergeRequestDiscussion)
}import (
ofctx "github.com/OpenFunction/functions-framework-go/context"
"github.com/prometheus/client_golang/prometheus"
)
var speedGauge prometheus.Gauge
func init() {
speedGauge = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "speed",
Help: "vehicle speed",
})
}
func trackingHandler(ctx ofctx.Context, vehicleTracking VehicleTracking) (ofctx.Out, error) {
setAndPushGauge(speedGauge, vehicleTracking.Params.Speed)
return ctx.ReturnOnSuccess(), nil
}OpenFunction 在无人驾驶业务中的一种应用
如何让同步函数可以更方便和更灵活的被访问?
OpenFunction 0.5.0 - 0.6.0 中可使用
🚧 每个函数对应的 Service 不便于外部直接访问,如 sample-serving-d55zz-ksvc-gd64w
💡 直接的想法:由 Ingress 统一函数访问入口
🧑💻 通过 Domain CR 为 Knative 同步函数提供 Ingress 访问入口:http://<domain_name>.<domain_ns>/<function_ns>/<function_name>
😵 这个方案可用,但也存在一些问题:
OpenFunction 0.7.0 开始可使用
💡 进一步的想法:直接使用 K8s Gateway API
更强大更灵活的 Kubernetes 原生网关,越来越多的
社区支持:Contour ✅,Istio,Apache APISIX 等等
🧑💻 构建新的 OpenFunction Gateway 体系
Domain 体系从 OpenFunction 0.7.0 开始被移除
🚀 OpenFunction Gateway 的一些特性:
{{.Name}}.{{.NS}}.{{.Domain}}使用 APISIX Ingress Controller 作为函数网关
enable_gateway_api=true 开启HTTPRouteFilter 计划 在 1.6.0 中支持💪 APISIX 强大而丰富的插件生态也正在助力 OpenFunction 的应用和发展:
openfunction“ 👉 详见 集成细节