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:
apiVersion: v1
kind: Pod
metadata:
name: webserver
spec:
containers:
- name: web1
image: nginx:latest
ports:
- containerPort: 80.kinddefines the kind of deployment. You already knew, we’re deploying our app asPod..metadata.nameis the name of our pod (webserver)..spec.containersare group of containers in our pod — group of one container (web1) in our example.- container
web1usesnginx:latestas it’simage..spec.containers[N].nameis just the name of container namelyweb1.web1is listening on port80(.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.yamlpod/webserver createdStep3: Verify Pod is Running
You can see the deployed pod:
kubectl get podsNAME READY STATUS RESTARTS AGE
webserver 1/1 Running 0 95sWhere 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 webserver8225b4731463 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_0In 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 wideSince 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 sshFrom inside the cluster:
curl 10.244.0.14You 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/tcpsl 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 0See 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 millisecondsNot because app is not listening but because we don’t have route to 10.244.0.14:
ip routedefault 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 routedefault 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.2The 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/16next_hop⇒minikubecontainer ip addressoutgoing_interface⇒br-5c79d95b6ad3
How to get
minikubecontainer IP Address?
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' minikubeHow to find correct outgoing interface?
ip addrFind 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-5c79d95b6ad3It’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-5c79d95b6ad3Pod 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.