MAS 9 on OpenShift: High-Availability Deployment Patterns and Production Sizing
>
MAS 9 on OpenShift: High-Availability Deployment Patterns and Production Sizing
Deploying Maximo Application Suite 9 on OpenShift is not the same as deploying it. The difference is the gap between a cluster that runs and a cluster that survives. Most organizations discover this gap during their first production incident, when a node failure takes down Manage, or a storage misconfiguration corrupts the database, or a network partition splits the cluster in ways the documentation never mentioned.
This article covers the deployment patterns, sizing decisions, and operational practices that separate a lab deployment from a production-grade MAS 9 platform. It assumes you have already read the IBM documentation and understand the basics of OpenShift. What follows is the layer of knowledge that comes from running MAS in production.
Cluster Topology: Beyond the Minimum
IBM publishes minimum sizing requirements for MAS 9. Those minimums are accurate for functional testing. They are not sufficient for production. A production MAS cluster needs to account for node failures, workload spikes, maintenance windows, and the operational overhead of running OpenShift itself.
The Three-Zone Pattern
The foundational pattern for production MAS deployments is the three-zone topology. Each zone is a failure domain: a separate physical host, rack, or availability zone in a cloud provider. OpenShift distributes control plane nodes and worker nodes across all three zones so that the loss of any single zone does not take down the cluster.
Zone A Zone B Zone C
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ cp-node-1 │ │ cp-node-2 │ │ cp-node-3 │
│ (control │ │ (control │ │ (control │
│ plane) │ │ plane) │ │ plane) │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ worker-mas-1 │ │ worker-mas-2 │ │ worker-mas-3 │
│ (MAS apps) │ │ (MAS apps) │ │ (MAS apps) │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ worker-db-1 │ │ worker-db-2 │ │ worker-db-3 │
│ (databases) │ │ (databases) │ │ (databases) │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ worker-infra │ │ worker-infra │ │ worker-infra │
│ (monitoring, │ │ (monitoring, │ │ (monitoring, │
│ registry) │ │ registry) │ │ registry) │
└──────────────┘ └──────────────┘ └──────────────┘
This topology requires a minimum of 12 worker nodes (4 per zone) plus 3 control plane nodes. Each node type has different resource requirements:
| Node Role | vCPU | RAM | Storage | Count | Notes |
|---|---|---|---|---|---|
| Control Plane | 4 | 16 GB | 120 GB | 3 | etcd is I/O sensitive; use SSD |
| MAS Worker | 16 | 64 GB | 200 GB | 3-6 | Scale based on concurrent users |
| Database Worker | 8 | 32 GB | 500 GB | 3 | Use local SSD or high-IOPS block storage |
| Infrastructure Worker | 8 | 32 GB | 200 GB | 3 | Monitoring, logging, image registry |
Node Affinity and Tolerations
MAS components must be scheduled onto the correct node types. This is enforced through OpenShift node labels and pod affinity rules. The MAS operator handles most of this automatically, but you need to label your nodes correctly before installation:
# Label worker nodes for MAS component placement
# Run on each worker node after cluster provisioning
# Label MAS application nodes
oc label node worker-mas-1 node-role.kubernetes.io/mas=""
oc label node worker-mas-2 node-role.kubernetes.io/mas=""
oc label node worker-mas-3 node-role.kubernetes.io/mas=""
# Label database nodes
oc label node worker-db-1 node-role.kubernetes.io/mas-db=""
oc label node worker-db-2 node-role.kubernetes.io/mas-db=""
oc label node worker-db-3 node-role.kubernetes.io/mas-db=""
# Label infrastructure nodes
oc label node worker-infra-1 node-role.kubernetes.io/mas-infra=""
oc label node worker-infra-2 node-role.kubernetes.io/mas-infra=""
oc label node worker-infra-3 node-role.kubernetes.io/mas-infra=""
The MAS operator uses these labels to place pods. If you skip this step, pods will be scheduled randomly, and you will discover the problem when a database pod lands on a node with insufficient I/O and your work order queries take 30 seconds.
Pod Anti-Affinity for HA
For true high availability, you need pod anti-affinity rules that prevent multiple replicas of the same component from landing on the same node. The MAS operator configures these by default for critical components, but you should verify:
# Example: Pod anti-affinity for Manage pods
# This is configured by the MAS operator, shown here for understanding
apiVersion: apps/v1
kind: Deployment
metadata:
name: manage-deployment
spec:
replicas: 3
template:
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- manage
topologyKey: kubernetes.io/hostname
The requiredDuringSchedulingIgnoredDuringExecution rule is strict: it prevents two Manage pods from ever sharing a node. If you have 3 Manage replicas, you need at least 3 MAS worker nodes. If a node fails, the surviving pods continue serving traffic while OpenShift reschedules the lost pod onto a remaining node.
Storage Architecture
Storage is where most MAS deployments fail in production. The default storage configuration works for demos. It does not work for production workloads where database performance directly impacts user experience.
Database Storage: Db2 and MongoDB
MAS 9 uses Db2 for transactional data (work orders, assets, PMs) and MongoDB for document-oriented data (reports, attachments, configuration). Both databases need high-performance storage, but their requirements differ:
| Database | Workload Type | IOPS Requirement | Latency Target | Storage Type |
|---|---|---|---|---|
| Db2 | Random read/write, OLTP | 3,000+ IOPS | <5ms | Local SSD or io2 Block Express |
| MongoDB | Sequential write, document store | 1,000+ IOPS | <10ms | SSD-backed persistent volume |
| Kafka | Sequential write, append-only | 2,000+ IOPS | <5ms | Local SSD |
| Elasticsearch | Random read, search indexing | 1,500+ IOPS | <10ms | SSD-backed persistent volume |
For on-premises deployments, use local NVMe SSDs with the Local Storage Operator. For cloud deployments, use the highest-IOPS block storage tier available. Do not use NFS for any database workload. NFS introduces latency that compounds with every database query, and the result is a system that feels slow even when it is not under load.
# StorageClass for Db2 on AWS with io2 Block Express
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: mas-db2-high-iops
provisioner: ebs.csi.aws.com
parameters:
type: io2
iopsPerGB: "100"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
Backup and Disaster Recovery
A production MAS deployment needs a backup strategy that covers three distinct data domains:
- Database backups: Db2 and MongoDB need regular full and incremental backups. Use Db2's native backup with log archiving for point-in-time recovery. MongoDB backups should use
mongodumpwith oplog capture. - Persistent volume snapshots: Use OpenShift's CSI snapshot capability to take point-in-time snapshots of all persistent volumes. This captures the full state of the cluster at a moment in time.
- Configuration backups: MAS suite configuration, including user registry settings, application configurations, and customizations, should be exported and stored separately.
# Example: Db2 backup script for MAS 9
#!/bin/bash
# Run as a CronJob in OpenShift
BACKUP_DIR="/mnt/backups/db2/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# Full online backup
db2 "BACKUP DATABASE maximo ONLINE TO $BACKUP_DIR \
COMPRESS INCLUDE LOGS"
# Archive logs for point-in-time recovery
db2 "ARCHIVE LOG FOR DATABASE maximo"
# Verify backup
db2 "RESTORE DATABASE maximo FROM $BACKUP_DIR \
TAKEN AT $(ls $BACKUP_DIR | head -1) \
REDIRECT GENERATE SCRIPT verify.sql"
echo "Backup complete: $BACKUP_DIR"
The recovery time objective (RTO) and recovery point objective (RPO) for your MAS deployment should be documented and tested. A backup that has never been restored is not a backup. It is a wish.
Sizing for Concurrent Users
The most common sizing question is "how many users can this cluster support?" The answer depends on workload characteristics, not just user count. A user running a complex BIRT report consumes more resources than a user creating a work order. A user running an automation script that queries 50,000 records consumes more than a user viewing a single asset.
Workload Classification
MAS workloads fall into three categories:
| Workload Type | Description | Resource Profile | Example |
|---|---|---|---|
| Light | Simple CRUD, single-record operations | Low CPU, low memory | Creating work orders, viewing assets |
| Medium | List views, simple queries, small reports | Medium CPU, medium memory | Work order tracking, assignment manager |
| Heavy | Complex reports, bulk operations, integrations | High CPU, high memory | BIRT reports, MIF bulk loads, automation scripts |
A production cluster should be sized for the peak concurrent workload, not the average. The formula is:
Required Pods = (Light Users × 0.1) + (Medium Users × 0.3) + (Heavy Users × 1.0)
For example, an organization with 200 concurrent users where 150 are light, 40 are medium, and 10 are heavy:
Required Pods = (150 × 0.1) + (40 × 0.3) + (10 × 1.0) = 15 + 12 + 10 = 37
This means you need approximately 37 Manage pods to handle peak load. With 3 pods per node (a reasonable density), that requires 13 MAS worker nodes. Add 3 for redundancy, and you are at 16 MAS worker nodes.
Horizontal Pod Autoscaling
MAS 9 supports horizontal pod autoscaling (HPA) based on CPU and memory metrics. Configure HPA to handle load spikes without manual intervention:
# HorizontalPodAutoscaler for Manage
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: manage-hpa
namespace: mas-manage
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: manage-deployment
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 30
The scale-down stabilization window of 300 seconds prevents thrashing. Without it, a brief drop in load causes pods to terminate, only to be recreated moments later when load returns.
Networking and Ingress
MAS 9 exposes services through OpenShift Routes, which are backed by the cluster's ingress controller. For production, you need to configure TLS termination, network policies, and potentially an external load balancer.
TLS Configuration
MAS 9 requires TLS for all external communication. The recommended approach is to terminate TLS at the OpenShift ingress controller and use re-encryption for internal traffic:
Client ──TLS──▶ Ingress Controller ──TLS──▶ MAS Service
(certificate A) (certificate B)
Certificate A is your organization's public certificate, typically issued by a corporate CA or a public CA like Let's Encrypt. Certificate B is the MAS internal certificate, managed by the MAS operator.
# Route configuration for MAS with TLS re-encryption
apiVersion: route.openshift.io/v1
kind: Route
metadata:
name: mas-route
namespace: mas-core
spec:
host: mas.example.com
to:
kind: Service
name: mas-core-service
port:
targetPort: https
tls:
termination: reencrypt
certificate: |
-----BEGIN CERTIFICATE-----
# Your organization's TLS certificate
-----END CERTIFICATE-----
key: |
-----BEGIN PRIVATE KEY-----
# Your organization's private key
-----END PRIVATE KEY-----
caCertificate: |
-----BEGIN CERTIFICATE-----
# CA certificate chain
-----END CERTIFICATE-----
insecureEdgeTerminationPolicy: Redirect
Network Policies
By default, OpenShift allows all pod-to-pod communication within the cluster. For production, you should restrict this with network policies that enforce least-privilege communication:
# NetworkPolicy: Allow only MAS components to talk to Db2
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db2-ingress
namespace: mas-data
spec:
podSelector:
matchLabels:
app: db2
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
mas-component: "true"
ports:
- protocol: TCP
port: 50000
Operational Considerations
Monitoring and Alerting
MAS 9 integrates with Prometheus and Grafana for observability. At minimum, you should configure alerts for:
- Pod restarts exceeding 3 in 15 minutes
- Node CPU or memory utilization exceeding 85%
- Database connection pool exhaustion
- Persistent volume utilization exceeding 80%
- Certificate expiration within 30 days
# PrometheusRule: Alert on high pod restart rate
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: mas-pod-restarts
namespace: mas-core
spec:
groups:
- name: mas-alerts
rules:
- alert: HighPodRestartRate
expr: rate(kube_pod_container_status_restarts_total{namespace=~"mas-.*"}[15m]) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "High pod restart rate in {{ $labels.namespace }}"
description: "Pod {{ $labels.pod }} has restarted {{ $value }} times in the last 15 minutes."
Upgrade Strategy
MAS 9 supports rolling upgrades for most components. The upgrade process follows this sequence:
- Upgrade the MAS operator
- Upgrade the suite administration plane
- Upgrade individual applications (Manage, Monitor, Health, etc.)
- Upgrade the data layer (if required)
Each step can be performed independently, which means you can upgrade Manage without touching Monitor, or upgrade the operator without affecting running applications. This is a significant improvement over the monolithic upgrade model of Maximo 7.6.
# Upgrade MAS operator via CLIibm-mas
# 1. Check current operator version
oc get csv -n openshift-operators | grep # 2. Approve the new operator versionibm-mas
oc patch installplan $(oc get installplan -n openshift-operators \
-o json | jq -r '.items[] | select(.spec.clusterServiceVersionNames[] | startswith("ibm-mas")) | .metadata.name') \
-n openshift-operators \
--type merge \
--patch '{"spec":{"approved":true}}'
# 3. Verify operator upgrade
oc get csv -n openshift-operators | grep
Practical Implications
The decisions you make during initial deployment determine your operational experience for years. Here are the implications of the patterns described above:
If you deploy with the minimum 3 worker nodes, you will have no redundancy. A single node failure takes down part of your MAS deployment. You will also have no room for maintenance: draining a node for updates means losing capacity.
If you skip node labeling, pods will be scheduled randomly. Your database may end up on a node with insufficient I/O, causing performance problems that are difficult to diagnose because they are intermittent and depend on pod placement.
If you use NFS for database storage, you will experience latency spikes under load. Database engines are designed for direct-attached or block storage. NFS adds a network round-trip to every I/O operation, and the cumulative effect on a busy database is severe.
If you do not configure HPA, you will either over-provision (wasting resources) or under-provision (causing performance degradation during peak usage). HPA allows the cluster to adapt to actual workload patterns.
If you do not test your backups, you do not have backups. You have files that you hope will work. The time to discover that a backup is corrupt is not during a disaster recovery exercise. It is during a scheduled test restore.
Bottom Line
A production-grade MAS 9 deployment on OpenShift requires 15-20 worker nodes across three failure domains, high-performance storage for databases, proper node labeling and pod anti-affinity, TLS with re-encryption, network policies, monitoring and alerting, and a tested backup and disaster recovery strategy.
The minimum sizing in the IBM documentation is for functional validation, not production. Plan your cluster for peak load plus one node failure per zone. Test your backups. Label your nodes. Configure HPA. These are not optional steps. They are the difference between a platform that runs Maximo and a platform that keeps Maximo running when things go wrong.
The investment in proper deployment architecture pays for itself the first time a node fails at 2 AM and nobody gets paged because the cluster healed itself.