Backend / Kubernetes / 05_ingress_and_networking.md

Ingress and Networking

Updated 6 interview angles 5 min read source
On this page11
  1. Service types recap
  2. Ingress
  3. Ingress controllers
  4. TLS
  5. Network Policies
  6. DNS inside the cluster
  7. North-south vs east-west
  8. AWS specifics: ALB Ingress Controller
  9. Egress: how pods reach the internet
  10. Common bugs
  11. Interview angle

Ingress and Networking

How external traffic reaches your pods, and how pods reach each other or are restricted.

Service types recap

ClusterIP (default, in-cluster), NodePort (rarely used directly), LoadBalancer (provisions a cloud LB), ExternalName (DNS alias). For internet traffic, you typically pair a Service: LoadBalancer with an Ingress for L7 features.

Ingress

The HTTP-aware front door. One LB → many services routed by host + path.

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public
  namespace: prod
  annotations:
    # or alb, traefik, ...
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts: [api.example.com]
    secretName: api-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - { path: /v1/orders,   pathType: Prefix, backend: { service: { name: orders,   port: { number: 80 } } } }
      - { path: /v1/users,    pathType: Prefix, backend: { service: { name: users,    port: { number: 80 } } } }
      - { path: /v1/payments, pathType: Prefix, backend: { service: { name: payments, port: { number: 80 } } } }

The Ingress object is just a spec; it does nothing without an Ingress Controller to implement it (nginx-ingress, AWS Load Balancer Controller, Traefik, Contour).

Ingress controllers

Controller Notes
ingress-nginx self-hosted; powerful; many annotations
AWS Load Balancer Controller provisions ALB per Ingress; native AWS
Traefik label-based, good DX
HAProxy Ingress high performance
Contour / Envoy Gateway Envoy-backed; modern API

Modern direction: Gateway API (the successor to Ingress, k8s 1.29+ GA), with proper resource types Gateway, HTTPRoute, GRPCRoute, etc. Cleaner separation of “I run an LB” from “I route paths”.

TLS

cert-manager is the de-facto: it watches Ingress resources, requests certs from Let’s Encrypt / ACME / private CA, stores them as Secrets, and renews.

yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata: { name: letsencrypt-prod }
spec:
  acme:
    email: ops@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef: { name: letsencrypt-prod-key }
    solvers:
    - http01:
        ingress: { class: nginx }

The cert-manager.io/cluster-issuer annotation on the Ingress triggers cert issuance/renewal automatically.

Network Policies

Default k8s networking: flat — every pod can talk to every other pod. Network Policies restrict this.

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: orders-allow, namespace: prod }
spec:
  podSelector: { matchLabels: { app: orders } }
  policyTypes: [Ingress, Egress]
  ingress:
  - from:
    - podSelector: { matchLabels: { app: api-gateway } }
    ports: [{ port: 8000 }]
  egress:
  - to:
    - podSelector: { matchLabels: { app: postgres } }
    ports: [{ port: 5432 }]
  - to:
    - namespaceSelector: { matchLabels: { name: kube-system } }
    ports: [{ port: 53, protocol: UDP }]   # DNS

“orders pods accept ingress only from api-gateway, egress only to postgres and DNS.” Default-deny once you create any policy for a pod.

Network policies need a CNI that enforces them: Calico, Cilium, Weave. Flannel doesn’t enforce; AWS VPC CNI requires Calico add-on or recent versions.

DNS inside the cluster

CoreDNS resolves:

text
orders                    → orders.<current-ns>.svc.cluster.local  (in-namespace short name)
orders.prod               → orders.prod.svc.cluster.local
orders.prod.svc.cluster.local → ClusterIP
<pod-ip-dotted>.<ns>.pod.cluster.local → pod IP (rarely useful)

DNS gotchas:

  • ndots:5 in /etc/resolv.conf makes external.com first try external.com.prod.svc.cluster.local, etc. — 5 failed lookups before falling back. For chatty external calls, set dnsPolicy: ClusterFirstWithHostNet carefully or override ndots.
  • DNS caching in JVMs / language runtimes — Python’s socket.getaddrinfo doesn’t cache by default; requests/httpx don’t cache (relies on the OS / glibc). Usually fine; jvm/glibc issues are language-specific.
  • A pod restart changes the pod IP. Service ClusterIPs stay stable until the Service is deleted.

North-south vs east-west

Direction What handles it
north-south (client → cluster) Ingress / Gateway API / API Gateway
east-west (pod → pod) Service + Network Policy + (optionally) Service Mesh

The mesh is the layer that adds mTLS + retries + tracing for east-west traffic without touching app code.

AWS specifics: ALB Ingress Controller

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: public
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    # bypass NodePort
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:...
spec:
  ...

The AWS Load Balancer Controller watches Ingress objects, provisions an ALB, creates Target Groups, registers pod IPs (with target-type: ip for direct, no NodePort hop). Most efficient pattern on EKS.

Egress: how pods reach the internet

By default, pods egress out via the node’s IP (NAT). On EKS:

  • Public subnets → node IP is public; pods egress directly.
  • Private subnets + NAT Gateway → pods egress via NAT GW. Costs $$ at high throughput.
  • VPC Endpoints (Interface / Gateway) → in-cluster traffic to S3, DynamoDB, etc. without NAT.

For cost-sensitive workloads, VPC Endpoints for S3 + ECR save a lot vs NAT egress.

Common bugs

  • “Service exists, no endpoints.” Selector doesn’t match pod labels, or pods are not ready. kubectl describe svc + kubectl get endpoints.
  • “Ingress works internally, 502 externally.” ALB security group doesn’t allow 443 from internet; or target group health checks fail (wrong path or no readiness probe at that path).
  • “DNS lookup is slow.” ndots:5 thrashing on external hostnames. Use FQDNs (s3.amazonaws.com.) with trailing dot, or override.
  • “Network policy applied, nothing works.” Default-deny took effect; you forgot to allow egress to DNS (53/UDP to kube-system).
  • “TLS cert renewal failing.” ACME challenge fails because Ingress wasn’t routing /.well-known/acme-challenge/... correctly. cert-manager logs show this.

Interview angle 6

  • “What’s the difference between a Service and an Ingress?” — Service is L3/L4 (TCP/UDP) and gives a stable ClusterIP for a set of pods. Ingress is L7 (HTTP) and routes by host/path to backend Services. One LB can front many Services through one Ingress.
  • “How do you do TLS termination?” — cert-manager + Ingress: cert-manager requests certs from Let’s Encrypt, stores them as Secrets, Ingress controller reads them and terminates TLS at the LB.
  • “How does pod A reach pod B in another namespace?”http://<service>.<namespace> resolves via CoreDNS. NetworkPolicy can restrict cross-namespace flow.
  • “What does a NetworkPolicy default to?” — without any policy, all-allow. The moment you create a policy targeting a pod, that pod becomes default-deny except for what your policy allows. Forgetting DNS egress (53/UDP) is the classic gotcha.
  • “Ingress vs Service Mesh?” — Ingress is north-south (external → cluster). Service mesh is east-west (in-cluster service-to-service). Different jobs; commonly combined.
  • “Gateway API vs Ingress?” — Gateway API is the GA-as-of-1.29 successor with better separation of concerns (Gateway = LB, HTTPRoute = route rules, GRPCRoute, etc.) and richer features (header-based routing, traffic splitting). New deployments should prefer it.