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

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

タグ:cloudant

CouchDBCloudant というデータベースに対してブラウザの JavaScript から(API サーバーなどを使わずに)直接アクセスできるような SDK を作ってみました。


CouchDB CRUD SDK について】
CouchDB は現在は Apache CouchDB としてオープンソースで開発されている JSON ドキュメント型の NoSQL データベースです。そして Cloudant はその CouchDB を IBM からのサポート付きで IBM Cloud から提供されているものです。要するに CouchDB と Cloudant は実体としては同じものです。ということもあって、以下では両方を指す言葉として "CouchDB" という表現をします(Cloudant を使う場合は "Cloudant" と読み替えてください):
2024090900


CouchDB には REST API が提供されています。したがって CORS に注意する必要はありますが、ウェブブラウザの JavaScript とこの REST API だけでデータベースやドキュメントの読み書き更新削除(=CRUD、Create Read Update Delete)といった処理そのものは可能です。ただ REST API を実行するのはパスやパラメータの指定で直感的に分かりにくい点があったり、検索方法や CouchDB の特徴ともいえる添付ファイル対応(バイナリデータ対応)の REST API は更に複雑だったりするので、私自身も使うたびに調べる必要に迫られたりと、少々使いにくい点があると感じていました。そういった使い勝手の点も含めて分かりやすい JavaScript での SDK を提供できないかと思いついて作ってみたものです。

作成した CouchDB CRUD SDK 自体は README.md 含めて GitHub で公開しています:
https://github.com/dotnsf/couchdb-crud-sdk


SDK の説明(英語)も GitHub に含まれる SDK.md で紹介していますが、せっかくなのでこのブログでは日本語で使い方を紹介します。


CouchDB CRUD SDK を使う前提条件】
前提条件というか、当然条件というか、使う対象となる CouchDB(Cloudant) サーバーが必要です。この CouchDB サーバーはインターネットからアクセスできる必要はありませんが、クライアントとなるウェブブラウザを使う PC がインターネットに接続していない場合は後述の CDN が使えないため、SDK の JavaScript ファイルをあらかじめダウンロードするなどしておく必要があります。

またここで用意する CouchDB サーバーはユーザー ID とパスワードで(ベーシック認証で)アクセスできるように構築されている必要があります。API キーを使う方法には未対応です。

自由に使える CouchDB サーバーを持っていない場合に備えて、以下では2つの方法で CouchDB サーバーを用意する方法を紹介します:

(1)docker のコンテナとして用意する
自分の PC に docker をインストールしてある場合であれば以下のコマンドを実行するだけで CouchDB サーバーを localhost:5984 で(ユーザーID=user、パスワード=pass で)動かすことができます:
$ docker run -d --name couchdb -p 5984:5984 -e COUCHDB_USER=user -e COUCHDB_PASSWORD=pass

(2)IBM Cloud のアカウントをお持ちであれば、lite plan という無料枠の範囲内で使える Cloudant サーバーを1アカウントにつき1つだけ使うことができます。ちなみに Cloudant の lite plan で使える容量は 1GB です(その他の条件は下図参照)。バイナリデータなどの膨大な容量のファイルを添付したりしなければ、そこそこ使えるサイズだと思っています:
2024090800


IBM Cloud 内の Cloudant サーバーを作成した場合のデータベースサーバーのホスト名、ユーザーID、パスワードなどの情報は「サービス資格情報」タブ内の下図の変数として確認することができます:
2024090802


なお、この方法で Cloudant サーバーを用意する場合は認証方法として "IAM and legacy credential" を選択してください(この "legacy credential" がベーシック認証です。"IAM" のみだとベーシック認証には非対応です)
2024090801


IBM Cloud のアカウントをこれから作る場合はこちらから登録してください(アカウント作成時にクレジットカードの登録が必要ですが、有料サービスを使わなければ課金対象にはなりません)。

もう1つの前提条件として外部の JavaScript から CouchDB サーバーを利用できるように CORS を正しく設定している必要があります。CouchDB の設定画面内で CORS タブから CORS が enalbed になっていることを確認してください。また CORS 対象のオリジンについては "All domains" を選択するか、"Restrict to specific domains" を選択した上で、この後動かすことになるウェブページのオリジン(URL の "スキーマ://ホスト:ポート" の部分)を指定してください。ここが正しく設定できていないと CORS の制約にかかって JavaScript SDK が正しく実行できなくなります:
2024090802


CouchDB CRUD SDK のロードと初期化】
実際にデータの読み書きをする前に SDK ライブラリをロードして初期化する必要があります。その流れを説明します:

まずブラウザ内に以下の1行を加えて、CDN の SDK ライブラリをロードします:
<script src="https://dotnsf.github.io/couchdb-crud-sdk/couchdb-crud-sdk.js"></script>

インターネットに接続していない環境で使う場合は上記 URL から couchdb-crud-sdk.js をあらかじめダウンロードして、HTML と同じフォルダにコピーするなどして(その場合は src="./couchdb-crud-sdk.js" と指定)対応してください。

次にユーザー ID、パスワード、CouchDB サーバーのベース URL を指定して CouchDB_CRUD_SDK クラスのインスタンスを初期化します:
var cdb = new CouchDB_CRUD_SDK( username, password, base_url );

これでインスタンス変数 cdb の初期化が完了しました。この変数を使うことで CouchDB サーバー内のデータを読み書きできます。


【CouchDB CRUD SDK を使ったデータベースの作成と削除と一覧取得】
上述の初期化まで完了していればインスタンス変数(cdb)のメソッドでリモートの CouchDB に対する各種処理が実行できます。例えば CouchDB のデータベースに関しては以下のようなメソッドが用意されています:
//. データベース一覧取得
var result0 = await cdb.readAllDbs();

//. データベース新規作成
var result1 = await cdb.createDb( 'newdb' );

//. データベース削除
var result2 = await cdb.deleteDb( 'newdb' );

後述のドキュメント向けメソッドについてもいえることですが、メソッドの実行結果は成功した場合は
{
  status: true,
  result: (実行した結果のオブジェクトや配列)
}

といった JSON が返されます。また失敗した場合は
{
  status: false,
  error: (失敗した原因の内容を示すオブジェクトや文字列)
}

という JSON になります。status の true/false で成功か失敗かを判断し、成功の場合は(必要であれば)result の内容を参照、失敗の場合は error の内容に失敗の理由が記載されているのでそれぞれ参照して処理することができます。 なお、cdb のメソッドは全て非同期(async)関数として定義されている点に注意してください。


【CouchDB CRUD SDK を使ったドキュメントの操作】
次にデータベース内のドキュメントに関しては以下のようなメソッドが用意されています:
//. データベース内のドキュメント一覧取得
var result3 = await cdb.readAllDocs( 'db' );

//. ドキュメント新規作成
var result4 = await cdb.createDoc( 'db', doc );

//. ドキュメント一件取得
var result5 = await cdb.readDoc( 'db', 'doc_id' );

//. ドキュメント更新
var result6 = await cdb.updateDoc( 'db', 'doc_id', doc );

//. ドキュメント削除
var result7 = await cdb.deleteDoc( 'db', 'doc_id' );

createDoc や updateDoc のパラメータで指定されている doc オブジェクトは
{
  _id: 'doc_id',
  name: "ジュース",
  price: 120
}

といった JSON オブジェクトです(_id の値はいわゆるドキュメント ID です。新規作成時であれば指定されていてもされていなくても構いません、指定がない場合は自動的に付与されます)。

CouchDB の特徴的な機能を使ったメソッドについても紹介しておきます。例えば以下のようなメソッドも用意されています:
//. 特定ドキュメントの全リビジョン取得
var result8 = await cdb.readAllRevisions( 'db', 'doc_id' );

//. 添付ファイルをドキュメントとして保存する
var result9 = await cdb.saveFile( 'db', 'doc_id', 'selector', 'filename' );

上は特定の ID ('doc_id')を持ったドキュメントの全リビジョンを取得するメソッドです。CouchDB はドキュメントの更新履歴が全て(自動的に)リビジョンとして記録されています。つまりドキュメントを新規作成し(リビジョン1)、一度更新して(リビジョン2)、更に更新(リビジョン3)した場合、最新データはリビジョン3ですが、過去のリビジョンも全て記録されていて、取り出すことができます。上の readAllRevisions() メソッドは特定ドキュメントの全リビジョンを一度の取り出すメソッドです。

下は添付ファイルを JSON データの一部として記録する場合のメソッドです。例えば
<input type="file" id="attachment_file"/>

のような HTLM 要素を使って添付ファイルを読み込もうとした場合であれば、'selector' は "#attachment_file" となります(jQuery などのセレクタだと思ってください)。'filename' は指定されていればそのファイル名で保存されます('filename' 指定がなかった場合は実際のファイル名で保存されます)。なお 'doc_id' が指定されている場合はその ID のドキュメントを更新し、'doc_id' が指定されていない場合は新規にドキュメントを作成します。 この関数を使って(バイナリデータなどの)添付ファイルもブラウザの JavaScript でリモートデータベースに格納/更新できるようになります。またこの添付ファイルの更新記録もリビジョンとして記録されます。

一応ここに挙げたメソッドで一通りの読み書き更新削除まではカバーしていると思いますが、その他全てのメソッドを(最新情報として)参照したい場合はこちらを参照ください(英語です):
https://github.com/dotnsf/couchdb-crud-sdk/blob/main/SDK.md


【CouchDB CRUD SDK のサンプル】
この CouchDB CRUD SDK を比較的汎用的に使ったサンプルアプリケーションを GitHub Pages に用意しておきました。docker コンテナであっても CouchDB/Cloudant 環境があれば試しにアクセスすることができます:
https://dotnsf.github.io/couchdb-crud-sdk/


上記 URL にアクセスすると以下のような画面が表示されます:
2024090901


上部に3つのテキストフィールドがあり、左からユーザーID、パスワード、CouchDB の URL (docker であれば http://localhost:5984 など)を入力し、最後に Login ボタンをクリックします:
2024090902


入力した内容が正しい場合は指定された CouchDB にアクセスし、画面左にデータベースの一覧が表示されます。またログインフィールドが画面から消えます(ログインフィールドが残っている場合はログインに失敗しています)。この画面から新しくデータベースを作成(Create DB)することもできます:
2024090903


データベースを1つ選択すると、そのデータベース内のドキュメント一覧が画面右側に表示されます。この部分からは選択したデータベースの削除(Delete DB)、ドキュメントの新規作成(Create Doc)や、既存ドキュメントの参照(Show Doc)、変更(Edit Doc)、削除(Delete Doc)といったアクションを実行できます:
2024090904


以下は "Create Doc" ボタンをクリックしてドキュメントを新規作成している画面です:
2024090905


ドキュメントの内容は JSON 形式であればどのようなフォーマットでも指定可能です。最後に "Save" で保存します:
2024090906


(わかりにくいかもしれませんが)新たに1つドキュメントが追加されました(赤枠):
2024090907


このドキュメントの横にある "Show Doc" ボタンをクリックするとドキュメントの内容が表示されます。実際に入力した JSON の内容に加えて、"_id" や "_rev" といったキーとその値が追加されているのがわかります。これらが(自動的に付与された)ドキュメント ID や、そのリビジョンです。同様にして編集や削除も可能です:
2024090908


このサンプルアプリでできることはこの程度ですが、ブラウザの JavaScript だけでリモートのデータベース内を読み書きできていることがわかると思います。もちろんデータベースを読み書きする REST API があれば(CORS の設定もできていれば)普通にできることではあるんですが、REST API を意識しなくても関数を呼び出すだけでできるようにしたのはまあまあ便利かなと自分でも思っています。API サーバーなしでも動くフロントエンドアプリケーションの SDK という位置付けであり、それが実現できているので現に GitHub Pages でこのサンプルが作れているわけです。

サンプルアプリの内容を見たい場合はこちらを参照ください(index.html, viewer.js, viewer.css の3つのファイルで作られているウェブアプリケーションです):
https://github.com/dotnsf/couchdb-crud-sdk/tree/main/docs


【最後に】
・・・というものを作って公開してみました。ローカルストレージではなくリモートデータベースをブラウザの JavaScript だけで(比較的簡単に)操作できることと、CouchDB の持つリビジョン管理機能や添付ファイル管理まで含めて操作できる点が特徴的なライブラリかな、と思っています。

CouchDB や Cloudant といったデータベースに馴染みのない人もいるかもしれませんが、この記事をきっかけに知ったり興味持ったりしてもらうことがあれば嬉しいです。


本ブログエントリは IBM Cloud アドベントカレンダー 2021 に参加しています。12/4 ぶんのネタです。


IBM Cloud から提供されているマネージド NoSQL データベースである IBM Cloudant をよく使っています。最大の魅力は無料アカウントでも容量 1GB まで使えるストレージで、かつデータが分散管理されているという安全性です。仕様が曖昧な状態からアジャイルに作り始める際は(テーブル定義などを意識する必要がないこともあって)むしろ NoSQL データベースの方が便利だったりもします。

この IBM Cloudant を Node.js プログラムから使う場合のライブラリとして長く @cloudant/cloudant を使ってきましたが、2021/12/31 を以って End of Support を迎えることになり、現在は deprecated なライブラリ、という扱いになっています。2021年8月の今は「まだ使えるけど、もうすぐ使えなくなるよ」って所でしょうか。可能であれば、これから新たに作り始める時には利用を避けたほうが安全だと思います。

で、その後継ライブラリとして公開されているのが @ibm-cloud/cloudant (IBM Cloudant Node.js SDK)です。IBM Cloudant だけでなく、そのベースとなっている Apache CouchDB に対してももちろん使うことができるものです。現在 @cloudant/cloudant ライブラリを使っているアプリケーションはなるべく今年中にこの新しい @ibm-cloud/cloudant に移植することをおすすめします。

・・・と言うのは簡単ですが、実際この @ibm-cloud/cloudant は @cloudant/cloudant と比べてどのくらい似ていて/同じで、移植はどの程度簡単/難しいのでしょう? というわけで、まずは @ibm-cloud/cloudant を使ってみることにしました。


【サンプルソースコード】
以下で紹介するサンプルアプリケーションのソースコードを github で公開しています:
https://github.com/dotnsf/cloudant-node-sdk_sample


【接続方法の決定】
@ibm-cloud/cloudant を使って Cloudant(CouchDB) にアクセスする場合の接続方法には3通りあります:
(1)IAM
(2)COUCHDB_SESSION
(3)BASIC


(1)の IAM は IBM Cloudant を IAM 接続サポート形式で作成した場合に利用できる方法です。公式ドキュメントでも「この方法が使える場合はこの方法で」と紹介されています。

(2)の COUCHDB_SESSION を使った方法でも接続が可能です。

(3)の BASIC はいわゆる「ベーシック認証」です。この3つの中では「この方法しか使えない場合の選択肢」と紹介されています。CouchDB を自分でインストールして使う場合など、この方法で接続する前提のセットアップをしているとこの方法でしか接続できないことになります。もちろん Cloudant でもベーシック認証をサポートした形式で作成している場合は利用可能です。

(1)、(2)、(3)のいずれの方法でアプリケーションとデータベースを接続するかを決めておく必要があります。正確には @ibm-cloud/cloudant は「環境変数を設定して接続する」のですが、どの方法で接続するかによって、設定が必要になる環境変数が変わる点に注意が必要です。


【Node.js から接続する際に必要な環境変数】
@ibm-cloud/cloudant を使って Cloudant/CouchDB に接続する際に設定が必要な環境変数は以下です(横のカッコ付き数字は、上述の(1)、(2)、(3)のどの方法を使った時に必要な環境変数か、を表しています)。また変数名の頭の "CLOUDANT" 部分は別の値でも構いませんが、同じ文字列に統一して設定する必要があります:

変数意味
CLOUDANT_AUTH_TYPE上述の接続方法。"IAM"(1), "COUCHDB_SESSION"(2), "BASIC"(3) のいずれか。デフォルト値は "IAM"
CLOUDANT_URLデータベースの URL((1)、(2)、(3))
CLOUDANT_APIKEYAPI キーの値((1))
CLOUDANT_USERNAMEユーザー名((2)、(3))
CLOUDANT_PASSWORDパスワード((2)、(3))


環境に合わせてこれらの値を用意して実際に接続してみます。


【Node.js から Cloudant に接続】
(1)IAM 接続を行う場合は、上述の表より以下の環境変数を設定します:
process.env['CLOUDANT_AUTH_TYPE'] = 'IAM'; //(デフォルト値なので設定しなくてもよい)
process.env['CLOUDANT_APIKEY'] = 'xxxxx'; //(API Key の値)
process.env['CLOUDANT_URL'] = 'https://xxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud'; //(Cloudant のURL)

そして @ibm-cloud/cloudant ライブラリを読み込んで、以下のコードを実行します:
var { CloudantV1 } = require( '@ibm-cloud/cloudant' );

var client = CloudantV1.newInstance( { serviceName: 'CLOUDANT' } ); 

このコード実行が成功すると、Cloudant に接続したインスタンスが client という変数に格納され、実際のデータの CRUD 処理が可能になります。

なお上記コードの serviceName 値として 'CLOUDANT' を指定していますが、この部分は変更可能です。ただ変更する場合は環境変数として指定した変数名の頭の CLOUDANT 部分をここで指定する値と同じものに変更してください。

(2)COUCHDB_SESSION 接続を行う場合は、上述の表より以下の環境変数を設定します:
process.env['CLOUDANT_AUTH_TYPE'] = 'COUCHDB_SESSION';
process.env['CLOUDANT_USERNAME'] = 'username'; //(ユーザー名の値)
process.env['CLOUDANT_PASSWORD'] = 'password'; //(パスワードの値)
process.env['CLOUDANT_URL'] = 'https://xxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud'; //(Cloudant/CouchDB のURL)

そして @ibm-cloud/cloudant ライブラリを読み込んで、以下のコードを実行します:
var { CloudantV1 } = require( '@ibm-cloud/cloudant' );

var client = CloudantV1.newInstance( { serviceName: 'CLOUDANT' } ); 

(3)BASIC 接続を行う場合は、上述の表より以下の環境変数を設定します:
process.env['CLOUDANT_AUTH_TYPE'] = 'BASIC';
process.env['CLOUDANT_USERNAME'] = 'username'; //(ユーザー名の値)
process.env['CLOUDANT_PASSWORD'] = 'password'; //(パスワードの値)
process.env['CLOUDANT_URL'] = 'http://xxx.xxx.xxx.xxx:5984'; //(Cloudant/CouchDB のURL)

そして @ibm-cloud/cloudant ライブラリを読み込んで、以下のコードを実行します:
var { CloudantV1 } = require( '@ibm-cloud/cloudant' );

var client = CloudantV1.newInstance( { serviceName: 'CLOUDANT', disableSslVerification: true } ); 

なお(CouchDB の場合に多いと想像していますが) SSL 接続が不要の場合は上述のように接続時のパラメータに disableSslVerification: true を追加してください。

参考までに、@cloudant/cloudant の場合は、
var Cloudantlib = require( '@cloudant/cloudant' );
var cloudant = Cloudantlib( { account: "username", password: "password", url: 'https://xxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud' } );
var db = cloudant.db.use( "dbname" );

といった感じでデータベースまでを取得した上で、
db.get( "id", function( err, result ){
  if( err ){
    console.log( err );
  }else{
    console.log( result );
  }
});

のようにして特定データを取得したりしていましたが、この @ibm-cloud/cloudant ではまだデータベースが特定されていない点にご注意ください。


【接続後の CRUD 処理の例】
では接続後の Cloudant クライアントを使って、IBM Cloudant のデータを読み書きしてみましょう。まず対象データベースと id がわかっている特定データを取得してみます。@ibm-cloud/cloudant では以下のような getDocument() メソッドを使うコードとなります:
client.getDocument( { db: "dbname", docId: "id" } ).then( function( result ){
  console.log( result );
}).catch( function( err ){
  console.log( err );
});

client.getDocument() を実行し、そのパラメータ内でデータベースと id を指定する、という方式になります。この点からして @cloudant/cloudant とは異なってますね。

また特定データベースの全データをまとめて取得(@cloudant/cloudant だと db.list() )する場合は以下のような postAllDocs() メソッドを使うコードです:
client.postAllDocs( { db: "dbname", includeDocs: true } ).then( function( result ){
  console.log( result );
}).catch( function( err ){
  console.log( err );
});

データを一件追加する場合は以下のような postDocument() メソッドを使うコードになります:
client.postDocument( { db: "dbname", document: { name: "Kimura" } } ).then( function( result ){
  console.log( result );
}).catch( function( err ){
  console.log( err );
});

以上、あくまでいくつかのメソッドを紹介しただけですが、全般的に行うオペレーションとメソッド名の関係がわかりやすく整頓されているように感じて、比較的慣れやすいのではないかと感じています。



IBM Cloud から提供されている 30 日間無料 Kubernetes サービスIBM Kubernetes Service 、以下 "IKS")環境を使って利用することのできるコンテナイメージを1日に1個ずつ 30 日間連続で紹介していきます。

環境のセットアップや制約事項については Day0 のこちらの記事を参照してください。

Day 6 からはデータベース系コンテナとその GUI ツールを中心に紹介してます。Day 16 では個人的に大ファンな IBM Cloudant データベースサービスのベース製品でもあるApache CouchDB イメージをデプロイする例を紹介します。
couchdb



【イメージの概要】
元 IBM 社員だった Damien Katz さんが開発した NoSQL 型データベースです。単にデータを格納するだけでなく、画面の UI データを格納して文書データと合わせて表示する、といった(ノーツに似た)デザインドキュメントの概念も実装されていて、ノーツファンとしては非常に感銘を受けるデータベースです。IBM Cloudant ではこの CouchDB をマルチサーバーによる分散データ管理する仕組みがマネージドサービスとして提供されています。



【イメージのデプロイ】
まずはこちらのファイルを自分の PC にダウンロードしてください:
https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/couchdb.yaml

次にこのファイルをテキストエディタで開いてパラメータを編集します。具体的には以下2箇所の value 値を変更してください:
・COUCHDB_USER : ログイン時のユーザー名(初期値 admin)
・COUCHDB_PASSWORD : ログイン時のパスワード(初期値 P@ssw0rd)


ではこのダウンロード&編集した couchdb.yaml ファイルを指定してデプロイします。以下のコマンドを実行する前に Day 0 の内容を参照して ibmcloud CLI ツールで IBM Cloud にログインし、クラスタに接続するまでを済ませておいてください。

そして以下のコマンドを実行します:
$ kubectl apply -f couchdb.yaml

以下のコマンドで CouchDB 関連の Deployment, Service, Pod, Replicaset が1つずつ生成されたことと、サービスが 30984 番ポートで公開されていることを確認します:
$ kubectl get all

NAME                           READY   STATUS    RESTARTS   AGE
pod/couchdb-7866c5d4c6-z462x   1/1     Running   0          12s

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/couchdbserver   NodePort    172.21.71.164   <none>        5984:30984/TCP   13s
service/kubernetes      ClusterIP   172.21.0.1      <none>        443/TCP          27d

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/couchdb   1/1     1            1           14s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/couchdb-7866c5d4c6   1         1         1       14s

この後に実際にサービスを利用するため、以下のコマンドでワーカーノードのパブリック IP アドレスを確認します(以下の例であれば 161.51.204.190):
$ ibmcloud ks worker ls --cluster=mycluster-free
OK
ID                                                       パブリック IP    プライベート IP   フレーバー   状態     状況    ゾーン   バージョン
kube-c3biujbf074rs3rl76t0-myclusterfr-default-000000df   169.51.204.190   10.144.185.144    free         normal   Ready   mil01    1.20.7_1543*

つまりこの時点で(上述の結果であれば)アプリケーションは http://169.51.204.190:30984/ で稼働している、ということになります。CouchDB はこの URL が管理ダッシュボードの URL になっているので早速実行してみます。ウェブブラウザを使って、アプリケーションの URL(上述の方法で確認した URL)にアクセスしてみます:
couchdb1


(シンプルすぎて)一瞬びっくりしますが、稼働中の CouchDB に関する情報が表示されました。CouchDB も IKS 上で動かすことができました。


【YAML ファイルの解説】
YAML ファイルはこちらを使っています(編集する前の状態です):
apiVersion: v1
kind: Service
metadata:
  name: couchdbserver
spec:
  selector:
    app: couchdb
  ports:
  - port: 5984
    protocol: TCP
    targetPort: 5984
    nodePort: 30984
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: couchdb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: couchdb
  template:
    metadata:
      labels:
        app: couchdb
    spec:
      containers:
      - name: couchdb
        image: couchdb
        env:
        - name: COUCHDB_USER
          value: "admin"
        - name: COUCHDB_PASSWORD
          value: "P@ssw0rd"
        ports:
        - containerPort: 5984

Deployment 1つと、Service 1つのごくごくシンプルな YAML ファイルですが、一応解説を加えておきます。アプリケーションそのものは 5984 番ポートで動作するように作られているため、NodePort 30080 番を指定して、外部からは 30080 番ポートでアクセスできるようにしています(NodePort として指定可能な番号の範囲は 30000 ~ 32767 です、指定しない場合は空いている番号がランダムに割り振られます)。また ReplicaSet は1つだけで作りました(データベースなので、別途クラスタ構成の準備をしない限りはこの数値だけを増やしてもあまり意味ないと思います)。


デプロイしたコンテナイメージを削除する場合はデプロイ時に使った YAML ファイルを再度使って、以下のコマンドを実行します。不要であれば削除しておきましょう:
$ kubectl delete -f couchdb.yaml


【紹介したイメージ】
https://hub.docker.com/_/couchdb


【紹介記録】
Dayカテゴリーデプロイ内容
0準備準備作業
1ウェブサーバーhostname
2Apache HTTP
3Nginx
4Tomcat
5Websphere Liberty
6データベースMySQL
7phpMyAdmin
8PostgreSQL
9pgAdmin4
10MongoDB
11Mongo-Express
12Redis
13RedisCommander
14ElasticSearch
15Kibana
16CouchDB
17CouchBase
18HATOYA
19プログラミングNode-RED
20Scratch
21Eclipse Orion
22Swagger Editor
23R Studio
24Jenkins
25アプリケーションFX
262048
27DOS Box
28VNC Server(Lubuntu)
29Drupal
30WordPress

IBM Cloud から提供されている NoSQL のマネージドデータベースである Cloudant (実体は Apache CouchDB)を便利に使っています。最大の特徴の一つが「無料でも一定条件下で使える」点ですが、今回は無料である以外の便利な使い方を紹介します。

この記事の中で紹介するのはこのような使いかたです:
(1) Cloudant の CORS を有効に設定する(特定ドメインのページからの REST API リクエストを受け付けて読み書きできるようにする)
(2) Github ページに用意したウェブページから (1) で有効に設定した Cloudant のデータを読み書きする


2021022401


最終的に実現したいのは、世の中の SaaS(といっていいのかな)の中でもかなり可用性高く、安定して動いている印象の Github ページを使ったウェブアプリケーションを作ることです。ただ静的ページそのものは安定して稼働していてもデータの読み書きが出来なければウェブアプリケーションとしては不十分で、そこを REST API で読み書きできる IBM Cloud の Cloudant データベースで賄えないか、と考えました。ただそのままでは CORS の制約もあって API が実行できないのですが、Cloudant 側の設定で CORS を正しく設定して Github ページからの読み書きを可能な状態にする、そのための Cloudant の設定手順を確認する、というのが目標です。

以下では Github のアカウントを持っている方を対象に、Github ページを使って作成するウェブページから Cloudant にアクセスするサンプルを紹介します。Github ページでなくても(静的ページのホスティング環境があれば)同様に可能なはずですが、その場合は適宜読み直してください(特に CORS 設定時のドメインなど)。


【CORS】
"Cross-Origin Resource Sharing" 、つまり「オリジン間リソース共有」の略語です。ここでの「オリジン」は「ドメイン」と読み替えたほうが理解しやすいかもしれまえん。

例えば A というウェブサーバーが https://aaa.com/ 上で、また B というデータベース・サーバーが https://bbb.net/ 上で動いていると仮定します(つまり全く別々に管理されている2つのサーバーです。この状態を Cross-Origin と呼びます)。A 上のウェブページで表示される画面の中で B から取得したデータを表示しようと思っています。

例えば A が Java などのプログラミング言語によって作られたアプリケーションサーバーであった場合、A は B からのデータ取得に Java を使うことになります。A の中で Java が実行され、B にアクセスしてデータを取り出し、その結果を使って A のページの画面を作成することができます。A の内部でプログラミング言語が使われている場合は、Cross-Origin であってもこのような方法でデータを取得することができます。

ところが A がアプリケーションサーバーではなく、ただの(既存の HTML ページを表示するだけの)ウェブサーバーであった場合、A から B にアクセスしてデータを取得する手段はかなり限られてしまいます。その限られた手段の1つが AJAX などに代表される JavaScript 処理です。A のサーバーにプログラミング言語が用意されていなくても、ウェブブラウザ自体が持つ JavaScript 実行環境を使って B サーバーにアクセスしてデータを取得する、という考え方です。ただし Cross-Origin の場合、A にアクセスしたウェブブラウザから B のサーバーに JavaScript で HTTP アクセスすることは原則できないことになっています(Cross-Origin でなければ、つまり A = B であれば可能です)。これが CORS の制約です。

CORS の制約はウェブブラウザが持っている制約なので、ウェブブラウザを使わない HTTP 通信(上述の Java 言語によるプログラミングによる通信など)には関係ありませんが、(ウェブブラウザの)利用者側でこの制約を解除する方法はありません。唯一の方法がリソース提供側(この場合だと B サーバー)による許可のみです。

今回のブログエントリは IBM Cloud のデータベースの1つである Cloudant の CORS をうまく設定することで、代表的な静的ウェブページの1つである Github ページから(Cloudant 内の)外部データを読み書きする方法について紹介する、というものです。


【Cloudant の用意】
まず IBM Cloud のアカウントを取得します。IBM Cloud アカウントを新規に取得した時点では「ライトアカウント」と呼ばれる無料の制約のあるアカウントとなります。今回紹介する作業を実行するだけであれば(ライトアカウントだとデータは 1GB までの格納となりますが、この容量などに問題なければ)ライトアカウントのまま実行いただいても構いません。

改めて取得したアカウントで IBM Cloud にログインし、「リソースの作成」ボタンクリック後に「Cloudant」を選択します:
2021022501

2021022502


インスタンス作成前にいくつか設定箇所があります。主なものは以下になりますが、"Authentification Method" だけはデフォルトの "IAM" ではなく "IAM and legacy credentials" を選択してください:
・Available Regions: インスタンスを作成するエリア、お好きな場所でいいが「東京」あたりが無難
・Authentification Method: 今回は必ず "IAM and legacy credentials" を選択
・Plan: 料金プラン。「Lite」であれば無料

最後に "Create" ボタンで作成します:
2021022503


Authentification Method について補足しておくと、デフォルトの "IAM" だといわゆる API キーを使った認証/認可を行います。ただ今回はアプリケーションサーバーからではなく(GitHub ページの)静的なページから Basic 認証を使ってデータベースの REST API を使うことを想定しています。この場合は "IAM" ではなく "IAM and legacy credentials" (API キーまたは Basic 認証を使う)を選択しておく必要があるためです。


少し待つと Cloudant インスタンスが起動済みになります。インスタンスを開いて「サービス資格情報」タブを選び、「新規資格情報」ボタンを押して資格情報を作成します。作成後に作成した資格情報(「サービス資格情報-1」のような名前になっていると思います)を選択・展開して、中身を確認します:
2021022504


資格情報の中身は以下のような内容の JSON 文字列になっているはずです(この中身は他人に見せないように注意してください):
{
  "apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "host": "xxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "iam_apikey_description": "Auto-generated for key xxxxxxxxxxxxxxxxxxx",
  "iam_apikey_name": "サービス資格情報-1",
  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/xxxxxxxxxxxxxxxxxxxx::serviceid:ServiceId-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "password": "パスワード",
  "port": 443,
  "url": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "username": "ユーザー名"
}

この情報のうち、後で必要になるのが以下の(赤字の)4つです。メモするか、コピペできるようにしておいてください:
キー意味補足
hostnameホスト名*******-bluemix.cloudantnosqldb.appdomain.cloud といった感じの文字列になっています(******* 部分が可変)
portポート番号443 で固定のはずです
usernameユーザー名ログイン時にこの2つの値が必要です
passwordパスワード


認証情報を確認したら、実際に Cloudant データベースのダッシュボードを見てみましょう。画面左のメニューから「管理」タブを選択し、右上の「Launch Dashboard」ボタンをクリックします:
2021022505


(初期状態では空の)ダッシュボード画面が表示されます。最初にアクセスした時点ではまだ何のデータベースも作成されていないため、下図のような画面になります。後述の作業のためのデータベースをここで1つ作成しておくことにします。画面右上の「Create Database」ボタンをクリックします:
2021022506


データベースを作成するメニューが画面右に表示されます。ここでは Database name に「mydb(異なる名前を指定しても構いませんが、その場合は後述のコマンド等を作成した名前に読み替えて実行してください)」、Partitioning は「Non-partitioned」を選択し、「Create」ボタンをクリックします:
2021022507


作成した mydb データベースが出来上がりました。このデータベースもまだデータが1件もないので、このような画面になります。一旦元の画面に戻るため、画面左上の "<" マークをクリックします:
2021022508


一つ前のデータベース一覧画面に戻りました。先程は空でしたが、作成したデータベースが1つ追加された画面になっているはずです:
2021022509


Cloudant 側の準備はいったんここまでできていればOKとします。 次に CORS のための設定を行います。


【Cloudant に CORS を設定する】
次に Cloudant に CORS の設定をして外部からのアクセスを許可します。ダッシュボードのメニューから「アカウント」(下から3番め)を選び、「CORS」タブを選択します。以下のような画面(「CORS は有効になっていて、外部のどこからのリクエストも受け付けない」という内容)になるはずです:
2021022501


画面下部の「Restrict to specific domains」欄にアクセスを許可するドメインの URL を指定して「Add Domain」ボタンで追加します。ここでは "https://(github のアカウント名).github.io/" を指定することで自分の Github ページからのアクセスを許可することになるので、自分の github アカウントに合わせて設定してください:
2021022502


このような画面になれば自分の github ページ用の CORS 設定ができたことになり、自分の github ページからは JavaScript で Cloudant データを読み書きできることになります:
2021022503


【Cloudant に初期データを入力する】
今回の紹介では Github ページから Cloudant のデータを REST API 経由で表示する(CORS の設定をしていれば取得できるはず)、ということを試してみます。そのための「表示するデータ」をあらかじめ Cloudant に入力しておきます。

もちろん(ダッシュボードから)自分で好きにデータを入力いただいても構いませんが、比較的簡単に動作を確認できるサンプルを用意しており、そこで対象とするデータを curl 一発でまとめて入力できるように準備しているのでこちらの方法を紹介します。

まず以下の URL からサンプルデータをダウンロード(表示後に右クリック→名前を付けて保存で "prefs.json" という名前で保存)してください:
https://raw.githubusercontent.com/dotnsf/cloudant_cors/main/prefs.json

2021022701


このファイルの中身は以下のようなフォーマットになっており、各都道府県庁所在地と、その位置情報が格納されています:
{
  "docs": [
    { "code": 1, "prefecture": "北海道", "capital": "札幌市", "geometry": { "type": "Point", "coordinates": [ 141.34694, 43.06417 ] } },
    { "code": 2, "prefecture": "青森県", "capital": "青森市", "geometry": { "type": "Point", "coordinates": [ 140.74, 40.82444 ] } },
    { "code": 3, "prefecture": "岩手県", "capital": "盛岡市", "geometry": { "type": "Point", "coordinates": [ 141.1525, 39.70361 ] } },
    { "code": 4, "prefecture": "宮城県", "capital": "仙台市", "geometry": { "type": "Point", "coordinates": [ 140.87194, 38.26889 ] } },
    { "code": 5, "prefecture": "秋田県", "capital": "秋田市", "geometry": { "type": "Point", "coordinates": [ 140.1025, 39.71861 ] } },
    { "code": 6, "prefecture": "山形県", "capital": "山形市", "geometry": { "type": "Point", "coordinates": [ 140.36333, 38.24056 ] } },
    { "code": 7, "prefecture": "福島県", "capital": "福島市", "geometry": { "type": "Point", "coordinates": [ 140.46778, 37.75 ] } },
    { "code": 8, "prefecture": "茨城県", "capital": "水戸市", "geometry": { "type": "Point", "coordinates": [ 140.44667, 36.34139 ] } },
    { "code": 9, "prefecture": "栃木県", "capital": "宇都宮市", "geometry": { "type": "Point", "coordinates": [ 139.88361, 36.56583 ] } },
    { "code": 10, "prefecture": "群馬県", "capital": "前橋市", "geometry": { "type": "Point", "coordinates": [ 139.06083, 36.39111 ] } },
    { "code": 11, "prefecture": "埼玉県", "capital": "さいたま市", "geometry": { "type": "Point", "coordinates": [ 139.64889, 35.85694 ] } },
    { "code": 12, "prefecture": "千葉県", "capital": "千葉市", "geometry": { "type": "Point", "coordinates": [ 140.12333, 35.60472 ] } },
    { "code": 13, "prefecture": "東京都", "capital": "新宿区", "geometry": { "type": "Point", "coordinates": [ 139.69167, 35.68944 ] } },
    { "code": 14, "prefecture": "神奈川県", "capital": "横浜市", "geometry": { "type": "Point", "coordinates": [ 139.6425, 35.44778 ] } },
      :
      :
  ]
}

このデータを先程作成した Cloudant 内の mydb データベースに格納します。curl コマンドを使うと以下のコマンド(※)で格納できます:
$ curl -u "username:password" -XPOST "https://host/mydb/_bulk_docs" -H "Content-Type: application/json" -d @prefs.json

username, password, host の値は上述で確認した以下の値を指定して入力してください:
 username: 上述の資格情報で確認した username の値
 password: 上述の資格情報で確認した password の値
 host: 上述の資格情報で確認した host の値


このコマンドが成功すると Cloudant の mydb データベースに47都道府県のデータが挿入されます。ダッシュボードから mydb データベースを選択すると以下のように 47 件のデータが表示されるようになります:
2021022702


試しにどれか1つ選択してみると、個々の内容を確認することも可能です(下図は北海道の例、"Cancel" で一覧に戻ります):
2021022703


ここまでの作業で Cloudant に Github ページからアクセスするための CORS を設定し、表示用のデータも格納できました。ではこのデータを Github ページから REST API 経由で取得して表示してみましょう。


【Github ページを用意する】
では実際に Github ページからクロスオリジンな Cloudant データベースに REST API 経由でアクセスできることを確認してみます。

まず Github にログインし、その後で以下のページにアクセスし、画面右上の "Fork" をクリックしてください:
https://github.com/dotnsf/cloudant_cors

2021022706


Fork 処理が成功すると、
 https://github.com/(あなたの github アカウント名)/cloudant_cors
というリポジトリができるはずです。

このリポジトリの Github ページを有効にします。リポジトリページの右上にある "Settings" をクリックします:
2021022707


画面を下に "GitHub Pages" と書かれている箇所までスクロールします。"Source" に "main" ブランチを指定して保存すると、"main" ブランチの内容がそのまま Github ページとして公開され、この中にある CORS の動作確認用に作った index.html を参照するための URL(https://(あなたの github アカウント名).github.io/cloudant_cors/)とそのリンクが表示されます(リンク先が有効になるまで1分程度かかります):
2021022708


少し待ってからリンク先にアクセスします。以下のような画面が表示されるはずです(Github ページなので、****.github.io というドメインのページになっていることを再確認してください):
2021022704


改めて資格情報から取得した値を画面内の各該当箇所に入力します。上にある横幅の大きなフィールドには host の値、その下は左から作成した DB 名(mydb)、username、password の値を入力します。最後に "Refresh" ボタンを押すと入力された情報を使ってこのページから AJAX で Cloudant へ REST API を実行して文書一覧を取得し、表形式で出力します。成功すると以下のように47都道府県の情報が(クロスオリジンの制約を乗り越えて)表示されます:
2021022705


以上、正しく設定することでクロスオリジンの壁を超えて Github ページから Cloudant のデータを利用することができました。Github も Cloudant も(一定制限の中で)無料で利用できるので、安定したウェブページを Github で運用しつつ、表示データを Cloudant から提供する、といった形で利用することができそうです。


なお、Cloudant(CouchDB) の REST API についての詳しくは、こちらのリファレンスを参照してください:
https://docs.couchdb.org/en/stable/api/index.html


なお、今回使った「特定データベース内の全文書を取得する」 REST API はこちらです:
GET /{db}/_all_docs

また準備段階で都道府県データをバルクインサートしましたが、その REST API はこちらで紹介されています:
POST /{db}/_bulk_docs


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

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 やフォーマットを変えることで応用範囲が広くなりそうだと思っています。

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


このページのトップヘ