How to Automate Scripts with Golang and CronJobs using Kubernetes

August 27, 2020
Written by
Chinenye Ogbuchiekwe
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by
Diane Phan
Twilion

header - How to Automate Scripts with Golang and CronJobs using Kubernetes

Oftentimes when developing scripts with the intent of running them forever on a server, I struggle with making the system robust. There have been countless times where I would have a script running for days or even weeks, but due to unforeseen bugs, I had to restart or update my server. Well, there is a handy tool called CronJobs, and this may sound intimidating, but trust me if I can do it you can too!

Essentially, a CronJob is a utility that automatically runs some type of task periodically on a recurring basis, we will be combining CronJobs with Kubernetes. Minikube is a tool made by Kubernetes which I like to think of in terms of a “big server” and a “baby server”, this tool is run locally and runs a single cluster. Kubernetes runs clusters which is what I refer to as a big server that deploys pods which can be thought of as a baby server. Our big server will deploy out many baby servers and each baby server will serve one purpose. In our case, the one purpose our baby server will serve is to be a CronJob!

That was a lot of information to digest, take a moment to enjoy a gif of this french bulldog!

oscar frenchie dog halloween puppy bear GIF

Get started

Install Golang

Head over to the Golang website and download the Golang installer for your system. For installation details for your system check out the installing Golang docs.

Install Minikube

If you already installed Homebrew then run a brew install command to install Minikube:

 brew install minikube

If HomeBrew isn’t installed on your Mac you must install Minikube via direct download with this command:

curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64 \
  && chmod +x minikube

Add the Minikube executable to your path using the command:

sudo mv minikube /usr/local/bin

If you are using a Windows machine, run the following commands:

choco install minikube

Checkout the documentation to install Minikube for further instructions on how to install Minikube on your machine.

Install Kubectl

Now that we have Minikube installed we need to install the Kubernetes command-line tool to run commands on clusters.

For Homebrew users, type the following:

brew install kubectl
Kubectl version --client

Without Homebrew, use this command:

curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl

If you are using a Windows machine, run the following command:

curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.18.0/bin/windows/amd64/kubectl.exe

Install Docker

To package everything together, we will need to install Docker.

Visit the Docker site and download the appropriate client for your OS.

kid saying Fun Fun Fun GIF

Lights, Camera, Action!

Now it's time to have some fun and create your first CronJob. Make a new directory to hold the project files. Run the command mkdir {insert-dir-name} or create a directory with your IDE then run the command cd <dir-name> to change into that directory.

In the previously created directory write a basic script also known as a job. I named mine hello.go but, you can name yours whatever you want. Since this article pertains to running a script automatically we will build a basic “hello world” script. This script's job is to print a starting job message, “hello world”, and a stopping job message.

In general jobs are scripts that you’d like to run on their own. Other examples of jobs could be hitting an endpoint or making various requests. There really isn’t a limit to what a job can be.

package main
import (
        "fmt"
        "time"
)
func main(){
        fmt.Println("Starting Job")
        fmt.Println("hello world")
        time.Sleep(30 * time.Second)
        fmt.Println("Stopping Job")
}
Nick Young Meme GIF question marks

You’ll have to bear with me for a moment as I need to explain one last dependency called Docker. Since the main purpose of this is to teach one how to run a script automatically with Minikube & CronJobs I won’t go into too much detail but I know I owe you a quick summary.

Docker is a virtualization technology that makes it easy to create and deploy apps in a neatly packaged container environment.

Create a file with the name “Dockerfile”. This will package up your “hello world” script and allow it to be run on an image. An image is a template with instructions on creating a container. You may simply use the example below and your Dockerfile will be set! If you would like to make your own Dockerfile however, you can find more information in the Docker documentation.

Create a file in your directory and name it Dockerfile. Copy and paste the following text below into your file:

FROM golang:alpine
RUN mkdir /app
ADD . /app/
WORKDIR /app
RUN go build -o main .
RUN adduser -S -D -H -h /app appuser
USER appuser
CMD ["./main"]

Configure a CronJob

Last but not least we need to configure a CronJob which will be a yaml file. I named it job.yaml but you can name it anything you want with the extension .yaml.

Inside of this yaml file we will tell our pod to run our image at a certain time and name our CronJob “simplejob” in the name field. We will be running our image every minute, which can be stated in the schedule. You can put any timing you want in the schedule field but, for the purpose of testing I suggest leaving it at one minute. For more information about how to format timing checkout CronJob timing.

Refer to the text fields from the yaml file below as an example of what your yaml file should look like or feel free to copy and paste what's below. Check out this site for an introduction to yaml files and pod templates.  

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  creationTimestamp: null
  name: simplejob
spec:
  jobTemplate:
    metadata:
      creationTimestamp: null
      name: simplejob
    spec:
      template:
        metadata:
          creationTimestamp: null
        spec:
          containers:
          - image: hello:latest
            imagePullPolicy: IfNotPresent
            name: simplejob
            resources: {}
          restartPolicy: OnFailure
  schedule: '*/1 * * * *'
status: {}

Run your first automated script

Now that we have everything set up, let's see if we can get things to work. You'll run the following commands in a terminal or a command shell.

Start Minikube by opening up the terminal and running the following command:

minikube start

Set the environment variable by running the following command in your terminal:

eval $(minikube docker-env)

Build a local Docker image with the tag hello.

docker build -t hello . -f Dockerfile

Create and Update Resources in your cluster. You may have to specify the whole path of your job.yaml.

kubectl apply -f job.yaml

You can expect to see this output:

CronJob.batch/simplejob created

Watch your pod spin up with this command. If you wait thirty seconds after the pod is created, you will also see the pod terminate:

kubectl get pod --watch

In a separate terminal window or tab, check your first pod’s logs in order to see your CronJobs output. Here’s an example of the command you need to type:

kubectl logs simplejob-1597939920-dchqd

The name above is specific to my Kubernetes cluster. Yours will be different based on your CronJob’s name.

After running all these commands you should be able to see this sample output!

Starting Job
hello world
Stopping Job

It's important to clean up your CronJobs when you no longer want them to run, this can be done with the commands below. If you do not delete the CronJob it will run forever.

kubectl delete CronJob simplejob
minikube stop
My Work Is Done Reaction GIF by SpongeBob SquarePants

I know this might have seemed like a lot of setup but that's just a part of the beautiful and complex world of software engineering. However, if you’re still looking for another explanation of CronJobs, this video helped me understand the concept better during my internship.  Sometimes things like this can be tricky and I understand how hard it can be but, hopefully this gives you some insight as to how all these tools work together. I knew you could do it and I am glad you stuck through it, as a wise person once said “easy peasy lemon squeezy”!

Other ideas you can try with CronJobs:

Chinenye Ogbuchiekwe is an intern on Twilio's Email Infrastructure team. He believes that the ability to code is extremely empowering because it allows him to pursue any project his mind can think of. His goal is to empower himself and those around him. Get in touch with Chinenye over LinkedIn.