Skip to content

Your First k8s App — Pod Deployment


A pod is a smallest and minimal deployable unit for any kubernetes cluster. If you are comming from the world of containers, you can think a pod as similar (not same) to a container. At it core, pod is actually a specification/definition on “how to run a container” because pod itself is a group of one or more containers at the end of the day. Ultimately, what kubernetes manages are containers but through pods even in very minimal setup.

Deploy Your First App In K8s

Everything in kubernetes is YAML. You write all the desired configuration in YAML. It is not advisable to remember syntax of configuration — it’s practically not possible. But what you need is the understanding of how things work and what individual elements inside the YAML file are. This will come naturally with practice and patience.

Everytime you deploy a pod, you start by an example. You can find tons of examples in the internet and in the official documentation. You then are responsible to make changes to the key: value pair in the configuration to get the desired state of your application.

Step1: Define Pod Specification

With that in mind, this is another example:

webserver_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webserver
spec:
  containers:
  - name: web1
    image: nginx:latest
    ports:
    - containerPort: 80
  • .kind defines the kind of deployment. You already knew, we’re deploying our app as Pod.
  • .metadata.name is the name of our pod (webserver).
  • .spec.containers are group of containers in our pod — group of one container (web1) in our example.
  • container web1 uses nginx:latest as it’s image.
    • .spec.containers[N].name is just the name of container namely web1.
    • web1 is listening on port 80 (.spec.containers[N].ports[n].containerPort).

For pod deployment, .kind remains the same for all kind of apps — you modify all other fields value as per app requirement.

Step2: Apply Pod Specification

The simplest way to apply the pod specification is:

kubectl apply -f webserver_pod.yaml
pod/webserver created

Step3: Verify Pod is Running

You can see the deployed pod:

kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
webserver   1/1     Running   0          95s

Where is The Pod Running?

We already know Minikube is a single node cluster which means the worker node and the control plane lives in a single node. What that implies is out webserver cluster running nginx is inside Minikube kubernetes cluster.

From inside Minikube (after minkube ssh) if you see running containers:

docker container ls --format 'table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.Names}}'  | grep webserver
8225b4731463   nginx                          "/docker-entrypoint.…"   k8s_web1_webserver_default_e58b9fb8-87f1-4f4e-a081-30cbf6d7f9c9_0
6eb1eec9e375   registry.k8s.io/pause:3.10.1   "/pause"                 k8s_POD_webserver_default_e58b9fb8-87f1-4f4e-a081-30cbf6d7f9c9_0

In Kubernetes, every pod is actually made up of at least two containers: your application container and a hidden infra container called the Pause container (k8s_POD).

Step4: Access Your Application

Our pod is running nginx container at port 80. You can get the IP Address for the pod viewing pods in wide output format:

kubectl get pods webserver -o wide

Since we already know the name of our pod we use webserver in kubectl get pods. If name is not known kubectl get pods -o wide will also do the job.

NAME        READY   STATUS    RESTARTS   AGE     IP            NODE       NOMINATED NODE   READINESS GATES
webserver   1/1     Running   0          9m41s   10.244.0.14   minikube   <none>           <none>

What we care is IP i.e. 10.244.0.14 for our webserver pod. But the question is:

Can your host reach the pod at IP 10.244.0.14?

The answer is NO (not yet) because we have not exposed any port for the pod so the web1 container is not available for the outsiders just in this setup yet. This is however not the case in real world because any webserver meant for public will be exposed.

The Easy Way

Exposing port is not part of this lesson (we will look later in the course), we have several ways of accessing the pod or container. And the easiest one is by logging into k8s cluster:

minikube ssh

From inside the cluster:

curl 10.244.0.14

You will be thanked by nginx default page:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
<h1>Welcome to nginx!</h1>
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Note

It was possible to access the webserver from the cluster because it was listening in global interface address. You can even verify that by can:

docker container exec 8225b4731463 cat /proc/net/tcp
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                
0:  00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 818788 1 0000000000000000 100 0 0 10 0

See the local_address encoded in HEX is 00000000:0050 which is actually 0.0.0.0:80.

The Another Way: Hard Way

TL;DR — This is never the way to access application unless you want to explore more. If you don’t wanna know why, proceed directly to the last part of this lesson.

Since we can reach Minikube cluster (minikube ssh) and it can reach webserver, indirectly with the same link we can reach webserver too.

Till this point, We are not able to curl 10.244.0.14 --max-time 4:

curl: (28) Connection timed out after 4066 milliseconds

Not because app is not listening but because we don’t have route to 10.244.0.14:

ip route
default via 172.31.48.1 dev eth0 proto kernel 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
172.31.48.0/20 dev eth0 proto kernel scope link src 172.31.52.95 
192.168.49.0/24 dev br-5c79d95b6ad3 proto kernel scope link src 192.168.49.1 

Conversely, Minikube was able to reach 10.224.0.14 because it has route to that network:

docker container exec minikube ip route
default via 192.168.49.1 dev eth0
10.244.0.0/16 dev bridge proto kernel scope link src 10.244.0.1
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
192.168.49.0/24 dev eth0 proto kernel scope link src 192.168.49.2

The second line is the route for network 10.244.0.0/16. What this implies is that if we add route to 10.244.0.0/16 in our host we can reach our webserver at 10.224.0.14.

We can add route using a simple syntax:

ip route add <destination_network> via <next_hop> dev <outgoing_interface>

For our use case:

  • destination_network ⇒ 10.244.0.0/16
  • next_hop ⇒ minikube container ip address
  • outgoing_interface ⇒ br-5c79d95b6ad3

How to get minikube container IP Address?

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' minikube

How to find correct outgoing interface?

ip addr

Find matching network interface with minikube ip address (complex lookups may be required if you have multiple matching networks which is very unlikely in test/learning environment).

Now add route:

sudo ip route add 10.244.0.0/16 via 192.168.49.2 dev br-5c79d95b6ad3

It’s party time!

curl 10.244.0.14
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
<h1>Welcome to nginx!</h1>
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

A word of caution:

Warning

Routes added by ip route doesn’t persist reboot and Minikube cluster IP may change too. Also there is unnecessary overhead in this setup for almost no benefit (because you can always minikube ssh for test) therefore you should always prefer The Easy Way while keeping this in mind that there are other ways (not this hard way one) to expose your app in real world which we will explore in later chapters.

Routes added here doesn’t persist reboot but if you wish to delete before reboot, you can do that (just replace add by del):

sudo ip route del 10.244.0.0/16 via 192.168.49.2 dev br-5c79d95b6ad3

Pod Deployment is NOT For Real World Applications: What’s Then?

We have moved from containers to kubernetes because we want entreprise or production level features like load balancing, auto healing and others which by only pod itself is not possible. What we have done here can easily be done with just a container too — noone is looking after our application. If pod or container is deleted/down, it won’t be recreated and the app will go down immediately. The upcomming lessons solve this problem.

Last updated on