ACME is embarking on deploying a hyperscale web application.
Concerns:
Decoupling Applications from Infrastructure
Automatic Scaling
Seamless Upgrades
High Availability (HA)
Security
Cost Management
Performance Monitoring and Logging
Disaster Recovery and Data Persistence
Compliance and Data Sovereignty
Service Mesh Integration
Developer Productivity
Interoperability and Integration
Considered Alternatives:
Docker Compose
Docker Swarm
Apache Mesos
Nomad
Kubernetes Achievements:
Decoupling: Application from Infrastructure
Scaling: Automatic and Efficient
Upgrades: Seamless with Zero Downtime
HA: Robust Support for High Availability
Security: Enhanced with RBAC, Network Policies, PSP
Resource Utilization: Optimized and Efficient
Monitoring: Integration with Tools like Prometheus
Data Persistence: Support for StatefulSets and Persistent Volumes
Flexibility: Across Various Environments
Service Mesh: Easy Integration with Istio/Envoy
Developer Productivity: Automation of Deployment and Management
Integration: With CI/CD, Databases, Monitoring Tools, etc.
Non-Kubernetes solutions face challenges with scalability, automation, complexity in management, cost-effectiveness at scale, and lack of ecosystem support.
This workshop focuses on deploying, scaling, and upgrading applications with Kubernetes, addressing these challenges by leveraging Kubernetes’ comprehensive and extensible platform, designed for efficient container orchestration.
Subsections of Fortinet XPerts 2026
Kubernetes TECWorkshop
Welcome to K8s 101 workshop
About Kubernetes
Kubernetes is an open-source container orchestration platform that automates the deployment, scaling, and management of containerized applications. Originally developed by Google and now maintained by the Cloud Native Computing Foundation (CNCF), Kubernetes has become the de facto standard for managing containerized workloads in production environments. K8s is short name of Kubernetes.
Workshop Objectives
In this workshop, you will:
Understand why containers are important, and how they fit into a microservices architecture orchestrated by K8s
Build a Kubernetes cluster on a Virtual Machine (VM)in Azure.
Gain a fundamental understanding of Kubernetes concepts.
Learn how to deploy, manage, and scale applications using Kubernetes.
Explore key Kubernetes resources such as Pods, Deployments, Services, and more.
Gain hands-on experience with practical examples.
Building a K8s cluster from scratch vs using Managed Services like AKS,EKS,GKE?
Building a self-managed cluster instead of relying on managed services for learning Kubernetes offers beginners hands-on experience, deeper understanding of core concepts, and greater control over configurations.
Building a cluster offers deeper understanding of core Kubernetes components such as pods, nodes, and controllers.
In production environments, many organizations use managed Kubernetes services provided by major cloud providers.
These services—Amazon Elastic Kubernetes Service (EKS), Azure Kubernetes Service (AKS), and Google Kubernetes Engine (GKE)—are managed Kubernetes offerings from AWS, Azure, and Google Cloud. They provide features such as automated updates, scaling, monitoring, and seamless integration with other cloud services.
However, the choice often depends on factors such as existing cloud provider relationships, specific feature requirements, pricing, and the level of control and customization desired by the organization.
Formatting Conventions
Yellow Highlighted Text:
Indicates that is a command lines you can execute on shell.
Black Box:
Contains bash commands intended for you to copy and paste directly into your terminal for your task.
Grey Box:
Displays the output from executed commands. The text above each box explains the command’s purpose and intent.
Unless explicitly stated otherwise, all CLI commands should be executed directly in the Azure Cloud Shell.
If you have prior Kubernetes experience, move quickly through K8s install and spend your time in Kubernetes in depth. Do not skip Workshop Quickstart and Setup — it runs the provisioning form and the Terraform that creates the two Azure VMs every later task depends on.
Subsections of Workshop Logistics
Quickstart
Provisioning the Azure environment (40min)
Provision your Azure Environment, enter your Email address and click Provision
Provision your Portal Accounts
Required for hands-on portion of this workshop
Info
Enter your email and click **Provision Accounts** once. You'll see a live progress bar while your account is created — this typically takes a few minutes. When it's done, your credentials (username and sign-in info) will appear directly on this page, and a copy is also sent to your email as a backup. If you reload this page or come back later, your credentials will still be here — no need to re-submit. If you're continuing from an earlier workshop in this session, you'll be offered the option to reuse your existing credentials instead of provisioning new ones.
Tasks
Setup Azure Cloud Shell
Run Terraform
Verify Terraform
Subsections of Quickstart
Task 1 - Setup Azure Cloud Shell
Azure Cloud Shell is a browser-based command-line environment built into the Azure portal. It includes the Azure CLI and common tools needed for Kubernetes tasks, ensuring a consistent environment for everyone without the need to install tools locally.
You can manually specify your username (found in your Azure Account email) in the command If your Workshop Azure account login is se31@ftntxxxxx.onmicrosoft.com, your username is se31, and the command to enter is:
Use kubeadm to install a self-managed Kubernetes cluster with 1 master node and 1 worker node on Azure ubuntu VM.
Choose your Kubernetes
Although Cloud-Managed Kubernetes becoming the popular choice for enteprise to use in production network, But Self Managed Kubernetes give users full control over their Kubernetes environments. Choosing the right method to install Self Managed Kubernetes can vary significantly based on the intended use case, from development and testing environments to production deployments. Here’s a short description of different ways to install Kubernetes, tailored to specific needs:
For Development and Testing
Minikube:
Best For: Individual developers and small teams experimenting with Kubernetes applications or learning the Kubernetes ecosystem.
Kind (Kubernetes in Docker):
Best For: Kubernetes contributors, developers working on CI/CD pipelines, and testing Kubernetes configurations.
OrbStack Kubernetes:
Best for: development and testing on MacOS desktop with Apple Silicon or intel chipset, it eliminates the complexity of setting up and managing full-fledged Kubernetes clusters.
For Production Deployment
Kubeadm:
Best For: Organizations looking for a customizable production-grade Kubernetes setup that adheres to best practices. Suitable for those with specific infrastructure requirements and those who wish to integrate Kubernetes into existing systems with specific configurations.
Kubespray:
Best For: Users seeking to deploy Kubernetes on a variety of infrastructure types (cloud, on-premises, bare-metal) and require a tool that supports extensive customization and scalability.
Rancher:
Best For: Organizations looking for an enterprise Kubernetes management platform that simplifies the operation of Kubernetes across any infrastructure, offering UI and API-based management.
Subsections of K8s install
Task 3 - Install Kubernetes
Use kubeadm to install kubernetes
This task builds a simple Kubernetes cluster with one control-plane node and one worker node.
The scripts in this workshop now use the Kubernetes package repository at pkgs.k8s.io, containerd as the container runtime, kubeadm for cluster bootstrap, and Calico as the CNI.
Info
This lab pins Kubernetes to `v1.30` for repeatable workshop builds and FortiAIGate readiness. FortiAIGate 8.0.1 requires Kubernetes 1.25.0 or later, plus working CNI, Helm, ingress, and storage prerequisites.
The master and worker scripts are non-interactive. They automatically overwrite existing Kubernetes apt keyring files and use apt-get -y, so students should not see manual y/N prompts during package setup.
If you need to pin a different supported Kubernetes minor version, export it before running the scripts, for example:
exportK8S_MINOR=v1.30
Use Azure Cloud Shell as kubernetes client
To use Azure Cloud Shell as a Kubernetes client, ensure you have completed your Terraform deployment in Azure Cloud Shell. Azure Cloud Shell comes with kubectl pre-installed, facilitating Kubernetes operations.
Preflight check
There is no cluster yet, so this check is about the two lab VMs. Confirm Terraform finished and that both VM DNS names are published. From Azure Cloud Shell:
The FQDN value is unique for each student and is pulled from Terraform output. It is added to the Kubernetes API server certificate so kubectl can connect from Azure Cloud Shell without a TLS certificate error.
Install Kubernetes packages and container runtime on the worker node.
kubectl get nodes -o wide
kubectl get pods -A
kubectl cluster-info
You can also watch the worker become Ready:
watch kubectl get nodes
NAME STATUS ROLES AGE VERSION
node-worker Ready <none> 10m v1.30.x
node-master Ready control-plane 15m v1.30.x
Container runtime should show containerd, for example:
kubectl get nodes -o wide
# CONTAINER-RUNTIME: containerd://...
Summary
This chapter installs Kubernetes using kubeadm. The workshop creates one control-plane node and one worker node. The current script uses containerd instead of CRI-O and installs Kubernetes through pkgs.k8s.io.
What is the version of this Kubernetes server?
Click for Answer…
kubectl version
kubectl get nodes
Expected server version is the package available in the configured K8S_MINOR repository, for example v1.30.x.
What is the container runtime name and version?
Click for Answer…
kubectl get nodes -o wide
Expected runtime is containerd://....
Describe the general steps to add a new VM as worker node in this cluster.
Click for Answer…
Create a new VM.
Run scripts/install_kubeadm_workernode.sh on the new VM.
Get a fresh join command from the master node using kubeadm token create --print-join-command.
Run the join command on the new worker node.
Verify with kubectl get nodes.
IN CASE YOU RUNNING INTO PROBLEM
You can reinstall Kubernetes or delete and recreate the VMs.
Re-Install Kubernetes
Warning
If you want a clean reset, run the following commands on both master and worker nodes. This removes Kubernetes state from the node.
This task deploys an nginx application and demonstrates basic Kubernetes scaling with HPA.
The updated script keeps the original workshop flow but updates the supporting components:
Local Path Provisioner for simple lab storage
Metrics Server for HPA CPU metrics
MetalLB for LoadBalancer testing
Kong Ingress Controller for ingress testing
cert-manager for a self-signed test certificate
nginx deployment and HPA
Preflight check
This task builds on the two-node cluster from Task 3 - Install Kubernetes. Confirm that cluster is healthy and that the default namespace is still empty before you run the deploy script. From Azure Cloud Shell:
kubectl get nodes
kubectl get pods
Both nodes must report Ready:
NAME STATUS ROLES AGE VERSION
node-worker Ready <none> 10m v1.30.x
node-master Ready control-plane 15m v1.30.x
The default namespace should have no workloads yet. kubectl reports an empty list on stderr:
No resources found in default namespace.
If a node is missing or NotReady, go back to Task 3 and finish it before continuing.
kubectl get nodes
kubectl get pods -A
kubectl get deployment nginx-deployment
kubectl get hpa
kubectl get ingress
kubectl get svc -A
Expected nginx deployment:
NAME READY UP-TO-DATE AVAILABLE
nginx-deployment 2/2 22
Expected HPA object:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-hpa Deployment/nginx-deployment <unknown>/50% 2102 34s
After Metrics Server starts collecting CPU metrics, the HPA target changes from <unknown> to a CPU percentage.
FortiAIGate readiness checks
After this task completes, run these checks before starting a FortiAIGate deployment:
kubectl cluster-info
Expected output
Kubernetes control plane is running at https://k8sXX-master.eastus.cloudapp.azure.com:6443
CoreDNS is running at https://k8sXX-master.eastus.cloudapp.azure.com:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION
node-master Ready control-plane 53m v1.30.14
node-worker Ready <none> 50m v1.30.14
Both commands must succeed. The exact version numbers depend on the helm build installed in your environment, so do not compare them literally — what matters is that helm version prints a version.BuildInfo{...} line rather than command not found, and that helm list returns without a connection error.
helm list confirms helm can reach the cluster with your kubeconfig. With no releases installed it prints only the column header and exits 0. An error mentioning the Kubernetes API server instead means your kubeconfig is not working — recheck the kubeconfig step in Task 3.
kubectl get ingressclass
Expected output — the deploy script you ran above installs the Kong Ingress Controller, so one IngressClass named kong must already exist:
NAME CONTROLLER PARAMETERS AGE
kong ingress-controllers.konghq.com/kong <none> 9h
The AGE column just reflects how long ago the deploy script installed Kong, so your value will differ. If this list is empty, the Kong step of the deploy script did not complete — rerun it before continuing.
kubectl get storageclass
Expected output
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 52m
Generate load
Run a temporary load generator from the cluster:
kubectl run -i --tty load-generator --rm --image=busybox:1.36 --restart=Never -- /bin/sh
Inside the shell, run:
while true; do wget -q -O- http://nginx-deployment.default.svc.cluster.local; done
In another terminal, watch HPA:
watch kubectl get hpa
You can also watch pods scale:
watch kubectl get pods -l app=nginx
Stop load
Press CTRL+C in the load generator shell, then type:
This chapter demonstrates deploying an application, exposing it through Kubernetes objects, installing Metrics Server, and using HPA to scale pods based on CPU utilization.
Review Questions
Describe how to make the client application generate more traffic.
Click for Answer…
Run a load generator pod and continuously call the nginx service.
How many minutes do you need to wait before nginx pods start increasing?
Click for Answer…
It depends on when Metrics Server reports CPU data and when HPA decides to scale. Wait a few minutes and monitor with kubectl get hpa.
How do you stop sending traffic to nginx deployment?
Click for Answer…
Stop or delete the load generator pod.
Kubernetes in depth
This chapter is your guide to mastering essential Kubernetes concepts.
You’ll start with Pods, the building blocks of applications in Kubernetes, understanding how they work and how to manage them effectively.
Then, you’ll explore ConfigMaps and Secrets, learning how to handle configuration data and sensitive information securely.
Next, you’ll dive into upgrade strategies, discovering how to update your applications smoothly without causing downtime.
Deployment techniques will be covered extensively, showing you how to deploy, update, and roll back application versions with ease.
Lastly, you’ll learn about scaling, exploring methods to adjust your application’s capacity to meet changing demands effortlessly.
By the end, you’ll have the skills to confidently deploy, manage, and scale applications in Kubernetes environments.
Subsections of Kubernetes in depth
Task 5 - Creating and Managing Pods
Objective: Learn Pod, container and init container
What is Pod
In Kubernetes, a Pod is the smallest and most basic deployable unit. It represents a single instance of a running process in your cluster. Pods are the building blocks of Kubernetes applications and encapsulate one or more containers, storage resources, a unique network IP, and options that govern how the containers should run. More info about Pods:
The smallest, most basic unit in Kubernetes.
Represents a single instance of a running process in your cluster.
Can contain one or more containers that are tightly coupled and share resources, such as storage and networking.
Often represents a single microservice, but can also encapsulate multiple closely related containers.
Pods are ephemeral by nature, meaning they can be created, destroyed, and replaced dynamically based on the cluster’s needs.
Managed by Kubernetes controllers like Deployments, ReplicaSets, or StatefulSets to ensure desired state and high availability.(We will learn about these in next chapters)
Provides a unique IP address within the cluster, allowing communication between Pods.
Can have one or more volumes attached to it for persistent storage.
Logs and metrics of individual containers within a Pod can be accessed using kubectl.
Pods in a Kubernetes cluster are used in two main ways:
Pods that run a single container: The “one-container-per-Pod” model is the most common Kubernetes use case; in this case, you can think of a Pod as a wrapper around a single container; Kubernetes manages Pods rather than managing the containers directly.
Pods that run multiple containers: that need to work together. A Pod can encapsulate an application composed of multiple co-located containers that are tightly coupled and need to share resources. These co-located containers form a single cohesive unit.
Preflight check
This task needs the two-node cluster from Task 3 - Install Kubernetes. Confirm it is healthy and that the default namespace is empty before you create the first Pod. From Azure Cloud Shell:
kubectl get nodes
kubectl get pods
Both nodes must report Ready, and there should be no Pods yet — kubectl reports the empty list on stderr:
NAME STATUS ROLES AGE VERSION
node-worker Ready <none> 10m v1.30.x
node-master Ready control-plane 15m v1.30.x
No resources found in default namespace.
If a node is missing or NotReady, go back to Task 3 and finish it. If a Pod named nginx or nginx-pod-manual is already listed from an earlier run, delete it with kubectl delete pod nginx or kubectl delete pod nginx-pod-manual first, otherwise the manifests below fail as already existing.
Init containers in Kubernetes are specialized containers that run before the main containers in a Pod, serving to prepare the environment or perform initialization tasks. They execute sequentially, ensuring that each init container completes successfully before the next one starts and before the main containers begin execution. Init containers share the same volume mounts as the main containers, facilitating the sharing of resources such as configuration files or secrets. They are transient in nature, running to completion once and then terminating, and are not restarted if they fail.
Why can the init container access the shared-data folder?
Click for Answer…
Both the init container and the main nginx container mount this same volume
Can you restart a Pod created with kubectl create pod or a YAML file specifying kind: Pod?
Click for Answer…
You cannot directly restart a Pod created with kind: Pod
If you delete the Pod nginx-pod-manual and recreate it using the same YAML, will the previous data in the shared-data folder persist?"
Click for Answer…
No, The volume is defined as an emptyDir, emptyDir volumes are ephemeral and tied to the Pod's lifecycle.
Clean up
kubectl delete pod nginx-pod-manual
Task 6 - Configuration
Objective: Master injecting configuration into Pods.
Description: Learn to externalize application configuration using ConfigMaps and the Downward API. Understand how to pass environment variables, configure application settings, and expose Pod and container metadata to applications. Labs include creating ConfigMaps and using the Downward API to expose Pod information.
ConfigMaps:
In Kubernetes, ConfigMaps are a resource object used to store non-sensitive configuration data in key-value pairs that can be consumed by pods or other system components. They provide a way to decouple configuration artifacts from container images, allowing for more flexible and manageable application configurations.
Here are some reasons why ConfigMaps are used in Kubernetes:
Separation of Concerns: ConfigMaps allow you to separate configuration data from application code. This separation makes it easier to manage configurations independently of the application’s lifecycle, which can be particularly useful in scenarios where multiple instances of the same application are deployed with different configurations.
Dynamic Configuration Updates: ConfigMaps support dynamic updates, meaning that changes to the configuration can be applied to running pods without requiring a restart. This allows for more flexibility and agility in managing application configurations.
Environment Agnostic: ConfigMaps are environment-agnostic, meaning that the same configuration can be used across multiple environments (e.g., development, testing, production) without modification. This helps maintain consistency and simplifies the deployment process.
Immutable Infrastructure: By externalizing configurations into ConfigMaps, the underlying infrastructure becomes more immutable. This means that changes to configurations do not require modifications to the underlying infrastructure, making deployments more predictable and reliable.
Centralized Management: ConfigMaps provide a centralized location for managing configuration data. This can be particularly beneficial in large-scale deployments where multiple applications and components require different configurations.
Integration with Other Resources: ConfigMaps can be easily integrated with other Kubernetes resources such as pods, deployments, and services. This allows you to inject configuration data into your application containers at runtime, making them highly configurable and adaptable to different environments.
Overall, ConfigMaps play a crucial role in Kubernetes by providing a flexible and efficient mechanism for managing configuration data in a containerized environment, contributing to improved application deployment, scalability, and maintainability.
Secrets:
In Kubernetes, Secrets are another type of resource object used to store sensitive information, such as passwords, OAuth tokens, and SSH keys, in a secure manner. They are similar to ConfigMaps but are specifically designed to handle sensitive data. Here are some key aspects of Secrets in Kubernetes:
Secure Storage: Secrets are stored securely within the Kubernetes cluster, encrypted at rest by default. This ensures that sensitive information is not exposed or accessible to unauthorized users.
Base64 Encoding: Secret data is typically stored in Base64 encoded format. While Base64 encoding does not provide encryption, it helps prevent accidental exposure of sensitive data in logs or other places where plaintext might be displayed.
Multiple Types of Secrets: Kubernetes supports various types of Secrets, including generic secrets, Docker registry credentials, TLS certificates, and service account tokens. Each type of Secret has its specific use case and configuration options.
Usage in Pods: Secrets can be mounted as volumes or exposed as environment variables within pods. This allows containers running within the pod to access the sensitive information stored in the Secret without exposing it directly in the container specification or source code.
Access Control: Kubernetes provides role-based access control (RBAC) mechanisms to manage access to Secrets. This ensures that only authorized users or applications can create, read, update, or delete Secrets within the cluster.
Automatic Injection: In some cases, Kubernetes can automatically inject certain types of Secrets into pods. For example, service account tokens are automatically mounted as a volume in pods running within the Kubernetes cluster, allowing them to authenticate with the Kubernetes API server.
Immutable Once Created: Unlike ConfigMaps, Secrets are immutable once created. This means that you cannot update the data stored in a Secret directly. Instead, you must delete the existing Secret and create a new one with the updated data.
Overall, Secrets in Kubernetes provide a secure and convenient way to manage sensitive information within a cluster, ensuring that sensitive data is protected from unauthorized access while still being accessible to the applications that need it.
Creating ConfigMaps:
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-index-html
data:
index.html: |
<!DOCTYPE html>
<html>
<head>
<title>Welcome to NGINX!</title>
</head>
<body>
<h1>Welcome to NGINX!</h1>
<p>This is a custom index.html page served by NGINX at $(date).</p>
</body>
</html>
EOF
To get configmap, try kubectl get configmap
Creating Secret:
Firstly, we need encode a secret value to base64. On linux you can use the following commands to encode.
There are several types of built in Secrets [!https://kubernetes.io/docs/concepts/configuration/secret/#secret-types] in Kubernetes. Opaque is a secret which is an arbitrary user defined data.
Delete Pod nginx-pod-with-configmap-secret and create again, check the web page of nginx again ? did you see any difference ? why ?
Click for Answer…
you should not see any difference, as The ConfigMap and Secret remain unchanged in the cluster.
Modify below nginx-deployment with VolumeMounts to use ConfigMap
Click for Answer…
Objective: Master the Creation and Management of Deployments
Description: This module zeroes in on Deployments as the primary mechanism for deploying applications on Kubernetes. You will delve into the creation of Deployments, learn how to scale them effectively, and update applications with zero downtime. Through hands-on lab exercises, you will experience deploying a multi-replica application and conducting rolling updates to ensure seamless application transitions.
Deploying an Application with a Deployment
We’ve previously seen how to use kubectl create deployment Kubernetes-bootcamp –image=gcr.io/google-samples/kubernetes-bootcamp:v1 to create a deployment directly from the command line. However, Kubernetes also supports deploying applications using YAML or JSON manifests. This approach provides greater flexibility than using the kubectl CLI alone and facilitates version control of your deployment configurations.
By defining deployments in YAML or JSON files, you can specify detailed configurations, manage them through source control systems, and apply changes systematically. This method enhances the maintainability and reproducibility of your deployments within a Kubernetes environment.
Preflight check
This task creates the kubernetes-bootcamp Deployment from scratch, so the default namespace must not already have one. Confirm the cluster from Task 3 - Install Kubernetes is healthy and empty:
kubectl get nodes
kubectl get deployment
Both nodes must report Ready, and no Deployment should be listed — kubectl reports the empty list on stderr:
NAME STATUS ROLES AGE VERSION
node-worker Ready <none> 10m v1.30.x
node-master Ready control-plane 15m v1.30.x
No resources found in default namespace.
If a node is missing or NotReady, go back to Task 3 and finish it. If kubernetes-bootcamp is already listed from an earlier task, delete it with kubectl delete deployment kubernetes-bootcamp first — kubectl create below fails when the object already exists.
Deployment kubernetes-bootcamp application
cat << EOF | tee kubernetes-bootcamp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubernetes-bootcamp
spec:
replicas: 1 # Default value for replicas when not specified
selector:
matchLabels:
app: kubernetes-bootcamp
template:
metadata:
labels:
app: kubernetes-bootcamp
spec:
containers:
- name: kubernetes-bootcamp
image: gcr.io/google-samples/kubernetes-bootcamp:v1
EOFkubectl create -f kubernetes-bootcamp.yaml
replicas: 1: Specifies that only one replica of the Pod should be running.
selector: Defines how the Deployment finds which Pods to manage using matchLabels.
template: Describes the Pod template used by the Deployment to create new Pods.
metadata.labels: Sets the label app: kubernetes-bootcamp on the Pod, matching the selector in the Deployment spec.
spec.containers: Lists the containers to run in the Pod.
image: gcr.io/google-samples/kubernetes-bootcamp:v1: Specifies the container image to use.
Scale your deployment
After deploying the Kubernetes Bootcamp application, you might find the need to scale your deployment to accommodate an increased load or enhance availability. Kubernetes allows you to scale a deployment, increasing the number of replicas from 1 to 10, for instance. This ensures your application can handle a higher load. There are several methods to scale a deployment, each offering unique benefits.
1. Using kubectl scale
Benefits
Immediate: This command directly changes the number of replicas in the deployment, making it a quick way to scale.
Simple: Easy to remember and use for ad-hoc scaling operations.
Check the existing Deployment
kubectl get deployment
expected output
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 63m
kubectl rollout status deployment kubernetes-bootcamp
Expected output
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 3 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 4 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 5 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 6 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 7 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 8 of 10 updated replicas are available...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 9 of 10 updated replicas are available...
deployment "kubernetes-bootcamp" successfully rolled out
4. Verify the Deployment
kubectl get deployment kubernetes-bootcamp
After scaling, you should observe that the number of Pods has increased to meet the deployment’s requirements.
expected output
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 10/10 10 10 64m
This output confirms the deployment now runs 10 replicas of the Kubernetes Bootcamp application, demonstrating successful scaling.
6. delete the deployment
kubectl delete deployment kubernetes-bootcamp
2. use kubectl apply
Benefits
If the intended resource to update is in yaml file, we can directly edit the yaml file with any editor, then use kubectl apply to update.
Version Controlled: Can be version-controlled if using a local YAML file, allowing for tracking of changes and rollbacks.
Reviewable: Changes can be reviewed by team members before applying if part of a GitOps workflow.
The kubectl apply -f command is more flexible and is recommended for managing applications in production. It updates resources with the changes defined in the YAML file but retains any modifications that are not specified in the file.It’s particularly suited for scenarios where you might want to maintain manual adjustments or unspecified settings.
create Kubernetes-bootcamp deployment with yaml file
cat << EOF | tee kubernetes-bootcamp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubernetes-bootcamp
spec:
replicas: 1 # Default value for replicas when not specified
selector:
matchLabels:
app: kubernetes-bootcamp
template:
metadata:
labels:
app: kubernetes-bootcamp
spec:
containers:
- name: kubernetes-bootcamp
image: gcr.io/google-samples/kubernetes-bootcamp:v1
EOFkubectl apply -f kubernetes-bootcamp.yaml
Verify the deployment
kubectl get deployment
expected output
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 3s
modify replicas in yaml file
Now, use editor like vim to change the replicas=1 in the yaml file to replicas=10. Alternatively you can also use sed to change it
sed -i 's/replicas: 1/replicas: 10/' kubernetes-bootcamp.yaml
Apply the change with kubectl apply
kubectl apply -f kubernetes-bootcamp.yaml
Verify the result
kubectl get deployment kubernetes-bootcamp
expected outcome
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 10/10 10 10 99s
clean up
kubectl delete -f kubernetes-bootcamp.yaml
3. use kubectl edit
This approach involves manually editing the resource definition in a text editor (invoked by kubectl edit), where you can change any part of the resource. After saving and closing the editor, Kubernetes applies the changes. This method requires no separate kubectl apply, as kubectl edit directly applies the changes once the file is saved and closed.
Benefit: Ablility to review and modify other configuration which is not in YAML file
above command show you into VI EDITOR with opened yaml file, locate the text
spec:
progressDeadlineSeconds: 600
replicas: 1
then change replicas from 1 to 10, save the change and exit the EDITOR with press Ctrl-C follow :wq!.
Verify the deployment
kubectl get deployment kubernetes-bootcamp
delete deployment
kubectl delete deployment kubernetes-bootcamp
4. Kubectl patch
kubectl patch directly updates specific parts of a resource without requiring you to manually edit a file or see the entire resource definition. It’s particularly useful for making quick changes, like updating an environment variable in a Pod or changing the number of replicas in a deployment.
Benefit: Ideal for scripts and automation because you can specify the exact change in a single command line.
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 10/10 10 10 97s
Delete the Deployment
kubectl delete deployment kubernetes-bootcamp
5. use kubectl replace
The kubectl replace -f command replaces a resource with the new state defined in the YAML file. If the resource doesn’t exist, the command fails. This command requires that the resource be defined completely in the file being applied because it replaces the existing configuration with the new one provided.
Deletion and Recreation: Under the hood, replace effectively deletes and then recreates the resource, which can lead to downtime for stateful applications or services. This method does not preserve unspecified fields or previous modifications made outside the YAML file.
Usage: Use kubectl replace -f when you want to overwrite the resource entirely, and you are certain that the YAML file represents the complete and desired state of the resource.
create deployment with image=gcr.io/google-samples/kubernetes-bootcamp:v1
cat << EOF | tee kubernetes-bootcamp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubernetes-bootcamp
spec:
replicas: 5 # Default value for replicas when not specified
selector:
matchLabels:
app: kubernetes-bootcamp
template:
metadata:
labels:
app: kubernetes-bootcamp
spec:
containers:
- name: kubernetes-bootcamp
image: gcr.io/google-samples/kubernetes-bootcamp:v1
EOFkubectl create -f kubernetes-bootcamp.yaml
verify the deployment
kubectl get pod -l app=kubernetes-bootcamp
expected output
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-5485cc6795-5wdrb 1/1 Running 0 4s
kubernetes-bootcamp-5485cc6795-79qkm 1/1 Running 0 4s
kubernetes-bootcamp-5485cc6795-ltqst 1/1 Running 0 4s
kubernetes-bootcamp-5485cc6795-szgqt 1/1 Running 0 4s
kubernetes-bootcamp-5485cc6795-xw7l6 1/1 Running 0 4s
the Pod has name from deployment template hash kubernetes-bootcamp-5485cc6795
Modify image in yaml file
Modify container image in kubernetes-bootcamp.yaml. Modify this will affect the Pod template hash
sed -i 's|image: gcr.io/google-samples/kubernetes-bootcamp:v1|image: jocatalin/kubernetes-bootcamp:v2|' kubernetes-bootcamp.yaml
You should observe the creation of new Pods with a new hash in their names, indicating an update. Concurrently, all previous Pods with the old hash in their names will be deleted, suggesting that the entire resource has been recreated.
Delete the deployment
kubectl delete deployment kubernetes-bootcamp
Risk of Downtime: For some resources, using kubectl replace can cause downtime since it may delete and recreate the resource, depending on the type and changes made. It’s important to use this command with caution, especially for critical resources in production environments.
Summary
kubectl scale: Quickly scales the number of replicas for a deployment, ideal for immediate, ad-hoc adjustments.
kubectl edit: Offers an interactive way to scale by manually editing the deployment’s YAML definition in a text editor, providing a chance to review and adjust other configurations simultaneously.
kubectl patch: Efficiently updates the replicas count with a single command, suitable for scripts and automation without altering the rest of the deployment’s configuration.
kubectl replace -f: Replaces the entire deployment with a new configuration from a YAML file, used when you have a prepared configuration that includes the desired replicas count.
kubectl apply -f: Applies changes from a YAML file to the deployment, allowing for version-controlled and incremental updates, including scaling operations.
Task 8 - Auto Scaling Deployment
Objective: Automate Deployment Scaling with HPA
Learn to configure the Horizontal Pod Autoscaler (HPA) for a deployment and simulate traffic to test scaling.
Using kubectl autoscale
When Kubernetes has Resouce Metrics API installed, We can using the kubectl autoscale command kubectl autoscale deployment to automatically scale a deployment based on CPU utilization (or any other metric) requires that the Kubernetes Metrics Server (or an equivalent metrics API) is installed and operational in your cluster. The Metrics Server collects resource metrics from Kubelets and exposes them in the Kubernetes API server through the Metrics API for use by Horizontal Pod Autoscaler (HPA) and other components.
using the kubectl autoscale command to automatically scale a deployment based on CPU utilization (or any other metric) requires that the Kubernetes Metrics Server (or an equivalent metrics API) is installed and operational in your cluster. The Metrics Server collects resource metrics from Kubelets and exposes them in the Kubernetes API server through the Metrics API for use by Horizontal Pod Autoscaler (HPA) and other components.
Enable resource-API
The Resource Metrics API in Kubernetes is crucial for providing core metrics about Pods and nodes within a cluster, such as CPU and memory usage to enable feature like Horizontal Pod Autoscaler (HPA), Vertical Pod Autoscaler (VPA) and enable efficent resource scheduling.
Preflight check
This task installs the Metrics Server and then creates its own nginx-deployment Deployment, Service and HPA, so the default namespace must not already have them. Confirm the cluster from Task 3 - Install Kubernetes is healthy and empty:
kubectl get nodes
kubectl get deployment
Both nodes must report Ready, and no Deployment should be listed — kubectl reports the empty list on stderr:
NAME STATUS ROLES AGE VERSION
node-worker Ready <none> 10m v1.30.x
node-master Ready control-plane 15m v1.30.x
No resources found in default namespace.
If a node is missing or NotReady, go back to Task 3 and finish it. If nginx-deployment is already listed, it is left over from Task 4 - Scaling Application, whose scripts/deploy_application_with_hpa_masternode.sh creates a deployment with the same name. Remove it with kubectl delete deployment nginx-deployment and kubectl delete svc nginx-deployment before continuing.
Target CPU Utilization: This is set to 50%. It means the HPA will aim to adjust the number of Pods so that the average CPU utilization across all Pods is around 50% of the allocated CPU resources for each Pod.
Scaling Out: If the average CPU utilization across all Pods in the nginx-deployment exceeds 50%, the HPA will increase the number of Pods, making more resources available to handle the workload, until it reaches the maximum limit of 10 Pods.
Scaling In: If the average CPU utilization drops below 50%, indicating that the resources are underutilized, the HPA will decrease the number of Pods to reduce resource usage, but it won’t go below the minimum of 2 Pod.
Check Result
use kubectl get hpa nginx-deployment-hpa to check deployment
kubectl get hpa nginx-deployment-hpa
Expected Outcome
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-deployment-hpa Deployment/nginx-deployment 0%/50% 2 10 2 23s
For a deeper understanding, consider using the following command to conduct further observations"
use kubectl get deployment nginx-deployment to check the change of deployment.
use kubectl get hpa and kubectl describe hpa to check the size of replicas.
Send http traffic to application
since the nginx-deployment service is cluster-ip type service which can only be accessed from cluster internal, so we need to create a POD which can send http traffic to nginx-deployment service.
create deployment for generate http traffic, in this deployment, we will use wget to similuate the real traffic towards ngnix-deployment cluster-ip service which has service name http://nginx-deployment.default.svc.cluster.local.
check the log from infinite-calls Pods.
{.items[0]} means use first Pod
podName=$(kubectl get pod -l app=infinite-calls -o=jsonpath='{.items[0].metadata.name}')kubectl logs po/$podName
you will see the response from nginx web server container. use ctr-c to stop.
use kubectl top pod and kubectl top node to check the resource usage status
user expected to see the number of Pod increased
You shall see that expected Pod now increased automatically without use attention.
kubectl get pod -l app=nginx
expected outcome
NAME READY STATUS RESTARTS AGE
nginx-deployment-55c7f467f8-f2qbp 1/1 Running 0 19m
nginx-deployment-55c7f467f8-hxs79 1/1 Running 0 2m2s
nginx-deployment-55c7f467f8-jx2k9 1/1 Running 0 19m
nginx-deployment-55c7f467f8-r7vdv 1/1 Running 0 3m2s
nginx-deployment-55c7f467f8-w6r8l 1/1 Running 0 3m17s
Use kubectl get hpa shall tell you that hpa is action which increased the replicas from 2 to other numbers.
kubectl get hpa
expected outcome
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-deployment-hpa Deployment/nginx-deployment 50%/50% 2 10 5 11m
check hpa detail
kubectl describe hpa
expected outcome
Name: nginx-deployment-hpa
Namespace: default
Labels: <none>
Annotations: <none>
CreationTimestamp: Wed, 08 Jul 2026 16:21:40 +0000
Reference: Deployment/nginx-deployment
Metrics: ( current / target )
resource cpu on pods (as a percentage of request): 117% (11m) / 50%
Min replicas: 2
Max replicas: 10
Deployment pods: 10 current / 10 desired
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True ReadyForNewScale recommended size matches current size
ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request)
ScalingLimited True TooManyReplicas the desired replica count is more than the maximum replica count
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 58s horizontal-pod-autoscaler New size: 4; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 43s horizontal-pod-autoscaler New size: 8; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 28s horizontal-pod-autoscaler New size: 10; reason: cpu resource utilization (percentage of request) above target
delete infinite-calls to stop generate the traffic
kubectl delete deployment infinite-calls
after few minutes later, due to no more traffic is hitting the nginx server. hpa will scale in the number of Pod to save resource.
kubectl get hpa
expected output
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
nginx-deployment-hpa Deployment/nginx-deployment 0%/50% 2 10 5 12m
use kubectl describe hpa will tell you the reason why hpa scale in the number of Pod.
kubectl describe hpa
expected outcome
Name: nginx-deployment-hpa
Namespace: default
Labels: <none>
Annotations: <none>
CreationTimestamp: Wed, 08 Jul 2026 16:21:40 +0000
Reference: Deployment/nginx-deployment
Metrics: ( current / target )
resource cpu on pods (as a percentage of request): 0% (0) / 50%
Min replicas: 2
Max replicas: 10
Deployment pods: 2 current / 2 desired
Conditions:
Type Status Reason Message
---- ------ ------ -------
AbleToScale True ReadyForNewScale recommended size matches current size
ScalingActive True ValidMetricFound the HPA was able to successfully calculate a replica count from cpu resource utilization (percentage of request)
ScalingLimited True TooFewReplicas the desired replica count is less than the minimum replica count
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulRescale 10m horizontal-pod-autoscaler New size: 4; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 10m horizontal-pod-autoscaler New size: 8; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 10m horizontal-pod-autoscaler New size: 10; reason: cpu resource utilization (percentage of request) above target
Normal SuccessfulRescale 96s horizontal-pod-autoscaler New size: 2; reason: All metrics below target
Choosing the right scaling method depends on your specific needs, such as whether you need to quickly adjust resources, maintain performance under varying loads, or integrate scaling into a CI/CD pipeline. Manual methods like kubectl scale or editing the deployment are straightforward for immediate needs, while kubectl autoscale and HPA provide more dynamic, automated scaling based on actual usage, making them better suited for production environments with fluctuating workloads.
Task 9 - Upgrade Deployment
Objective: Mastering Deployment Upgrades and Downgrades
Discover upgrade strategies and learn how to effectively upgrade or downgrade deployments
Upgrade the deployment
Upgrading a deployment in Kubernetes, particularly changing the version of the application your Pods are running, can be smoothly managed using Kubernetes’ built-in strategies to ensure minimal downtime and maintain stability. The most popular strategies for upgrading a deployment are:
Rolling Update (Default Strategy)
How It Works: This strategy updates the Pods in a rolling fashion, gradually replacing old Pods with new ones. Kubernetes automatically manages this process, ensuring that a specified number of Pods are running at all times during the update.
Advantages: Zero downtime, as the service remains available during the update. It allows for easy rollback in case the new version is faulty.
Blue/Green Deployment
This strategy involves running two versions of the application simultaneously - the current (blue) and the new (green) versions. Once the new version is ready and tested, traffic is switched from the old version to the new version, either gradually or all at once.
Canary Deployment
A small portion of the traffic is gradually shifted to the new version of the application. Based on feedback and metrics, the traffic is slowly increased to the new version until it handles all the traffic.
ReCreate Strategy
The “Recreate” strategy is a deployment strategy in Kubernetes that is particularly useful for managing stateful applications during updates. Unlike the default “RollingUpdate” strategy, which updates Pods in a rolling fashion to ensure no downtime, the “Recreate” strategy works by terminating all the existing Pods before creating new ones with the updated configuration or image.
the use case for Recreate Strategy is for stateful application where it’s critical to avoid running multiple versions of the application simultaneously.
Performing a Rolling Update
Objectives
Perform a rolling update using kubectl.
Updating an application
Users expect applications to be available all the time, and developers are expected to deploy new versions of them several times a day. In Kubernetes this is done with rolling updates. A rolling update allows a Deployment update to take place with zero downtime. It does this by incrementally replacing the current Pods with new ones. The new Pods are scheduled on Nodes with available resources, and Kubernetes waits for those new Pods to start before removing the old Pods.
In the previous module we scaled our application to run multiple instances. This is a requirement for performing updates without affecting application availability. By default, the maximum number of Pods that can be unavailable during the update and the maximum number of new Pods that can be created, is one. Both options can be configured to either numbers or percentages (of Pods). In Kubernetes, updates are versioned and any Deployment update can be reverted to a previous (stable) version.
Rolling updates overview
Similar to application Scaling, if a Deployment is exposed publicly, the Service will load-balance the traffic only to available Pods during the update. An available Pod is an instance that is available to the users of the application.
Rolling updates allow the following actions:
Promote an application from one environment to another (via container image updates)
Rollback to previous versions
Continuous Integration and Continuous Delivery of applications with zero downtime
If a Deployment is exposed publicly, the Service will load-balance the traffic only to available Pods during the update.
In the following interactive tutorial, we’ll update our application to a new version, and also perform a rollback.
Perform Rolling Update
Preflight check
Step 1 below creates the kubernetes-bootcamp Deployment and Service from scratch, so neither may already exist. Confirm the cluster from Task 3 - Install Kubernetes is healthy and empty:
kubectl get nodes
kubectl get deployment
Both nodes must report Ready, and no Deployment should be listed — kubectl reports the empty list on stderr:
NAME STATUS ROLES AGE VERSION
node-worker Ready <none> 10m v1.30.x
node-master Ready control-plane 15m v1.30.x
No resources found in default namespace.
If a node is missing or NotReady, go back to Task 3 and finish it. If kubernetes-bootcamp is already listed from Task 7 - Deploy and Scale Deployments, run kubectl delete deployment kubernetes-bootcamp and kubectl delete svc kubernetes-bootcamp first — kubectl create deployment and kubectl expose both fail when the object already exists.
Create deployment with image set to kubernetes-bootcamp:v1
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 4/4 4 4 23s
check the service
use curlpod from cluster-internal to check the service
kubectl run curlpod --image=appropriate/curl --restart=Never --rm -it -- curl http://kubernetes-bootcamp.default.svc.cluster.local
expected outcome showing v=1
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5485cc6795-4m7p9 | v=1
pod "curlpod" deleted
upgrade deployment
Upgrade the deployment with image set to kubernetes-bootcamp:v2
kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2
kubectl rollout status deployment/kubernetes-bootcamp
expected outcome
deployment.apps/kubernetes-bootcamp image updated
Waiting for deployment spec update to be observed...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 3 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 3 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 old replicas are pending termination...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
deployment "kubernetes-bootcamp" successfully rolled out
you can find above that create new replicas first then delete old replicas to avoid service disruption.
check the service
check the service use curlpod
kubectl run curlpod --image=appropriate/curl --restart=Never --rm -it -- curl http://kubernetes-bootcamp.default.svc.cluster.local
expected outcome showing v=2
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-7c6644499c-lsxm9 | v=2
rollback to old version
To rollback to orignal version, simple change the container image
kubectl set image deployments/kubernetes-bootcamp kubernetes-bootcamp=gcr.io/google-samples/kubernetes-bootcamp:v1
kubectl rollout status deployment/kubernetes-bootcamp
expected outcome
kubectl rollout status deployment/kubernetes-bootcamp
deployment.apps/kubernetes-bootcamp image updated
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 0 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 0 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 3 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 3 out of 4 new replicas have been updated...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "kubernetes-bootcamp" rollout to finish: 1 old replicas are pending termination...
deployment "kubernetes-bootcamp" successfully rolled out
check the service use curlpod
kubectl run curlpod --image=appropriate/curl --restart=Never --rm -it -- curl http://kubernetes-bootcamp.default.svc.cluster.local
expected outcome showing v=1
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5485cc6795-gvw6l | v=1
pod "curlpod" deleted
Restart the deployment
Restarting a deployment using kubectl rollout restart deployment can be necessary or beneficial for Refreshing the Application, Troubleshooting etc in a Kubernetes environment.
check existing deployment
kubectl get pod -o wide -l app=kubernetes-bootcamp
Notice that the Pod’s IP address has changed, and the deployment’s Pod template hash has also been updated to new prefix (d9f576d69). This indicates that all resources have been recreated following the kubectl rollout restart command.
scale out juice-shop deployment from 2 replicas to 6 replicas
Click for Answer…
kubectl scale deployment juice-shop --replicas=6
use rolling upgrade to upgrade your juice-shop deployment to use version v16.0.0.
Click for Answer…
kubectl set image deployment/juice-shop juice-shop=bkimminich/juice-shop:v16.0.0
Use kubectl to find the specifcation for imagePullPolicy which is need for create a deployment. and set the juice-shop deployment container imagePullPolicy to use “IfNotPresent”.
Click for Answer…
Task 10 - Exposing Applications with Loadbalancer and Ingress
Objective: Expose Your Deployment Externally
Learn to expose your deployment through NodePort, LoadBalancer, and Ingress methods, and how to secure services with HTTPS certificates
Preflight check — do not re-install the addons if they are already present
Task 4 may have already installed MetalLB, the Kong ingress controller and cert-manager on this cluster, at newer versions than the manifests used later on this page. Run these three checks first and follow the matching branch below.
kubectl get ipaddresspool -n metallb-system
kubectl get svc kong-proxy -n kong
kubectl get namespace cert-manager
If the addons are present (MetalLB shows the first-pool IP pool, Kong shows a kong-proxy LoadBalancer with an external IP, and the cert-manager namespace exists), for example:
NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES
first-pool true false ["10.0.0.5/32"]
kong-proxy LoadBalancer 10.97.121.60 10.0.0.5 80:32477/TCP,443:31364/TCP 2m10s
then skip the install steps on this page and continue from the configuration/verification steps:
Under metallb loadbalancer — skip step 1 Install metallb LoadBalancer and step 2 create ippool for metallb. Continue from step 3 Verify created ipaddresspool.
Under Install Kong ingress controller — skip step 1 Install Kong as Ingress Controller. Continue from step 2 check installed load balancer.
Under Install cert-manager — skip step 8 deploy cert-manager. Continue from step 9 Create certficate.
Everything else on this page still applies, including the whole NodePort section, which does not depend on these addons.
If the addons are absent — for example because you ran the Cleanup Addons step of Task 4, so the commands above report not found / no resources — then run every step on this page exactly as written, including the install steps.
Never “fix” a version difference by re-applying the older manifests below over a newer installation, and do not delete a working newer installation in order to install the older one: leftover CRDs from the newer release can break the older controller in ways that are hard to diagnose during the lab.
Version note
The install commands and the configuration steps on this page were authored against MetalLB v0.14.3, Kong ingress controller v2.10.0 and cert-manager v1.3.1. If Task 4 already installed the newer versions, the configuration steps below (ingress annotations, ingressClassName, apiVersion values) may need small adjustments for the newer Kong and cert-manager releases. If a step does not behave as documented, check the resource with kubectl describe before changing anything, and raise it with your lab administrator.
In the previous chapter, we discussed Kubernetes ClusterIP services. This chapter will focus on exposing applications externally using NodePort, LoadBalancer, and Ingress services..
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 2/2 2 2 4s
NodePort
To enable external access to an app, such as from the internet, we can expose the service using NodePort or LoadBalancer
NodePort: Exposes the app through a NATted port on the worker node running the container. If an external firewall exists, you may need to whitelist this port. NodePort uses a default range of 30000-32767. When creating a NodePort service without specifying a port, Kubernetes automatically allocates one from this range.
To expose the service, we can use the kubectl expose command or create a service YAML definition and apply it with kubectl create -f, which offers more flexibility..
To create a NodePort service, you can use the kubectl expose command, for example
kubectl expose deployment kubernetes-bootcamp --port 80 --type=NodePort --target-port=8080 --name kubernetes-bootcamp-nodeportsvc --save-config
However, using a YAML file provides more flexibility. Let’s create a NodePort service using a YAML file.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-bootcamp-nodeportsvc NodePort 10.103.189.68 <none> 80:30913/TCP 24s
the NATTED PORT on worker node that running POD is 30913.
NodePort service will exposes the service on a static port which is 30913 in this example on every node in the cluster, including both master and worker nodes. This means you can access the service using the IP address of any node in the cluster followed by the NodePort.
Verify the service
from azure shell , access the application via nodeport service
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-bcbb7fc75-q7sqc | v=1
Check the endpoints of service
you can find the actual endpoints for nodeport service through command
kubectl get ep -l app=kubernetes-bootcamp
expected output
NAME ENDPOINTS AGE
kubernetes-bootcamp-nodeportsvc 10.244.152.116:8080,10.244.152.117:8080 20m
If the curl to port 30913 times out
Reaching http://$(whoami)-master.centralus.cloudapp.azure.com:30913 from the Azure Cloud Shell requires inbound TCP port 30913 to be permitted by the network security group attached to the lab subnet. This workshop’s Terraform does not create or modify any network security group — it only reads the pre-created resource group — so port 30913 is not opened by anything in this repository.
A timeout does not by itself mean the NodePort service is broken. Use the check endpoints step above to tell the two apart:
kubectl get ep -l app=kubernetes-bootcamp
If endpoints are listed, the service is healthy and the timeout is a network-path problem rather than a Kubernetes one. Ask your lab administrator to allow inbound TCP 30913 on the lab subnet’s network security group if you need external access to the NodePort.
summary
Using NodePort services in Kubernetes, while useful for certain scenarios, comes with several disadvantages, especially when considering the setup where traffic is directed through the IP of a single worker node has limitation of Inefficient Load Balancing, Exposure to External Traffic,Lack of SSL/TLS Termination etc., so NodePort services are often not suitable for production environments, especially for high-traffic applications that require robust load balancing, automatic scaling, and secure exposure to the internet. For scenarios requiring exposure to external traffic, using an Ingress controller or a cloud provider’s LoadBalancer service is generally recommended. These alternatives offer more flexibility, better load distribution, and additional features like SSL/TLS termination and path-based routing, making them more suitable for production-grade applications.
A LoadBalancer service in Kubernetes is a way to expose an application running on a set of Pods to the external internet in a more accessible manner than NodePort.
we can use the kubectl expose command as follow to create a loadbalancer service for deployment kubernetes-bootcamp.
LoadBalancer service require an external IP to use which we use metallb and create an ippool to assign external ip to loadbalancer
metallb loadbalancer
In a self-managed Kubernetes environment, external traffic management and service exposure are not handled automatically by the infrastructure, unlike managed Kubernetes services in cloud environments (e.g., AWS ELB with EKS, Azure Load Balancer with AKS, or Google Cloud Load Balancer with GKE). This is where solutions like MetalLB and the Kong Ingress Controller become essential
MetalLB provides a network load balancer implementation for Kubernetes clusters that do not run on cloud providers, offering a LoadBalancer type service. In cloud environments, when you create a service of type LoadBalancer, the cloud provider provisions a load balancer for your service. In contrast, on-premises or self-managed clusters do not have this luxury. MetalLB fills this gap by allocating IP addresses from a configured pool and managing access to services through these IPs, enabling external traffic to reach the cluster services.
The IP address in the metallb IP pool is designated for assignment to the load balancer. Since Azure VMs have only one IP, which serves as the Node IP, you can retrieve the IP address using the `kubectl get node -o wide`` command.
since loadBalancer service require an dedicated external ip, if IP has already occupied by other loadBalancer, we will not able to create new loadBalancer. so if you have kong loadbalaner installed, delete it first
kubectl get svc kong-proxy -n kong && kubectl delete svc kong-proxy -n kong
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-bcbb7fc75-nh9sn | v=1
How it works
When we use the kubectl expose command to create a LoadBalancer service in a Kubernetes cluster with MetalLB installed in Layer 2 (L2) advertisement mode, the process simplifies to these key points:
Creating the Service: The command creates a LoadBalancer type service named kubernetes-bootcamp-lb-svc, which targets the kubernetes-bootcamp deployment.
Assigning an External IP: MetalLB automatically assigns an external IP address from its configured IP pool to the service, making it accessible outside the Kubernetes cluster.
L2 Advertisement: MetalLB advertises the assigned IP address across the local network using ARP, directing traffic to the Kubernetes node responsible for the service.
Traffic Routing: Incoming traffic to the external IP is routed to the targeted Pods within the cluster, enabling external access to the application.
This streamlined process allows MetalLB to provide external IPs for services, enabling external access in environments without native cloud provider LoadBalancer support.
If you use cloud managed kubernetes like EKS, GKE, AKE, then cloud provider will responsible for create loadbalancer instance and assign ip address , then Metallb is not reqiured in that case.
Ingress is not classified as a type of Kubernetes Service because it operates at a higher layer in the network stack and serves a different purpose.
Ingress operates at the application layer (Layer 7 of the OSI model), dealing with HTTP and HTTPS traffic. It allows for more complex routing based on the request’s URL path or host, and can manage SSL/TLS termination, name-based virtual hosting, and more.
It’s designed to give developers more control over the access to services from outside the Kubernetes cluster, including URL path-based routing, domain name support, and managing SSL/TLS certificates.
An Ingress typically routes traffic to one or more Kubernetes Services. It acts as an entry point to the cluster that forwards incoming requests to the appropriate Services based on the configured rules. In this sense, Ingress depends on Services to function, but it provides a more flexible and sophisticated way to expose those Services to the outside world.
Ingress requires an Ingress controller to be running in the cluster, which is a separate component that watches the Ingress resources and processes the rules they define. While Kubernetes supports Ingress resources natively, the actual routing logic is handled by this external component. There are many Ingress controller you can use for example, nginx based ingress controller, kong ingress controller, also some vendor like fortinet offer fortiweb as ingress controller.
Install Kong ingress controller
Install Kong as Ingress Controller
The Kong Ingress Controller is an Ingress controller for Kubernetes that manages external access to HTTP services within a cluster using Kong Gateway. It processes Ingress resources to configure HTTP routing, load balancing, authentication, and other functionalities, leveraging Kong’s powerful API gateway features for Kubernetes services. Kong will use the ippool that managed by metallb.
kubectl apply -f https://raw.githubusercontent.com/Kong/kubernetes-ingress-controller/v2.10.0/deploy/single/all-in-one-dbless.yaml
kubectl rollout status deployment proxy-kong -n kong
kubectl rollout status deployment ingress-kong -n kong
When Kong installed, Kong automatically configures itself as the default IngressClass for the cluster. With a default IngressClass set, you have the option to omit specifying ingressClassName: kong in your Ingress specifications.
kubectl get ingressclasses
expected outcome
NAME CONTROLLER PARAMETERS AGE
kong ingress-controllers.konghq.com/kong <none> 9h
Create nginx deployment
Create nginx deployment
Create nginx deployment with replicas set to 2. the container also configured resource usage limition for cpu and memory.
cd$HOMEcat <<EOF | tee nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi" # Minimum memory requested to start the container
cpu: "10m" # 100 millicpu (0.1 CPU) requested to start the container
limits:
memory: "128Mi" # Maximum memory limit for the container
cpu: "40m" # 200 millicpu (0.2 CPU) maximum limit for the container
EOFkubectl apply -f nginx-deployment.yaml
kubectl rollout status deployment nginx-deployment
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx kong k8s50-master.centralus.cloudapp.azure.com,k8s50-master.centralus.cloudapp.azure.com 10.0.0.5 80, 443 7m58s
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5485cc6795-rsqs9 | v=1
verify nginx ingress rule with https url
curl -k https://${nodename}/default
or plain http url
curl http://${nodename}/default
Expected outcome
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Review Questions
Create juice-shop with v15.0.0 deployment and ClusterIP svc
Click for Answer…
Only complete this cleanup step if you are finished with the workshop.
If you plan to continue with other labs like [AI-101](https://fortinetcloudcse.github.io/ai-101/) or [FortiAIGate Workshop](https://fortinetcloudcse.github.io/faig-training-workshop/), skip the cleanup.
After completing all tasks with the Self-Managed Kubernetes, use the following command to delete the two Azure VMs:
The life cycle of a Kubernetes Pod involves several key stages from creation to termination. Here’s a brief overview of these stages, illustrated with commands related to deploying a Pod using the gcr.io/google-samples/kubernetes-bootcamp:v1 image:
Pod Creation
A Pod is created when you deploy it using a YAML file or directly via the kubectl command.
Pending
The Pod enters the Pending state as Kubernetes schedules the Pod on a node and the container image is being pulled from the registry.
Running
Once the image is pulled and the Pod is scheduled, it moves to the Running state. The Pod remains in this state until it is terminated or stopped for some reason.
Succeeded/Failed
A Pod reaches Succeeded if all of its containers exit without error and do not restart.
A Pod is marked as Failed if any of its containers exit with an error.
CrashLoopBackOff
This status indicates that a container in the Pod is failing to start properly and Kubernetes is repeatedly trying to restart it.
Termination
Pods can be terminated gracefully by deleting them. Kubernetes first sends a SIGTERM signal to allow containers to shut down gracefully.
Deletion
The Pod’s entry remains in the system for a period after termination, allowing you to inspect its status posthumously. Eventually, Kubernetes cleans it up automatically.
Through these stages, Kubernetes manages the application’s lifecycle, ensuring that the desired state specified by the Deployment configurations is maintained. Monitoring the Pod’s lifecycle helps in managing and troubleshooting applications running on Kubernetes.
Preflight check
Every command on this page selects the kubernetes-bootcamp Deployment with -l app=kubernetes-bootcamp, but this page never creates it — and the clean up at the end of Task 10 - Exposing application deletes it. Check it is there first, otherwise every command below returns nothing:
kubectl get deployment kubernetes-bootcamp
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1/1 1 1 63m
If kubectl reports it was not found, recreate it before continuing:
Delete it again with kubectl delete deployment kubernetes-bootcamp when you are done with this page.
we can use kubectl get pod -l app=kubernetes-bootcamp and kuectl describe pod -l app=kubernetes-bootcamp to check the detail state for a Pod.
Useful Command for Pod
You can try a few useful command for operating a Pod
get the Pod name only
To retrieve just the name(s) of the Pod(s) with a specific label (app=kubernetes-bootcamp), use the following command:
kubectl get pods -l app=kubernetes-bootcamp -o=jsonpath='{.items[*].metadata.name}'
shell into the Pod
To access the shell of the default container in a Pod labeled with app=kubernetes-bootcamp, first capture the Pod name in a variable, then use kubectl exec:
Note: This command assumes that your selection returns a single Pod name or you are only interested in the first Pod. Use exit to leave the container shell. some of container in Pod does not have bash or sh , then you will not able to shell into the container in that Pod.
you will be drop into Pod’s default container shell, use exit to exit the container.
check log for a Pod
To view the logs from the container in real-time:
PODNAME=$(kubectl get pods -l app=kubernetes-bootcamp -o=jsonpath='{.items[*].metadata.name}')kubectl logs -f po/$PODNAME
You will see logs output from the container. Press Ctrl-C to exit the log stream.
Kubernetes Bootcamp App Started At: 2024-02-21T05:41:33.993Z | Running On: kubernetes-bootcamp-5485cc6795-cdwz7
Delete Pod and Observe IP Address Change
First, check the current Pod’s IP address:
kubectl get pod -l app=kubernetes-bootcamp -o wide
then delete Pod
PODNAME=$(kubectl get pods -l app=kubernetes-bootcamp -o=jsonpath='{.items[*].metadata.name}')kubectl delete po/$PODNAME
You will see an output similar to:
pod "kubernetes-bootcamp-5485cc6795-cdwz7" deleted
After deletion, check the Pods again. You will find a new Pod has been automatically recreated with a new IP address. This behavior is due to the Kubernetes Controller Manager ensuring the actual state matches the desired state specified by the Deployment’s replicas. A new Pod is generated to maintain the desired number of replicas.
kubectl get pod -l app=kubernetes-bootcamp -o wide
The IP address assigned to a Pod is ephemeral and will assign next available ip for recreation.
These commands provide a basic but powerful set of tools for interacting with Pods in a Kubernetes environment, from accessing shells and viewing logs to managing Pod lifecycles.
ServiceAccount
A ServiceAccounts are primarily designed for use by processes running in Pods is like an identity for processes running in a Pod, allowing them to interact with the Kubernetes API securely. When you create a Pod, Kubernetes can automatically give it access to a ServiceAccount, so your applications can ask Kubernetes about other parts of the system without needing a separate login. It’s a way for your apps to ask Kubernetes “Who am I?” and “What am I allowed to do?”
check the default service account for a POD
podName=$(kubectl get pod -l app=kubernetes-bootcamp -o=jsonpath='{.items[*].metadata.name}')kubectl describe pod $podName | grep 'Service Account' | uniq
Expected output:
Service Account: default
Kubernetes adheres to the principle of least privilege, meaning the default ServiceAccount is assigned minimal permissions necessary for a Pod’s basic operations. Should your Pod require additional permissions, you must create a new ServiceAccount with the requisite permissions and associate it with your Pod. use kubectl create rolebinding to bind pre-defined role or custom role to serviceAccount.
Kubernetes API-resources
Kubernetes is fundamentally built around APIs that adhere to the OpenAPI specification, defining resources and their operations. Based on API input, Kubernetes creates objects and stores them in the etcd database. Let’s explore using the Kubernetes API to create a Pod, utilizing the kubectl api-resources and kubectl explain commands for guidance.
Finding the API Resource for Pods
First, identify the API resource needed to create a Pod. You can list all API resources with `kubectl api-resources``:
This command filters the list of API resources to show only those related to Pods. The output will look similar like this:
kubectl api-resources | head -n 1kubectl api-resources | grep pods
expect to see output
kubectl api-resources | head -n 1NAME
NAME SHORTNAMES APIVERSION NAMESPACED KIND
kubectl api-resources | grep pods
pods po v1 true Pod
pods metrics.k8s.io/v1beta1 true PodMetrics
From the output, we see that the “KIND” for Pods is “Pod”, and the API version is v1.
Understanding Pod Specifications
Next, use kubectl explain to understand how to structure a YAML definition for a Pod specification. Execute the following commands to explore the Pod resource specifications:
kubectl explain Pod
and
kubectl explain Pod.apiVersion
and
kubectl explain Pod.kind
and
kubectl explain Pod.metadata
Crafting a Minimal YAML Definition for a Pod
Now, we can construct a minimal YAML file to create a Pod. The essential elements include the Pod’s name and the container image:
To see the details of the created Pod, including any default values Kubernetes applied during creation, use:
kubectl get pod test-pod -o yaml
This command outputs the complete configuration of the Pod, test-pod, showing all properties set by Kubernetes, many of which use default values that you can customize in the Pod YAML definition.
use kubectl delete pod test-pod to delete pod or use yaml file below