Container Orchestration
we can run our application with docker in a single instance but in a production environment we need to scale up and scale down number of instances according to read time traffic. Also when an application is installed in a single instance, it creates single point of failure. So we need to scale it. There are several techniques that facilitates container clustering.
- docker swarm
- kubernetes
Minikube
We can spin up a kubernetes cluster in the cloud. But that’s for production. To test and work with kubernetes for practice, we can spin up a very basic kubernetes cluster here in our laptop. We will use minikube to practice and understand kubernetes basics.
Install Minikube
-
First Install
kubectlIf kubectl is not installed, minikube will face difficulty to configure kube config. The following command installs
kubectlon Linux AMD 64.curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"Detail about other installation procedure can be found here.
-
No we can install
minikubeN:B: Commands are for linux amd64.
curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube && rm minikube-linux-amd64 -
Start minikube
minikube start -
Explore minikube commands
minikube pause minikube unpause minikube stop minikube addons listFor more, explore minikube Get Started blog.
kubectl
kubectl is the cli client for kubernetes. We can interact with kubernetes cluster with this client. When we started minikube cluster, it stored a kubernetes configuration in .kubec/config file. Currently, we only have minikube cluster. But in a real scenario, most of the cases, we might need to interact with multiple clusters. In Kubernetes, a kubectl context is a named collection of access parameters that define which Kubernetes cluster you are interacting with, the user credentials to use for authentication, and the default namespace for commands. Contexts are stored in kubeconfig file, typically located at ~/.kube/config. We currently have single k8s cluster configured, so don’t need to mention which context to use in our kubectl commands. But later we will need to use it.
Let’s try to interact with our k8s cluster and understand various k8s component.
namespace
Let’s run
kubectl get namespace
It will list down all the namespaces in the cluster.
What’s a namespace? According to k8s official documentation
In Kubernetes, namespaces provide a mechanism for isolating groups of resources within a single cluster. Names of resources need to be unique within a namespace, but not across namespaces. Namespace-based scoping is applicable only for namespaced objects (e.g. Deployments, Services, etc.) and not for cluster-wide objects (e.g. StorageClass, Nodes, PersistentVolumes, etc.).
If we further look into the result of kubectl get namespace, we will see the following namespaces.
rt@bp:~$ kubectl get namespaces
NAME STATUS AGE
default Active 3d1h
kube-node-lease Active 3d1h
kube-public Active 3d1h
kube-system Active 3d1h
kubernetes-dashboard Active 12m
Let’s check what’s inside each namespace.
To list down all objects inside default namespace, we need to run the following command.
rt@bp:~$ kubectl get all -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d1h
Here, -n is used to specify namespace. Well, as the name suggests, default namespace is the default namespace. If we don’t mention any namespace with -n, default namespace will be used by default.
rt@bp:~$ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d1h
Let’s explore other namespaces …
rt@bp:~$ kubectl get all kube-node-lease
error: you must specify only one resource
rt@bp:~$ kubectl get all -n kube-node-lease
No resources found in kube-node-lease namespace.
rt@bp:~$ kubectl get all -n kube-public
No resources found in kube-public namespace.
rt@bp:~$ kubectl get all -n kube-system
NAME READY STATUS RESTARTS AGE
pod/coredns-66bc5c9577-zvfwj 1/1 Running 2 (2d20h ago) 3d1h
pod/etcd-minikube 1/1 Running 2 (2d20h ago) 3d1h
pod/kube-apiserver-minikube 1/1 Running 2 (26m ago) 3d1h
pod/kube-controller-manager-minikube 1/1 Running 2 (2d20h ago) 3d1h
pod/kube-proxy-dr48r 1/1 Running 2 (2d20h ago) 3d1h
pod/kube-scheduler-minikube 1/1 Running 2 (2d20h ago) 3d1h
pod/storage-provisioner 1/1 Running 4 (26m ago) 3d1h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 3d1h
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/kube-proxy 1 1 1 1 1 kubernetes.io/os=linux 3d1h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 1/1 1 1 3d1h
NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-66bc5c9577 1 1 1 3d1h
Yap things are running inside kube-system namespace. Let’s get familar with other kubernetes objects.
Let’s create a new namespace.
kubectl create namespace ostad
With the command we have created a namespace named ostad. From now on, in this class we will use ostad namespace.
Pod
A pod is the smallest kubernetes object that can be replicated. In most cases a pod consists of a single container. But it is possible for a pod to have multiple containers, volumes etc. More about pod.
Let’s run a pod in ostad namespace.
kubectl run nginx --image=nginx -n ostad
This will spin up a single pod with single container inside. We can check it with the following command.
rt@bp:~$ kubectl get pod/nginx -n ostad
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 9s
While kubectl get <object> <name> gives brief information about k8s object, kubectl describe shows descriptive information.
rt@bp:~$ kubectl describe pod/nginx -n ostad
Name: nginx
Namespace: ostad
Priority: 0
Service Account: default
Node: minikube/192.168.49.2
Start Time: Thu, 23 Oct 2025 09:31:52 +0600
Labels: run=nginx
Annotations: <none>
Status: Running
IP: 10.244.0.80
IPs:
IP: 10.244.0.80
Containers:
nginx:
Container ID: docker://be2600b9aeed24849e738cbfd953dcf2e3ff139508aad906a147024e07e252f6
Image: nginx
Image ID: docker-pullable://nginx@sha256:029d4461bd98f124e531380505ceea2072418fdf28752aa73b7b273ba3048903
Port: <none>
Host Port: <none>
State: Running
Started: Thu, 23 Oct 2025 09:31:56 +0600
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-5crjh (ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-5crjh:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
Optional: false
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 60s default-scheduler Successfully assigned ostad/nginx to minikube
Normal Pulling 60s kubelet Pulling image "nginx"
Normal Pulled 57s kubelet Successfully pulled image "nginx" in 3.274s (3.274s including waiting). Image size: 151840157 bytes.
Normal Created 57s kubelet Created container: nginx
Normal Started 57s kubelet Started container nginx
From pod description, we can check
- The pod has got an uniq IP Address.
- We can use
kubectl describecommand to check the steps taken by k8s cluster to run this pod. - Sometimes, a pod might fail to spin up and show error. On that time we can further analyze the problem using
kubectl describecommand.
Another useful kubectl command is kubectl logs. We can see logs from pod using it.
rt@bp:~$ kubectl logs -f pod/nginx -n ostad
Ok. The pod is running. It has got uniq ip. But it’s inside the cluster. There’s kubectl command that we can use to make a bridge between our local machine to the pod using kubectl port-forwarding.
kubectl port-forward pod/nginx 8080:80 -n ostad
Deployment
deployment: We don’t just run pods right? Sometimes we need to scale it up, scale it down, update it, downgrade it, allocate resource on it etc. deployment does the task. A Deployment provides declarative updates for Pods and ReplicaSets.
Before
kubectl create deployment nginx --
Replica Set
ReplicaSet: According the kubernetes official documentation, A ReplicaSet’s purpose is to maintain a stable set of replica Pods running at any given time. Usually, you define a Deployment and let that Deployment manage ReplicaSets automatically.
Service
Service: Expose an application running in your cluster behind a single outward-facing endpoint, even when the workload is split across multiple backends.
We don’t need to know about daemonset now, we will explore daemonset and other kubernetes objects later. But for the very curious minds, you can exlore here.
ToDo:
rt@bp:~$ minikube status
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
Servcie
kubectl delete pod nginx
kubectl run nginx --image=nginx --expose --port 80
kubectl expose pod/nginx --port=<> --target-port=80
Or with deployment
kubectl create deployment nginx --image=nginx --replicas=2
kubectl expose deployment/nginx --port=8080 --target-port=80
We will see, a service is now created to expose port 80.
https://kubernetes.io/docs/reference/kubectl/generated/kubectl_expose/
kubectl get all
There are 4 types of services in kubernetes
- ClusterIP: Exposes the Service on a cluster-internal IP. Choosing this value makes the Service only reachable from within the cluster. This is the default that is used if you don’t explicitly specify a type for a Service. You can expose the Service to the public internet using an Ingress or a Gateway.
- NodePort: Exposes the Service on each Node’s IP at a static port (the NodePort). To make the node port available, Kubernetes sets up a cluster IP address, the same as if you had requested a Service of type: ClusterIP.
- LoadBalancer: the Service externally using an external load balancer. Kubernetes does not directly offer a load balancing component; you must provide one, or you can integrate your Kubernetes cluster with a cloud provider.
- ExternalName: Maps the Service to the contents of the externalName field (for example, to the hostname api.foo.bar.example). The mapping configures your cluster’s DNS server to return a CNAME record with that external hostname value. No proxying of any kind is set up.
- https://kubernetes.io/docs/reference/kubectl/generated/kubectl_create/kubectl_create_service_externalname/
ReplicaSet
Deployment resource makes it easier for updating your pods to a newer version.
Let’s say you use ReplicaSet-A for controlling your pods, then You wish to update your pods to a newer version, now you should create Replicaset-B, scale down ReplicaSet-A and scale up ReplicaSet-B by one step repeatedly (This process is known as rolling update). Although this does the job, but it’s not a good practice and it’s better to let K8S do the job.
A Deployment resource does this automatically without any human interaction and increases the abstraction by one level.
Note: Deployment doesn’t interact with pods directly, it just does rolling update using ReplicaSets.
https://stackoverflow.com/questions/69448131/kubernetes-whats-the-difference-between-deployment-and-replica-set
Ingress:
https://kubernetes.io/docs/concepts/services-networking/ingress/