Pods are ephemeral. They get created, destroyed, and rescheduled all the time. Every time a Pod restarts, it gets a new IP address. So how do other Pods find and talk to our app? That’s what Services solve — they give us a stable endpoint that doesn’t change even when the Pods behind it do.
Service Types
ClusterIP — The Default
Most Services are ClusterIP. They give us a virtual IP inside the cluster that load-balances traffic across matching Pods.
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: ClusterIP # default, can omit this line
selector:
app: my-app # routes to Pods with this label
ports:
- port: 80 # port the Service listens on
targetPort: 8080 # port on the Pod
The selector is the glue. The Service watches for all Pods with app: my-app label and routes traffic to them.
NodePort
Builds on ClusterIP but also opens a static port on every node in the cluster.
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # optional, K8s picks one if omitted
Now we can hit http://<any-node-ip>:30080 from outside. Not great for production (ugly ports, no SSL), but handy for quick access.
LoadBalancer
The production choice on cloud providers. It creates a ClusterIP + NodePort + a cloud load balancer that routes external traffic in.
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
The cloud provider assigns an external IP. One downside: each LoadBalancer Service gets its own cloud LB, which can get expensive. That’s where Ingress comes in.
Service Discovery via DNS
Kubernetes runs a DNS server (CoreDNS) inside the cluster. Every Service gets a DNS entry automatically:
my-app-service— works within the same namespacemy-app-service.default.svc.cluster.local— fully qualified (namespace =default)
So our app code just uses the Service name as the hostname. No hardcoded IPs.
# From inside any Pod in the same namespace
curl http://my-app-service:80
Ingress — Smart HTTP Routing
Instead of giving every Service its own LoadBalancer, we can use a single Ingress controller to route traffic based on hostnames or URL paths.
An Ingress Controller (like nginx-ingress or Traefik) is the actual reverse proxy. An Ingress resource is the routing config.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
This routes api.example.com to one Service and app.example.com to another — all through a single load balancer. Way cheaper and cleaner.
In simple language, Services give Pods a stable address, and Ingress lets us route external HTTP traffic to the right Service based on domain names or paths. For internal communication we use ClusterIP, for external access we use Ingress or LoadBalancer.