Introduction: From Cluster to Running Application

You've successfully created a GKE cluster, but it's currently empty. This lesson will guide you through deploying your first containerized application using a Deployment manifest — a YAML file that declaratively describes everything Kubernetes needs to know to run your application.

We'll dissect the manifest section by section, covering container configuration, environment variables, resource management, and health checks. You'll learn how each section works and why it matters for production deployments. Then, you'll see how to apply the manifest to your cluster and verify that your application is running correctly. We'll also briefly cover imperative kubectl commands that let you make quick changes on the fly. By the end of this lesson, you'll be able to write, apply, and verify a complete Deployment manifest on GKE.

Understanding Kubernetes Deployments

A deployment is a Kubernetes resource that manages a set of identical Pods. Before we go further, let's clarify what a pod is. A Pod is the smallest deployable unit in Kubernetes, and it contains one or more containers that share networking and storage. In most cases, a Pod contains just one container, which is your application. When you create a deployment, Kubernetes creates Pods based on the configuration you provide and ensures that the desired number of Pods are always running.

The reason we use deployments instead of creating Pods directly is that deployments provide several critical features.

  • First, they handle replication, meaning you can easily run multiple copies of your application for high availability and load distribution.
  • Second, they provide self-healing capabilities. If a Pod crashes or a node fails, the deployment automatically creates replacement Pods.
  • Third, they enable rolling updates, which means you can update your application to a new version without downtime by gradually replacing old Pods with new ones.
  • Fourth, they maintain a history of your deployments, allowing you to roll back to a previous version if something goes wrong.

When you create a deployment, you specify how many replicas you want. A replica is simply a copy of your . If you set replicas to , Kubernetes will ensure that exactly three are running at all times. If one crashes, Kubernetes immediately creates a new one to maintain the desired count. If you manually delete a , Kubernetes replaces it. This automatic management is what makes Kubernetes reliable for production workloads.

The Deployment Manifest: Structure Overview

The standard way to define applications in Kubernetes is through declarative configuration files, also known as manifests. A manifest is a YAML file that describes the desired state of a resource. You tell Kubernetes what you want, and Kubernetes figures out how to achieve it. This approach is powerful because your configuration is version-controlled, reusable, and easy to review. The upcoming practice exercises will have you work directly with manifest files, so understanding this structure thoroughly is essential.

Here is a complete example of a deployment.yaml file. We'll break down each section in detail throughout the rest of this lesson:

Every Kubernetes manifest starts with three required top-level fields:

  • apiVersion: Specifies the Kubernetes API version to use. For Deployments, this is always apps/v1.
  • kind: The type of resource you're creating. In this case, Deployment.
  • metadata: Contains data that identifies the object, like its and . The field places this Deployment in the namespace, keeping it separate from system components in .
Replicas and Selectors

The spec section is where you define the desired state of your Deployment. The first two fields control how many Pods run and how the Deployment finds them:

  • replicas: The number of identical Pods you want Kubernetes to run. Setting this to 3 means Kubernetes will maintain exactly three copies of your application at all times. If one crashes, a replacement is created immediately.
  • selector: Tells the Deployment which Pods it manages. The matchLabels field specifies a set of key-value pairs that must appear on the Pods. The Deployment only manages Pods whose labels match this selector. This is how a Deployment "finds" its Pods in a cluster that might have hundreds of other Pods running.

The selector labels must match the labels defined in the Pod template (covered next). If they don't match, Kubernetes will reject the manifest with an error.

The Pod Template: Container Basics

The template section is the blueprint for the Pods that the Deployment creates. It has its own metadata and spec, nested inside the Deployment's spec:

  • template.metadata.labels: Labels applied to each Pod. These must include the labels specified in the Deployment's selector.matchLabels so that the Deployment can identify and manage its Pods.
  • template.spec.containers: A list of one or more containers to run inside each Pod. Most Pods contain just one container.
    • name: A name for your container. You'll reference this name when viewing logs or updating the image.
    • image: The Docker image to run. The example path us-central1-docker.pkg.dev/... points to Google Artifact Registry. To pull private images in a real GKE cluster, your nodes need permission. The recommended method on GKE is Workload Identity, which securely grants pods the necessary permissions. An alternative is to use an imagePullSecret.
    • : Declares which network port your container listens on. The entry documents that your application accepts connections on port . While this field is primarily informational, it's important for clarity and is used by some Kubernetes features.
Environment Variables

Environment variables are the standard way to pass configuration to containerized applications without rebuilding container images. They're commonly used for settings like application names, logging levels, feature flags, database connection strings, and port numbers. In the manifest, environment variables are defined as a list under the env key:

Each entry in the env list has two fields:

  • name: The name of the environment variable, which your application code reads at runtime.
  • value: The value to set. Note that values must always be strings in YAML, so numeric values like port numbers should be quoted (e.g., "5000").

You can define as many environment variables as your application needs:

For an environment variable like PORT to have an effect, the application running inside the container must be coded to read it. By externalizing configuration through environment variables, you create a portable application that can be configured differently across environments (development, staging, production) without changing the container image.

Resource Requests and Limits

The resources section specifies how much CPU and memory your container needs. This is crucial for cluster stability and application performance. Without proper resource configuration, your application might not get the resources it needs to run reliably, or it could consume too many resources and impact other applications on the cluster.

There are two subsections, each serving a distinct purpose:

  • requests: The minimum amount of resources Kubernetes guarantees for your container. Kubernetes uses these values when scheduling — it only places the Pod on a node that has enough available capacity to satisfy the requests. For example, cpu: "100m" requests one-tenth of a CPU core (the m suffix stands for millicores, where 1000m equals one full core), and memory: "128Mi" requests 128 mebibytes of memory.
  • limits: The maximum amount of resources your container is allowed to use. If your container tries to use more CPU than its limit, Kubernetes throttles it, slowing it down. If it exceeds its memory limit, Kubernetes terminates and restarts the container (an OOMKilled event). Limits prevent a single faulty application from consuming all node resources and impacting other applications.

Setting appropriate requests and limits is a balancing act. Requests ensure your application has what it needs to start and run reliably, while limits protect the cluster from misbehaving applications. In a production environment, you would monitor your application's resource consumption over time to fine-tune these values for optimal efficiency and stability.

Health Checks: Readiness, Liveness, and Startup Probes

Health checks are essential for running reliable applications. They allow Kubernetes to automatically detect and recover from failures without manual intervention. There are three types of probes, and they serve different purposes:

  • Readiness probe: Determines if the container is ready to start accepting traffic. If the probe fails, Kubernetes stops sending traffic to the Pod but does not restart it. This is useful during application startup when the process is running but not yet fully initialized (e.g., warming up a cache or establishing database connections).
  • Liveness probe: Determines if the container is still healthy. If the probe fails repeatedly, Kubernetes kills the container and restarts it. This helps recover from application deadlocks or other unrecoverable states where the process is running but not functional.
  • Startup probe: Determines if the container application has finished starting up. If a startup probe is configured, liveness and readiness probes do not begin until it succeeds, preventing them from interfering with a slow-starting container. If the startup probe never succeeds, Kubernetes kills the container according to its restart policy. This is the recommended alternative to setting a very large initialDelaySeconds on your liveness probe — it handles slow starters cleanly without delaying failure detection once the application is up.

In the manifest, both the readiness and liveness probes are configured under the container specification:

Both probes use an httpGet check, which sends an HTTP GET request to the specified path and port. If the application responds with a success status code (200–399), the probe passes. If it returns an error or doesn't respond, the probe fails. Two timing fields control when and how often the checks run:

Creating a Namespace and Applying Your Manifest

Now that you understand every section of the manifest, let's deploy it to your cluster. Before applying the manifest, you need to create the apps namespace that the manifest references. Namespaces in Kubernetes provide a way to organize and isolate resources. By creating a dedicated namespace, you separate your applications from system components in kube-system:

You'll see output confirming the namespace was created:

You only need to create the namespace once — it will continue to exist until you explicitly delete it. Now you can apply the manifest to your cluster:

This single command tells Kubernetes to create or update resources to match the state defined in the file. The output confirms the Deployment was created:

Behind the scenes, several things happen when Kubernetes processes your manifest. First, Kubernetes creates the Deployment resource in the apps namespace. Then, the Deployment controller creates a ReplicaSet, which is an intermediate resource that manages the Pods. The ReplicaSet creates the specified number of Pods based on the template in your manifest. Each Pod is scheduled to a worker node, the container image is pulled, the container starts, and the health probes begin checking whether it's ready to accept traffic.

If you later modify your manifest and run kubectl apply -f deployment.yaml again, Kubernetes compares the new desired state to the current state and makes only the necessary changes. This declarative workflow is the foundation of modern infrastructure-as-code practices with Kubernetes.

Quick Changes with Imperative Commands

While manifests are the standard for production deployments, Kubernetes also supports imperative commands that let you make changes directly from the command line without editing a YAML file. These are useful for quick experiments, learning, or one-off changes.

Here are the most common imperative commands for managing deployments:

  • Create a deployment without a YAML file: kubectl create deployment my-web-deploy --image=nginx:alpine --replicas=1 --namespace=apps
  • Set environment variables: kubectl set env deployment/my-web-deploy PORT=80 --namespace=apps
  • Set resource requests and limits: kubectl set resources deployment/my-web-deploy --requests=cpu=100m,memory=128Mi --limits=cpu=500m,memory=256Mi --namespace=apps
  • Patch a resource for more complex changes like adding health probes: kubectl patch deployment my-web-deploy -n apps --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/readinessProbe", "value": {"httpGet": {"path": "/", "port": 5000}, "initialDelaySeconds": 5, "periodSeconds": 10}}]'

Each of these commands triggers a rolling update, where Kubernetes creates new Pods with the updated configuration and terminates the old ones. While imperative commands are convenient for quick changes, they have a key drawback: the changes aren't tracked in a file. If you need to recreate the deployment later, you'd have to remember every command you ran. For this reason, the declarative manifest approach is preferred for any configuration you want to keep, share, or reproduce.

Verifying Your Deployment

After applying your manifest, it's important to verify that everything is working correctly. You can use several kubectl commands to inspect your Deployment and its Pods.

First, check the overall status of your Deployment:

You should see that all replicas are ready and available:

Next, check the Pods:

All Pods should show a Running status with 1/1 containers ready:

The Pod names are generated automatically by combining the Deployment name, the ReplicaSet hash, and a random suffix. The READY column shows that 1 out of 1 containers in each Pod is ready. The STATUS column shows that the Pods are running. The RESTARTS column should be 0 for healthy Pods.

If you want to see more details about your Deployment, use the describe command:

This command shows detailed information about the Deployment, including its configuration, events, and the status of its Pods. You'll see all the settings you configured in your manifest, including environment variables, resource requests and limits, and health check probes. The events section is particularly valuable for troubleshooting, as it often contains clues about what went wrong if something isn't working.

About the Practice Environment

In the upcoming practice exercises, you'll use a simulated environment with mocked command-line tools that replicate the behavior of kubectl on a GKE cluster. This approach allows you to practice the commands you've learned in a fast and focused way, without the overhead of managing a live Kubernetes cluster.

The mocked kubectl is designed to provide the same output you would expect from a real GKE cluster for the commands covered in this lesson. This hands-on practice will solidify your understanding of Deployment manifests and prepare you for working with production GKE clusters.

Summary: Your First Deployed Application

In this lesson, you learned how to deploy your first application to a Kubernetes cluster using a declarative Deployment manifest. You dissected the manifest section by section, understanding how metadata identifies the resource, how replicas and selector control Pod management, how containers defines what runs inside each Pod, how env passes configuration, how resources manages CPU and memory allocation, and how readinessProbe and livenessProbe enable automatic health monitoring and self-healing.

You also learned the essential workflow: create a namespace with kubectl create namespace, apply your manifest with kubectl apply -f, and verify the result with kubectl get and kubectl describe. Finally, you saw how imperative commands can be used for quick, one-off changes as an alternative to editing YAML files. In the upcoming practice exercises, you'll apply this knowledge by configuring and debugging Deployment manifests, preparing you for the next unit on operating your applications.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal