Watson Machine Learningでカスタム・イメージを利用する

##はじめに
この記事では、IBM Watson Machine Learningが提供しているRuntimeイメージをベースに、新しいカスタム・イメージを作成する方法をご紹介します。
これによりデプロイメントに必要なパッケージが含まれたカスタムのイメージを作成し、Machine Learningの資産をデプロイできるようになります。
##環境
- IBM Cloud Pak for Datab 3.5.2(Watson Machine Learning)
- 作業マシン（Red Hat 7.8)

##カスタム・イメージの作成
こちらのマニュアル記述に沿って必要な構成を行います。
[Watson Machine Learning のカスタム・イメージを使用したデプロイ](https://www.ibm.com/docs/ja/cloud-paks/cp-data/3.5.0?topic=overview-deploying-using-custom-image)
以下の手順を行います。詳しくは後述します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404035/c40903ff-a405-8e9e-ac76-9efd04825892.png)
　　　　　　　　　　　　図１. カスタム・イメージの作成

1.　ランタイム・イメージのダウンロード
2.　ランタイム・イメージのカスタマイズ
3.　カスタム・イメージのアップロード
4.　ソフトウェア仕様を作成
5.　ランタイム定義ファイルを作成
###1. ランタイム・イメージのダウンロード
以下を実行します。

```
POD=$(oc get pod -l name=cpd-install-operator -o=jsonpath='{.items[0].metadata.name}')
```

```
oc exec $POD -- helm  get values wml  -a --tls |  grep -A2  -B1 image | egrep -v '^-|image'  | sed -e 's/repository://' -e 's/tag://' | paste - - -
```

以下の例のような出力が現れますので自分がカスタマイズしたいものを選択します。

```
    wml-deployment-runtime-py37-1:v.0.1.40.51-amd64
    wml-deployment-runtime-py37-1:v.0.1.40.51-ppc64le
    wml-deployment-runtime-py37-1:v.352.0.1.40.51.10-amd64
    wml-deployment-runtime-py37-1:v.352.0.1.40.51.10-ppc64le
    wml-deployment-runtime-py37-1:v.353.0.1.40.51.1-amd64
    wml-deployment-runtime-py37-1:v.353.0.1.40.51.1-ppc64le
```    

loginし、目的のイメージをPullします。

```
# podman login -u <username> -p <password> <image-registry-url>
```

以下、pullの実行例です。

```
#  podman pull <image-registry-host>/<name_space>/wml-deployment-runtime-py37-1:v.351.0.1.40.51.3-amd64
```

###2. ランタイム・イメージのカスタマイズ
カスタマイズするランタイムのイメージをダウンロードしたら、ダウンロードしたイメージをベースに新しいカスタム・イメージを作成できます。これを行うには、カスタマイズ内容を`Dockerfile`に追加します。
####2-1. Dockerfileの編集
Dockerfileには追加構成したいpipパッケージを含めます。

```
ARG  base_image_tag
FROM ${base_image_tag}
RUN umask 002 \
 && pip install \
    ＜追加パッケージ＞
```

####2-2. カスタム・イメージのビルド
新規イメージを作成します。
以下実行例です。ここでは`podman`コマンドを利用し、`--format docker`を指定しています。また、「イメージ名＝wmlimagetest」、「タグ=04051545」を指定しました。カスタマイゼーションを含んだ`Dockerfile`は`./cp4dimagetest.wml`にて提供しました。

```
# podman build -t wmlimagetest:04051545 --format docker --build-arg base_image_tag=<your registry-URL>/wml-deployment-runtime-py37-1:v.351.0.1.40.51.3-amd64 -f cp4dimagetest.wml .
```
新しいイメージができました。

```
# podman images
REPOSITORY               TAG       IMAGE ID       CREATED         SIZE
localhost/wmlimagetest   04051545  7ee18f8fa5f5   2 minutes ago    5.82 GB
```

###3. カスタム・イメージのアップロード
イメージを作成したら、そのイメージをレジストリー・サーバーにプッシュして、イメージを登録する必要があります。
以下実行例です。

```
podman tag 7ee18f8fa5f5 <your registry-url>/wmlimagetest:04051545
```

```
podman push <image-registry-host>/<name space>/wmlimagetest:04051545
```

###4. ソフトウェア仕様を作成
カスタム・イメージを作成、登録、およびアップロードしたら、Watson Machine Learning のカスタム・イメージに指定したライブラリーとパッケージを使用できるように、カスタム・ソフトウェア仕様とランタイム定義ファイルを作成する必要があります。
まず、以下の手順でボリューム・サービスを作成し、そこに必要なファイルをアップロードします。

####4-1 アクセス・トークンの取得
以下を実行し、アクセス・トークンを取得します。

```
curl <CloudPakforData_URL>/v1/preauth/validateAuth -u <username>:<password>
```


####4-2 ボリューム・サービスの作成
取得したアクセス・トークンは`TOKEN`にセットし、以下の例のようにボリューム・サービスを作成します。

```
VSNAME=CCHome
NAMESPACE=<namespace>

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

以下を実行して、正常に作成されたことを確認します。

```
curl -k \
    $CPDHOST/zen-data/v3/service_instances \
    -H "Authorization: Bearer ${TOKEN}" \
    -H 'Content-Type: application/json' | jq '.service_instances[] | .display_name,.id,.metadata.existing_pvc_name'
```
このファイル・サーバーを起動します。

```
curl -k -X POST \
  $CPDHOST/zen-data/v1/volumes/volume_services/$VSNAME \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{}'
```
停止するには以下を実行します。次工程でソフトウェア仕様をアップロードするときにはまた起動してください。

```
curl -k -X DELETE \
  $CPDHOST/zen-data/v1/volumes/volume_services/$VSNAME \
  -H "Authorization: Bearer $TOKEN"
```
###4-3 ソフトウェア仕様の作成

ソフトウェア仕様をJSON形式で作成してアップロードします。
以下の例の様に変数をセットします。

```
SW_SPEC_NAME=demo-spec-1_py37
DISPLAY_NAME='test sw spec base 0.1.40-amd64'
SW_SPEC_JSON=${SW_SPEC_NAME}.json
PATH_SW_SPEC_JSON=`pwd`"/"$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": "scikit-learn",
         "version": "0.23"
       },
       {
         "name": "pytorch-onnx",
         "version": "1.3"
       },
       {
         "name": "tensorflow",
         "version": "2.1"
       },
       {
         "name": "gensim",
         "version": "3.8.0"
       }         
     ],
     "platform": {
       "name": "python",
       "version": "3.7"
     }
   }
 }
  }
}' > $SW_SPEC_JSON
```
上記の後、`cat $SW_SPEC_JSON`を実行し、正しくソフトウェア仕様を示すJSONが作られていることを確認してください。

####4-4 ソフトウェア仕様のアップロード
できたソフトウェア仕様をアップロードします。

```
curl -k -X PUT \
$CPDHOST/zen-volumes/$VSNAME/v1/volumes/files/%2F_global_%2Fconfig%2Fenvironments%2Fsoftware-specifications%2F$SW_SPEC_JSON \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: multipart/form-data' \
-F upFile=@"$PATH_SW_SPEC_JSON"
```
以下が表示されれば成功です。

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

####4-5 ソフトウェア仕様のダウンロード
前項でソフトウェア仕様が正常にアップロードされたことを確認します。実際に取得してみます。
以下を実行します。

```
curl -k -X GET \
$CPDHOST/zen-volumes/$VSNAME/v1/volumes/files/%2F_global_%2Fconfig%2Fenvironments%2Fsoftware-specifications%2F$SW_SPEC_JSON \
-H "Authorization: Bearer $TOKEN"
```
行ったカストマイゼーションの内容が反映されていることを確認します。

####4-6 ボリューム・サービスの再起動とソフトウェア仕様の確認
以下を実行してボリューム・サービスの再起動を行います。

```
curl -k -X DELETE \
$CPDHOST/zen-data/v1/volumes/volume_services/$VSNAME \
 -H "Authorization: Bearer $TOKEN"

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

以下を実行しソフトウェア仕様を確認します。

```
curl -k -X GET \
    $CPDHOST/v2/software_specifications?name=$SW_SPEC_NAME \
    -H "Authorization: Bearer $TOKEN" | jq "."
```

以下の例の様な値が戻ります。

```
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   727  100   727    0     0    580      0  0:00:01  0:00:01 --:--:--   580
{
  "total_results": 1,
  "resources": [
    {
      "metadata": {
        "created_at": "2021-04-05T08:05:04.658Z",
        "updated_at": "2021-04-05T08:05:04.658Z",
        "name": "demo-spec-1_py37",
        "description": "Test",
        "asset_type": "software_specification",
        "asset_id": "51301be2-ea21-5848-bb86-bdd86c4b893d",
        "href": "/v2/software_specifications/51301be2-ea21-5848-bb86-bdd86c4b893d"
      },
      "entity": {
        "software_specification": {
          "type": "base",
          "built_in": false,
          "package_extensions": [],
          "display_name": "test sw spec base 0.1.40-amd64",
          "software_configuration": {
            "included_packages": [
              {
                "name": "scikit-learn",
                "version": "0.23"
              },
              {
                "name": "pytorch-onnx",
                "version": "1.3"
              },
              {
                "name": "tensorflow",
                "version": "2.1"
              },
              {
                "name": "gensim",
                "version": "3.8.0"
              }
            ],
            "platform": {
              "name": "python",
              "version": "3.7"
            }
          }
        }
      }
    }
  ]
}
```

###5 ランタイム定義の作成
作成したソフトウェア仕様をWatson Machine Learningのイメージとマップするため、ランタイム定義ファイルを作成します。

####5-1 テンプレートの編集
以下の様なテンプレートがマニュユアルに提供されていますので必要な編集を行います。

```
# NOTE: The filename needs to end with *-server.json 
CUSTOM_WML_RUN_DEF=wml-$SW_SPEC_NAME-server.json

# template WML runtime definition
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": "sample-software-spec-name",
    "image": "image-registry.openshift-image-registry.svc:5000/ns21/wml-sample-image:test-1"
}' > $CUSTOM_WML_RUN_DEF

# replace the template with your values
tmp=$(mktemp)
jq  --arg dn "WML test" \
    --arg ds "WML description test" \
    --arg sw "${SW_SPEC_NAME}" \
    --arg img "${CUSTOM_IMAGE}" \
    '.displayName = $dn | .description = $ds | .software_specification_name = $sw | .image = $img' \
    $CUSTOM_WML_RUN_DEF > "$tmp" && mv "$tmp" $CUSTOM_WML_RUN_DEF
```
以下を実行し、意図通りの修正であることを確認します。

```    
PATH_CUSTOM_RUN_DEF=`pwd`"/"$CUSTOM_WML_RUN_DEF
echo "Custom Runtime definition file path:"
echo $PATH_CUSTOM_RUN_DEF

echo "Content of the file:"
cat $CUSTOM_WML_RUN_DEF | jq "."
```

####5-2 ランタイム定義のアップロード
ランライム定義をアップロードします。以下を実行します。

```
curl -k -X PUT \
 $CPDHOST/zen-data/v1/volumes/files/%2F_global_%2Fconfig%2F.runtime-definitions%2Fibm \
 -H "Authorization: Bearer $TOKEN" \
 -H 'content-type: multipart/form-data' \
 -F upFile=@"$PATH_CUSTOM_RUN_DEF"
```
以下の様なメッセージが返ります。

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

####5-3 ランタイム定義がボリュームインスタンスにあることの確認
以下を実行し、ランタイム定義がボリュームインスタンスにあることの確認を行います。

```
curl -k -X GET  $CPDHOST/zen-data/v1/volumes/files/%2F_global_%2Fconfig%2F.runtime-definitions%2Fibm%2F$CUSTOM_WML_RUN_DEF  --header "Authorization: Bearer $TOKEN"
```
以下の例の様にカストマイズ・イメージの情報が得られます。

```
{
  "displayName": "WML test",
  "description": "WML description test",
  "author": "IBM",
  "tested": true,
  "isService": true,
  "features": [
    "wml"
  ],
  "runtimeType": "wml",
  "software_specification_name": "demo-spec-1_py37",
  "image": "＜Image_url＞/wmlimagetest:04051545"
}
```
####5-4 ランタイムイメージがRESTエンドポイントでもアクセスできることを確認
以下を実行します。

```
curl -k -s -X GET \
 $CPDHOST/v2/runtime_definitions \
 -H "Authorization: Bearer $TOKEN" 
```
これでカスタム・イメージを利用したモデルのデプロイができるようになりました。
##おわりに
今回示した手順により、必要なライブラリーやパッケージを追加したイメージをカスタム・イメージとして作成し、これをWatson Machine Learningで利用できる様になります。