Tips for building a Kubernetes proof of concept

Kubernetes' powerful automation features can streamline operations, saving time and costs. Here's how to make a business case for it.
332 readers like this.
How Kubernetes became the solution for migrating legacy applications

Opensource.com

What is the best way to introduce a new technology into your employer's ecosystem? You'd probably start by scheduling a meeting. But what if you're asked what the benefits are, if it will save money, and how it will make developers more efficient?

The answers may be obvious to you, but you need to be prepared to relay this information in a way that makes business sense. It's much easier to explain these benefits when you have a proof of concept.

Why use Kubernetes?

Unfortunately, "because it's cool" isn't a good enough reason to adopt a new technology. That said, Kubernetes is really cool.

There are a ton of use-cases, from hosting your own function-as-a-service (FaaS) to a full-blown application (both the microservices and monolith flavors). Or sometimes you just need a cron job to run once a day—throw the script into a container, and you've got yourself a perfect candidate for the K8s cron job object.

The real question: Will Kubernetes bring business value? As always, it depends. If your main application is already microservice-ish, you can make a good argument that some of the services could be broken off into containers managed by Kubernetes to better utilize those precious CPU cycles. It gets a little tougher when you attempt to shove a monolith into a container—but it is possible.

Another thing to consider is performance. There is a lot of complex networking involved with containerized services in K8s. Your application may suffer a response time increase if you're used to it running all on one machine.

Ok, let's say you've decided it will fit into your use case. What now?

Building the proof of concept

I'm not going to go over the details of deploying a cluster here; there are plenty of guides out there already. Instead, we'll focus on getting something up and running quickly to prove your case. I should also note that there are services available to provide a K8s cluster with minimal hassle: Google Cloud's GKE, Microsoft Azure's AKS, and Red Hat's Openshift. As of this writing, Amazon's service—EKS—is not available to most folks, but it might be the best option in the future if your company is heavily invested in AWS.

If none of those options are feasible for your PoC, you can accomplish a lot with Minikube and a laptop.

What to include in your PoC

So you've got a cluster. What sort of things should you start showing off? Ideally, you'd be able to operationalize a microservice or small app that your team manages. If time is a limiting factor, it's still possible to give a great presentation of an example application being deployed, scaled, and upgraded. In my opinion, the following is a strong list of features to display in your PoC:

  1. A deployment with replicas
  2. A service that exposes a set of pods internally to the cluster
  3. An ExternalName service that creates an internal endpoint for a service outside of the cluster
  4. Scaling those deployments up and down
  5. Upgrading a deployment with a new container image tag

Bonus points if any or all of that can be automated with a CI/CD pipeline that builds and deploys containers with few manual steps. Let's look at some config files that will help you accomplish this.

Sample PoC

For all of these examples, I'll be using the official nginx container image. It would be sufficient to use this in your PoC to demonstrate the functionality of Kubernetes. Of course, if your company already has a containerized service, use that.

Also, a quick note: I'm assuming you have installed kubectl and configured it to communicate with your new cluster or Minikube install. Minikube will actually set up your kube config and context for you.

I'll include the source code of all my example configs in this repo. I have tested these on a Minikube install.

Deployment

We'll start with the YAML, and then we'll dissect the various parts of it. The FILENAME indicator in my code snippets indicate the filename in the repository.

# FILENAME: k8s-configs/nginx-deploy-v1.12.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: poc-nginx-deploy
spec:
  selector:
    matchLabels:
      app: poc-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: poc-nginx
        version: "1.12-alpine"
    spec:
      containers:
      - name: poc-nginx
        imagePullPolicy: Always
        image: nginx:1.12-alpine
        ports:
        - name: http
          containerPort: 80
        livenessProbe:
          httpGet:
            path: /
            port: http
          initialDelaySeconds: 5
          periodSeconds: 3

A deployment object is an abstraction level above pods (and ReplicaSets). Pods are the objects that run a set of one or more containers. You can learn more about them at the Kubernetes website.

Let's talk about the metadata—specifically, labels. Label keys are arbitrary, and you can set them to whatever you want. For instance, you could have objects with labels for the application version number, application name, or application tier. Here we just give app—for the name of our app—and version—where we'll track the currently deployed version of the app. Labels allow various parts of Kubernetes to find out about other parts by matching against their labels. For instance, if we have some other pods already running that are labeled app: poc-nginx, when we apply this deployment for the first time, the spec.selector.matchLabels section tells the deployment to bring any pods with those labels under control of the deployment object.

The spec.template.spec section is where we create the pod definition that this deployment should manage. Containers could have more than one container defined for the pod, but in most cases, pods control only one container. If there are multiple, you are saying that those two containers should always be deployed together on the same node. If one of the containers fails, though, the whole pod will be relaunched—meaning the healthy container will be relaunched along with the unhealthy one. You'll find a full list of the pod spec variables on the Kubernetes website.

One last note on the deployment config: In the container ports section above, I gave Port 80 the name http. This is also arbitrary and optional. You can name the ports anything you want, or exclude the name config altogether. Giving a name to a port allows you to utilize the name instead of the port number in other configs, such as Ingresses. This is powerful because now you can change the port number your pod container listens on by changing one config line instead of every other config line that references it.

Add this deployment to the Kubernetes cluster by running:

kubectl create -f k8s-configs/nginx-deploy-v1.12.yaml

Service

The service config for your nginx deployment would look something like this:

# FILENAME: k8s-configs/nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: poc-nginx-svc
  labels:
    app: poc-nginx
spec:
  type: NodePort
  ports:
  - port: 80
    protocol: TCP
    targetPort: http
  selector:
    app: poc-nginx

As a quick rundown, a service object sets up a single endpoint for inter-cluster communication to a set of pods. With the type set to NodePort, however, it also allows access to the service from outside the cluster if your worker machines are available to your company network. NodePort chooses a random high-level port number between 30000 and 32767 (unless you specify the port it should use) and then every machine in the cluster will map that port to forward traffic your pods.

Notice in the spec.selector section above, you are using the label of your pods (created by your deployment) to tell the service object where traffic should be sent.

Add this service by running:

kubectl create -f k8s-configs/nginx-svc.yaml

External service

This portion of your PoC is optional, and I haven't created a config for this in my example repo. But let's say you have a database cluster in Amazon RDS that you want multiple apps to interact with. To make this easier, you can create a service object of the type ExternalName. All this does is create a CNAME record in your Kube DNS that points the svc endpoint to whatever address you give it. The CNAME can be hit from any namespace with <service_name>.<namespace>.svc.<cluster_name>. Here's an example config:

kind: Service
apiVersion: v1
metadata:
  name: poc-rds-db
  namespace: default
  labels:
    app: poc-db
spec:
  type: ExternalName
  externalName: my-db-0.abcdefghijkl.us-east-1.rds.amazonaws.com

Now when something inside the cluster looks for poc-rds-db.default.svc.minikube over DNS (assuming a Minikube cluster), it will find a CNAME pointing to my-db-0.abcdefghijkl.us-east-1.rds.amazonaws.com.

Accessing the nginx deployment

Now you have a deployment up, with a service allowing you to talk to it, at least within the cluster. If you're using Minikube, you can reach your nginx service like so:

# Take note of the IP address from this command
minikube status
minikube: Running
cluster: Running
kubectl: Correctly Configured: pointing to minikube-vm at 192.168.99.100

# Take note of the port number from this command
kubectl get svc/poc-nginx-svc
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
poc-nginx-svc   NodePort   10.103.171.66   <none>        80:32761/TCP   5s

Using the above examples, you could use your browser to hit http://192.168.99.100:32761 to see your service, which is just the nginx welcome screen at this point.

Scaling and upgrading

The exciting topics of scaling and upgrading are going to be the bread and butter of your PoC. It's so easy to do that, they may even seem anticlimactic. Here is how I would scale up our deployment in a pinch:

# This will take us from 2 replicas to 5
kubectl scale deploy/poc-nginx-deploy --replicas=5

Yeah, that's it. I did say this is how I would do it in a pinch. This is how you can temporarily update a deployment to have more capacity, but if you are permanently changing the number of replicas, you should update the YAML files and run kubectl apply -f path/to/config.yml and of course, keep all your YAML configs in source control.

Now with upgrading, the default upgrade strategy is a rolling update. This will ensure no downtime in your application as it brings up a pod with the new version (or whatever configuration that was changed) before any containers are taken offline.

Let's make a quick adjustment to your deployment in a new YAML file to bump up the image version to 1.13 instead of 1.12. I also keep replicas up at 5 for this version instead of 2:

# FILENAME: k8s-configs/nginx-deploy-v1.13.yaml
apiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: poc-nginx-deploy
spec:
  selector:
    matchLabels:
      app: poc-nginx
  replicas: 5
  template:
    metadata:
      labels:
        app: poc-nginx
        version: "1.13-alpine"
    spec:
      containers:
      - name: poc-nginx
        imagePullPolicy: Always
        image: nginx:1.13-alpine
        ports:
        - name: http
          containerPort: 80
        livenessProbe:
          httpGet:
            path: /
            port: http
          initialDelaySeconds: 5
          periodSeconds: 3

Before you upgrade, open another terminal window and keep an eye on your pods:

watch kubectl get po --show-labels -l app=poc-nginx

Notice I'm making use of labels by passing the -l flag to limit the output to any pod with the label app as poc-nginx Your output should look similar to this:

poc-nginx-deploy-75c8f68dd6-86js8   1/1       Running   0          7m        app=poc-nginx,pod-template-hash=3174924882,version=1.12-alpine
poc-nginx-deploy-75c8f68dd6-pvh2p   1/1       Running   0          7m        app=poc-nginx,pod-template-hash=3174924882,version=1.12-alpine
poc-nginx-deploy-75c8f68dd6-sfkvl   1/1       Running   0          15m       app=poc-nginx,pod-template-hash=3174924882,version=1.12-alpine
poc-nginx-deploy-75c8f68dd6-stcqk   1/1       Running   0          7m        app=poc-nginx,pod-template-hash=3174924882,version=1.12-alpine
poc-nginx-deploy-75c8f68dd6-z6bgz   1/1       Running   0          15m       app=poc-nginx,pod-template-hash=3174924882,version=1.12-alpine

Now, just run the following in another terminal window and watch the magic happen:

kubectl apply -f k8s-configs/nginx-deploy-v1.13.yaml

Endnotes

This is a good start for your PoC, but it is just brushing the surface. I hope this article piques your interest and you dive in. If so, here is some suggested reading from the Kubernetes documentation:

User profile image.
Senior Cloud Platform Engineer @ Fairwinds Ops. Husband. Metalhead. Thoughts/views are my own. https://lreed.net

Comments are closed.

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-Share Alike 4.0 International License.

Download the Open Organization Guide to IT Culture Change

Open principles and practices for delivering unparalleled business value.