まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:cloud

IBM Cloud から提供されているマネージドデータベースサービスの1つである PostgreSQL を使う機会がありました(MySQL は過去に使ったことありましたが、PostgreSQL でサービスを作ったのは初めてでした)。PostgreSQL をデータベースとする Node.js のウェブアプリケーションを開発するのが目的でしたが、最初の準備がちと独特だと感じたので次回戸惑わないようにその手順をまとめておきました:
2021020300



【サービス作成までの手順】
まずは IBM Cloud 内のダッシュボードで Database for PostgreSQL という名前のサービスを探して選択します。"PostgreSQL" という名前でいくつかのサービスが見つかりますが、他を選択しないように注意してください(なおこのサービスは有償サービスのみです。無料でのサービスプランは存在していません):
2021020301


"Standard" プランを選択し、必要に応じて容量などのパラメータを調整し、最後に "Create" ボタンでサービスを作成します:
2021020302


サービス作成が完了するとダッシュボードからも参照・選択できるようになります:
2021020303


【サービス作成後の作業、および手順】
Database for PostgreSQL サービスを実際に利用する(テーブルを作ったり、データを読み書きしたりする)ためには、その前にいくつかの準備段階が必要です:
1. 証明書のダウンロード
2. admin ユーザーのパスワード変更
3. 接続情報の確認


まずは証明書をダウンロードします(プログラムコードからデータベースに接続する際に必要です)。作成したサービスインスタンスの画面を開き、画面左の "Overview" メニューを選択します:
2021020304


"Overview" 画面を下にスクロールすると、エンドポイントに関する情報を参照できる画面が現れます。ここの "Quick start" タブ内に TLS 証明書の内容が表示されている箇所があります。その下の "Download Certificate" ボタンをクリックすると、同証明書の内容をテキストファイルでダウンロードできます:
2021020305

(後述の作業のため、ダウンロードしたファイルの名前を cert.crt と変更しておきます)

次に実際にデータベースを読み書きする際のログインユーザー(admin)のパスワードを設定します。画面左の "Setup" メニューを選び、"Change Password" 欄から新しいパスワードを設定できます。ここで設定したパスワードを使って PostgreSQL にログインできるようになります:
2021020306


最後に PostgreSQL へ接続するための接続情報を確認します。"Service credentials" メニューから "New credential" ボタンをクリックして新しい接続情報を作成します:
2021020307


作成した接続情報を展開して、以下3箇所の内容を確認しておきます:
2021020308

接続情報の場所値が意味するもの
connection.postgres.hosts.hostnameホスト名
connection.postgres.hosts.portポート番号
connection.postgres.databaseデータベース名


以上、この3点の作業を行うことで外部プログラムからデータベースに接続するための準備は完了しました。


【外部アプリケーションからデータベースに接続】
以下、Node.js を例として、プログラムから同データベースに接続してクエリーを発行するサンプルを紹介します。上述の準備段階で確認した内容を使って、以下のようなコードを記述して実行します:
var fs = require( 'fs' );
var PG = require( 'pg' );  //. node-postgres https://www.npmjs.com/package/pg

//. PostgreSQL
var pg_hostname = 'hostname';    //. connection.postgres.host.hostname の値
var pg_port = 35432;             //. connection.postgres.host.port の値
var pg_database = 'ibmclouddb';  //. connection.postgres.database の値
var pg_username = 'admin';       //. ユーザー名(固定値)
var pg_password = 'password';    //. 設定したパスワード

var connectionString = "postgres://" + pg_username + ":" + pg_password + "@" + pg_hostname + ":" + pg_port + "/" + pg_database;//+ "?sslmode=verify-full";
var caCert = fs.readFileSync( './cert.crt', 'utf-8' );  //. ダウンロードした証明書ファイル
var pg = new PG.Client({ 
    connectionString: connectionString,
    ssl: { ca: caCert, rejectUnauthorized: true }
});
pg.connect( function( err, client ){
  if( err ){
    console.log( 'err00', err );
  }else{
    var sql = 'select * from mytable where id = $1';
    var query = { text: sql, values: [ "123" ] };
    client.query( query, function( err, result ){
      if( err ){
        console.log( err );
      }else{
        console.log( result );
      }
    });
  }
});

今回は node-postgres という npm パッケージを使って PostgreSQL にアクセスしています。まず接続情報から取得した値を使って接続文字列を作成します。pg_username(ユーザー名)だけは "admin" で固定になりますが、それ以外の値は上述の作業で接続情報から参照したものや、自分で設定したパスワードを指定します。

次に接続する際に TLS 証明書が必要です。この証明書も上述の作業でダウンロードした TLS 証明書ファイル(cert.crt という名前でダウンロードしたと仮定しています)をファイルパスを指定して読み込み、接続時のパラメータとして含めています。こうすることで node-postgres 経由で証明書を使った接続が可能となります。

接続後は一般的な PostgreSQL 利用と同じですが、接続結果として得られた client オブジェクトを利用して SQL が実行できるようになる、というものです。


↑の「証明書ファイルを指定して接続する」という部分が(手順としては)少し特殊ですが、より安全な接続が実現できるようになるものです。



IBM Cloud の比較的新しい機能として、オブジェクトストレージ内の CSV ファイルに対して SQL を実行してレコードを抽出することができるようになりました。その新機能 SQL Query を紹介します。なお以下で紹介する手順は IBM Cloud のライトアカウント(無料版)の範囲内でも実施可能なので、興味ある方はぜひ本記事を参考に使ってみてください。
2021011600



この SQL Query はオブジェクトストレージ内の CSV ファイルを対象として実行するので、まずは(まだ利用していない場合は)クラウドオブジェクトストレージサービスを作成します。プランが「ライト」のものを選択して作成すれば一定条件内で無料で利用可能です:
2021011601


作成したオブジェクトストレージサービスの "Integrations" メニューを表示すると、このオブジェクトストレージサービスに統合されたサービスを確認できます。作成直後ではここには何もないはずですが、画面下部に「SQL Query を新規に作成して統合可能」と案内されています。この SQL Query のアイコンを選択することで、SQL Query (の無料版)を新規に作成して、このオブジェクトストレージと紐付けて利用することができるようになります:
2021011602


作成する SQL Query サービスの属性を設定する画面が表示されます。このプランとして「ライト」を選択すれば一定条件内で無料利用が可能です。最後に「作成」をクリック:
2021011603


SQL Query サービスが追加され、オブジェクトストレージと統合されて利用できるようになりました:
2021011604


また「バケット」メニューを参照すると、SQL Query 用のバケットが1つ追加されていることが確認できます。このバケットは必ずしも利用する必要はありませんが、SQL を実行する CSV ファイルや、SQL の実行結果をファイルとして残したい場合のファイル格納場所として利用できます。

今回はこの作成されたバケット内に CSV ファイルを置いて SQL を実行し、その実行結果ファイルもこのバケット内に作成する手順を以下で紹介します。というわけで、まずはこのバケット内に CSV ファイルを格納するため、バケット名部分を選択します:
2021011605


選択した(作成したばかりの)バケット内のファイル一覧が表示されますが、この時点ではまだ CSV ファイルは格納されていません:
2021011606


もし自分でお持ちの CSV ファイルを利用したい場合は、その CSV ファイルをこの一覧部分にドラッグ&ドロップしてアップロードしていただいても構いません。以下のサンプルでは以前に自分が全く別の目的でラズベリーパイの機器温度等を1秒おきに取得した際の CSV ファイルがあったので、このファイルを使って紹介することにします。

同 CSV ファイルは以下に公開しているので、以下ページを表示して、マウス右クリックから「名前を付けて保存」し、RPDATA.csv という名前で保存してください:
https://raw.githubusercontent.com/dotnsf/RPDATA/master/RPDATA.csv

2021011607
(1行目は列定義、2行目以降がデータで、左から ID、CPU 温度(℃)、CPU 負荷(%)、サイン値)


ダウンロードした RPDATA.csv をオブジェクトストレージ内のバケットにドラッグ&ドロップ(または「アップロード」ボタンからアップロード)して同バケット内に格納しておきます:
2021011608


これで SQL を実行する準備ができました。では SQL Query のサービスに移動します。改めてオブジェクトストレージのサービス画面から Integrations メニューを選択し、統合されている SQL Query サービス名部分を選択します:
2021011601


SQL Query サービス画面が表示されたら、画面右上の "Launch SQL Query UI" ボタンをクリックして UI 画面に移動します:
2021011602


以下のような IBM SQL Query の UI 画面が表示されます(↓この画面ではエラーメッセージが表示されていますが気にしないでください(苦笑)):
2021011603


画面上部に SQL を入力します。その際の from 直後のテーブル部を指定する箇所では、アップロードした CSV ファイルを指定します。CSV ファイルは Target location に表示されている文字列の、最後の result/ を除いて変わりに RPDATA.csv を指定し、最後に Run ボタンをクリックします:
2021011601
("select * from cos://us-south/XXXXXX/RPDATA.csv limit 10" と入力しました。一般的な SQL SELECT 文のテーブル名を指定する箇所にオブジェクトストレージ内の CSV ファイルを URI で指定する形になっています)


すると画面下部に SQL クエリーの実行結果が表示されます。特にテーブル定義をすることなく、CSV の1行目にかかれていた列情報をそのまま使って SELECT 文の実行ができることがわかります:
2021011602


またこの実行結果はもとのオブジェクトストレージのバケット内の新 CSV ファイルとしても保存されています。必要に応じてダウンロードするなどして結果を確認できます:
2021011603


オブジェクトストレージに格納された CSV ファイルに対して直接クエリーを実行できるので、IoT システムなどによって記録された CSV ファイルをバッチ処理でオブジェクトストレージ内に格納できてしまえすれば、(SQL 型のデータベースにインポートすることなく)その CSV ファイルの中から条件にあった列だけを取り出すことができる、ということがわかりました。実行結果もオブジェクトストレージ内のファイルとして生成することができるので、IoT システムなどでセンサーデータを集める場合でも「とりあえずは CSV にしてオブジェクトストレージに入れておけば、条件を指定した取り出しは後からできる」ことになって便利だと感じました。




※本ブログ記事は IBM Cloud アドベントカレンダー 2020 の 12/3 分にエントリーしています。


IBM Cloud のダッシュボードでユーザー権限周りの調べ物をしていると、見慣れないメニューに目が止まりました:
2020110601


「IP アドレスのアクセス制御」、と書かれています。現在の設定値は「無効」で「すべての IP アドレスからのアクセスを許可します」との説明が表示されています。 ということは、ここを「有効」にすると「アクセスできる IP アドレスに制約をかけることができる」と読めます。こんな機能があったことに気付いてなかった(苦笑)ので、この機会に実際の挙動を確認することにしてみました。

まずは準備として自分の環境での外部 IP アドレスを確認します。例えば確認くんあたりで自分の IP アドレスがどのように見えているかを調べます:
2020111601

(↑「あなたの IP アドレス」として表示されている内容を確認します)


自分の IP アドレスが分かったら、この値を使って IBM Cloud の「IP アドレスアクセス制御」を有効にしてみます。IBM Cloud にログイン後、メニューから「管理」→「アクセス(IAM)」を選択します:
2020111602


そして「アクセス(IAM)」画面の左ペインで「設定」を選ぶと「IP アドレスのアクセス制御」が変更できるようになります:
2020110601


ここの「無効」と表示されている部分をクリックすると、「IP アドレスのアクセス制御」ダイアログが表示されます。ダイアログ内では「有効」にチェックがついています。この「許可された IP アドレス」欄に先程確認した自分の IP アドレス(※)を入力し、最後に「保存」ボタンをクリックします:
2020111603


※単一の IP アドレスであればそのまま入力できます。これ以外にサブネットを指定して 192.168.0.0/16 形式で指定することもできますし、あるいは IP アドレスの範囲を指定して 192.168.0.1 - 192.168.0.100 と指定することもできます。


すると下図のように変更されました。これで IP アドレスによるダッシュボードへのアクセスが制御される状態になっているはずです(現在はアクセスが許可されている IP アドレスなので、このままダッシュボード内を操作することができます):
2020111604


確認するため一旦ログアウトします。そしてこれまで自宅の契約ネットワークから使っていた無線 LAN を、スマホのテザリング回線に切り替えます(IP アドレスが変わります)。そして、改めて IBM Cloud にログインしようとすると・・・
2020111605


無事に(?)アクセスを断られ、ログアウトするしか選択肢がない状態になりました。期待通りの挙動でした。

IBM Cloud と契約した後に、ダッシュボードを利用する人やそのネットワーク環境が限られているのであれば、その情報をここで有効にしておくことで想定していない利用者に勝手にダッシュボードを使われる、というリスクを軽減することができるようになります。会社のオフィスや固定回線の自宅からしか使わせないのであればそのネットワーク情報を登録するだけです。モバイル機器からのテザリングを許可させたいのであれば、(ネットワークの範囲を調べるのが大変そうですが)該当する IP アドレス範囲を指定できるのでダッシュボードへの不正なログインのリスクを軽減することができそうです。


詳しくは公式ドキュメントも参照ください:
https://cloud.ibm.com/docs/account?topic=account-ips#ips_account


(注 このブログを書いた時点では 2021/02/12 だった更新期限は 2021/05/25 に変更されました)


IBM Watson サービスのエンドポイント URL が更新され、2021年2月12日5月26日に旧URLが廃止される予定です:
IBM Watsonサービスのネットワーク分離機能拡張のためのIAMの更新


IBM Watson の各種サービス API を(以前から)使っていて、そのエンドポイント URL のホスト部分が *.watsonplatform.net というパターンになっている場合にアプリケーションが正しく動作しなくなるなどの影響を受けます。その場合は月の旧URL廃止前に後述の作業を行って、新しい URL に更新する必要があります。

以下、Watson NLC(Natural Language Classifier) を例に対応手順を含めて個人でまとめたので紹介します。NLC 以外のサービスでも概ね同様ですので参考にしてください。また詳しくは後述のリンク先も参照ください。


【現在使っている Watson API のエンドポイント URL を確認】
まず今回の作業は例えば Watson Assistant の画面を使って作業しているだけなど、外部アプリケーションから API を使って呼び出したりしていない場合は関係ありません。apiKey を指定してプログラミングで Watson API を外部から呼び出す形で利用しているケースが対象となります。

現在 Watson API を使ってアプリケーションを動かしている場合、まずはその API のエンドポイントが旧 URL を使っているのか新 URL を使っているのかを確認します(新 URL であれば後述の作業は不要です)。

例えば Watson NLC を使ったアプリケーションであれば、IBM Cloud にログインし、リソース画面のサービス一覧から利用している該当サービス(Watson NLC サービス)を選択します:
2020103001


選択したサービスの概要が表示される画面内に API Key と URL が表示されています(※)。この URL という部分に着目してください:
2020103002


上図の例では
  https://gateway-tok.watsonplatform.net/natural-language-classifier/api
と表示されている部分です。ここがこのように watsonplatform.net という文字を含んでいる場合は旧 URL を利用しています。一方、ここが api.*****.*****.watson.cloud.ibm.com というパターンになっている場合は新 URL を使っています。

※API Key と URL は「サービス資格情報」メニューからも確認できます。

ここで新 URL を使っていることが確認できた場合は後述の作業は不要です。旧 URL を使っている場合は続けて対処が必要です。


【変更先の Watson API 新エンドポイント URL を確認】
上述の作業で旧 URL を使っているアプリケーションは利用エンドポイント URL を新 URL に変更する必要があります。

まず新しい URL を確認するために新しいサービス資格情報を作成する必要があります。上述の作業に続けて画面左のメニューから「サービス資格情報(Service credentials)」を選択し、新しく資格情報を追加して作成します(その際に現在使っている資格情報と同じロールを指定して作成します):
2020103003


追加された資格情報の名前の左側にある小さな矢印をクリックして展開します:
2020103004


Watson サービスの種類によっても異なりますが、概ね以下のような JSON フォーマットの情報が含まれた内容になっています(一部 ***** でマスクしています):
{
  "apikey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "iam_apikey_description": "Auto-generated for key *********************",
  "iam_apikey_name": "Service credentials-1",
  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::********************::serviceid:ServiceId-*************************",
  "url": "https://api.jp-tok.natural-language-classifier.watson.cloud.ibm.com/instances/*************"
}

この JSON の中の url の値(上図では https://api.jp-tok.natural-language-classifier.watson.cloud.ibm.com/instances/************* )が新 URL です(実際の文字列パターンは使っている IBM Watson サービスの種類やロケーションによって異なります。また最後の ***** でマスクされている部分はインスタンス ID という個別の文字列となります)。アプリケーション内の旧 URL が使われている部分をこの新 URL に変更する必要があります。 また同時に旧アプリケーションで使われている apiKey も新しく作成したもの(上図の apikey で表示されている値 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX )に書き換える必要があります。


【変更作業】 
ここまでの作業で変更が必要な箇所と、変更後の値がわかりました。アプリケーションのソースコードを編集し、旧 URL (上述例では https://gateway-tok.watsonplatform.net/natural-language-classifier/api)が使われている部分を新 URL (上述例では https://api.jp-tok.natural-language-classifier.watson.cloud.ibm.com/instances/*************)に、また古い apiKey が使われている部分を新しい apiKey の値(上述例では XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX)に、それぞれ書き直して保存し、新しいコードで動作確認をしてください。

なお、NLC のように学習が必要な API も再学習の必要はありません。学習済みのデータへそのまま問い合わせが利用できるはずです。


以上、個人でまとめたものですが、背景や詳細な情報はソリューションブログや IBM Cloud Document に記載されています。以下情報も参考にしてください:
IBM Watsonサービスのネットワーク分離機能拡張のためのIAMの更新
Updating endpoint URLs from watsonplatform.net(英語)

このフローは以前に少し違う形で業務やイベントのネタ(の裏側)として使っていたことがあったのですが、埋もれてしまうのはもったいない気がしたので公開しちゃいます。

Node-REDFX (外国為替)情報を取得するフローを作りました。FX というのは例えば USDJPY だと USD と JPY 、つまり米ドルと日本円の関係です。「1ドル=107円23銭」みたいなやつですね。これの EURUSD (ユーロドル)やら EURJPY (ユーロ円)やら AUDJPY (豪ドル円)やら、、主に日本円が絡む通貨ペアを中心に 20 ペアの情報を1分おきにリアルタイムで取得するものです。

フローはこちらで公開しています:
https://flows.nodered.org/flow/9d045f691b6d7c5cb3259c197ad365d0

2020060105



このページ内のフロー定義を "Copy" して、Node-RED 環境に「クリップボードから読み込み」するだけでフローが再現できます。フロー1本だけの、それも標準ノードの組み合わせだけで構成されているシンプルな内容です。動く条件は「Node-RED 環境がインターネットに接続されていること」だけでいけると思います:
2020060101


↓ペースト後、こんなフローのタブが作られていれば成功:
2020060102


あとはこのまま「デプロイ」すれば1分おきに inject ノードが動き出し、取得した FX 情報を debug タブに出力し続けます:
2020060103


1回実行した時の debug タブの様子はこんな感じです。_id に実行時の日付時刻が入り、あとは通貨ペアとその瞬間の価格がまとめて出力されます:
2020060100



公開しているフローではこれだけ(debug タブに出力するだけ)ですが、IBM Cloud 内の Node-RED として動いている環境であれば、バインド済みの Cloudant out ノードをフローの最後に追加して、DB 名を指定するだけで出力される情報を1つのレコードとして DB に格納する所まで簡単に実現できます。他の環境でも各種データベースノードに渡すことで取得データの DB 格納ができます:
2020060104


中身は inject node が一分ごとに発火してオープンな API を使って FX 相場を取得し、(Cloudant DB に格納する前提での)JSON フォーマットに変換して debug ノードに渡す、というものです。FX は24時間相場が動くので、1日に 60 * 24 = 1440 データ集まります。(データ量に気をつけながら)1ヶ月程度動かしっぱなしにしておくとそこそこの為替情報データベースができあがります。シンプルですが API やフォーマットを変えることで応用範囲が広くなりそうだと思っています。

本来は集まったデータをグラフ表示したり、上下動の予測をしたり、、、といった使いみちになると思っています。サンプルではない実データを簡単に集めることができるので、説得力のあるデモアプリに応用しやすいと思っています。興味ある方は使ってみてください。


このページのトップヘ