Handbooks / Kubernetes / Chapter 2
Services and Ingress
7 pages · ~5 min✓ Reviewed
Builds on Pods and Deployments. Next up: ConfigMaps and Secrets.
Labels, Selectors and Endpoints
1. Service and Labels
- Service = stable virtual IP + DNS name for changing Pods
- Pod IP changes whenever a Pod is replaced, so clients use the Service
- Labels = key/value tags on objects
spec.selector: equality only, all keys must match (AND)
2. From Selector to Backends
- Ready Podslabels match; failed probe = removed
- EndpointSlicescontrol plane
- kube-proxyiptables or IPVS
| Service | Deployment, RS, Job | |
|---|---|---|
| Match | Equality map | Equality + matchExpressions |
| Operators | app: web | In, NotIn, Exists |
3. Example and Mistakes
yaml
selector: app: web ports: - port: 80 targetPort: 8080
matches Pod labels app=web,tier=fe
bash
kubectl get pods -l app=web kubectl get endpoints web
- Selector typo or no selector = empty endpoints: refused or timeouts
port= Service port,targetPort= container port; swapping breaks traffic
ClusterIP Service
1. What ClusterIP Is
- Default Service type
- Virtual IP, reachable inside the cluster only
- Not bound to any interface
- kube-proxy rules DNAT it to a random ready Pod
- Fits internal service-to-service traffic
2. Each Type Builds on the Last
| Type | Adds | Reach |
|---|---|---|
| ClusterIP | virtual IP | internal only |
| NodePort | port on every node | node IPs |
| LoadBalancer | cloud LB | external |
3. Example
bash
kubectl expose deploy web --port=80 --target-port=8080yaml
apiVersion: v1
kind: Service
metadata: {name: web}
spec:
type: ClusterIP
selector: {app: web}
ports: [{port: 80, targetPort: 8080}]4. Mistakes
pingfails: only Service ports answer; test withcurlfrom a PodclusterIP: None= headless: returns Pod IPs, used with StatefulSets
NodePort and LoadBalancer
1. How they expose a Service
- NodePort: same port on every node,
30000-32767by default - LoadBalancer: cloud creates an external LB with a public IP or DNS name
- It also creates a NodePort and a ClusterIP behind it
- Client<nodeIP>:<nodePort>
- Cloud LBpublic IP
- NodePort30080
- ClusterIP
- Pod
2. Compare and example
| NodePort | LoadBalancer | |
|---|---|---|
| Address | Node IPs, you manage firewalls | One stable external address |
| Cost | None extra | Own cloud LB per Service |
| Fits | Dev, bare metal | Cloud |
yaml
spec: type: NodePort ports: - port: 80 targetPort: 8080 nodePort: 30080
pinned nodePort; or type: LoadBalancer
kubectl get svcshows EXTERNAL-IP <pending> until the cloud provides one
3. Mistakes and interview points
- EXTERNAL-IP stuck on <pending> on minikube, kind or bare metal: no cloud controller
- Fix: MetalLB or
minikube tunnel externalTrafficPolicy: Localkeeps client IP, but nodes with no local Pod drop traffic
- NodePort range?
30000-32767by default - Three types related? Each builds on the last: ClusterIP, NodePort, LoadBalancer
- Why Ingress? One LB for many Services, not one paid LB each
Cluster DNS
1. Service DNS Names
- CoreDNS runs in
kube-system - Name:
<svc>.<namespace>.svc.cluster.local - Headless Service resolves to the Pod IPs
- StatefulSet Pods: stable
pod-0.headless-svc
2. Short Names
- Pod
/etc/resolv.conf: cluster DNS IP, search path, ndots:5 - Search:
<ns>.svc.cluster.local,svc.cluster.local,cluster.local - Short names expand through the search path
| Name | Works |
|---|---|
web | same namespace only |
web.prod | across namespaces |
web.prod.svc.cluster.local | FQDN, safest |
3. Test and Debug
bash
kubectl run tmp --rm -it --image=busybox -- nslookup web.prodthrowaway Pod runs the lookup
- Bare
webfor another namespace's Service fails - Usual causes: CoreDNS down, NetworkPolicy blocking port 53
kubectl get pods -n kube-system -l k8s-app=kube-dns
4. Interview Point
- Full Service name?
<svc>.<ns>.svc.cluster.local - Resolve
web? Search path adds the Pod's namespace - DNS failing? Check CoreDNS Pods, port 53 policy,
nslookup
Ingress and Ingress Controllers
1. Ingress and Controller
- Ingress = HTTP/HTTPS rules by host and path → Services
- Gives L7 routing, TLS termination, one entry point
- Alone it does nothing: a controller (NGINX, Traefik, HAProxy, cloud ALB) configures the proxy
- Controller itself is exposed via LoadBalancer or NodePort
- Client
- LB
- Controllerproxy
- Service
- Pod
2. LoadBalancer vs Ingress
| LB Service | Ingress | |
|---|---|---|
| Layer | L4 | L7 |
| Per LB | one Service | many Services |
| Host/path | none | yes |
| Successor | — | Gateway API |
3. Example and Pitfalls
host+path+ Prefix route to Serviceapi;tlssecret enables HTTPS
yaml
spec:
ingressClassName: nginx
tls: [{secretName: app-tls}]
rules:
- host: app.example.com
http: {paths: [{path: /api, pathType: Prefix,
backend: {service: {name: api,
port: {number: 80}}}}]}- No controller, or wrong/missing
ingressClassName→ silently ignored - Backend name or port typo → 503 or 404
Services and Ingress Cheat Sheet
1. Service Types
| Type | Reach | Note |
|---|---|---|
| ClusterIP | Internal only | Default |
| NodePort | <node>:30000-32767 | Port on every node |
| LoadBalancer | Cloud LB | On top of NodePort |
| ExternalName | DNS CNAME | Maps to a name |
clusterIP: None | Headless | Pod IPs returned |
2. Selectors and DNS
- Pod labelsapp=web
- spec.selectorapp: web
- EndpointSlicesReady Pods
bash
kubectl get endpoints <svc> kubectl get pods -l k=v kubectl describe svc <svc>
| Name | Scope |
|---|---|
web | Same namespace |
web.prod | Across namespaces |
web.prod.svc.cluster.local | FQDN |
| CoreDNS | kube-system |
3. Ingress and Debugging
- Needs a controller plus an Ingress:
host,path,pathType,ingressClassName, tls secret - L7 routing for many Services behind one LB
Can't reach it
- 1Pod Ready
- 2Endpoints
- 3targetPort
- 4nslookup
- 5Ingress class, controller logs
Check yourself
A Service has selector app: web, tier: be. Pods are labeled app=web, tier=fe. What does kubectl get endpoints web show?
- No endpoints. Selector keys are ANDed, so
tiermust match too. - Traffic gets refused or times out.
Service port: 80, targetPort: 80, but the container listens on 8080. Endpoints are listed. Do requests work?
- No. Traffic goes to Pod port 80 and is refused.
- Fix: set
targetPort: 8080.
An Ingress with ingressClassName: nginx exists, but nothing answers. What is the likely cause?
- No ingress controller is installed, so the Ingress is silently ignored.
- Install a controller that matches the class.