This project demonstrates how to deploy an IBM® Operational Decision Manager (ODM) clustered topology using the container-native load balancer of GKE.
The ODM services will be exposed using the Ingress provided by the ODM on Kubernetes Helm chart. This deployment implements Kubernetes and Docker technologies. Here is the Google Cloud home page: https://cloud.google.com
The ODM on Kubernetes Docker images are available in the IBM Entitled Registry. The ODM Helm chart is available in the IBM Helm charts repository.
Important
Deployment Options:
There are three ways to expose ODM services on GKE:
-
GKE Ingress (Default - Documented in this README): Uses the GKE Ingress controller with container-native load balancing. This is the standard approach documented in the steps below.
-
GKE Gateway API (Recommended for Advanced Features): Uses the GKE Gateway API which provides more advanced routing capabilities, better session affinity management, and is the future direction for Kubernetes networking. See the GKE Gateway API deployment guide.
-
NGINX Ingress Controller (Deprecated): The NGINX Ingress Controller deployment guide is deprecated and will be removed in the coming months. Please use GKE Ingress or GKE Gateway API instead.
The project comes with the following components:
- IBM Operational Decision Manager
- Google Kubernetes Engine (GKE)
- Google Cloud SQL for PostgreSQL
- IBM License Service
The commands and tools have been tested on macOS and Linux.
First, install the following software on your machine:
Then, perform the following tasks:
-
Create a Google Cloud account by connecting to the Google Cloud Platform console. When prompted to sign in, create a new account by clicking Create account.
Without the relevant billing level, some Google Cloud resources will not be created.
Note
Prerequisites and software supported by ODM 9.6.0 are listed on the Detailed System Requirements page.
- Prepare your GKE instance 30 min
- Create the Google Cloud SQL PostgreSQL instance 10 min
- Prepare your environment for the ODM installation 10 min
- Manage a digital certificate 2 min
- Install the ODM release 10 min
- Access ODM services
- Track ODM usage
Refer to the GKE quickstart for more information.
After installing the gcloud tool, use the following command line:
gcloud auth loginThere are several types of clusters.
In this article, we chose to create a regional cluster.
Regions and zones (used below) can be listed respectively with gcloud compute regions list and gcloud compute zones list.
-
Set the project (associated to a billing account):
gcloud config set project <PROJECT_ID>
-
Set the region:
gcloud config set compute/region <REGION (ex: europe-west9)>
-
Set the zone:
gcloud config set compute/zone <ZONE (ex: europe-west9-b)>
-
Create a cluster and enable autoscaling. Here, we start with 6 nodes (16 max):
gcloud container clusters create <CLUSTER_NAME> \ --release-channel=regular --cluster-version=1.34 \ --enable-autoscaling --num-nodes=6 --total-min-nodes=1 --total-max-nodes=16
Note
You can also create your cluster from the Google Cloud Platform using the Kubernetes Engine > Clusters panel and clicking the Create button

-
Create a kubeconfig to connect to your cluster:
gcloud container clusters get-credentials <CLUSTER_NAME>
Note
You can also retrieve the command line to configure kubectl from the Google Cloud Console using the Kubernetes Engine > Clusters panel and clicking Connect on the dedicated cluster.

-
Check your environment
If your environment is set up correctly, you should be able to get the cluster information by running the following command:
kubectl cluster-info
We will use the Google Cloud Platform console to create the database instance.
- Go to the SQL context, and then click the CREATE INSTANCE button
- Click Choose PostgreSQL
- Database version:
PostgreSQL 18 - Instance ID:
<YourInstanceName> - Password:
<PASSWORD>- Take note of this password. - Region:
<REGION>(must be the same as the cluster for the communication to be optimal between the database and the ODM instance) - Eventually select Multiple zones for Zonal availability for redundancy
- Expand Show customization option and expand Connections
- As Public IP is selected by default, in Authorized networks, click the ADD NETWORK button, put a name and add 0.0.0.0/0 for Network, then click DONE.
NOTE: It is not recommended to use a public IP. In a production environment, you should use a private IP.
- As Public IP is selected by default, in Authorized networks, click the ADD NETWORK button, put a name and add 0.0.0.0/0 for Network, then click DONE.
- Database version:
- Click CREATE INSTANCE
After the database instance is created, you can drill on the SQL instance overview to retrieve needed information to connect to this instance, like the IP address and the connection name. Take note of the Public IP address.
To secure access to the database, you must create a secret that encrypts the database user and password before you install the Helm release.
kubectl create secret generic odmdbsecret \
--from-literal=db-user=postgres \
--from-literal=db-password=<PASSWORD>Where:
<PASSWORD>is the database password (PASSWORD set during the PostgreSQL instance creation above)
To get access to the ODM material, you need an IBM entitlement key to pull the images from the IBM Entitled Registry.
-
Log in to MyIBM Container Software Library with the IBMid and password that are associated with the entitled software.
-
In the Container software library tile, verify your entitlement on the View library page, and then go to Get entitlement key to retrieve the key.
kubectl create secret docker-registry ibm-entitlement-key \
--docker-server=cp.icr.io \
--docker-username=cp \
--docker-password='<API_KEY_GENERATED>'Where <API_KEY_GENERATED> is the entitlement key from the previous step. Make sure you enclose the key in quotes.
Note:
- The cp.icr.io value for the docker-server parameter is the only registry domain name that contains the images. You must set the docker-username to cp to use an entitlement key as docker-password.
- The
ibm-entitlement-keysecret name will be used for theimage.pullSecretsparameter when you run a Helm install of your containers. Theimage.repositoryparameter is also set by default tocp.icr.io/cp/cp4a/odm.
helm repo add ibm-helm https://raw.githubusercontent.com/IBM/charts/master/repo/ibm-helm
helm repo updatehelm search repo ibm-odm-prod
NAME CHART VERSION APP VERSION DESCRIPTION
ibm-helm/ibm-odm-prod 26.0.0 9.6.0.0 IBM Operational Decision ManagerIn this step, you will generate a certificate to be used by the GKE load balancer.
If you do not have a trusted certificate, you can use OpenSSL and other cryptography and certificate management libraries to generate a certificate file and a private key to define the domain name and to set the expiration date. The following command creates a self-signed certificate (.crt file) and a private key (.key file) that accept the domain name mynicecompany.com. The expiration is set to 1000 days:
openssl req -x509 -nodes -days 1000 -newkey rsa:2048 -keyout mynicecompany.key \
-out mynicecompany.crt -subj "/CN=mynicecompany.com/OU=it/O=mynicecompany/L=Paris/C=FR"kubectl create secret tls mynicecompany-tls-secret --key mynicecompany.key --cert mynicecompany.crtThe certificate must be the same as the one you used to enable TLS connections in your ODM release. For more information, see Server certificates and Working with certificates and SSL.
The ODM services will be exposed with an Ingress that uses the previously created mynicecompany certificate.
It automatically creates an HTTPS GKE load balancer. We will disable the ODM internal TLS as it is not needed.
-
Get the gcp-values.yaml file and replace the following key:
<DB_ENDPOINT>: the database IP
Note
You can configure the driversUrl parameter to point to the appropriate version of the Google Cloud SQL PostgreSQL driver. For more information, refer to the Cloud SQL Connector for Java documentation.
-
Install the chart from IBM's public Helm charts repository:
helm install <release> ibm-helm/ibm-odm-prod -f gcp-values.yaml
Run the following command to check the status of the pods that have been created:
kubectl get pods
NAME READY STATUS RESTARTS AGE
<release>-odm-decisioncenter-*** 1/1 Running 0 20m
<release>-odm-decisionrunner-*** 1/1 Running 0 20m
<release>-odm-decisionserverconsole-*** 1/1 Running 0 20m
<release>-odm-decisionserverruntime-*** 1/1 Running 0 20mTo get the status of the current deployment, go to the Kubernetes Engine / Services & Ingress Panel in the console.
The Ingress remains in the state Creating ingress for several minutes until the pods are up and running, and the backend gets in a healthy state.
You can also check the load balancer status. It provides information about the backend using the service health check.
In the Ingress details, you should get a HEALTHY state on all backends. This panel also provides some logs on the load balancer activity. When the Ingress shows an OK status, all ODM services can be accessed.
Sticky session is needed for Decision Center. The browser contains a cookie which identifies the user session that is linked to a unique container. The ODM on Kubernetes Helm chart has a clientIP for the Decision Center session affinity. Unfortunately, GKE does not use it automatically. You will not encounter any issue until you scale up the Decision Center deployment.
A configuration that uses BackendConfig is needed to manage session affinity at the load balancer level.
-
Create the Decision Center Backend Config:
kubectl create -f decisioncenter-backendconfig.yaml
-
Annotate the Decision Center Service with this GKE Backend Config:
kubectl annotate service <release>-odm-decisioncenter \ cloud.google.com/backend-config='{"ports": {"80":"dc-backendconfig"}}'
As soon as GKE manages Decision Center session affinity at the load balancer level, you can check the ClientIP availability below the Decision Center Network Endpoint Group configuration from the Google Cloud Console in the Load Balancer details.
In a real enterprise use case, to access the mynicecompany.com domain name, you have to deal with Google Managed Certificate and Google Cloud DNS.
In this trial, we use a self-signed certificate. So, there is no extra charge like certificate and domain purchase. We only have to manage a configuration to simulate the mynicecompany.com access.
-
Get the EXTERNAL-IP with the command line:
kubectl get ingress <release>-odm-ingress -o jsonpath='{.status.loadBalancer.ingress[].ip}'
-
Edit your /etc/hosts file and add the following entry:
<EXTERNAL-IP> mynicecompany.com
-
You can now access all ODM services with the following URLs:
| SERVICE NAME | URL | USERNAME/PASSWORD |
|---|---|---|
| Decision Server Console | https://mynicecompany.com/res | odmAdmin/odmAdmin |
| Decision Center | https://mynicecompany.com/decisioncenter | odmAdmin/odmAdmin |
| Decision Center REST-API | https://mynicecompany.com/decisioncenter-api | odmAdmin/odmAdmin |
| Decision Server Runtime | https://mynicecompany.com/DecisionService | odmAdmin/odmAdmin |
| Decision Runner | https://mynicecompany.com/DecisionRunner | odmAdmin/odmAdmin |
Note
You can also click the Ingress frontends accessible from the Google Cloud console under the Kubernetes Engine/Services & Ingress Details Panel.

The IBM Usage Metering Service (UMS) is a critical component that gathers metrics to monitor compliance and create reports. It captures business value metrics for auditing purposes, visualizes metric usage in reporting tools, and sends the information to IBM Software Central.
Prerequisites:
- Cluster-admin permissions or appropriate RBAC roles
- Target namespace must exist before installation
- ODM 9.6.0 or later installed
Important Requirements:
- From ODM 9.6.0 onwards, UMS must be installed in the same namespace as ODM
- ODM reports usage metrics to UMS through a scheduled CronJob
- If UMS is not installed, the CronJob will fail when it runs
Installation:
For detailed installation and configuration instructions, see Installing the usage metering service.
Troubleshooting:
If the CronJob fails, check the pod logs:
kubectl logs -n <namespace> -l job-name=<cronjob-name>Configuration Options:
After installing UMS, choose one of the following configuration modes based on your environment:
- Online Mode (Recommended): Automatic data transmission to IBM Software Central
- Offline Mode (Air-gapped): Manual data download and upload process
In online mode, the Usage Metering Service automatically sends usage data to IBM Software Central on a scheduled basis. This is the recommended configuration for environments with internet connectivity.
Key Features:
- Automatic data transmission every 24 hours
- No manual intervention required after initial setup
- Automatic retry on transmission failures
Configuration Requirements:
- IBM Entitlement Key (required for authentication)
- Network connectivity to IBM Software Central (
swc.saas.ibm.com)
For complete step-by-step instructions on configuring online mode, refer to:
📖 Automatic data transmission to IBM Software Central
This documentation covers:
- Creating the IBM Entitlement Key secret
- Configuring the IBMUsageMetering instance
- Verifying the configuration
- Monitoring data transmission
- Troubleshooting common issues
For offline/air-gapped environments where the Usage Metering Service cannot connect directly to IBM Software Central, you need to manually download and upload usage data.
Step 1: Expose the Usage Metering Service
Create a LoadBalancer service to expose UMS:
kubectl apply -f usage-metering-service-loadbalancer.yamlStep 2: Download Usage Data
Retrieve the metering service data from the LoadBalancer:
export NAMESPACE=<namespace>
EXTERNAL_IP=$(kubectl get service ibm-usage-metering-instance-loadbalancer -n "${NAMESPACE}" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
UMS_TOKEN=$(kubectl get secret ibm-usage-metering-upload-token -n "${NAMESPACE}" -o jsonpath='{.data.token}' 2>/dev/null | base64 -d || echo "")
curl -k --output "swc_payload.tar.gz" \
--header "Authorization: Bearer ${UMS_TOKEN}" \
--url "https://${EXTERNAL_IP}:8080/api/v1/swc"Step 3: Upload to IBM Software Central
After downloading the swc_payload.tar.gz file, you need to upload it to IBM Software Central to report your usage metrics. The upload process requires authentication with your IBM ID and must be performed from a machine with internet access.
For detailed instructions on how to upload the usage data file to IBM Software Central, including authentication steps and troubleshooting, refer to:
📖 Uploading usage metrics to IBM Software Central
Additional Resources:
For general information about collecting and sending usage metrics, see: 📖 Collecting and sending usage metrics
This section explains how to track ODM usage with the IBM License Service.
Follow the instructions in the Installation section of the Manual installation without the Operator Lifecycle Manager (OLM)
Wait a couple of minutes for the installation to be done.
You should see two pods running:
NAME READY STATUS RESTARTS AGE
ibm-licensing-operator-b8564f765-jsb95 1/1 Running 0 4m26s
ibm-licensing-service-instance-787996886d-pzmlg 1/1 Running 0 88sTo expose the licensing service using the GKE LoadBalancer, run the command:
kubectl patch svc ibm-licensing-service-instance -p '{"spec": { "type": "LoadBalancer"}}' -n ibm-licensingWait a couple of minutes for the changes to be applied. Then, you should see an EXTERNAL-IP available for the exposed licensing service.
kubectl get service -n ibm-licensing
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ibm-licensing-service-instance LoadBalancer 10.0.58.142 xxx.xxx.xxx.xxx 8080:32301/TCP 10mGet the licensing-instance.yaml file and run the command:
kubectl patch IBMLicensing instance --type merge --patch-file licensing-instance.yaml -n ibm-licensing Wait a couple of minutes for the changes to be applied.
You will be able to access the IBM License Service by retrieving the URL and the required token with this command:
export LICENSING_URL=$(kubectl get service ibm-licensing-service-instance -n ibm-licensing -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export TOKEN=$(kubectl get secret ibm-licensing-token -n ibm-licensing -o jsonpath='{.data.token}' |base64 -d)Note If
LICENSING_URLis empty, take a look at the troubleshooting page.
You can access the }http://${LICENSING_URL}:8080/status?token=${TOKEN URL to view the licensing usage or retrieve the licensing report .zip file by running:
curl -k "http://${LICENSING_URL}:8080/snapshot?token=${TOKEN}" --output report.zipIf your IBM License Service instance is not running properly, refer to this troubleshooting page.
For complete information about reporting license usage to IBM Software Central, refer to the official documentation:
📖 Reporting License Usage to IBM Software Central
For detailed steps on configuring online mode (automatic data transmission), including creating the IBM Entitlement Key secret, configuring the IBMLicensing Custom Resource, and verifying the setup, refer to the online mode documentation.
For air-gapped environments where ILS cannot directly connect to Software Central, download the usage data using the LoadBalancer-specific commands below:
export LICENSING_URL=$(kubectl get service ibm-licensing-service-instance -n ibm-licensing -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export TOKEN=$(kubectl get secret ibm-licensing-token -o jsonpath={.data.token} -n ibm-licensing | base64 -d)
curl --insecure --output "swc_payload.tar.gz" \
"http://${LICENSING_URL}:8080/swc_aggregations?token=${TOKEN}"For complete instructions on transferring and uploading the downloaded file to Software Central, refer to the offline mode documentation.
If your ODM instances are not running properly, refer to our dedicated troubleshooting page.
Get hands-on experience with IBM Operational Decision Manager in a container environment by following this Getting started tutorial.





