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

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

私自身、ふだん CMS (コンテンツ管理システム)といえばワードプレスを使うことが多かったのですが、思い立って自分でも CMS を作ってみることにしました。それもせっかくなので(?)あまり例のない Node.jsIBM Cloudant をベースとした、IBM Cloud 環境向きのインフラで実装し、かつオプションで、この中で管理するコンテンツが IBM Watson の自然言語分類機能の学習コンテンツにできるような機能も付与して BlueCMS という名称で github.com で公開しました:
https://github.com/dotnsf/bluecms

使い方やセットアップ手順は README.md に(英語で)記載していますが、いちおうここでは日本語で(図解入りで)、2017/Jul/11 時点での実装内容やセットアップ手順を紹介します。

なお、このブログエントリのタイトルで「IBM Cloud 向き」と表現していますが、この意図は「IBM Cloud (の PaaS)を使うとセットアップが楽」という意味であって、普通の PC や仮想環境、各種 IaaS サーバーなどからでもセットアップ可能です(ただし IBM Cloud の Cloudant は必要です)。


BlueCMS はコンテンツ管理システムの実装の1つです。現時点では機能的にはまだまだ足りない部分もあると思いますが、CMS としての最小限の機能に加えて、以下のような特徴を持っています:
(1) 実装は Node.js、データストアには IBM Cloudant を利用
(2) IBM Cloud のライトアカウント(無料版)でも使える
(3) コンテンツ管理だけでなく、ユーザー管理機能を内蔵している
(4) 添付ファイルを格納することもできる(IBM Cloudant 内にバイナリデータとして格納)
(5) IBM Cloud ライトアカウントの範囲ではないが、IBM ワトソンと連動する機能をビルトインで使える
(6) ソースは MIT ライセンスで公開


BlueCMS そのものは IBM Cloud 以外の環境でも動きますが、IBM Cloud のアカウントを所有していると PaaS の機能を活用して比較的すぐ&簡単に環境構築できます。なお BlueCMS 自体は無料のライトアカウントでも(他にランタイムやサービスを使っていなければ)環境構築可能ですが、後述するオプションのワトソン NLC(Natural Language Classifier) 機能を利用する場合はクレジットカードを登録してスタンダードアカウント等にしておく必要があります(その場合でも利用量によっては無料枠内で運用可能です)。


セットアップ手順

IBM Cloud アカウントを所有している場合、以下の4ステップで最低限の動作環境を構築します:

(1) インフラ(ランタイムインスタンスとサービスインスタンス)の作成

とりあえず IBM Cloud にログインします:
2018071001


まず BlueCMS を実行するための Node.js ランタイムを作成します。ログイン直後の画面(ダッシュボード)右上の「リソースの作成」ボタンをクリックします:
2018071002



「Compute」カテゴリーから「SDK for Node.js」ランタイムを選択します:
2018071102


ランタイムを作成する場合は名称と地域に注意が必要です。名称は URL の一部になるためユニークな文字列を指定します(下図では bluecms としていますが、この名称は僕が使っているので使えません)。また地域はどこでもいいのですが、以降で Cloudant や NLC を作成する場合に同じ地域を指定する必要があるので、どこを選択したかを覚えておきましょう。最後に「作成」ボタンをクリック:
2018071004


これで Node.js のランタイム(アプリケーションサーバー)を作ることができました:
2018071005


このまま次のステップに移りますが、ダッシュボードへの戻り方をガイドしておきます。IBM Cloud の画面のどこからでも画面左上の三本線メニュー(「ハンバーガーメニュー」)からダッシュボードに戻ることができます。まずはここをクリック:
2018071006


そしてポップアップメニューから「ダッシュボード」を選択します。これでダッシュボードに戻ります:
2018071007


ダッシュボードに戻ると、上記で作成したランタイムが一覧に加わっていることが確認できるはずです:
2018071008


次にデータをストアするための IBM Cloudant サービスインスタンスを作成します。やはりダッシュボードから「リソースの作成」をクリックし、今度は「Databases」カテゴリの「Cloudant」を選択します:
2018071103


サービスの地域は先程 Node.js ランタイムを作成した時と同じ地域を選択し、またプランは "Lite" を選択しておくと容量やトランザクションパフォーマンスに制約があるものの、料金はかかりません。最後に「作成」をクリック:
2018071010


これだけで IBM Cloudant のインスタンスを作ることができました:
2018071011


なお Cloudant のライトプランでの制限やライトプラン以外での料金についてはこの画面内の記述を参照してください。ライトプランの場合、容量は 1GB、パフォーマンスとしては 20 データ読み取り/秒、10 データ書き込み/秒、5 データクエリー/秒となります:
2018071105



有料アカウントを利用している場合で、かつワトソン連携機能を有効にしたい場合は IBM Watson NLC サービスインスタンスを作成します。再度ダッシュボードに戻り、「リソースの作成」から「AI」カテゴリーの「Natural Language Classifier」を選択します:
2018071101


こちらでも Node.js ランタイムを作った時と同じ地域を選択し、「作成」をクリックします:
2018071013


なお NLC の料金や無料枠についてはこの画面内の記述を参照してご注意ください。1ヶ月あたりで最初の1インスタンス、4 回の学習実行、1000件の問い合わせまでは無料ですが、それらを超えた分は課金対象となります:
2018071104


こちらもサービスを作ることができました:
2018071014


ここまでの作業でアプリケーションサーバーとデータベース(、とオプションでワトソン)の各サービスが IBM Cloud 内で有効になりました。 PaaS だとこういう所が簡単で便利です:
2018071003



(2) ランタイムとサービスのバインド

IBM Cloud を利用している場合、ランタイムとサービスをバインドすることで面倒な認証情報の設定や交換を安全かつ簡易化することができます。BlueCMS はこの仕組に対応しているので、そのための設定を行います。

まずダッシュボードを表示し、ランタイム一覧から作成した Node.js ランタイムを選択します:
2018071015


現在のランタイム動作状況が確認できる画面が表示されます:
2018071016


ここで画面左のメニューから「接続」を選択します。バインド済みサービスの一覧が表示されますが、この時点では何もバインドされていないので、何も表示されません。ここに上記で作成した IBM Cloudant(とワトソン NLC)サービスをバインドします。「接続の作成」ボタンをクリックします:
2018071017


バインド可能なサービスの一覧が表示されます。上記のインスタンス作成の過程で作成地域が全て同じであれば、この一覧の中に IBM Cloudant (とワトソン NLC)が含まれているはずです。まずは IBM Cloudant サービスを選び(マウスオーバーし)、「Connect」ボタンをクリックします:
2018071018


環境を再ステージングするか、という確認ダイアログが表示されるので「再ステージ」を選択して、再起動します。再起動が完了するまで5分弱かかります:
2018071019


再ステージが完了すると、Node.js ランタイムに IBM Cloudant がバインドされた状態になり、接続一覧からも確認できるようになります:
2018071020


ワトソン NLC サービスも作成している場合は、こちらも続けてバインドします。接続メニューの一覧から NLC を選択(マウスオーバー)し、「Connect」をクリックします:
2018071021


こちらも再ステージして、Cloudant と NLC 両方のサービスがバインドされた状態になりました:
2018071022


ここまでの作業で作成した各インスタンスがバインドされ、連携できる準備が整いました:
2018071004



これでアプリケーションインフラ部分の構築が完了しました。後は BlueCMS の中身をアップロードするだけです。


(3) ソースコードの用意とランタイムにプッシュ

あとは BlueCMS のソースコードを用意して、ランタイムにプッシュ(転送)すればいいのですが、そのためには cf というツールを使います。最初に cf ツールをダウンロード&インストールします:
https://github.com/cloudfoundry/cli/releases

上記ページから自分の環境にあった cf ツールをダウンロードしてインストールします。

改めて、いよいよソースコードを用意します。github のリポジトリからソースコードを git clone するか、(その意味がわからなければ)zip ダウンロード&展開します:
2018071001


本来であればここで認証情報の取得や設定が必要になるのですが、IBM Cloud を使っているとその部分を上記の「バインド」設定で済ませていることになります。設定を行う場合はソースコード内の settings.js 内の各種値を変更します(IBM Cloud 環境を使う場合、変更する必要があるとしたら exports.search_analyzer(検索インデックスで使う言語)と、exports.nlc_language(ワトソン NLC の学習時に指定する言語)くらいです。コンテンツが日本語の場合はともに変更の必要はありません):
2018071002


settings.js の準備が完了したら、いよいよ cf を使ってコードをランタイムにプッシュします。コマンドプロンプトやターミナルを開き、まずは cf コマンドで IBM Cloudant にログインします(ログインパスワードを聞かれるので入力します):
$ cf login -a https://api.ng.bluemix.net/ -u (IBM Cloud のログイン名)

ログインできたらランタイムにプッシュするだけです:
$ cf push (ランタイムの名前(上記例だと bluecms となっている所に指定したもの))

プッシュが成功すると BlueCMS が Node.js ランタイム上で動き出します。BlueCMS は起動と同時に Cloudant 上にデータベースをインデックスごと作成するので、あらかじめ Cloudant 側で準備しておく必要はありません:
2018071005


まだ必要な管理用/編集用のユーザーを作っていないため投稿はできないのですが、この時点でトップページを表示することはできます。

ウェブブラウザで https://(ランタイム作成時に指定したアプリケーション名).mybluemix.net/ にアクセスします:
2018071000
 (まだ中身がないので今はこれだけ)

↑こんな感じのトップ画面が表示されればほぼ完成、あともう少しです!


(4) 最初の管理ユーザー ID の作成

(注 この部分の手順は将来変更の可能性がありますが)専用の API を使って BlueCMS の最初の管理ユーザーを作成します。

BlueCMS には2種類のユーザーがいます。1つが管理者ユーザー、もう1つが編集者ユーザーです。BlueCMS のコンテンツを作成/編集することができるのは管理者ユーザーと編集者ユーザーで、ユーザーの作成/変更/削除といったことができるのが編集者ユーザーです。

BlueCMS の場合、ログインしなくてもコンテンツの一覧や1つ1つを参照することはできます。ただコンテンツの中身を編集したり、編集者ユーザーを登録したりするにはユーザー登録が必要になります。これらの編集/登録作業は BlueCMS にログインした後のコンソールから行うことが可能ですが、一番最初の管理者ユーザーだけは(まだ誰もコンソールにアクセスできないため)別の方法で作成する必要があります。その方法を以下に紹介します。

最初の管理者ユーザーは curl コマンドを使って、以下の REST API を使って作成します:
$ curl -XPOST -H 'Content-Type: application/json' 'https://(ランタイム作成時に指定したアプリの名前).mybluemix.net/adminuser' -d '{"id":"abc@xyz.com","password":"yourpassword","name":"yourname","email":"abc@xyz.com"}'

-d オプションに続いてパラメータが指定されています。それぞれ以下のような意味があります:
 id: ログインID(ログイン時に指定するユーザーID)
 password: ログインパスワード(ログイン時に指定するパスワード)
 name: 画面に表示される時の名前、省略時は id が使われる
 email: メールアドレス、省略時は admin@admin となる


管理者権限で編集することはあまり想定しておらず、あくまで管理者権限で編集者を作成し、編集者権限でログインしてコンテンツを編集、することを想定しています。そのためこの最初の管理者ユーザーはどちらかというと「編集者ユーザーの管理者」的な位置づけになりますが、その管理者ユーザーを上記コマンドで作成しました:
2018071006


このコマンドが成功(結果に status:true が含まれている)すれば、このユーザーで BlueCMS にログインすることができるようになります。これで BlueCMS を使うために必要な最低限の準備は完了です。


ログイン

では作成した管理者ユーザーでログインしてみます。ウェブブラウザで https://(ランタイムに作成したアプリ名).mybluemix.net/ にアクセスし、画面右上の login ボタンをクリックします:
2018071000


ログインフォームが表示されたら、先程の REST API で作成した管理者ユーザーの id とパスワードを指定して login をクリックします:
2018071001


正しい情報が指定されていればログインし、管理用コンソールに移動します:
2018071002


この管理用コンソールでユーザーの作成/編集/削除を行ったり、コンテンツの作成/編集/追加、添付ファイルの作成と削除を行うことができます。その使い方についてはまた別の機会に、とりあえず BlueCMS とその初期セットアップ方法の紹介でした。


(2018/Jul/12 追記)
続きの使い方の説明はこちら:
ワトソン対応の IBM Cloud 向き CMS "BlueCMS" を公開しました(使い方)


Node.js を使っていて 404 エラーや 500 エラーなどが発生した場合に表示されるエラーページをカスタマイズできないか、と思って挑戦してみました。

今回挑戦した環境では Node.js にフレームワークの Express と、テンプレートエンジンに EJS を使いました。エラーページを EJS で作ることを想定しています。

まず、JavaScript のコードはこんな感じです:

app.js
// app.js
var express = require( 'express' );
var app = express();

//. EJS テンプレートエンジン
app.set( 'views', __dirname + '/templates' );
app.set( 'view engine', 'ejs' );

//. / へのアクセスは正常にできる
app.get( '/', function( req, res ){
  res.render( 'index', {} );
});

//. /err へのアクセスは 500 エラーとする
app.get( '/err', function( req, res ){
  res.render( 'index', { value: novalue } ); //. novalue 変数が未定義なので 500 エラーが発生する
});

//. 有効なルーティングを上記に記述
//. /, /err 以外のパスは 404 エラー

//. 404 エラーが発生した場合、
app.use( function( req, res, next ){
  res.status( 404 ); //. 404 エラー
  res.render( 'err404', { path: req.path } ); //. 404 エラーが発生したパスをパラメータとして渡す
});

//. 500 エラーが発生した場合、
app.use( function( err, req, res, next ){
  res.status( 500 ); //. 500 エラー
  res.render( 'err500', { error: err } ); //. 500 エラーの内容をパラメータとして渡す
});

var port = 3000;
app.listen( port );
console.log( 'server started on ' + port );

上記を補足すると、Express でルーティングを2つ定義しています。1つめがドキュメントルート(/)へのアクセスで、この場合は正常な処理として(後述の)index.ejs を表示します。

2つめは /err へのアクセスで、こちらの場合はわざと 500 エラーを発生させています(index.ejs でページを表示させるような記述にしていますが、実際にはこの中で使われている novalue という変数が未定義なので、そこで 500 エラーが発生するようにしています)。

ルーティングとしてはこの2つ(/ と /err)だけを定義しているので、これら以外のパス(例えば /home)にアクセスがあった場合は 404 エラーが発生します。404 エラーが発生した場合の処理としては err404.ejs というテンプレートを使って、アクセスしたパス(/home)が存在していない、という旨のエラーページを表示します。

最後に 500 エラーが発生した場合の処理を定義しています。ここでは err500.ejs というテンプレートを使って、エラーの内容をあわせて表示します。上記で /err ページにアクセスすると 500 エラーが発生するので、この err500.ejs のページが表示されることになります。


次に上記で使うことにした3種類のテンプレート(index.ejs, err404.ejs, err500.ejs)を用意します。それぞれ以下のような内容にしました:

index.ejs
<html>
<head>
<title>index</title>
</head>
<body>
<h1>index</h1>
</body>
</html>
↑単に index と表示されるだけのシンプルなページ


err404.ejs
<html>
<head>
<title>err404</title>
</head>
<body>
<h1>err404</h1>
<%= path %> is not defined nor found.
</body>
</html>
↑ "(エラーがあったパス)is not defined nor found." というエラーメッセージが表示されるページ


err500.ejs
<html>
<head>
<title>err500</title>
</head>
<body>
<h1>err500</h1>
Error: <%= error %>
</body>
</html>
↑ "Error: (エラー内容)" というエラーメッセージが表示されるページ


app.js ではこれらのテンプレートファイルが templates/ フォルダに存在していることを前提に参照するようにしています。したがって templates/ フォルダを作って、その中にこれら3つの .ejs ファイルを格納しておきます。これで準備OK。


実際に動かして試してみました。まずは正常系、/ へのアクセスは普通に問題なくできます:
2018070401


次に /home にアクセスして、わざと 404 エラーを発生させてみた時の画面です。err404.ejs で定義されたテンプレートを使って期待通りにエラーページが表示されています:
2018070402


最後に /err にアクセスして、わざと 500 エラーを発生させてみた時の画面です。こちらも err500.ejs で定義されたテンプレートを使って期待通りにエラー内容が表示されています:
2018070403


上記コードのサンプルをこちらに用意しました:
https://github.com/dotnsf/nodejserror


これで Node.js でもカスタムエラーページを作れることがわかりました。

僕自身はオープンソース製品である Hyperledger FabricHyperledger Composer を使ったブロックチェーンプラットフォームや、その対応アプリケーションを業務で開発する機会が多くあります。

ありがたいことに実際に作る機会も多いのですが、「作る」という決断に到達する前に方針を変更することも少なくありません。よくある理由としては「(現行の)ブロックチェーンに向かない要件」であるというケースです。パフォーマンス要件だったり、検索機能の要件だったり、現在のブロックチェーンでは本格運用に向かないケースが前提になっていたりすると、そのことを伝えた上で「それでも作るかどうか」を判断していただいています。そして「作らずに検討し直す」と判断されることもある、という意味です。

でも、これはブロックチェーンを使いたいと思いながらも「現在のブロックチェーン技術」では向かない、と判断されたからです。需要としては存在していることも意味しています。

そんな場面を多く見ている中で「だったらブロックチェーンといえるかどうかはともかく、同じような仕組みで耐改竄性を高めながら、この要件も満たせるようなデータストアの仕組みを作れないか?」と考えるようになりました。それが今回紹介するものです。


【「ブロックチェーン」の定義】
まず最初に、以下で紹介するアプリケーションは「ブロックチェーン」と呼べるものかどうかと言われると、私個人的には「呼べないのではないか」と考えています。これは「ブロックチェーンの定義」をどのようにするか、という問題にも関わってくるのですが、ここでは1つの目安として、JBA("Japan Blockchain Association" 日本ブロックチェーン協会)が定義した以下の2つの定義を満たすものをブロックチェーンと定義するものとします:

・ビザンチン障害を含む不特定多数のノードを用い、時間の経過とともにその時点の合意が覆る確率が0へ収束するプロトコル、またはその実装をブロックチェーンと呼ぶ。
・電子署名とハッシュポインタを使用し改竄検出が容易なデータ構造を持ち、且つ、当該データをネットワーク上に分散する多数のノードに保持させることで、高可用性及びデータ同一性等を実現する技術を広義のブロックチェーンと呼ぶ。

(参照)
http://jba-web.jp/archives/2011003blockchain_definition

2018062901



要するに「ブロックチェーンとは『ブロック』が『チェーン』のようにつながって格納される仕組み」というゆるい定義ではなく(注 私個人的にはこれがブロックチェーンの定義でもいいと思ってます)、「ハッシュポインタを用いて接続し、データを複数のノード上に分散して保持し、かつその複数ノードが耐ビザンチン障害性を持っている実装」のことを「ブロックチェーン」と定義する、ということです。 そしてこの定義の上で考えた場合、以下で紹介するものはブロックチェーンではない、ということになることを予め伝えておきます。

自分の考えは、上記のものだと耐ビザンチン障害性をもたせるためのコンセンサスアルゴリズムに問題が発生したり(最近だと「51%攻撃」と呼ばれる手法が話題になりました)、そのためにトランザクションのパフォーマンスを高く実現するのが難しかったりする、と思っています。なのでこの要件が必須でないケースでは、この条件を確保しないことでパフォーマンスや検索機能など他の要件機能を向上させることができるのではないかと考えました。


【色々出てきている】
現状のブロックチェーンにはパフォーマンス遅延や電力消費など当初は想定していなかったことが問題になっていることもあって、それらの苦手部分に対処した新しい仕組みも生まれています(それらが上記ブロックチェーンの定義を満たしているかどうかはともかく)。例えば全参加者のコンセンサスを得る上でのパフォーマンス遅延を解決するために、ランダムに選ばれた一部参加者のコンセンサスを得るように改良したものも出ているようです:
ブロックチェーンを超える?ハッシュグラフ(Hashgraph)とは?


【作ってみようと思ったもの】
今回、自分は以下4つの特徴を持つようなデータストアシステムを新たに開発してみようと思いました:
(1)ハッシュポインタを利用した、改竄検出が容易なブロックチェーンの仕組みはそのまま取り入れる
(2)データは中央集権管理型とする。これによりコンセンサスアルゴリズムや耐ビザンチン障害性の問題を発生させなくした上で、トランザクションパフォーマンスの向上を実現する
(3)データそのものは分散データストアに格納して(格納可能にして)高可用性を実現する。つまり単なる1ピア運用とは異なる
(4)バイナリデータやファイル添付を含む大きなレコードのデータストアへの格納や全文検索機能、データを読み書きするための REST API を標準装備する



(2)の時点で上記ブロックチェーンの定義を満たさなくなっています(ブロックチェーンは非中央管集権管理型)。(3)ただ単に1ピアにデータを格納するのではなく、論理的な1データストアが実際には複数のロケーションに分散できるようにしており、これにより特定ロケーションが単一障害点とはならないようにします。そして(4)ブロックチェーンが比較的苦手とする巨大オブジェクトの格納や検索機能もこのシステムでは標準装備することを目標としています。加えて標準で REST API を準備することで(導入すればすぐ使えるようになって)導入時の負担軽減も実現するつもりです。

2018062902



【作っているもの】
上記仕組みを実現するためのソフトウェア(というかプラットフォームというか、API 群というか・・)を開発中です。まだ開発中ですが Github にて MIT ライセンスで公開しています:
https://github.com/dotnsf/hashchainsolo


上記の事情もあり、開発コード名には「ブロックチェーン」は含めず「ハッシュチェーンソロ」という表現にしました。レコードのブロックをチェーンのようにつなげていくものではあるのですが、上述の定義を満たすようなものではなく(むしろそこを犠牲にしている)、自分も「ブロックチェーン」という表現にこだわるわけではないので「ハッシュチェーン」という表現にしたかったのですが、これはこれで別の意味に使われており、ややこしいことになりそうだったので(ブロックチェーンだと単一ピアに相当する設計になるという意味で)「ハッシュチェーンソロ」と命名しています。某○icrosoft Office 365 Solo の影響とかではありませんw

インフラとして、分散データストアには IBM Cloudant を採用しています。Apache CouchDB をベースとした NoSQL 型のマネージド DBaaS で、容量・トランザクションパフォーマンスに制約があるものの無料プランを選択することもできるので、「とりあえず動作確認してみる」ためのハードルは低く実現できると思っています。一方でプランを見直して容量やパフォーマンスを変えたり、要件によってはプライベートクラウド版やオンプレミスソフトウェア版を使うことも可能です。またこの Cloudant 自体がバイナリ(添付ファイル)格納や検索インデックスといった機能を内包しているので、ブロックチェーンでいうところの「ステート DB」に相当する仕組みや外部ファイルシステム等がなくても各種データを格納することができ、上述の4つの特徴を兼ね備えた単独の仕組みを比較的作りやすいと思っています。

#現時点では実装していない余談ですが、IBM Cloudant をデータストアに使っていることで、携帯版の PouchDB と組み合わせてモバイルプラットフォームへの移植も想定しやすい設計にしています。

ハッシュポインタを使って耐改ざん性を高める部分は全てスクラッチで実装しています。タイムスタンプと nonce を併用してマイニングを行い、特定条件(変更可)を満たした時だけハッシュを採用できるようにしています。

2018/Jun/30 の現時点での開発状況は「とりあえず一通りの REST API は動くようになっている」レベルです。時間を見つけてぼちぼちと改良中です。


【Call for Code】
この製品で Call for Code に参加予定です。自然災害の支援を目的としたソリューションのコンテストで、ブロックチェーンを使う事例は多く存在すると思いますが、そのブロックチェーン技術の選択肢の1つとして、既存技術では難しいものも場合によっては解決できる、と思っていて、その実装として提供・参加予定です。




 

このページのトップヘ