Splitting Kubernetes

Kubernetes can be difficult to learn, harder to master. Split also has a few twists and turns. Can we get an application that uses a Split SDK to run in a Kubernetes cluster?

Yes, we can. This article is aimed at being the “simplest possible” set of steps to accomplish this.

Pre-requisites

I run OSX. To run kubernetes on OSX, I used a set of underlying technologies: docker, virtual box, minikube, and kubectl. You can find discussion of installing these pieces in an article, How to Install Kubernetes on Mac.

You also need to have node.js installed (npm and node commands, etc).

After your installation work…

minikube start
kubectl api-versions

If you’ve followed through the installation in the article, you’re prepared to create a new application.

Write your application

In a split-server/ directory, create a new node app.

npm init
npm install --save @splitsoftware/splitio@10.20.0

Or whatever version is latest on the node.js SDK help page.

My sample application just listens on port 7081 and returns some simple HTML. Copy this into an index.js

var http = require("http");

http.createServer(async function (request, response) {
    response.writeHead(200, {'Content-Type': 'text/html'});
    const SplitFactory = require('@splitsoftware/splitio').SplitFactory;
    const factory = SplitFactory({
        core: {
            authorizationKey: 'your split api key'
        }
    });
    let body  = "<html><head><title>Sample Split Page</title></head><body bgcolor=\"white\">";
 
    const attributes = {
        region: 'US'
    };
    const client = factory.client();
    await client.ready();

    const result = client.getTreatmentWithConfig('userid', 'multivariant_demo', attributes);
    console.log(result.treatment);
    const json = JSON.parse(result.config);
    console.log(JSON.stringify(json));	

    body += "<img style=\"width: 60%\" src=\"" + json.image + "\"/><p style=\"font-size: 40px\">" + json.text + "</p>";
    body += "</body></html>";

    response.write(body);
    response.end('');

}).listen(7081);

console.log('Server running at port 7081/');

Edit in your server-side Split API authorization key.

This application uses a dynamic config from a split called “multivariant_demo”. You can use whatever split you like. The app expects each treatment of the split to have a “text” and “image” field. Text can be any string, it’s printed in a paragraph tag. The “image” is expected to be a valid URL pointing to an image file.

For example for a split with red, green and blue treatments:

blue: {"text":"Bring a Pet Home","image":"http://www.cortazar-split.com/dog_melancholy.jpeg"}
red: {"text":"Adopt a  Dog","image":"http://www.cortazar-split.com/dog_origin.jpeg"}
green: {"text":"This dog is chillin'","image":"http://www.cortazar-split.com/dog_on_the_couch.jpeg"}

You can test your app…

node index.js

It prints that it is running on port 7081. If I use my browser to visit http://localhost:7081, I get a dog with text under it.

Dockerize the app

First, you have to create a docker image. To do this, you need to write a Dockerfile.

# syntax=docker/dockerfile:1

FROM node:18.4.0
ENV NODE_ENV=production
WORKDIR /Users/davidmartin/kube-workspace/split-server

COPY ["package.json", "package-lock.json*", "./"]
RUN npm install --production
COPY . .

EXPOSE 7081 

CMD [ "node", "index.js" ]

You’ll have to change your WORKDIR to reflect your own path to the split-server directory (in which index.js is located).

On the Very Idea of a Docker Repository

At this point, there was a great fork in the path for me. Instructions were saying, “Go ahead and start a local directory! It’s no big deal!” But other examples showed using a public repository. I thought, “my stuff is just for me; I’ll stay local…”

It isn’t hard to start a local directory, tag and push to it. The problem is that Kubernetes by default insists on using HTTPS to pull images from the directory. The local directory can apparently be SSL enabled, but it was very complicated. Couldn’t get that to work. Instead, you can introduce the notion of an “insecure registry” to both docker and minikube… 192.168.4.26 was my mac’s IP address.

~/.docker/daemon.json

{
  "insecure-registries" : [ "192.168.4.26:5000" ],
  "debug" : true,
  "experimental" : false,
  "builder" : {
    "gc" : {
      "defaultKeepStorage" : "20GB",
      "enabled" : true
    }
  }
}

Docker can also edit the daemon.json from the UI component in the system tray.

And when starting minikube…

minikube start  --driver=docker --insecure-registry="192.168.4.26"

The problem is that, gosh darn it, there must have been one more place I didn’t find… because kubernetes always insisted it had failed to pull the image because the repository responded HTTP.

I had to give up on local repository.

hub.docker.com

Sign up for a free account.

Create a new repository. I had username dbmartin00 and repository split-server

Build and push the image to docker hub…

docker build -t dbmartin00/split-dbm .
docker push dbmartin00/split-dbm

Now you’re ready for the land of kube.

Create your service and pod description

This is done with a single file I called split-server.yaml

apiVersion: v1
kind: Service
metadata:
  name: split-server
spec:
  selector:
    app: split-server
  ports:
    - port: 7081
      targetPort: 7081
  type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: split-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: split-server
  template:
    metadata:
      labels:
        app: split-server
    spec:
      containers:
        - name: app
          image: dbmartin00/split-dbm
          ports:
            - containerPort: 7081
          env:
            - name: SPLIT_API_KEY
              value: your_split_api_key
          imagePullPolicy: Always

The first section describes a load balancer service. Then a Deployment descriptor. The heavy lifter here is the image name. This is going to be pulled from hub.docker.com

Update your_split_api_key if you’re going to bring your authorization key into the app by environment variable.

Start minikube

minikube start --driver=docker

This takes a minute and has colorful icons. Afterwards you can

minikube status

And get back a bunch of thumbs up.

Now, engage in the act of creation!

kubectl create -f split-server.yaml

Check on yo’ server!

kubectl describe pod split-server

You should see output something like this…

Type  Reason Age From Message
----  ------ ----  ---- -------
Normal  Scheduled  45m default-scheduler  Successfully assigned default/split-server-7544b6fcd4-hhcl2 to minikube
Normal  Pulling  45m kubelet  Pulling image "dbmartin00/split-dbm"
Normal  Pulled 44m kubelet  Successfully pulled image "dbmartin00/split-dbm" in 1.863877267s
Normal  Created  44m kubelet  Created container app
Normal  Started  44m kubelet  Started container app

If you don’t see “Successfully pulled image”, you’re probably in trouble.

Once, I had to use this command to remove some taints.

kubectl patch nodes minikube --patch '{"spec":{"unschedulable": false}}'

Try that and restart your pods:

kubectl rollout restart deployment split-server

If you still have trouble, let’s discuss.

Finish line

If you got this far, you create and started your container app with what looks like success. How do you test it out?

You have to get minikube to poke the port of the service out:

minikube service split-server

With any luck, that gets you a dog in your browser window!

There are a variety of other topics worth discussing, but this is the shortest path to success.

David Brooke Martin
2022

Written with StackEdit.