Interloc Solutions Blog

MAS deployment series: Installing the MAS AI broker for Work Order intelligence

Written by Julio Perera | Feb 19, 2025 10:45:03 PM

While the IBM Ansible scripting covers the installation of the AI broker, at the time of writing, there are several dependencies and caveats that are not installed by default and the documentation is insufficient to completely do a successful installation.  

Additionally, the script was developed with the “Open Data HubOperator version v2.11.1 in mind.  However, time has passed, and the actual version being installed at the time of writing is v2.23.0 which causes issues with the script execution. Even if we could work around these issues, installing version v2.23.0 causes incompatibility with some of the installed CRDs that make the installation fail later. The root of the issue appears to be around the inability of one of the IBM Ansible python functions to create Operator Subscriptions supporting the “spec.startingCSV” tag which allows to install a specific version instead of the latest. 

Also, there are important caveats around the plans to use on the WatsonX AI service as we found out when trying to train the model and after consulting with IBM support. Basically, if we use the “Lite” plan as stated on https://www.ibm.com/docs/en/masv-and-l/maximo-manage/continuous-delivery?topic=setup-create-watsonx-api-key then the model will refuse to train as a rate limit will be hit and cause errors. IBM support confirmed that the “Essentials” plan can be used and won’t have the issues that the “Lite” plan has.

First, we created a Project, Service ID, Deployment Space with a Plan (“Essentials” recommended, which is a paid plan), and setup the API Key as stated in the IBM link that appears in the previous paragraph. We are not documenting these steps in greater detail here as they worked without much tinkering although the navigation around the WatsonX.ai site was confusing at times. The output of this task should be the following settings that we store as Environment variables to be used by the IBM scripting:

export MAS_AIBROKER_WATSONXAI_APIKEY="<value-omitted>" ## The WatsonX       API key 
export MAS_AIBROKER_WATSONXAI_URL="https://us-south.ml.cloud.ibm.com"       ## The WatsonX API URL
export MAS_AIBROKER_WATSONXAI_PROJECT_ID="5aba7552-9c38-43c0-9e86-           9ea730f0c323" ## The WatsonX Project ID 

Notice that we chose to deploy our testing WatsonX instance in Dallas and the Endpoint URL reflects that choice. 

First, there are several dependencies that are required to be installed which are not covered in the IBM Ansible scripts, basically these are: 

A) A relational database (we are using a new MariaDB instance; but potentially another pre-existing instance could be used) 
B) An S3 (Object Storage) set of buckets (we are using a new Minio instance; but potentially another pre-existing S3 service could be used).

The above is meant to populate several Environment Variables used by the Ansible Galaxy collection (more on it later). 

Regarding the MariaDB and Minio instances, we intended to use specific instances of each for each MAS Core, so data and files don’t get mixed between instances. Therefore, we used project names containing our intended (testing) MAS Core Instance ID, in our case “p01-main”. Separate projects and installs could be used for different MAS Core Instances and the Project Names should follow suit, in our case: 

The MariaDB project/namespace was named “mas-p01-main-aibroker-mariadb”. 

The Minio project/namespace was named “mas-p01-main-aibroker-minio”. 

The actual AI broker project generated for the mentioned MAS Core Instance ID is going to be “mas-p01-main-aibroker” so project naming should be all related. 

Therefore, to accomplish that, we submitted the following YAMLs to the Cluster: 

For the MariaDB instance:

---
apiVersion: v1 

kind: Namespace 
metadata: 
  name: mas-p01-main-aibroker-mariadb 
--- 
apiVersion: v1
kind: PersistentVolumeClaim 
metadata: 
  name: mariadb-pvc 
  namespace: mas-p01-main-aibroker-mariadb 
  labels: 
    app: mariadb-instance 
    component: data-science-pipelines 
spec: 
  accessModes: 
    - ReadWriteOnce 
  storageClassName: ocs-storagecluster-cephfs 
  resources: 
    requests: 
      storage: 20Gi 
--- 
apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: ds-pipelines-mariadb-sa-instance 
  namespace: mas-p01-main-aibroker-mariadb 
  labels: 
    app: mariadb-instance 
    component: data-science-pipelines 
--- 
kind: NetworkPolicy 
apiVersion: networking.k8s.io/v1 
metadata: 
  name: mariadb-instance 
  namespace: mas-p01-main-aibroker-mariadb 
spec: 
  podSelector: 
    matchLabels: 
      app: mariadb-instance 
      component: data-science-pipelines 
  ingress: 
    - ports: 
        - protocol: TCP 
          port: 3306 
      from: 
        - podSelector: 
            matchLabels: 
              app.kubernetes.io/name: data-science-pipelines-operator 
          namespaceSelector: 
            matchLabels: 
              kubernetes.io/metadata.name: opendatahub 
        - podSelector: 
            matchLabels: 
              app: ds-pipeline-instance 
              component: data-science-pipelines 
          namespaceSelector: 
            matchLabels: 
              kubernetes.io/metadata.name: mas-p01-main-aibroker 
        - podSelector: 
            matchLabels: 
              app: ds-pipeline-metadata-grpc-instance 
              component: data-science-pipelines 
          namespaceSelector: 
            matchLabels: 
              kubernetes.io/metadata.name: mas-p01-main-aibroker 
  policyTypes: 
    - Ingress 
--- 
kind: Secret 
apiVersion: v1 
metadata: 
  name: mariadb-instance 
  namespace: mas-p01-main-aibroker-mariadb 
stringData: 
  password: <mariadb-password> 
type: Opaque 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: mariadb-instance 
  namespace: mas-p01-main-aibroker-mariadb 
  labels: 
    app: mariadb-instance 
    component: data-science-pipelines 
    dspa: instance 
spec: 
  strategy: 
    type: Recreate 
  selector: 
    matchLabels: 
      app: mariadb-instance 
      component: data-science-pipelines 
      dspa: instance 
  template: 
    metadata: 
      labels: 
        app: mariadb-instance 
        component: data-science-pipelines 
        dspa: instance 
    spec: 
      serviceAccountName: ds-pipelines-mariadb-sa-instance 
      containers: 
        - name: mariadb 
          image: registry.redhat.io/rhel8/mariadb-103:1-188 
          ports: 
            - containerPort: 3306 
          readinessProbe: 
            exec: 
              command: 
                - /bin/sh 
                - "-i" 
                - "-c" 
                - >- 
                  MYSQL_PWD=$MYSQL_PASSWORD mysql -h 127.0.0.1 -u $MYSQL_USER -D 
                  $MYSQL_DATABASE -e 'SELECT 1' 
            failureThreshold: 3 
            initialDelaySeconds: 5 
            periodSeconds: 10 
            successThreshold: 1 
            timeoutSeconds: 1 
          livenessProbe: 
            failureThreshold: 3 
            initialDelaySeconds: 30 
            periodSeconds: 10 
            successThreshold: 1 
            tcpSocket: 
              port: 3306 
            timeoutSeconds: 1 
          env: 
            - name: MYSQL_USER 
              value: "mariadb" 
            - name: MYSQL_PASSWORD 
              valueFrom: 
                secretKeyRef: 
                  key: password 
                  name: "mariadb-instance" 
            - name: MYSQL_DATABASE 
              value: "kmpipeline" 
            - name: MYSQL_ALLOW_EMPTY_PASSWORD 
              value: "true" 
          resources: 
            requests: 
              cpu: 300m 
              memory: 800Mi 
            limits: 
              cpu: "1" 
              memory: 1Gi 
          volumeMounts: 
            - name: mariadb-persistent-storage 
              mountPath: /var/lib/mysql 
      volumes: 
        - name: mariadb-persistent-storage 
          persistentVolumeClaim: 
            claimName: mariadb-pvc 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: mariadb-instance 
  namespace: mas-p01-main-aibroker-mariadb 
  labels: 
    app: mariadb-instance 
    component: data-science-pipelines 
spec: 
  ports: 
    - name: http 
      port: 3306 
      protocol: TCP 
      targetPort: 3306 
  selector: 
    app: mariadb-instance 
    component: data-science-pipelines

Notice that we have put some values above in bold, to signify that these will match and must follow the configuration for the target environment as mentioned. Also, the MariaDB password will have to be filled up with a value as well. 

The Storage Class could be any Storage Class that offers ReadWriteOnce capabilities. ReadWriteMany could be used as well although it is not needed. Size may need to be adjusted as well. 

As a result, we ended up with the following set of related environment variables that will make the input to the IBM Ansible script: 

export MAS_AIBROKER_DB_HOST="mariadb-instance.mas-p01-main-aibroker-                 mariadb.svc.cluster.local" # Take it from the Service on the project

export MAS_AIBROKER_DB_PORT="3306" # Take it from the Service on the
     project  

export MAS_AIBROKER_DB_USER="mariadb" # Take it from the environment     
     variables on the Deployment 

export MAS_AIBROKER_DB_DATABASE="kmpipeline" # Take it from the environment           variables on the Deployment 

export MAS_AIBROKER_DB_SECRET_NAME="mariadb-instance" # Take it from the             environment variables on the Deployment 

export MAS_AIBROKER_DB_SECRET_VALUE="<mariadb-password>" # Take it from the           Secret as specified above 

 

For the Minio instance:

--- 
apiVersion: v1 
kind: Namespace 
metadata: 
  name: mas-p01-main-aibroker-minio 
--- 
apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: minio-pvc 
  namespace: mas-p01-main-aibroker-minio 
spec: 
  accessModes: 
    - ReadWriteOnce 
  volumeMode: Filesystem 
  storageClassName: ocs-storagecluster-cephfs 
  resources: 
    requests: 
      storage: 40Gi 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: minio 
  namespace: mas-p01-main-aibroker-minio 
spec: 
  selector: 
    matchLabels: 
      app: minio 
  template: 
    metadata: 
      labels: 
        app: minio 
    spec: 
      volumes: 
        - name: storage 
          persistentVolumeClaim: 
            claimName: minio-pvc 
      containers: 
        - name: minio 
          image: quay.io/minio/minio:latest 
          command: 
            - /bin/bash 
            - -c 
          args: 
            - minio server /data --console-address :9090 
          env: 
            - name: MINIO_ROOT_USER 
              value: minio 
            - name: MINIO_ROOT_PASSWORD 
              value: <minio-password> 
          volumeMounts: 
            - mountPath: /data 
              name: storage 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: minio-service 
  namespace: mas-p01-main-aibroker-minio 
spec: 
  type: ClusterIP 
  ports: 
    - name: api 
      port: 9000 
      targetPort: 9000 
      protocol: TCP 
    - name: console 
      port: 9090 
      targetPort: 9090 
      protocol: TCP 
  selector: 
    app: minio 
--- 
kind: Route 
apiVersion: route.openshift.io/v1 
metadata: 
  name: minio-route 
  namespace: mas-p01-main-aibroker-minio 
spec: 
  to: 
    kind: Service 
    name: minio-service 
    weight: 100 
  port: 
    targetPort: 9090 
  tls: 
    termination: edge 
    insecureEdgeTerminationPolicy: None 

 

Notice that we have put some values above in bold, to signify that these will match and must follow the configuration for the target environment as mentioned. Also, the Minio password will have to be filled up with a value as well. 

The Storage Class could be any Storage Class that offers ReadWriteOnce capabilities. ReadWriteMany could be used as well although it is not needed. Size may need to be adjusted as well. 

As a result, we ended up with the following set of related environment variables that will make the input to the IBM Ansible script: 

export MAS_AIBROKER_STORAGE_ACCESSKEY="minio" # Take it from the environment         variables on the Deployment 
export MAS_AIBROKER_STORAGE_SECRETKEY="<minio-password>" # Take it from the           environment variables on the Deployment 
export MAS_AIBROKER_STORAGE_HOST="minio-service.mas-p01-main-aibroker-               minio.svc.cluster.local" # Take it from the Service 
export MAS_AIBROKER_STORAGE_PORT="9000" # Take it from the Service (the "api"         port) 
export MAS_AIBROKER_STORAGE_REGION="" # Leave blank 
export MAS_AIBROKER_STORAGE_PROVIDER="minio"  
export MAS_AIBROKER_STORAGE_SSL=false 
export MAS_AIBROKER_STORAGE_PIPELINES_BUCKET="km-pipelines" 
export MAS_AIBROKER_STORAGE_TENANTS_BUCKET="km-templates" 
export MAS_AIBROKER_STORAGE_TEMPLATES_BUCKET="km-tenants" 

Notice that Bucket names are recommended.  

Once submitted the Route, go to it and note the URL, in our case:

https://minio-route-mas-p01-main-aibroker-minio.apps.demo05.mas.interloc.cloud/

Then we should be able to login using the credentials in the Deployment above, in our case: 

Username: minio 
Password: <minio-password> 

Next, we went to Administrator -> Buckets and created the following buckets (using default options): 

km-pipelines 
km-templates 
km-tenants 

 
Ater going to the object browser, these buckets will appear with zero size. We are not sure if the Buckets will be created automatically if they don’t exist therefore, we resorted to create them manually. 

Next, we already had an instance of the MAS Ansible Galaxy collection installed (or a container could be used to deploy the latest image) and we had to update it and install a dependency that was not specified initially, in our case:

ansible-galaxy collection install --upgrade ibm.mas_devops 
python3 -m pip install boto3 

 
To execute the Ansible collection we need also to finish setting up some additional environment variables, in our case: 

export MAS_ENTITLEMENT_KEY="<ibm-entitlement-key>" # The IBM Entitlement
     key to access the IBM Container Registry, from                                   https://myibm.ibm.com/products-services/containerlibrary 
export MAS_INSTANCE_ID="p01-main" # Declare the instance ID for the AI Broker         install 

But before executing the Ansible collection, we needed to make some changes to it in order for it to work as intended. 

To work around the error “"namespaces "openshift-serverless" not found. We submitted the following YAMLs first:

--- 
apiVersion: v1 
kind: Namespace 
metadata: 
  name: openshift-serverless 
--- 
apiVersion: operators.coreos.com/v1 
kind: OperatorGroup 
metadata: 
  name: openshift-serverless-og 
  namespace: openshift-serverless 
spec: 
  upgradeStrategy: Default 

Then to avoid the errors around the Open Data Hub Operator mentioned above; we manually submitted the following Subscription:

apiVersion: operators.coreos.com/v1alpha1 
kind: Subscription   
  name: opendatahub-operator 
  namespace: openshift-operators 
spec: 
  channel: fast 
  installPlanApproval: Manual 
  name: opendatahub-operator 
  source: community-operators 
  sourceNamespace: openshift-marketplace 
  startingCSV: opendatahub-operator.v2.11.1 

Optionally approve the Install Plan for the Operator (as it is Manual otherwise it automatically upgrades to the latest version which will cause issues as mentioned above). 

Next, we proceeded to comment out the section that installs the Operator on the related IBM Ansible script, located in our case at:

~/.ansible/collections/ansible_collections/ibm/mas_devops/roles/odh/tasks/odh-operator.yml 

The change looked like the below: 

# Install Operator & create entitlement openshift-odh 
# ----------------------------------------------------------------------------- 
#- name: "Install Openshift odh Operator" 
#  ibm.mas_devops.apply_subscription: 
#    namespace: "" 
#    package_name: "opendatahub-operator" 
#    package_channel: "" 
#    catalog_source: "" 
#  register: subscription 

Next, we executed the actual Ansible Playbook from the collection, by using: 

ansible-playbook ibm.mas_devops.oneclick_add_aibroker 

We should also check that all Pods on the “aibroker” (in our case “mas-p01-main-aibroker”) project/namespace are Ready and execute without errors: 

To configure Manage next, we need to retrieve some additional information, first, let’s get the AI Broker API Key from the secret named “aibroker-user----apikey-secret” on the AI Broker project/namespace. That value should be used into the “mxe.int.aibrokerapikeyManage System Property. 

Also, we should grab the value of the AI Broker Route, in the same project/namespace, our Route has the URL “https://aibroker.p01.demo05.mas.interloc.cloud/” and we used it to configure the “mxe.int.aibrokerapiurlManage System Property by adding “/ibm/aibroker/service/rest/api/v1/” to it, so it became “https://aibroker.p01.demo05.mas.interloc.cloud/ibm/aibroker/service/rest/api/v1/”. 

Finally, we configured the “mxe.int.aibrokertenantid” Manage System Property. The value we used was “aibroker-user” which appears to be a hardcoded value that can be retrieved from the internal URL: https://km-controller.mas-p01-main-aibroker:8443/api/v1/tenants, values in our case we used (on the Terminal of the “*-aibroker-api-*” Pod): 

wget https://km-controller.mas-p01-main-aibroker:8443/api/v1/tenants --no-check-     certificate 

And the response came back as “[aibroker-user]”. 

Next, we went to the "Cron Task Setup" application and ensure the Crontask Instances for “AIINFJOB” and “AITRAINJOB” are enabled and set to run periodically. Wait for the “AIINFJOB” to run at least once. 

Finally, we went to the "AI configuration" application and there should be only one row named "WOPROBLEMCODE". We selected it and from Actions choose "Activate" then "Train Model". Waited while the model is trained as it may take some time. 

A row should appear on the Model Training log table stating that the model training was completed. Then going back into the list of AI Configuration Models, check for the "Check Model Status" option that appears when you hover over the Active column value and the Model ID should be populated as well as the Ready value should be "True". 

 

References: 

We mostly followed a great tutorial by Sai Manjunath that can be found at https://developer.ibm.com/tutorials/awb-ai-driven-work-order-intelligence-maximo/ 

Also, the related IBM documentation can be found under https://www.ibm.com/docs/en/masv-and-l/maximo-manage/continuous-delivery?topic=watsonx-maximo-manage-ai-overview