We show how to automate updating your OpenFaaS Functions using Argo CD and its Image Updater

Introduction

In this tutorial we’ll set up Argo CD, install OpenFaaS and manage a number of functions using the app-of-apps pattern. Then we’ll show you how to deploy and configure the Argo CD Image Updater to update your functions as new container images get published from your CI pipeline.

Did you know? Argo CD is one of the Cloud Native Computing Foundation’s (CNCF) projects for Continuous Delivery and was originally developed at Intuit. Another popular option is Flux, which we covered in a previous article: Upgrade to Flux v2 to keep OpenFaaS up to date.

How it works

Conceptual diagram of deploying and updating OpenFaaS functions with Argo CD and Argo CD Image Updater

Conceptual diagram: deploy OpenFaaS functions with Argo CD and keep them up to date with the Argo CD Image Updater.

Argo CD is a continuous delivery tool for Kubernetes based upon the principles of GitOps and declarative infrastructure. It uses Git repositories as the source of truth for defining the desired state of your applications.

Argo CD automates the deployment of the desired application states in the specified target environments. It will continuously monitor the running applications and compare the current live state against the desired state. It reports any deviations and depending on your configuration automatically updates the application to the desired state.

The app of apps pattern is commonly used to install multiple applications in a cluster. Its concept is to declaratively specify one Argo CD app that only consists of other apps. In our case we want to create and deploy two Argo CD apps, one for the OpenFaaS Operator and one for our functions. We will need to create an app called applications that consists of both the openfaas-operator and openfaas-functions app.

Learn more about hte OpenFaaS Operator Learn how to manage your functions with kubectl

The Argo CD Image updater is used to automatically update functions when a new version is published. It polls the image registry to check if a new version of an image is found. If a new version is available and the version constraint is met, the Image Updater instructs Argo CD to update the application with the new image.

app-of-apps

Pictured: The Argo CD UI showing the applications app containing both the openfaas-operator and openfaas-functions app.

Prerequisites

Developers use arkade to install the latest versions of their favourite tools and Kubernetes apps. We will use it in this tutorial to install Argo CD and get some other required tools.

Get arkade with:

# Note: you can also run without `sudo` and move the binary yourself
# to a directory in your $PATH
curl -sLS https://get.arkade.dev | sudo sh

One of the limitations of the Argo CD Image Updater is that you can only update images for applications whose manifest is rendered using either Kustomize or Helm. In this article, we’ll use Helm, so the helm template will also need to take the image name of a function as an input parameter. This means that you will need to create a Helm Chart for your functions and make the image names of your functions configurable. We show you how to do this in one of our previous blog post:

Install ArgoCD

Create a cluster with KinD if you don’t have one already.

kind create cluster

There are various ways to install ArgoCD, we’ll use arkade for brevity.

arkade install argocd

Arkade will print out instructions for port forwarding the Argo CD API and logging in using the ArgoCD CLI. If you need to, you can always retrieve these instructions later by running arkade info argocd.

We will be using the ArgoCD CLI to manage ArgoCD.

arkade get argocd

Start by port-forwarding the ArgoCD API server to make it accessible on our local host:

kubectl port-forward svc/argocd-server -n argocd 8443:443

Next we will retrieve the password and use it to login with the ArgoCD CLI

# Get the password
PASS=$(kubectl get secret argocd-initial-admin-secret \
  -n argocd \
  -o jsonpath="{.data.password}" | base64 -d)

# Log in:
argocd login --name local 127.0.0.1:8443 --insecure \
  --username admin \
  --password $PASS

Verify that you were logged in successfully before moving on to the next steps:

argocd account list

NAME   ENABLED  CAPABILITIES
admin  true     login

argocd app list

NAME  CLUSTER  NAMESPACE  PROJECT  STATUS  HEALTH  SYNCPOLICY  CONDITIONS  REPO  PATH  TARGET

Argo CD also has its own UI, follow the instructions from arkade, if you want to see what it looks like.

Create the applications

Kubernetes YAML manifests can be used to define Argo CD applications, projects and settings. We’ll create a Helm Chart to configure our applications.

Checkout the Argo CD documentation for more info on a declarative application setup

The layout of our Git repository:

chart
├── applications
│   ├── Chart.yaml
│   ├── templates
│   │   ├── namespaces.yaml
│   │   ├── openfaas-functions-app.yaml
│   │   └── openfaas-operator-app.yaml
│   └── values.yaml
└── functions
    ├── Chart.yaml
    ├── templates
    │   ├── email-notify-func.yaml
    │   └── marketing-list-func.yaml
    └── values.yaml

The applications Chart has templates for two Argo CD applications. The openfaas-operator-app for the deployment of OpenFaaS and the openfaas-functions-app for the deployment of our functions.

The functions Chart is also pictured here, it is used by the openfaas-functions app. As mentioned in the prerequisites, you can checkout our tutorial on how to package functions with helm to create this Chart.

The openfaas-operator application template for installing OpenFaaS CE:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: openfaas-operator
  namespace: argocd
spec:
  destination:
    namespace: openfaas
    server: {{ .Values.spec.destination.server }}
  project: default
  source:
    helm:
      parameters:
      - name: operator.create
        value: "true"
      - name: generateBasicAuth
        value: "true"
      - name: functionNamespace
        value: openfaas-fn
    path: chart/openfaas
    repoURL: https://github.com/openfaas/faas-netes.git
  syncPolicy:
    automated:
      selfHeal: true

To deploy as an OpenFaaS Pro customer you have to add some additional Helm parameters:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: openfaas-operator
  namespace: argocd
spec:
  destination:
    namespace: openfaas
    server: {{ .Values.spec.destination.server }}
  project: default
  source:
    helm:
      parameters:
      - name: operator.create
        value: "true"
      - name: generateBasicAuth
        value: "true"
      - name: functionNamespace
        value: openfaas-fn
      - name: openfaasPro
        value: true
      - name: autoscaler.enabled
        value: true
    path: chart/openfaas
    repoURL: https://github.com/openfaas/faas-netes.git
  syncPolicy:
    automated:
      selfHeal: true

Don’t forget to create the required secret with your OpenFaaS Pro license

kubectl create secret generic \
  -n openfaas \
  openfaas-license \
  --from-file license=$HOME/.openfaas/LICENSE

Your OpenFaaS deployment and functions may need secrets. Argo CD is un-opinionated about how secrets are managed. The Argo CD documentation lists some ways people are doing GitOps secrets.

The template for the openfaas-functions application:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: openfaas-functions
  namespace: argocd
spec:
  destination:
    server: {{ .Values.spec.destination.server }}
  project: default
  source:
    path: chart/functions
    repoURL: https://github.com/welteki/openfaas-argocd-example.git
  syncPolicy:
    automated:
      selfHeal: true

We made the destination server configurable. In the default values.yaml it is set to the application’s K8s API server address, https://kubernetes.default.svc. This address should be used when deploying to the same cluster that Argo CD is running in. You can refer to the Argo CD docs for more details on registering another cluster to deploy apps to.

spec:
  destination:
    server: https://kubernetes.default.svc

The full example is available on GitHub

The parent app can be created and synced via the CLI:

argocd app create applications \
    --dest-namespace argocd \
    --dest-server https://kubernetes.default.svc \
    --repo https://github.com/welteki/openfaas-argocd-example.git \
    --path chart/applications

argocd app sync applications

You can check the Argo CD UI to see if the apps were synced successfully or use kubectl to verify if OpenFaaS and the functions are running.

kubectl get pods -n openfaas

NAME                              READY   STATUS    RESTARTS          AGE
alertmanager-c4df79ff7-2fxcs      1/1     Running   2 (4h42m ago)     39h
basic-auth-plugin-588f588-rkgmz   1/1     Running   2 (4h42m ago)     39h
gateway-55fd54cb76-xf4n7          2/2     Running   3 (4h42m ago)     39h
nats-67d8f684f8-x46zj             1/1     Running   2 (4h42m ago)     39h
prometheus-cd4844fc7-c6j2b        1/1     Running   2 (4h42m ago)     39h
queue-worker-5795ff9bb5-tkwpv     1/1     Running   3 (4h42m ago)     39h
kubectl get functions -n openfaas-fn

NAME             IMAGE
email-notify     welteki/email-notify:0.1.0
marketing-list   welteki/marketing-list:0.1.0

Install and configure the ArgoCD Image Updater

The installation documentation for the Argo CD Image Updater describes multiple installation options. We are going to install it in the same namespace where Argo CD is running using its Kubernetes manifest.

kubectl apply -n argocd  \
  -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml

The Kubernetes resource of our openfaas-functions applications must be annotated correctly in order for the Argo CD Image Updater to know it should inspect and update the container images.

We need to update the openfaas-functions application template and add the following annotations:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    argocd-image-updater.argoproj.io/image-list: marketingList=welteki/marketing-list:~0.1
    argocd-image-updater.argoproj.io/marketingList.helm.image-spec: marketingList.image
  name: openfaas-functions
  namespace: argocd
spec:
  destination:
    server: {{ .Values.spec.destination.server }}
  project: default
  source:
    path: chart/functions
    repoURL: https://github.com/welteki/openfaas-argocd-example.git
  syncPolicy:
    automated:
      selfHeal: true

The annotation argocd-image-updater.argoproj.io/image-list is used to specify a list of images that should be considered for updates. It should be a comma separated list of image specifications.

The image specification uses the following format:

[<alias_name>=]<image_path>[:<version_constraint>]

In our example we configure the Image Updater to watch for updates of the welteki/marketing-list image. The version constraint ~0.1 will tell it to update to any patch version within the 0.1 minor release only. The image is assigned an alias name of marketingList. This is required because our functions Helm Chart contains multiple functions each with their own parameter to set the image name.

The second annotation is used to set the helm.image-spec for the marketingList alias to the appropriate Helm value:

argocd-image-updater.argoproj.io/marketingList.helm.image-spec: marketingList.image

For a detailed explanation of the configuration options see:

Watch how functions get updated

In the previous steps we used a Helm chart and the Function CRD to deploy our functions.

We still need the OpenFaaS Stack YAML file to publish new versions of the functions from CI (using faas-cli).

Whilst the Image Updater will keep the image up to date, anything else you change in your stack.yml file like memory limits or labels will need to synchronised manually.

We can use the envsubst feature of the OpenFaaS Stack YAML file to make the image tag configurable, then we can pass in the TAG environment variable to faas-cli publish.

  marketing-list:
    lang: node17
    handler: ./marketing-list
    image: ${REGISTRY:-docker.io}/${REPO:-welteki}/marketing-list:${TAG:-dev}

To build and push a new version of the images run

TAG=0.1.4 faas-cli publish -f stack.yml

If you’d like an example of how we recommend using GitHub Actions with OpenFaaS, see the chapter in Serverless For Everyone Else.

The logs for the Argo Image Updater show that it is considering 1 annotated application for update. Once it queries the registry for updated images it will detect the new version of our function image and update the openfaas-functions application to use the new version of the marketing-list function.

time="2022-06-14T20:33:41Z" level=info msg="Starting image update cycle, considering 1 annotated application(s) for update"
time="2022-06-14T20:33:43Z" level=info msg="Setting new image to welteki/marketing-list:0.1.4" alias=marketingList application=openfaas-functions image_name=welteki/marketing-list image_tag=0.1.3 registry=
time="2022-06-14T20:33:43Z" level=info msg="Successfully updated image 'welteki/marketing-list:0.1.3' to 'welteki/marketing-list:0.1.4', but pending spec update (dry run=false)" alias=marketingList application=openfaas-functions image_name=welteki/marketing-list image_tag=0.1.3 registry=
time="2022-06-14T20:33:43Z" level=info msg="Committing 1 parameter update(s) for application openfaas-functions" application=openfaas-functions
time="2022-06-14T20:33:43Z" level=info msg="Successfully updated the live application spec" application=openfaas-functions

The Image Updater is polling for changes, so it could take a few moments for the change to be detected.

Conclusion

We used Argo CD’s app-of-apps pattern to install OpenFaaS and a set of OpenFaaS Functions in a helm chart. We then installed the Image Updater to update the Helm chart every time a new function image was published that matched our update policy.

Normally, you’d need to update the values.yaml file by hand for your functions, changing each image as new ones are published. The Image Updater did this for us, and at the same time we can even use it to automatically update OpenFaaS itself, by applying the same technique

If you’re deploying your functions via faas-cli today, there’s nothing wrong with that, but if you want automatic updates, you’ll need to create a Helm chart for them. You can learn how here:

For more info about the Argo CD Image Updater checkout the official GitHub repo: https://github.com/argoproj-labs/argocd-image-updater

Take a look at these posts on our blog if you want to read some more about how to apply GitOps to OpenFaaS:

Han Verstraete

Associate Software Developer, OpenFaaS Ltd

Co-authored by:

Alex Ellis

Founder of @openfaas.