mlflow tracking serverのバックエンドストアをIBM CloudのPostgreSQLとObject Storageにしてみた

# 概要
mlflowのtracking serverのバックエンドストアにIBM Cloudのサービスを設定する手順を記載します。
以下の図のような構成を構築します。
backend-storeは、Databases for PostgreSQLで、artifactの格納先は、Cloud Object Storageを
使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/0bc49786-4dc1-5e89-d105-b114849bc653.png)


# 手順
## 手順概要
1. ibmcloudコマンドのセットアップ
2. Databases for PostgreSQLを作成
3. Cloud Object Storageを作成
4. mlflow tracking serverを起動
5. 実験結果書き出し
6. IBM Cloudへの書き出しを確認

## 1. ibmcloudコマンドのセットアップ

以下の手順に従いibmcloudコマンドをインストールします。

手順： スタンドアロン IBM Cloud CLI のインストール
https://cloud.ibm.com/docs/cli?topic=cli-install-ibmcloud-cli&locale=ja

Cloud Databaseのプラグインをインストールします。

```
$ ibmcloud plugin install cloud-databases -r 'IBM Cloud' 
```

IBM Cloudにログインして、リソースグループを設定します。

```
$ ibmcloud login --sso       # ログイン
$ ibmcloud resource groups   # リソースグループをリスト
$ ibmcloud target -g Default # リソースグループを設定
```

## 2. Databases for PostgreSQLを作成
ibmcloudコマンドでサービスインスタンスの作成を行います。
作成後、adminのパスワードを設定して、接続出来ることを確認します。

```
$ ibmcloud catalog service databases-for-postgresql # サービスIDとプランIDを確認
$ ibmcloud resource service-instance-create postgreSQL2 databases-for-postgresql standard jp-tok  # サービスインスタンスを作成
$ ibmcloud resource service-instances # サービスインスタンスがステータスを確認。アクティブになるまで待つ
$ ibmcloud cdb ls  # デプロイメントをリスト
$ ibmcloud cdb user-password postgreSQL2 admin  # adminのパスワードを設定
User Password>> 
Confirm User Password>> 
$ ibmcloud cdb cxn postgreSQL2 --start   # 接続テスト、psqlがないと言われたらpostgreSQLのクライアントを入れます
Database Password>> 
psql (13.1, server 12.5)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

ibmclouddb=> select user;
 user  
-------
 admin
(1 row)

ibmclouddb=> create table t1(c1 int);
CREATE TABLE
ibmclouddb=> drop table t1;
DROP TABLE
ibmclouddb=> \q
```

## 3. Cloud Object Storageを作成
ibmcloudコマンドを使ってサービスインスタンスの作成を行います。
その後、バケットの作成を行います。

```
$ ibmcloud catalog service cloud-object-storage  # サービスIDとプランIDを確認
$ ibmcloud resource service-instance-create cos1 cloud-object-storage standard global  # サービスインスタンスを作成
$ ibmcloud resource service-instance cos1  # crnを確認
$ ibmcloud cos config crn # crnを設定
$ ibmcloud cos bucket-create --bucket tsota-bucket --region ap # バケットを作成
$ ibmcloud cos buckets  # バケットを確認
```

aws cliをインストールします。
参考：Linux で AWS CLI バージョン 2 をインストールする
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-linux.html#cliv2-linux-install

```
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
$ aws --version
```

aws cliからアクセスできるようにサービスキーを作成して、Access KeyとSecret Access Keyをaws clieに設定します。

```
$ ibmcloud resource service-key-create tsota-cos1-key "管理者" --instance-name cos1 --parameters '{"HMAC":true}' # サービスキーを作成
$ ibmcloud resource service-key tsota-cos1-key  # サービスキーを確認
$ aws configure # 上記で確認したAccess KeyとSecret Access Keyを設定
$ aws --endpoint-url https://s3.ap.cloud-object-storage.appdomain.cloud s3 ls # バケットが参照できることを確認
```

## 4. mlflow tracking serverを起動
Databases for PostgreSQLの接続情報を確認します。

```
$ ibmcloud cdb ls  # デプロイメントをリスト
$ ibmcloud cdb cxn postgreSQL2  # 接続情報をリスト
Retrieving public connection strings for postgreSQL2...
OK

Type         Connection String   
PostgreSQL   postgres://admin:$PASSWORD@xxxx:30731/ibmclouddb?sslmode=verify-full   
CLI          PGPASSWORD=$PASSWORD PGSSLROOTCERT=/User/xxxx/.bluemix/plugins/cloud-databases/certificates/xxxxx psql 'host=xxxxx port=30731 dbname=ibmclouddb user=admin sslmode=verify-full'   
(base) tsota@tsotanoMacBook-Air mlflow % 

```

上記で確認した情報をもとに、PGSSLROOTCERTの設定と--backend-store-uriの設定を行います。
以下のように環境変数とmlflowのコマンド引数を設定します。

```
$ export MLFLOW_S3_ENDPOINT_URL=https://s3.ap.cloud-object-storage.appdomain.cloud
$ export PGPASSWORD=xxxxxx
$ export PGSSLROOTCERT=/User/xxxx/.bluemix/plugins/cloud-databases/certificates/xxxxx
$ mlflow server \
--backend-store-uri "postgresql://admin:${PGPASSWORD}@xxxxxxx:xxxx/ibmclouddb?sslmode=verify-full" \
--default-artifact-root "s3://tsota-bucket/"
```

## 5. 実験結果書き出し

以下のようなプログラムを作成してTracking Serverへのデータの書き出しのテストを行います。
参考： https://www.mlflow.org/docs/latest/quickstart.html

```python:test.py
import os
from random import random, randint
from mlflow import log_metric, log_param, log_artifacts

if __name__ == "__main__":
    # Log a parameter (key-value pair)
    log_param("param1", randint(0, 100))

    # Log a metric; metrics can be updated throughout the run
    log_metric("foo", random())
    log_metric("foo", random() + 1)
    log_metric("foo", random() + 2)

    # Log an artifact (output file)
    if not os.path.exists("outputs"):
        os.makedirs("outputs")
    with open("outputs/test.txt", "w") as f:
        f.write("hello world!")
    log_artifacts("outputs")
```

MLFLOW_TRACKING_URIとMLFLOW_S3_ENDPOINT_URLの環境変数を設定して、プログラムを実行します。

```bash:
$ export MLFLOW_TRACKING_URI=http://localhost:5000
$ export MLFLOW_S3_ENDPOINT_URL=https://s3.ap.cloud-object-storage.appdomain.cloud
$ python test.py
```

ブラウザーからhttp://localhost:5000 にアクセスします。
実験結果が記録されていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/715873d9-2fc0-d6ea-9bfc-548b50870d17.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103795/b7f453c8-8d63-41d3-b2ae-263a5e8e70f1.png)

aws cliとibmcloudコマンドでバケットを参照して、データがCloud Object Storage格納されていることを確認します。

```bash
$ aws --endpoint-url https://s3.ap.cloud-object-storage.appdomain.cloud s3 ls s3://tsota-bucket --recursive
2020-12-12 18:14:16         12 0/5e1c1d3db8da4cc98cf80abd017b73fa/artifacts/test.txt

$ ibmcloud cos objects --bucket tsota-bucket --region ap
OK
見つかりました 2 バケットにオブジェクトがあります 'tsota-bucket':

名前                                                    最終変更日時               オブジェクト・サイズ   
0/5e1c1d3db8da4cc98cf80abd017b73fa/artifacts/test.txt   Dec 12, 2020 at 09:14:16   12 B   

```

PostgreSQLにデータが格納されていることを確認します。

```bash
$ ibmcloud cdb ls # デプロイメントをリスト
$ ibmcloud cdb cxn postgreSQL2 --start  # データベースに接続
Database Password>> 
psql (13.1, server 12.5)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

ibmclouddb=> \dt
               List of relations
 Schema |         Name          | Type  | Owner 
--------+-----------------------+-------+-------
 public | alembic_version       | table | admin
 public | experiment_tags       | table | admin
 public | experiments           | table | admin
 public | latest_metrics        | table | admin
 public | metrics               | table | admin
 public | model_version_tags    | table | admin
 public | model_versions        | table | admin
 public | params                | table | admin
 public | registered_model_tags | table | admin
 public | registered_models     | table | admin
 public | runs                  | table | admin
 public | tags                  | table | admin
(12 rows)

ibmclouddb=> select * from runs;
             run_uuid             | name | source_type | source_name | entry_point_name | user_id |  status  |  start_time   |   end_time    | source_version | lifecycle_stage |                          artifact_uri                          | experiment_id 
----------------------------------+------+-------------+-------------+------------------+---------+----------+---------------+---------------+----------------+-----------------+----------------------------------------------------------------+---------------
 bd4839dc1361485ea77f2d48424814e9 |      | UNKNOWN     |             |                  | tsota   | FINISHED | 1607763915418 | 1607763921939 |                | active          | s3://tsota-bucket/0/bd4839dc1361485ea77f2d48424814e9/artifacts  |             0
 75b80ba3ea7d4884ac14103df8cc08c1 |      | UNKNOWN     |             |                  | tsota   | FINISHED | 1607764075123 | 1607764080151 |                | active          | s3://tsota-bucket/0/75b80ba3ea7d4884ac14103df8cc08c1/artifacts  |             0
 39b7125319e34c20988c5a49fd37f9b7 |      | UNKNOWN     |             |                  | tsota   | FINISHED | 1607764202187 | 1607764208573 |                | active          | s3://tsota-bucket/0/39b7125319e34c20988c5a49fd37f9b7/artifacts  |             0
 f4a4d5387af74656b29fe6f7e7f5379c |      | UNKNOWN     |             |                  | tsota   | FINISHED | 1607764373330 | 1607764384498 |                | active          | s3://tsota-bucket/0/f4a4d5387af74656b29fe6f7e7f5379c/artifacts |             0
 5e1c1d3db8da4cc98cf80abd017b73fa |      | UNKNOWN     |             |                  | tsota   | FINISHED | 1607764451430 | 1607764456363 |                | active          | s3://tsota-bucket/0/5e1c1d3db8da4cc98cf80abd017b73fa/artifacts |             0
(5 rows)

(END)

```

以上。