Beginners guide to Docker and Kubernetes

Ansu K
Geek Culture
Published in
8 min readMay 3, 2022

--

Photo by Towfiqu barbhuiya on Unsplash

Earlier we had solved the problem of CI/CD and scaling with AWS CodeDeploy, GitHub Actions and AWS Auto Scaling Groups (ASG). This works well as long as you have a couple of services to manage, as your services grow so do your servers and managing a huge number of ASGs need a better orchestration than simply creating a new ASG every time you need to add a new service for your users.

Before we start, I’d recommend you visit my previous blog on how we solved the scaling issue here so you can compare what’s changing and why.

In a nutshell, we placed instances behind an ASG to scale automatically in case of traffic surge, then we ran AWS CodeDeploy in GitHub Actions Workflow to deploy code pushed on GitHub automatically to all instances behind the ASG.

With time we kept on adding new services and consequently new ASGs for each service with different type of instance running different code in each ASG. We now have over 50 ASGs and managing these is a hell of a task.

ENTER KUBERNETES..

To understand how we have setup Kubernetes we need to understand the components it is built on. I’ll explain little about each of them below, feel free to skip to Part II where I talk about how we used these components to solve the above problem with code examples, if you already know how these components work.

Docker, docker images and containers

Docker is simply a way to containerize your application. When you write an application on your local machine, you can transfer the code to the internet to let other people use it but that doesn’t guarantee that it’ll work exactly how it worked on your machine or even work at all. This is because your application might have dependencies like modules, frameworks, databases etc, which might not get satisfied in other user’s machine.

To solve this we can put your application inside a container that not only contains your code but all its dependencies and the specified version of all the modules. This is called containerization. If we use a docker container it is called Dockerization.

In order to dockerize your app you need to add a file in your repository called Dockerfile (this file cannot be named anything else, the D should be in capital and rest in small without any extension) which, when run, builds a sharable image of your application. This image can be pushed to a registry online and shared with anyone to run in their system inside a container. Below is an example Dockerfile for a node application.

A container is a lightweight environment that separates your application from other applications in the system. Conceptually it is similar to Virtual Machines but unlike virtual machines it does not have a guest OS on top of the machine OS, hence more lightweight and manageable.

How to run applications on Docker

Docker has a few components/objects that you’d need to run your applications and below are the frequently used commands associated with them:

a. images —

An image is the template of your application packaged in a file that, when executed, runs your application.

$ docker run <image_id/image_name>: You can use this command to create a running instance of your image. If the image exists on your local, docker will use that else it will pull the image from the DockerHub registry during the execution of the same command.

$ docker images: list all the images that exist in your local machine with this command.

$ docker rmi <image_id/image_name>: to delete an image from your local.

b. containers —

A container is simply all the isolated applications that is being run in your Docker.

$ docker ps : to list all the containers that are currently running.

$ docker ps --all : to list all the containers in various states, ie. running, exited, completed etc.

$ docker container start/stop/kill <container_id/container_name> : to start a container that is listed but currently not running, to stop a running container, to kill a container without allowing the processes to finish respectively.

NOTE: when writing container or image id you don’t have to write the complete hash, just using the unique initial letters works.

$ docker exec -it <container_id/container_name> bash : to enter your container application.

$ docker rm <container_id/container_name> : to delete a container.

Kubernetes

AKA k8s, the best resource I found to get acquainted with k8s is the official hands on tutorial and here is the link to it. For this blog though, I’ll give you a brief introduction to k8s and its objects that we need to be familiar with, with respect to our system, to setup and work with our infra on k8s.

Kubernetes is a way to manage all the containers and its associated images and other resources.

Kubernetes provides you with a framework to run distributed systems resiliently. It takes care of scaling and failover for your application, provides deployment patterns, and more. For example, Kubernetes can easily manage a canary deployment for your system.

Kubernetes Cluster

A k8s cluster is an ecosystem which comprises of highly available cluster of computers that are connected to work as a single unit that run your applications and services binding your application.

Below are some commands to see and switch cluster/context:

$ kubectl config get-clusters : to list all the clusters configured in your system

$ kubectl config get-contexts : to list all contexts configured in your system. A cluster with an authenticated user form a context.

$ kubectl config current-context : to check which context are you currently working on, any changes you make would be applied to this context.

$ kubectl config use-context <context_name> : to switch contexts.

Node

It is the underlying physical or virtual machine that runs your application. A bunch of these nodes form the k8s cluster.

Pod

A pod is an abstraction that runs a containerized application. Ideally, one pod only runs one container although you can specify k8s to run multiple containers in a single pod but it is only recommended for very tightly coupled applications. Multiple pods run on a single node.

Common commands you need to know when working with pods:

$ kubectl get po : to list all the pods present in the default namespace in the current context.

$ kubectl exec -it <pod_name> -- bash : to get inside the pod

$ kubectl logs -f <pod_name> : to get console logs of the pod

Deployment

A k8s deployment is one of k8s objects written in yaml format and contains all specifications for your application pod management, for example, the number of replicas your application should have, the image your application pod should use, any environment variable that needs to be set in your pod containers etc. Below is a sample deployment file.

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

You can either use the kubectl create command to create a default deployment providing image in the command or write a yaml file of kind deployment, as shown above, and run the kubectl apply command to create that deployment.

To create a default deployment, run the below command:

$ kubectl create deployment <deployment_name> --image=<image_name/image_id>Eg: 
kubectl create deployment kubernetes-bootcamp --image=gcr.io/google-samples/kubernetes-bootcamp:v1

To create a deployment using the above deployment file, run the below command:

$ kubectl apply -f <path_to_deployment_yaml_file>Eg:
kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

Deployment is one of the most used objects, to learn more about it you can refer this official doc.

Ingress

k8s ingress is an object to allow users from outside your cluster to access your applications and services. It majorly provides routing rules typically via HTTP/HTTPS and also acts a Load Balancer.

Below is a sample ingress yaml file.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-example
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80

You can create an ingress file similar to deployment file with either the kubectl create command or kubectl apply command with a yaml file path.

Other objects and common commands

k8s has a lot of objects and it is not feasible to remember commands for each one of them, thus it follows a common pattern which is useful in most cases.

Some of the k8s objects are:

  • Deployments
  • Pods
  • Services
  • Namespaces
  • Ingress
  • ReplicaSets
  • ConfigMaps
  • DaemonSets
  • Horizontal Pod Autoscaler (HPA)
  • etc

Common commands to use with above objects:

$ kubectl get <object_type>: to list all objects of same category. For example to list all pods that are running or to see all the deployments there is in the current context you can use kubectl get pods or kubectl get deployments respectively.

Most objects also have a shorthand to use with command for example:

pods: kubectl get pods/podeployments: kubectl get deployments/deployservices: kubectl get services/svcnamespaces: kubectl get namespaces/nsHPA: kubectl get horizontalpodautoscaler/hpaConfigMaps: kubectl get configmaps/cmDaemonSets: kubectl get daemonsets/dsIngress: kubectl get ingress/ing

$ kubectl edit <object_type> <object_name> : to edit any object on the fly you can use this command, this opens the yml file in vi editor where you can use i command to insert changes and esc + :wq! to save those changes.

$ kubectl describe <object_type> <object_name> : to get the detailed description/configuration of the object.

$ kubectl get <object_type> <object_name> -o yaml : to see the configurations of the object as a yaml file.

$ kubectl delete <object_type> <object_name> : to delete any object and it’s dependencies. For example, if you delete a deployment, any pod that the deployment was supposed to run/create will also get deleted.

$ <any_of_above_command> -n <namespace_name> : by default if you use any of the above commands it will only work for the default namespace, to use the command in different namespaces you can use the -n flag. For instance, $ kubectl get po -n test .

This concludes our introduction to pre-requisites, now let’s talk about how are we solving the initial problem using k8s in the next part linked here.

--

--