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,然后直接到达函数:
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 - 路由
什么是 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/
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