Skip to content

Challenges of Accessing Apps in Pods


Deployment is a great way of deploying your application because it provides higher-level abstraction of resource to declaratively manage, scale and update stateless application. But with just Deployment in place, there comes the challenges of accessing application running in Pods because pods are ephemeral in nature and you can’t trust an individual Pod to be reliable, durable — Pods get killed, dies without consent and appears out of nowhere (ReplicaSet does that though) and suddenly the backend app you were accessing on one IP address is inaccessible. This slows down development, organizational access and makes user level access almost impossible — Service enters the game just here.

Understanding The Problem

Let’s move very slow so that the problem is more vivid. Assume there is active development going on and backend is accessible at port 80 — it is just NGINX index.html file with:

index.html
I am backend!

which is deployed as:

backend_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-deployment
  labels:
    app: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: backend:v1
        ports:
        - containerPort: 80

Currently your application architecture looks like:

    ---
title: Your Application
---
flowchart
    subgraph K8s Cluster
        subgraph backend
            P3["Pod1<br>(10.244.0.93)"]
            P4["Pod2<br>(10.244.0.94)"]
        end
    end
  

Assuming your frontend is already inside the cluster, the team now requires access to backend for further development. The code looks like (noway near real app, just demo):

import socket
import time

import requests

BACKEND_HOST = ""
BACKEND_PORT = 80

LISTEN_HOST = "0.0.0.0"
LISTEN_PORT = 80

PREFIX = "Backend says: "

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((LISTEN_HOST, LISTEN_PORT))
server.listen()

print(f"Listening on {LISTEN_HOST}:{LISTEN_PORT}")

while True:
    client_socket, address = server.accept()

    print(f"Client connected: {address}")

    try:
        while True:
            url = f"http://{BACKEND_HOST}:{BACKEND_PORT}"

            try:
                response = requests.get(url=url, timeout=5)
                data = PREFIX + str(response)
                data = data.encode()
                client_socket.sendall(data)

            except (BrokenPipeError, ConnectionResetError, OSError):
                print(f"Client disconnected: {address}")
                break

            time.sleep(3)

    finally:
        client_socket.close()

It need BACKEND_HOST to proceed. You as a cluster manager queries K8s API and provide one of the IP address of backend Pods:

kubectl get pods --selector='app=backend' \
-o custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME                                 IP
backend-deployment-99c89984c-4rjwf   10.244.0.93
backend-deployment-99c89984c-xs4xc   10.244.0.94

Now, frontend app can proceed as:

# ...
BACKEND_HOST = "10.244.0.93"
# ...

You then deploy your frontend:

frontend_deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: frontend:v1
        ports:
        - containerPort: 80

Now your application architecture is frontend-backend:

    ---
title: Your Application
---
flowchart
    subgraph K8s Cluster
        subgraph backend
            direction LR
            P3["Pod1<br>(10.244.0.93)"]
            P4["Pod2<br>(10.244.0.94)"]
        end
        subgraph frontend
            direction LR
            P1["Pod3<br>(10.244.0.120)"]
            P2["Pod4<br>(10.244.0.121)"]
        end
        frontend -- connects to --> backend
    end
  

Problem1: Pods Are Ephemeral

Currently we can access frontend on either of 10.244.0.120 or 10.244.0.121 as such:

nc 10.244.0.121 80
Backend says: I am backend!
Backend says: I am backend!
Backend says: I am backend!
^C

But if those Pods die and recreated, IP Address changes (often) too and suddenly you can’t access application you were using two minutes ago:

nc 10.244.0.121 80 -v
nc: connect to 10.244.0.120 port 80 (tcp) failed: No route to host
    ---
title: Your Application
---
flowchart
    subgraph K8s Cluster
        subgraph backend
            direction LR
            P3["Pod1<br>(10.244.0.93)"]
            P4["Pod2<br>(10.244.0.94)"]
        end
        subgraph frontend
            direction LR
            P1["Pod3<br>(10.244.0.120)"]
            P2["Pod4<br>(10.244.0.122)<br>[CHANGED]"]
        end
        frontend -- connects to --> backend
    end
    style P2 fill:#ffff00,stroke:#8b0000,color:#000000,stroke-width:1px
  

The same ephemeral nature applies to backend. Remember when we specified BACKEND_HOST as:

# ...
BACKEND_HOST = "10.244.0.93"
# ...

Now imagine BACKEND_HOST at 10.244.0.93 dies and recreated — frontend hangs forever searching for backend and you will never get response.

    ---
title: Your Application
---
flowchart
    subgraph K8s Cluster
        subgraph backend
            direction LR
            P3["Pod1<br>(10.244.0.95)<br>[CHANGED]"]
            P4["Pod2<br>(10.244.0.94)"]
        end
        subgraph frontend
            direction LR
            P1["Pod3<br>(10.244.0.120)"]
            P2["Pod4<br>(10.244.0.122)"]
        end
        frontend -- connects to --> backend
    end
    style P3 fill:#ffff00,stroke:#8b0000,color:#000000,stroke-width:1px
  

To simulate this, I tried connecting frontend on the new IP address:

nc 10.244.0.122 80 -v
Connection to 10.244.0.122 80 port [tcp/http] succeeded!
^C

You were able to connect to frontend on it’s new IP 10.244.0.122 but frontend isn’t able to find backend at 10.244.0.93.

The naive solution would be to connect frontend with new IP Address and also modify backend8 application with different IP Address anytime it changes but WHO WILL WATCH FOR THE CHANGES?

Problem2: No Load Balancing

Even if you update backend with new IP Address — What’s the point of having two replicas when all you are using is one? The same applies for frontend.

    ---
title: Your Application
---
flowchart
    subgraph K8s Cluster
        subgraph backend
            direction LR
            P3["Pod1<br>(10.244.0.95)<br>[OVERLOAD]"]
            P4["Pod2<br>(10.244.0.94)<br>[NO LOAD]"]
        end
        subgraph frontend
            direction LR
            P1["Pod3<br>(10.244.0.120)"]
            P2["Pod4<br>(10.244.0.122)"]
        end
        frontend -- connects to --> backend
    end
    style P3 fill:#ff3f00,stroke:#8b0000,color:#000000,stroke-width:1px
  

Deployment Can’t Solve

Kubernetes Deployment resource doesn’t have any mechanism to solve any problem mentioned above. You can’t stop the change in IP Address, it’s dynamic. You can’t load balance unless there is something that keeps track of all the endpoints and expose them through one virtual endpoint (IP:PORT).

Who Solves Then?

The responsibility of exposing apps in Kubernetes is deligated to Service. Roughly speaking, Service keeps list of all endpoints and expose your application onto one endpoint which you can connect to — internal load balancing is automatically taken care. You are eager to know how Service does what it does — see you in the next chapter.

Last updated on