I found this hashnode article to be a great introduction to docker. It shows us how we can dockerize our app and run it as a container. I thought I'll put up a follow-up blog post which will explain how to deploy and manage these containerized apps. Enter kubernetes!
What is kubernetes?
Let's look at the current scenario, we have an app which is dockerized and ready to be deployed and Let's say we have some servers at our disposal. Kubernetes is a software which can be installed on these servers, which then allows us to deploy containers(our apps) on them. Kubernetes allows us to treat this group of servers as one entity and interact with it. Our group of servers will be called a cluster. We can interact with the cluster using the API it provides.
For the rest of the blog post, I'll try demystifying kubernetes part by part. For now, Let it be a black box. This black box has an API which allows us to interact with it. Let me introduce one utility -
kubectl. It is a CLI which allows us to interact with the cluster. Please find the instructions to install it here.
Let's try to set up a kubernetes cluster for us to play with. All of the cloud providers now provide a managed kubernetes service and most of them provide free credits to get started. Getting a cluster up and running in these cloud environments is well documented and pretty easy. The following are some links which can guide you to get one up and running.
If you want to set up a local cluster you can go with
I'll use a digitalocean cluster for this tutorial. I created one by following the documentation. The next step is to configure
kubectl to talk with our cluster. For this, I've to download the config file for our cluster and configure
kubectl to use this config file.
Now I can set an environment variable
KUBECONFIG to the absolute path of the downloaded file and then,
kubectl will be configured to use the downloaded config file and can connect with our cluster.
/Users/aravindkp/Downloads ✘-1 15:05:32 ▶ export KUBECONFIG="$(pwd)/test-cluster-blog-post-kubeconfig.yaml"
We can confirm that the setup was successful by issuing the following command.
kubectl get nodes to list the nodes in our cluster.
/Users/aravindkp/Downloads ✔ 15:05:51 ▶ kubectl get nodes NAME STATUS ROLES AGE VERSION test-cluster-blog-post-1-7p0g Ready <none> 3m59s v1.13.5
I created a single node cluster for this blog post. In a production environment, it is recommended to create more than 3 nodes for High Availability.
Once you have kubectl configured to interact with your cluster you can start communicating with the cluster and start deploying your containers.
Managing kubernetes can be done declaratively or imperatively, to learn more about the differences between them, refer here. We'll use the declarative model here. In this model, we declare how the system should be and kubernetes will take the necessary steps to achieve that state of the system.
Now let's deploy an App, we'll use the
https://httpbin.org/ as an example here.
The first kubernetes object that we are going to create is called Deployment. Kubernetes uses
yaml files to specify objects. We'll create a
deployment.yaml file and fill in the following details.
apiVersion: apps/v1 kind: Deployment # Specify the type of object to create metadata: name: httpbin # Name for object labels: app: httpbin spec: replicas: 3 # Run 3 replicas of this Pod selector: matchLabels: app: httpbin template: metadata: labels: app: httpbin spec: containers: - name: httpbin # Name of the container to run image: kennethreitz/httpbin # Container image ports: - containerPort: 80 # Expose port 80 of the container
We can create the above object in our cluster using by issuing the following command in
blogpost master L …1 ✔ 15:36:04 ▶ kubectl apply -f deployment.yaml deployment.apps/httpbin created
We can see that the deployment was successfully created. We can confirm that by doing,
blogpost master L …1 ✔ 15:48:26 ▶ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE httpbin 3/3 3 3 12m
The smallest schedulable object in kubernetes is not a container but something called a Pod. For simplicity let's assume it is synonymous to a container.
Now we have a deployment up and running. It has 3 replicas(pods) running. If we want to inspect the three individual pods,
blogpost master L …1 ✔ 15:48:32 ▶ kubectl get pods NAME READY STATUS RESTARTS AGE httpbin-549db4d877-b5q6b 1/1 Running 0 13m httpbin-549db4d877-ljmp5 1/1 Running 0 13m httpbin-549db4d877-pvh9c 1/1 Running 0 13m
To make sure our containers are working properly we can use
kubectl port forward to pipe the traffic from our local port to these containers.
16:00:50 ▶ kubectl port-forward deployments/httpbin 8080:80 Forwarding from 127.0.0.1:8080 -> 80 Forwarding from [::1]:8080 -> 80
Now if you visit the
localhost:8080, you will be greeted with the
Now let us see the magic kubernetes does for us. I'm going to delete one of these pods by issuing the following command.
blogpost master L …1 ✔ 15:49:46 ▶ kubectl delete pod httpbin-549db4d877-b5q6b pod "httpbin-549db4d877-b5q6b" deleted
Now we should see only two pods running right? Let's confirm.
15:51:16 ▶ kubectl get pod NAME READY STATUS RESTARTS AGE httpbin-549db4d877-ljmp5 1/1 Running 0 15m httpbin-549db4d877-pvh9c 1/1 Running 0 15m httpbin-549db4d877-sdmz5 1/1 Running 0 33s
What? we still have 3 pods running. How did this happen?
Remember writing the
deployment.yaml file? We specified that we need 3 replicas of this pod right, so kubernetes will take the necessary steps to make sure this condition is satisfied. So when we deleted one pod, kubernetes kicked in and created a new pod to satisfy the required state. If we look at the
AGE of the third container, it was created
33s ago and others
15m ago which clearly explains this.
Now let's try editing our
deployment.yaml the file itself, we'll change the replicas count to
6 and then issue the same command we used earlier.
blogpost master L …1 ✔ 16:09:31 ▶ kubectl apply -f deployment.yaml deployment.apps/httpbin configured
Now we can see that we have 6 replicas as we confirm with,
blogpost master L …1 ✔ 16:10:08 ▶ kubectl get pods NAME READY STATUS RESTARTS AGE httpbin-549db4d877-5z44t 1/1 Running 0 22s httpbin-549db4d877-ljmp5 1/1 Running 0 34m httpbin-549db4d877-nd7lk 1/1 Running 0 22s httpbin-549db4d877-p7shj 1/1 Running 0 22s httpbin-549db4d877-pvh9c 1/1 Running 0 34m httpbin-549db4d877-sdmz5 1/1 Running 0 19m
Now we want to showcase awesome app to the world, right?
For this, we have to make it on the internet. For that, we'll create a kubernetes object called Service. Create a file called
service.yaml and fill it in with the following contents.
kind: Service apiVersion: v1 metadata: name: httpbin spec: selector: app: httpbin # select all pods with this label ports: - protocol: TCP port: 80 # listen on port 80 targetPort: 80 # forward traffic to port 80 of containers type: LoadBalancer
The service resource will listen on port
80 and load balance requests across the different replicas of our app.
Create the service by issuing the following command
blogpost master L …2 ✔ 16:15:46 ▶ kubectl apply -f service.yaml service/httpbin created
If you look at the service we can see that something called
EXTERNAL-IP is showing a status of pending.
blogpost master L …2 ✔ 16:15:51 ▶ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpbin LoadBalancer 10.245.207.44 <pending> 80:31723/TCP 7s
It is because when we create
service with type
LoadBalancer the cloud provider will create a load balancer for us. Wait for some time and the LoadBalancer will be up. You can check the progress using the same command above.
After it's issued it'll look something like this,
blogpost master L …2 ✔ 16:16:04 ▶ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpbin LoadBalancer 10.245.207.44 188.8.131.52 80:31723/TCP 10m
Now if we go and visit the external IP, we should see our app running :smile:
Using a service of type
LoadBalancer is not recommended in production. We will use another kubernetes object called Ingress to specify rules for traffic coming in to the cluster.
We have just touched the tip of an iceberg with this blog post.This was a very basic introduction to kubernetes. If you found it interesting and would like to see more follow up blog posts about the architecture, tools and the ecosystem, please leave a comment below.