Handbooks / Kubernetes / Chapter 2

Services and Ingress

7 pages · ~5 min✓ Reviewed

Builds on Pods and Deployments. Next up: ConfigMaps and Secrets.

KubernetesPage 1

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

  1. Ready Podslabels match; failed probe = removed
  2. EndpointSlicescontrol plane
  3. kube-proxyiptables or IPVS
ServiceDeployment, RS, Job
MatchEquality mapEquality + matchExpressions
Operatorsapp: webIn, 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
KubernetesPage 2

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

TypeAddsReach
ClusterIPvirtual IPinternal only
NodePortport on every nodenode IPs
LoadBalancercloud LBexternal

3. Example

bash
kubectl expose deploy web --port=80 --target-port=8080
yaml
apiVersion: v1
kind: Service
metadata: {name: web}
spec:
  type: ClusterIP
  selector: {app: web}
  ports: [{port: 80, targetPort: 8080}]

4. Mistakes

  • ping fails: only Service ports answer; test with curl from a Pod
  • clusterIP: None = headless: returns Pod IPs, used with StatefulSets
KubernetesPage 3

NodePort and LoadBalancer

1. How they expose a Service

  • NodePort: same port on every node, 30000-32767 by default
  • LoadBalancer: cloud creates an external LB with a public IP or DNS name
  • It also creates a NodePort and a ClusterIP behind it
  1. Client<nodeIP>:<nodePort>
  2. Cloud LBpublic IP
  3. NodePort30080
  4. ClusterIP
  5. Pod

2. Compare and example

NodePortLoadBalancer
AddressNode IPs, you manage firewallsOne stable external address
CostNone extraOwn cloud LB per Service
FitsDev, bare metalCloud
yaml
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080

pinned nodePort; or type: LoadBalancer

  • kubectl get svc shows 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: Local keeps client IP, but nodes with no local Pod drop traffic
  • NodePort range? 30000-32767 by default
  • Three types related? Each builds on the last: ClusterIP, NodePort, LoadBalancer
  • Why Ingress? One LB for many Services, not one paid LB each
KubernetesPage 4

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
NameWorks
websame namespace only
web.prodacross namespaces
web.prod.svc.cluster.localFQDN, safest

3. Test and Debug

bash
kubectl run tmp --rm -it --image=busybox -- nslookup web.prod

throwaway Pod runs the lookup

  • Bare web for 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
KubernetesPage 5

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
  1. Client
  2. LB
  3. Controllerproxy
  4. Service
  5. Pod

2. LoadBalancer vs Ingress

LB ServiceIngress
LayerL4L7
Per LBone Servicemany Services
Host/pathnoneyes
Successor—Gateway API

3. Example and Pitfalls

  • host + path + Prefix route to Service api; tls secret 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
KubernetesPage 6

Services and Ingress Cheat Sheet

1. Service Types

TypeReachNote
ClusterIPInternal onlyDefault
NodePort<node>:30000-32767Port on every node
LoadBalancerCloud LBOn top of NodePort
ExternalNameDNS CNAMEMaps to a name
clusterIP: NoneHeadlessPod IPs returned

2. Selectors and DNS

  1. Pod labelsapp=web
  2. spec.selectorapp: web
  3. EndpointSlicesReady Pods
bash
kubectl get endpoints <svc>
kubectl get pods -l k=v
kubectl describe svc <svc>
NameScope
webSame namespace
web.prodAcross namespaces
web.prod.svc.cluster.localFQDN
CoreDNSkube-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
  1. 1Pod Ready
  2. 2Endpoints
  3. 3targetPort
  4. 4nslookup
  5. 5Ingress class, controller logs
KubernetesPage 7

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 tier must 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.