Event-driven scripting in Kubernetes with Brigade

In most projects that I’ve been part of, sooner or later the need for various types of automation jobs arises. For example cleaning up old files, moving database backups, running health checks or system tests and so on.

Historically we’ve implemented these tasks using for example the Windows task scheduler, or through some custom Windows Service app. More recently, we’ve been using Azure Automation jobs for this. Sometimes it can also make sense to use CI/CD automation tools like Azure DevOps for these jobs.

With the move to containers and Kubernetes, it can make a lot of sense to use that platform not just for the business apps that you are developing, but also for these type of automation workloads. It means that you don’t have to invest and manage another platform, and you can leverage existing and 3rd part container images to build automation workflows.

Brigade

Brigade is a platform that makes it easy to create simple or complex workflows that run on Kubernetes. You use Docker containers as the basis for each step in the workflow, and wire them together using Javascript.

Brigade

Brigade is an open-source project, read more about it at:
https://brigade.sh/

Brigade runs on any vanilla Kubernetes cluster,  you don’t need anything extra installed to run brigade pipelines.

Installing Brigade is as easy as running the following two commands:

helm repo add brigade https://brigadecore.github.io/charts
helm install brigade/brigade --name brigade-server

The image below shows the main concepts in use by Brigade:image

Project
For every automation workflow that you want to implement, you will create a project. Every project has some metadata attached to it, such as id, name and so on. It also either contains or reference the Javascript code that contains the pipeline logic.

Build
A build is created every time a script is triggered, through some  external event. The build runs until all jobs are finished, and you can view the output logs from the running build as well as after it finished.

Job
Each build will contain one or more jobs. For each job, a container instance is started, and then a series of tasks is executed inside that container. You specify the jobs and the tasks in the Javascript code, and how the jobs should be scheduled.

Gateway
A gateway transform outside triggers (a Git pull request, a Trello card move etc) into events, that is passed into the pipeline where you will handle them in your code.

Brigade comes with a Generic gateway that listens and accepts POST JSON messages on any format (it also explicitly supports the CloudEvents format). In addition, there are several custom gateways that makes integration a lot easier with services such as GitHub, Docker Container Registry or Azure Event Grid.

A basic “hello-world” type of Brigade pipeline can look like this:

const { events, Job } = require("brigadier");

//Handler for exec event
events.on("exec", () => {

  var job = new Job("say-hello", "alpine:3.8");
  job.tasks = [
    "echo Hello",
    "echo World"
  ];

  job.run();
  
});

Here, the pipeline is triggered by the exec event, and inside that event handler it starts a new job called “say-hello” which contains two tasks where each task just prints a message. The job is executed inside a container from the alpine:3.8 image, that will be downloaded from Dockerhub and started automatically for you. Of course you can use any public image, or a private image from your own container registry.

Brigade has excellent documentation, I encourage you to read up on it more at https://docs.brigade.sh/

In this post I will show a slightly more complex example, that is taken from a recent customer project where we developed a microservice application running on Kubernetes, and found the need for some extra automation.

Removing Kubernetes environment on PR completion

Kubernetes makes it easy to create new isolated environments for your application when you need to. A common desire of many teams is to deploy the application into a fresh environment every time a pull request is created. This lets the team and stakeholders test and verify the feature that is being developed, before it gets merged into the master branch.

Using Azure DevOps, it’s quite easy to setup a release pipeline where every PR is deployed into a new namespace in Kubernetes. You can enable stages in a pipeline to be triggered by pull requests, and then use information from that PR to create a new namespace in your Kubernetes cluster and then deploy the app into that namespace.

The problem we experienced recently at a customer with this was, how can we make sure this namespace (and everything in it) is removed once the PR is complete and merged? We can’t keep it around since that will consume all the resources eventually in the cluster, and we don’t want to rely on cleaning this up manually.

This turned out to be a perfect case for Brigade. We can configure a service hook in Azure DevOps, so that every time a PR is updated we trigger a Brigade pipeline. In the pipeline we check if the PR was completed and if so, extract the relevant information from the PR and then clean up the corresponding namespace. To do this, we used existing container images that let us run helm and kubecl commands.

The Brigade script looks like this:

const { events, Job } = require("brigadier");
const util = require('util')

const HELM_VERSION = "v2.13.0"
const HELM_CONTAINER = "lachlanevenson/k8s-helm:" + HELM_VERSION;

const KUBECTL_VERSION = "v1.12.8";
const KUBECTL_CONTAINER = "lachlanevenson/k8s-kubectl:" + KUBECTL_VERSION;

events.on("simpleevent", (event, project) => {
    const payload = JSON.parse(event.payload);
    const prId = payload.resource.pullRequestId;

    if (!payload.resource.sourceRefName.includes('/feature/') && !payload.resource.sourceRefName.includes('/bug/')) {
        console.log(`The source branch ${payload.resource.sourceRefName} is not a /feature/ or /bug/ and is therefore skipped.`)
        return;
    }

    if (payload.resource.status !== "completed" && payload.resource.status !== "abandoned") {
        console.log(`PullRequest not complete or abandoned (current status: ${payload.resource.status}).`);
        return;
    }

    var helm_job = new Job("helm-delete-release", HELM_CONTAINER);
    helm_job.env = {
        'HELM_HOST': "10.0.119.135:44134"
    };
    helm_job.tasks = ["helm init --client-only", `helm delete --purge samplewebapp-${prId}`];

    var kubectl_job = new Job("kubectl-delete-ns", KUBECTL_CONTAINER);
    kubectl_job.tasks = [`kubectl delete namespace samplewebapp-${prId}`];

    console.log("==> Running helm_job Job")
    helm_job.run().then(helmResult => {
        console.log(helmResult.toString())

        kubectl_job.run().then(kubectlResult => {
            console.log(kubectlResult.toString());
        });
    })
});

events.on("error", (e) => {
    console.log("Error event " + util.inspect(e, false, null))
    console.log("==> Event " + e.type + " caused by " + e.provider + " cause class" + e.cause + e.cause.reason)
})

events.on("after", (e) => {
    console.log("After event fired " + util.inspect(e, false, null))
});
 

This code is triggered when the “simpleevent” event is triggered. This event is handled by the generic gateway in Brigade, and can be used to send any kind of information (as a json document) to your pipeline. To trigger this event, we configure a service hook in Azure DevOps for the Pull Request updated event, and point it to the generic gateway:

SNAGHTMLae0651d[4]

The full URL looks like this:

https://brigadedemo.ehn.nu/simpleevents/v1/brigade-55cbf57f7aaeb59afa1fe4d33ca6a5a635eefe060b057c423c97a0/somesecret

The URL contains the project id and the secret that were specified when creating the project. This is how external requests is authenticated and routed to the correct brigade script.

Inside the event handler we use two different container images, the first one is for running a Helm command to delete the Kubernetes deployment. Since Helm can’t delete the namespace, we need to run a second job inside another container image that contains the Kubectl tool, where we can delete the namespace by running

kubectl delete namespace samplewebapp-${prId}`

The prId variable is parsed from the PullRequest updated event coming from Azure DevOps. We use the id of the pull request to create a unique namespace (in this case pull request with id 99 will be deployed into the samplewebapp-99 namespace).

NB: You will need to make sure that the service account for brigade have enough permission to delete the namespace. Namespaces are a cluster level resource, so it requires a higher permission compared to deleting a deployment inside a namespace. 

One easy way to do this is to assign a cluster-admin role to the brigade service account, this is not recommended for production though.

Now, when a PR is complete, our pipeline is triggered and it will delete the deployment and then the namespace.

To view the running jobs and their output, you can either use the brigade dashboard (called Kashti) by running brig dashboard or you can install the brigade terminal which will give you a similar UI but inside your favourite console.

Here is the output from the PR job in the brigade terminal:

image

It shows that two jobs were executed in this build, and you can see the images that were used and the id of each job. To see the output of each job, just return into each job:

image

Here you can see the the output of the helm job that deletes my helm deployment for the corresponding pull request.

Summary

I encourage you to take a look at Brigade, it’s easy to get started with and you can implement all sorts of automation without having to resort to other platforms and services. And although Javascript might put some people off, the power of a real programming language (compared to some DSL language) pays off when you want to implemtent something non-trivial.

If you already are using Kubernetes, why not use it for more things than your apps!

Thanks to my colleague Tobias Lolax (https://twitter.com/Tobibben) who did the original implementation of this for our customer.

My Speaking Year 2019

When I set out my goals for 2019, one of them was to speak at new conferences. Up until 2018, I had only spoken at conferences in Sweden (like DevSum, TechDays and SweTugg). While these conferences are great, I felt that I wanted to raise the bar a bit and try to visit other conferences, including conferences abroad. And as it turned out, I reached my goal!

I thought that it would be nice to sum up my speaking year in a blog post, with some comments about each comference and a picture or two.

However I want to start this post by (once again) give a big shout out to my employer Active Solution. While I do spend a lot(!) of my spare time preparing talks and travelling to and from conferences, Active Solution is what makes all this possible by allowing me to use some of my work time for speaking and community related work, and for creating an environment at work where visiting conferences is a natural part of our core activities.

Active Solution works very strategically with regards to developer conferences and meetups. We host a lot of different meetups at our office, and we also very often sponsor and/or exhibits at the three largest conferences for Microsoft/.NET developers in Sweden (DevSum, TechDays and SweTugg, see more about these conferences below). Doing this allows us to meet face to face with a lot of developers. We also have a group of people who have a passion for sharing their knowledge through public speaking, allowing us to share experiences with each other and give feedback on each others talks, CFP’s etc.

Image
A typical Active conference booth, with competitions and nice give aways

Let’s walk through my speaking activities for 2019(not including several smaller meetup talks) for some highlights.

WinOps 2018 London

As you’ll note, this conference was actually at the end of 2018, but since it was my first conference abroad I’ll include it here Smile

WinOps is a two-day conference in London that focus on DevOps for Windows. There are a lot of DevOps conferences out there, but this is one of the few (if not the only one) with this focus. It’s not a very big conference, but I was very pleasantly surprised by the quality of the presentations that I saw, and the friendly atmosphere of the whole event.

My talk was about running Kubernetes in Azure, using Azure Kubernetes Service (AKS), and was very well received. You can tell from the questions afterwards if a talk was appreciated or not, in this case I had a lot of questions and interesting discussions afterwards.

For this conference I brought my 11-year old son Svante with me as company. He joined me for my session (not focusing too much on it though Smile ) and after the conference we stayed for two nights more in London and expored this fantastic city, where the christmas lights had just been lit up. Among other things we enjoyed a proper afternoon tea at the Dorchester hotel at Hyde Park.

winops1
Svante in a nice WinOps t-shirt

winops2
Ready for some afternoon tea!

NDC London

Being accepted to an NDC conference has definitely been on my bucket list for some time, and finally it happened. NDC London accepted my talk “A Lap around Azure DevOps” which is basically an hour of demos, where I try to show as much as possible how team can be more productive with Azure DevOps.  Unfortunately I had some network problems, so some demoes were a bit slow but I think that overall the talk was well received and I managed to finish all the demos within the hour.

Here is a link to the recording of this session:
https://www.youtube.com/watch?v=N78NxZ-cKUc

NDC is well known for organizing great conferences and taking care of their attendees and speakers. I enjoyed hanging out with the other speakers during the conference.

ndclondon

MVP Summit (Seattle)

So, the MVP Summit is a special conference in this context since it’s not really about presenting anything but instead meeting with the product teams at Microsoft and learn and discuss current and future investments and roadmaps together with them and all the other MVP’s from around the world.

However, I did do a short presentation during the “MVP2MVP day” which is a long tradition of the ALM/DevOps MVP group, where we meet on the sunday before the summit begins, and share knowledge with each other in a packed day. Typically there are 20 minute sessions going on from 10AM to 5PM with a short lunch break, and is great fun. A big kudos here to Brian Randall and Neno Loje who are the master minds behind this day! 

Although my MVP award was recently moved from the ALM category to the Azure category, I’m still hanging out with this amazing group of people that I’ve come to know through my 8 years of being an MVP.

mvpsummit1
Celebrating TFS (mow Azure DevOps) on it’s 13th birthday Smile

mvpsummit2
During the summit I had my avatar drawn live with the one and only @reverentgeek (David Neal)

DevSum (Stockholm)

DevSum is the biggest .NET conference in Sweden, and 2019 was my 5th year in a row to speak at this conference. Active solution has been a proud sponsor at this event and we always have a nice booth where we try to combine cutting edge technologies with some  fun competitions!

This time, I did my “A Lap around Azure DevOps” talk again. It’s always nice to deliver a session more that once, it allows me to refine the presentation and make it a little bit better than last time.

Of course, things are changing so every time I redeliver a talk I end up changing both slides and demos in order to incorporate new things. This time I had no Internet problems so all the demos went as planned!

devsum

Ignite Tour Stockholm

208/2019 saw the first edition of Ignite The Tour running around the world. Microsoft took their big Ignite conference in tour, together with speakers from Microsoft and also local community speakers on each location. I submitted a couple of talks to Ignite Tour in Stockholm and got two talks accepted:

Continuous Delivery with Azure Kubernetes Service
In this talk I showed how to implemented CD techniques like A/B and Canary testing using Kubernetes and AKS.

Keeping your builds green using Docker
This talk was based on work that I’ve been doing for my current customes during the last year, where we have used Docker as the basis for the build environment. Instead of relying on manually patched servers with flaky behaviour, we moved everything to Docker which gives as Infrastructure as code and full isolation which is great when you want to have fully repeatable builds. I also talked about and showed how you can use Docker for building your applications, which has several advantages.

ignite2
Talking about Kubernetes and AKS

ignite1
Using Docker for build automation

NDC Sydney

Without a doubt, the biggest thing for me last year was being accepted to NDC Sydney. Travelling to Australia is something that I always wanted to do, so having this opportunity was nothing but amazing. Of course, trafelling to Sydney from Stockholm is a VERY long trip, so I made sure to add some vacation before and after the conference so that I was able to explore the beatiful city of Sydney.

Of course, following the news on the fires in Australia and around Sydney these last couple of months has been very painful to watch, probably even more so since I visited it so recently.

At the conference, I delivered once again delivered the “Keeping your builds green using Docker” talk, which went very well.

Here is a link to the recording:
https://www.youtube.com/watch?v=ekNSwDS1ya4

sydney1
Visiting friend and fellow MVP Adam Cogan and his wife Anastasia over at Coogee beach

sydney2
A mandatory shot of the Opera house in the Sydney harbour

sydney4

Registration opens at NDC, which

sydney5Heather Downing opened the conference with a keynote on how to treat and motivate your software engineers

sydney6The (in)famous PubConf was held on friday night after the NDC conference ended


Beautiful night skyline of Sydney

TechDays Sweden

Another big Microsoft conference in Sweden is TechDays Sweden, which celebrated 10 years in 2019. TechDays is a big conference with almost 2000 participants. Usually there is around 10 different tracks with a mixture of IT/Operations and Developer tracks.

This time, I coordinated a bit with my colleague Chris Klug (@zerokoll). Since we are both working with and speaking a lot about Docker and Kubernetes, we decided to make sure that our sessions didn’t overlap but instead built on each other. So Chris did a session that introduced Kubernetes for developers, and I did a session about “DevOps with Azure Kubernetes Service”, where I showed how to setup a CI/CD pipeline, how to make sure that your AKS cluster and applications are secure , compliant and highly available.

techdays1
Waiting for everyone to take their seat

UpdateConf (Prague)

The Czech Republic is a country that I had never visited before, so I was very glad when I was accepted to speak at this conference in Prague. Unfortunately, as it turned I had to rearrange my travels a bit so I didn’t really have any time to visit the city so I sure hope to come back again!

At UpdateConf, I delivered a new session that is called “Event-driven computing with Kubernetes”, where I talk about some open source tooling that let’s you implement event-based automation and scaling, Brigade and Keda. 

prag1
My colleague Cecilia Wiren (@ceciliasharp) about to start one of her sessions

prag2Me speaking about Brigade and Keda

prag3
Action pic Smile

CloudBrew (Mechelen, Belgium)

The last conference of the year for me was CloudBrew. I have heard so many good things about this community conference over the years so I was naturally thrilled when I was accepted to speak at this conference. And all the good things I heard turned out to be true, or better. The conference has grown a lot over the years, this year there were around 400 attendees which was twice as much as the year before. The crew from the Belgian Azure user group (AZUG) does an amazing job with this conference, everything worked flawlessly and I met a lot of new people at this conference, all with a passion for Azure in common.

I delivered the session about event-driven computing with Kubernetes again, this time with no other than Clemens Vasters in the audience, who of course is the father of the Messaging services in Azure. He has recently been involved in the work around Keda and cloud events which I covered in this talk.

cloudbrew3Alex Mangs opened the conference with a keynote looking at the future of Azure

cloudbrew2
Time to talk about Brigade and Keda again

cloudbrew4
My colleague Alan Smith preparing for his session

cloudbrew1
A great speaker dinner with both old a new friends

Summary

From a speaker perspective 2019 was an amazing year where I experienced so much and met so many new people. I can only hope that 2020 will bring some of the same experiences for me.

As with everything else, the more you prepare and speak at conferences the better you will become. For me, public speaking doesn’t really come naturally but I do feel that I have improved it over the years. 

I keep my list of upcoming (and past) speaking engagements updated here:
https://blog.ehn.nu/speaking/

Hope to see you at a conference soon!