Skip to content

EX280 - 15 Hands-On Scenarios

These mirror the format of EX280 tasks. Each is a hands-on objective; complete on a live OpenShift cluster.

Time yourself. Aim for 8-12 minutes per scenario.


Scenario 1 - Configure HTPasswd identity provider

Add two HTPasswd users (alice/secret and bob/secret2) to the cluster. Make alice a cluster-admin.

Solution
htpasswd -c -B -b users.htpasswd alice secret
htpasswd -B -b users.htpasswd bob secret2

oc create secret generic htpass-secret \
    --from-file=htpasswd=users.htpasswd \
    -n openshift-config

oc edit oauth cluster
# Add:
# spec:
#   identityProviders:
#   - name: my_htpasswd
#     mappingMethod: claim
#     type: HTPasswd
#     htpasswd:
#       fileData:
#         name: htpass-secret

# Wait ~30s for OAuth pods to roll
oc -n openshift-authentication get pods

oc adm policy add-cluster-role-to-user cluster-admin alice

oc login -u alice -p secret
oc whoami

Scenario 2 - Create project with quota and limits

Create project production. Quota: 4 CPU requests, 8 GB memory requests, 10 PVCs, 20 pods. LimitRange: per-container default 200m/256Mi request, 1/1Gi limit.

Solution
oc new-project production --description='Production' --display-name='Production'

cat <<EOF | oc apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: '4'
    requests.memory: 8Gi
    persistentvolumeclaims: '10'
    pods: '20'
---
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
  - type: Container
    defaultRequest: { cpu: 200m, memory: 256Mi }
    default: { cpu: '1', memory: 1Gi }
EOF

oc -n production describe quota
oc -n production describe limitrange

Scenario 3 - Grant RBAC roles

Make user bob a project admin in production. Grant charlie only view access.

Solution
oc adm policy add-role-to-user admin bob -n production
oc adm policy add-role-to-user view charlie -n production

# Verify
oc auth can-i create deployments --as=bob -n production
oc auth can-i delete pods --as=charlie -n production       # should be no
oc auth can-i get pods --as=charlie -n production          # should be yes

Scenario 4 - Deploy and expose an app

In production, deploy nginx with 3 replicas, expose at nginx.apps.example.com over edge-terminated TLS, redirecting HTTP to HTTPS.

Solution
oc -n production new-app docker.io/nginx --name=nginx
oc -n production scale deploy/nginx --replicas=3

oc -n production expose deploy/nginx --port=80
oc -n production create route edge nginx \
    --service=nginx \
    --hostname=nginx.apps.example.com \
    --insecure-policy=Redirect

oc -n production get routes
curl -I https://nginx.apps.example.com

Scenario 5 - Persistent storage

Add a 5 GB ReadWriteOnce PVC to the nginx deployment, mounted at /usr/share/nginx/html.

Solution
oc -n production set volume deploy/nginx \
    --add --type=pvc \
    --claim-class=gp3-csi \
    --claim-size=5Gi \
    --claim-mode=ReadWriteOnce \
    --mount-path=/usr/share/nginx/html

oc -n production get pvc
oc -n production rollout status deploy/nginx
oc -n production rsh deploy/nginx df -h /usr/share/nginx/html

Scenario 6 - NetworkPolicy: default deny + allow frontend

In production, deploy two apps with labels app=frontend and app=backend. Configure: backend only accepts ingress from frontend pods.

Solution
oc -n production new-app docker.io/nginx --name=frontend -l app=frontend
oc -n production new-app docker.io/nginx --name=backend -l app=backend

cat <<EOF | oc -n production apply -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes: [Ingress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
spec:
  podSelector: { matchLabels: { app: backend } }
  policyTypes: [Ingress]
  ingress:
  - from:
    - podSelector: { matchLabels: { app: frontend } }
EOF

oc -n production get netpol
# Test:
# oc rsh deploy/frontend -- curl backend             (should work)
# oc run test --image=curlimages/curl --rm -it -- /bin/sh -c "curl backend.production"   (should fail)

Scenario 7 - Install an operator

Install the OpenShift Pipelines operator from OperatorHub via YAML.

Solution
cat <<EOF | oc apply -f -
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: openshift-pipelines-operator
  namespace: openshift-operators
spec:
  channel: latest
  name: openshift-pipelines-operator-rh
  source: redhat-operators
  sourceNamespace: openshift-marketplace
  installPlanApproval: Automatic
EOF

# Wait
oc get csv -n openshift-operators -w
oc get pods -n openshift-operators

Scenario 8 - Configure cluster monitoring

Enable user-workload monitoring with 15-day Prometheus retention.

Solution
cat <<EOF | oc apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-monitoring-config
  namespace: openshift-monitoring
data:
  config.yaml: |
    enableUserWorkload: true
    prometheusK8s:
      retention: 15d
EOF

oc -n openshift-user-workload-monitoring get pods

Scenario 9 - Custom storage class

Create a new StorageClass gp3-encrypted with EBS gp3, encrypted, and make it the cluster default.

Solution
cat <<EOF | oc apply -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3-encrypted
  annotations:
    storageclass.kubernetes.io/is-default-class: 'true'
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  encrypted: 'true'
allowVolumeExpansion: true
reclaimPolicy: Delete
EOF

# Remove default annotation from old default
oc annotate storageclass gp3-csi storageclass.kubernetes.io/is-default-class-

oc get sc

Scenario 10 - Cordon and drain a node

Cordon worker node worker-2, drain it, do "maintenance" (just sleep), then bring it back.

Solution
oc adm cordon worker-2
oc adm drain worker-2 --ignore-daemonsets --delete-emptydir-data
# Maintenance window:
# (do whatever)
oc adm uncordon worker-2
oc get nodes

Scenario 11 - Restrict project creation

Restrict project creation so only cluster admins can create new projects.

Solution
oc adm policy remove-cluster-role-from-group self-provisioner system:authenticated:oauth

# Verify (as a regular user)
oc login -u alice -p secret
oc new-project test    # should fail
To re-enable later:
oc adm policy add-cluster-role-to-group self-provisioner system:authenticated:oauth

Scenario 12 - SCC for legacy app

Deploy an image that requires running as a specific UID (anyuid). Grant the necessary SCC to the service account.

Solution
oc -n production create sa legacy-app
oc adm policy add-scc-to-user anyuid -z legacy-app -n production

cat <<EOF | oc apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: legacy
  namespace: production
spec:
  replicas: 1
  selector: { matchLabels: { app: legacy } }
  template:
    metadata: { labels: { app: legacy } }
    spec:
      serviceAccountName: legacy-app
      containers:
      - name: legacy
        image: docker.io/some-legacy-image
EOF

oc -n production get pods

Scenario 13 - Configure cluster autoscaling (Machine API)

Scale a worker MachineSet from 3 to 5 replicas.

Solution
oc -n openshift-machine-api get machineset
oc -n openshift-machine-api scale machineset/<name> --replicas=5

oc -n openshift-machine-api get machine
# Wait for new nodes
oc get nodes -w

Scenario 14 - Backup and restore an ETCD snapshot

Take an etcd backup on a control plane node.

Solution
# SSH to a control plane node, OR oc debug node/<master>
oc debug node/<master>
chroot /host

# Run cluster backup script
sudo /usr/local/bin/cluster-backup.sh /home/core/backup

# Confirm
ls /home/core/backup
# snapshot_<date>.db, static_kuberesources_<date>.tar.gz

Scenario 15 - Diagnose a broken pod

A deployment in production has 5 replicas but only 2 are running. Find why and fix.

Solution
oc -n production get deployments
oc -n production get pods -o wide
oc -n production describe pod <one-failing>
# Read Events section. Common causes:
# - PVC unbound  β†’ fix PVC
# - ImagePullBackOff β†’ check image name / pull secret
# - SCC violation β†’ grant SCC to SA
# - Insufficient resources on nodes β†’ scale node count or reduce replica resources

oc -n production logs <one-failing>      # for crashed/crashing
oc -n production logs -p <one-failing>   # previous container if crash-looping
Apply fix, then:
oc -n production rollout restart deploy/<name>
oc -n production get pods -w

Scoring guide

  • All 15 in <2 hours, no notes: ready to schedule the exam.
  • 12-14 in 2-3 hours: one more week of practice on weak areas.
  • <12, or you needed notes: keep practicing. EX280 is performance-based; reading isn't enough.

The real exam has roughly 15-20 tasks in 3 hours. If you can do these 15 in 2 hours, you have buffer for verification and harder real-exam tasks.