Stackvora.tech

Your First Deployment & Service

Deploying an application to Kubernetes involves defining its desired state and how it should be exposed to consumers. This typically entails creating at…

Deploying an application to Kubernetes involves defining its desired state and how it should be exposed to consumers. This typically entails creating at least two core Kubernetes objects: a Deployment to manage the application's lifecycle and a Service to provide stable network access. This article details the essential components of a basic web application deployment, using Nginx as an example, and its corresponding Service definition.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web-container
        image: nginx:alpine
        ports:
        - containerPort: 80
          name: http
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
  name: web
  labels:
    app: web
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
  - port: 80
    targetPort: http # Refers to the name of the container port, 'http'
    protocol: TCP

Understanding the Deployment Object

The Kubernetes Deployment object manages a replicated set of Pods. It declares the desired state for your application, and the Kubernetes controller manager works to maintain that state. If a Pod crashes or a node fails, the Deployment automatically replaces the affected Pods to ensure the desired replica count is met.

apiVersion: apps/v1

This specifies the API version for the Deployment object. apps/v1 is the stable and recommended API version for Deployments since Kubernetes 1.9.

kind: Deployment

This explicitly declares the type of Kubernetes object being defined.

metadata

The metadata section provides identifying information for the Deployment.

  • name: web: A unique name for this Deployment within its namespace. This name will be used to refer to the Deployment in kubectl commands.
  • labels: { app: web }: Key-value pairs that help organize and select Kubernetes objects. These labels are crucial for the Service to identify the Pods it should route traffic to.

spec

The spec section defines the desired state of the Deployment.

  • replicas: 3: Instructs the Deployment controller to maintain three identical Pods running your application. If any Pods fail, new ones will be created until this count is reached.
  • selector: { matchLabels: { app: web } }: This field is critical. It defines how the Deployment finds the Pods it manages. It must match the labels defined in the Pod template. In this case, any Pod with the label app: web is considered part of this Deployment.
  • template: This is the Pod template, which describes the Pods that the Deployment will create. Any changes here will trigger a rolling update of the Pods.
    • metadata: { labels: { app: web } }: Labels for the Pods themselves. These labels must match the Deployment's selector.
    • spec:: Defines the Pod's configuration.
      • containers:: A list of containers to run inside the Pod. A Pod can contain multiple containers that share network and storage, but typically one primary application container is used.
        • name: web-container: A unique name for this container within the Pod.
        • image: nginx:alpine: The Docker image to pull and run. nginx:alpine is a lightweight Nginx image based on Alpine Linux.
        • ports: [{ containerPort: 80, name: http }]: Declares that the container listens on TCP port 80. The name: http is optional but highly recommended for readability and for referencing in Service definitions (as seen later with targetPort: http).
        • resources:: Defines resource requests and limits for the container. This is crucial for scheduling and stability.
          • requests:: The minimum resources guaranteed to the container. The scheduler uses these values to place Pods on nodes.
            • memory: "64Mi": Request 64 mebibytes of memory.
            • cpu: "100m": Request 100 millicores (0.1 CPU core).
          • limits:: The maximum resources the container can consume. If a container exceeds its memory limit, it will be terminated. If it exceeds its CPU limit, it will be throttled.
            • memory: "128Mi": Limit memory to 128 mebibytes.
            • cpu: "200m": Limit CPU to 200 millicores (0.2 CPU core).

Understanding the Service Object

While a Deployment ensures your Pods are running, Pods are ephemeral and can be replaced with new IP addresses. A Kubernetes Service provides a stable network endpoint for a set of Pods. It acts as an abstraction, allowing clients to access the application without needing to know the specific IP addresses of individual Pods.

apiVersion: v1

This specifies the API version for the Service object. v1 is the stable API version for Services.

kind: Service

This explicitly declares the type of Kubernetes object being defined.

metadata

The metadata section provides identifying information for the Service.

  • name: web: A unique name for this Service within its namespace. This name is often used for DNS resolution within the cluster.
  • labels: { app: web }: Similar to Deployments, labels help organize and identify the Service.

spec

The spec section defines the behavior of the Service.

  • type: LoadBalancer: This defines how the Service is exposed.
    • ClusterIP (default): Exposes the Service on an internal IP address within the cluster. Only reachable from within the cluster.
    • NodePort: Exposes the Service on each Node's IP at a static port (the NodePort). Makes the Service accessible from outside the cluster using <NodeIP>:<NodePort>.
    • LoadBalancer: Exposes the Service externally using a cloud provider's load balancer. This type automatically provisions an external IP address and load balancer, making your application directly accessible from the internet (e.g., AWS ELB, GCP Load Balancer). This requires a cloud provider integration.
    • ExternalName: Maps the Service to a DNS name, not to a selector.
  • selector: { app: web }: This is how the Service finds the Pods it should route traffic to. It uses labels to select all Pods that have app: web. This is the crucial link between your Deployment and your Service.
  • ports:: Defines the ports the Service will expose.
    • port: 80: The port on the Service itself that clients will connect to.
    • targetPort: http: The port on the Pods (containers) that the Service will forward traffic to. It's best practice to use the named port (http in this case) defined in the container spec for clarity and flexibility, rather than the raw number (80). This allows you to change the container's internal port without modifying the Service, as long as the port name remains consistent.
    • protocol: TCP: The IP protocol used for the port. Can be TCP or UDP.

Applying the Configuration

Once you have your YAML manifest, you can deploy it to your Kubernetes cluster using kubectl.

kubectl apply -f web.yaml

This command creates or updates the Deployment and Service objects defined in web.yaml.

Verifying the Deployment

After applying the configuration, you can check the status of your created objects:

kubectl get pods -l app=web

This command lists Pods with the label app=web. You should see three Pods in a Running state.

NAME                         READY   STATUS    RESTARTS   AGE
web-6789abcd-jklmn           1/1     Running   0          2m
web-6789abcd-opqrs           1/1     Running   0          2m
web-6789abcd-tuvwx           1/1     Running   0          2m
kubectl get svc web

This command shows the details of your web Service.

NAME   TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
web    LoadBalancer   10.96.10.100   <pending>      80:30000/TCP   2m

Initially, EXTERNAL-IP might show <pending>. It takes a short time for the cloud provider to provision and assign an external IP address to the LoadBalancer. Keep checking with kubectl get svc web --watch until an IP address appears. Once assigned, you can access your Nginx welcome page using that external IP address in your browser.

Common Pitfalls

  • Label Mismatch: The most common error. The selector.matchLabels in your Deployment and the selector in your Service must exactly match the metadata.labels in your Pod template. If they don't, the Service won't find any Pods to route traffic to, and the Deployment won't manage the correct Pods.
  • Port Mismatch: Ensure that targetPort in your Service points to the correct containerPort (or its named equivalent) defined in your Deployment's Pod template. If the Nginx container listens on 80, but your Service's targetPort is 8080, traffic won't reach the application.
  • LoadBalancer External IP Pending: When using type: LoadBalancer on a cloud provider, it takes time for the external IP to be provisioned. Don't assume an error if it's <pending> immediately after deployment. If it remains pending for an unusually long time (e.g., over 5-10 minutes), check your cloud provider's network logs or Kubernetes events for provisioning failures (kubectl describe svc web).
  • Resource Limits and Requests: While not directly preventing deployment, inadequate resource requests can lead to Pods not being scheduled, and insufficient limits can cause Pods to be evicted (OOMKilled) or throttled, leading to application instability.
  • Image Pull Errors: Verify the image name and tag (e.g., nginx:alpine). If the image doesn't exist or there's an issue with container registry access (e.g., private registry authentication), Pods will enter an ImagePullBackOff or ErrImagePull state. Check kubectl describe pod <pod-name> and kubectl logs <pod-name> for details.

Back to the knowledge base · Ask the AI assistant