Deploy Applications on Kubernetes

KubernetesKubernetesBeginner
Practice Now

Introduction

In this lab, you will learn how to deploy applications on a Kubernetes cluster. You will start by setting up a local Kubernetes cluster using Minikube, then explore fundamental kubectl commands to interact with the cluster and manage Kubernetes resources. Next, you will create a simple YAML manifest, apply it to the cluster, and verify the deployment status. Finally, you will access the deployed application using kubectl proxy.

The lab covers essential Kubernetes skills, including cluster setup, basic resource management, and application deployment. By the end of this lab, you will have a solid understanding of how to work with Kubernetes and deploy your own applications.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL kubernetes(("`Kubernetes`")) -.-> kubernetes/TroubleshootingandDebuggingCommandsGroup(["`Troubleshooting and Debugging Commands`"]) kubernetes(("`Kubernetes`")) -.-> kubernetes/BasicCommandsGroup(["`Basic Commands`"]) kubernetes(("`Kubernetes`")) -.-> kubernetes/AdvancedCommandsGroup(["`Advanced Commands`"]) kubernetes(("`Kubernetes`")) -.-> kubernetes/ConfigurationandVersioningGroup(["`Configuration and Versioning`"]) kubernetes/TroubleshootingandDebuggingCommandsGroup -.-> kubernetes/proxy("`Proxy`") kubernetes/TroubleshootingandDebuggingCommandsGroup -.-> kubernetes/describe("`Describe`") kubernetes/BasicCommandsGroup -.-> kubernetes/create("`Create`") kubernetes/BasicCommandsGroup -.-> kubernetes/get("`Get`") kubernetes/AdvancedCommandsGroup -.-> kubernetes/apply("`Apply`") kubernetes/ConfigurationandVersioningGroup -.-> kubernetes/version("`Version`") subgraph Lab Skills kubernetes/proxy -.-> lab-434644{{"`Deploy Applications on Kubernetes`"}} kubernetes/describe -.-> lab-434644{{"`Deploy Applications on Kubernetes`"}} kubernetes/create -.-> lab-434644{{"`Deploy Applications on Kubernetes`"}} kubernetes/get -.-> lab-434644{{"`Deploy Applications on Kubernetes`"}} kubernetes/apply -.-> lab-434644{{"`Deploy Applications on Kubernetes`"}} kubernetes/version -.-> lab-434644{{"`Deploy Applications on Kubernetes`"}} end

Start the Kubernetes Cluster

In this step, you'll learn how to start a local Kubernetes cluster using Minikube, which is perfect for development and learning purposes. We'll verify the cluster's status and ensure everything is set up correctly.

First, start the Minikube cluster:

minikube start

Example output:

๐Ÿ˜„  minikube v1.29.0 on Ubuntu 22.04
โœจ  Automatically selected the docker driver
๐Ÿ“Œ  Using Docker driver with root permissions
๐Ÿ”ฅ  Creating kubernetes in kubernetes cluster
๐Ÿ”„  Restarting existing kubernetes cluster
๐Ÿณ  Preparing Kubernetes v1.26.1 on Docker 20.10.23 ...
๐Ÿš€  Launching Kubernetes ...
๐ŸŒŸ  Enabling addons: storage-provisioner, default-storageclass
๐Ÿ„  Done! kubectl is now configured to use "minikube" cluster and "default" namespace

Let's verify the cluster status:

minikube status
kubectl get nodes

Example output:

minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   1m    v1.26.1

These commands confirm that:

  1. Minikube is running
  2. A local Kubernetes cluster has been created
  3. The cluster is ready to use
  4. You have a single-node cluster with control plane capabilities

Learn Basic kubectl Commands and Syntax

In this step, you'll explore fundamental kubectl commands that are essential for managing Kubernetes resources. We'll demonstrate how to interact with your cluster, view resources, and understand basic Kubernetes object management.

Let's start by exploring the cluster's namespaces:

kubectl get namespaces

Example output:

NAME              STATUS   AGE
default           Active   10m
kube-node-lease   Active   10m
kube-public       Active   10m
kube-system       Active   10m

Next, let's view the system components running in the cluster:

kubectl get pods -n kube-system

Example output:

NAME                               READY   STATUS    RESTARTS   AGE
coredns-787d4945fb-j8rhx           1/1     Running   0          15m
etcd-minikube                       1/1     Running   0          15m
kube-apiserver-minikube             1/1     Running   0          15m
kube-controller-manager-minikube    1/1     Running   0          15m
kube-proxy-xb9rz                    1/1     Running   0          15m
kube-scheduler-minikube             1/1     Running   0          15m
storage-provisioner                 1/1     Running   0          15m

Now, let's explore some basic kubectl command patterns:

## Get all resources in the default namespace
kubectl get all

## Describe a specific resource type
kubectl describe nodes minikube

Example output for kubectl get all:

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   20m

Key kubectl command syntax to remember:

  • kubectl [command] [TYPE] [NAME]
  • Common commands: get, describe, create, delete, apply
  • Resource types: pods, deployments, services, nodes

These commands help you:

  1. View cluster resources
  2. Get detailed information about cluster components
  3. Understand the current state of your Kubernetes cluster

Create a Simple YAML Manifest

Before creating your first YAML manifest, let's take a moment to understand the key Kubernetes objects you will work with. These objects help manage and orchestrate your applications:

Understanding Kubernetes Objects

  • Pod: The smallest deployable unit in Kubernetes. A pod encapsulates one or more containers, shared storage, and a network identity. It represents a single instance of a running process in your cluster.
  • Deployment: A higher-level abstraction that manages pods. Deployments provide features like scaling, rolling updates, and rollback capabilities. They ensure that the desired number of pod replicas are running.
  • Service: An abstraction that defines a logical set of pods and a policy to access them. Services enable communication between different components of your application or with external clients.

Hereโ€™s a diagram to clarify their relationships:

graph TD; A[Deployment] -->|Manages| B[Pods] B -->|Contains| C[Containers] B -->|Communicates via| D[Services] D -->|Exposes| E[External Clients]

Understanding these objects is crucial as you proceed to define their configurations in YAML.

YAML Manifest Overview

A YAML manifest in Kubernetes is a declarative way to define the desired state of resources. Using YAML has several benefits:

  1. Declarative Management: You define the "what" (desired state), and Kubernetes handles the "how" (implementation).
  2. Version Control: YAML files can be stored in version control systems (e.g., Git), allowing easy tracking of changes.
  3. Reusability: YAML manifests can be reused across environments (e.g., development, testing, production) with minor modifications.

With this understanding, you are ready to create your first YAML manifest.

Creating a YAML Manifest

Navigate to your project directory:

cd ~/project

Create a new directory for your Kubernetes manifests:

mkdir -p k8s-manifests
cd k8s-manifests

Now, create a YAML file for a simple NGINX pod:

nano nginx-pod.yaml

Paste the following content into the file:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
        - containerPort: 80

Press Ctrl+X, then Y, and Enter to save and exit the file.

Let's break down the YAML manifest:

  • apiVersion: Specifies the Kubernetes API version used for the resource definition.
  • kind: Defines the resource type (Pod in this case).
  • metadata: Provides information about the resource, such as its name and labels.
  • spec: Describes the desired state of the resource.
    • containers: Lists the containers to run in the pod.
    • image: Specifies the container image to use.
    • ports: Exposes the container's port.

Apply the manifest to create the pod:

kubectl apply -f nginx-pod.yaml

Example output:

pod/nginx-pod created

Verify the pod creation:

kubectl get pods
kubectl describe pod nginx-pod

Example output for kubectl get pods:

NAME        READY   STATUS    RESTARTS   AGE
nginx-pod   1/1     Running   0          1m

Now, let's create a more complex deployment manifest:

nano nginx-deployment.yaml

Paste the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80

Key differences from the pod manifest:

  • kind: Deployment: Creates a deployment that manages multiple pods
  • replicas: 3: Ensures 3 identical pods are running
  • selector: Helps the deployment manage the correct pods

Apply the deployment:

kubectl apply -f nginx-deployment.yaml

Verify the deployment:

kubectl get deployments
kubectl get pods

Example output:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           1m

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-xxx-yyy            1/1     Running   0          1m
nginx-deployment-xxx-zzz            1/1     Running   0          1m
nginx-deployment-xxx-www            1/1     Running   0          1m

Apply the YAML Manifest

In this step, you'll dive deeper into the kubectl apply command and explore different ways to apply Kubernetes manifests. We'll build on the previous step's YAML files and demonstrate various application techniques.

First, ensure you're in the correct directory:

cd ~/project/k8s-manifests

Let's create a new directory to organize our manifests:

mkdir -p manifests
cd manifests

Create a simple web application manifest:

nano web-app.yaml

Add the following content:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:alpine
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80

This manifest demonstrates:

  • Multiple resources in a single file (Deployment and Service)
  • Use of --- to separate resources
  • A ClusterIP service to expose the deployment internally

Apply the manifest using different methods:

## Method 1: Apply entire file
kubectl apply -f web-app.yaml

## Method 2: Apply from a directory
kubectl apply -f .

## Method 3: Apply with URL (optional)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/examples/master/guestbook/redis-master-deployment.yaml

Example output:

deployment.apps/web-app created
service/web-service created

Explore additional kubectl apply options:

## Dry run to see what would be created
kubectl apply -f web-app.yaml --dry-run=client

## Verbose output to see more details
kubectl apply -f web-app.yaml -v=7

Verify the resources:

## List deployments
kubectl get deployments

## List services
kubectl get services

## Describe the deployment
kubectl describe deployment web-app

Example output:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           3m33s
redis-master       0/1     1            0           23s
web-app            2/2     2            2           42s
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP   8m28s
web-service   ClusterIP   10.106.220.33   <none>        80/TCP    46s

Learn about declarative vs imperative management:

  • kubectl apply: Declarative (recommended)
    • Tracks and manages resource state
    • Allows incremental updates
  • kubectl create: Imperative (less flexible)
    • Creates resources directly
    • Doesn't handle updates as smoothly

Verify the Deployment Status

In this step, you'll learn how to inspect and verify the status of Kubernetes deployments using various kubectl commands. We'll explore different ways to gather information about your running resources.

First, ensure you're in the project directory:

cd ~/project/k8s-manifests/manifests

Let's start by listing all deployments:

kubectl get deployments

Example output:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           4m44s
redis-master       1/1     1            1           94s
web-app            2/2     2            2           113s

Get more detailed information with wide output:

kubectl get deployments -o wide

Example output:

NAME               READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES                      SELECTOR
nginx-deployment   3/3     3            3           4m58s   nginx        nginx:latest                app=nginx
redis-master       1/1     1            1           108s    master       registry.k8s.io/redis:e2e   app=redis,role=master,tier=backend
web-app            2/2     2            2           2m7s    web          nginx:alpine                app=web

Inspect the pods associated with the deployment:

kubectl get pods -l app=web

Example output:

NAME                      READY   STATUS    RESTARTS   AGE
web-app-xxx-yyy           1/1     Running   0          10m
web-app-xxx-zzz           1/1     Running   0          10m

Use kubectl describe for in-depth resource information:

kubectl describe deployment web-app

Example output:

Name:                   web-app
Namespace:              default
CreationTimestamp:      [current timestamp]
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=web
Replicas:               2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=web
  Containers:
   web:
    Image:        nginx:alpine
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   web-app-xxx (2/2 replicas created)
Events:          <some deployment events>

Check the services:

kubectl get services

Example output:

NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP   10m
web-service   ClusterIP   10.106.220.33   <none>        80/TCP    2m47s

Describe the service:

kubectl describe service web-service

Example output:

Name:              web-service
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=web
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.106.220.33
IPs:               10.106.220.33
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.0.7:80,10.244.0.8:80
Session Affinity:  None
Events:            <none>

Key things to look for:

  • Deployment READY status (e.g., 2/2)
  • Pod STATUS (Running)
  • Service Endpoints
  • Any potential warnings or errors in the description

Access the Application using kubectl proxy

In this step, you'll learn how to access your Kubernetes applications using kubectl proxy, which creates a secure connection to the Kubernetes API server.

First, ensure you're in the project directory:

cd ~/project/k8s-manifests/manifests

Start the kubectl proxy:

kubectl proxy --port=8080 &

Example output:

Starting to serve on 127.0.0.1:8080

Press Ctrl+C, if the terminal hangs.

Now, let's find the pod names and details:

## Get pod names
kubectl get pods -l app=web

Example output:

NAME                      READY   STATUS    RESTARTS   AGE
web-app-xxx-yyy           1/1     Running   0          20m
web-app-xxx-zzz           1/1     Running   0          20m

Construct the API path to access a specific pod:

## Replace 'xxx-yyy' with your actual pod name from the previous output
POD_NAME=$(kubectl get pods -l app=web -o jsonpath='{.items[0].metadata.name}')

Access the pod details via proxy:

curl http://localhost:8080/api/v1/namespaces/default/pods/${POD_NAME}/proxy/

Example output:

<!doctype html>
<html>
  <head>
    <title>Welcome to nginx!</title>
    ...
  </head>
  <body>
    <h1>Welcome to nginx!</h1>
    ...
  </body>
</html>

Let's explore more proxy capabilities:

## List all pods in the default namespace via proxy
curl http://localhost:8080/api/v1/namespaces/default/pods/

## Get detailed pod information
curl http://localhost:8080/api/v1/namespaces/default/pods/${POD_NAME}

To stop the proxy, find its process and terminate:

## Find proxy process
jobs

Key points about kubectl proxy:

  • Creates a secure, authenticated connection to the Kubernetes API
  • Allows local access to cluster resources
  • Useful for debugging and exploration
  • Works only on localhost for security reasons

Summary

In this lab, you learned how to start a local Kubernetes cluster using Minikube, verify the cluster's status, and explore fundamental kubectl commands for managing Kubernetes resources. You learned to view the cluster's namespaces, get information about nodes, and understand the basic Kubernetes object management. These steps provide a solid foundation for deploying applications on Kubernetes.

You then proceeded to create a simple YAML manifest, apply it to the cluster, and verify the deployment status using kubectl commands. This hands-on experience allowed you to understand the process of deploying applications on Kubernetes and interact with the cluster effectively.

Other Kubernetes Tutorials you may like