Deployment, Service and Ingress

A Deployment runs the replicas, a Service gives them one stable address, and an Ingress routes outside traffic to it.

Syntaxkubectl apply -f deployment.yaml -f service.yaml -f ingress.yaml

Kubernetes is told what you want, not what to do. You write manifests (YAML files describing objects), apply them, and controllers work to make the cluster match. A Node HTTP service normally needs three.

Deployment

A Deployment names the image to run and how many replicas (identical running copies) to keep. It also carries the environment variables for each copy. If a copy exits, the Deployment starts another.

Service

Each replica gets its own IP address, and that address changes whenever the replica is replaced. A Service is one stable name and address in front of all of them, spreading connections across the ready ones. port is the port the Service answers on; targetPort is the port inside the container it forwards to.

Ingress

A Service of the default type, ClusterIP, is reachable from inside the cluster only. An Ingress maps an outside hostname and path to a Service, and it needs an ingress controller running in the cluster to act on it. Without one, the Ingress object exists and nothing happens.

What Node has to do

Listen on every interface. server.listen(port) with the host left out binds the unspecified address — :: where IPv6 is available, otherwise 0.0.0.0 — and accepts connections from anywhere. The longhand server.listen(port, '0.0.0.0') says the same thing explicitly. server.listen(port, '127.0.0.1') accepts connections only from inside the container itself, so the Service and the probes get nothing.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Putting a Node HTTP service behind one stable in-cluster name other services can call.
  • Exposing that service on a public hostname through an ingress controller.
  • Finding out why a Service returns nothing when the pods are running.
  • Giving each environment its own replica count and image tag in one file.

More examples

The Deployment and the Service, side by side

Two labels do the wiring: the Deployment's selector finds its own pods, and the Service's selector finds the same pods. Referring to targetPort by name rather than number means the number lives in one place. A Service named api also creates the DNS name api, which is how another service in the same namespace calls this one.

Example · yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api            # must match the pod labels below
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: registry.example.com/api:1.4.2
          ports:
            - name: http        # a name the Service and probes can refer to
              containerPort: 8080
          env:
            - name: PORT
              value: "8080"
---
apiVersion: v1
kind: Service
metadata:
  name: api                 # other pods reach this as http://api
spec:
  selector:
    app: api                # picks up every pod with this label
  ports:
    - port: 80              # the port the Service answers on
      targetPort: http      # the named container port; 8080 works too

An Ingress for one hostname

The port here is the Service's port (80), not the port Node listens on (8080), which is the pair most often mixed up. pathType has no default and the API rejects a path without it. If requests return nothing after applying this, check that a controller matching ingressClassName is actually installed.

Example · yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api
spec:
  ingressClassName: nginx     # which controller should handle this
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix  # required; Prefix or Exact
            backend:
              service:
                name: api
                port:
                  number: 80  # the Service's port, not the container's

Seeing what the cluster did with it

An empty endpoint list is the single most useful diagnostic: it separates a labelling mistake from an application problem. port-forward takes the Ingress and the load balancer out of the picture, so a failure there is inside the cluster and a success there points at the Ingress.

Example · bash
kubectl apply -f deployment.yaml -f service.yaml -f ingress.yaml

# Did the pods come up, and are they Ready?
kubectl get pods -l app=api

# Does the Service have endpoints? Empty means no pod matches the selector,
# or no pod is passing its readiness check.
kubectl get endpointslices -l kubernetes.io/service-name=api

# Reach the Service from your machine without an Ingress, for a quick check.
kubectl port-forward service/api 8080:80

Discussion

  • Be the first to comment on this lesson.