Kubernetes Deployment Service and Ingress wiring in one manifest

devops-kubernetes

Three resources applied at once, and traffic still returned 503. The Ingress looked fine at a glance , the controller was running, the pod was up, the Service existed. The problem was a one-character mismatch between the service.name field in the Ingress backend and the actual Service name. Kubernetes didn’t error on apply. It just silently routed to nothing.

That’s the thing about Ingress debugging: kubectl apply won’t tell you the backend is broken. You have to run kubectl describe ingress web-app-ingress -n production and look for backend resolution errors in the output. That’s where the real feedback lives.

deployment.yaml , labels, probes, and the port that doesn’t actually bind anything

The manifest below puts all three resources in one file separated by ---. The Deployment runs three replicas of myrepo/web-app:1.4.2 in the production namespace. One thing that trips people up: containerPort: 8080 in the Deployment spec is documentation, not configuration. The container process has to actually bind that port itself. Kubernetes doesn’t enforce it.

What does enforce readiness is the readinessProbe. With httpGet pointed at /healthz on port 8080, the pod won’t receive traffic until that endpoint returns 2xx. The initialDelaySeconds: 5 gives the process time to start before the first check, and periodSeconds: 10 keeps checking on an interval after that. Skip the probe and you’ll get 502s during deploys when the new pod is registered before the app is ready.

The spec.selector.matchLabels on the Deployment and the spec.template.metadata.labels on the pod template have to be identical. Same rule applies to the Service’s spec.selector. All three point to app: web-app here. Change one and traffic stops routing, no error surfaced on apply.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: myrepo/web-app:1.4.2
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: web-app-svc
  namespace: production
spec:
  selector:
    app: web-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-app-svc
                port:
                  number: 80

The Service type here is ClusterIP, which is the default when you don’t specify spec.type. That means it’s only reachable inside the cluster. External access goes through the Ingress. If you don’t have an Ingress controller installed , nginx, traefik, whatever your cluster uses , the Ingress resource does nothing. It’s just a config object waiting for something to act on it.

ingressClassName: nginx and why it’s required on 1.22+

Before Kubernetes 1.22, you’d set kubernetes.io/ingress.class: nginx as an annotation. That annotation is deprecated now. The replacement is the ingressClassName field directly in spec. If you omit it on a cluster running 1.22 or later and you have multiple controllers installed, it’s not guaranteed which controller picks up the resource , or whether any does.

After applying, kubectl get ingress -n production will show the ADDRESS column empty until the controller assigns an IP or hostname. That’s normal. Give it a minute. If it stays empty after two or three minutes, the controller probably isn’t watching that namespace or the ingressClassName doesn’t match what the controller was deployed with.

Don’t rename your Service after the Ingress is already live without updating the backend service.name to match. That’s the exact sequence that produced the 503 , Service got renamed during a cleanup, Ingress wasn’t touched, and nginx started returning unavailable with no obvious log entry on the application side.

official docs

Leave a Reply

Your email address will not be published. Required fields are marked *

โ˜• Support us ยท ๐Ÿ’ณ Monobank