Cloud Pak for Data

 View Only

Build a custom image for Watson Machine Learning in Cloud Pak for Data 4

By Harris Yang posted Sun June 11, 2023 05:21 AM

  

This blog describes the detail steps to create a customer software specification for Watson Machine Learning in Cloud Pak for Data v4

Step 1: Preparation

You may build a custom image in bastion node of your air-gapped CPD cluster.

Pull out a terminal on bastion node

export cpd_url=<url of CPD console, eg. https://cpd-cpd-instance1.apps.bj-prod-16.luban.cdl.ibm.com>
export cpd_py_runtime_file=wml-deployment-runtime-py39-server.json

export PRIVATE_REGISTRY_LOCATION=<your private container registry, eg. bastion.bj-prod-18.luban.cdl.ibm.com:15000>
export PRIVATE_REGISTRY_PULL_USER=<user_name>
export PRIVATE_REGISTRY_PULL_PASSWORD=<password_of_user>

Step 2: Download default runtime configuration

Replace the <user_name:password> with your CPD login user name and password to get token

myToken=`curl -k ${cpd_url}/v1/preauth/validateAuth -u <user_name:password> | sed -n -e 's/^.*accessToken":"//p' | cut -d'"' -f1`

curl -X GET \
"${cpd_url}/zen-data/v1/volumes/files/%2F_global_%2Fconfig%2F.runtime-definitions%2Fibm%2F${cpd_py_runtime_file}" \
--header "Authorization: Bearer ${myToken}" \
-k > ${cpd_py_runtime_file}

Step 3: Pull default runtime image

cat ${cpd_py_runtime_file} | grep image

Example of the output:

    "image": "cp.icr.io/cp/cpd/wml-deployment-runtime-py39-1@sha256:c604bf14c3b9d137a0632d2323c234d9da279d31e3bd8e214f6c1a5b6175d3de",
    ...

Copy out the image address and replace it with your private contianer registry address, eg. bastion.bj-prod-18.luban.cdl.ibm.com:15000/cp/cpd/wml-deployment-runtime-py39-1@sha256:c604bf14c3b9d137a0632d2323c234d9da279d31e3bd8e214f6c1a5b6175d3de

podman login -u ${PRIVATE_REGISTRY_PULL_USER} -p ${PRIVATE_REGISTRY_PULL_PASSWORD} ${PRIVATE_REGISTRY_LOCATION}

podman pull bastion.bj-prod-18.luban.cdl.ibm.com:15000/cp/cpd/wml-deployment-runtime-py39-1@sha256:c604bf14c3b9d137a0632d2323c234d9da279d31e3bd8e214f6c1a5b6175d3de


Step 4: build a custom image

Create a Dockerfile

vi Dockerfile

Content of the Dockerfile, in this example, pyecharts==1.9.1 is the package added, you can add more packages necessarily.

ARG  base_image_tag
FROM ${base_image_tag}

# For installing OS packages, use root:root
# e.g.
USER root:root
RUN yum install -y gdb

# For installing to conda site-packages,
# use wmlfuser:condausers
# e.g.
USER wmlfuser:condausers
RUN umask 002 && \
pip install gensim \
            pyecharts==1.9.1
#
# Alternatively, for Installing to location other than conda site-packages.
# It can be installed to
#    --target <any_file_system_dir_path>
# Provided <any_file_system_dir_path> full path is accessible to any user with group "condausers"
#
# Important:
#     The <any_file_system_path> must then be added to the environment variable PYTHONPATH
#
# e.g 
# use wmlfuser:condausers to install to the path /home/wmlfuser/mypkgs and add
# the path to PYTHONPATH environment variable
#
USER wmlfuser:condausers
RUN umask 002 && \
mkdir -p /home/wmlfuser/mypkgs && \
pip install pydot --target /home/wmlfuser/mypkgs
ENV PYTHONPATH=$PYTHONPATH:/home/wmlfuser/mypkgs
# If pip --user is used for installing package.
# The installation path needs to be explicitly added to PYTHONPATH
# e.g.
#    
#    for user wmlfuser:condausers it by default goes to user install directory
#    (See the Python documentation for site.USER_BASE for full details.)
#    use `python -m site --user-site` command to get the path.
#
#    for wml-deployment-runtime-py39-1, it goes to
#     /home/wmlfuser/.local/lib/python3.9/site-packages
#
#

Build the custom image, in this example, custom_image:image_tage is wml-cust-runtime-pyecharts:1.9.1

podman build -t wml-cust-runtime-pyecharts:1.9.1 \
--build-arg base_image_tag=bastion.bj-prod-18.luban.cdl.ibm.com:15000/cp/cpd/wml-deployment-runtime-py39-1@sha256:c604bf14c3b9d137a0632d2323c234d9da279d31e3bd8e214f6c1a5b6175d3de \
-f ./Dockerfile

Step 5: Push custom image

podman tag wml-cust-runtime-pyecharts:1.9.1 bastion.bj-prod-18.luban.cdl.ibm.com:15000/cp/cpd/wml-cust-runtime-pyecharts:1.9.1

podman push bastion.bj-prod-18.luban.cdl.ibm.com:15000/cp/cpd/wml-cust-runtime-pyecharts:1.9.1

Step 6: Creating a custom software specification

Create a volume instance, PROJECT_CPD_INSTANCE is OCP project installed CP4D

export PROJECT_CPD_INSTANCE=cpd-instance1
export VSNAME=CCHome

curl -k -X POST \
 ${cpd_url}/zen-data/v3/service_instances \
 -H "Authorization: Bearer ${myToken}" \
 -H "Content-Type: application/json" \
 -d "{
       \"addon_type\": \"volumes\", \
       \"addon_version\": \"-\", \
       \"create_arguments\": { \
       \"metadata\": {
         \"existing_pvc_name\": \"cc-home-pvc\" \
       } \
       }, \
       \"namespace\": \"${PROJECT_CPD_INSTANCE}\", \
       \"display_name\": \"${VSNAME}\" \
   }"

Check whether the new Volume instance is available

curl -k \
    ${cpd_url}/zen-data/v3/service_instances \
    -H "Authorization: Bearer ${myToken}" \
    -H 'Content-Type: application/json' | jq '.service_instances[] | .display_name,.id,.metadata.existing_pvc_name'

Example of output

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  7607    0  7607    0     0   9121      0 --:--:-- --:--:-- --:--:--  9110
"cpd-instance1::CCHome"
"1670411879751802"
"cc-home-pvc"
"default-openpages-instance"
"1668058558932671"

Stop the file server for a volume (In case it is started by default)

curl -k -X DELETE \
${cpd_url}/zen-data/v1/volumes/volume_services/${PROJECT_CPD_INSTANCE}::${VSNAME} \
-H "Authorization: Bearer ${myToken}"

Output message {"_messageCode_":"200","message":"Successfully stopped volume service"}

Or {"_statusCode_":404,"exception":"deployment cchome-deploy does not exist","message":"Not Found"} means the file server is not active yet.

Start the file server for a volume

curl -k -X POST \
${cpd_url}/zen-data/v1/volumes/volume_services/${PROJECT_CPD_INSTANCE}::${VSNAME} \
-H "Authorization: Bearer ${myToken}" \
-H 'Content-Type: application/json' \
-d '{}'

Output message {"_messageCode_":"200","message":"Successfully started volume service"}

Create custom software specification

SW_SPEC_NAME='pyecharts_py39'
DISPLAY_NAME='Pyecharts custom image'

SW_SPEC_JSON=${SW_SPEC_NAME}.json
PATH_SW_SPEC_JSON=`pwd`"/"$SW_SPEC_JSON
echo $PATH_SW_SPEC_JSON

echo '{
"metadata": {
 "name": "'"$SW_SPEC_NAME"'",
 "description": "Test"
 },
"entity": {
 "software_specification": {
   "type": "base",
   "built_in": false,
   "package_extensions": [],
   "display_name": "'"$DISPLAY_NAME"'",
   "software_configuration": {
     "included_packages": [
      {
        "name": "pyecharts",
        "version": "1.9.1"
      }
     ],
     "platform": {
       "name": "python",
       "version": "3.9"
       }
     }
   }
 }
}' > $SW_SPEC_JSON

Check out the content

cat $SW_SPEC_JSON

Upload custom software specification

curl -k -X PUT \
${cpd_url}/zen-volumes/${VSNAME}/v1/volumes/files/%2F_global_%2Fconfig%2Fenvironments%2Fsoftware-specifications%2F${SW_SPEC_JSON} \
-H "Authorization: Bearer ${myToken}" \
-H 'content-type: multipart/form-data' \
-F upFile=@"${PATH_SW_SPEC_JSON}"

Output message {"_messageCode_":"Success","message":"Successfully uploaded file and created the directory structure"}

Check out custom software specification is uploaded

curl -k -X GET \
${cpd_url}/zen-volumes/${VSNAME}/v1/volumes/files/%2F_global_%2Fconfig%2Fenvironments%2Fsoftware-specifications%2F${SW_SPEC_JSON} \
-H "Authorization: Bearer ${myToken}"

Restart the file server

curl -k -X DELETE \
${cpd_url}/zen-data/v1/volumes/volume_services/${PROJECT_CPD_INSTANCE}::${VSNAME} \
-H "Authorization: Bearer ${myToken}"

Output message {"_messageCode_":"200","message":"Successfully stopped volume service"}

curl -k -X POST \
${cpd_url}/zen-data/v1/volumes/volume_services/${PROJECT_CPD_INSTANCE}::${VSNAME} \
-H "Authorization: Bearer ${myToken}" \
-H 'Content-Type: application/json' \
-d '{}'

Output message {"_messageCode_":"200","message":"Successfully started volume service"}

Check out custom software specification is available from a REST endpoint

curl -k -X GET ${cpd_url}/v2/software_specifications?name=${SW_SPEC_NAME} -H "Authorization: Bearer ${myToken}" | jq "." 

Output

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   601  100   601    0     0   3320      0 --:--:-- --:--:-- --:--:--  3320
{
  "total_results": 1,
  "resources": [
    {
      "metadata": {
        "created_at": "2022-12-07T12:01:36.691Z",
        "updated_at": "2022-12-07T12:01:36.691Z",
        "name": "pyecharts_py39",
        "description": "Test",
        "asset_type": "software_specification",
        "href": "/v2/software_specifications/b2590c9b-343f-57f0-a8d3-29bf270a7e93",
        "asset_id": "b2590c9b-343f-57f0-a8d3-29bf270a7e93"
      },
      "entity": {
        "software_specification": {
          "type": "base",
          "built_in": false,
          "package_extensions": [],
          "display_name": "Pyecharts custom image",
          "software_configuration": {
            "included_packages": [
              {
                "name": "pyecharts",
                "version": "1.9.1"
              }
            ],
            "platform": {
              "name": "python",
              "version": "3.9"
            }
          }
        }
      }
    }
  ]
}

Step 7: Upload custom runtime configuration

The value of image is the custom image pushed in step 5

Limitation: The filename needs to end with *-server.json


CUSTOM_WML_RUN_DEF=wml-$SW_SPEC_NAME-server.json
echo ${CUSTOM_WML_RUN_DEF}

echo '{
     "displayName": "WML custom image sample",
     "description": "description for WML custom image sample RD",
     "author": "IBM",
     "tested": true,
     "isService": true,
     "features": ["wml"],
     "runtimeType": "wml",
     "software_specification_name": "'"$SW_SPEC_NAME"'",
     "image": "bastion.bj-prod-18.luban.cdl.ibm.com:15000/cp/cpd/wml-cust-runtime-pyecharts:1.9.1"
 }' > $CUSTOM_WML_RUN_DEF

Upload the custom runtime definition file

curl -k -X PUT \
${cpd_url}/zen-data/v1/volumes/files/%2F_global_%2Fconfig%2F.runtime-definitions%2Fibm \
-H "Authorization: Bearer ${myToken}" \
-H 'content-type: multipart/form-data' \
-F upFile=@"${CUSTOM_WML_RUN_DEF}"

Check out custom runtime definition file is on the volume

curl -k -X GET \
${cpd_url}/zen-data/v1/volumes/files/%2F_global_%2Fconfig%2F.runtime-definitions%2Fibm%2F${CUSTOM_WML_RUN_DEF} \
--header "Authorization: Bearer ${myToken}"

Check out custom runtime definition is available in REST endpoint

curl -k -s -X GET \
${cpd_url}/v2/runtime_definitions \
-H "Authorization: Bearer ${myToken}"

Step 8: Use the custom software specification to deploy a python function

Deploy a python function from a Jupyter Notebook

#wml_python_function
def my_deployable_function():

    def score( payload ):

        message_from_input_payload = payload.get("input_data")[0].get("values")[0][0]
        response_message = "Received message - {0}".format(message_from_input_payload)

        # Score using the pre-defined model
        score_response = {
            'predictions': [{'fields': ['Response_message_field'], 
                             'values': [[response_message]]
                            }]
        } 
        return score_response

    return score

from ibm_watson_machine_learning import APIClient

from project_lib.utils import environment
url = environment.get_common_api_url()

import sys,os,os.path
token = os.environ['USER_ACCESS_TOKEN']

wml_credentials = {
     "instance_id": "openshift",
     "token": token,
     "url": url,
     "version": "4.5"
}

client = APIClient(wml_credentials)

space_name = 'Pyecharts-Space'
space_uid = ''

for space in client.spaces.get_details()['resources']:
    if space['entity']['name'] ==space_name:
        space_uid=space['metadata']['id']

if space_uid == '':
    space_meta_data = {
        client.spaces.ConfigurationMetaNames.NAME : space_name
        }
    stored_space_details = client.spaces.store(space_meta_data)
    space_uid = stored_space_details['metadata']['id']

client.set.default_space(space_uid)

sw_spec_id = client.software_specifications.get_id_by_name('pyecharts_py39')

function_meta = {
    client.repository.FunctionMetaNames.NAME:"pyecharts_function",
    client.repository.FunctionMetaNames.SOFTWARE_SPEC_ID: sw_spec_id
}
func_details = client.repository.store_function(my_deployable_function, function_meta)

Go to the deployment space and should be able to see the pyecharts_function is listed as custom software specification

Deploy the python function as online service with custom software specification 

And deployment is succeed 

You may refer CPD KC on this topic: https://www.ibm.com/docs/en/cloud-paks/cp-data/4.5.x?topic=functions-working-custom-images


#data-featured-area-3
#data-spotlight

0 comments
1500 views

Permalink