This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Networking

1 - Introduction

Overview

Previously starting from v0.5.0, OpenFunction uses Kubernetes Ingress to provide unified entrypoints for sync functions, and a nginx ingress controller has to be installed.

With the maturity of Kubernetes Gateway API, we decided to implement OpenFunction Gateway based on the Kubernetes Gateway API to replace the previous ingress based domain method in OpenFunction v0.7.0.

You can find the OpenFunction Gateway proposal here

OpenFunction Gateway provides a more powerful and more flexible function gateway including features like:

  • Enable users to switch to any gateway implementations that support Kubernetes Gateway API such as Contour, Istio, Apache APISIX, Envoy Gateway (in the future) and more in an easier and vendor-neutral way.

  • Users can choose to install a default gateway implementation (Contour) and then define a new gateway.networking.k8s.io or use any existing gateway implementations in their environment and then reference an existing gateway.networking.k8s.io.

  • Allow users to customize their own function access pattern like hostTemplate: "{{.Name}}.{{.Namespace}}.{{.Domain}}" for host-based access.

  • Allow users to customize their own function access pattern like pathTemplate: "{{.Namespace}}/{{.Name}}" for path-based access.

  • Allow users to customize each function’s route rules (host-based, path-based or both) in function definition and default route rules are provided for each function if there’re no customized route rules defined.

  • Send traffic to Knative service revisions directly without going through Knative’s own gateway anymore. You will need only OpenFunction Gateway since OpenFunction 0.7.0 to access OpenFunction sync functions, and you can ignore Knative’s domain config errors if you do not need to access Knative service directly.

  • Traffic splitting between function revisions (in the future)

The following diagram illustrates how client traffics go through OpenFunction Gateway and then reach a function directly:

2 - OpenFunction Gateway

Inside OpenFunction Gateway

Backed by the Kubernetes Gateway, an OpenFunction Gateway defines how users can access sync functions.

Whenever an OpenFunction Gateway is created, the gateway controller will:

  • Add a default listener named ofn-http-internal to gatewaySpec.listeners if there isn’t one there.

  • Generate gatewaySpec.listeners.[*].hostname based on domain or clusterDomain.

  • Inject gatewaySpec.listenters to the existing Kubernetes Gateway defined by the gatewayRef of the OpenFunction Gateway.

  • Create an new Kubernetes Gateway based on the gatewaySpec.listenters field in gatewayDef of the OpenFunction Gateway.

  • Create a service named gateway.openfunction.svc.cluster.local that defines a unified entry for sync functions.

After an OpenFunction Gateway is deployed, you’ll be able to find the status of Kubernetes Gateway and its listeners in OpenFunction Gateway status:

status:
  conditions:
  - lastTransitionTime: "2022-08-04T10:20:57Z"
    message: Gateway is scheduled
    observedGeneration: 2
    reason: Scheduled
    status: "True"
    type: Scheduled
  - lastTransitionTime: "2022-08-04T10:20:57Z"
    message: Valid Gateway
    observedGeneration: 2
    reason: Valid
    status: "True"
    type: Ready
  listeners:
  - attachedRoutes: 0
    conditions:
    - lastTransitionTime: "2022-08-04T10:20:57Z"
      message: Valid listener
      observedGeneration: 2
      reason: Ready
      status: "True"
      type: Ready
    name: ofn-http-internal
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
  - attachedRoutes: 0
    conditions:
    - lastTransitionTime: "2022-08-04T10:20:57Z"
      message: Valid listener
      observedGeneration: 2
      reason: Ready
      status: "True"
      type: Ready
    name: ofn-http-external
    supportedKinds:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute

The Default OpenFunction Gateway

OpenFunction Gateway uses Contour as the default Kubernetes Gateway implementation. The following OpenFunction Gateway will be created automatically once you install 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

You can customize the default OpenFunction Gateway like below:

kubectl edit gateway openfunction -n openfunction

Switch to a different Kubernetes Gateway

You can switch to any gateway implementations that support Kubernetes Gateway API such as Contour, Istio, Apache APISIX, Envoy Gateway (in the future) and more in an easier and vendor-neutral way.

Here you can find more details.

Multiple OpenFunction Gateway

Multiple Gateway are meaningless for OpenFunction, we currently only support one OpenFunction Gateway.

3 - Route

What is Route?

Route is part of the Function definition. Route defines how traffic from the Gateway listener is routed to a function.

A Route specifies the Gateway to which it will attach in GatewayRef that allows it to receive traffic from the Gateway.

Once a sync Function is created, the function controller will:

  • Look for theGateway called openfunction in openfunction namespace, then attach to this Gateway if route.gatewayRef is not defined in the function.
  • Automatically generate route.hostnames based on Gateway.spec.hostTemplate, if route.hostnames is not defined in function.
  • Automatically generate route.rules based on Gateway.spec.pathTemplate or path of /, if route.rules is not defined in function.
  • a HTTPRoute custom resource will be created based on Route. BackendRefs will be automatically link to the corresponding Knative service revision and label HTTPRouteLabelKey will be added to this HTTPRoute.
  • Create service {{.Name}}.{{.Namespace}}.svc.cluster.local, this service defines an entry for the function to access within the cluster.
  • If the Gateway referenced by route.gatewayRef changed, will update the HTTPRoute.

After a sync Function is deployed, you’ll be able to find Function addresses and Route status in Function’s status field, e.g:

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:
      - lastTransitionTime: "2022-08-04T10:43:29Z"
        message: Valid HTTPRoute
        observedGeneration: 1
        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

Host Based Routing

Host-based is the default routing mode. When route.hostnames is not defined, route.hostnames will be generated based on gateway.spec.hostTemplate. If route.rules is not defined, route.rules will be generated based on path of /.

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

If you are using the default OpenFunction Gateway, the function external address will be as below:

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

Path Based Routing

If you define route.hostnames in a function, route.rules will be generated based on gateway.spec.pathTemplate.

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

If you are using the default OpenFunction Gateway, the function external address will be as below:

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

Host and Path based routing

You can define hostname and path at the same time to customize how traffic should be routed to your function.

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

If you are using the default OpenFunction Gateway, the function external address will be as below:

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

4 - Function Entrypoints

There are several methods to access a sync function. Let’s elaborate on this in the following section.

This documentation will assume you are using default OpenFunction Gateway and you have a sync function named function-sample.

Access functions from within the cluster

Access functions by the internal address

OpenFunction will create this service for every sync Function: {{.Name}}.{{.Namespace}}.svc.cluster.local. This service will be used to provide the Function internal address.

Get Function internal address by running following command:

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

This address provides the default method for accessing functions within the cluster, it’s suitable for use as sink.url of EventSource.

Access Function using curl in pod:

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

Access functions from outside the cluster

Access functions by the Kubernetes Gateway’s IP address

Get Kubernetes Gateway’s ip address:

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

Get Function’s HOST and 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}')

Access Function using curl directly:

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

Access functions by the external address

To access a sync function by the external address, you’ll need to configure DNS first. Either Magic DNS or real DNS works:

  • Magic DNS

    Get Kubernetes Gateway’s ip address:

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

    Replace domain defined in OpenFunction Gateway with Magic DNS:

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

    Then, you can see Function external address in Function’s status field:

    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:
      - lastTransitionTime: "2022-08-20T11:03:14Z"
        message: Valid HTTPRoute
        observedGeneration: 13
        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

    If you have an external IP address, you can configure a wildcard A record as your domain:

    # Here example.com is the domain defined in OpenFunction Gateway
    *.example.com == A <external-ip>
    

    If you have a CNAME, you can configure a CNAME record as your domain:

    # Here example.com is the domain defined in OpenFunction Gateway
    *.example.com == CNAME <external-name>
    

    Replace domain defined in OpenFunction Gateway with the domain you configured above:

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

    Then, you can see Function external address in Function’s status field:

    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:
      - lastTransitionTime: "2022-08-20T13:07:17Z"
        message: Valid HTTPRoute
        observedGeneration: 14
        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
    

Then, you can get Function external address by running following command:

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

Now, you can access Function using curl directly:

curl -sv $FUNC_EXTERNAL_ADDRESS