Exercises
Exercise 1: Horizontal Pod Autoscaler
In this exercise, you will create and configure a Horizontal Pod Autoscaler (HPA) to automatically scale a deployment based on CPU utilization.
Step 1: Create a Deployment
First, create a deployment that will be scaled by the HPA:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: quay.io/nginx/nginx-unprivileged:latest
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
Apply the deployment:
oc apply -f web-deployment.yaml
oc get deployment web-deployment
Step 2: Create an HPA
Create a Horizontal Pod Autoscaler that scales the deployment based on CPU utilization:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-deployment
minReplicas: 2
maxReplicas: 4
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Apply the HPA:
oc apply -f web-hpa.yaml
oc get hpa web-hpa
Step 3: Monitor HPA Status
Monitor the HPA to see its current status and scaling decisions:
# View HPA status
oc get hpa web-hpa
# Get detailed HPA information
oc describe hpa web-hpa
Watch the HPA and the deployment replicas, one at a time (press Ctrl+C to stop each watch):
oc get hpa web-hpa -w
oc get deployment web-deployment -w
Observe the current replica count and target metrics.
Step 4: Generate Load
Generate CPU load on the pods to trigger scaling. In a separate terminal, run:
# Get a pod name
POD_NAME=$(oc get pods \
-l app=web \
-o jsonpath='{.items[0].metadata.name}')
# Generate CPU load (this will run in the background)
oc exec $POD_NAME -- \
sh -c "while true; do dd if=/dev/zero of=/dev/null; done" &
Monitor the HPA and deployment to observe scaling behavior:
# Watch HPA scaling
oc get hpa web-hpa -w
# Check deployment replica count
oc get deployment web-deployment
Step 5: Observe Scaling Behavior
After a few minutes, observe how the HPA responds:
# View HPA events
oc describe hpa web-hpa
# Check current replica count
oc get pods -l app=web
# View HPA metrics
oc get hpa web-hpa -o yaml
Note how the replica count changes based on CPU utilization.
Step 6: Stop Load and Observe Scale-Down
Stop the load generation and observe the scale-down behavior. In the terminal where you started the load, run:
# Stop the local oc exec job;
# the loop keeps running inside the container
kill %1
# Delete the loaded pod; the Deployment replaces it with an idle pod
oc delete pod $POD_NAME
Monitor the scale-down, one watch at a time (press Ctrl+C to stop each watch):
oc get hpa web-hpa -w
oc get deployment web-deployment -w
Observe the stabilization window and scale-down policies in action.
Exercise 2: Vertical Pod Autoscaler
In this exercise, you will create and configure a Vertical Pod Autoscaler (VPA) to automatically adjust CPU and memory requests and limits for containers based on historical usage.
Step 1: Create a Deployment
Create a deployment that will be managed by the VPA:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vpa-deployment
spec:
replicas: 2
selector:
matchLabels:
app: vpa-app
template:
metadata:
labels:
app: vpa-app
spec:
containers:
- name: app
image: quay.io/nginx/nginx-unprivileged:latest
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
Apply the deployment:
oc apply -f vpa-deployment.yaml
oc get deployment vpa-deployment
oc get pods -l app=vpa-app
Step 2: Create a VPA in Recommendation Mode
Create a VPA in "Off" mode to view recommendations without automatic updates:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: vpa-recommender
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: vpa-deployment
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 2
memory: 4Gi
controlledResources: ["cpu", "memory"]
Save it as vpa-recommender.yaml and apply it:
oc apply -f vpa-recommender.yaml
oc get vpa vpa-recommender
Installing the Vertical Pod Autoscaler Operator adds VPA permissions to the default project admin and edit roles, so you can create and delete VPAs in your own project.
|
Step 3: View VPA Recommendations
After the VPA has collected some usage data, view its recommendations:
# View VPA status
oc get vpa vpa-recommender -o yaml
# Get detailed VPA information
oc describe vpa vpa-recommender
The VPA will show recommendations including:
-
Target: Recommended values for optimal performance
-
Lower Bound: Minimum values to maintain performance
-
Upper Bound: Maximum recommended values
Step 4: Generate Load and Monitor
Generate some load on the pods to provide usage data for VPA analysis:
# Get a pod name
POD_NAME=$(oc get pods \
-l app=vpa-app \
-o jsonpath='{.items[0].metadata.name}')
# Generate CPU load for two minutes
oc exec $POD_NAME -- timeout 120 sh -c 'while true; do :; done'
Wait a few minutes, then check the VPA recommendations again:
oc describe vpa vpa-recommender
Observe how the recommendations may change based on actual usage patterns.
Step 5: Create VPA in Initial Mode
Create a new VPA in "Initial" mode that sets resource requests only when pods are first created:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: vpa-initial
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: vpa-deployment
updatePolicy:
updateMode: "Initial"
resourcePolicy:
containerPolicies:
- containerName: app
minAllowed:
cpu: 50m
memory: 64Mi
maxAllowed:
cpu: 2
memory: 4Gi
controlledResources: ["cpu", "memory"]
Save it as vpa-initial.yaml.
Delete the previous VPA so that only one VPA targets the deployment, then apply the new one:
oc delete vpa vpa-recommender
oc apply -f vpa-initial.yaml
A new VPA has no recommendation yet, and pods created before it has one keep their original requests. Wait until the VPA reports a recommendation (this usually takes one to two minutes):
oc wait vpa/vpa-initial \
--for=condition=RecommendationProvided \
--timeout=5m
Initial mode only acts when pods are created, so delete and recreate the deployment to see it in action:
oc delete deployment vpa-deployment
oc apply -f vpa-deployment.yaml
Check the new pods to see if VPA has set resource requests:
oc get pods \
-l app=vpa-app \
-o yaml |
grep -A 10 resources
oc describe pod -l app=vpa-app
Step 6: Compare Resource Settings
Compare the original resource settings with VPA recommendations:
# View VPA recommendations
oc get vpa vpa-initial -o yaml | grep -A 20 recommendation
# View actual pod resource settings
oc get pod \
-l app=vpa-app \
-o jsonpath='{.items[0].spec.containers[0].resources}'
Exercise 3: Descheduler Operator
In this exercise, you will examine the Descheduler Operator configuration and understand how descheduling profiles work. Note that creating or modifying the Descheduler requires administrator privileges.
Step 1: View Descheduler Configuration
Check if the Descheduler Operator is installed and view its configuration:
# View Descheduler custom resources
# (only administrators can create or change them,
# but you can view them)
oc get kubedescheduler -n openshift-kube-descheduler-operator
# View the cluster Descheduler configuration
oc get kubedescheduler cluster \
-n openshift-kube-descheduler-operator \
-o yaml
If the Descheduler is configured, you’ll see the active profiles and settings.
Step 2: Examine Descheduler Profiles
View the profiles that are enabled in the Descheduler configuration:
# View enabled profiles
oc get kubedescheduler cluster \
-n openshift-kube-descheduler-operator \
-o jsonpath='{.spec.profiles}'
# View full Descheduler spec
oc get kubedescheduler cluster \
-n openshift-kube-descheduler-operator \
-o jsonpath='{.spec}' |
jq .
Note which profiles are active. The available profiles include:
-
AffinityAndTaints
-
TopologyAndDuplicates
-
SoftTopologyAndDuplicates
-
LifecycleAndUtilization
-
LongLifecycle
-
CompactAndScale
-
KubeVirtRelieveAndMigrate
-
EvictPodsWithPVC
-
EvictPodsWithLocalStorage
Step 3: View Descheduler Settings
Examine the Descheduler configuration settings:
# View descheduling interval
oc get kubedescheduler cluster \
-n openshift-kube-descheduler-operator \
-o jsonpath='{.spec.deschedulingIntervalSeconds}'
# View descheduler mode (Predictive or Automatic)
oc get kubedescheduler cluster \
-n openshift-kube-descheduler-operator \
-o jsonpath='{.spec.mode}'
# View eviction limits
oc get kubedescheduler cluster \
-n openshift-kube-descheduler-operator \
-o jsonpath='{.spec.evictionLimits}'
Step 4: View Descheduler Operator Status
Check the status of the Descheduler Operator:
# View Descheduler Operator deployment
oc get deployment -n openshift-kube-descheduler-operator
# View Descheduler pods
oc get pods -n openshift-kube-descheduler-operator
# View Descheduler Operator logs
oc logs \
-n openshift-kube-descheduler-operator \
-l name=descheduler-operator \
--tail=50
Step 5: Understand Descheduler Behavior
Create a deployment to observe how descheduling might affect it:
apiVersion: apps/v1
kind: Deployment
metadata:
name: descheduler-test
spec:
replicas: 3
selector:
matchLabels:
app: descheduler-test
template:
metadata:
labels:
app: descheduler-test
spec:
containers:
- name: app
image: quay.io/nginx/nginx-unprivileged:latest
resources:
requests:
cpu: 100m
memory: 128Mi
oc apply -f descheduler-test.yaml
oc get pods \
-l app=descheduler-test \
-o wide
Monitor the pods to see their distribution across nodes. If the Descheduler is active with certain profiles, it may move pods to optimize cluster balance.
Step 6: Check for Descheduling Events
Look for descheduling-related events:
# View events recorded by the descheduler in your project
oc get events \
--field-selector reportingComponent=sigs.k8s.io.descheduler
# View recent events for your test deployment
oc get events --field-selector involvedObject.name=descheduler-test
# Watch pod movements
oc get pods \
-l app=descheduler-test \
-o wide \
-w
Note: If the Descheduler is in Predictive mode, it will simulate evictions but not actually move pods. Only Automatic mode performs actual evictions and records an event for each one; the event reason is the name of the strategy that evicted the pod (for example, RemovePodsViolatingInterPodAntiAffinity). The descheduler needs at least two ready nodes: on a single-node cluster it skips every cycle and evicts nothing.
Step 7: Example Descheduler Configuration
| Creating or modifying the Descheduler requires administrator privileges. This example shows what a Descheduler configuration might look like: |
apiVersion: operator.openshift.io/v1
kind: KubeDescheduler
metadata:
name: cluster
namespace: openshift-kube-descheduler-operator
annotations:
do-not-deploy: "true"
spec:
managementState: Managed
deschedulingIntervalSeconds: 3600
profiles:
- AffinityAndTaints
- TopologyAndDuplicates
mode: Automatic
evictionLimits:
total: 10
node: 5
This configuration would:
-
Run descheduling every hour (3600 seconds)
-
Use the AffinityAndTaints and TopologyAndDuplicates profiles
-
Actually evict pods (Automatic mode)
-
Limit evictions to 10 total and 5 per node per run