K3s part 10: OpenFaaS

Updated December 28, 2020 4 minutes

OpenFaaS is a platform for creating serverless functions and microservices on kubernetes.

## Same git repo for infrastructure as in prior posts:
FLUX_INFRA_DIR=${HOME}/git/flux-infra
CLUSTER=k3s.example.com

Add the flux HelmRepository

OpenFaaS is distributed as a helm chart. The easiest way to consume this, is to add the helm repository directly to flux, and let flux handle it. HelmRepository objects must be created in the flux-system namespace, as shown:

cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/flux-system/helm-repositories.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
  name: openfaas
  namespace: flux-system
spec:
  interval: 1m
  url: https://openfaas.github.io/faas-netes/
EOF

Update the flux-system/kustomization.yaml file, to include the helm repository:

cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/flux-system/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
- helm-repositories.yaml
EOF

Create namespaces for OpenFaaS

Create kustomization.yaml to list all of the manifests:

mkdir -p ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas
cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- configmap.yaml
- helm.release.yaml
EOF

Create namespaces: openfaas is for the system, and openfaas-fn is for running functions:

cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: openfaas
  labels:
    role: openfaas-system
    access: openfaas-system
---
apiVersion: v1
kind: Namespace
metadata:
  name: openfaas-fn
  labels:
    role: openfaas-fn
EOF

Create OpenFaaS helm release

cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas/helm.release.yaml
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: openfaas
  namespace: openfaas
spec:
  interval: 5m
  chart:
    spec:
      chart: openfaas
      sourceRef:
        kind: HelmRepository
        name: openfaas
        namespace: flux-system
      interval: 1m
  valuesFrom:
  - kind: ConfigMap
    name: openfaas
    valuesKey: values.yaml
EOF

Create Helm values

You can refer to the upstream chart values.yaml for settings.

cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: openfaas
  namespace: openfaas
data:
  values.yaml: |
    functionNamespace: openfaas-fn
    generateBasicAuth: true
    serviceType: ClusterIP
    operator:
      create: true
    ingress:
      enabled: true
      hosts:
      - host: gateway.faas.${CLUSTER}
        serviceName: gateway
        servicePort: 8080
        path: /
      annotations:
        kubernetes.io/ingress.class: traefik
        traefik.ingress.kubernetes.io/router.tls.certresolver: default
EOF

Commit and push manfests

git -C ${FLUX_INFRA_DIR} add ${CLUSTER}
git -C ${FLUX_INFRA_DIR} commit -m "${CLUSTER} OpenFaaS"
git -C ${FLUX_INFRA_DIR} push

Login to OpenFaaS

Go to https://gateway.faas.k3s.example.com in your browser

The Username is admin - the Password can be obtained by running:

PASSWORD=$(kubectl -n openfaas get secret basic-auth -o jsonpath="{.data.basic-auth-password}" | base64 -d)
echo "OpenFaaS username: admin"
echo "OpenFaaS password: ${PASSWORD}"

Login using faas-cli:

export OPENFAAS_URL=https://gateway.faas.${CLUSTER}
echo -n ${PASSWORD} | faas-cli login -g ${OPENFAAS_URL} -u admin --password-stdin

Create test function

Create a new git repository someplace to create functions

FUNCTIONS_ROOT=${HOME}/git/functions
REGISTRY=registry.${CLUSTER}
FUNCTION=hello-python
LANGUAGE=python
export OPENFAAS_URL=https://gateway.faas.${CLUSTER}
mkdir -p ${FUNCTIONS_ROOT}
git init ${FUNCTIONS_ROOT}
(cd ${FUNCTIONS_ROOT}; faas-cli new --prefix ${REGISTRY}/functions \
  --lang ${LANGUAGE} ${FUNCTION})

Create the Dockerfile for the container:

(cd ${FUNCTIONS_ROOT}; faas-cli build -f ${FUNCTION}.yml --shrinkwrap)
echo "Dockerfile created in ${FUNCTIONS_ROOT}/build/${FUNCTION}"

Build the function image

In order to build the image for the test function you will use podman.

podman build -f ${FUNCTIONS_ROOT}/build/${FUNCTION} \
  -t ${REGISTRY}/functions/${FUNCTION}

Login to the registry with podman:

podman login ${REGISTRY}

(Recall the username and password created when you setup the registry. Or check /etc/rancher/k3s/registries.yaml on the k3s node.)

Push the image to the registry:

podman push ${REGISTRY}/functions/${FUNCTION}

Deploy the test function

Create the manifests for the openfaas-fn namespace:

mkdir -p ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas-fn
cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas-fn/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- test-functions.yaml
EOF

OpenFaaS functions can be declared in kubernetes with a Function CRD object.

cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/openfaas-fn/test-functions.yaml
apiVersion: openfaas.com/v1
kind: Function
metadata:
  name: ${FUNCTION}
  namespace: openfaas-fn
spec:
  name: ${FUNCTION}
  image: ${REGISTRY}/functions/${FUNCTION}
  labels:
    com.openfaas.scale.min: "2"
    com.openfaas.scale.max: "15"
  environment:
    write_debug: "true"
  limits:
    cpu: "200m"
    memory: "256Mi"
  requests:
    cpu: "10m"
    memory: "128Mi"
EOF

Commit and push manfests

git -C ${FLUX_INFRA_DIR} add ${CLUSTER}
git -C ${FLUX_INFRA_DIR} commit -m "${CLUSTER} OpenFaaS functions"
git -C ${FLUX_INFRA_DIR} push

Test the function

Test the function responds:

curl -v -d "It works!" \
  https://gateway.faas.${CLUSTER}/function/${FUNCTION}.openfaas-fn

It should respond with debug headers and an echo of the text you sent: It works!

...
< HTTP/2 200 
< content-type: application/x-www-form-urlencoded
< date: Sat, 26 Dec 2020 20:11:38 GMT
< x-duration-seconds: 0.076057
< content-length: 10
< 
It works!
...

See the contents of the handler function:

HANDLER=${FUNCTIONS_ROOT}/build/${FUNCTION}/index.py
cat ${HANDLER}
echo "## Next steps:"
echo "## Modify ${HANDLER} to change this function"
echo "## Rebuild and push image to registry"
echo "## Redeploy function"


You can discuss this blog on Matrix (Element): #blog-rymcg-tech:enigmacurry.com

This blog is copyright EnigmaCurry and dual-licensed CC-BY-SA and MIT. The source is on github: enigmacurry/blog.rymcg.tech and PRs are welcome. ❤️