Setting Up LokiStack on OpenShift: A Step-by-Step Guide

Deploying the LokiStack Operator in OpenShift requires establishing an S3-compatible Object Storage bucket (such as AWS S3, MinIO, or OpenShift Data Foundation NooBaa), storing the bucket credentials in a Kubernetes Secret, and defining a LokiStack Custom Resource.

Step 1: Install the Loki Operator via OLM

Before creating the LokiStack instance, install the Loki Operator from OperatorHub into the openshift-operators-redhat namespace.

YAML

apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: loki-operator
namespace: openshift-operators-redhat
spec:
channel: stable-5.x # Or the matching channel for your OCP release
name: loki-operator
source: redhat-operators
sourceNamespace: openshift-marketplace

Step 2: Create the Object Storage Credentials Secret

Loki requires access to an S3-compatible object storage bucket to store chunked log data and index files.

  1. Create a Secret in the openshift-logging namespace containing your S3 bucket access keys and endpoint configuration:

YAML

apiVersion: v1
kind: Secret
metadata:
name: logging-loki-s3
namespace: openshift-logging
stringData:
access_key_id: "YOUR_S3_ACCESS_KEY"
access_key_secret: "YOUR_S3_SECRET_KEY"
bucketnames: "ocp-loki-logs-bucket"
endpoint: "https://s3.us-east-1.amazonaws.com" # Or internal MinIO/NooBaa endpoint URL
region: "us-east-1"
type: Opaque

Note for AWS IAM Roles for Service Accounts (IRSA) / STS: If using AWS IRSA or ROSA with STS, you can omit access_key_id and access_key_secret in favor of an AWS IAM role annotation on the service account.

Step 3: Deploy the LokiStack Custom Resource

Define the size and replication footprint of your Loki cluster using the LokiStack CRD. Red Hat provides pre-tuned size profiles (1x.extra-small, 1x.small, 1x.medium, etc.) to automatically calculate resource limits and replica counts.

YAML

apiVersion: loki.grafana.com/v1
kind: LokiStack
metadata:
name: logging-loki
namespace: openshift-logging
spec:
size: 1x.small # Pre-tuned production profile (extra-small, small, medium)
storage:
schemas:
- version: v13
effectiveDate: "2024-01-01"
secret:
name: logging-loki-s3
type: s3
storageClassName: fast-ssd # Fast block storage PVCs for Loki write-ahead logs (WAL)
managementState: Managed
replication:
factor: 2 # Number of chunk replicas across Loki ingesters

Apply the manifest:

oc apply -f lokistack.yaml

Step 4: Verify Deployment Health

Check that the Loki components (Ingester, Querier, Query-Frontend, Distributor, Compactor, Gateway) are fully provisioned and healthy:

  1. Verify Pod Readiness:
    Bashoc get pods -n openshift-logging -l app.kubernetes.io/name=loki
  2. Verify LokiStack CR Status:
    Bashoc get lokistack logging-loki -n openshift-logging -o jsonpath='{.status.conditions}' | jq Ensure all conditions (such as Pending, Ready) transition to Status: "True".

Step 5: Connect Vector to Loki (ClusterLogForwarder)

Now that LokiStack is running, configure the ClusterLogForwarder to collect and stream logs into your new Loki instance using the internal Gateway route.

YAML

apiVersion: logging.openshift.io/v1
kind: ClusterLogForwarder
metadata:
name: instance
namespace: openshift-logging
spec:
outputs:
- name: default-loki-stack
type: lokiStack
lokiStack:
target:
name: logging-loki
namespace: openshift-logging
authentication:
token:
from: serviceAccount
url: 'https://logging-loki-gateway.openshift-logging.svc:8080'
pipelines:
- name: all-logs-to-loki
inputRefs:
- application
- infrastructure
- audit
outputRefs:
- default-loki-stack

How to Configure OpenShift’s Cluster Monitoring Operator

The Cluster Monitoring Operator (CMO) is a core, default-installed OpenShift operator responsible for deploying, managing, and maintaining the platform’s entire observability stack.

It implements a fully managed Prometheus-based monitoring solution designed to monitor core OpenShift platform components, node infrastructure, and (optionally) user-defined workload applications.

1. Architecture and Key Components

The CMO acts as a high-level orchestrator. It manages custom resources (CRs), deployments, daemonsets, and configurations across the cluster (primarily within the openshift-monitoring and openshift-user-workload-monitoring namespaces).

Plaintext

                               ┌────────────────────────────────┐
│ Cluster Monitoring Operator │
│ (CMO) │
└───────────────┬────────────────┘
│ Manages
┌─────────────────────────────────┼─────────────────────────────────┐
▼ ▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ Prometheus Core │ │ Alertmanager │ │ Metrics Exporters │
│ (Platform Metrics DB) │ │ (Routing & Alerts) │ │ (kube-state-metrics, │
└────────────┬────────────┘ └─────────────────────────┘ │ node-exporter, cAdvisor)│
│ Scrapes └─────────────────────────┘

┌─────────────────────────┐
│ User Workload Monitoring│
│ (Thanos / Prometheus) │
└─────────────────────────┘

The stack deployed by CMO includes:

  • Prometheus: High-performance time-series database configured to scrape platform metrics (control plane services, etcd, OVN-Kubernetes, routers).
  • Thanos Querier: Provides a unified query interface (via PromQL) across both platform metrics and user workload metrics.
  • Alertmanager: Handles alerting logic, deduplication, grouping, and notification routing (via PagerDuty, Slack, Webhooks, Email).
  • Metrics Exporters: Includes node-exporter (host system metrics), kube-state-metrics (Kubernetes object state), and OpenShift component exporters.
  • Telemeter Client: Sends a curated, anonymized subset of platform health metrics back to Red Hat Insights for proactive support.

2. Core Responsibilities

  1. Automated Configuration & Reconciliation: You do not manage raw Prometheus YAML configs directly. Instead, you modify a central ConfigMap (cluster-monitoring-config), and the CMO reconciles and applies those changes across all underlying Prometheus and Alertmanager instances.
  2. Platform Health Enforcement: CMO ensures critical alert rules (e.g., etcd high latency, node disk pressure, API server errors) are always active and cannot be accidentally deleted by cluster users.
  3. User Workload Monitoring (UWM): By default, CMO only monitors OpenShift platform components. However, administrators can enable UWM in the CMO configuration to allow developers to deploy ServiceMonitor and PodMonitor CRDs for custom application metrics in their own namespaces.

3. Key Configuration Example

To configure CMO settings (such as setting retention periods, persistent volume claims, or enabling User Workload Monitoring), administrators edit the cluster-monitoring-config ConfigMap in the openshift-monitoring namespace.

YAML

apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-monitoring-config
namespace: openshift-monitoring
data:
config.yaml: |
# Enable User Workload Monitoring for application metrics
enableUserWorkload: true
# Configure Platform Prometheus Retention and Storage
prometheusK8s:
retention: 15d
volumeClaimTemplate:
metadata:
name: prometheus-pvc
spec:
storageClassName: fast-ssd
resources:
requests:
storage: 100Gi
# Configure Alertmanager Storage
alertmanagerMain:
volumeClaimTemplate:
metadata:
name: alertmanager-pvc
spec:
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi

4. Useful oc Commands for Troubleshooting CMO

  • Check CMO Operator Status:
    oc get clusteroperator monitoring
  • View Monitoring Pods:
    oc get pods -n openshift-monitoring
  • View User Workload Monitoring Pods (if enabled):
    oc get pods -n openshift-user-workload-monitoring
  • Inspect CMO Logs: oc logs -n openshift-monitoring deployment/cluster-monitoring-operator -c cluster-monitoring-operator

Top Core Operators for OpenShift Architecture

While OpenShift Container Platform (OCP) runs dozens of background operators, a select group of essential core and day-2 operators are critical for architecture, security, and cluster operations.

1. The Core Infrastructure Operators (The Engine)

These operators are installed automatically by default and manage the fundamental lifecycle of the cluster itself.

  • Cluster Version Operator (CVO): The “master controller” of OpenShift. It checks the update payload image, manages the lifecycle of all other cluster operators, and ensures the cluster stays in its declared version state.
  • Machine Config Operator (MCO): Manages the host operating system (Red Hat Enterprise Linux CoreOS / RHCOS). Any change to kernel settings, SSH keys, network configurations, or storage mounts on host nodes is applied declaratively via the MCO.
  • Ingress Operator: Manages the HAProxy-based routers that handle incoming HTTP/HTTPS traffic from outside the cluster into internal application services.
  • Storage Operator / CSI Operators: Manages persistent storage drivers (AWS EBS, vSphere CSI, ODF) to allow dynamic provisioning of PersistentVolumeClaims (PVCs).

2. Platform Expansion & Management Operators

These operators are typically installed on Day 2 from OperatorHub to manage fleet operations, workloads, and security.

OperatorPrimary PurposeKey Benefit
OpenShift GitOps (Argo CD)Declarative Application & Cluster DeliveryEnables Infrastructure-as-Code. Reconciles cluster state directly against Git repositories.
Advanced Cluster Management (ACM)Multi-Cluster Fleet ManagementManages, policy-governs, and updates multiple OpenShift clusters from a single control plane.
Advanced Cluster Security (ACS / StackRox)Kubernetes-Native DevSecOpsProvides runtime threat detection, vulnerability scanning, compliance monitoring, and network policy enforcement.
OpenShift Pipelines (Tekton)Cloud-Native CI/CD PipelinesExecutes containerized build steps directly inside Kubernetes pods without requiring a central Jenkins server.
OpenShift Virtualization (KubeVirt)Modernization / Hybrid ComputeAllows traditional Virtual Machines (Linux/Windows) to run alongside containers on the same nodes.

3. Observability & Operations

Operators that provide operational visibility and cluster-wide telemetry.

  • Cluster Monitoring Operator (CMO): Deploy and manage the built-in Prometheus, Grafana, and Alertmanager stack to collect cluster and node-level metrics.
  • Red Hat OpenShift Logging (Vector / OpenSearch / Loki): Aggregates, parses, and stores logs from cluster components, host nodes, and application containers.
  • Node Feature Discovery (NFD) & GPU Operators: Automatically detects specialized hardware (like NVIDIA GPUs or SR-IOV network interfaces) on host nodes and applies corresponding labels for workload scheduling.

Essential Guide to Backing Up and Restoring etcd in OpenShift (OCP)

Performing an etcd backup is a mandatory step before any major OpenShift Container Platform (OCP) lifecycle operation (such as cluster updates or major infrastructure changes).

OpenShift provides built-in scripts to automate the backup process and restore state directly onto control plane (master) nodes.

Part 1: How to Back Up etcd

The backup script creates a snapshot of the etcd database along with static pod resources, certificates, and API keys.

1.Establish a Debug Session on a Control Plane Node:Prerequisite: Cluster-admin rights required.

Select an active master node and start a debug pod on it:

oc debug node/master-0

2.Set Root Directory Context:

Inside the debug shell, switch your filesystem context to access the host system drives:

chroot /host

3.Execute the Built-in Cluster Backup Script:

Run the automated backup script provided in the RHCOS image, specifying a target output directory:

/usr/local/bin/cluster-backup.sh /var/usr/temp/etcd-backup

Expected Output:

Plaintext

found latest kube-apiserver: /etc/kubernetes/static-pod-resources/kube-apiserver-pod-12
etcdctl version: 3.5.x
snapshot saved at /var/usr/temp/etcd-backup/snapshot_2026-08-29_110156.db
...
etcd backup complete!

4.Verify and Save Backup Artifacts:

Check that both the snapshot DB file and static pod resource archives were generated:

ls -lh /var/usr/temp/etcd-backup

Important: Copy these files off the host node to a secure external storage location (S3 bucket, NFS share, or local admin machine) via oc cp or scp.

Part 2: How to Restore etcd (Disaster Recovery)

Use this process if the cluster’s state database suffers unrecoverable corruption or consensus loss across the control plane.

Warning: A restore is a destructive, high-impact action. It resets the cluster state back to the exact point in time when the backup snapshot was taken.

1.Identify the Recovery Master Node:

Select one surviving master node (master-0) to act as the primary recovery host. Transfer your backup files to this node if they aren’t already present on /var/usr/temp/etcd-backup.

2.Stop the Control Plane Static Pods on ALL Master Nodes:

Open SSH or debug sessions to every master node in the cluster and move the static pod manifests out of /etc/kubernetes/manifests/ to stop kube-apiserver, etcd, and kube-controller-manager:

# Run this on master-0, master-1, and master-2
chroot /host
mv /etc/kubernetes/manifests/etcd-pod.yaml /tmp/
mv /etc/kubernetes/manifests/kube-apiserver-pod.yaml /tmp/

3.Run the Restore Script on the Recovery Node:

On master-0 (the recovery node only), run the restore script using the path to your backup snapshot directory:

chroot /host
/usr/local/bin/cluster-restore.sh /var/usr/temp/etcd-backup

What this script does:

  • Clears corrupted local etcd member data on master-0.
  • Restores the snapshot DB.
  • Generates a single-member etcd cluster configuration.

4.Restart Kubelet on the Recovery Node:

Restart kubelet on master-0 to re-initialize static pods:

systemctl restart kubelet

5.Force etcd Operator Redeployment:

Exit the host debug shell back to your workstation CLI and patch the Cluster Version / etcd Operator to force quorum reconciliation across the remaining control plane nodes:

# Force the etcd operator to re-evaluate cluster membership
oc patch etcd cluster --type=merge -p '{"spec": {"forceRedeploymentReason": "recovery-'$(date +%s)'"}}'

The etcd-operator will automatically redeploy and scale etcd instances across master-1 and master-2, restoring multi-node HA consensus.

Optimize OpenShift with Canary MachineConfigPools

A canary update strategy allows you to test an OpenShift node operating system and config update on a small subset of worker nodes (e.g., a non-critical worker pool) before committing the change across your entire production cluster.

By default, OpenShift updates all nodes in a MachineConfigPool (MCP) automatically once the control plane is updated. Pausing an MCP freezes the Machine Config Operator (MCO) from updating that specific group of nodes.


Step 1: Create a Dedicated Canary MachineConfigPool

Instead of applying updates directly to the default worker pool, isolate 1 or 2 nodes into a custom canary pool.

  1. Label a target worker node for Canary testing:
oc label node worker-canary-0.example.com node-role.kubernetes.io/canary=
  1. Define and create the Canary MachineConfigPool manifest:
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfigPool
metadata:
name: canary
spec:
machineConfigSelector:
matchExpressions:
- {key: machineconfiguration.openshift.io/role, operator: In, values: [worker, canary]}
nodeSelector:
matchLabels:
node-role.kubernetes.io/canary: ""

Save as canary-mcp.yaml and apply:

oc apply -f canary-mcp.yaml

Step 2: Pause the Primary Worker Pool

Before starting the cluster-wide upgrade, pause the default worker pool so its nodes remain on the current version while the control plane and your canary pool update.

  1. Pause the worker MCP:
oc patch mcp/worker --type merge --patch '{"spec":{"paused":true}}'
  1. Verify MCP status:
oc get mcp

Output:

NAME CONFIG UPDATED UPDATING DEGRADED PAUSED
master rendered-master-11111 True False False False
worker rendered-worker-11111 True False False True
canary rendered-worker-11111 True False False False

Step 3: Trigger the OpenShift Upgrade

Initiate the cluster upgrade as normal:

oc adm upgrade --to=<target_version>

What happens next:

  1. The Cluster Version Operator (CVO) updates the Control Plane (master nodes).
  2. The Machine Config Operator (MCO) updates the canary MCP (re-imaging and rebooting worker-canary-0).
  3. The worker MCP is skipped entirely because spec.paused is set to true.

Step 4: Validate the Canary Node

Check that the canary pool reaches an UPDATED=True state and run application validation:

# Verify the Canary node has completed its rollout
oc get mcp/canary
# Verify the node is running the target release kernel and core components
oc describe node worker-canary-0.example.com
  • Leave the cluster running in this state for your designated soak period (e.g., 24–48 hours) to verify application performance and stability.

Step 5: Unpause and Complete Cluster Rollout

Once you confirm the canary update is stable and safe:

  1. Unpause the worker pool:
oc patch mcp/worker --type merge --patch '{"spec":{"paused":false}}'
  1. Monitor the full rollout:
    The MCO will immediately begin cordoning, draining, re-imaging, and rebooting the remaining worker nodes one by one:
oc get mcp -w

Step-by-Step Guide: Upgrading Your OpenShift(OCP) Cluster

OCP Cluster Upgrade — Best Practices Guide


The Golden Rule

Never upgrade a sick cluster. Every check below exists because upgrading on top of an existing problem turns a recoverable issue into a potential outage.


Phase 1 — Pre-Upgrade Preparation (Days Before)

1.1 Know Your Upgrade Path

OCP does not support arbitrary version jumps. Upgrades must follow a validated graph:

4.12 → 4.13 → 4.14 ✅ valid sequential path
4.12 → 4.14 ❌ may not be supported (check the graph)
4.14.5 → 4.14.12 ✅ z-stream (patch) always OK
4.14.12 → 4.15.0 ✅ minor version (must check graph)

Always verify the upgrade path first:

# Check available updates from the official graph
oc adm upgrade
# Output example:
# Recommended updates:
# VERSION IMAGE
# 4.14.13 quay.io/openshift-release-dev/ocp-release@sha256:...
# 4.14.14 quay.io/openshift-release-dev/ocp-release@sha256:...

Use the official upgrade path tool:

https://access.redhat.com/labs/ocpupgradegraph/update_path

1.2 Read the Release Notes

For every version you are jumping through:

https://docs.openshift.com/container-platform/<version>/release_notes/ocp-<version>-release-notes.html

Look specifically for:

  • Deprecated APIs — if your workloads use removed APIs, they break post-upgrade
  • Known issues — bugs that affect your environment
  • Operator compatibility — OLM-managed operators may need separate upgrades
  • Behavior changes — network policy, SCC, authentication changes

1.3 Check Operator Compatibility (OLM)

Operators installed via OLM have their own upgrade lifecycle, separate from OCP:

# List all installed operators and their current versions
oc get csv -A
# Check operator subscription channels
oc get subscription -A -o custom-columns=\
'NAME:.metadata.name,CHANNEL:.spec.channel,SOURCE:.spec.source'

Check the OperatorHub or vendor docs to confirm your operators support the target OCP version before upgrading the platform.


1.4 Check for Deprecated API Usage

OCP removes old API versions between minor releases. If your workloads use removed APIs they will fail silently after upgrade:

# Check for deprecated API usage in the cluster
oc get apirequestcounts -o json | jq -r \
'.items[] |
select(.status.removedInRelease != null) |
select(.status.requestCount > 0) |
"\(.metadata.name) → removed in \(.status.removedInRelease) | used \(.status.requestCount)x"'

Any resource showing removedInRelease with a non-zero requestCount must be migrated before upgrading.


1.5 Validate etcd Health

etcd is the most critical component. A degraded etcd during upgrade can corrupt cluster state:

# Check etcd cluster operator
oc get co etcd
# Check etcd member status
oc get etcd -o jsonpath=\
'{.items[0].status.conditions[?(@.type=="EtcdMembersAvailable")].status}'
# Expected: True
# Check etcd pods directly
oc get pods -n openshift-etcd -l app=etcd
# Check etcd endpoint health
oc rsh -n openshift-etcd $(oc get pods -n openshift-etcd -l app=etcd \
-o jsonpath='{.items[0].metadata.name}') \
etcdctl endpoint health \
--cluster \
--cacert /etc/kubernetes/static-pod-certs/configmaps/etcd-serving-ca/ca-bundle.crt \
--cert /etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-$(hostname).crt \
--key /etc/kubernetes/static-pod-certs/secrets/etcd-all-certs/etcd-peer-$(hostname).key

Expected output:

https://192.168.1.10:2379 is healthy: committed revision: 294858, took: 3ms
https://192.168.1.11:2379 is healthy: committed revision: 294858, took: 4ms
https://192.168.1.12:2379 is healthy: committed revision: 294858, took: 3ms

1.6 Verify All Nodes Are Ready
# All nodes must be Ready with no scheduling issues
oc get nodes
# Check for any taints blocking scheduling
oc get nodes -o custom-columns=\
'NAME:.metadata.name,STATUS:.status.conditions[-1].type,TAINTS:.spec.taints'
# Check node resource pressure
oc adm top nodes

1.7 Check All Cluster Operators
# All must be: AVAILABLE=True PROGRESSING=False DEGRADED=False
oc get co
# Quick one-liner to find any not healthy:
oc get co -o json | jq -r \
'.items[] | select(
(.status.conditions[] | select(.type=="Degraded" and .status=="True"))
or
(.status.conditions[] | select(.type=="Available" and .status=="False"))
) | .metadata.name'
# Expected: empty output

1.8 Check MachineConfigPools
# All pools must be: UPDATED=True UPDATING=False DEGRADED=False
oc get mcp
# Check for paused pools (they won't upgrade automatically)
oc get mcp -o jsonpath='{.items[?(@.spec.paused==true)].metadata.name}'

1.9 Review PodDisruptionBudgets
# Find PDBs that currently block all disruptions
oc get pdb -A -o json | jq -r \
'.items[] |
select(.status.disruptionsAllowed == 0) |
"\(.metadata.namespace)/\(.metadata.name) — minAvailable:\(.spec.minAvailable)"'

For each blocking PDB, verify whether it will open up during drain (more replicas than minAvailable) or whether it is misconfigured and will permanently block node drains.


1.10 Check Active Alerts
# Via AlertManager route
ALERTMANAGER=$(oc get route alertmanager-main \
-n openshift-monitoring \
-o jsonpath='{.spec.host}')
TOKEN=$(oc create token prometheus-k8s -n openshift-monitoring)
curl -sk -H "Authorization: Bearer $TOKEN" \
"https://${ALERTMANAGER}/api/v2/alerts" | \
jq -r '.[] | select(.labels.severity=="critical") | .labels.alertname'

No critical alerts should be firing before upgrade begins.


Phase 2 — The Day Before

2.1 Take an etcd Backup

This is non-negotiable. If the upgrade corrupts cluster state, this is your only recovery path:

# SSH to a master node
ssh core@master-0
# Run backup
sudo /usr/local/bin/cluster-backup.sh /home/core/backup
# Verify both files created
ls -lh /home/core/backup/
# snapshot_<timestamp>.db
# static_kuberesources_<timestamp>.tar.gz
# Copy off-cluster immediately (S3, NFS, bastion host)
scp core@master-0:/home/core/backup/* bastion:/backups/pre-upgrade/

The backup must be from the same z-stream as the running cluster. A 4.14.12 cluster backup cannot restore a 4.15.x cluster.


2.2 Notify Stakeholders

Upgrades cause rolling node reboots. Even with zero downtime on properly configured workloads, communicate:

  • Upgrade window start/end time
  • Which node groups reboot and in what order (masters first, then workers)
  • Expected impact on any workloads that are not highly available

2.3 Scale Down Non-Critical Workloads (Optional)

For resource-constrained clusters, reducing workload during upgrade gives the upgrade controller more room to drain nodes:

# Note current replicas first, then scale down
oc get deployments -A --no-headers | \
awk '{print $1, $2, $3}' > /tmp/pre-upgrade-replicas.txt
# Scale down non-critical namespaces
oc scale deployment --all -n non-critical-namespace --replicas=0

Phase 3 — Initiating the Upgrade

3.1 Choose the Right Channel

OCP upgrade channels control which versions are offered:

stable-4.14 → production-recommended, fully validated
fast-4.14 → validated but released sooner than stable
candidate-4.14 → pre-release, NOT for production
eus-4.14 → Extended Update Support (long-term maintenance)
# Check current channel
oc get clusterversion -o jsonpath='{.items[0].spec.channel}'
# Set channel if needed
oc patch clusterversion version \
--type merge \
--patch '{"spec":{"channel":"stable-4.14"}}'

3.2 Trigger the Upgrade
# Upgrade to the latest available in channel
oc adm upgrade --to-latest=true
# Upgrade to a specific version (recommended — explicit and auditable)
oc adm upgrade --to=4.14.13
# For disconnected clusters (specify the full image digest)
oc adm upgrade \
--to-image=quay.io/openshift-release-dev/ocp-release@sha256:<digest> \
--allow-explicit-upgrade

3.3 Monitor the Upgrade Progress
# Watch CVO progress — the top-level view
watch oc get clusterversion
# More detail
oc describe clusterversion version | grep -A 30 "Conditions:"
# Watch all cluster operators rolling through upgrade
watch oc get co
# Watch nodes rebooting (masters first, then workers)
watch oc get nodes
# Tail CVO operator logs for real-time detail
oc logs -n openshift-cluster-version \
$(oc get pods -n openshift-cluster-version \
-o jsonpath='{.items[0].metadata.name}') -f

3.4 Understand the Upgrade Sequence

OCP upgrades in a strict order — never all at once:

1. CVO downloads new release image
2. Control plane operators update
(etcd, kube-apiserver, controller-manager, scheduler)
One at a time, with health checks between each
3. Master nodes drain → reboot → rejoin
One at a time (never all 3 simultaneously)
4. Cluster operators update
(authentication, console, ingress, monitoring, etc.)
5. Worker nodes drain → reboot → rejoin
Controlled by MachineConfigPool maxUnavailable setting
6. CVO marks upgrade complete
clusterversion: Progressing=False, Available=True

3.5 Control Worker Upgrade Speed

By default only 1 worker upgrades at a time. For large clusters you can safely increase this:

# Check current maxUnavailable (default is 1)
oc get mcp worker -o jsonpath='{.spec.maxUnavailable}'
# Allow up to 20% of workers to upgrade simultaneously
oc patch mcp worker \
--type merge \
--patch '{"spec":{"maxUnavailable":"20%"}}'

⚠️ Never set maxUnavailable higher than your workloads can tolerate. If minAvailable in your PDBs is 2 and you only have 3 replicas, draining 2 workers simultaneously will block.


Phase 4 — During the Upgrade

Watch for Stuck Upgrades

# If upgrade appears frozen, check:
# 1. Any operator stuck progressing?
oc get co | grep -v "True.*False.*False"
# 2. Any node stuck draining?
oc get nodes | grep SchedulingDisabled
# 3. Any pods blocking drain (PDB)?
oc get pdb -A | grep -v "^NAMESPACE"
# 4. Pending CSRs (workers trying to re-join after reboot)
oc get csr | grep Pending
oc get csr -o name | xargs oc adm certificate approve
# 5. Any pods stuck Terminating (blocking node drain)?
oc get pods -A | grep Terminating
# Force delete if stuck:
oc delete pod <pod> -n <ns> --grace-period=0 --force

Approve Pending CSRs

After each node reboots, it issues a CSR to re-register with the cluster. In some configurations these need manual approval:

# Watch for and approve all pending CSRs
watch -n 10 "oc get csr | grep Pending"
# Approve all at once
oc get csr -o name | xargs oc adm certificate approve

Phase 5 — Post-Upgrade Validation

5.1 Confirm Upgrade Completed
# Check cluster version
oc get clusterversion
# AVAILABLE=True PROGRESSING=False VERSION=<new-version>
# Confirm exact version
oc get clusterversion -o jsonpath='{.items[0].status.desired.version}'

5.2 Validate All Operators
# All must return to healthy state
oc get co
# All: AVAILABLE=True PROGRESSING=False DEGRADED=False

5.3 Validate All Nodes
# All nodes Ready, running new kubelet version
oc get nodes
# All: STATUS=Ready, VERSION=v1.27.x (new version)

5.4 Validate Workloads
# Check for any crashlooping or failed pods
oc get pods -A | grep -vE "Running|Completed|Succeeded"
# Check deployments fully available
oc get deployments -A | grep -v "^\(NAMESPACE\)" | \
awk '$3 != $4 {print}' # READY != DESIRED
# Check routes are responding
oc get routes -A

5.5 Take a Post-Upgrade etcd Backup
# The pre-upgrade backup is now stale — take a fresh one
sudo /usr/local/bin/cluster-backup.sh /home/core/backup-post-upgrade
scp core@master-0:/home/core/backup-post-upgrade/* bastion:/backups/post-upgrade/

Phase 6 — OLM Operator Upgrades

Platform upgrade does not upgrade OLM-managed operators. Do this separately:

# Check which operators have updates available
oc get csv -A | grep -v Succeeded
# Check subscription installPlanApproval settings
oc get subscription -A -o custom-columns=\
'NS:.metadata.namespace,NAME:.metadata.name,APPROVAL:.spec.installPlanApproval'
# For Manual approval — approve pending InstallPlans
oc get installplan -A | grep RequiresApproval
oc patch installplan <name> -n <namespace> \
--type merge \
--patch '{"spec":{"approved":true}}'

Complete Upgrade Checklist

PRE-UPGRADE
□ Verify upgrade path on Red Hat graph
□ Read release notes for target version
□ Check deprecated API usage (apirequestcounts)
□ Verify etcd health (all 3 members healthy)
□ All cluster operators: Available=True, Degraded=False
□ All MachineConfigPools: Updated=True, Degraded=False
□ All nodes: Ready
□ No PDBs blocking all disruptions (disruptionsAllowed=0 on critical apps)
□ No critical alerts firing
□ Operator compatibility confirmed (OLM operators)
□ Stakeholders notified
□ etcd backup taken and stored off-cluster
□ Upgrade channel set correctly (stable/fast/eus)
DURING UPGRADE
□ Monitor CVO progress (watch oc get clusterversion)
□ Monitor cluster operator rollout (watch oc get co)
□ Monitor node reboots (watch oc get nodes)
□ Approve pending CSRs as nodes rejoin
□ Watch for stuck Terminating pods
□ Watch for PDB-blocked drains
POST-UPGRADE
□ clusterversion: Available=True, Progressing=False
□ All cluster operators healthy
□ All nodes Ready on new version
□ All workload pods Running/Completed
□ All routes responding
□ Upgrade OLM operators to compatible versions
□ Take fresh etcd backup
□ Update documentation / CMDB with new version

Key Timings to Expect

PhaseTypical Duration
CVO image pull + validation5–15 min
Control plane operator updates30–60 min
Master node rolling reboots15–30 min
Cluster operator updates20–40 min
Worker node rolling reboots30 min – several hours (depends on count)
Total (small cluster ~6 nodes)~2–3 hours
Total (large cluster 50+ workers)~4–8 hours

OpenShift(OCP) Pre-Upgrade Health Check Script

Purpose

This script is a pre-flight gate before triggering an OCP upgrade. It runs 7 sequential checks and blocks the upgrade if any critical condition is detected. Think of it as a checklist that a senior SRE would run manually — automated.

1. Verify Active CLI Session & Cluster Admin Access

2. Check ClusterVersion Operator (CVO) Status

What is the CVO?
The Cluster Version Operator is the top-level operator that manages the OCP version and coordinates all upgrades. If it’s unhealthy or busy, no upgrade should start.

What the jsonpath queries extract:

The clusterversion object has a status.conditions array:

yaml

status:
conditions:
- type: Available
status: "True" ← extracted by first query
- type: Progressing
status: "False" ← extracted by second query
- type: Degraded
status: "False"

The condition check:

AvailableProgressingMeaningAction
TrueFalseCVO healthy and idle✅ OK to upgrade
TrueTrueUpgrade already running❌ Block
FalseFalseCVO degraded/broken❌ Block
FalseTrueUpgrade running AND broken❌ Block

Equivalent manual check:

oc get clusterversion
# NAME VERSION AVAILABLE PROGRESSING SINCE STATUS
# version 4.14.12 True False 5d Cluster version is 4.14.12

3. Audit Core Cluster Operators

4. Check MachineConfigPools (MCP)

What are MCPs?

MachineConfigPools define groups of nodes and the configuration applied to them. During an upgrade, the MCP controller drains and reboots each node to apply the new RHCOS and MachineConfig.

master MCP → controls all 3 master nodes
worker MCP → controls all worker nodes
infra MCP → controls infra nodes (if defined)

Degraded MCP check:
A degraded MCP means at least one node in the pool failed to apply its MachineConfig — it didn’t reboot correctly, got stuck, or had a rendering error. Upgrading on top of a degraded MCP compounds the problem.

5. Check Node Readiness

6. Audit PodDisruptionBudgets (PDB) for Potential Deadlocks

What is a PDB?

A PodDisruptionBudget is a policy that limits how many pods of an application can be voluntarily disrupted (evicted) at once:

7. Check Active Critical Alerts

What oc get alerts does:

This uses the OpenShift alerts API resource — a custom OCP resource that surfaces Prometheus AlertManager alerts via the Kubernetes API:

oc get alerts -A
# NAMESPACE NAME STATE SEVERITY AGE
# openshift-* etcdHighNumberOfFailed firing critical 5m ← caught
# openshift-* NodeNotReady pending warning

The 2>/dev/null || echo "":
oc get alerts is not available on all OCP versions or configurations. The 2>/dev/null suppresses errors and || echo "" ensures FIRING_CRITICALS is empty (not unset) if the command fails — avoiding the -u unset variable trap.

Why critical alerts block an upgrade:

A firing critical alert means something is actively broken in the cluster. Upgrading on top of an existing critical condition risks:

  • Making a broken component worse during its own operator-driven upgrade
  • Masking the original problem behind upgrade noise
  • A critical alert like etcdMemberDown means your etcd quorum is at risk — the worst time to upgrade

Complete flow:

START

├─ [1/7] oc whoami → HARD EXIT if not logged in
├─ [2/7] CVO Available+Idle → ERRORS++ if degraded or progressing
├─ [3/7] Cluster Operators → ERRORS++ if any Degraded or Unavailable
├─ [4/7] MachineConfigPools → ERRORS++ if Degraded / WARN if Paused
├─ [5/7] Node Readiness → ERRORS++ if any NotReady
├─ [6/7] PDB Deadlocks → WARN only (no ERRORS++)
└─ [7/7] Critical Alerts → ERRORS++ if any firing

├─ ERRORS == 0 → exit 0 (safe to upgrade)
└─ ERRORS > 0 → exit 1 (do not upgrade)

The script :

#!/usr/bin/env bash
#
# OCP Pre-Upgrade Health Check Automation Script
# Validates cluster readiness before initiating an OpenShift platform update.
#
set -euo pipefail
# ANSI Color Codes for Scannable Output
RED='\030[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
ERRORS=0
echo -e "${YELLOW}====================================================${NC}"
echo -e "${YELLOW} OpenShift Pre-Upgrade Automated Health Check ${NC}"
echo -e "${YELLOW}====================================================${NC}\n"
# 1. Verify Active CLI Session & Cluster Admin Access
echo -n "[1/7] Checking OpenShift CLI authentication... "
if ! oc whoami &>/dev/null; then
echo -e "${RED}[FAILED]${NC} Not logged into an OpenShift cluster. Run 'oc login' first."
exit 1
fi
echo -e "${GREEN}[OK]${NC} Authenticated as $(oc whoami)"
# 2. Check ClusterVersion Operator (CVO) Status
echo -n "[2/7] Checking ClusterVersion status... "
CVO_STATUS=$(oc get clusterversion -o jsonpath='{.items[0].status.conditions[?(@.type=="Available")].status}')
CVO_PROGRESSING=$(oc get clusterversion -o jsonpath='{.items[0].status.conditions[?(@.type=="Progressing")].status}')
if [[ "$CVO_STATUS" == "True" && "$CVO_PROGRESSING" == "False" ]]; then
echo -e "${GREEN}[OK]${NC} CVO is Available and idle."
else
echo -e "${RED}[FAILED]${NC} CVO is degraded or an update is already in progress."
((ERRORS++))
fi
# 3. Audit Core Cluster Operators
echo "[3/7] Auditing Cluster Operators state..."
DEGRADED_OPS=$(oc get clusteroperator -o json | jq -r '.items[] | select(.status.conditions[] | select(.type=="Degraded" and .status=="True")) | .metadata.name')
UNAVAILABLE_OPS=$(oc get clusteroperator -o json | jq -r '.items[] | select(.status.conditions[] | select(.type=="Available" and .status=="False")) | .metadata.name')
if [[ -n "$DEGRADED_OPS" ]]; then
echo -e " ${RED}✗ Degraded Operators found:${NC}\n$DEGRADED_OPS"
((ERRORS++))
else
echo -e " ${GREEN}✓ Zero Degraded operators.${NC}"
fi
if [[ -n "$UNAVAILABLE_OPS" ]]; then
echo -e " ${RED}✗ Unavailable Operators found:${NC}\n$UNAVAILABLE_OPS"
((ERRORS++))
else
echo -e " ${GREEN}✓ All operators are Available.${NC}"
fi
# 4. Check MachineConfigPools (MCP)
echo "[4/7] Validating MachineConfigPools (MCP)..."
DEGRADED_MCP=$(oc get mcp -o jsonpath='{.items[?(@.status.conditions[?(@.type=="Degraded")].status=="True")].metadata.name}')
PAUSED_MCP=$(oc get mcp -o jsonpath='{.items[?(@.spec.paused==true)].metadata.name}')
if [[ -n "$DEGRADED_MCP" ]]; then
echo -e " ${RED}✗ Degraded MachineConfigPools:${NC} $DEGRADED_MCP"
((ERRORS++))
else
echo -e " ${GREEN}✓ All MachineConfigPools healthy.${NC}"
fi
if [[ -n "$PAUSED_MCP" ]]; then
echo -e " ${YELLOW}! Warning: Paused MachineConfigPools detected:${NC} $PAUSED_MCP"
fi
# 5. Check Node Readiness
echo "[5/7] Checking Node status across cluster..."
NOT_READY_NODES=$(oc get nodes --no-headers | awk '$2 != "Ready" {print $1}')
if [[ -n "$NOT_READY_NODES" ]]; then
echo -e " ${RED}✗ Nodes in NotReady state:${NC}\n$NOT_READY_NODES"
((ERRORS++))
else
echo -e " ${GREEN}✓ All nodes report Ready status.${NC}"
fi
# 6. Audit PodDisruptionBudgets (PDB) for Potential Deadlocks
echo "[6/7] Checking PodDisruptionBudgets (PDBs) for drain lock risks..."
DEADLOCKED_PDBS=$(oc get pdb -A -o json | jq -r '.items[] | select(.status.disruptionsAllowed == 0) | "\(.metadata.namespace)/\(.metadata.name)"')
if [[ -n "$DEADLOCKED_PDBS" ]]; then
echo -e " ${YELLOW}! PDBs currently allowing 0 disruptions (May block node drains):${NC}"
echo "$DEADLOCKED_PDBS" | sed 's/^/ /'
else
echo -e " ${GREEN}✓ No blocking PDBs detected.${NC}"
fi
# 7. Check Active Critical Alerts
echo "[7/7] Checking Prometheus Alerts..."
FIRING_CRITICALS=$(oc get alerts -A -o json 2>/dev/null | jq -r '.items[] | select(.status.state=="firing" and .labels.severity=="critical") | .labels.alertname' || echo "")
if [[ -n "$FIRING_CRITICALS" ]]; then
echo -e " ${RED}✗ Firing Critical Alerts:${NC}\n$FIRING_CRITICALS"
((ERRORS++))
else
echo -e " ${GREEN}✓ Zero firing critical alerts.${NC}"
fi
# Final Summary
echo -e "\n${YELLOW}====================================================${NC}"
if [[ $ERRORS -eq 0 ]]; then
echo -e "${GREEN} SUCCESS: Cluster passed all health checks. Safe to proceed with upgrade.${NC}"
echo -e "${YELLOW}====================================================${NC}"
exit 0
else
echo -e "${RED} FAILURE: Found $ERRORS issue(s). Resolve all errors before upgrading.${NC}"
echo -e "${YELLOW}====================================================${NC}"
exit 1
fi

OpenShift (OCP) Upgrade Checklist: 5 Essential Phases

Planning an OpenShift Container Platform (OCP) upgrade requires structured validation across 5 distinct phases. Because OpenShift performs rolling updates of both the control plane and the host OS (RHCOS), executing pre-upgrade checks prevents node-drain locks, degraded operators, or broken API dependencies.

Phase 1: Pre-Upgrade Review & Path Mapping

  1. Verify Your Upgrade Path:
    • OpenShift does not support skipping Y-stream releases (e.g., jumping from 4.14 to 4.16 directly). You must upgrade sequentially (4.14 → 4.15 → 4.16).
    • Check your configured update channel (stable-4.x, fast-4.x, or eus-4.x).For enterprise production environments, stick to stable-4.x or eus-4.x (Extended Update Support).
    • Query the OpenShift Update Service (OSUS) via CLI:
      adm upgrade
  2. Audit Deprecated & Removed Kubernetes APIs:
    • Before a minor version jump (e.g., 4.15 → 4.16), check if your workloads use removed K8s API versions.
    • Review cluster alerts for API deprecations and migrate manifests accordingly. OCP will block minor version upgrades if administrator acknowledgment is missing for API removals.
  3. Check Operator & OLM Compatibility:
    • Non-core platform operators (installed via OperatorHub/OLM) must be upgraded to versions compatible with the target OCP release before updating the cluster core.

Phase 2: Cluster Health & Capacity Assessment

Run a complete health scan. Never trigger an upgrade on a degraded cluster.

  1. Verify ClusterOperator Status:Ensure all core operators report AVAILABLE=True, PROGRESSING=False, and DEGRADED=False:
    oc get clusteroperator
  2. Verify MachineConfigPool (MCP) Health:Ensure all node pools are updated and none are paused or degraded:
    oc get mcp (If an MCP is paused for canary testing, verify it is intentional before proceeding.)
  3. Check Node Capacity & PodDisruptionBudgets (PDBs):
    • Node Capacity: During node-draining, remaining workers must have enough CPU/RAM capacity to host evacuated workloads.
    • PDB Deadlocks: Inspect custom PodDisruptionBudgets (oc get pdb -A). If minAvailable is set equal to the total replica count (or maxUnavailable: 0), node drains will stall, hanging the upgrade loop.
  4. Clear Active Alerts:In the console, go to Observe → Alerting and resolve all critical alerts.

Phase 3: Mandatory Backups (Non-Negotiable)

Because OpenShift updates are fail-forward (rolling back to an older minor version is not supported), taking backups prior to execution is mandatory:

  1. Backup etcd: Take a manual snapshot of the etcd database from a control plane node:
    oc debug node/<master-node-name> -- /usr/local/bin/cluster-backup.sh /host/var/usr/temp/etcd-backup
  2. Export Cluster Custom Resources (CRDs): Export your custom deployment manifests, routes, and secret configurations using a GitOps tool (ArgoCD/ACM) or CLI backups.
  3. Storage Snapshots: Create CSI volume snapshots for critical stateful application data.

Phase 4: Execution & Monitoring

  1. Trigger the Upgrade:
    • Via Web Console:Navigate to Administration → Cluster Settings → Details, select your desired target version, and confirm.
    • Via CLI:
      oc adm upgrade --to=<target_version>
  2. Monitor the Phased Rollout:
    • Control Plane Phase: Watch the CVO reconcile cluster operators:
      oc get clusterversion -w
    • Worker Pool Phase: Watch the MCO cordon, drain, re-image (rpm-ostree), reboot, and uncordon nodes pool-by-pool:
      oc get nodes
    • oc get mcp

Phase 5: Post-Upgrade Validation

Once the CVO reports Cluster version is <target_version>:

  • [ ] Verify all nodes report Ready and run the new RHCOS kernel version.
  • [ ] Confirm all ClusterOperators return to an AVAILABLE=True state.
  • [ ] Verify routes, ingress controllers, and critical business applications are accepting traffic.
  • [ ] Re-enable any paused MachineConfigPools or automated maintenance windows.

Designing Azure Architecture for AI Models and Data Lakes

For a senior Azure architect interview, I’d design this as a layered enterprise platform: ingestion → lakehouse → AI/ML → API serving → security/governance → observability. Azure’s current architecture guidance supports combining ADLS, Databricks/data pipelines, private networking, API Management, and Microsoft Foundry/Azure OpenAI for this pattern. (Microsoft Learn)

Reference architecture

                    USERS / APPLICATIONS
                           |
                    Azure Front Door
                     + WAF / DDoS
                           |
                           v
                 +--------------------+
                 | Azure API          |
                 | Management (APIM)  |
                 |--------------------|
                 | Auth / JWT         |
                 | Rate limiting      |
                 | AI gateway         |
                 | Quotas / logging   |
                 +---------+----------+
                           |
              +------------+-------------+
              |                          |
              v                          v
      +---------------+          +----------------+
      | AKS / App     |          | Microsoft      |
      | Services      |          | Foundry /      |
      |---------------|          | Azure OpenAI   |
      | REST APIs     |          |----------------|
      | AI agents     |--------->| GPT / LLM      |
      | Orchestrator  |          | Embeddings     |
      | RAG services  |          | Model inference|
      +-------+-------+          +----------------+
              |
              | Search context
              v
      +--------------------+
      | Azure AI Search    |
      |--------------------|
      | Vector index       |
      | Hybrid search      |
      | Semantic search    |
      +---------+----------+
                ^
                |
              Embeddings
                |
+---------------+------------------------------------------+
|                    DATA / AI PLATFORM                    |
|                                                          |
|        Azure Databricks / Spark                          |
|     +------------------------------------+               |
|     | Data cleaning / transformation     |               |
|     | Feature engineering                |               |
|     | ML training                        |               |
|     | Embedding generation               |               |
|     | Batch / streaming processing       |               |
|     | MLflow / model lifecycle           |               |
|     +-----------------+------------------+               |
|                       |                                  |
|                 Delta / Lakehouse                        |
|                       |                                  |
|                       v                                  |
|             +---------------------+                      |
|             | ADLS Gen2           |                      |
|             |---------------------|                      |
|             | Bronze - Raw        |                      |
|             | Silver - Clean      |                      |
|             | Gold - Curated      |                      |
|             | ML datasets         |                      |
|             | Documents           |                      |
|             +---------------------+                      |
+----------------------------------------------------------+
                       ^
                       |
             INGESTION / PIPELINES
                       |
        +--------------+---------------+
        |                              |
 Azure Data Factory               Event Hubs
 / Fabric Data Factory            / streaming
        |                              |
        +--------------+---------------+
                       |
       +---------------+------------------+
       |               |                  |
   Databases         APIs              Files
   SQL/Oracle        SaaS          CSV/JSON/PDF
       |
   On-premises
   systems

ADLS Gen2 gives you the scalable storage foundation for analytics and AI, while Databricks can provide Spark processing, Delta Lake/lakehouse processing and ML workflows. Microsoft recommends Unity Catalog as the modern Databricks governance/access pattern for storage rather than older direct-access configurations. (Microsoft Learn)

1. Data lake and pipelines

I would make ADLS Gen2 the system of record and divide it into:

Bronze
Raw source data
Silver
Validated / cleaned / standardized
Gold
Business-ready / ML-ready datasets

Data arrives through Azure Data Factory for batch ingestion and Event Hubs for streaming workloads. ADF is designed to create and schedule data-driven ingestion and transformation workflows across different data stores. (Microsoft Learn)

For heavy transformation:

ADF
|
+----> Databricks Jobs
|
+--> Spark
+--> Delta Lake
+--> Feature engineering
+--> ML pipelines

2. AI model layer

I would support two types of models.

For foundation models:

Microsoft Foundry
|
Azure OpenAI / Foundry models
|
+-----+-----+
| |
GPT/LLM Embeddings

For custom ML models:

ADLS
|
Databricks
|
Training
|
MLflow
|
Model registry
|
Deployment
|
AKS / managed inference endpoint

This separation is useful because I don’t want to run a GPT-class foundation model myself unless there is a strong requirement. Managed Foundry models handle that side, while proprietary classifiers, forecasting models and specialized models can follow a traditional MLOps lifecycle.


3. RAG architecture

For enterprise GenAI, I’d normally add Azure AI Search.

                   Offline pipeline

Documents
   |
ADLS
   |
Databricks / AI Search ingestion
   |
Chunk documents
   |
Generate embeddings
   |
Azure AI Search
Vector index


                    Runtime

User
 |
 v
APIM
 |
 v
AKS AI API
 |
 +---- query ----> Azure AI Search
 |                     |
 |                  Relevant
 |                  documents
 |                     |
 +<--------------------+
 |
 | Prompt + context
 v
Azure OpenAI
 |
 v
Grounded answer

Azure AI Search can provide integrated vectorization and retrieval for Azure OpenAI RAG workloads, reducing the amount of custom embedding/indexing code required in some designs.


4. API architecture

I would not expose the model endpoint directly.

Instead:

Client
|
Front Door/WAF
|
APIM
|
AI orchestration API
|
+-------+---------+---------+
| | | |
GPT Search Databricks Business
APIs

APIM becomes the enterprise control point for:

  • OAuth2/OIDC and Microsoft Entra ID authentication
  • JWT validation
  • throttling and quotas
  • request/response policies
  • API versioning
  • usage/cost controls
  • model routing
  • observability

Microsoft now specifically documents APIM as a gateway in front of Foundry/Azure OpenAI workloads for centralized routing, load balancing, throttling and observability. (Microsoft Learn)

For multiple model deployments, APIM can also use backend pools to distribute requests across several backends. (Microsoft Learn)


5. AKS application layer

For an enterprise deployment I’d use:

AKS
|
+-- System Node Pool
|
+-- API Node Pool
| |
| +-- REST APIs
| +-- AI orchestration
| +-- RAG services
|
+-- ML Node Pool
|
+-- Custom inference
+-- GPU workloads if required

Then use:

HPA
|
Pod scaling
Cluster Autoscaler
|
Node scaling

The Azure Architecture Center maintains a dedicated baseline AKS architecture specifically for production infrastructure design. (Microsoft Learn)


6. Enterprise networking

This is where I would spend significant interview time.

                    Azure Landing Zone

                      HUB VNET
                 +----------------+
On-Prem -------->| ExpressRoute   |
                 | Azure Firewall |
                 | DNS Resolver   |
                 +-------+--------+
                         |
                  VNet Peering
                         |
               +---------+----------+
               |    AI Spoke VNet   |
               |                    |
               | AKS                |
               | APIM               |
               | Private Endpoints  |
               +---------+----------+
                         |
              Private Link only
                         |
        +----------------+----------------+
        |                |                |
       ADLS          Azure OpenAI      AI Search
        |                |                |
     Private           Private          Private
     Endpoint          Endpoint         Endpoint

For regulated environments, I would disable public access where supported and use Private Link/private endpoints for PaaS resources. Private endpoints assign a private VNet IP to reach the service rather than traversing a public endpoint. (Microsoft Learn)

Private DNS is critical:

AKS
|
DNS query
|
Azure DNS Private Resolver
|
Private DNS Zone
|
Private Endpoint IP
|
Azure OpenAI / ADLS / AI Search

Microsoft’s Foundry landing-zone architecture explicitly uses private DNS zones to resolve private endpoints securely from workload networks. (Microsoft Learn)


7. Identity and secrets

Avoid credentials in applications.

Pod
|
AKS Workload Identity
|
Microsoft Entra ID
|
+----------+----------+----------+
| | | |
ADLS Key Vault AI Search Azure OpenAI

Use:

Microsoft Entra ID → managed identities/workload identity → Azure RBAC

rather than embedding storage keys or model API keys inside Kubernetes Secrets whenever possible.

Key Vault stores unavoidable secrets, certificates and keys.


8. Governance

For a banking/regulated enterprise I’d add:

Microsoft Purview
|
Data classification
Lineage
Sensitive-data discovery
Governance
Azure Policy
|
Landing-zone guardrails
Private endpoints required
Allowed regions
Allowed SKUs
Databricks Unity Catalog
|
Tables
Files
Models
Permissions
Lineage

9. Observability

The entire stack feeds centralized monitoring:

AKS
Azure OpenAI
APIM
Databricks
AI Search
ADF
|
v
Azure Monitor
|
Log Analytics
|
Application Insights
|
Microsoft Sentinel

For AI specifically, capture:

Model latency
Token consumption
HTTP 429s
Model errors
APIM latency
Retrieval latency
Prompt/response safety signals
Cost per application
Model quality

Microsoft provides an architecture specifically for advanced monitoring of Foundry/Azure OpenAI models through a gateway.


10. CI/CD + MLOps

I’d separate application, infrastructure, data and model deployment pipelines:

GitHub / Azure DevOps
|
+---- Terraform/Bicep
| |
| Azure Infrastructure
|
+---- App CI/CD
| |
| ACR
| |
| AKS
|
+---- DataOps
| |
| Databricks / ADF
|
+---- MLOps
|
Training
|
MLflow
|
Validation
|
Registry
|
Deployment

The interview answer I’d memorize

If the interviewer asks:

“Design an Azure architecture to host AI models, APIs, a data lake and supporting pipelines.”

You can answer:

“I’d build the platform around ADLS Gen2 as the enterprise data lake, with Data Factory and Event Hubs handling batch and streaming ingestion. Databricks would provide the lakehouse, Spark transformations, ML training and model lifecycle capabilities. For generative AI I’d use Microsoft Foundry/Azure OpenAI and Azure AI Search for RAG and vector retrieval.

Application and AI orchestration APIs would run on AKS and be exposed through Azure API Management, which provides authentication, throttling, model routing and API governance.

From a security perspective I’d deploy the platform into an Azure landing-zone hub-and-spoke architecture, use private endpoints for ADLS, AI Search and model services, use Entra ID and workload identities instead of static credentials, and Key Vault where secrets are unavoidable.

Finally, I’d use Azure Monitor, Log Analytics, Application Insights and Sentinel for centralized observability, with Terraform and CI/CD pipelines managing infrastructure and application deployments.”

That’s approximately a 90-second architect-level answer, while still giving the interviewer several areas—AKS, networking, AI, data, security and MLOps—to drill into. (Microsoft Learn)

Modern analytics architecture with Azure Databricks - Azure ...

Azure Data Lakehouse: Bronze, Silver, Gold Explained

For an enterprise Azure analytics platform, I would use a polyglot data architecture: each storage technology handles the workload it is best suited for, while ADLS Gen2 becomes the central analytical system of record. Microsoft’s current guidance supports ADLS-based medallion/lakehouse architectures and emphasizes storage layout, file formats, partitioning, and parallelism for analytical performance. (Microsoft Learn)

Reference architecture

                         DATA SOURCES
                              |
       +----------------------+----------------------+
       |                      |                      |
   Applications          Enterprise DBs        Files / APIs
       |                      |                      |
       v                      v                      v
   Cosmos DB              Azure SQL /           ADF / Event
 operational DB            SQL Server             Hubs
       |                      |                      |
       +----------------------+----------------------+
                              |
                              v
                    Azure Data Factory
                  / Event Hubs / Databricks
                              |
                              v
              +--------------------------------+
              | Azure Blob / ADLS Gen2         |
              |                                |
              |  BRONZE - Raw                  |
              |  SILVER - Cleaned              |
              |  GOLD   - Curated              |
              +---------------+----------------+
                              |
                         Delta / Parquet
                              |
                    Azure Databricks
                         Spark / SQL
                              |
               +--------------+-------------+
               |                            |
               v                            v
        Azure SQL DB /                 AI / ML
        SQL serving layer              workloads
               |
               v
        Power BI / APIs /
        Analytics applications

Azure Data Factory can directly copy and transform data into ADLS Gen2, making it appropriate as the orchestration and ingestion layer. (Microsoft Learn)


1. Azure Blob Storage / ADLS Gen2

This should contain the largest volume of analytical data.

Conceptually:

Storage Account
├── bronze/
│ ├── cosmos/
│ ├── sql/
│ ├── api/
│ └── documents/
├── silver/
│ ├── customers/
│ ├── transactions/
│ └── products/
└── gold/
├── customer_360/
├── revenue/
├── risk/
└── ai_training/

I would normally enable the hierarchical namespace, effectively using Azure Data Lake Storage Gen2 capabilities on Azure Blob Storage. ADLS Gen2 is designed for large-scale analytical workloads. (Microsoft Learn)

Bronze — immutable/raw

Keep data as close to the source as possible:

Cosmos JSON
SQL extracts
CSV
JSON
Avro
application logs
documents

Example:

/bronze/cosmos/orders/year=2026/month=08/day=11/
/bronze/sql/customer/year=2026/month=08/day=11/

Do not let consumers directly modify Bronze.


2. Silver — analytical processing

Databricks transforms Bronze into standardized datasets:

Bronze
|
v
Azure Databricks
|
+-- schema enforcement
+-- data cleaning
+-- deduplication
+-- joins
+-- standardization
+-- quality checks
+-- PII handling
|
v
Silver

For example:

bronze:
customer JSON + transactions + CRM
silver:
Customer
Transaction
Account
Product

I’d generally use Delta Lake/Parquet rather than CSV for analytical datasets. A lakehouse architecture combines scalable object storage with data-management and warehouse-style capabilities. (Microsoft Learn)


3. Gold — business-ready analytics

Gold contains datasets optimized for consumption rather than raw ingestion.

Silver
|
+----------+
| |
v v
Customer360 SalesMetrics
| |
+----+-----+
|
GOLD

Example:

/gold/customer360/
/gold/monthly_revenue/
/gold/risk_analysis/
/gold/executive_reporting/

The Gold layer can then feed:

Power BI
Azure SQL
APIs
Databricks SQL
Machine learning
AI/RAG

This Bronze → Silver → Gold medallion structure is a common Azure lakehouse design; Microsoft’s architecture guidance also shows ADF ingestion, Databricks processing and Azure SQL as a downstream serving store. (Microsoft Learn)


4. Where Cosmos DB fits

This distinction matters in interviews:

Cosmos DB should generally not become your primary enterprise data lake.

Use Cosmos DB for:

High transaction volume
+
Low-latency access
+
Semi-structured JSON
+
Global distribution
+
Application operational data

Example:

{
"customerId": "C10001",
"eventType": "purchase",
"productId": "P889",
"amount": 142.75,
"timestamp": "2026-08-11T18:05:03"
}

An application might use:

Web/Mobile App
|
v
API
|
v
Cosmos DB
latency: milliseconds

Analytics should normally be separated:

        OLTP                        Analytics

Application
     |
     v
 Cosmos DB
     |
     +---------------------> Analytics platform
                              |
                              v
                         ADLS/Lakehouse

That prevents heavy analytical queries from competing with production application traffic.


5. Cosmos DB analytics: an important current change

This is worth knowing for a 2026 interview.

Historically, you would often hear:

Cosmos DB
|
Analytical Store
|
Synapse Link
|
Synapse Analytics

But Microsoft now states that Azure Synapse Link for Cosmos DB isn’t supported for new projects and recommends Azure Cosmos DB Mirroring for Microsoft Fabric for new implementations. (Microsoft Learn)

So for a new Fabric-centric design:

Cosmos DB
|
| zero-ETL mirroring
v
Microsoft Fabric
|
OneLake
|
Analytics / Power BI

For an existing estate that already uses Synapse Link, analytical store can still separate analytical access from the transactional workload and provide near-real-time analytical data without traditional ETL. (Microsoft Learn)

That’s a very useful distinction to make in an interview because many older architecture diagrams still recommend Synapse Link as the default for new solutions.


6. Where Azure SQL fits

I would use Azure SQL primarily for structured relational workloads and analytical serving, rather than storing petabytes of raw lake data.

For example:

                   ADLS Gold
                      |
                  Databricks
                      |
                      v
                  Azure SQL
                 Serving DB
                      |
             +--------+--------+
             |                 |
          Power BI            APIs

Azure SQL could contain:

DimCustomer
DimProduct
DimDate
FactSales
FactOrders
FactRevenue

Conceptually:

              FactSales
                  |
          +-------+-------+
          |       |       |
     Customer  Product   Date

That structure works well when consumers expect SQL semantics, relational joins, predictable schemas and conventional BI integration.

Microsoft’s medallion reference architecture explicitly describes Azure SQL as a persisted downstream serving store after ADF ingestion and Databricks processing. (Microsoft Learn)


7. Don’t query operational SQL for every report

This is an important architecture rule.

Avoid:

Power BI
|
v
Production Azure SQL

when complex BI queries could impact application traffic.

Instead:

Production SQL
|
| CDC / incremental ingestion
v
ADF
|
v
ADLS Bronze
|
Databricks
|
ADLS Gold
|
+----> BI serving layer
|
+----> Power BI

This creates separation between:

OLTP

many small
INSERT/UPDATE/SELECT

and OLAP

SELECT SUM(...)
FROM billions_of_rows
GROUP BY ...

8. File format optimization

For analytics, the storage format matters considerably.

I would prefer:

RAW
CSV / JSON / original source
|
v
Bronze
transform
Silver / Gold
Parquet / Delta

Why?

Parquet is column-oriented.

Imagine:

customer_id
name
country
age
salary
transaction_total

If a query needs only:

SELECT country, SUM(transaction_total)
FROM sales
GROUP BY country;

a columnar format can avoid unnecessarily reading all unrelated columns.

ADLS performance guidance emphasizes that file format, file size and directory structure affect both performance and cost, and recommends parallelizing ingestion and access. (Microsoft Learn)


9. Partitioning

Don’t create:

/data/everything.parquet

and don’t create millions of tiny files either.

Use useful business/query partitions:

/sales/
year=2026/
month=08/
day=11/
part-00001.parquet
part-00002.parquet

Then:

WHERE year = 2026
AND month = 8

allows the processing engine to avoid scanning irrelevant partitions.

Possible partition keys:

date
region
source_system
business_domain

But avoid partitioning on very high-cardinality fields such as:

customer_id
transaction_id
GUID

because that can create a huge number of directories/files.


10. Handle the small-file problem

An enterprise ingestion system can easily produce:

10 million × 10 KB JSON files

which is inefficient for analytical processing.

I’d periodically compact them:

10,000 tiny files
Databricks
10–100 appropriately sized
Parquet / Delta files

Microsoft’s ADLS guidance notes that larger files generally provide better analytical performance and lower transaction overhead compared with large numbers of tiny files. (Microsoft Learn)


11. Data flow between the three storage systems

A good architecture might look like this:

                  OPERATIONAL SYSTEMS

              ┌──────────────────┐
              │    Cosmos DB     │
              │ JSON/events      │
              └────────┬─────────┘
                       |
                       |
┌─────────────┐        |         ┌─────────────┐
│ Azure SQL   │        |         │ APIs/Files  │
│ OLTP        │        |         │ SaaS        │
└──────┬──────┘        |         └──────┬──────┘
       |               |                |
       +---------------+----------------+
                       |
                ADF / Streaming
                       |
                       v
             ┌─────────────────────┐
             │ Azure Blob/ADLS G2  │
             │                     │
             │ BRONZE              │
             │ SILVER              │
             │ GOLD                │
             └──────────┬──────────┘
                        |
                   Databricks
                 Spark / Delta
                        |
           +------------+------------+
           |                         |
           v                         v
      Azure SQL                  AI / ML
   analytical serving            Models
           |
           v
        Power BI

12. Security architecture

For an enterprise deployment:

                Azure Landing Zone
                       |
                     VNet
                       |
              Private Endpoints
                       |
       +---------------+---------------+
       |               |               |
      ADLS          Cosmos DB       Azure SQL

I would add:

Microsoft Entra ID
|
Managed Identity
|
Azure RBAC

and:

Key Vault
|
CMK/secrets/certificates

Plus:

  • disable public network access where appropriate;
  • use private endpoints;
  • use managed identities rather than embedded account keys;
  • encrypt data at rest and in transit;
  • apply least-privilege RBAC;
  • classify sensitive datasets;
  • maintain audit logs and lineage.

13. Analytics performance design

My high-level optimization rules would be:

LayerOptimization
ADLSParquet/Delta
ADLSsensible partitioning
ADLSavoid tiny files
Databricksdistributed Spark
Cosmos DBgood partition key
Cosmos DBisolate operational from analytical workloads
SQLindexes appropriate to serving queries
SQLdimensional/star schemas where appropriate
Pipelinesincremental loads
Pipelinesparallel ingestion
Goldpre-aggregated datasets

Microsoft specifically recommends maximizing parallel reads/writes for ADLS analytical workloads rather than serializing large ingestion jobs. (Microsoft Learn)


Interview architecture to memorize

                SOURCES
                   |
        +----------+----------+
        |                     |
   Azure SQL              Cosmos DB
     OLTP                Operational JSON
        |                     |
        +----------+----------+
                   |
              ADF / Events
                   |
                   v
              ADLS Gen2
           ┌─────────────┐
           │ BRONZE      │
           │ raw         │
           └──────┬──────┘
                  ↓
             Databricks
                  ↓
           ┌─────────────┐
           │ SILVER      │
           │ clean       │
           └──────┬──────┘
                  ↓
             Databricks
                  ↓
           ┌─────────────┐
           │ GOLD        │
           │ curated     │
           └──────┬──────┘
                  |
          +-------+-------+
          |               |
      Azure SQL          AI/ML
       Serving
          |
       Power BI
90-second interview answer

“I would use a polyglot storage architecture. Azure Blob Storage with ADLS Gen2 would be the central analytical data lake because it provides low-cost scalable storage for structured, semi-structured and unstructured data.

I would organize it using Bronze, Silver and Gold zones. Raw data from Azure SQL, Cosmos DB, APIs and other systems lands unchanged in Bronze. Databricks then performs schema enforcement, quality checks, deduplication and transformations, storing Silver and Gold datasets primarily in Delta or Parquet formats.

Cosmos DB remains the operational store for low-latency, high-volume JSON workloads rather than becoming the primary data lake. Azure SQL handles relational transactional workloads and can also provide a curated SQL serving layer for BI applications.

For performance, I’d use incremental ingestion, parallel processing, appropriate partitioning, columnar storage and file compaction. From a security perspective, all three platforms would use Entra ID, managed identities, RBAC and private endpoints.

Finally, I keep operational and analytical workloads isolated so large BI or ML queries never affect production databases.”

One particularly good 2026 interview point is to mention that although Cosmos DB Synapse Link appears in many older architectures, Microsoft now recommends Cosmos DB Mirroring for Microsoft Fabric for new projects rather than starting a new Synapse Link implementation. (Microsoft Learn)