Getting started with OpenFunction

Published: Dec 21, 2023 by Isaac Johnson

OpenFunction is a a cloud-native Function as a Service framework in the same realm as KNative and OpenFaaS. It’s a more recent offering with releases starting in May 2021 from the author Benjamin Huo of Kubesphere.

Let’s dig into this Apache 2.0 licenses CNCF Sandbox Project

Installation

We’ll start by adding the Helm repo and updating

$ helm repo add openfunction https://openfunction.github.io/charts/
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "openfunction" chart repository
...Successfully got an update from the "adwerx" chart repository
...Successfully got an update from the "longhorn" chart repository
...Successfully got an update from the "opencost" chart repository
...Successfully got an update from the "actions-runner-controller" chart repositoryn.com):
...Successfully got an update from the "ngrok" chart repositoryokup helm.epsagon.com on 172.22.64.1:53: no such host
...Successfully got an update from the "jfelten" chart repositoryitory
...Successfully got an update from the "zabbix-community" chart repository
...Successfully got an update from the "azure-samples" chart repository
...Successfully got an update from the "rook-release" chart repository
...Successfully got an update from the "confluentinc" chart repository
...Successfully got an update from the "nfs" chart repositorytory
...Successfully got an update from the "rhcharts" chart repositoryry
...Successfully got an update from the "btungut" chart repositoryository
...Successfully got an update from the "akomljen-charts" chart repository
...Successfully got an update from the "hashicorp" chart repositoryry
...Successfully got an update from the "portainer" chart repository
...Successfully got an update from the "sonarqube" chart repositoryepository
...Unable to get an update from the "freshbrewed" chart repository (https://harbor.freshbrewed.science/chartrepo/library):
...Succefailed to fetch https://harbor.freshbrewed.science/chartrepo/library/index.yaml : 404 Not Found
...Unable to get an update from the "myharbor" chart repository (https://harbor.freshbrewed.science/chartrepo/library):
...Succefailed to fetch https://harbor.freshbrewed.science/chartrepo/library/index.yaml : 404 Not Found
...Successfully got an update from the "dapr" chart repositoryrt repository
...Successfully got an update from the "kuma" chart repositoryt repository
...Successfully got an update from the "nginx-stable" chart repositorytps://harbor.freshbrewed.science/chartrepo/library):
...Successfully got an update from the "gitea-charts" chart repositoryibrary/index.yaml : 404 Not Found
...Successfully got an update from the "lifen-charts" chart repository://harbor.freshbrewed.science/chartrepo/library):
...Successfully got an update from the "castai-helm" chart repository
...Successfully got an update from the "open-telemetry" chart repository
...Successfully got an update from the "novum-rgi-helm" chart repository
...Successfully got an update from the "uptime-kuma" chart repository
...Successfully got an update from the "kube-state-metrics" chart repository
...Unable to get an update from the "epsagon" chart repository (https://helm.epsagon.com):
...SucceGet "https://helm.epsagon.com/index.yaml": dial tcp: lookup helm.epsagon.com on 172.22.64.1:53: no such host
...Successfully got an update from the "signoz" chart repository
...Successfully got an update from the "elastic" chart repositoryy
...Successfully got an update from the "openzipkin" chart repository
...Successfully got an update from the "kiwigrid" chart repository
...Successfully got an update from the "kubecost" chart repository
...Successfully got an update from the "sumologic" chart repository
...Successfully got an update from the "harbor" chart repository
...Successfully got an update from the "crossplane-stable" chart repository
...Successfully got an update from the "rancher-latest" chart repository
...Successfully got an update from the "incubator" chart repository
...Successfully got an update from the "datadog" chart repository
...Successfully got an update from the "gitlab" chart repository
...Successfully got an update from the "newrelic" chart repository
...Successfully got an update from the "argo-cd" chart repository
...Successfully got an update from the "grafana" chart repository
...Successfully got an update from the "bitnami" chart repository
...Successfully got an update from the "prometheus-community" chart repository
Update Complete. ⎈Happy Helming!⎈

I’m starting to think my blogging has amassed perhaps a bit too many chart repos…

We can now install OpenFunction into a new namespace

$ helm install openfunction openfunction/openfunction -n openfunction --create-namespace
W1208 16:45:19.684408   24437 warnings.go:70] The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1208 16:45:19.701421   24437 warnings.go:70] The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1208 16:45:48.569363   24437 warnings.go:70] The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1208 16:45:48.630311   24437 warnings.go:70] The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
NAME: openfunction
LAST DEPLOYED: Fri Dec  8 16:45:08 2023
NAMESPACE: openfunction
STATUS: deployed
REVISION: 1
TEST SUITE: None

This is my test cluster which is generally newer than production which would explain the Helm complaints on v1alpha2 gateways

$ kubectl get nodes
NAME                  STATUS   ROLES                  AGE   VERSION
builder-macbookpro2   Ready    <none>                 50d   v1.27.6+k3s1
isaac-macbookpro      Ready    <none>                 50d   v1.27.6+k3s1
anna-macbookair       Ready    control-plane,master   50d   v1.27.6+k3s1

If we want to add in the Revision Controller, which is optional, we can do that too

$ helm upgrade openfunction openfunction/openfunction -n openfunction --set revisionController.enable=true
Release "openfunction" has been upgraded. Happy Helming!
NAME: openfunction
LAST DEPLOYED: Fri Dec  8 16:49:10 2023
NAMESPACE: openfunction
STATUS: deployed
REVISION: 2
TEST SUITE: None

This installed quite a lot.

We have a contour gateway:

$ kubectl get gateway --all-namespaces
NAMESPACE        NAME      CLASS     ADDRESS   PROGRAMMED   AGE
projectcontour   contour   contour                          4m19s

And a slew of new namespaces

$ kubectl get ns
NAME               STATUS   AGE
default            Active   50d
kube-system        Active   50d
kube-public        Active   50d
kube-node-lease    Active   50d
punq               Active   50d
immich             Active   39d
openfunction       Active   4m56s
projectcontour     Active   4m55s
tekton-pipelines   Active   4m29s
dapr-system        Active   4m29s
shipwright-build   Active   4m29s
keda               Active   4m29s
knative-serving    Active   4m29s

We can verify it’s running

$ kubectl get pods -n openfunction
NAME                                                READY   STATUS    RESTARTS   AGE
openfunction-controller-manager-7fcbdb6bf9-2hzp6    2/2     Running   0          8m47s
openfunction-revision-controller-867b9c74f4-9gwgq   1/1     Running   0          4m57s

Setup

I first need to create a docker cred that we can use to push and pull images.

$ kubectl create secret docker-registry push-secret --docker-server=harbor.freshbrewed.sc
ience --docker-username=forgejo --docker-password=nottherealpassword\!\!
secret/push-secret created

Next we create a GIT credential for our repo

$ cat forgejoPass.yaml
apiVersion: v1
kind: Secret
metadata:
  name: git-repo-secret
  annotations:
    build.shipwright.io/referenced.secret: "true"
type: kubernetes.io/basic-auth
stringData:
  username: isaac
  password: xxxxxxxx

$ kubectl apply -f forgejoPass.yaml
secret/git-repo-secret created

I’m now going to push the samples to Forgejo

builder@DESKTOP-QADGF36:~/Workspaces$ git clone https://github.com/OpenFunction/samples.git
Cloning into 'samples'...
remote: Enumerating objects: 2411, done.
remote: Counting objects: 100% (852/852), done.
remote: Compressing objects: 100% (324/324), done.
remote: Total 2411 (delta 622), reused 609 (delta 521), pack-reused 1559
Receiving objects: 100% (2411/2411), 4.66 MiB | 7.53 MiB/s, done.
Resolving deltas: 100% (1179/1179), done.

I’ll make fork into my Forgejo by first creating a repo

/content/images/2023/12/openfunction-01.png

I’ll just push a repo

builder@DESKTOP-QADGF36:~/Workspaces/samples$ git remote add origin2 https://forgejo.freshbrewed.science/isaac/openfuncsamples.git
builder@DESKTOP-QADGF36:~/Workspaces/samples$ git push -u origin2 main
Enumerating objects: 1996, done.
Counting objects: 100% (1996/1996), done.
Delta compression using up to 16 threads
Compressing objects: 100% (996/996), done.
Writing objects: 100% (1996/1996), 4.52 MiB | 3.60 MiB/s, done.
Total 1996 (delta 942), reused 1952 (delta 918)
remote: Resolving deltas: 100% (942/942), done.
remote: . Processing 1 references
remote: Processed 1 references in total
To https://forgejo.freshbrewed.science/isaac/openfuncsamples.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin2'.

Launching a Function

We can now launch a basic helloworld sample

$ cat mySample.yaml
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: function-sample
spec:
  version: "v2.0.0"
  image: "freshbrewedprivate/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://forgejo.freshbrewed.science/isaac/openfuncsamples.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"

$ kubectl apply -f mySample.yaml
function.core.openfunction.io/function-sample created    

We can now see it building

$ kubectl get functions.core.openfunction.io
NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING   ADDRESS   AGE
function-sample   Building                    builder-l2h64                       3m19s

It was taking some time so I checked back in the morn and saw it failed

$ kubectl get functions.core.openfunction.io
NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING   ADDRESS   AGE
function-sample   Failed                      builder-l2h64                       17h

Just getting the logs only helps to determine the pod name

$ kubectl describe functions.core.openfunction.io function-sample | tail -n 8
  Build:
    Build Duration:  4m13s
    Message:         buildrun step step-create failed in pod builder-l2h64-buildrun-wj48w-w2z7s-pod, for detailed information: kubectl --namespace default logs builder-l2h64-buildrun-wj48w-w2z7s-pod --container=step-create
    Reason:          Failed
    Resource Hash:   17904119356002819818
    Resource Ref:    builder-l2h64
    State:           Failed
Events:              <none>

It does hint at the container in error above. We can see there are four in total

$ kubectl get pod builder-l2h64-buildrun-wj48w-w2z7s-pod
NAME                                     READY   STATUS   RESTARTS   AGE
builder-l2h64-buildrun-wj48w-w2z7s-pod   0/4     Error    0          17h

$ kubectl get pod builder-l2h64-buildrun-wj48w-w2z7s-pod -o json | jq '.spec.containers[] | .name'
"step-source-default"
"step-prepare"
"step-create"
"step-results"

Since it says “step-create” is in err, let’s see the logs for that container in the pod

$ kubectl logs  builder-l2h64-buildrun-wj48w-w2z7s-pod --container step-create
===> DETECTING
4 of 5 buildpacks participating
openfunction.go.of-functions-framework 0.6.0
google.go.build                        0.9.0
google.go.clear_source                 0.9.0
google.utils.label                     0.0.1
===> ANALYZING
Previous image with name "freshbrewedprivate/sample-go-func:v1" not found
===> RESTORING
===> BUILDING
=== Go - OpenFunction Functions Framework (openfunction.go.of-functions-framework@0.6.0) ===
--------------------------------------------------------------------------------
Running "go run /cnb/buildpacks/openfunction.go.of-functions-framework/0.6.0/converter/get_package/main.go -dir /workspace/source/functions/knative/hello-world-go/serverless_function_source_code (GOCACHE=/tmp/serverless_function_app3331328171)"
{"name":"hello","imports":{"fmt":{},"github.com/OpenFunction/functions-framework-go/context":{},"github.com/OpenFunction/functions-framework-go/functions":{},"net/http":{}}}Done "go run /cnb/buildpacks/openfunction.go.of-functions-framewor..." (596.102417ms)
Found framework version v0.5.0
=== Go - Build (google.go.build@0.9.0) ===
--------------------------------------------------------------------------------
Running "go list -f {{if eq .Name \"main\"}}{{.Dir}}{{end}} ./..."
/workspace/source/functions/knative/hello-world-go
Done "go list -f {{if eq .Name \"main\"}}{{.Dir}}{{end}} ./..." (729.810902ms)
--------------------------------------------------------------------------------
Running "go build -o /layers/google.go.build/bin/main ./. (GOCACHE=/layers/google.go.build/gocache CGO_ENABLED=0)"
Done "go build -o /layers/google.go.build/bin/main ./. (GOCACHE=/l..." (19.660144235s)
=== Go - Clear Source (google.go.clear_source@0.9.0) ===
Clearing source
=== Utils - Label Image (google.utils.label@0.0.1) ===
function package: {"name":"hello","imports":{"fmt":{},"github.com/OpenFunction/functions-framework-go/context":{},"github.com/OpenFunction/functions-framework-go/functions":{},"net/http":{}}}===> EXPORTING
Adding layer 'google.go.build:bin'
Adding 1/1 app layer(s)
Adding layer 'launcher'
Adding layer 'config'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Saving freshbrewedprivate/sample-go-func:v1...
*** Images (sha256:0ff9c9013ae07a4aa18b036a05ac7f9b0e2e32ff792b1207b10f9abd3108dec3):
      freshbrewedprivate/sample-go-func:v1 - HEAD https://index.docker.io/v2/freshbrewedprivate/sample-go-func/blobs/sha256:1ae7dafa9034f094a95f86577b809637b83522ae2cce442874d41b113a67e6d1: unexpected status code 401 Unauthorized (HEAD responses have no body, use GET for details)
ERROR: failed to export: failed to write image to the following tags: [freshbrewedprivate/sample-go-func:v1: HEAD https://index.docker.io/v2/freshbrewedprivate/sample-go-func/blobs/sha256:1ae7dafa9034f094a95f86577b809637b83522ae2cce442874d41b113a67e6d1: unexpected status code 401 Unauthorized (HEAD responses have no body, use GET for details)]

Interesting, it tried to push to Dockerhub instead of my private CR:

 https://index.docker.io/v2/freshbrewedprivate/sample-go-func/blobs/sha256:1ae7dafa9034f094a95f86577b809637b83522ae2cce442874d41b113a67e6d1

I realized the issue was that I needed to pack the “image” field with the full path or it would assume docker.io.

image: harbor.freshbrewed.science/freshbrewedprivate/sample-go-func:v1

Editing the function in kubernetes, then triggered and build and deploy.

Now when i get the YAML I see the status and endpoints

$ kubectl get function function-sample -o yaml
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"core.openfunction.io/v1beta1","kind":"Function","metadata":{"annotations":{},"name":"function-sample","namespace":"default"},"spec":{"build":{"builder":"openfunction/builder-go:latest","env":{"FUNC_CLEAR_SOURCE":"true","FUNC_NAME":"HelloWorld"},"srcRepo":{"credentials":{"name":"git-repo-secret"},"revision":"main","sourceSubPath":"functions/knative/hello-world-go","url":"https://forgejo.freshbrewed.science/isaac/openfuncsamples.git"}},"image":"freshbrewedprivate/sample-go-func:v1","imageCredentials":{"name":"push-secret"},"serving":{"runtime":"knative","template":{"containers":[{"imagePullPolicy":"IfNotPresent","name":"function"}]}},"version":"v2.0.0"}}
  creationTimestamp: "2023-12-08T23:14:13Z"
  generation: 2
  name: function-sample
  namespace: default
  resourceVersion: "4509924"
  uid: ffe35158-3045-4f31-9276-b62528c1a868
spec:
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_CLEAR_SOURCE: "true"
      FUNC_NAME: HelloWorld
    srcRepo:
      credentials:
        name: git-repo-secret
      revision: main
      sourceSubPath: functions/knative/hello-world-go
      url: https://forgejo.freshbrewed.science/isaac/openfuncsamples.git
  image: harbor.freshbrewed.science/freshbrewedprivate/sample-go-func:v1
  imageCredentials:
    name: push-secret
  serving:
    template:
      containers:
      - imagePullPolicy: IfNotPresent
        name: function
        resources: {}
    triggers:
      http:
        port: 8080
        route:
          gatewayRef:
            name: openfunction
            namespace: openfunction
  version: v2.0.0
  workloadRuntime: OCIContainer
status:
  addresses:
  - type: External
    value: http://function-sample.default.ofn.io/
  - type: Internal
    value: http://function-sample.default.svc.cluster.local/
  build:
    buildDuration: 3m57s
    message: Succeeded
    reason: Succeeded
    resourceHash: "14463020583153908224"
    resourceRef: builder-9cgms
    state: Succeeded
  revision:
    imageDigest: sha256:0ff9c9013ae07a4aa18b036a05ac7f9b0e2e32ff792b1207b10f9abd3108dec3
  route:
    hosts:
    - function-sample.default.ofn.io
    - function-sample.default.svc.cluster.local
    paths:
    - type: PathPrefix
      value: /
  serving:
    lastSuccessfulResourceRef: serving-vwczj
    message: Running
    reason: Running
    resourceHash: "10343863681372965255"
    resourceRef: serving-vwczj
    service: serving-vwczj-ksvc-fmmls
    state: Running
  sources:
  - git:
      commitAuthor: Benjamin Huo
      commitSha: 318ee2452fc1bb61a35b2c5016a8ab6fc29eef0c
    name: default

The addresses might be hard to reach. We can test the internal but the external isn’t real

status:
  addresses:
  - type: External
    value: http://function-sample.default.ofn.io/
  - type: Internal
    value: http://function-sample.default.svc.cluster.local/

I’ll try and connect

This cluster is a test cluster with Istio. Let’s respin with Traefik this time.

builder@LuiGi17:~/Workspaces/jekyll-blog/_posts$ kubectl get nodes
NAME                  STATUS   ROLES                  AGE     VERSION
anna-macbookair       Ready    control-plane,master   10m     v1.27.6+k3s1
builder-macbookpro2   Ready    <none>                 9m44s   v1.27.6+k3s1
isaac-macbookpro      Ready    <none>                 9m44s   v1.27.6+k3s1
builder@LuiGi17:~/Workspaces/jekyll-blog/_posts$ kubectl get ingress --all-namespaces
No resources found
builder@LuiGi17:~/Workspaces/jekyll-blog/_posts$ kubectl get pods --all-namespaces | grep trae
kube-system   helm-install-traefik-crd-pllrr           0/1     Completed   0          10m
kube-system   helm-install-traefik-nm4lg               0/1     Completed   1          10m
kube-system   svclb-traefik-12bb1adc-9hm8q             2/2     Running     0          10m
kube-system   traefik-64f55bb67d-hq59x                 1/1     Running     0          10m
kube-system   svclb-traefik-12bb1adc-6mq2w             2/2     Running     0          10m
kube-system   svclb-traefik-12bb1adc-5h87p             2/2     Running     0          10m

I’ll re-install OpenFunction with helm

$ helm install openfunction openfunction/openfunction -n openfunction --set revisionController.enable=true --create-namespace
W1212 22:00:37.091442  172573 warnings.go:70] The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1212 22:00:37.169330  172573 warnings.go:70] The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1212 22:01:12.932392  172573 warnings.go:70] The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1212 22:01:12.988751  172573 warnings.go:70] The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
NAME: openfunction
LAST DEPLOYED: Tue Dec 12 22:00:23 2023
NAMESPACE: openfunction
STATUS: deployed
REVISION: 1
TEST SUITE: None

I’ll add back the important pieces like the GIT credentials and Harbor cred

builder@LuiGi17:~/Workspaces/openFunction$ kubectl apply -f forgejoPass.yaml
secret/git-repo-secret created

$ kubectl create secret docker-registry push-secret --docker-server=harbor.freshbre
wed.science --docker-username=forgejo --docker-password=NotTheRealPassword\!

Now I can try again

builder@LuiGi17:~/Workspaces/openFunction$ cat mySample.yaml
apiVersion: core.openfunction.io/v1beta1
kind: Function
metadata:
  name: function-sample
spec:
  version: "v2.0.0"
  image: "harbor.freshbrewed.science/freshbrewedprivate/sample-go-func:v2"
  imageCredentials:
    name: push-secret
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "HelloWorld"
      FUNC_CLEAR_SOURCE: "true"
    srcRepo:
      url: "https://forgejo.freshbrewed.science/isaac/openfuncsamples.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"
builder@LuiGi17:~/Workspaces/openFunction$ kubectl apply -f mySample.yaml
function.core.openfunction.io/function-sample created

It looks like I made some mistake in the auth

n$ kubectl get pod builder-p5zkx-buildrun-f7xbt-7562m-pod
NAME                                     READY   STATUS   RESTARTS   AGE
builder-p5zkx-buildrun-f7xbt-7562m-pod   0/4     Error    0          2m29s

$ kubectl logs builder-p5zkx-buildrun-f7xbt-7562m-pod
Defaulted container "step-source-default" out of: step-source-default, step-prepare, step-create, step-results, prepare (init)
2023/12/14 01:43:48 Info: ssh (/usr/bin/ssh): OpenSSH_8.0p1, OpenSSL 1.1.1k  FIPS 25 Mar 2021
2023/12/14 01:43:48 Info: git (/usr/bin/git): git version 2.31.1
2023/12/14 01:43:48 Info: git-lfs (/usr/bin/git-lfs): git-lfs/2.13.3 (GitHub; linux amd64; go 1.17.5)
2023/12/14 01:43:48 /usr/bin/git clone -h
2023/12/14 01:43:48 /usr/bin/git submodule -h
2023/12/14 01:43:48 /usr/bin/git clone --quiet --no-tags --single-branch --branch main --depth 1 -c credential.helper=store --file /tmp/cred-helper-file2155860431 -- https://forgejo.freshbrewed.science/isaac/openfuncsamples.git /workspace/source
2023/12/14 01:43:49 remote: Verify
fatal: Authentication failed for 'https://forgejo.freshbrewed.science/isaac/openfuncsamples.git/' (exit code 128)

I escaped the password unneccesarily. I fixed the password and tried again

We can see it build and complete

Now this is pretty interesting. I had just refreshed the cluster. There shouldn’t be too much there…

$ kubectl get svc --all-namespaces
NAMESPACE          NAME                                              TYPE           CLUSTER-IP      EXTERNAL-IP                                      PORT(S)                                      AGE
default            kubernetes                                        ClusterIP      10.43.0.1       <none>                                           443/TCP                                      22h
kube-system        kube-dns                                          ClusterIP      10.43.0.10      <none>                                           53/UDP,53/TCP,9153/TCP                       22h
kube-system        metrics-server                                    ClusterIP      10.43.139.244   <none>                                           443/TCP                                      22h
kube-system        traefik                                           LoadBalancer   10.43.24.35     192.168.1.13,192.168.1.159,192.168.1.206         80:32382/TCP,443:30693/TCP                   22h
dapr-system        dapr-placement-server                             ClusterIP      None            <none>                                           50005/TCP,8201/TCP                           21h
dapr-system        dapr-sidecar-injector                             ClusterIP      10.43.134.173   <none>                                           443/TCP                                      21h
openfunction       openfunction-webhook-service                      ClusterIP      10.43.174.53    <none>                                           443/TCP                                      21h
knative-serving    default-domain-service                            ClusterIP      10.43.198.127   <none>                                           80/TCP                                       21h
knative-serving    controller                                        ClusterIP      10.43.3.33      <none>                                           9090/TCP,8008/TCP                            21h
projectcontour     contour                                           ClusterIP      10.43.60.211    <none>                                           8001/TCP                                     21h
knative-serving    domainmapping-webhook                             ClusterIP      10.43.133.233   <none>                                           9090/TCP,8008/TCP,443/TCP                    21h
dapr-system        dapr-webhook                                      ClusterIP      10.43.70.213    <none>                                           443/TCP                                      21h
keda               keda-operator                                     ClusterIP      10.43.97.170    <none>
9666/TCP                                     21h
keda               keda-operator-metrics-apiserver                   ClusterIP      10.43.79.81     <none>
443/TCP,8080/TCP                             21h
tekton-pipelines   tekton-pipelines-controller                       ClusterIP      10.43.135.228   <none>
9090/TCP,8008/TCP,8080/TCP                   21h
knative-serving    webhook                                           ClusterIP      10.43.175.149   <none>
9090/TCP,8008/TCP,443/TCP                    21h
dapr-system        dapr-sentry                                       ClusterIP      10.43.240.216   <none>
80/TCP                                       21h
dapr-system        dapr-api                                          ClusterIP      10.43.44.206    <none>
80/TCP                                       21h
knative-serving    activator-service                                 ClusterIP      10.43.139.192   <none>
9090/TCP,8008/TCP,80/TCP,81/TCP              21h
openfunction       openfunction-controller-manager-metrics-service   ClusterIP      10.43.126.72    <none>
8443/TCP                                     21h
tekton-pipelines   tekton-pipelines-webhook                          ClusterIP      10.43.234.237   <none>
9090/TCP,8008/TCP,443/TCP,8080/TCP           21h
projectcontour     contour-envoy                                     LoadBalancer   10.43.135.195   <pending>
80:32112/TCP,443:32502/TCP                   21h
knative-serving    autoscaler                                        ClusterIP      10.43.7.56      <none>
9090/TCP,8008/TCP,8080/TCP                   21h
keda               keda-admission-webhooks                           ClusterIP      10.43.32.15     <none>
443/TCP                                      21h
knative-serving    autoscaler-bucket-00-of-01                        ClusterIP      10.43.146.196   <none>
8080/TCP                                     21h
openfunction       gateway                                           ExternalName   <none>          contour-envoy.projectcontour.svc.cluster.local   80/TCP                                       21h
default            serving-t54c9-ksvc-c5xfm-v200-private             ClusterIP      10.43.249.252   <none>
80/TCP,9090/TCP,9091/TCP,8022/TCP,8012/TCP   5m9s
default            serving-t54c9-ksvc-c5xfm-v200                     ClusterIP      10.43.90.246    <none>
80/TCP                                       5m9s
default            serving-t54c9-ksvc-c5xfm                          ClusterIP      None            <none>
80/TCP                                       5m1s
default            function-sample                                   ExternalName   <none>          gateway.openfunction.svc.cluster.local

I’m not sure if that is quite viewable as a text table, but the number of services seemed a lot

/content/images/2023/12/openfun-03.png

So then let’s find the service URLs, which should be the same

$ kubectl get function function-sample -o json | jq .status.route.hosts[]
"function-sample.default.ofn.io"
"function-sample.default.svc.cluster.local"

I fired up a shell and tried again, but luck

root@my-shell:/# wget http://function-sample.default.svc.cluster.local
--2023-12-14 02:04:07--  http://function-sample.default.svc.cluster.local/
Resolving function-sample.default.svc.cluster.local (function-sample.default.svc.cluster.local)... 10.43.135.195
Connecting to function-sample.default.svc.cluster.local (function-sample.default.svc.cluster.local)|10.43.135.195|:80... failed: Connection refused.
root@my-shell:/# wget http://function-sample.default.svc.cluster.local
--2023-12-14 02:04:11--  http://function-sample.default.svc.cluster.local/
Resolving function-sample.default.svc.cluster.local (function-sample.default.svc.cluster.local)... 10.43.135.195
Connecting to function-sample.default.svc.cluster.local (function-sample.default.svc.cluster.local)|10.43.135.195|:80... failed: Connection refused.
root@my-shell:/# curl -O http://function-sample.default.svc.cluster.local
curl: Remote file name has no length!
curl: (23) Failed writing received data to disk/application
root@my-shell:/# curl -O http://function-sample.default.svc.cluster.local
curl: Remote file name has no length!
curl: (23) Failed writing received data to disk/application

Let me try a different sample.

I’ll need to install the pack CLI which comes from BuildPacks

$ brew install buildpacks/tap/pack

I can now build the buildpack

$ pack build func-helloworld-go --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld"  --env FUNC_CLEAR_SOURCE=true
v2.4.0-1.17: Pulling from openfunction/builder-go
47d17572643b: Pull complete
b69d4a399026: Pull complete
3c2cba919283: Pull complete
1632a81579ce: Pull complete
4cd5b698a987: Pull complete
d4f838d4cf95: Pull complete
6ef24a7a5aea: Pull complete
7ed6c3b6bba9: Pull complete
357fefdf9bc9: Pull complete
733c34a8a537: Pull complete
093b93186394: Pull complete
fd77c1508b78: Pull complete
1bf97eb692a3: Pull complete
aa95a14c5d42: Pull complete
09dd98da6b83: Pull complete
7938e8415fc0: Pull complete
3ba32ee46a69: Pull complete
4f4fb700ef54: Pull complete
Digest: sha256:a9b4c1df1b35cde0ca88e92f0b2376d372bfb9fbaac9bfce785c2ee5d429066c
Status: Downloaded newer image for openfunction/builder-go:v2.4.0-1.17
v2.4.0-1.17: Pulling from openfunctiondev/buildpacks-run-go
aa49f28a0db9: Pull complete
3d89ead22a45: Pull complete
Digest: sha256:726b67ac4b9cf220f9cf64f7ea9f42a5c06659b3dee96297ee8266aba32a6361
Status: Downloaded newer image for openfunctiondev/buildpacks-run-go:v2.4.0-1.17
0.13.2: Pulling from buildpacksio/lifecycle
e8614d09b7be: Pull complete
ee78fc634fdc: Pull complete
Digest: sha256:b7f2b9f44525ed0e962bdf5be186a266e36adc502b3dc0595dd2aac7f2b92e75
Status: Downloaded newer image for buildpacksio/lifecycle:0.13.2
===> ANALYZING
[analyzer] Previous image with name "func-helloworld-go" not found
===> DETECTING

If you aren’t in a sample repo, this will crash out with an error about no buildpacks:

[detector] ERROR: No buildpack groups passed detection.
[detector] ERROR: Please check that you are running against the correct path.
[detector] ERROR: failed to detect: no buildpacks participating
ERROR: failed to build: executing lifecycle: failed with status code: 20

But if we are in a samples repo

builder@LuiGi17:~/Workspaces$ git clone https://github.com/OpenFunction/samples.git
Cloning into 'samples'...
remote: Enumerating objects: 2411, done.
remote: Counting objects: 100% (852/852), done.
remote: Compressing objects: 100% (324/324), done.
remote: Total 2411 (delta 622), reused 609 (delta 521), pack-reused 1559
Receiving objects: 100% (2411/2411), 4.66 MiB | 1.41 MiB/s, done.
Resolving deltas: 100% (1179/1179), done.
builder@LuiGi17:~/Workspaces$ cd samples/
builder@LuiGi17:~/Workspaces/samples$ cd functions/knative/hello-world-go/

Then it works

builder@LuiGi17:~/Workspaces/samples/functions/knative/hello-world-go$ pack build func-helloworld-go --builder openfunction/builder-go:v2.4.0-1.17 --env FUNC_NAME="HelloWorld"  --env FUNC_CLEAR_SOURCE=true
v2.4.0-1.17: Pulling from openfunction/builder-go
Digest: sha256:a9b4c1df1b35cde0ca88e92f0b2376d372bfb9fbaac9bfce785c2ee5d429066c
Status: Image is up to date for openfunction/builder-go:v2.4.0-1.17
v2.4.0-1.17: Pulling from openfunctiondev/buildpacks-run-go
Digest: sha256:726b67ac4b9cf220f9cf64f7ea9f42a5c06659b3dee96297ee8266aba32a6361
Status: Image is up to date for openfunctiondev/buildpacks-run-go:v2.4.0-1.17
0.13.2: Pulling from buildpacksio/lifecycle
Digest: sha256:b7f2b9f44525ed0e962bdf5be186a266e36adc502b3dc0595dd2aac7f2b92e75
Status: Image is up to date for buildpacksio/lifecycle:0.13.2
===> ANALYZING
[analyzer] Previous image with name "func-helloworld-go" not found
===> DETECTING
[detector] 4 of 5 buildpacks participating
[detector] openfunction.go.of-functions-framework 0.6.0
[detector] google.go.build                        0.9.0
[detector] google.go.clear_source                 0.9.0
[detector] google.utils.label                     0.0.1
===> RESTORING
===> BUILDING
[builder] === Go - OpenFunction Functions Framework (openfunction.go.of-functions-framework@0.6.0) ===
[builder] --------------------------------------------------------------------------------
[builder] Running "go run /cnb/buildpacks/openfunction.go.of-functions-framework/0.6.0/converter/get_package/main.go -dir /workspace/serverless_function_source_code (GOCACHE=/tmp/serverless_function_app3505450428)"
[builder] {"name":"hello","imports":{"fmt":{},"github.com/OpenFunction/functions-framework-go/context":{},"github.com/OpenFunction/functions-framework-go/functions":{},"net/http":{}}}Done "go run /cnb/buildpacks/openfunction.go.of-functions-framewor..." (514.152005ms)
[builder] Found framework version v0.5.0
[builder] === Go - Build (google.go.build@0.9.0) ===
[builder] --------------------------------------------------------------------------------
[builder] Running "go list -f {{if eq .Name \"main\"}}{{.Dir}}{{end}} ./..."
[builder] /workspace
[builder] Done "go list -f {{if eq .Name \"main\"}}{{.Dir}}{{end}} ./..." (769.651385ms)
[builder] --------------------------------------------------------------------------------
[builder] Running "go build -o /layers/google.go.build/bin/main ./. (GOCACHE=/layers/google.go.build/gocache CGO_ENABLED=0)"
[builder] Done "go build -o /layers/google.go.build/bin/main ./. (GOCACHE=/l..." (9.662469444s)
[builder] === Go - Clear Source (google.go.clear_source@0.9.0) ===
[builder] Clearing source
[builder] === Utils - Label Image (google.utils.label@0.0.1) ===
[builder] function package: {"name":"hello","imports":{"fmt":{},"github.com/OpenFunction/functions-framework-go/context":{},"github.com/OpenFunction/functions-framework-go/functions":{},"net/http":{}}}
===> EXPORTING
[exporter] Adding layer 'google.go.build:bin'
[exporter] Adding 1/1 app layer(s)
[exporter] Adding layer 'launcher'
[exporter] Adding layer 'config'
[exporter] Adding layer 'process-types'
[exporter] Adding label 'io.buildpacks.lifecycle.metadata'
[exporter] Adding label 'io.buildpacks.build.metadata'
[exporter] Adding label 'io.buildpacks.project.metadata'
[exporter] Setting default process type 'web'
[exporter] Saving func-helloworld-go...
[exporter] *** Images (80a32bf5a475):
[exporter]       func-helloworld-go
[exporter] Adding cache layer 'openfunction.go.of-functions-framework:functions-framework'
Successfully built image func-helloworld-go

Now let’s push that to our Private CR

builder@LuiGi17:~/Workspaces/samples/functions/knative/hello-world-go$ docker tag func-helloworld-go:latest harbor.freshbrewed.science/freshbrewedprivate/sample-go-func:v3
builder@LuiGi17:~/Workspaces/samples/functions/knative/hello-world-go$ docker push harbor.freshbrewed.science/freshbrewedprivate/sample-go-func:v3
The push refers to repository [harbor.freshbrewed.science/freshbrewedprivate/sample-go-func]
83d85471d9f8: Pushed
a86763bbd83f: Pushed
957a2c8ae307: Pushed
5506412630d7: Pushed
2c7256d0680c: Pushed
db1c3f3a541e: Layer already exists
30ffe6ef068e: Layer already exists
v3: digest: sha256:d815ce56bf22f61460288c3edb08b2f180a2d2c5df555af948ca8a15f009fee3 size: 1777

Since we built and pushed it ourselves, we can run the sample without the build block

builder@LuiGi17:~/Workspaces/samples/functions/knative/hello-world-go$ cat runTest.yaml
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  image: "harbor.freshbrewed.science/freshbrewedprivate/sample-go-func:v3"
  version: "v3.0.0"
  imageCredentials:
    name: push-secret
  serving:
    template:
      containers:
        - name: function # DO NOT change this
          imagePullPolicy: IfNotPresent
    triggers:
      http:
        port: 8080
builder@LuiGi17:~/Workspaces/samples/functions/knative/hello-world-go$ kubectl apply -f runTest.yaml
function.core.openfunction.io/function-sample created

with the service back it still doesnt work

root@my-shell:/# curl http://function-sample.default.svc.cluster.local/OpenFunction
curl: (7) Failed to connect to function-sample.default.svc.cluster.local port 80 after 10 ms: Connection refused
root@my-shell:/# curl http://function-sample.default.svc.cluster.local/OpenFunction
curl: (7) Failed to connect to function-sample.default.svc.cluster.local port 80 after 9 ms: Connection refused
root@my-shell:/# curl http://function-sample.default.svc.cluster.local/
curl: (7) Failed to connect to function-sample.default.svc.cluster.local port 80 after 10 ms: Connection refused

I ran a few options without success.

However, at the very least I can run with docker

$ 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
I1214 02:44:55.836924       1 framework.go:214] Plugins for pre-hook stage:
I1214 02:44:55.836975       1 framework.go:222] Plugins for post-hook stage:
I1214 02:44:55.836983       1 framework.go:144] no 'FUNCTION_TARGET' is provided, register all the functions in the registry
I1214 02:44:55.836988       1 framework.go:147] registering function: HelloWorld on path: /{greeting}
I1214 02:44:55.837019       1 knative.go:48] Knative Function serving http: listening on port 8080


builder@LuiGi17:~$ wget http://localhost:8080/OpenFunction
--2023-12-13 20:45:38--  http://localhost:8080/OpenFunction
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 21 [text/plain]
Saving to: ‘OpenFunction’

OpenFunction                          100%[======================================================================>]      21  --.-KB/s    in 0s

2023-12-13 20:45:38 (2.78 MB/s) - ‘OpenFunction’ saved [21/21]

builder@LuiGi17:~$ cat OpenFunction
Hello, OpenFunction!

AKS

Let’s pivot and use a full Kubernetes stack in Azure. Perhaps some of these troubles come from using a very new on-prem K3s without a fully integrated external load balancer.

I’ll create a Resource Group, SP and set aside it’s creds

isaac [ ~ ]$ az group create --name openfunrg --location centralus
{
  "id": "/subscriptions/738dcfa0-0af6-4806-ab1d-a3dd7b0823e1/resourceGroups/openfunrg",
  "location": "centralus",
  "managedBy": null,
  "name": "openfunrg",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}
isaac [ ~ ]$ az ad sp create-for-rbac -n idjaksupg01sp --skip-assignment --output json > my_sp.json
WARNING: Option '--skip-assignment' has been deprecated and will be removed in a future release.
WARNING: Found an existing application instance: (id) 34fede26-2ae7-4ccd-9c29-3bc220b9784a. We will patch it.
WARNING: The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
isaac [ ~ ]$ export SP_PASS=`cat my_sp.json | jq -r .password`
isaac [ ~ ]$ export SP_ID=`cat my_sp.json | jq -r .appId`

Next I’ll create a Cluster using those creds

isaac [ ~ ]$ az aks create --resource-group openfunrg --name idjaksopenfun --location centralus --node-count 3 --enable-cluster-autoscaler --min-count 2 --max-count 4 --generate-ssh-keys --network-plugin azure --network-policy azure --service-principal $SP_ID --client-secret $SP_PASS
SSH key files '/home/isaac/.ssh/id_rsa' and '/home/isaac/.ssh/id_rsa.pub' have been generated under ~/.ssh to allow SSH access to the VM. If using machines without permanent storage like Azure Cloud Shell without an attached file share, back up your keys to a safe location
docker_bridge_cidr is not a known attribute of class <class 'azure.mgmt.containerservice.v2023_10_01.models._models_py3.ContainerServiceNetworkProfile'> and will be ignored
 / Running ..

I can now log in and see some nodes are up and ready to serve traffic

isaac [ ~ ]$ az aks get-credentials -n idjaksopenfun -g openfunrg --admin
Merged "idjaksopenfun-admin" as current context in /home/isaac/.kube/config
isaac [ ~ ]$ kubectl get nodes
NAME                                STATUS   ROLES   AGE    VERSION
aks-nodepool1-31922335-vmss000000   Ready    agent   4m4s   v1.27.7
aks-nodepool1-31922335-vmss000001   Ready    agent   4m4s   v1.27.7
aks-nodepool1-31922335-vmss000002   Ready    agent   4m4s   v1.27.7

Since I’m using an Azure Cloud Shell, I’ll need to add the Helm repo and update before installing

isaac [ ~ ]$ helm repo add openfunction https://openfunction.github.io/charts/
"openfunction" has been added to your repositories
isaac [ ~ ]$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "openfunction" chart repository
Update Complete. ⎈Happy Helming!⎈

I can now install OpenFunction into the cluster with Helm

isaac [ ~ ]$ helm install openfunction openfunction/openfunction -n openfunction --set revisionController.enable=true --create-namespace
W1214 12:41:35.769178    2418 warnings.go:70] The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1214 12:41:36.043967    2418 warnings.go:70] The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1214 12:41:59.655475    2418 warnings.go:70] The v1alpha2 version of Gateway has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
W1214 12:41:59.690314    2418 warnings.go:70] The v1alpha2 version of GatewayClass has been deprecated and will be removed in a future release of the API. Please upgrade to v1beta1.
NAME: openfunction
LAST DEPLOYED: Thu Dec 14 12:41:10 2023
NAMESPACE: openfunction
STATUS: deployed
REVISION: 1
TEST SUITE: None

and I can see all that was created

/content/images/2023/12/openfun-04.png

I’ll pivot a bit this time and use DockerHub over my private CR

isaac [ ~ ]$ REGISTRY_SERVER=https://index.docker.io/v1/ REGISTRY_USER=idjohnson REGISTRY_PASSWORD=asdfasdf-asdfsadf-asdfasdf-asdfasdf
isaac [ ~ ]$ kubectl create secret docker-registry push-secret \
    --docker-server=$REGISTRY_SERVER \
    --docker-username=$REGISTRY_USER \
    --docker-password=$REGISTRY_PASSWORD
secret/push-secret created

And creds for my private Git repo

isaac [ ~ ]$ cat forgejoPassword.yaml 
apiVersion: v1
kind: Secret
metadata:
  name: git-repo-secret
  annotations:
    build.shipwright.io/referenced.secret: "true"
type: kubernetes.io/basic-auth
stringData:
  username: isaac
  password: NotMyPasswordHereEither
isaac [ ~ ]$ kubectl apply -f forgejoPassword.yaml 
secret/git-repo-secret created

I’ll clone the repo down then just change the image to use my Dockerhub path on the image: line

isaac [ ~/samples/functions/knative/hello-world-go ]$ vi function-sample.yaml 
isaac [ ~/samples/functions/knative/hello-world-go ]$ cat function-sample.yaml 
apiVersion: core.openfunction.io/v1beta2
kind: Function
metadata:
  name: function-sample
spec:
  version: "v2.0.0"
  image: "idjohnson/sample-go-func:v7"
  imageCredentials:
    name: push-secret
  build:
    builder: openfunction/builder-go:latest
    env:
      FUNC_NAME: "HelloWorld"
      FUNC_CLEAR_SOURCE: "true"
      # # Use FUNC_GOPROXY to set the goproxy if failed to fetch go modules
      # FUNC_GOPROXY: "https://goproxy.cn"
    srcRepo:
      url: "https://github.com/OpenFunction/samples.git"
      sourceSubPath: "functions/knative/hello-world-go"
      revision: "main"
  serving:
    template:
      containers:
        - name: function # DO NOT change this
          imagePullPolicy: IfNotPresent 
    triggers:
      http:
        port: 8080

I’ll apply the sample file:

I can now see services running

isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl get svc
NAME                                    TYPE           CLUSTER-IP     EXTERNAL-IP                              PORT(S)                                      AGE
function-sample                         ExternalName   <none>         gateway.openfunction.svc.cluster.local   80/TCP                                       91s
kubernetes                              ClusterIP      10.0.0.1       <none>                                   443/TCP                                      35m
serving-qrnvd-ksvc-ppznb                ClusterIP      None           <none>                                   80/TCP                                       92s
serving-qrnvd-ksvc-ppznb-v200           ClusterIP      10.0.163.249   <none>                                   80/TCP                                       99s
serving-qrnvd-ksvc-ppznb-v200-private   ClusterIP      10.0.220.122   <none>                                   80/TCP,9090/TCP,9091/TCP,8022/TCP,8012/TCP   99s

Much as before, I see the same URLs exposed

isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl get function
NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         ADDRESS                                             AGE
function-sample   Succeeded    Running        builder-v7v8v   serving-qrnvd   http://function-sample.default.svc.cluster.local/   6m13s
isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl get function -o json | jq .status.route.hosts[]
jq: error (at <stdin>:119): Cannot iterate over null (null)
isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl get function function-sample -o json | jq .status.route.hosts[]
"function-sample.default.ofn.io"
"function-sample.default.svc.cluster.local"

I can’t really expose the function directly

isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl expose service function-sample --port=8000 --target-port=80 --name=func-sample-80
error: couldn't retrieve selectors via --selector flag or introspection: the service has no pod selector set

isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl expose service gateway -n openfunction --port=8000 --target-port=80 --name=gateway-80
error: couldn't retrieve selectors via --selector flag or introspection: the service has no pod selector set

I’ll fire up an ephemeral shell

isaac [ ~/samples/functions/knative/hello-world-go ]$ kubectl run my-shell --rm -i --tty --image ubuntu -- bash
If you don't see a command prompt, try pressing enter.
root@my-shell:/# 

Then install curl and wget and try the internal url

root@my-shell:/# apt update && apt install -y wget curl
Get:1 http://security.ubuntu.com/ubuntu jammy-security InRelease [110 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy InRelease [270 kB]
Get:3 http://security.ubuntu.com/ubuntu jammy-security/multiverse amd64 Packages [44.0 kB]
Get:4 http://security.ubuntu.com/ubuntu jammy-security/restricted amd64 Packages [1572 kB]
Get:5 http://security.ubuntu.com/ubuntu jammy-security/universe amd64 Packages [1036 kB]
Get:6 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [1325 kB]     
Get:7 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [119 kB]              
Get:8 http://archive.ubuntu.com/ubuntu jammy-backports InRelease [109 kB]
Get:9 http://archive.ubuntu.com/ubuntu jammy/restricted amd64 Packages [164 kB]
Get:10 http://archive.ubuntu.com/ubuntu jammy/multiverse amd64 Packages [266 kB]
Get:11 http://archive.ubuntu.com/ubuntu jammy/main amd64 Packages [1792 kB]
Get:12 http://archive.ubuntu.com/ubuntu jammy/universe amd64 Packages [17.5 MB]
Get:13 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 Packages [1304 kB]
Get:14 http://archive.ubuntu.com/ubuntu jammy-updates/restricted amd64 Packages [1602 kB]
Get:15 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 Packages [1598 kB]
Get:16 http://archive.ubuntu.com/ubuntu jammy-updates/multiverse amd64 Packages [49.8 kB]
Get:17 http://archive.ubuntu.com/ubuntu jammy-backports/universe amd64 Packages [28.1 kB]
Get:18 http://archive.ubuntu.com/ubuntu jammy-backports/main amd64 Packages [50.4 kB]
Fetched 28.9 MB in 3s (8716 kB/s)                       
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
3 packages can be upgraded. Run 'apt list --upgradable' to see them.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
  ca-certificates libbrotli1 libcurl4 libldap-2.5-0 libldap-common libnghttp2-14 libpsl5 librtmp1 libsasl2-2 libsasl2-modules libsasl2-modules-db libssh-4 openssl publicsuffix
Suggested packages:
  libsasl2-modules-gssapi-mit | libsasl2-modules-gssapi-heimdal libsasl2-modules-ldap libsasl2-modules-otp libsasl2-modules-sql
The following NEW packages will be installed:
  ca-certificates curl libbrotli1 libcurl4 libldap-2.5-0 libldap-common libnghttp2-14 libpsl5 librtmp1 libsasl2-2 libsasl2-modules libsasl2-modules-db libssh-4 openssl publicsuffix wget
0 upgraded, 16 newly installed, 0 to remove and 3 not upgraded.
Need to get 3353 kB of archives.
After this operation, 8132 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 openssl amd64 3.0.2-0ubuntu1.12 [1182 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 ca-certificates all 20230311ubuntu0.22.04.1 [155 kB]
...
Setting up ca-certificates (20230311ubuntu0.22.04.1) ...
debconf: unable to initialize frontend: Dialog
debconf: (No usable dialog-like program is installed, so the dialog based frontend cannot be used. at /usr/share/perl5/Debconf/FrontEnd/Dialog.pm line 78.)
debconf: falling back to frontend: Readline
debconf: unable to initialize frontend: Readline
debconf: (Can't locate Term/ReadLine.pm in @INC (you may need to install the Term::ReadLine module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.34.0 /usr/local/share/perl/5.34.0 /usr/lib/x86_64-linux-gnu/perl5/5.34 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base /usr/lib/x86_64-linux-gnu/perl/5.34 /usr/share/perl/5.34 /usr/local/lib/site_perl) at /usr/share/perl5/Debconf/FrontEnd/Readline.pm line 7.)
debconf: falling back to frontend: Teletype
Updating certificates in /etc/ssl/certs...
137 added, 0 removed; done.
Setting up libcurl4:amd64 (7.81.0-1ubuntu1.15) ...
Setting up curl (7.81.0-1ubuntu1.15) ...
Processing triggers for libc-bin (2.35-0ubuntu3.4) ...
Processing triggers for ca-certificates (20230311ubuntu0.22.04.1) ...
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
root@my-shell:/# 

This time the curl works

root@my-shell:/# curl http://function-sample.default.svc.cluster.local/OpenFunction
Hello, OpenFunction!

So at least in the cluster works

Before we wrap AKS, to save costs, I’ll remove the AKS cluster and Resource Group

isaac [ ~/samples/functions/knative/hello-world-go ]$ az aks delete -n idjaksopenfun -g openfunrg 
Are you sure you want to perform this operation? (y/n): y
 | Running ..

isaac [ ~/samples/functions/knative/hello-world-go ]$ az group delete -n openfunrg
Are you sure you want to perform this operation? (y/n): y
 / Running ..

Images

Let’s take a moment to check the images that were built.

/content/images/2023/12/openfun-06.png

We can see these are actually quite small, just a few Mb. Running a quick scan only found 11 issues, none critical

/content/images/2023/12/openfun-07.png

Summary

OpenFunction is a fascinating product. It claims to do a lot of things, but we barely scratched the surface of its capabilities. As you can see from the architecture diagram, it relies on a bunch of other open source systems and frameworks to work.

/content/images/2023/12/openfun-05.png

I find it a bit overkill for my needs. I tried to set up a test cluster, but it failed miserably. It also spawned too many things for me to use it in my production cluster.

The function framework used for the images was decent and launching through docker seemed like a nice way to get started with python or go code. However, given the weight and hassle, I think I’ll stick with KNative or OpenFaaS.

Serverless OpenFunction OpenSource

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes