Get your hands dirty with Kubernetes

Recap..

Kubernetes, the container orchestration service from google is an awesome tool in many ways. It provides a vast array of features that make container deployments and management super easy. In my earlier post, I described how you can get started with Kubernetes. I also mentioned how you can use kubernetes to create replication controllers and service. In this post, I would actually build on the same example and demonstrate some other features that Kubernetes offers to the developer community.

Deployments in Kubernetes

In the previous post, we saw how to create Replication Controllers and Services in an imperative way using the commands of kubectl and minikube. For starters, that’s a great way to get things underway with kubernetes. But, there’s an inherent issue with this approach. It’s mostly manual and command driven. On a single developer team, this approach might work, especially when you are trying to figure out how to use the software and doing some proof-of-concepts per say; but the moment the team grows and the scale of deployment becomes larger you would end up having a situation where the imperative (read manual) way of managing containers using commands proves to be inefficient and simply cannot be managed. OK…so what’s the solution for this? …. …. …. Deployments, a declarative way to manage and update your pods and replica-sets (nothing but next generation Replication Controllers). Deployment object was added in Kubernetes 1.2 to introduce a declarative way to manage state of your resources in a Kubernetes cluster.

Some of the cool features of deployments are as follows — —

You can think ahead of time about the desired state of your resources
Keep track of changes easily by maintaining versions
Toggle between different versions of the resources(including rollback to a previous working/desired version)

For a detailed study on deployments, please visit the official documentation.


Get me some locations

To put deployments to use, let’s first create a back-end micro-service that would populate some countries and cities in a UI. I have already fired up that microservice using spring-boot and here’s how the sample output from curl/postman looks like

http://localhost:9000/location/countriesMock
[
 {
 “geonameId”: “6252001”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: null
 },
 {
 “geonameId”: “2661886”,
 “countryCode”: “SE”,
 “countryName”: “Sweden”,
 “name”: null
 },
 {
 “geonameId”: “2921044”,
 “countryCode”: “DE”,
 “countryName”: “Germany”,
 “name”: null
 },
 {
 “geonameId”: “6251999”,
 “countryCode”: “CA”,
 “countryName”: “Canada”,
 “name”: null
 }
]

Similarly, another REST endpoint provides the list of cities for a given country:

http://localhost:9000/location/citiesMock?geonameId=6252001
[
 {
 “geonameId”: “4083805”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “Plano”
 },
 {
 “geonameId”: “4058069”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “Dallas”
 },
 {
 “geonameId”: “4063993”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “Georgetown”
 },
 {
 “geonameId”: “4085986”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “Richardson”
 },
 {
 “geonameId”: “4058738”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “Detroit”
 },
 {
 “geonameId”: “4096463”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “Washington”
 },
 {
 “geonameId”: “Francisco”,
 “countryCode”: “US”,
 “countryName”: “United States”,
 “name”: “San Francisco”
 }
]

Simple enough thus far. Before we can run this in Kubernetes, we need to make this a container. So we oblige by dockerizing the app using spotify’s docker-maven-plugin and push it to docker hub. Here’s the pom.xml file in case you need to see how to use the docker maven plugin.

I actually created two versions(demo and demo-v2) of the same location service; this will come in handy shortly when we explore the deployments versions. My docker repo shows the same thing:

Next task is to run this image as a Kubernetes deployment.

Declarative (yml) files

You can define the desired state of your deployment by writing a declarative .yml file for your deployment. Here’s mine for our current use case:

location-deployment.yml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: location
spec:
 replicas: 3
 selector:
 matchLabels:
 name: location
 template:
 metadata:
 labels:
 name: location
 spec:
 containers:
 — name: location
 image: chakrar27/locationservice:demo-v2
 ports:
 — name: location-port
 containerPort: 9000

The above file tells Kubernetes to create a deployment object (See how we mention the Kind attribute to specify that it’s a Deployment) and with it 3 replica sets. It assigns some metadata to the object and specifies the name of the docker image to run. For a full description of how to write a .yml file for deployments please refer to this page.

Use Kubectl to create the deployments:

C:\kubectl>kubectl create -f location-deployment.yml
deployment “location” created

Soon after if you check for the pods, you should see

C:\kubectl>kubectl get pods
NAME READY STATUS RESTARTS AGE
location-1648611014-ck8rb 1/1 Running 0 25s
location-1648611014-klg3m 1/1 Running 0 25s
location-1648611014-t7lj2 1/1 Running 0 23s

Create the Service

Once the deployment has been created, next task is to create the service out of it. We are going to use the same declarative approach here as well. Here goes the .yml file for the corresponding service creation.

location-nodeport-service.yml

apiVersion: v1
kind: Service
metadata:
 name: location-service
spec:
 ports:
 — port: 32157
 nodePort: 32157
 targetPort: location-port
 protocol: TCP
 selector:
 name: location
 type: NodePort

See how we specify the value for the “nodeport” attribute and Kind as “Service”. For details about how to write a .yml file for a service, check out this link.

Run it through Kubectl command and you get

C:\kubectl>kubectl create -f location-nodeport-service.yml
service “location-service” created

Now. is a good time to have a look at the services that are running inside the cluster.

C:\kubectl>kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 <none> 443/TCP 1d
location-service 10.0.0.59 <nodes> 32157:32157/TCP 6h

One important thing to note here is the Cluster-ip for the “location-service”. It’s an ip that’s non-static, meaning that if you happen to delete the service and recreate it, the ip will change. If you need a static ip, then you would have to specify it explicitly in the service.yml file.

We can now describe our location-service:

C:\kubectl>kubectl describe service location-service
Name: location-service
Namespace: default
Labels: <none>
Selector: name=location
Type: NodePort
IP: 10.0.0.59
Port: <unset> 32157/TCP
NodePort: <unset> 32157/TCP
Endpoints:
Session Affinity: None
No events.

To access the service externally, do this

minikube service location-service — url=true
http://192.168.99.102:32157.

You can now test the endpoint from either curl or postman or any other tool.

Versioning and Rollbacks

One key advantage that Deployments offers is the ability to version your desired state and the ability to undo/rollback a deployment and also switch to a specific version if needed. This is a great feature to have at your disposal.

If you notice closely, I have used the version 2 of the image as my container in the deployment.yml file above. The output from the version 2 of the microservice is an additional country (Canada) being added in the response and some additional cities for country United States; whereas the version 1 of the app doesn’t return “Canada” and also returns only four cities for USA.

I built a pretty naive UI to test my service. Here’s how the UI looks like with version2 (remember this is the currently deployed version)

Now, let’s use the original version of the app image to see the difference in behavior:

C:\kubectl>kubectl set image deployment/location location=chakrar27/locationservice:demo
deployment “location” image updated

Now. let’s check the rollout status of the deployment.

C:\kubectl>kubectl rollout status deployment/location
deployment “location” successfully rolled out

If we check the pods now, we should see some new pods being spawned

C:\kubectl>kubectl get pods
NAME READY STATUS RESTARTS AGE
location-805752305-qgx22 1/1 Running 0 55s
location-805752305-t1swn 1/1 Running 0 1m
location-805752305-xgr19 1/1 Running 0 1m

By looking at their ages, you can see that these pods were created afresh.

We can check the deployment history at this point

C:\kubectl>kubectl rollout history deployment/location
deployments “location”
REVISION CHANGE-CAUSE
1 <none>
2 <none>

The CHANGE-CAUSE column is showing nothing here since didn’t record any changes when creating the deployments.

OK…let’s test out our original version (version 1 from now on) to see the difference

All right, we can see that country list is not showing “Canada” any more and also list of US cities is a smaller one than in version 2. So in effect, we manged to deploy a different version of the microservice in a matter of minutes.

Wait, this ain’t over just yet. Next we are going to see how we can undo the current deployment and roll back to the previous version. Here’s how you do it ->

C:\kubectl>kubectl rollout undo deployment/location — to-revision=1
deployment “location” rolled back

And then check the status of the deployment

C:\kubectl>kubectl rollout status deployment/location
deployment “location” successfully rolled out

The UI at this point should show the new country being added back into the list.

And the app does exactly that. So now, we have successfully rolled back our deployment to the initial version (version 2). I know this whole versioning business is getting out of hand, but please bear with me.

You can also add Labels to your deployment objects. Labels are key value pairs and offers a way of tagging resources. This is useful when you need to segregate resources based on environment type (such as dev/stage/prod) or other groupings (department/team for instance). To see the labels associated with the pods, just add “ — show-labels” at the end of the command. Here’s one example:

C:\kubectl>kubectl get pods — show-labels
NAME READY STATUS RESTARTS AGE LABELS
location-805752305-qgx22 1/1 Running 0 9m name=location,pod-template-hash=805752305
location-805752305-t1swn 1/1 Running 0 9m name=location,pod-template-hash=805752305
location-805752305-xgr19 1/1 Running 0 9m name=location,pod-template-hash=805752305

The “name” is showing “location” as its value since we declared it in the .yml files mentioned above.

Using Kubernetes Web UI

So far, we have seen how you can create application resources using a declarative approach. However, for a newbie to the Kubernetes world, there’s a powerful web UI that does the same thing. Of course it’s not as efficient as the .yml file templates when it comes to versioning the state of your infrastructure and deployment resources, but it has its niceties and something that is useful when you want a wizard like flow without going into the details of the .yml file templates. Here’s how the UI looks like

As you can see, you can provide the details for your setup and the UI creates the deployment for you. You can create deployments, pods, services, replication controllers and other resources using this user interface.

Troubleshooting

One issue you might encounter while trying to create deployments and services is because of the mismatch in versions of Kubectl client and server. If you encounter the following error while creating a deployment

unable to load "kubernetes/deployment.yaml": json: cannot unmarshal object into Go value of type string"

then please check the client and server versions in your Kubectl instalaltion.

C:\kubectl>kubectl version
Client Version: version.Info{Major:”1", Minor:”1", GitVersion:”v1.1.3+$Format:%h
$”, GitCommit:”$Format:%H$”, GitTreeState:”not a git tree”}
Server Version: version.Info{Major:”1", Minor:”5", GitVersion:”v1.5.3", GitCommi
t:”029c3a408176b55c30846f0faedf56aae5992e9b”, GitTreeState:”clean”}

There’s a clear mismatch in the minor versions of the client and the server and that’s the reason you would get the “unmarshall” error.

Fix: Download the latest Kubectl windows version 1.5.0 from this link. After pulling my hair apart for some time this actually solved the issue for me. You can take a look at GitHub issue filed with Kubernetes about the same problem. Hope this helps.

One last thing…the big fat kubernetes web…don’t let it awe you

Kubernetes is a powerful tool; however it can by pretty dry and overwhelming at times with all its nitty-gritties. Often it becomes a wee bit difficult to find your way into the Kubernetes landscape of massive tool-sets and their usage; even to the extent that sometimes your head would hurt going through all the jargon and their feature-sets and trying to figure out which one to use. Relax, this is a common syndrome; just hang on, give yourself a little bit of time and your perseverance will see you through.