【IBM Cloud】Cloud FuntionsからDb2 on Cloudのテーブルへアクセスしてみた(python)


##はじめに
クラウドサービスといえばAWSのようなイメージがあったのですが、IBMも"IBM Cloud"としてサービスを提供しています。
調べてみると、IBM Cloudにも多くのサービスが提供されており、いくつかのサービスは無料で使えるとのこと。
特に、代表的なIBM製RDBMS"DB2"を200MB以下であれば無料で使える(2020/07時点)、というのはかなり大きいのではないかなと。個人で色々やるなら、200MBもあれば十分でしょう。
同じく、一定量のリクエスト以下なら無料で使える"IBM Cloud Funtions"と連携して、Db2 on Cloud上のテーブル参照/更新ができるようになるまでを記載してみました。

##やったこと

### ①DB2サービスを作ってみる
IBM Cloudのアカウント作成後からスタートです。
[IBM Cloudログイン画面](https://cloud.ibm.com/login)

![01.Login.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/9cb6d718-d59f-4055-b402-899e726ab031.jpeg)

検索ボックスにdb2と入力して、db2のサービス画面を開きます。
![02.db2を選択.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/49bac73a-1f90-9ba6-3662-b0c4e0afe1d5.jpeg)

リージョンはロンドンかダラス、料金プランはLiteを選択します。この２つ以外のリージョンではLiteプランが無いので注意です。
右下の作成ボタンで、db2サービスが作られます
![03.db2サービス作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/4e3a2295-8b02-ce54-88f3-69f5b2153bd2.png)

db2サービスが作成されました。
テーブルとかを作る前に、資格情報を作成しておきます。Cloud Functionからデータベースへ接続する際に必要となります。
左のメニューから"サービス資格情報"を開きます。
![04.db2サービス作成した後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/0cd15966-5905-a5e0-cf7f-58021e9c0c86.png)

db2サービスを作った直後は、資格情報が何も無い状態だと思います。
①"資格情報作成"ボタンを押します。名称の入力を求められるので、何かしら入力します。
②資格情報が作成されるので、名称の左側の"V"みたいな部分を押して情報を開きます。
③"ssldsn"で始まる部分が接続に必要となるため、控えておきます。
![07.資格情報作成の後_copy.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/7fd02360-6c8f-ba21-a612-148ece5b0680.jpeg)

資格情報を作成したら、db2のコンソールを開きます。
左メニューから"管理"を選択し、"Open Console"ボタンを押します。
![08.コンソールを開く.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/95009200-8327-2f32-0733-891a942d5632.jpeg)

db2サービスが作られたので、テーブルを作ります。今回は事前に用意したcsvファイルを読み込ませることで、テーブルを作成することとします。
左上の緑色三本線のアイコンをクリックし、LOAD→Load Dataの順に開きます。
![09.LoadData.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/5aa99fed-e63e-d4d3-d0b6-8e38f46ba6be.jpeg)

今回は、以下のようなcsvファイルを使うこととします。

```text:table_test.csv
NUMBER,NAME,UPDATE_TIME
1,Taro,2020-01-01 18:00:00
2,Hanako,2020-02-03 21:00:00
3,Mike,2020-02-14 14:30:00
```
真ん中あたりの"File Selection"に、対象のcsvファイルをドラッグアンドドロップします。
ファイルをアップロードしたら、右下の"Next"ボタンで先に進みます。
![10.FileUpload.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/ab026282-a0fb-5be2-c2de-2fef18c50597.jpeg)

スキーマー/テーブルを選択する画面となります。今回は既存のスキーマーで、新規にテーブルを作成する形にします。
Schemaは、"AUDIT"や"DB2INST1"みたいなシステムチックな名称のスキーマーとは別に、英語3文字+数字5桁のスキーマーがあると思うので、それを選択します。
次のTableは、右上の"New Table"をクリックします。
Create a New Tableでは、テーブル名を入力して、"Create"ボタンをクリックします。
最後に右下の"Next"ボタンを押すと、次の画面に進みます。
![11.CreateTable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/66528591-4dd8-785e-a501-c56836b3c0dc.png)

列名などを設定する画面となります。
今回はそのまま、右下の"Next"ボタンで次に進みます。
![12.TableColumnSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/66310b14-a5c9-46ea-cacd-ea4a98886d4d.png)

右下の"Begin Load"ボタンをクリックします。
![14.ReviewSetting.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/8a200937-0fe5-25f8-ed75-7740031e25dc.jpeg)

ロードが実行されるまで、少し待ちます。
ロード完了後、"View Table"ボタンで、ロードしたテーブルを確認することができます。
円グラフでLoadに成功/失敗したレコードの割合が確認できるようです。
![15.LoadDetail.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/a7f5379e-6684-6ec9-793c-6255b9b8f21d.jpeg)

以上で、Db2側のセットアップ/テーブル作成は完了です。

### SELECTするFunctionを作ってみる

続いて、pythonを実行するためのCloud Functions(アクション)を準備します。
IBM Cloudのトップページに戻り、検索ボックスにfunctionと入力し、"Functions"を開きます。
![16.FindFunction.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/5e9dd930-0a1d-3a35-8577-4c4a4dab9eeb.jpeg)

"作成の開始"ボタンをクリックします。
![17.BeginFunction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/986d87be-6074-8f9d-54d9-74b088b2eb9c.png)

今回は"アクション"を選択します。
![16-2.SelectFunction.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/2d674ccb-9869-d3a4-fc5b-9ed03ec02898.jpeg)

アクション名を入力します。
今回はpythonでコーディングするので、ランタイムは"Python 3.7"を選択して、"作成"ボタンを押します。
![18.MakeAction.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/f7625cd3-9514-9ff3-b965-442df1c225ba.jpeg)

コーディングの画面となりました。
デフォルトで用意されているmain関数の中に記述した処理が、アクションを起動することで実行されるようです。
![19.Coding.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/9e40d91c-6f55-f8f6-bc3a-01d6ee16979e.jpeg)

main関数の中に、pythonで処理を書いていきます。
今回は下記のような、テストテーブルから特定の行を取得するコードにしました。最低限、動くための処理のみを記載しているので、実際に運用していく処理を記述する際にはエラー処理や処理状況のログ出力などがあったほうが良いと思います。

```python:select_test
import sys
import ibm_db

def main(dict):
    
    # 資格情報の内容をssldsn変数に代入する
    # "ssldsn":"DATABASE=XXXXX;HOSTNAME=XXXXX;PORT=XXXXX;PROTOCOL=TCPIP;UID=XXXXX;PWD=XXXXX;Security=SSL;"
    ssldsn = "DATABASE=XXXXX;HOSTNAME=XXXXX;PORT=XXXXX;PROTOCOL=TCPIP;UID=XXXXX;PWD=XXXXX;Security=SSL;"
    
    #DB接続
    db_conn = ibm_db.connect(ssldsn,"","")
    
    #SQLの組み立て
    sql = "SELECT * FROM TEST_TABLE WHERE NUMBER = ?"
    db_stmt = ibm_db.prepare(db_conn,sql)
    number = 3
    ibm_db.bind_param(db_stmt,1,number)
    
    #SQLの実行
    ibm_db.execute(db_stmt)
    row = ibm_db.fetch_tuple(db_stmt)
    
    #DBの切断
    ibm_db.close(db_conn)
    
    return {'name' : row[1] }
```
コーディングしたら、右上の"保存"ボタンで保存します。
![21.ActionSave.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/5d5b782d-1e20-96b3-5808-704cb990820c.jpeg)

保存できたら、同じ位置に"起動"ボタンが表示されます。
押すと記述したコードが実行されて、処理結果が表示されます。
![20.ActionExec.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/55b04d04-381b-e3ae-6cfe-22be8d458bf8.jpeg)

実行する際に覚えておくと良いのが、Functionsの課金額は(使用したメモリーサイズ)×(使用した累計秒数)で計算されます(2020/07時点、400,000GB分は無料枠として使えます)。
なので、お試しで使う際は使用するメモリーサイズは小さくしておくほうが良いです。左側のメニューの"ランタイム"から、使用するメモリーサイズを変更することができます。
![26.Runtime.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/519eb8c2-eb8b-6f69-d3f7-7aa63cc416b0.jpeg)

### INSERTするFunction(引数あり)を作ってみる
次はテスト用テーブルに行を追加するFunctions(アクション)を作ってみます。その際、"NAME"列の値はアクション実行時の引数から取得するようにしてみます。また、"UPDATE_TIME"列にはアクション実行時の時刻が入るようにします。
前のセクションと同じ流れで、Funtionsで新規アクションを作成します。今回は"insert_test"という名前にしました。
コード内容は下記の通りです。

```python:insert_test
import sys
import ibm_db
import datetime

def main(dict):
    # 資格情報の内容をコピペ
    ssldsn = "DATABASE=XXXXX;HOSTNAME=XXXXX;PORT=XXXXX;PROTOCOL=TCPIP;UID=XXXXX;PWD=XXXXX;Security=SSL;"    
    
    #DB接続
    db_conn = ibm_db.connect(ssldsn,"","")
    
    #SQL文の組み立て
    sql = "INSERT INTO TEST_TABLE VALUES(?,?,?)"
    db_stmt = ibm_db.prepare(db_conn,sql)
    
    id = 5
    name = dict['name']
    update_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    ibm_db.bind_param(db_stmt,1,id)
    ibm_db.bind_param(db_stmt,2,name)
    ibm_db.bind_param(db_stmt,3,update_time)
    
    #SQL文の実行
    rtn = ibm_db.execute(db_stmt)
    
    #DB切断
    rc = ibm_db.close(db_conn)
    
    return { 'return-code': rtn }
```

引数を指定してアクションを実行するには、"起動"ボタンの左にある"パラメータを付けて起動"ボタンをクリックします。

下記のようなポップアップが表示されるので、引数情報をjson形式で記載します。記載した内容がmain関数の引数dictに格納されます。
今回、dictから'name'というキーで'Jiro'という値を取得するようにしたいので、
`{ 'name' : 'Jiro'}`
と入力して、"適用"ボタンを押します。
![22.ActionInput.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/168973c4-a17a-626f-98f3-8b3d5d9ba595.jpeg)

あとは、"起動"ボタンを押してアクションを実行します。

テスト用テーブルに正しく行が追加されていることを確認しておきます。
Db2コンソールを開き、左上の緑色三本線のアイコンをクリックし、RUN SQLを開きます。
![23.OpenSQLConsole.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/03bf9d9e-7059-ff27-dbff-231edfe585d6.jpeg)

SQL記述用のコンソールが開きます。ここにSQLを記述して左下の"Run all"を押すとSQL文が実行されます。
今回はテストテーブルの中身を確認したいため、
`SELECT * FROM TEST_TABLE;`
のSQLを実行してみたところ、Nameが'Jiro'の行が追加されているのが確認できました。
![24.RunSQL.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47734/91e84e44-0140-df96-bcf1-6a749e15564e.png)

## 終わりに
IBM Cloudを始めたばかりで、Liteアカウントでもこうやって動くものを作れるみたいです。クラウド環境のサーバーレス処理の体験にぴったりと思います。
今後、トリガー(時刻到来/ファイルのアップロード)でアクションを実行したり、処理結果を別のサービスに渡せるように拡張していきたいです。