Kubernetes Networking: Hoe Services Elkaar Vinden

Service discovery, DNS debugging en de verschillen tussen ClusterIP, NodePort en LoadBalancer — praktisch uitgelegd met voorbeelden die je direct kunt gebruiken.

Jean-Pierre Broeders

Freelance DevOps Engineer

7 maart 20267 min. leestijd

Kubernetes Networking: Hoe Services Elkaar Vinden

Pods deployen is stap één. Maar zodra service A met service B moet praten, wordt het interessant. Kubernetes heeft een eigen netwerkmodel en dat wijkt behoorlijk af van wat de meeste developers gewend zijn van traditionele servers.

Het netwerkmodel in een notendop

Elke Pod krijgt een eigen IP-adres. Geen NAT, geen port mapping tussen Pods. Ze kunnen elkaar gewoon bereiken alsof ze op hetzelfde netwerk zitten. Klinkt simpel, maar onder de motorkap draait er behoorlijk wat magie via CNI plugins als Calico, Flannel of Cilium.

Het probleem: Pod IPs zijn efemeer. Bij elke herstart krijgt een Pod een nieuw adres. Direct op IP-adressen verbinden is dus geen optie. Daar komen Services om de hoek kijken.

ClusterIP, NodePort en LoadBalancer

Drie typen Services, elk voor een ander scenario.

TypeBereikbaar vanafTypisch gebruik
ClusterIPAlleen binnen het clusterInterne communicatie tussen services
NodePortElke node op een vaste poort (30000-32767)Quick & dirty externe toegang, dev/test
LoadBalancerExtern via cloud load balancerProductie-verkeer van buitenaf

ClusterIP is verreweg het meest gebruikt. Een backend API die alleen door de frontend container benaderd wordt? ClusterIP. Geen reden om dat naar buiten te exposen.

apiVersion: v1
kind: Service
metadata:
  name: user-api
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: user-api
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP

Andere Pods bereiken deze service via user-api.production.svc.cluster.local of simpelweg user-api als ze in dezelfde namespace draaien.

NodePort opent een poort op alle nodes. Handig voor testen, maar in productie wil je dit eigenlijk nooit. Het beperkte poortbereik en het feit dat je node-IPs moet kennen maakt het onpraktisch.

LoadBalancer werkt alleen in cloud-omgevingen (of met MetalLB on-prem). Voor elke Service wordt een externe load balancer aangemaakt. Bij tientallen services wordt dat duur — één LoadBalancer per service is zelden wat je wilt.

Ingress: de betere oplossing voor HTTP-verkeer

In plaats van tien LoadBalancers gebruik je één Ingress Controller die HTTP-verkeer routeert op basis van hostnaam of pad.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.nl
      secretName: api-tls
  rules:
    - host: api.example.nl
      http:
        paths:
          - path: /users
            pathType: Prefix
            backend:
              service:
                name: user-api
                port:
                  number: 80
          - path: /orders
            pathType: Prefix
            backend:
              service:
                name: order-api
                port:
                  number: 80

Eén extern IP, meerdere services. TLS-terminatie op Ingress-niveau. Dat scheelt een hoop LoadBalancer-kosten en maakt het beheer overzichtelijk.

DNS debugging: als services elkaar niet vinden

Vroeg of laat gaat er iets mis met DNS in het cluster. De standaard aanpak om te debuggen:

# Start een debug pod
kubectl run dns-debug --image=busybox:1.36 --rm -it -- sh

# Vanuit de pod:
nslookup user-api.production.svc.cluster.local
nslookup kubernetes.default
cat /etc/resolv.conf

Dat resolv.conf bestand is cruciaal. Daar staat welke DNS-server (CoreDNS) gebruikt wordt en welke search domains actief zijn. Een veelgemaakte fout: de CoreDNS pods draaien niet of zitten vast in CrashLoopBackOff.

# Check CoreDNS status
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50

Negen van de tien keer is het een van deze drie:

  • CoreDNS pods zijn crashed (check logs)
  • NetworkPolicy blokkeert DNS-verkeer op poort 53
  • De service naam is verkeerd gespeld (ja, echt)

Headless Services voor StatefulSets

Soms moet je individuele Pods aanspreken in plaats van via een load-balanced Service. Denk aan databases of message brokers waar elke instance een eigen identiteit heeft.

apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  clusterIP: None  # Dit maakt het headless
  selector:
    app: postgres
  ports:
    - port: 5432

Met clusterIP: None maakt Kubernetes geen virtueel IP aan. In plaats daarvan geeft een DNS-lookup alle Pod-IPs terug. In combinatie met een StatefulSet worden individuele Pods bereikbaar via postgres-0.postgres.default.svc.cluster.local, postgres-1.postgres..., enzovoort.

NetworkPolicies: verkeer beperken

Standaard kan elke Pod met elke andere Pod communiceren. In productie is dat een risico. NetworkPolicies werken als een firewall binnen het cluster.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: user-api-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: user-api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

Alleen Pods met label app: frontend mogen bij de user-api op poort 8080. Al het andere verkeer wordt geblokkeerd. Let op: niet elke CNI plugin ondersteunt NetworkPolicies. Flannel doet het standaard niet, Calico en Cilium wel.

Veelgemaakte fouten

Poort verwarring. Een Service heeft port (waar de Service op luistert) en targetPort (de poort van de container). Die hoeven niet gelijk te zijn, maar als ze niet kloppen bereikt het verkeer de Pod gewoon niet. Geen foutmelding, gewoon timeouts.

Namespace vergeten. Services zijn namespace-scoped. user-api vanuit een andere namespace bereiken vereist user-api.production of de volledige FQDN. Zonder namespace zoekt DNS alleen in de eigen namespace.

Selector mismatch. De selector van een Service moet exact matchen met de labels op de Pods. Eén typfout en de Service heeft geen endpoints. Snel te checken:

kubectl get endpoints user-api -n production

Lege endpoints? Dan matcht de selector niet of draaien er geen Pods met die labels.

Wrap-up

Kubernetes networking is niet ingewikkeld zodra het mentale model klopt: Pods hebben IPs, Services bieden stabiele adressen, DNS plakt het aan elkaar. De meeste problemen zitten in configuratiefouten — verkeerde poorten, missende labels, geblokkeerd DNS-verkeer. Met de debug-stappen hierboven kom je er in de meeste gevallen snel achter waar het misgaat.

Wil je op de hoogte blijven?

Schrijf je in voor mijn nieuwsbrief of neem contact op voor freelance projecten.

Neem Contact Op