Watson Studio CloudのJupyter NotebookジョブをAPIで外部から起動

Watson Studio CloudでJupyter Notebookをジョブ化できることを以下の記事で紹介しました。
https://qiita.com/kawada2017/items/fd802ec9c0b5c5a55e47

この機能はスケジュールもついていて気軽につかえて便利なのですが、簡易的なものであるため、失敗したときなどに通知する機能がなかったり、祝日カレンダーなどもありません。起動トリガーも時間だけなので、他システムのジョブの成功の後に実行するような連携もやりやすくはありません。
本格的に自動運用をシステム化する場合にはIBM Workload SchedulerやJP1などのジョブ・スケジューラーから呼び出したくなります。

そこで、Jupyter Notebookジョブは外部のプログラムからWatson Data APIでも呼び出す機能を使います。
@ttsuzuku さんのCloud Pak for DataでJupyter NotebookジョブをAPI実行する以下の記事を参考に、Watson Studio CloudでAPIでの実行方法をご紹介します。

Cloud Pak for Dataの分析プロジェクトのJobを環境変数付きでAPI実行する - Qiita
https://qiita.com/ttsuzuku/items/3a2267f90c9c184cf07a

# 概要

全体概要を以下に図示しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/7c5d58e9-2ec5-9294-7531-952dac6fbf7f.png)

外部プログラムからはIBM CloudのAPI KEYでアクセスして、トークンを取得します。その後ジョブを起動します。するとジョブからNotebookが呼び出され、そのNotebookからデータ資産への書き出しを行います。

# ジョブの作成
最初にデータ資産への書き出しを行う簡単なサンプルジョブを作成します。
このサンプルではprojectオブジェクトをつかってデータ資産を登録したいので、Projectのトークンを作ります。

設定タブのなかでアクセス・トークンの「新規作成+」をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a90c6814-c715-a569-db46-ed34cbdaf92e.png)

任意の名前を設定し、プロジェクトのアクセス・ロールとしてエディターを選び、作成をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/5d5b50f9-ddc5-ed35-cf79-3d5c2db5e868.png)

以下のように作成されればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1b5d1b12-4e8d-b76c-300a-e14ac6403dc2.png)

資産タブに移り、「プロジェクトに追加＋」をクリックし、Notebookをクリックして、新規Notebookを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/5dd44620-61c5-b10c-4aee-94e08f1837ab.png)

「URLから」を選び、名前に「Jobtest」と入力します。NotebookURLには以下を入力してください。
https://github.com/hkwd/200616WSCJobExecution/blob/master/Jobtest_pub.ipynb
入力ができたら作成ボタンをクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a96f69f0-2eea-645f-581d-d13b816a2bbc.png)

Notebookが読み込まれたら、プロジェクト・トークンの挿入をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/64b5e1bd-a0cf-7fd7-4d17-9f0df77ad1bc.png)

以下のようにプロジェクト・トークンを作成するコードが自動的に挿入されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a8233cbc-4438-d915-ffcc-af4b2a15ab0e.png)

全てのセルを実行すると、以下のようにjobtestout.csvというファイルにタイムスタンプと環境変数が書き出されます。
FileメニューからSave Versionでバージョンを保存してください。ジョブとして実行するためには必ずバージョンが保存されている必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f1d42c90-4626-f61e-ab8c-d85f2a439c00.png)

プロジェクトの資産のタブに戻り、jobtestout.csvをクリックして開いてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/3f460e74-4020-3eec-acdd-3f767caa36b8.png)

確かにタイムスタンプと環境変数が書き出されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/cfebb199-f98e-5c97-0386-6f79509e9730.png)

プロジェクトのジョブタブに移り、「新規ジョブ＋」をクリックして、ジョブを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/17980ba5-598e-51ef-d511-7f2baa1a3e23.png)

ジョブ名にJobTestJobと任意で説明を入力し、「資産の選択」をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d99566bd-344b-5da7-89a1-594e22b11b77.png)

Jobtestを選び、保存をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/47e93662-afdd-0f36-2068-f64c9ce4a92c.png)

作成して実行をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e5b8e8f4-8ee8-70cd-f76f-8f8477701795.png)

少し待つと実行が完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/ff293639-139d-b7fa-1dad-fd07640d18d5.png)

このNotebookジョブをAPIから実行してみたいと思います。

# API keyの取得
APIから実行するためには、以下からIBM CloudのAPI Keyを作成します。「IBM Cloud APIキーの作成」ボタンを押してください。
https://cloud.ibm.com/iam/apikeys

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/46e33c88-39ac-724e-ece2-0e13d5671609.png)

ちなみに一覧の中には以下のようなWatson Studio Jobsをスケジュール実行するためのAPI Keyがありました。Watson Studio内のスケジューラーでも同じように使われているようです。なお、このAPI Keyを使いまわすことはできません。API Keyは生成時にしか表示されないためです。
IBMid-50H9DGS2KP - Jobs api delegate key - do not delete
Created by Watson Studio Jobs to run scheduled jobs

名前と説明には任意のものを設定し、作成ボタンを押してください。ここでは以下のように設定してみました。
名前: 200618API Key for Watson Studio Job API
説明: Watson Studio Job APIを外部プログラムから呼び出すためのAPI Key

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/775c77c9-1e2d-61f9-05d4-099a1c8813b4.png)

ダウンロードして、ポップアップを閉じてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/5cbdbc29-7eb3-f761-9c1a-ad69b8cb39e9.png)

このAPI Keyは悪用されると第三者にご自身のIBM Cloud環境の操作をされてしまうので、厳重に管理してください。

# APIによるJobの起動
以下のプログラムで実行ができます。
https://github.com/hkwd/200616WSCJobExecution/blob/master/JobExec2_pub.py

プログラム先頭の以下のパラメーターを環境に合わせて修正してください。

```python
params = {}
params['apikey'] = 'xxxxxxxx'
params['project_id'] = "xxxxxxxx"
```

先にダウンロードしたAPI Keyからparams['apikey']をセットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/782e5e09-b848-63af-902e-59ae32e4e6c0.png)

次にparams['project_id']もセットします。
プロジェクト名をクリックするとブラウザのURLにproject_idが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/c6735053-af85-6a25-cdf4-cea7ed9a3476.png)

以下のようにセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d15e1ee0-181d-e381-c782-cfec55351808.png)

プログラムも簡単に見てみます。

以下でWatson Studioに外部のプログラムからAPIアクセスするためのトークンを取得しています。

```python
#IAMのトークン取得
iam_response  = requests.post( params['token_url'], headers=params['token_headers'], data=params['token_data'] )
bearer_token = "Bearer " + iam_response.json()["access_token"]

headers = {
    'Authorization': bearer_token,
    'Content-Type': 'application/json'
}
```
参考
How to Access Watson Studio Assets by API | IBM
https://www.ibm.com/cloud/blog/how-to-access-watson-studio-assets-by-api

以下でプロジェクト内のジョブのリストを取得して、JobTestJobという名前を持つJob_idを取得しています。

```python
# Job list取得
project_id =params['project_id']
url=params['url']
response = requests.get(url+"/v2/jobs?project_id="+project_id, headers=headers).json()

MyJobName="JobTestJob"
job_id=next(v['metadata']['asset_id'] for v in response['results'] if v['metadata']['name'] ==MyJobName)
```

ちなみにJob_idはブラウザでジョブを選べばURL内に書かれていますので、ここからとってくることもできます。ただ、可読性がさがるのでリストから取得するほうがよいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/4287eb74-a897-e75b-db9c-849632fce918.png)

環境変数を設定して、ジョブを実行し、そのジョブのRunIDを取得しています。

```python

#ジョブ実行のパラメーター
jobrunpost = {
  "job_run": {
      "configuration" : {
          "env_variables" :  ["MYENV1=100"] 
      }
  }
}

# Run job
response = requests.post(url+"/v2/jobs/"+job_id+"/runs?project_id="+project_id, headers=headers, json=jobrunpost).json()

# Job run id
job_run_id = response['metadata']['asset_id']

```
ジョブは非同期で実行されるため、ジョブのRunIDをつかってその終了を確認します。ここでは3秒おきに'Starting', 'Running'のStatusではなくなったかどうかを調べ終了を判定しています。また、12回チェックをしても'Starting', 'Running'のStatsuの場合はTime Upとしました。実際の処理にあわせてこの回数やスリープ時間を調節したり、永久ループで判定するようにしてください。

```python
#Jobの終了を確認
import time
retry_times=12
retry_sleep=3
    
for i in range(retry_times):
    response = requests.get(url+"/v2/jobs/"+job_id+"/runs/"+job_run_id+"?project_id="+project_id, headers=headers).json()
    jobstatus = response['entity']['job_run']['state']
    print(jobstatus)
    if jobstatus not in ['Starting', 'Running']: 
        break
    time.sleep(retry_sleep)
    if i==retry_times-1:
        print("Time up")
```

以下のようなイメージで動作します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/6850a327-af08-d5d1-f9e8-17837e4fcd7c.png)

ジョブを開いてみると実行の履歴が記録されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/4277562b-c256-6698-b771-194af76f7c9f.png)

資産タブにうつり、jobtestout.csvをクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/bea316ee-db77-3595-eee1-6e5af9075904.png)

APIで実行したタイムスタンプと与えた環境変数でファイルが更新されています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0a00eb2f-3f6e-c2e3-a5cd-6be65bcbe28f.png)






#参考

Watson Data API - IBM Cloud API Docs
https://cloud.ibm.com/apidocs/watson-data-api-cpd#get-list-of-jobs-under-a-project
Watson Data API のマニュアルです。

Jobs API
https://api.dataplatform.cloud.ibm.com/v2/jobs/docs/swagger/#/

JobAPIの仕様です。出力例もあります。