Examples

Keel can be used in many different environments with a bunch of various settings. See these examples to understand which configuration suits your needs.

Example 1: Push to deploy

In this tutorial, we will configure several tools to enable automated Kubernetes updates on Git push. This workflow is mostly useful when developing apps for Kubernetes. For production, we recommend tag approach where a tagged release would trigger an image build and Keel update policies would increase the version.

Keel Force Workflow

Once a workflow is ready, any push to the master branch (or merge requests from develop/feature branches) will update your app running in Kubernetes.

In this tutorial we will use:

  • Minikube - our local development Kubernetes environment. Mac users are free to use Docker for Mac with Kubernetes support, works fine!
  • Helm - Kubernetes package manager
  • GitHub - we will store our code here
  • DockerHub - our Docker images will be built and stored here
  • Webhook Relay - will relay public webhooks to our internal Kubernetes environment so we don't have to expose Keel to the public internet

Set up GitHub repository

First, let's set up our versioning control system. Let's create a local repo of our example app and push it to our GitHub repository.

Our example app will be a really really simple one:

package main

import (
    "fmt"
    "log"
    "net/http"
)

var version = "v0"

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Welcome to my website! Version %s", version)
    })
    fmt.Printf("App is starting, version: %s \n", version)
    log.Fatal(http.ListenAndServe(":8500", nil))
}

Dockerfile and Kubernetes deployment files can be found here: https://github.com/keel-hq/push-workflow-example.

Commit your code and push to remote:

git add .
git commit -m "first"
git push origin master

Create Kubernetes namespace

To keep things isolated, create a new Kubernetes namespace:

kubectl create namespace push-workflow

From here, we will use namespace called 'push-workflow'. You can choose any other name, just update following commands to target your namespace.

It's also handy to set this namespace as your current context:

kubectl config set-context $(kubectl config current-context) --namespace=push-workflow

Enable Webhook Relay forwarding

Webhook Relay operator will create a public endpoint and destination where to forward webhooks. Let's install it:

helm repo add webhookrelay https://charts.webhookrelay.com
helm repo update

Get access token from here. Once you click on 'Create Token', it will generate it and show a helper to set environment variables:

export RELAY_KEY=*****-****-****-****-*********
export RELAY_SECRET=**********

Install through Helm:

helm upgrade --install webhookrelay-operator --namespace=push-workflow webhookrelay/webhookrelay-operator \
  --set credentials.key=$RELAY_KEY --set credentials.secret=$RELAY_SECRET

To view installed application:

helm list
NAME                 	NAMESPACE    	REVISION	UPDATED                                	STATUS  	CHART                      	APP VERSION
webhookrelay-operator	push-workflow	1       	2020-06-21 15:39:32.978269521 +0100 BST	deployed	webhookrelay-operator-0.1.3	1.16.0     

Once operator is deployed, you can create a simple CR (Custom Resource, located in the push-workflow-example repository) that will let you receive and forward webhooks:

# webhookrelay_cr.yaml
apiVersion: forward.webhookrelay.com/v1
kind: WebhookRelayForward
metadata:
  name: keel-forward
spec:
  buckets:
  - name: dockerhub-to-keel
    inputs:
    - name: dockerhub-endpoint
      description: "Public endpoint"
      responseBody: "OK"
      responseStatusCode: 200
    outputs:
    - name: keel
      destination: http://keel:9300/v1/webhooks/dockerhub
      internal: true

kubectl apply -f webhookrelay_cr.yaml

You should see two pods running now:

$ kubectl get pods
NAME                                           READY   STATUS    RESTARTS   AGE
keel-forward-whr-deployment-869c4d4c86-txptt   1/1     Running   0          17s
webhookrelay-operator-74984df6d8-l475z         1/1     Running   0          9m31s

If you use kubectl describe command on the created CRD, you should be able to see your public endpoint (you can also view it if you go to your buckets page):

$ kubectl describe webhookrelayforwards.forward.webhookrelay.com keel-forward
...
...
Status:
  Agent Status:  Running
  Public Endpoints:
    https://gz66jjp2wvfczfsjhoxutk.hooks.webhookrelay.com
  Ready:           true
  Routing Status:  Configured

Grab that ***.hooks.webhookrelay.com URL, we will need it in the next step.

Configure DockerHub (code repository + webhook)

Now, we need to tell DockerHub to build a new image on every GitHub push to the master branch. First, go to https://cloud.docker.com, then Repositories and click on Create button. Once you have created the repository, link it to your GitHub account and click on Configure Automated Builds:

configure automated builds

Select your GitHub repository and create a trigger that will:

  • React to changes on a master branch
  • Tag image as latest

Ensure that autobuild is switched on and click on "Save and Build". You will get your first image prepared.

configure automated builds

Also, we will need to setup DockerHub webhooks to Keel via Webhook Relay. To configure webhooks, go to https://hub.docker.com:

dockerhub webhooks

Deploying Keel

helm repo add keel https://charts.keel.sh
helm repo update

And install it (we are disabling Keel's helm provider here as we are only going to work with Kubernetes manifests):

helm upgrade --install keel --namespace=push-workflow keel/keel --set helmProvider.enabled="false" --set service.enabled="true" --set service.type="ClusterIP"

Deploy your app

Now, we need to create a deployment file of our app:

apiVersion: apps/v1
kind: Deployment
metadata: 
  name: pushwf  
  labels: 
    name: "pushwf"
  annotations:
    # force policy will ensure that deployment is updated
    # even when tag is unchanged (latest remains)
    keel.sh/policy: force
spec:
  replicas: 1
  revisionHistoryLimit: 5
  selector:
    matchLabels:
      app: pushwf
  template:
    metadata:
      name: pushwf
      labels:
        app: pushwf
    spec:     
      containers:                    
        - image: keelhq/push-workflow-example:latest
          imagePullPolicy: Always # this is required to force pull image     
          name: pushwf
          ports:
            - containerPort: 8500
          livenessProbe:
            httpGet:
              path: /
              port: 8500
            initialDelaySeconds: 10
            timeoutSeconds: 5

Save it as deployment.yaml and create it via kubectl:

kubectl create -f deployment.yaml

Check whether it's running:

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
pushwf-8476855f97-nw4st   1/1       Running   0          1m
$ kubectl logs pushwf-8476855f97-nw4st
App is starting, version: v0

Push to update

Now, update your Go program's version string to v1:

package main

import (
    "fmt"
    "log"
    "net/http"
)

var version = "v1"

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Welcome to my website! Version %s", version)
    })
    fmt.Printf("App is starting, version: %s \n", version)
    log.Fatal(http.ListenAndServe(":8500", nil))
}

Commit and push. In a minute or two (depending on how fast DockerHub can build your image) our app should be updated. Since it's using webhooks, an update should be pretty much instantaneous.

If you visit Webhook Relay dockerhub bucket's page, it should show relayed webhook:

dockerhub webhooks

Let's check our deployments rollout history:

$ kubectl rollout history deployment/pushwf
deployments "pushwf"
REVISION  CHANGE-CAUSE
1         <none>
2         keel automated update, version latest -> latest

And logs, just to be sure that our application is running the latest code:

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
pushwf-74c574f9cf-l6lq2   1/1       Running   0          4m
$ kubectl logs pushwf-74c574f9cf-l6lq2
App is starting, version: v1 

Conclusion

While setting up Keel and Webhook Relay can take several of your precious minutes away, it saves an enormous amount of time later. Not only you get an instant update to your applications based on policies but you also ensure that you won't update wrong cluster or environment by mistake. And, of course, you won't even need to use kubectl for your application updates.

Once Keel is set up in your cluster in can manage many (all) of your applications. When you add your next app to the cluster, just specify the policy and point DockerHub webhook to the same Webhook Relay endpoint. Keel will filter out relevant deployments based on webhook payload and update them.

If you have any questions or find parts of this tutorial incorrect, please raise an issue on Keel's repository here

Example 2: Enable automated semver updates

Once in a while (or more often) you need to update your application that's running in a cloud-native fashion inside Kubernetes.

Let's see how easy it is to do it with Keel.

Keel Quick Start

Install Keel

Installing Keel is the first step, as long as no update policies are defined in your application deployment files or Helm Charts, Keel will ignore them. You can choose your preferred installation type (kubectl or Helm) to deploy Keel, more details are available here.

Specify update policy

Our example app is 'webhook-demo' which pretty much doesn't do anything except registering incoming webhooks and showing them. Deployment file can be found here: https://github.com/webhookrelay/webhook-demo/blob/master/hack/deployment.yml.

While traditional deployment manifest would look like this:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata: 
  name: wd
  namespace: default
  labels: 
      name: "wd"
spec:
  replicas: 1
  template:
    metadata:
      name: wd
      labels:
        app: wd        
    spec:
      containers:                    
        - image: karolisr/webhook-demo:0.0.8
          imagePullPolicy: Always            
          name: wd
          command: ["/bin/webhook-demo"]
          ports:
            - containerPort: 8090       
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8090
            initialDelaySeconds: 30
            timeoutSeconds: 10

We need to add Keel policy for updates and optional trigger type.

These settings have to be specified as annotations:

keel.sh/policy: major
keel.sh/trigger: poll      

Here:

  • keel.sh/policy: major specifies that all - major, minor and patch versions should trigger application update.
  • keel.sh/trigger: poll informs Keel to create an active watcher for the repository that's specified in the deployment file. For private repositories Keel will use existing secrets that Kubernetes uses to pull the image so no additional configuration required. Polling trigger is useful when webhooks or GCR pubsub is not availabe. If you have PUBSUB enabled for your cluster, then you shouldn't use polling trigger.

Now, our deployment file looks like this:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata: 
  name: wd
  namespace: default
  labels: 
    name: "wd"      
  annotations:
    keel.sh/policy: major
    keel.sh/trigger: poll      
    keel.sh/pollSchedule: "@every 30s"
spec:
  replicas: 1
  template:
    metadata:
      name: wd
      labels:
        app: wd        
    spec:
      containers:                    
        - image: karolisr/webhook-demo:0.0.8
          imagePullPolicy: Always            
          name: wd
          command: ["/bin/webhook-demo"]
          ports:
            - containerPort: 8090       
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8090
            initialDelaySeconds: 30
            timeoutSeconds: 10
          securityContext:
            privileged: true

That's it, we update our deployment with new labels, if you already have your app deployed:

kubectl apply -f your-app-deployment.yaml

And then wait for a few minutes till Keel picks up the changes and updates the workload.